arm-trusted-firmware可信启动机制

arm-trusted-firmware可信启动机制

ATF 可信启动调研

# ARM 安全技术(ATF 的可信启动)

# 一、目标

本文主要针对 ATF 的可信启动进行分析,可信机制主要位于代码的 BL2 文件夹中,目录结构如下:

image-20220419235053590

为便于后续分析给出 ATF 加载流程,给出官网中一个直观的流程图:

img

另外 ATF 固件源码整体架构如下:

在这里插入图片描述

# 二、核心服务原理分析

# (一)安全启动与信任链

安全启动是建立系统信任链(Chain of Trust)的基础。信任链(Chain of Trust)是基于根信任(Root of trust)创建的, 而根信任的实现是基于两种技术:不可修改的 bootloader 和不可被修改的公钥。公钥通常存放在 OTP(One-Time-Programmable)内存中, bootloader 同常存储在 ROM 中或者不可修改的 Flash 内存中。

# (二)安全启动流程图

为了便于后续对代码的理解分析,这里先给出安全启动流程图

image-20220419230432541

# 说明:

# arch 初始化

对于 AArch64:

  1. BL2 执行 normal world 和后续阶段所需的最小架构初始化。
  2. 通过清零 CPACR.FPEN 位,使 EL1 和 EL0 可以访问 SIMD 寄存器。

# Platform 初始化

BL2 主要执行如下初始化步骤:

1、初始化 console (PL101).(尽管在 bl1 时已经初始化过一次)
2、初始化和配置储存设备驱动,用于加载后续的 bl。
3、使能 MMU ,map the memory,访问权限.
4、平台安全设置,相关组件(寄存器,外设,地址等)的访问控制
5、为 BL3 阶段的 image 保留内存空间。
6、为 BL3 阶段的 image 定义可用内存地址范围。
7、如果 BL1 使用 TB_FW_CONFIG dynamic configuration file (保存在 arg0) , 解析配置参数

# image load

BL2 通过查找 image list 的方式加载 image,并且将这个 list 传递给下一个 BL 镜像。

平台实现方法提供的可加载 image list 还可以包含动态配置文件。这个配置文件可以根据需要在 bl2_plat_handle_post_image_load()函数中进行解析。 通过更新此函数中的相应 ep 信息,可以将这些配置文件作为参数传递给下一个 Boot Loader 阶段。

# SCP_BL2 image load

BL2 将可选的 SCP_BL2 镜像从平台存储设备加载到特定的安全内存区域。 SCP_BL2 的后续处理是特定于具体平台的,需要自行实现。 例如,Arm Juno ,BL2 先把 SCP_BL2 加载到 trust sram,再使用 Boot Over MHU (BOM) 协议,把 SCP_BL2 加载到 SCP 的内部 RAM 之后,SCP 运行 SCP_BL2,并给 AP 发出 signals,通知 BL2 继续执行。

# Load EL3 software

BL2 从平台存储设备加载 EL3 runtime software 到 trusted SRAM. 如果内存空间不够或者镜像不存在去,则 assert 停止运行。

# AArch64(Secure-EL1 payload) image load

BL2 将可选的 BL32 镜像从平台存储设备加载到特定于平台的安全存储区域。BL32 镜像在安全世界中执行。BL2 依靠 BL31 将控制权限传递给 BL32(如果存在)。 因此,BL2 也会使用 BL32 镜像的 entrypoint。 用于进入 BL32 的 Saved Processor Status Register(SPSR)的值不是由 BL2 确定的,它由 BL31 内的 Secure-EL1 Payload Dispatcher (SPD) 初始化,SPD 负责管理与 BL32 的交互。此信息将传递给 BL31。

# BL33(Non-trusted Fireware) image load

BL2 将 BL33 镜像(e.g. UEFI or other test or boot software)从平台存储设备加载到由平台定义的非安全内存中。

一旦安全状态初始化完成,BL2 依靠 EL3 Runtime Software 将控制权传递给 BL33。 因此,BL2 使用正常世界的镜像入口和保存程序状态寄存器(SPSR)填充平台指定的存储区域。entrypoint 是 BL33 镜像的加载地址。 SPSR 按照 PSCI PDD 中的规定确定(PSCI 5.13 节)。 此信息将传递给 EL3runtime software。

# AArch64 BL31(EL3 Runtime Software) execution

BL2 执行继续如下:

BL2 通过产生 SMC 异常将控制权传递回 BL1,并给 BL1 提供 BL31 入口点。 SMC 异常由 BL1 阶段 install 的 SMC exception handler 来处理。
BL1 关闭 MMU 并刷 Cache。清 SCTLR_EL3.M/ I / C 位,将 D-cache 刷新到 point of coherency 并使 TLB 无效。
BL1 在 EL3 的指定入口地址将控制权传递给 BL31。

# 三、源代码分析

# (一)BL1(Trusted Boot ROM)分析

BL1 启动最早的 ROM,是在 CPU 的 ROM 里不是和 BIOS 一起,是一起的信任根。BL1 主要目的是建立 Trusted SRAM、exception vector、初始化串口 console 等等。然后找到并验证 BL2(验签 CSF 头),然后跳过去。

入口点:bl1_entrypoint.S:

1
2
3
4
5
6
7
8
9
func bl1_entrypoint
....
bl bl1_early_platform_setup
bl bl1_plat_arch_setup
....
bl bl1_main
....
b el3_exit
endfunc bl1_entrypoint

# (二)BL2(Trusted Boot Firmware)分析

# 1、功能概要

BL2 主要负责对其他所有 BL 进行认证和加载,并执行 BL31, 该函数主要实现将 BL3x 的 image 加载 RAM 中,并通过 smc 调用执行 BL1 中指定的 smc handle 将 CPU 的全向交给 BL31。

# 2、主过程

各部分代码分析已在注释中给出

# BL2_entrypoint.S

  • BL2 入口位于 bl2/aarch64/bl2_entrypoint.S 中,BL2_entrypoint 是 BL2 的入口,前半部分主要进行一系列初始化工作,然后通过 BL2_main () 加载 BL3x 镜像到 RAM 中,最后通过 SMC 调用执行 BL1 中指定的 smc handler 将 CPU 执行权交给 BL31。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
	.globl	bl2_entrypoint



func bl2_entrypoint
mov x20, x1 /* x1保存了内存布局信息 */

/* 设置异常处理函数 */
adr x0, early_exceptions
msr vbar_el1, x0
isb

/* ---------------------------------------------
* 使能异常
* ---------------------------------------------
*/
msr daifclr, #DAIF_ABT_BIT

/* ---------------------------------------------
* 使能指令缓存,使能堆栈数据访问对齐检查
* ---------------------------------------------
*/
mov x1, #(SCTLR_I_BIT | SCTLR_A_BIT | SCTLR_SA_BIT)
mrs x0, sctlr_el1
orr x0, x0, x1
msr sctlr_el1, x0
isb

/* ---------------------------------------------
* 失效内存
* ---------------------------------------------
*/
adr x0, __RW_START__
adr x1, __RW_END__
sub x1, x1, x0
bl inv_dcache_range

/* ---------------------------------------------
* BSS内存初始化
* ---------------------------------------------
*/
ldr x0, =__BSS_START__
ldr x1, =__BSS_SIZE__
bl zeromem16

#if USE_COHERENT_MEM
ldr x0, =__COHERENT_RAM_START__
ldr x1, =__COHERENT_RAM_UNALIGNED_SIZE__
bl zeromem16
#endif

/* --------------------------------------------
* 设置SP指针
* --------------------------------------------
*/
bl plat_set_my_stack

/* ---------------------------------------------
* 串口初始化,更新内存布局信息,并初始化页表使能mmu
* ---------------------------------------------
*/
mov x0, x20
bl bl2_early_platform_setup
bl bl2_plat_arch_setup

/* ---------------------------------------------
* 跳转到主函数(通过SMC执行下一级BL不会返回)
* ---------------------------------------------
*/
bl bl2_main

/* ---------------------------------------------
* 下面的代码不会执行
* ---------------------------------------------
*/
no_ret plat_panic_handler

endfunc bl2_entrypoint

# BL2_main

