ATF加载自定义镜像

实际上包含了两个问题:

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

如何打包

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

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

准备自定义的image

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

shell 复制代码
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不同):

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

    c 复制代码
    /* 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​枚举类型。

    c 复制代码
    /*
     * 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​中添加该拓展域的内容:

    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:

    c 复制代码
    #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​。

    c 复制代码
    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。

如何加载

可以拆解为以下问题:

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

从哪里加载

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

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

    c 复制代码
    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进行解析提取。

    c 复制代码
    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),毕竟设备树很小。

    c 复制代码
    #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。

    c 复制代码
    #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到指定的镜像基地址进行验证。

    c 复制代码
    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值。

    c 复制代码
    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​文件中增加一个需要认证的节点的描述结构:

    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所需的父节点和方法。

    c 复制代码
    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。

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

相关推荐
CyberSecurity_zhang18 天前
Armv8的安全启动
安全·arm·tf-a·tf-m·安全启动
CyberSecurity_zhang2 个月前
汽车信息安全 -- 再谈车规MCU的安全启动
网络安全·信息安全·汽车·安全启动
代码改变世界ctw4 个月前
Armv8/Armv9的Pstate寄存器介绍
汇编·arm开发·trustzone·atf·tee·寄存器·secure
JUNIOR_MU8 个月前
【VMware ESXi】HP Z4G4 Workstation安装ESXi停留在Shutting down firmware services...的解决办法。
esxi·uefi·hp工作站·z4g4·secure boot
Hcoco_me9 个月前
什么是Boot Guard?电脑启动中的信任链条解析
arm开发·算法·电脑·uefi·intel·安全启动
安全二次方security²9 个月前
TF-A之供应链威胁模型分析
atf·tf-a·供应链·arm安全架构·arm架构·supply chain·威胁模型
安全二次方security²1 年前
ATF安全漏洞挖掘之FUZZ测试
atf·tf-a·smc·fuzz测试·模糊测试
安全二次方security²1 年前
ATF(TF-A)/OPTEE之动态代码分析汇总
atf·tf-a·optee·动态代码分析·ubsan·kasan·地址消毒
toradexsh1 年前
NXP iMX8MM 修改 UART4至 Cortex-A53 核心
linux·arm·atf·nxp·imx8mm