高通 UEFI:ABL(一)

这篇具有很好参考价值的文章主要介绍了高通 UEFI:ABL(一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

高通平台下的UEFI由XBL+ABL组成,主要完成各种客制化的需求实现,例如通过拉特定的gpio进入fastboot/recovery模式,读取ufs寿命,LCD兼容框架的实现等,想要实现客制化首先要搞明白源码种的框架组成,这篇文章先剖析一下abl阶段主要做了什么事情。

要分析abl框架,首先我们需要找到整个框架的入口,根据LinuxLoader.inf内的描述可以得到,abl的入口就在LinuxLoader.c内的LinuxLoaderEntry函数

文件路径:bootable/bootloader/edk2/QcomModulePkg/Application/LinuxLoader/LinuxLoader.inf

[Defines]
        INF_VERSION                    = 0x00010006
        BASE_NAME                      = LinuxLoader
        FILE_GUID                      = f536d559-459f-48fa-8bbc-43b554ecae8d
        MODULE_TYPE                    = UEFI_APPLICATION
        VERSION_STRING                 = 0.1
        ENTRY_POINT                    = LinuxLoaderEntry

[Sources]
        LinuxLoader.c
...

那么就从入口函数LinuxLoaderEntry开始代码分析,分析内容直接附在代码注释中。

文件路径

bootable/bootloader/edk2/QcomModulePkg/Application/LinuxLoader/LinuxLoader.c

/**
  Linux Loader Application EntryPoint

  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
  @param[in] SystemTable    A pointer to the EFI System Table.

  @retval EFI_SUCCESS       The entry point is executed successfully.
  @retval other             Some error occurs when executing this entry point.

 **/

EFI_STATUS EFIAPI  __attribute__ ( (no_sanitize ("safe-stack")))
LinuxLoaderEntry (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
{
  EFI_STATUS Status;

  UINT32 BootReason = NORMAL_MODE;
  UINT32 KeyPressed = SCAN_NULL;
  /* MultiSlot Boot */
  BOOLEAN MultiSlotBoot;

  DEBUG ((EFI_D_INFO, "Loader Build Info: %a %a\n", __DATE__, __TIME__));
  DEBUG ((EFI_D_VERBOSE, "LinuxLoader Load Address to debug ABL: 0x%llx\n",
         (UINTN)LinuxLoaderEntry & (~ (0xFFF))));
  DEBUG ((EFI_D_VERBOSE, "LinuxLoaderEntry Address: 0x%llx\n",
         (UINTN)LinuxLoaderEntry));

  Status = AllocateUnSafeStackPtr ();
  if (Status != EFI_SUCCESS) {
    DEBUG ((EFI_D_ERROR, "Unable to Allocate memory for Unsafe Stack: %r\n",
            Status));
    goto stack_guard_update_default;
  }

  StackGuardChkSetup ();
  
  //获取内核启动地址以及打印时间等
  BootStatsSetTimeStamp (BS_BL_START);

  //获取设备信息,涉及到oem unlock功能等
  Status = DeviceInfoInit ();
  if (Status != EFI_SUCCESS) {
    DEBUG ((EFI_D_ERROR, "Initialize the device info failed: %r\n", Status));
    goto stack_guard_update_default;
  }
  //枚举分区,根据provision文件内分配的lun卷进行枚举
  Status = EnumeratePartitions ();
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "LinuxLoader: Could not enumerate partitions: %r\n",
            Status));
    goto stack_guard_update_default;
  }

  UpdatePartitionEntries ();
  //判断本次启动是从slot_a还是slot_b启动
  MultiSlotBoot = PartitionHasMultiSlot ((CONST CHAR16 *)L"boot");
  if (MultiSlotBoot) {
    DEBUG ((EFI_D_VERBOSE, "Multi Slot boot is supported\n"));
    FindPtnActiveSlot ();
  }
  
  //判断是否此时存在按键事件选择进入不同模式
  Status = GetKeyPress (&KeyPressed);
  if (Status == EFI_SUCCESS) {
    if (KeyPressed == SCAN_DOWN)
      BootIntoFastboot = TRUE;
    if (KeyPressed == SCAN_UP)
      BootIntoRecovery = TRUE;
    if (KeyPressed == SCAN_ESC)
      RebootDevice (EMERGENCY_DLOAD);
  } else if (Status == EFI_DEVICE_ERROR) {
    DEBUG ((EFI_D_ERROR, "Error reading key status: %r\n", Status));
    goto stack_guard_update_default;
  }

  //获取重启原因并根据原因决定设备进入的模式
  Status = GetRebootReason (&BootReason);
  if (Status != EFI_SUCCESS) {
    DEBUG ((EFI_D_ERROR, "Failed to get Reboot reason: %r\n", Status));
    goto stack_guard_update_default;
  }

  switch (BootReason) {
  case FASTBOOT_MODE:
    BootIntoFastboot = TRUE;
    break;
  case RECOVERY_MODE:
    BootIntoRecovery = TRUE;
    break;
  case ALARM_BOOT:
    BootReasonAlarm = TRUE;
    break;
  case DM_VERITY_ENFORCING:
    // write to device info
    Status = EnableEnforcingMode (TRUE);
    if (Status != EFI_SUCCESS)
      goto stack_guard_update_default;
    break;
  case DM_VERITY_LOGGING:
    /* Disable MDTP if it's Enabled through Local Deactivation */
    Status = MdtpDisable ();
    if (EFI_ERROR (Status) && Status != EFI_NOT_FOUND) {
      DEBUG ((EFI_D_ERROR, "MdtpDisable Returned error: %r\n", Status));
      goto stack_guard_update_default;
    }
    // write to device info
    Status = EnableEnforcingMode (FALSE);
    if (Status != EFI_SUCCESS)
      goto stack_guard_update_default;

    break;
  case DM_VERITY_KEYSCLEAR:
    Status = ResetDeviceState ();
    if (Status != EFI_SUCCESS) {
      DEBUG ((EFI_D_ERROR, "VB Reset Device State error: %r\n", Status));
      goto stack_guard_update_default;
    }
    break;
  default:
    if (BootReason != NORMAL_MODE) {
      DEBUG ((EFI_D_ERROR,
             "Boot reason: 0x%x not handled, defaulting to Normal Boot\n",
             BootReason));
    }
    break;
  }
  
  //recovery模式初始化
  Status = RecoveryInit (&BootIntoRecovery);
  if (Status != EFI_SUCCESS)
    DEBUG ((EFI_D_VERBOSE, "RecoveryInit failed ignore: %r\n", Status));

  /* Populate board data required for fastboot, dtb selection and cmd line */
  Status = BoardInit ();
  if (Status != EFI_SUCCESS) {
    DEBUG ((EFI_D_ERROR, "Error finding board information: %r\n", Status));
    return Status;
  }

  DEBUG ((EFI_D_INFO, "KeyPress:%u, BootReason:%u\n", KeyPressed, BootReason));
  DEBUG ((EFI_D_INFO, "Fastboot=%d, Recovery:%d\n",
                                          BootIntoFastboot, BootIntoRecovery));
  if (!GetVmData ()) {
    DEBUG ((EFI_D_ERROR, "VM Hyp calls not present\n"));
  }

  //选择正常启动,开始加载镜像
  if (!BootIntoFastboot) {
    BootInfo Info = {0};
    Info.MultiSlotBoot = MultiSlotBoot;
    Info.BootIntoRecovery = BootIntoRecovery;
    Info.BootReasonAlarm = BootReasonAlarm;
    Status = LoadImageAndAuth (&Info);
    if (Status != EFI_SUCCESS) {
      DEBUG ((EFI_D_ERROR, "LoadImageAndAuth failed: %r\n", Status));
      goto fastboot;
    }

    BootLinux (&Info);
  }

