i.MX8 BootLoader中AVB启动验证流程分析

do_boota函数分析
函数在./drivers/usb/gadget/f_fastboot.c文件中。

1. 判断从boot分区还是recovery启动

1
2
3
4
5
/* get bootmode, default to boot "boot" */
if (argc > 1) {
is_recovery_mode =
(strncmp(argv[1], "recovery", sizeof("recovery")) != 0) ? false: true;
}

2. 判断是否跳过AVB

1
2
3
4
5
6
avb=getenv("skip_avb");
if(avb && !strcmp(avb, "1"))
{
skip_avb = true;
printf("Force skipping AVB!\n");
}

如果环境变量设置了skip_avb = 1,则将跳过AVB。

3. 如果定义了CONFIG_FASTBOOT_LOCK

3.1 检查fastboot有没有被锁定

如果设置了skip_avb = 1,则fsatboot已解锁,否则则认为已锁定。
如果设置了skip_avb = 1,则允许校验失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* check lock state */
FbLockState lock_status = fastboot_get_lock_stat();
if (lock_status == FASTBOOT_LOCK_ERROR) {
printf("In boota get fastboot lock status error. Set lock status\n");
if(skip_avb)
{
fastboot_set_lock_stat(FASTBOOT_UNLOCK);
lock_status = FASTBOOT_UNLOCK;
}
else
{
fastboot_set_lock_stat(FASTBOOT_LOCK);
lock_status = FASTBOOT_LOCK;
}
}
bool allow_fail = (lock_status == FASTBOOT_UNLOCK ? true : false);
if(skip_avb)
{
lock_status = FASTBOOT_UNLOCK;
allow_fail = true;
}

3.2 判断是否为A/B双系统

  • AB双系统:调用avb_ab_flow_fast()进行AVB校验
  • 非AB双系统:调用avb_single_flow()并判断从boot分区还是recovery分区启动
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #ifdef CONFIG_ANDROID_AB_SUPPORT
    /* we can use avb to verify Trusty if we want */
    const char *requested_partitions[] = {"boot", 0};
    avb_result = avb_ab_flow_fast(&fsl_avb_ab_ops, requested_partitions, allow_fail,
    AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &avb_out_data);
    #else /* CONFIG_ANDROID_AB_SUPPORT */
    if (!is_recovery_mode) {
    const char *requested_partitions[] = {"boot", 0};
    avb_result = avb_single_flow(&fsl_avb_ab_ops, requested_partitions, allow_fail,
    AVB_HASHTREE_ERROR_MODE_RESTART, &avb_out_data);
    } else {
    const char *requested_partitions[] = {"recovery", 0};
    avb_result = avb_single_flow(&fsl_avb_ab_ops, requested_partitions, allow_fail,
    AVB_HASHTREE_ERROR_MODE_RESTART, &avb_out_data);
    }
    #endif /* CONFIG_ANDROID_AB_SUPPORT */

3.2.1 avb_ab_flow_fast()

函数在文件./lib/avb/libavb_ab/avb_ab_flow.c中,这里只分析A/B双系统的情况。

  1. 首先加载meta dada,如果出错,则直接报错返回。
    然后判断slot启动优先级:

    1
    target_slot = (ab_data.slots[1].priority > ab_data.slots[0].priority ? 1 : 0);
  2. 然后判断该slot能否启动,如果不能启动则切换为另一个slot。

    1
    2
    3
    if (!slot_is_bootable(&ab_data.slots[target_slot])) {
    target_slot = (target_slot == 1 ? 0 : 1);
    continue;

    其中判断能否启动的函数是通过slot的successful_boo标志位和剩余尝试次数来判断的。

    1
    2
    3
    4
    static bool slot_is_bootable(AvbABSlotData *slot) {
    return slot->priority > 0 &&
    (slot->successful_boot || (slot->tries_remaining > 0));
    }
  3. 实际上调用avb_slot_verify函数进行AVB校验。

    1
    2
    3
    4
    5
    6
    verify_result = avb_slot_verify(ops,
    requested_partitions,
    slot_suffixes[target_slot],
    flags,
    hashtree_error_mode,
    &slot_data[target_slot]);
  4. 验证结果处理

  • 如果是ERROR_OOM或者ERROR_IO,会直接返回校验失败并goto至clean_up函数。

  • 如果验证成功,会将当前slot作为启动的slot。

    1
    2
    3
    4
    case AVB_SLOT_VERIFY_RESULT_OK:
    slot_index_to_boot = target_slot;
    n = 2;
    break;
  • 如果是不合法的元数据或者不允许的版本(回滚保护),及时设置flag允许校验失败也没有用,会将当前slot设置为unbootable。

    1
    2
    3
    4
    5
    6
    7
    case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
    case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
    /* Even with AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
    * these mean game over.
    */
    set_slot_unbootable = true;
    break;
  • 如果是下列的校验不通过结果,会判断是否设置了允许启动,是的话则允许启动,否则标记当前slot为不可启动,接下来会循环判断另一个分区。
    所以在安卓O里自动切换可启动分区是在这里实现的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* explicit fallthrough. */
case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
if (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR) {
/* Do nothing since we allow this. */
avb_debugv("Allowing slot ",
slot_suffixes[target_slot],
" which verified "
"with result ",
avb_slot_verify_result_to_string(verify_result),
" because "
"AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR "
"is set.\n",
NULL);
saw_and_allowed_verification_error = true;
slot_index_to_boot = target_slot;
n = 2;
} else {
set_slot_unbootable = true;
}
break;
1
2
3
4
5
6
7
8
9
10
11
12
if (set_slot_unbootable) {
avb_errorv("Error verifying slot ",
slot_suffixes[target_slot],
" with result ",
avb_slot_verify_result_to_string(verify_result),
" - setting unbootable.\n",
NULL);
slot_set_unbootable(&ab_data.slots[target_slot]);
set_slot_unbootable = false;
}
/* switch to another slot */
target_slot = (target_slot == 1 ? 0 : 1);
  • 如果参数不对,则也直接返回。
1
2
3
4
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT:
ret = AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT;
goto out;
/* Do not add a 'default:' case here because of -Wswitch. */

3.3 AVB校验结果处理

  • 校验成功:加载分区。
  • 校验失败但是设置了允许失败(allow_fail):设置了skip_avb = 1或者判断fastboot已解锁,still 加载分区
  • 校验失败且fastboot已锁定:打印校验失败且锁定,goto fail,进入fastboot刷新模式;
  • 校验失败但fastboot未锁定:实际不会走到该分支,fastboot解锁后失败会走第二个分支。

i.MX8 BootLoader中AVB启动验证流程分析
https://www.shangyexin.com/2018/12/28/imx8-bootloader-avb/
作者
Yasin
发布于
2018年12月28日
许可协议