sa6125平台启动异常

最近有一个版本出现启动异常,借此机会回顾一下abl里面的代码


由于问题机器得环境已经被破坏了,因此只能根据uart log去一步一步跟踪代码查看
[17:40:52]UEFI Start     [  768]
[17:40:52]Continue booting UEFI on Core 0
[17:40:52]UFS INQUIRY ID: MICRON  MT064GASAO2U21  0202
[17:40:52]HW Wdog Setting from PCD : Disabled
[17:40:52]PM0: 45, 
[17:40:53]UsbConfigLibOpenProtocols: PMI8998 not detected
[17:40:53]UsbConfigLibOpenProtocols: gPmicNpaClientSS1 cannot be created
[17:40:53]DisplayDxe: Getting PMIC mode failed with error(4), skipping peripheral power configuration!

[17:40:53]Alternate slot not FoundNo bootable slots found enter fastboot mode
[17:40:53]------ABL FV already mounted
[17:40:53]UEFI Total : 755 ms
[17:40:53]POST Time      [ 1523] OS Loader
[17:40:53]avb_slot_verify.c:236: ERROR: boot_a: Hash of data does not match digest in descriptor.

从串口中可以看到一个关键点,那就是avb_slot_verify报错。因此我们先看看这个函数做了什么

 if (Avb_StrnCmp ( (CONST CHAR8*)hash_desc.hash_algorithm, "sha256",
                 avb_strlen ("sha256")) == 0) {
    avb_sha256_init(&sha256_ctx);
    avb_sha256_update(&sha256_ctx, desc_salt, hash_desc.salt_len);
    avb_sha256_update(&sha256_ctx, image_buf, hash_desc.image_size);
    digest = avb_sha256_final(&sha256_ctx);
    digest_len = AVB_SHA256_DIGEST_SIZE;
  } else if (Avb_StrnCmp ( (CONST CHAR8*)hash_desc.hash_algorithm, "sha512",
                  avb_strlen ("sha512")) == 0) {
    avb_sha512_init(&sha512_ctx);
    avb_sha512_update(&sha512_ctx, desc_salt, hash_desc.salt_len);
    avb_sha512_update(&sha512_ctx, image_buf, hash_desc.image_size);
    digest = avb_sha512_final(&sha512_ctx);
    digest_len = AVB_SHA512_DIGEST_SIZE;
  } else {
    avb_errorv(part_name, ": Unsupported hash algorithm.\n", NULL);
    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
    goto out;
  }

  if (digest_len != hash_desc.digest_len) {
    avb_errorv(
        part_name, ": Digest in descriptor not of expected size.\n", NULL);
    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
    goto out;
  }

  if (avb_safe_memcmp(digest, desc_digest, digest_len) != 0) {
    avb_errorv(part_name,
               ": Hash of data does not match digest in descriptor.\n",
               NULL);
    ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
    goto out;
  } else {
    avb_debugv (part_name, ": success: Image verification completed\n", NULL);
  }

这里是调用avb_safe_memcmp函数,对digest,desc_digest内容进行比较发生错误。由于我们用的是sha256的方式进行校验,因此digest是在上面的avb_sha256_final计算得到的。digest裔据image_buf中的数据进行sha256计算,这部分的函数入口为load_and_verify_hash_partition。那么我们就得到第一个结论:abl中对boot分区进行hash校验失败从而导致上面那个打印以及进入fastboot。那么再继续往上查

  descriptors =
      avb_descriptor_get_all(vbmeta_buf, vbmeta_num_read, &num_descriptors);
  if (descriptors == NULL) {
      goto out;
  }
  for (n = 0; n < num_descriptors; n++) {
    AvbDescriptor desc;

    if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {
      avb_errorv(full_partition_name, ": Descriptor is invalid.\n", NULL);
      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
      goto out;
    }

    switch (desc.tag) {
      case AVB_DESCRIPTOR_TAG_HASH: {
        AvbSlotVerifyResult sub_ret;
        sub_ret = load_and_verify_hash_partition(ops,
                                                 requested_partitions,
                                                 ab_suffix,
                                                 allow_verification_error,
                                                 descriptors[n],
                                                 slot_data);
        if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
          ret = sub_ret;
          if (!allow_verification_error || !result_should_continue(ret)) {
            goto out;
          }
        }
      } break;

可以看到,通过switch选择后进入load_and_verify_hash_partition函数计算hash值。其中查看一下desc.tag的获取,avb_descriptor_validate_and_byteswap函数做什么并不需要关心(看多则乱),可以看到一个关键的函数avb_descriptor_get_all(vbmeta_buf, vbmeta_num_read, &num_descriptors);在这个函数中会对vbmeta进行解析,这一阶段的函数为load_and_verify_vbmeta,具体解析什么也先不要看。再往上翻翻

    /* No vbmeta partition, go through each of the requested partitions... */
    for (size_t n = 0; requested_partitions[n] != NULL; n++) {
      ret = load_and_verify_vbmeta(ops,
                                   requested_partitions,
                                   ab_suffix,
                                   flags,
                                   allow_verification_error,
                                   0 /* toplevel_vbmeta_flags */,
                                   0 /* rollback_index_location */,
                                   requested_partitions[n],
                                   avb_strlen(requested_partitions[n]),
                                   NULL /* expected_public_key */,
                                   0 /* expected_public_key_length */,
                                   slot_data,
                                   &algorithm_type);
      if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) {
        goto fail;
      }
    }

  } else {
    /* Usual path, load "vbmeta"... */
    ret = load_and_verify_vbmeta(ops,
                                 requested_partitions,
                                 ab_suffix,
                                 flags,
                                 allow_verification_error,
                                 0 /* toplevel_vbmeta_flags */,
                                 0 /* rollback_index_location */,
                                 "vbmeta",
                                 avb_strlen("vbmeta"),
                                 NULL /* expected_public_key */,
                                 0 /* expected_public_key_length */,
                                 slot_data,
                                 &algorithm_type);
    if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) {
      goto fail;
    }
  }