fastboot:
  DEBUG ((EFI_D_INFO, "Launching fastboot\n"));
  Status = FastbootInitialize ();
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "Failed to Launch Fastboot App: %d\n", Status));
    goto stack_guard_update_default;
  }

stack_guard_update_default:
  /*Update stack check guard with defualt value then return*/
  __stack_chk_guard = DEFAULT_STACK_CHK_GUARD;
  return Status;
}

LinuxLoader.c作为整个abl的入口,要完成的事情有点多,对于uefi功能开发,我们不需要把全部代码都记住,但是必须要了解其中与客制化开发关系较为紧密的部分。下面进行几个重要函数的代码剖析

  1. DeviceInfoInit

DeviceInfoInit函数根据使用的DevInfo内部成员就知道,与设备locked功能,verity_mode,user_public_key有关,事实上DeviceInfoInit会去读取devcfg分区内的数据,并且会对unlocked功能进行初始化设定,对于不是专门做于设备安全的朋友来说,了解个大概就好了。

typedef struct device_info {
  CHAR8 magic[DEVICE_MAGIC_SIZE];
  BOOLEAN is_unlocked;
  BOOLEAN is_unlock_critical;
  BOOLEAN is_charger_screen_enabled;
  CHAR8 bootloader_version[MAX_VERSION_LEN];
  CHAR8 radio_version[MAX_VERSION_LEN];
  BOOLEAN verity_mode; // TRUE = enforcing, FALSE = logging
  UINT32 user_public_key_length;
  CHAR8 user_public_key[MAX_USER_KEY_SIZE];
  UINT64 rollback_index[MAX_VB_PARTITIONS];
  struct usb_composition usb_comp;
} DeviceInfo;

