高通平台下的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功能开发,我们不需要把全部代码都记住,但是必须要了解其中与客制化开发关系较为紧密的部分。下面进行几个重要函数的代码剖析
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;}
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;}
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;}
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;}
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我们需要了解的应该就是如下几点:
编译启动开关:
android/device/qcom/qssi/qssi.mk# Enable AVB 2.0BOARD_AVB_ENABLE := true
对于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
了解当前系统运行的是安全熔丝版本还是非熔丝版本,avb对于非熔丝版本的话,即使校验失败也不会影响系统启动。
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