bl2_main 为 bl2 的主程序,位于 bl2/bl2_main.c 中,安全启动的最重要两步在这个函数中完成:初始化硬件和找到 BL31。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
void bl2_main(void)
{
entry_point_info_t *next_bl_ep_info;
/* 输出提示信息 */
NOTICE("BL2: %s\n", version_string);
NOTICE("BL2: %s\n", build_message);

/* Perform remaining generic architectural setup in S-EL1 */

/* 初始化,这里开启FP/SIMD的访问权限 */
bl2_arch_setup();

#if PSA_FWU_SUPPORT
fwu_init();
#endif /* PSA_FWU_SUPPORT */

crypto_mod_init(); //初始化加密库,加密库可以用于校验签名和哈希

/* Initialize authentication module */
auth_mod_init(); //初始化认证模块

/* Initialize the Measured Boot backend */
bl2_plat_mboot_init(); //初始化 measured boot后端

/* Initialize boot source */
bl2_plat_preload_setup(); //初始化镜像解析模块(img_parser_mod),用于校验镜像完整性以及从镜像中提取内容

/*加载后续引导加载程序映像。*/
next_bl_ep_info = bl2_load_images();


/*拆除 Measured Boot 后端*/
bl2_plat_mboot_finish();

#if !BL2_AT_EL3 && !ENABLE_RME
#ifndef __aarch64__
/*
* 对于 AArch32 状态,BL1 和 BL2 共享 MMU 设置。
* 鉴于 BL2 不映射 BL1 区域,MMU 需要
* 被禁用以返回 BL1。
*/
disable_mmu_icache_secure();
#endif /* !__aarch64__ */

console_flush(); //控制台刷新

#if ENABLE_PAUTH
/*
* 在运行下一个引导映像之前禁用指针身份验证
*/
pauth_disable_el1();
#endif /* ENABLE_PAUTH */


/* 调用smc指令,触发在bl1中设定的smc异常中断处理函数,跳转到bl31 */
smc(BL1_SMC_RUN_IMAGE, (unsigned long)next_bl_ep_info, 0, 0, 0, 0, 0, 0);
#else /* if BL2_AT_EL3 || ENABLE_RME */
NOTICE("BL2: Booting " NEXT_IMAGE "\n");
print_entry_point_info(next_bl_ep_info);
console_flush();

#if ENABLE_PAUTH
/*
* 在运行下一个引导映像之前禁用指针身份验证
*/
pauth_disable_el3();
#endif /* ENABLE_PAUTH */

bl2_run_next_image(next_bl_ep_info);
#endif /* BL2_AT_EL3 && ENABLE_RME */
}

下面依次对 bl2_main 中的安全模块进行分析:

# crypto_mod_init()

主要初始化加密库,加密库可以用于校验签名和哈希,跟进 crypto_mod_init()

image-20220419223805632

在 crypto_mod.h 中定义了 crypto_lib_desc_s 结构体

image-20220419223942221

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct crypto_lib_desc_s {
const char *name;/* 名称, */

void (*init)(void);/* 初始化方法 */

/* 校验签名的方法 */
int (*verify_signature)(
void *data_ptr, unsigned int data_len, /* 要签名的数据 */
void *sig_ptr, unsigned int sig_len, /* 签名 */
void *sig_alg, unsigned int sig_alg_len, /* 签名算法 */
void *pk_ptr, unsigned int pk_len); /* 公钥 */

/* 校验哈希的方法 */
int (*verify_hash)(
void *data_ptr, unsigned int data_len, /* 要计算哈希的数据 */
void *digest_info_ptr, unsigned int digest_info_len);/* 哈希值 */
} crypto_lib_desc_t;

通过 REGISTER_CRYPTO_LIB 宏实现一个名为 crypto_lib_desc 类型为 crypto_lib_desc_t 结构体。宏实现如下:

1
2
3
4
5
6
7
#define REGISTER_CRYPTO_LIB(_name, _init, _verify_signature, _verify_hash) \
const crypto_lib_desc_t crypto_lib_desc = { \
.name = _name, \
.init = _init, \
.verify_signature = _verify_signature, \
.verify_hash = _verify_hash \
}

此模块通过操作 crypto_lib_desc 变量,实现模块初始化、校验签名、校验哈希。函数声明如下:

1
2
3
4
5
6
7
8
9
10
11
/* 模块初始化 */
void crypto_mod_init(void);

/* 校验签名 */
int crypto_mod_verify_signature(void *data_ptr, unsigned int data_len,
void *sig_ptr, unsigned int sig_len,
void *sig_alg, unsigned int sig_alg_len,
void *pk_ptr, unsigned int pk_len);
/* 校验哈希值 */
int crypto_mod_verify_hash(void *data_ptr, unsigned int data_len,
void *digest_info_ptr, unsigned int digest_info_len);
# auth_mod_init()