EFI_STATUS DeviceInfoInit (VOID)
{
  EFI_STATUS Status = EFI_SUCCESS;

  if (FirstReadDevInfo) {
    Status =
        ReadWriteDeviceInfo (READ_CONFIG, (VOID *)&DevInfo, sizeof (DevInfo));
    if (Status != EFI_SUCCESS) {
      DEBUG ((EFI_D_ERROR, "Unable to Read Device Info: %r\n", Status));
      return Status;
    }

    FirstReadDevInfo = FALSE;
  }

  if (CompareMem (DevInfo.magic, DEVICE_MAGIC, DEVICE_MAGIC_SIZE)) {
    DEBUG ((EFI_D_ERROR, "Device Magic does not match\n"));
    gBS->SetMem (&DevInfo, sizeof (DevInfo), 0);
    gBS->CopyMem (DevInfo.magic, DEVICE_MAGIC, DEVICE_MAGIC_SIZE);
    DevInfo.user_public_key_length = 0;
    gBS->SetMem (DevInfo.rollback_index, sizeof (DevInfo.rollback_index), 0);
    gBS->SetMem (DevInfo.user_public_key, sizeof (DevInfo.user_public_key), 0);
    /*重点,判断设备是否已经开启安全融丝功能,默认设备开启安全熔丝功能的话unlocked功能是无法打开的,
    代表着fastboot模式下无法进行镜像烧录,大部分厂家为了防止非法镜像刷写,都不会去修改这部分*/
    if (IsSecureBootEnabled ()) {
      DevInfo.is_unlocked = FALSE;
      DevInfo.is_unlock_critical = FALSE;
    } else {
      DevInfo.is_unlocked = TRUE;
      DevInfo.is_unlock_critical = TRUE;
    }
    DevInfo.is_charger_screen_enabled = FALSE;
    DevInfo.verity_mode = TRUE;
    Status =
        ReadWriteDeviceInfo (WRITE_CONFIG, (VOID *)&DevInfo, sizeof (DevInfo));
    if (Status != EFI_SUCCESS) {
      DEBUG ((EFI_D_ERROR, "Unable to Write Device Info: %r\n", Status));
      return Status;
    }
  }

  return Status;
}
  1. FindPtnActiveSlot&&GetActiveSlot

FindPtnActiveSlot函数只是设定了一个默认启动slot为0,真正的工作都是放在GetActiveSlot内完成的,GetActiveSlot会查找当前寄存器内哪个slot是active状态,从而选择加载对应的slot镜像。

GetActiveSlot会获取当前系统启动槽(slot)为0,0表示slot_a,1表示slot_b。由于高通soc平台存在的a/b系统的设计,因此abl阶段会判断当前系统会从哪个slot启动。GetActiveSlot会去读取寄

存器内存放slot_a/b的active状态,默认是slot_a启动,正常情况下只有ota后才会设置为slot_b启动。