发现是在这里调用load_and_verify_vbmeta函数,由于sa6125平台存在vbmeta分区,因此我们走的是else的流程。再往上找,看看是哪里调用avb_slot_verify,

  if ( ( (!Info->MultiSlotBoot) ||
           IsDynamicPartitionSupport ()) &&
           Info->BootIntoRecovery) {
    if (!Info->MultiSlotBoot)
              VerifyFlags = VerifyFlags | AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION;
    AddRequestedPartition (RequestedPartitionAll, IMG_RECOVERY);
    NumRequestedPartition += 1;
    Result = avb_slot_verify (Ops, (CONST CHAR8 *CONST *)RequestedPartition,
               SlotSuffix, VerifyFlags, VerityFlags, &SlotData);
    if (AllowVerificationError &&
               ResultShouldContinue (Result)) {
      DEBUG ((EFI_D_VERBOSE, "State: Unlocked, AvbSlotVerify returned "
                          "%a, continue boot\n",
              avb_slot_verify_result_to_string (Result)));
    } else if (Result != AVB_SLOT_VERIFY_RESULT_OK) {
      DEBUG ((EFI_D_ERROR, "ERROR: Device State %a,AvbSlotVerify returned %a\n",
             AllowVerificationError ? "Unlocked" : "Locked",
            avb_slot_verify_result_to_string (Result)));
      Status = EFI_LOAD_ERROR;
      Info->BootState = RED;
      goto out;
    }
    if (SlotData == NULL) {
      Status = EFI_LOAD_ERROR;
      Info->BootState = RED;
      goto out;
    }
    BOOLEAN HeaderVersion = GetHeaderVersion (SlotData);
    DEBUG ( (EFI_D_VERBOSE, "Recovery HeaderVersion %d \n", HeaderVersion));
    if (!HeaderVersion) {
       AddRequestedPartition (RequestedPartitionAll, IMG_DTBO);
       NumRequestedPartition += 1;
       if (SlotData != NULL) {
          avb_slot_verify_data_free (SlotData);
       }
       Result = avb_slot_verify (Ops, (CONST CHAR8 *CONST *)RequestedPartition,
                  SlotSuffix, VerifyFlags, VerityFlags, &SlotData);
    }
  } else {
    if (!Info->NumLoadedImages) {
      AddRequestedPartition (RequestedPartitionAll, IMG_BOOT);
      NumRequestedPartition += 1;
    }
    AddRequestedPartition (RequestedPartitionAll, IMG_DTBO);
    NumRequestedPartition += 1;
    if (IsVmEnabled ()) {
      CHAR16 PartiName[MAX_GPT_NAME_SIZE];
      Slot CurrentSlot;

      GUARD (StrnCpyS (PartiName, (UINTN)MAX_GPT_NAME_SIZE,
                        (CONST CHAR16 *)L"vm-linux", StrLen (L"vm-linux")));

      if (Info->MultiSlotBoot) {
        CurrentSlot = GetCurrentSlotSuffix ();
        GUARD (StrnCatS (PartiName, MAX_GPT_NAME_SIZE,
                         CurrentSlot.Suffix, StrLen (CurrentSlot.Suffix)));
      }

      Index = GetPartitionIndex (PartiName);
    }
    if (Index == INVALID_PTN ||
               Index >= MAX_NUM_PARTITIONS) {
      DEBUG ((EFI_D_VERBOSE, "Invalid vm-linux partition\n"));
    } else {
      AddRequestedPartition (RequestedPartitionAll, IMG_VMLINUX);
      NumRequestedPartition += 1;
    }
    Result = avb_slot_verify (Ops, (CONST CHAR8 *CONST *)RequestedPartition,
                SlotSuffix, VerifyFlags, VerityFlags, &SlotData);
  }