auth_mod 实现了一个校验镜像的模型,此模型通过结构体 auth_img_desc_t 描述,跟进 auth_mod_init,

image-20220419224356942

在 auth.mod.h 中定义了 auth_img_desc_s 结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef struct auth_img_desc_s {
/* 镜像的ID,标志是哪一个镜像 */
unsigned int img_id;

/* 镜像类型(Binary、证书等) */
img_type_t img_type;

/* 父镜像,保存了认证当前镜像的公钥、哈希等 */
const struct auth_img_desc_s *parent;

/* 认证当前镜像的方法 */
auth_method_desc_t img_auth_methods[AUTH_METHOD_NUM];

/* 用于校验子镜像的公钥、哈希等 */
auth_param_desc_t authenticated_data[COT_MAX_VERIFIED_PARAMS];
} auth_img_desc_t;

并定义一个宏 REGISTER_COT ,用于注册 auth_img_desc_t 数组

1
2
3
4
#define REGISTER_COT(_cot) \
const auth_img_desc_t *const cot_desc_ptr = \
(const auth_img_desc_t *const)&_cot[0]; \
unsigned int auth_img_flags[sizeof(_cot)/sizeof(_cot[0])]

auth_mod_verify_img 通过 img_id 访问 cot_desc_ptr 数组,找到对应的镜像描述符 auth_method_desc_t ,即可知道当前镜像的认证方式,访问父节点找到签名的公钥或哈希,即可认证当前镜像是否合法。在认证完当前镜像后,从镜像中解析出公钥哈希等放入当前的镜像描述符中,便于对下一级镜像校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
int auth_mod_verify_img(unsigned int img_id,
void *img_ptr,
unsigned int img_len)
{
const auth_img_desc_t *img_desc = NULL;
const auth_method_desc_t *auth_method = NULL;
void *param_ptr;
unsigned int param_len;
int rc, i;

/* 根据img_id获取镜像描述符 */
img_desc = &cot_desc_ptr[img_id];

/* 校验镜像完整性 */
rc = img_parser_check_integrity(img_desc->img_type, img_ptr, img_len);
return_if_error(rc);

/* 根据镜像描述符的仍正方式对镜像进行认证 */
for (i = 0 ; i < AUTH_METHOD_NUM ; i++) {
auth_method = &img_desc->img_auth_methods[i];
switch (auth_method->type) {
case AUTH_METHOD_NONE:/* 不需要认证 */
rc = 0;
break;
case AUTH_METHOD_HASH:/* 哈希认证 */
rc = auth_hash(&auth_method->param.hash,
img_desc, img_ptr, img_len);
break;
case AUTH_METHOD_SIG:/* 签名认证 */
rc = auth_signature(&auth_method->param.sig,
img_desc, img_ptr, img_len);
break;
case AUTH_METHOD_NV_CTR:/* Non-Volatile counter认证? */
rc = auth_nvctr(&auth_method->param.nv_ctr,
img_desc, img_ptr, img_len);
break;
default:
/* 未知认证类型,报错 */
rc = 1;
break;
}
return_if_error(rc);
}

/* 从镜像中解析出公钥哈希等,以便对下一级镜像进行认证 */
for (i = 0 ; i < COT_MAX_VERIFIED_PARAMS ; i++) {
if (img_desc->authenticated_data[i].type_desc == NULL) {
continue;
}

/* 通过镜像解析器从镜像中提取内容 */
rc = img_parser_get_auth_param(img_desc->img_type,
img_desc->authenticated_data[i].type_desc,
img_ptr, img_len, &param_ptr, &param_len);
return_if_error(rc);

/* 异常检查
防止从镜像中解析出的数据字节数大于镜像描述符中的字节数
出现内存访问溢出 */
if (param_len > img_desc->authenticated_data[i].data.len) {
return 1;
}

/* 把解析出的内容拷贝到镜像描述符中,便于解析下一级BL */
memcpy((void *)img_desc->authenticated_data[i].data.ptr,
(void *)param_ptr, param_len);
}

/* 标记镜像以认证过 */
auth_img_flags[img_desc->img_id] |= IMG_FLAG_AUTHENTICATED;

return 0;
}

# BL2_image_load_v2.c