STATIC EFI_STATUS
GetActiveSlot (Slot *ActiveSlot)
{
  EFI_STATUS Status = EFI_SUCCESS;
  Slot Slots[] = {{L"_a"}, {L"_b"}};
  UINT64 Priority = 0;

  if (ActiveSlot == NULL) {
    DEBUG ((EFI_D_ERROR, "GetActiveSlot: bad parameter\n"));
    return EFI_INVALID_PARAMETER;
  }

  for (UINTN SlotIndex = 0; SlotIndex < ARRAY_SIZE (Slots); SlotIndex++) {
    //这里只需要知道PartitionEntry结构体内的成员EFI_PARTITION_ENTRY为分区入口地址,lun为启动分区对应的lun卷即可
    struct PartitionEntry *BootPartition =
        GetBootPartitionEntry (&Slots[SlotIndex]);
    UINT64 BootPriority = 0;
    if (BootPartition == NULL) {
      DEBUG ((EFI_D_ERROR, "GetActiveSlot: No boot partition "
                           "entry for slot %s\n",
              Slots[SlotIndex].Suffix));
      return EFI_NOT_FOUND;
    }
    //各种寄存器计算
    BootPriority =
        (BootPartition->PartEntry.Attributes & PART_ATT_PRIORITY_VAL) >>
        PART_ATT_PRIORITY_BIT;

    if ((BootPartition->PartEntry.Attributes & PART_ATT_ACTIVE_VAL) &&
        (BootPriority > Priority)) {
      GUARD (StrnCpyS (ActiveSlot->Suffix, ARRAY_SIZE (ActiveSlot->Suffix),
                       Slots[SlotIndex].Suffix,
                       StrLen (Slots[SlotIndex].Suffix)));
      Priority = BootPriority;
    }
  }
  DEBUG ((EFI_D_VERBOSE, "GetActiveSlot: found active slot %s, priority %d\n",
          ActiveSlot->Suffix, Priority));

  if (IsSuffixEmpty (ActiveSlot) == TRUE) {
    /* Check for first boot and set default slot */
    /* For First boot all A/B attributes for the slot would be 0 */
    UINT64 BootPriority = 0;
    UINT64 RetryCount = 0;
    struct PartitionEntry *SlotA = GetBootPartitionEntry (&Slots[0]);
    if (SlotA == NULL) {
      DEBUG ((EFI_D_ERROR, "GetActiveSlot: First Boot: No boot partition "
                           "entry for slot %s\n",
              Slots[0].Suffix));
      return EFI_NOT_FOUND;
    }

    BootPriority = (SlotA->PartEntry.Attributes & PART_ATT_PRIORITY_VAL) >>
                   PART_ATT_PRIORITY_BIT;
    RetryCount = (SlotA->PartEntry.Attributes & PART_ATT_MAX_RETRY_COUNT_VAL) >>
                 PART_ATT_MAX_RETRY_CNT_BIT;

    if ((SlotA->PartEntry.Attributes & PART_ATT_ACTIVE_VAL) == 0 &&
        (SlotA->PartEntry.Attributes & PART_ATT_SUCCESSFUL_VAL) == 0 &&
        (SlotA->PartEntry.Attributes & PART_ATT_UNBOOTABLE_VAL) == 0 &&
        BootPriority == 0) {

      DEBUG ((EFI_D_INFO, "GetActiveSlot: First boot: set "
                          "default slot _a\n"));
      SlotA->PartEntry.Attributes &=
          (~PART_ATT_SUCCESSFUL_VAL & ~PART_ATT_UNBOOTABLE_VAL);
      SlotA->PartEntry.Attributes |=
          (PART_ATT_PRIORITY_VAL | PART_ATT_ACTIVE_VAL |
           PART_ATT_MAX_RETRY_COUNT_VAL);

      GUARD (StrnCpyS (ActiveSlot->Suffix, ARRAY_SIZE (ActiveSlot->Suffix),
                       Slots[0].Suffix, StrLen (Slots[0].Suffix)));
      UpdatePartitionAttributes (PARTITION_ATTRIBUTES);
      FirstBoot = TRUE;
      return EFI_SUCCESS;
    }

    DEBUG ((EFI_D_ERROR, "GetActiveSlot: No active slot found\n"));
    DEBUG ((EFI_D_ERROR, "GetActiveSlot: Slot attr: Priority %ld, Retry "
                         "%ld, Active %ld, Success %ld, unboot %ld\n",
            BootPriority, RetryCount,
            (SlotA->PartEntry.Attributes & PART_ATT_ACTIVE_VAL) >>
                PART_ATT_ACTIVE_BIT,
            (SlotA->PartEntry.Attributes & PART_ATT_SUCCESSFUL_VAL),
            (SlotA->PartEntry.Attributes & PART_ATT_UNBOOTABLE_VAL)));

    return EFI_NOT_FOUND;
  }

  return EFI_SUCCESS;
}
  1. GetRebootReason

GetRebootReason根据函数名称就能猜到,是获取本次重启的原因,并且会对读取重启原因变量BootReason进行判断,如果是进入fastboot或者recovery的话那么就会将对应的属性值设置为true,

