返回顶部
首页 > 资讯 > 移动开发 >高通 UEFI:ABL(一)
  • 396
分享到

高通 UEFI:ABL(一)

linuxandroidPoweredby金山文档 2023-09-02 07:09:39 396人浏览 独家记忆
摘要

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

高通平台下的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

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;    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:        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));    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:    __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);        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_STATUSGetActiveSlot (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) {            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 UINT8GetRebootReason (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_STATUSRecoveryInit (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);    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模式(后者是假设)    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_STATUSFindBootableSlot (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分区索引    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 ());  }    if (Status == EFI_SUCCESS) {    GUARD_OUT (ValidateSlotGuids (BootableSlot));  }  MarkPtnActive (BootableSlot->Suffix);out:  if (Status != EFI_SUCCESS) {        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.0BOARD_AVB_ENABLE := true
  1. 对于hash值的计算方式:

andrioid/build/core/Makefile# vbmeta imageifeq ($(BOARD_AVB_ENABLE),true)BUILT_VBMETAIMAGE_TARGET := $(PRODUCT_OUT)/vbmeta.imgAVB_CHAIN_KEY_DIR := $(TARGET_OUT_INTERMEDIATES)/avb_chain_keysifdef 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_RSA4096BOARD_AVB_KEY_PATH := external/avb/test/data/testkey_rsa4096.pemendif
  1. 了解当前系统运行的是安全熔丝版本还是非熔丝版本,avb对于非熔丝版本的话,即使校验失败也不会影响系统启动。

  1. FastbootInitialize

最后一个主要功能函数就是FastbootInitialize,主函数内如果存在BootIntoFastboot=TRUE的语句的话,那么就会执行goto fastboot,进入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://blog.csdn.net/weixin_43453149/article/details/129587539

--结束END--

本文标题: 高通 UEFI:ABL(一)

本文链接: https://lsjlt.com/news/390150.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 高通 UEFI:ABL(一)
    高通平台下的UEFI由XBL+ABL组成,主要完成各种客制化的需求实现,例如通过拉特定的gpio进入fastboot/recovery模式,读取ufs寿命,LCD兼容框架的实现等,想要实现客制化首先要搞明白源码种的框架组成,这篇文章先剖析一...
    99+
    2023-09-02
    linux android Powered by 金山文档
  • 高通Android msm8953 驱动开发(一)--LCD调试
    本篇记录Andoird9 高通msm8953 ili9881c LCD调试过程,主要涉及到以下几个知识点:   (一).利用GCDB生成lk 头文件和 kernel dts,点亮屏幕;   (二).LCD兼容; (三).LCD方向调整; ...
    99+
    2023-10-03
    android 驱动开发
  • 通过syntaxhighlight实现帝国cms代码高亮/语法高亮(一)
    一、 在帝国cms中使用该插件: 1.下载syntaxhighlight插件,地址为: http://alexgorbatchev.com/SyntaxHighlighter/download/download.phps...
    99+
    2022-06-12
    帝国cms 代码高亮 语法高亮
  • vue 封装一个高质量的表单通用组件
    目录正文基于Element-plus实现二次封装表单组件步骤1步骤2富文本表单项封装上传表单项封装同行多个表单布局封装步骤3总结正文 我们都知道表单组件应该是后台管理系统中用得最多...
    99+
    2023-03-10
    vue 封装表单通用组件 vue 封装表单
  • vue如何封装一个高质量的表单通用组件
    这篇“vue如何封装一个高质量的表单通用组件”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“vue如何封装一个高质量的表单通用...
    99+
    2023-07-05
  • PHP编程技巧:打造高效多对一通讯录应用
    由于时间有限,我会为你提供一个基于PHP和MySQL的简单多对一通讯录应用的示例代码,并提供一定的解释。希望能够帮助你理解如何构建高效的多对一通讯录应用。 标题:PHP编程技巧:打造高...
    99+
    2024-04-02
  • 高通音频架构(三)
    一、Kernel层 音频由于其特殊的工作,使得它的结构特别的复杂,而且在自己的结构基础上还引入了ALSA架构,不过在android系统上所引入的并非完整的ALSA架构而是精简版的tinyalsa,但是就算精简版也是内容相当丰厚。除此,音频还...
    99+
    2023-08-18
    音视频 android 驱动开发
  • Golang 进程通信:构建高效沟通桥梁
    go中的进程间通信(ipc)通过管道、通道和共享内存实现。管道允许协程通过管道端点写入和读取数据,而通道则保证了发送和接收操作的原子性。共享内存通过让进程访问同一块内存实现快速数据交换,...
    99+
    2024-04-04
    golang 进程通信 并发访问
  • DSDS应用场景(高通5G)
    DSDS应用场景 1、背景 “双卡手机”在中国手机市场占据近90%市场份额,随着软卡、云卡、eSIM的发展,双卡的应用也将更加广泛。 此外,5G面向用户和场景定义三大应用场景,首先是...
    99+
    2024-04-02
  • 通过阿里云服务器上网一种高效便捷的方式
    通过阿里云服务器上网是一种高效便捷的方式,它能够让用户在任何地方、任何时间都可以使用云服务器进行工作和娱乐。本文将详细介绍如何通过阿里云服务器上网。 一、如何通过阿里云服务器上网首先,用户需要在阿里云服务器上安装必要的网络设备和软件,如路由...
    99+
    2023-12-16
    高效 阿里 便捷
  • Python通配符一览
    转自Python之%s%d%f,转载备用,若侵权请联系博主删除 %s string="hello" #%s打印时结果是hello print "string=%s" % string # output: stri...
    99+
    2023-01-31
    通配符 Python
  • mysql通配符(sql 高级过滤)
    目录  首先简单介绍一下通配符,用来匹配值的一部分的特殊字符。 搜索模式(search pattern) 由字面值、通配符或两者组合构成的搜索条件。 通配符是对操作符的一种...
    99+
    2024-04-02
  • js如何实现一个通用的“划词高亮”在线笔记功能
    这篇文章主要介绍了js如何实现一个通用的“划词高亮”在线笔记功能,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1. 什么是“划词高亮”?有些...
    99+
    2024-04-02
  • Android使用Notification实现普通通知栏(一)
    Notification是在你的应用常规界面之外展示的消息。当app让系统发送一个消息的时候,消息首先以图表的形式显示在通知栏。要查看消息的详情需要进入通知抽屉(notific...
    99+
    2022-06-06
    notification Android
  • python写一个通讯录
      闲着没事,用python写一个模拟通讯录,要求要实现常用的通讯录的功能,基本流程如下                          ​   接下来就按照这个流程实现各个模块的功能   1. 定义一个类,并初始化 1 imp...
    99+
    2023-01-31
    通讯录 python
  • Mysql高阶语句 (一)
    一、常用查询 (增、删、改、查) 对 MySQL 数据库的查询,除了基本的查询外,有时候需要对查询的结果集进行处理。 例如只取 10 条数据、对查询结果进行排序或分组等等 按关键字排序 PS:类比于windows 任务管理器 使用 SELE...
    99+
    2023-09-11
    mysql 数据库
  • 如何提高代理IP的连通率
    小编给大家分享一下如何提高代理IP的连通率,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、选购优质代理IP,代理IP质量越高,有效连接率越高。所以为什么尽量避免...
    99+
    2023-06-20
  • 高通处理器CPU性能路线图
    智能手机路线图:新的LTE调制解调器类别:智能手机平台:QCT计算路线图:QRD路线图:射频收发器POR路线图:RFFE路线图和产品组合:综合RFFE产品组合综合RFFE投资组合:全球SKU高级层RFFE:区域手机的RFFE SKU注:只节...
    99+
    2023-06-05
  • 怎么通过Kafka做高并发处理
    要通过Kafka实现高并发处理,可以采取以下步骤:1. 创建Kafka集群:搭建一个Kafka集群,包括多个Kafka Broker...
    99+
    2023-08-12
    Kafka
  • 阿里云普通代理佣金高吗
    阿里云普通代理佣金高吗? 在选择使用阿里云进行云计算服务时,有许多因素需要考虑,其中包括阿里云的价格、可靠性、安全性和可用性等方面。但是,最重要的因素之一是代理佣金。阿里云普通代理佣金相对较高,因此在选择阿里云的时候需要认真考虑,以确保可...
    99+
    2023-10-27
    阿里 佣金
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作