该函数用来加载 bl3x 的 image 到 RAM 中,返回一个具有 image 入口信息的变量。smc handle 根据该变量跳转到 bl31 进行执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
struct entry_point_info_t *bl2_load_images(void)
{
bl_params_t *bl2_to_next_bl_params;
bl_load_info_t *bl2_load_info;
const bl_load_info_node_t *bl2_node_info;
int plat_setup_done = 0;
int err;

/*
* Get information about the images to load.
*/
/* 获取bl3x image的加载和入口信息 */
bl2_load_info = plat_get_bl_image_load_info();

/* 检查返回的bl2_load_info中的信息是否正确 */
assert(bl2_load_info);
assert(bl2_load_info->head);
assert(bl2_load_info->h.type == PARAM_BL_LOAD_INFO);
assert(bl2_load_info->h.version >= VERSION_2);

/* 将bl2_load_info中的head变量的值赋值为bl2_node_info,即将bl31 image的入口信息传递給bl2_node_info变量 */
bl2_node_info = bl2_load_info->head;

/* 进入loop循环, */
while (bl2_node_info) {
/*
* Perform platform setup before loading the image,
* if indicated in the image attributes AND if NOT
* already done before.
*/
/* 在加载特定的bl3x image到RAM之前先确定是否需要做平台的初始化 */
if (bl2_node_info->image_info->h.attr & IMAGE_ATTRIB_PLAT_SETUP) {
if (plat_setup_done) {
WARN("BL2: Platform setup already done!!\n");
} else {
INFO("BL2: Doing platform setup\n");
bl2_platform_setup();
plat_setup_done = 1;
}
}

/* 对bl3x image进行电子验签,如果通过则执行加载操作 */
if (!(bl2_node_info->image_info->h.attr & IMAGE_ATTRIB_SKIP_LOADING)) {
INFO("BL2: Loading image id %d\n", bl2_node_info->image_id);
err = load_auth_image(bl2_node_info->image_id,
bl2_node_info->image_info);
if (err) {
ERROR("BL2: Failed to load image (%i)\n", err);
plat_error_handler(err);
}
} else {
INFO("BL2: Skip loading image id %d\n", bl2_node_info->image_id);
}

/* Allow platform to handle image information. */
/* 可以根据实际需要更改,通过给定image ID来更改image的加载信息 */
err = bl2_plat_handle_post_image_load(bl2_node_info->image_id);
if (err) {
ERROR("BL2: Failure in post image load handling (%i)\n", err);
plat_error_handler(err);
}

/* Go to next image */
bl2_node_info = bl2_node_info->next_load_info;
}

/*
* Get information to pass to the next image.
*/
/* 获取下一个执行的Image的入口信息,并且将以后会被执行的image的入口信息组合成链表 ,t通过判断image des中的ep_info.h.attr的值是否为(EXECUTABLE|EP_FIRST_EX)来确定接下来第一个被执行的image*/
bl2_to_next_bl_params = plat_get_next_bl_params();
assert(bl2_to_next_bl_params);
assert(bl2_to_next_bl_params->head);
assert(bl2_to_next_bl_params->h.type == PARAM_BL_PARAMS);
assert(bl2_to_next_bl_params->h.version >= VERSION_2);

/* Flush the parameters to be passed to next image */
plat_flush_next_bl_params();

/* 返回下一个进入的image的入口信息,即bl31的入口信息 */
return bl2_to_next_bl_params->head->ep_info;
}

# 四、分析结论

​ 结合 ATF 整个信任链条建立的流程图,我们了解到可信启动中的安全模块和可信机制,从作为信任根的 BL1 开始,逐步进行初始化和加载镜像,最后来到 BL33,后面就是 OS 了。

最后引用一张 ATF 的 UEFI 启动流程进行更直观展示:

img

以上仅是对 ATF 可信启动机制的简要分析,而对于 ATF 的更深层次技术需要更多信息搜集和整理研究。

# 五、参考文章

https://zhuanlan.zhihu.com/p/391101179

https://github.com/hardenedlinux/embedded-iot_profile/blob/master/docs/arm64/arm-trusted-firmware 分析.md

https://blog.csdn.net/puyoupuyou/article/details/109506419?spm=1001.2101.3001.6650.15&utm_medium=distribute.pc_relevant.none-task-blog-2defaultBlogCommendFromBaiduRate-15.topblog&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2defaultBlogCommendFromBaiduRate-15.topblog&utm_relevant_index=20

Author

y1seco

Posted on

2022-04-20

Updated on

2022-04-20

Licensed under

Comments

:D 一言句子获取中...