这里我们只需要知道:如果进行客制化需求实现,例如判断reboot reason从而执行某些操作,可以利用GetRebootReason (&BootReason)这个函数即可。

STATIC UINT8
GetRebootReason (UINT32 *ResetReason)
{
  EFI_RESETREASON_PROTOCOL *RstReasonIf;
  EFI_STATUS Status;

  Status = gBS->LocateProtocol (&gEfiResetReasonProtocolGuid, NULL,
                                (VOID **)&RstReasonIf);
  if (Status != EFI_SUCCESS) {
    DEBUG ((EFI_D_ERROR, "Error locating the reset reason protocol\n"));
    return Status;
  }

  RstReasonIf->GetResetReason (RstReasonIf, ResetReason, NULL, NULL);
  if (RstReasonIf->Revision >= EFI_RESETREASON_PROTOCOL_REVISION)
    RstReasonIf->ClearResetReason (RstReasonIf);
  return Status;
}
  1. RecoveryInit

RecoveryInit 会对misc分区内的数据进行解析,如果解析到的misc分区字段存在boot-recovery的话,会将BootIntoRecovery标志设置为TRUE,在LoadImageAndAuth内会对这个标志进行判断。

struct RecoveryMessage {
  CHAR8 command[32];
  CHAR8 status[32];
  CHAR8 recovery[1024];
};


EFI_STATUS
RecoveryInit (BOOLEAN *BootIntoRecovery)
{
  EFI_STATUS Status;
  struct RecoveryMessage *Msg = NULL;
  //misc分区的guid地址
  EFI_GUID Ptype = gEfiMiscPartitionGuid;
  MemCardType CardType = UNKNOWN;
  VOID *PartitionData = NULL;
  UINT32 PageSize;

  CardType = CheckRootDeviceType ();
  if (CardType == NAND) {
    Status = GetNandMiscPartiGuid (&Ptype);
    if (Status != EFI_SUCCESS) {
      return Status;
    }
  }

  GetPageSize (&PageSize);

  /* Get the first 2 pages of the misc partition.
   * If the device type is NAND then read the recovery message from page 1,
   * Else read from the page 0
   */
  Status = ReadFromPartition (&Ptype, (VOID **)&PartitionData, (PageSize * 2));
  if (Status != EFI_SUCCESS) {
    DEBUG ((EFI_D_ERROR, "Error Reading from misc partition: %r\n", Status));
    return Status;
  }

  if (!PartitionData) {
    DEBUG ((EFI_D_ERROR, "Error in loading Data from misc partition\n"));
    return EFI_INVALID_PARAMETER;
  }

  Msg = (CardType == NAND) ?
           (struct RecoveryMessage *) ((CHAR8 *) PartitionData + PageSize) :
           (struct RecoveryMessage *) PartitionData;
  // Ensure NULL termination
  Msg->command[sizeof (Msg->command) - 1] = '\0';
  if (Msg->command[0] != 0 && Msg->command[0] != 255)
    DEBUG ((EFI_D_VERBOSE, "Recovery command: %d %a\n", sizeof (Msg->command),
            Msg->command));
  
  //判断msg内的command属性值,如果为boot-recovery的话,那么BootIntoRecovery为true
  if (!AsciiStrnCmp (Msg->command, RECOVERY_BOOT_RECOVERY,
                       AsciiStrLen (RECOVERY_BOOT_RECOVERY))) {
    *BootIntoRecovery = TRUE;
  }
  
  //判断设备是否打开了动态分区,并且判断misc分区内的command是否为boot-fastboot,是的话则设定为进入recovery模式,而后在进入fastboot模式(后者是假设)
  /* Boot recovery partition to start userspace fastboot */
  if ( IsDynamicPartitionSupport () &&
       !AsciiStrnCmp (Msg->command, RECOVERY_BOOT_FASTBOOT,
                          AsciiStrLen (RECOVERY_BOOT_FASTBOOT))) {
    *BootIntoRecovery = TRUE;
  }

  FreePool (PartitionData);
  PartitionData = NULL;
  Msg = NULL;

  return Status;
}
  1. LoadImageAndAuth

LoadImageAndAuth 会传入一个BootInfo类型的变量&Info,Info内的MultiSlotBoot、BootIntoRecovery以及BootReasonAlarm,并且会查找可启动slot、进行avb校验等。由于代码量大,因此选择对每个函数进行截取单独分析

5.1 FindBootableSlot