可以看到这里有几个地方调用了avb_slot_verify,此使机器的情况是slot_a正常启动,并且支持dynamic_partition,因此走的是else逻辑。并且此时我们的机器设置的是locked。

  if (AllowVerificationError && ResultShouldContinue (Result)) {
    DEBUG ((EFI_D_VERBOSE, "State: Unlocked, AvbSlotVerify returned "
                         "%a, continue boot\n",
            avb_slot_verify_result_to_string (Result)));
  } else if (Result != AVB_SLOT_VERIFY_RESULT_OK) {
    DEBUG ((EFI_D_ERROR, "ERROR: Device State %a, AvbSlotVerify returned %a\n",
            AllowVerificationError ? "Unlocked" : "Locked",
            avb_slot_verify_result_to_string (Result)));
    Status = EFI_LOAD_ERROR;
    Info->BootState = RED;
    goto out;
  }

在Result=avb_slot_verify (Ops, (CONST CHAR8 *CONST *)RequestedPartition,
                SlotSuffix, VerifyFlags, VerityFlags, &SlotData)后,对Result进行判断,此使的Result=AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION.因此执行go out


  switch (AVBVersion) {
  case NO_AVB:
    return LoadImageNoAuthWrapper (Info);
    break;
  case AVB_1:
    Status = LoadImageAndAuthVB1 (Info);
    break;
  case AVB_2:
    Status = LoadImageAndAuthVB2 (Info);
    break;
  case AVB_LE:
    Status = LoadImageAndAuthForLE (Info);
    break;
  default:
    DEBUG ((EFI_D_ERROR, "Unsupported AVB version %d\n", AVBVersion));
    Status = EFI_UNSUPPORTED;
  }

  if (IsUnlocked () && Status != EFI_SUCCESS) {
    DEBUG ((EFI_D_ERROR, "LoadImageAndAuth failed %r\n", Status));
    return Status;
  }

out:
  if (Status != EFI_SUCCESS) {
    if (SlotData != NULL) {
      avb_slot_verify_data_free (SlotData);
    }
    if (Ops != NULL) {
      AvbOpsFree (Ops);
    }
    if (UserData != NULL) {
      avb_free (UserData);
    }
    if (VBData != NULL) {
      avb_free (VBData);
    }
    Info->BootState = RED;
    if (Info->MultiSlotBoot) {
      HandleActiveSlotUnbootable ();
      /* HandleActiveSlotUnbootable should have swapped slots and
       * reboot the device. If no bootable slot found, enter fastboot
       */
      DEBUG ((EFI_D_WARN, "No bootable slots found enter fastboot mode\n"));
    } else {
       DEBUG ((EFI_D_WARN,
           "Non Multi-slot: Unbootable entering fastboot mode\n"));
    }
  }

进入go out,由于当前的系统是没有ab双系统数据同步功能的,因此如果slot_a为异常无法启动的话,那么slot_b也是无法启动的。在这里进行判断结束后可以看到我们串口内的No bootable slots found enter fastboot mode在这里能看到了,最后再看看是哪里调用了LoadImageAndAuth

  if (!BootIntoFastboot) {
    BootInfo Info = {0};
    Info.MultiSlotBoot = MultiSlotBoot;
    Info.BootIntoRecovery = BootIntoRecovery;
    Info.BootReasonAlarm = BootReasonAlarm;
    //avb相关判断入口
    Status = LoadImageAndAuth (&Info);
    if (Status != EFI_SUCCESS) {
      DEBUG ((EFI_D_ERROR, "LoadImageAndAuth failed: %r\n", Status));
      goto fastboot;
    }

    BootLinux (&Info);
  }

在LinuxLoaderEntry内,选择进入recovery、fastboot还是normal模式时会进行各种判断,这里就是其中的一种,avb相关的判断,检测镜像的可靠性。如果ap侧镜像hash值与vbmeta内的hash值不一致,那么就会进入fastboot模式。具体问题具体分析,下一篇我会讲另一种进入fastboot模式的例子。

如果有任何逻辑问题,欢迎大佬指点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值