ATF加载自定义镜像

avatar
作者
猴君
阅读量:0

实际上包含了两个问题:

  1. 如何把自定义的二进制文件打包到fip.bin中?
  2. 如何在secure boot流程中load和认证自定义的二进制文件?

如何打包

证书创建工具和FIP打包工具是通过命令行传参的方式进行证书创建和打包的,如下:

image

由于我们需要打包自定义得image到fip,但是证书创建工具和FIP打包工具预定义的选项中并没有该选项,所以需要修改cert_createfip_tool工具。

准备自定义的image

我直接在ATF的platform.mk中生成了一个测试用的dtb作为image:

FDT_SOURCES += ${PLAT_QEMU_PATH}/test.dts HW_CONFIG := ${BUILD_PLAT}/fdts/test.dtb 

修改cert_create

  1. include/tools_share/tbbr_oid.h​文件中添加自定义的证书拓展域OID,定义如下(需保证与其他OID不同):

    #define USER_IMAGE_HASH_OID                     "1.3.6.1.4.1.4128.2100.1401" 
  2. tools/cert_create/include/tbbr/tbb_ext.h​添加一行自定义image的证书拓展域枚举类型:

    /* TBBR extensions */ enum { 	TRUSTED_FW_NVCOUNTER_EXT, 	NON_TRUSTED_FW_NVCOUNTER_EXT, 	TRUSTED_BOOT_FW_HASH_EXT, 	TRUSTED_BOOT_FW_CONFIG_HASH_EXT, .................  	SCP_FWU_CFG_HASH_EXT, 	AP_FWU_CFG_HASH_EXT, 	FWU_HASH_EXT, 	USER_IMAGE_EXT, /* user-defined image ext */ }; 
  3. 我们想要使用TRUSTED_BOOT_FW_CERT​去认证这个image,所以在tools/cert_create/src/tbbr/tbb_cert.c​文件中TRUSTED_BOOT_FW_CERT​的拓展域添加USER_IMAGE_EXT​枚举类型。

    /*  * Certificates used in the chain of trust  *  * The order of the certificates must follow the enumeration specified in  * tbb_cert.h. All certificates are self-signed, so the issuer certificate  * field points to itself.  */ static cert_t tbb_certs[] = { 	[TRUSTED_BOOT_FW_CERT] = { 		.id = TRUSTED_BOOT_FW_CERT, 		.opt = "tb-fw-cert", 		.help_msg = "Trusted Boot FW Certificate (output file)", 		.fn = NULL, 		.cn = "Trusted Boot FW Certificate", 		.key = ROT_KEY, 		.issuer = TRUSTED_BOOT_FW_CERT, 		.ext = { 			TRUSTED_FW_NVCOUNTER_EXT, 			TRUSTED_BOOT_FW_HASH_EXT, 			TRUSTED_BOOT_FW_CONFIG_HASH_EXT, 			HW_CONFIG_HASH_EXT, 			FW_CONFIG_HASH_EXT, 			USER_IMAGE_EXT, /* user-defined image ext */ 		}, 		.num_ext = 6 /*modify the size from five to six */ 	}, 
  4. 接着,在tools/cert_create/src/tbbr/tbb_ext.c​中添加该拓展域的内容:

    static ext_t tbb_ext[] = { 	[TRUSTED_FW_NVCOUNTER_EXT] = { 		.oid = TRUSTED_FW_NVCOUNTER_OID, 		.opt = "tfw-nvctr", 		.help_msg = "Trusted Firmware Non-Volatile counter value", 		.sn = "TrustedWorldNVCounter", 		.ln = "Trusted World Non-Volatile counter", 		.asn1_type = V_ASN1_INTEGER, 		.type = EXT_TYPE_NVCOUNTER, 		.attr.nvctr_type = NVCTR_TYPE_TFW 	}, ....................  /* new added */ 	[USER_IMAGE_EXT] = { 		.oid = USER_IMAGE_HASH_OID, /* 自定义的OID */ 		.opt = "user-img", /*这个会生成cert_create的选项 --user-img */ 		.help_msg = "User-defined Image file", 		.sn = "UserImgHash", 		.ln = "User-defined image HASH (SHA256)", 		.asn1_type = V_ASN1_OCTET_STRING, 		.type = EXT_TYPE_HASH, /* 使用hash校验该image */ 		.optional = 1 	} };  REGISTER_EXTENSIONS(tbb_ext); 

自此,cert_create可以使用–user-img命令为自定义的image生成hash并将hash值存储在TRUSTED_BOOT_FW_CERT​证书中。

修改fip_tool

image准备好了,证书生成了,该打包进fip了。

  1. 首先在include/tools_share/firmware_image_package.h​添加一个标识该image的唯一的UUID号,该UUID将会在加载认证阶段用于识别该image:

    #define UUID_USER_DEFINED_IMG \ 	{{0x23, 0x6e, 0xd3, 0x30}, {0x4e, 0xdf}, {0x11, 0xef}, 0x8d, 0xd7, {0x00, 0x15, 0x5d, 0xba, 0x59, 0x68} } 
  2. tools/fiptool/tbbr_config.c​文件中为fip_tool添加一个命令行选项--user-img​。

    toc_entry_t toc_entries[] = { 	{ 		.name = "Platform Key Certificate", 		.uuid = UUID_PLAT_KEY_CERT, 		.cmdline_name = "plat-key-cert" 	}, 	.................................     //添加命令 	{ 		.name = "User-defined Image", 		.uuid = UUID_USER_DEFINED_IMG, 		.cmdline_name = "user-img" 	}, 	{ 		.name = NULL, 		.uuid = { {0} }, 		.cmdline_name = NULL, 	} }; 

    这里的命令行选项需要和cert_create的保持一致。

  3. 做完以上操作,在使用fip_tool工具时在–user-img后面跟上自定义的image的路径就可以将image打包到fip_tool。

打包image

最后我们在platform.mk中使用一个宏TOOL_ADD_PAYLOAD即可完成:

  1. 将image的hash记录在TRUSTED_BOOT_FW_CERT​证书中,用于后续的验签
  2. 将image打包到fip.bin。

image

如何加载

可以拆解为以下问题:

  1. 从哪里加载?(需要修改IO storage)
  2. 加载到哪里?(定义加载的位置并进行MMU映射)
  3. 如何加载?怎么验签?(修改信任链)

从哪里加载

  1. 首先我们在include/export/common/tbbr/tbbr_img_def_exp.h​定义一个ID唯一标识该image:

    #define PLAT_USER_IMG_ID		U(38)  /* Max Images */ #define MAX_IMAGE_IDS			U(39) 
  2. 在平台路径plat/qemu/common/qemu_io_storage.c​中定义自定义image的描述符:

    static const io_uuid_spec_t user_image_uuid_spec = { 	.uuid = UUID_USER_DEFINED_IMG, }; 
  3. 在平台路径plat/qemu/common/qemu_io_storage.c​中的plat_io_policy数组中添加以下内容,表示该image从fip文件中根据UUID进行解析提取。

    static const struct plat_io_policy policies[] = { 	[FIP_IMAGE_ID] = { 		&memmap_dev_handle, 		(uintptr_t)&fip_block_spec, 		open_memmap 	}, ..................... 	[PLAT_USER_IMG_ID] = { 		&fip_dev_handle, 		(uintptr_t)&user_image_uuid_spec, 		open_fip 	}, }; 

加载到哪里

  1. 首先需要在plat/qemu/qemu/include/platform_def.h​中定义自定义镜像加载的基地址和边界,我这里定义的是在RAM的其实地址,最大为一个PAGE_SIZE(4KB),毕竟设备树很小。

    #define USER_IMG_BASE			BL_RAM_BASE #define USER_IMG_LIMIT			(USER_IMG_BASE + PAGE_SIZE) 
  2. 我们计划是在BL2阶段load镜像,所以需要在BL2起来之后进行memory map,不然未经过MMU平坦映射的地址是无法访问的。在plat/qemu/common/qemu_bl2_setup.c​中添加以下映射,内存属性为普通memory、可读写、secure。

    #define MAP_BL2_TOTAL		MAP_REGION_FLAT(			\ 					bl2_tzram_layout.total_base,	\ 					bl2_tzram_layout.total_size,	\ 					MT_MEMORY | MT_RW | MT_SECURE), \ 				/* new added */ \ 				MAP_REGION_FLAT(			\ 					USER_IMG_BASE,			\ 					USER_IMG_LIMIT			\ 						- USER_IMG_BASE,	\ 					MT_MEMORY | MT_RW | MT_SECURE) 

如何加载?怎么验签?

  1. 然后需要告诉BL2阶段需要加载这个自定义的image,在plat/qemu/common/qemu_bl2_mem_params_desc.c​的bl2_mem_params_descs​数组中添加如下内容。如此,BL2在load_image时就会解析该数组,根据镜像UUID在fip中查找到该镜像,并将镜像load到指定的镜像基地址进行验证。

    static bl_mem_params_node_t bl2_mem_params_descs[] = { 	/* Fill BL32 related information */ 	{ .image_id = BL32_IMAGE_ID, 	  SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, VERSION_2, 				entry_point_info_t, BL32_EP_ATTRIBS), 	  .ep_info.pc = BL32_BASE, 	  SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, VERSION_2, 				image_info_t, BL32_IMG_ATTRIBS), 	  .image_info.image_base = BL32_BASE, 	  .image_info.image_max_size = BL32_LIMIT - BL32_BASE,  	  .next_handoff_image_id = BL33_IMAGE_ID, 	}, ................................... 	/* new added */ 	{ .image_id = PLAT_USER_IMG_ID, /* 镜像ID */ 	   SET_STATIC_PARAM_HEAD(ep_info, PARAM_IMAGE_BINARY, VERSION_2, 				 entry_point_info_t, SECURE | NON_EXECUTABLE), 	   SET_STATIC_PARAM_HEAD(image_info, PARAM_IMAGE_BINARY, VERSION_2, 				 image_info_t, 0), 	   .image_info.image_base = USER_IMG_BASE, /* 加载的基地址 */ 	   .image_info.image_max_size = USER_IMG_LIMIT - USER_IMG_BASE, /*镜像最大size*/ 	   .next_handoff_image_id = INVALID_IMAGE_ID, 	}, }; 
  2. 然后还需要修改COT(chain of trust),否则BL2不知道如何验证该镜像的完整性。在drivers/auth/tbbr/tbbr_cot_common.c​中添加如下内容,表明TRUSTED_BOOT_FW_CERT_ID​证书的拓展域存储了自定义image的hash值。

    line 32: static unsigned char user_img_hash_buf[HASH_DER_LEN]; line 56: auth_param_type_desc_t user_img_hash = AUTH_PARAM_TYPE_DESC( 	AUTH_PARAM_HASH, USER_IMAGE_HASH_OID);  * trusted_boot_fw_cert */ line 63: const auth_img_desc_t trusted_boot_fw_cert = { 	.img_id = TRUSTED_BOOT_FW_CERT_ID, 	.img_type = IMG_CERT, 	.parent = NULL, 	.img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { 	 ................. 	}, 	.authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) { 		[0] = { 			.type_desc = &tb_fw_hash, 			.data = { 				.ptr = (void *)tb_fw_hash_buf, 				.len = (unsigned int)HASH_DER_LEN 			} 		}, 		................................ 		/* new added */ 		[4] = { 			.type_desc = &user_img_hash, /* hash 校验 */ 			.data = { 				.ptr = (void *)user_img_hash_buf, /* 证书拓展域的hash buffer */ 				.len = (unsigned int)HASH_DER_LEN 			} 		}, 	} }; 
  3. 还需要在drivers/auth/tbbr/tbbr_cot_common.c​文件中增加一个需要认证的节点的描述结构:

    const auth_img_desc_t user_img = { 	.img_id = PLAT_USER_IMG_ID, 	.img_type = IMG_RAW, 	.parent = &trusted_boot_fw_cert, /* 指定它需要由trusted_boot_fw_cert认证*/ 	.img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { 		[0] = { 			.type = AUTH_METHOD_HASH, /* 认证方法为hash */ 			.param.hash = { 				.data = &raw_data, 				.hash = &user_img_hash 			} 		} 	} }; 
  4. 最后将该结构加入到COT当中。在文件drivers/auth/tbbr/tbbr_cot_bl2.c​增加。这样,在验证该image时,BL2就可以根据COT找到认证该image所需的父节点和方法。

    static const auth_img_desc_t * const cot_desc[] = { 	[TRUSTED_BOOT_FW_CERT_ID]		=	&trusted_boot_fw_cert, 	[HW_CONFIG_ID]				=	&hw_config, 	[TRUSTED_KEY_CERT_ID]			=	&trusted_key_cert, 	[SCP_FW_KEY_CERT_ID]			=	&scp_fw_key_cert, 	[SCP_FW_CONTENT_CERT_ID]		=	&scp_fw_content_cert,  	................................. 	[PLAT_USER_IMG_ID]			=	&user_img, //new added }; 

Demo

从启动过程中log可以看出:BL2在加载ID为38的镜像(即我们添加的自定义镜像PLAT_USER_IMG_ID​)时,去加载了ID=6的TRUSTED_BOOT_FW_CERT​证书对齐进行认证,认证成功后才开始去解析该image。

image

文档提供完整patch资源,在ATF2.9.0的qemu平台的代码上实现了将一个自定义的dtb打包到fip中,并且在BL2阶段对该dtb进行加载和验证。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!