FindBootableSlot针对可启动slot进行各种寄存器值的判断,以及通过设定一个retry count来统计slot启动次数,厂商可以通过判断retry count来进行功能添加,如重启超过多少次则判定为slot无法起订,另外添加切换slot功能,让系统继续尝试重启等。

EFI_STATUS
FindBootableSlot (Slot *BootableSlot)
{
  EFI_STATUS Status = EFI_SUCCESS;
  struct PartitionEntry *BootEntry = NULL;
  UINT64 Unbootable = 0;
  UINT64 BootSuccess = 0;
  UINT64 RetryCount = 0;

  if (BootableSlot == NULL) {
    DEBUG ((EFI_D_ERROR, "FindBootableSlot: input parameter invalid\n"));
    return EFI_INVALID_PARAMETER;
  }
  //获取当前被激活的slot,默认为a
  GUARD (GetActiveSlot (BootableSlot));
  //根据GetActiveSlot返回的激活slot,去寻找对应的boot分区索引
  /* Validate Active Slot is bootable */
  BootEntry = GetBootPartitionEntry (BootableSlot);
  if (BootEntry == NULL) {
    DEBUG ((EFI_D_ERROR, "FindBootableSlot: No boot partition entry "
                         "for slot %s\n",
            BootableSlot->Suffix));
    return EFI_NOT_FOUND;
  }
  //gpt分区内的寄存器值获取
  Unbootable = (BootEntry->PartEntry.Attributes & PART_ATT_UNBOOTABLE_VAL) >>
               PART_ATT_UNBOOTABLE_BIT;
  BootSuccess = (BootEntry->PartEntry.Attributes & PART_ATT_SUCCESSFUL_VAL) >>
                PART_ATT_SUCCESS_BIT;
  RetryCount =
      (BootEntry->PartEntry.Attributes & PART_ATT_MAX_RETRY_COUNT_VAL) >>
      PART_ATT_MAX_RETRY_CNT_BIT;
  //如果当前slot之前没有被设置过unbootable标志,并且已经成功启动过了。那么就不需要做后续判断
  if (Unbootable == 0 && BootSuccess == 1) {
    DEBUG (
        (EFI_D_VERBOSE, "Active Slot %s is bootable\n", BootableSlot->Suffix));
  } else if (Unbootable == 0 && BootSuccess == 0 && RetryCount > 0) {
    //判断是否打开了ab分区计数切换宏AB_RETRYCOUNT_DISABLE,有些厂商会在这里进行系统异常后自行切换slot的功能添加
    if ((!IsABRetryCountDisabled () &&
        !IsBootDevImage ()) &&
      IsABRetryCountUpdateRequired ()) {
      RetryCount--;
      BootEntry->PartEntry.Attributes &= ~PART_ATT_MAX_RETRY_COUNT_VAL;
      BootEntry->PartEntry.Attributes |= RetryCount
                                         << PART_ATT_MAX_RETRY_CNT_BIT;
      UpdatePartitionAttributes (PARTITION_ATTRIBUTES);
      DEBUG ((EFI_D_INFO, "Active Slot %s is bootable, retry count %ld\n",
              BootableSlot->Suffix, RetryCount));
    } else {
      DEBUG ((EFI_D_INFO, "A/B retry count NOT decremented\n"));
    }
  } else {
    DEBUG ((EFI_D_INFO, "Slot %s is unbootable, trying alternate slot\n",
            BootableSlot->Suffix));
    //当前slot尝试重启次数已经超过了设定的retry count,将当前slot设置为unbootable
    GUARD_OUT (HandleActiveSlotUnbootable ());
  }

  /* Validate slot suffix and partition guids */
  if (Status == EFI_SUCCESS) {
    GUARD_OUT (ValidateSlotGuids (BootableSlot));
  }
  MarkPtnActive (BootableSlot->Suffix);
out:
  if (Status != EFI_SUCCESS) {
    /* clear bootable slot */
    BootableSlot->Suffix[0] = '\0';
  }
  return Status;
}
     

5.2 LoadImageAndAuthVB2

LoadImageAndAuthxxx,这个xxx主要取决于GetAVBVersion返回的结果,通过switch函数判断当前系统应该进行那种类型的avb校验,当前我用的是android 10,默认为avb2,那么就进入LoadImageAndAuthVB2。由于avb部分不是专门做系统安全的朋友一般不会接触,因此这里我们简单介绍一下android的avb即可。

