高通 UEFI:ABL(一)
高通平台下的UEFI由XBL+ABL组成,主要完成各种客制化的需求实现,例如通过拉特定的gpio进入fastboot/recovery模式,读取ufs寿命,LCD兼容框架的实现等,想要实现客制化首先要搞明白源码种的框架组成,这篇文章先剖析一下abl阶段主要做了什么事情。要分析abl框架,首先我们需要找到整个框架的入口,根据LinuxLoader.inf内的描述可以得到,abl的入口就在LinuxL
高通平台下的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功能开发,我们不需要把全部代码都记住,但是必须要了解其中与客制化开发关系较为紧密的部分。下面进行几个重要函数的代码剖析
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;
}
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;
}
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;
}
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;
}
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我们需要了解的应该就是如下几点:
编译启动开关:
android/device/qcom/qssi/qssi.mk
# Enable AVB 2.0
BOARD_AVB_ENABLE := true
对于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
了解当前系统运行的是安全熔丝版本还是非熔丝版本,avb对于非熔丝版本的话,即使校验失败也不会影响系统启动。
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第二篇文章内。
如有不对,欢迎指出,谢谢
更多推荐
所有评论(0)