android avb分为两个阶段:
1.bootloader阶段:bootloader阶段会对vbmeta、vbmeta_system、boot、dtbo等镜像进行安全性校验,其中vbmeta、vbmeta_system内,这部分是在镜像编译的时候,编译脚本会将待校验的分区的hash值写到分区内,同时也会写到vbmeta分区内,在avb校验的时候vbmeta会根据记录的hash值与待校验分区的进行比较,如果不一致那么就会报错。
2.init阶段:init阶段会对vendor、system、product(实际上就是super分区)进行校验,也可以认为就是hash值。原理应该同bootloader阶段的一样,如果在init阶段校验失败的话,内核会出现dm-verity failed的打印

对于avb我们需要了解的应该就是如下几点:

  1. 编译启动开关:

android/device/qcom/qssi/qssi.mk
# Enable AVB 2.0
BOARD_AVB_ENABLE := true
  1. 对于hash值的计算方式:

andrioid/build/core/Makefile
# vbmeta image
ifeq ($(BOARD_AVB_ENABLE),true)

BUILT_VBMETAIMAGE_TARGET := $(PRODUCT_OUT)/vbmeta.img
AVB_CHAIN_KEY_DIR := $(TARGET_OUT_INTERMEDIATES)/avb_chain_keys

ifdef BOARD_AVB_KEY_PATH
$(if $(BOARD_AVB_ALGORITHM),,$(error BOARD_AVB_ALGORITHM is not defined))
else
# If key path isn't specified, use the 4096-bit test key.
BOARD_AVB_ALGORITHM := SHA256_RSA4096
BOARD_AVB_KEY_PATH := external/avb/test/data/testkey_rsa4096.pem
endif
  1. 了解当前系统运行的是安全熔丝版本还是非熔丝版本,avb对于非熔丝版本的话,即使校验失败也不会影响系统启动。

  1. FastbootInitialize

最后一个主要功能函数就是FastbootInitialize,主函数内如果存在BootIntoFastboot=TRUE的语句的话,那么就会执行goto fastboot,进入fastboot的初始化。

/* Initialize and start fastboot */
EFI_STATUS FastbootInitialize (VOID)
{
  EFI_STATUS Status = EFI_SUCCESS;

  DEBUG ((EFI_D_INFO, "Fastboot Build Info: %a %a\n", __DATE__, __TIME__));
  BootStatsSetTimeStamp (BS_BL_START);

  //枚举usb设备   
  Status = FastbootUsbDeviceStart ();
  if (Status != EFI_SUCCESS) {
    DEBUG ((EFI_D_ERROR, "couldnt Start fastboot usb device, exiting"));
    return Status;
  }
  //屏幕显示fastboot菜单
  DisplayFastbootMenu ();

  //等待usb事件响应,进入fastboot时,需要通过usb进行指令发送,直到我们发送fastboot reboot这个指令,才会退出fastboot模式
  while (1) {
    Status = HandleUsbEvents ();
    if (EFI_ERROR (Status) && (Status != EFI_ABORTED)) {
      DEBUG ((EFI_D_ERROR, "Error, failed to handle USB event\n"));
      break;
    }

    if (FastbootFatal ()) {
      DEBUG ((EFI_D_ERROR, "Continue detected, Exiting App...\n"));
      break;
    }
  }

  //关闭usb时间,退出fastboot模式
  Status = FastbootCmdsUnInit ();
  if (Status != EFI_SUCCESS) {
    DEBUG ((EFI_D_ERROR, "couldnt uninit fastboot\n"));
    return Status;
  }

  ExitMenuKeysDetection ();

  Status = FastbootUsbDeviceStop ();
  return Status;
}

这篇的文章目的就是简单的介绍一下bootloader内的各个重要api,对于里面的一些框架本人还不是特别熟,例如avb解析逻辑、fastboot模式的usb枚举、事件上报等,另外还有一些重要的例如cmdline的构成,如何通过cmdline完成内核驱动的选择性加载,这些后续会更新在abl第二篇文章内。

如有不对,欢迎指出,谢谢文章来源地址https://www.toymoban.com/news/detail-483886.html

到了这里,关于高通 UEFI:ABL(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 半导体自动化专用静电消除器主要由哪些部分组成

    半导体自动化专用静电消除器是一种用于消除半导体生产过程中的静电问题的设备。由于半导体制造过程中对静电的敏感性,静电可能会对半导体器件的质量和可靠性产生很大的影响,甚至造成元件损坏。因此,半导体生产中采用专用的静电消除器是非常重要的。 半导体自动

    2024年02月11日
    浏览(45)
  • Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?

    目录 一、@SpringBootApplication 二、@SpringBootConfiguration 三、@EnableAutoConfiguration 四、@ComponentScan   @SpringBootApplication是Spring Boot框架的核心注解之一,它用于标识一个主配置类,通常是项目的入口类。该注解可以将Spring Boot应用程序自动配置和启动的相关注解整合在一起,简化了Sp

    2024年02月13日
    浏览(41)
  • 计算机组成原理中各种字长的概念

    字长:默认是机器字长 1 机器字长:计算机能直接处理的二进制数据位数 存储字长:每个存储单元能存放的二进制数据位数 指令字长:每条指令包含的二进制数据位数 各种硬件的位数与它要做的工作相关,总结如下: 硬件 位数 ALU 机器字长 通用寄存器 机器字长 IR 指令字长

    2024年02月06日
    浏览(43)
  • Springboot使用JustAuth完成各种第三方登陆

    目录 使用Gitee进行登陆 1.Gitee准备工作 2. 编码 3.建立数据表          4. 自由发挥 示例: 进入gitee,在设置中选择此选项   依赖   配置文件  接口编写 如果有spring security的话,还要打开这两个接口的访问权限为所有人都可以访问。 没有的可忽略 启动测试 访问: 同意授权

    2024年02月13日
    浏览(41)
  • 【大数据开发 Spark】第一篇:Spark 简介、Spark 的核心组成(5大模块)、Spark 的主要特征(4大特征)、Spark 对比 MapReduce

    初步了解一项技术,最好的方式就是去它的官网首页,一般首页都会有十分官方且准确的介绍,学习 Spark 也不例外, 官方介绍:Apache Spark ™是一种多语言引擎,用于在单节点机器或集群上执行数据工程、数据科学和机器学习。 我们可以得知,Spark 可以单节点运行,也可以搭

    2024年02月05日
    浏览(48)
  • shell_68.Linux在非控制台下运行脚本

    在非控制台下运行脚本 有时候,即便退出了终端会话,你也想在终端会话中启动 shell 脚本,让脚本一直以后台模式运行到结束。 这可以用 nohup 命令来实现。nohup 命令能阻断发给特定进程的 SIGHUP 信号。当退出终端会话时,这可以避免进程退出。 nohup 命令的格式如下: 下面

    2024年02月05日
    浏览(61)
  • 【设计模式】使用原型模式完成业务中“各种O”的转换

    原型模式是一种非常简单易懂的模型,在书上的定义是这样的: Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype. 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 通俗的讲,就是有一个现成的对象,我们通过复

    2024年02月07日
    浏览(80)
  • C++完成目录下文件夹和各种类型文件的超时删除

    最近的一个项目遇到了要删除过时的一些自己这边生成的文件 思路就是先找到文件(包括文件夹),然后判断他们的时间来确定是否删除文件(文件夹)。 1.CFileFind 类 执行本地文件搜索,并且是执行 Internet 文件搜索的 CGopherFileFind 和 CFtpFileFind 的基类。 语法: 能使用到的公

    2024年02月07日
    浏览(45)
  • CSS奇思妙想之-利用CSS裁剪(clip-path)完成各种图形

    在日常开发当中,如果想要开发多边形,一般都需要多个盒子或者伪元素的帮助,有没有一直办法能只使用一个盒子实现呢? 有的: css裁剪 clip-path介绍 css裁剪(clip-path)这个属性平时率非常低。但是其实clip并不是CSS3的新属性,很早就开始出现了。CSS裁剪的这一概念最早是

    2024年02月05日
    浏览(41)
  • 线性代数的学习和整理10:各种特殊类型的矩阵(草稿-----未完成 建设ing)

    目录 1 图形化分类 1.1对称矩阵 1.2 梯形矩阵 1.3 三角矩阵 1.3.1 上三角矩阵 1.4  对角线矩阵 2 按各自功能分 2.1 等价矩阵 2.2 增广矩阵 2.3 伴随矩阵 2.4 正交矩阵 2.5 正交矩阵 2.6 相似矩阵 1.3.1 上三角矩阵 1.3.2 下三角 1.3.3 反向的不是三角矩阵 1.4.1  1.4.2 这里面包含,单位矩阵I  

    2024年02月11日
    浏览(89)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包