| /* |
| * Copyright (c) 2009, Google Inc. |
| * All rights reserved. |
| * |
| * Copyright (c) 2009-2020, The Linux Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * * Neither the name of The Linux Foundation nor |
| * the names of its contributors may be used to endorse or promote |
| * products derived from this software without specific prior written |
| * permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| */ |
| |
| #include <Library/DeviceInfo.h> |
| #include <Library/DrawUI.h> |
| #include <Library/PartitionTableUpdate.h> |
| #include <Library/ShutdownServices.h> |
| #include <Library/VerifiedBootMenu.h> |
| #include <Library/HypervisorMvCalls.h> |
| #include <Protocol/EFIMdtp.h> |
| #include <Protocol/EFIScmModeSwitch.h> |
| #include <libufdt_sysdeps.h> |
| |
| #include "AutoGen.h" |
| #include "BootImage.h" |
| #include "BootLinux.h" |
| #include "BootStats.h" |
| #include "UpdateDeviceTree.h" |
| #include "libfdt.h" |
| #include <ufdt_overlay.h> |
| |
| STATIC QCOM_SCM_MODE_SWITCH_PROTOCOL *pQcomScmModeSwitchProtocol = NULL; |
| STATIC BOOLEAN BootDevImage; |
| |
| /* To set load addresses, callers should make sure to initialize the |
| * BootParamlistPtr before calling this function */ |
| UINT64 SetandGetLoadAddr (BootParamlist *BootParamlistPtr, AddrType Type) |
| { |
| STATIC UINT64 KernelLoadAddr; |
| STATIC UINT64 RamdiskLoadAddr; |
| |
| if (BootParamlistPtr) { |
| KernelLoadAddr = BootParamlistPtr->KernelLoadAddr; |
| RamdiskLoadAddr = BootParamlistPtr->RamdiskLoadAddr; |
| } else { |
| switch (Type) { |
| case LOAD_ADDR_KERNEL: |
| return KernelLoadAddr; |
| break; |
| case LOAD_ADDR_RAMDISK: |
| return RamdiskLoadAddr; |
| break; |
| default: |
| DEBUG ((EFI_D_ERROR, "Invalid Type to GetLoadAddr():%d\n", |
| Type)); |
| break; |
| } |
| } |
| |
| return 0; |
| } |
| |
| STATIC BOOLEAN |
| QueryBootParams (UINT64 *KernelLoadAddr, UINT64 *KernelSizeReserved) |
| { |
| EFI_STATUS Status; |
| EFI_STATUS SizeStatus; |
| UINTN DataSize = 0; |
| |
| DataSize = sizeof (*KernelLoadAddr); |
| Status = gRT->GetVariable ((CHAR16 *)L"KernelBaseAddr", &gQcomTokenSpaceGuid, |
| NULL, &DataSize, KernelLoadAddr); |
| |
| DataSize = sizeof (*KernelSizeReserved); |
| SizeStatus = gRT->GetVariable ((CHAR16 *)L"KernelSize", &gQcomTokenSpaceGuid, |
| NULL, &DataSize, KernelSizeReserved); |
| |
| return (Status == EFI_SUCCESS && |
| SizeStatus == EFI_SUCCESS); |
| } |
| |
| STATIC EFI_STATUS |
| UpdateBootParams (BootParamlist *BootParamlistPtr) |
| { |
| UINT64 KernelSizeReserved; |
| UINT64 KernelLoadAddr; |
| |
| if (BootParamlistPtr == NULL ) { |
| DEBUG ((EFI_D_ERROR, "Invalid input parameters\n")); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| /* The three regions Kernel, Ramdisk and DT should be reserved in memory map |
| * Query the kernel load address and size from UEFI core, if it's not |
| * successful use the predefined load addresses */ |
| if (QueryBootParams (&KernelLoadAddr, &KernelSizeReserved)) { |
| BootParamlistPtr->KernelLoadAddr = KernelLoadAddr; |
| if (BootParamlistPtr->BootingWith32BitKernel) { |
| BootParamlistPtr->KernelLoadAddr += KERNEL_32BIT_LOAD_OFFSET; |
| } else { |
| BootParamlistPtr->KernelLoadAddr += KERNEL_64BIT_LOAD_OFFSET; |
| } |
| |
| BootParamlistPtr->KernelEndAddr = KernelLoadAddr + KernelSizeReserved; |
| } else { |
| DEBUG ((EFI_D_VERBOSE, "QueryBootParams Failed: ")); |
| /* If Query of boot params fails, RamdiskEndAddress is end of the |
| kernel buffer we have. Using same as size of total available buffer, |
| for relocation of kernel */ |
| |
| if (BootParamlistPtr->BootingWith32BitKernel) { |
| /* For 32-bit Not all memory is accessible as defined by |
| RamdiskEndAddress. Using pre-defined offset for backward |
| compatability */ |
| BootParamlistPtr->KernelLoadAddr = |
| (EFI_PHYSICAL_ADDRESS) (BootParamlistPtr->BaseMemory | |
| PcdGet32 (KernelLoadAddress32)); |
| KernelSizeReserved = PcdGet32 (RamdiskEndAddress32); |
| } else { |
| BootParamlistPtr->KernelLoadAddr = |
| (EFI_PHYSICAL_ADDRESS) (BootParamlistPtr->BaseMemory | |
| PcdGet32 (KernelLoadAddress)); |
| KernelSizeReserved = PcdGet32 (RamdiskEndAddress); |
| } |
| |
| BootParamlistPtr->KernelEndAddr = BootParamlistPtr->BaseMemory + |
| KernelSizeReserved; |
| DEBUG ((EFI_D_VERBOSE, "calculating dynamic offsets\n")); |
| } |
| |
| /* Allocate buffer for ramdisk and tags area, based on ramdisk actual size |
| and DT maximum supported size. This allows best possible utilization |
| of buffer for kernel relocation and take care of dynamic change in size |
| of ramdisk. Add pagesize as a buffer space */ |
| BootParamlistPtr->RamdiskLoadAddr = (BootParamlistPtr->KernelEndAddr - |
| (LOCAL_ROUND_TO_PAGE ( |
| BootParamlistPtr->RamdiskSize + |
| BootParamlistPtr->VendorRamdiskSize, |
| BootParamlistPtr->PageSize) + |
| BootParamlistPtr->PageSize)); |
| BootParamlistPtr->DeviceTreeLoadAddr = (BootParamlistPtr->RamdiskLoadAddr - |
| (DT_SIZE_2MB + |
| BootParamlistPtr->PageSize)); |
| |
| if (BootParamlistPtr->DeviceTreeLoadAddr <= |
| BootParamlistPtr->KernelLoadAddr) { |
| DEBUG ((EFI_D_ERROR, "Not Enough space left to load kernel image\n")); |
| return EFI_BUFFER_TOO_SMALL; |
| } |
| |
| return EFI_SUCCESS; |
| } |
| |
| STATIC EFI_STATUS |
| SwitchTo32bitModeBooting (UINT64 KernelLoadAddr, UINT64 DeviceTreeLoadAddr) |
| { |
| EFI_STATUS Status; |
| EFI_HLOS_BOOT_ARGS HlosBootArgs; |
| |
| SetMem ((VOID *)&HlosBootArgs, sizeof (HlosBootArgs), 0); |
| HlosBootArgs.el1_x2 = DeviceTreeLoadAddr; |
| /* Write 0 into el1_x4 to switch to 32bit mode */ |
| HlosBootArgs.el1_x4 = 0; |
| HlosBootArgs.el1_elr = KernelLoadAddr; |
| Status = pQcomScmModeSwitchProtocol->SwitchTo32bitMode (HlosBootArgs); |
| if (EFI_ERROR (Status)) { |
| DEBUG ((EFI_D_ERROR, "ERROR: Failed to switch to 32 bit mode.Status= %r\n", |
| Status)); |
| return Status; |
| } |
| /*Return Unsupported if the execution ever reaches here*/ |
| return EFI_NOT_STARTED; |
| } |
| |
| STATIC EFI_STATUS |
| UpdateKernelModeAndPkg (BootParamlist *BootParamlistPtr) |
| { |
| Kernel64Hdr *Kptr = NULL; |
| |
| if (BootParamlistPtr == NULL ) { |
| DEBUG ((EFI_D_ERROR, "Invalid input parameters\n")); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| BootParamlistPtr->BootingWith32BitKernel = FALSE; |
| Kptr = (Kernel64Hdr *) (BootParamlistPtr->ImageBuffer + |
| BootParamlistPtr->PageSize); |
| |
| if (is_gzip_package ((BootParamlistPtr->ImageBuffer + |
| BootParamlistPtr->PageSize), BootParamlistPtr->KernelSize)) { |
| BootParamlistPtr->BootingWithGzipPkgKernel = TRUE; |
| } |
| else { |
| if (!AsciiStrnCmp ((CHAR8 *) Kptr, PATCHED_KERNEL_MAGIC, |
| sizeof (PATCHED_KERNEL_MAGIC) - 1)) { |
| BootParamlistPtr->BootingWithPatchedKernel = TRUE; |
| Kptr = (struct kernel64_hdr *)((VOID *)Kptr + |
| PATCHED_KERNEL_HEADER_SIZE); |
| } |
| |
| if (Kptr->magic_64 != KERNEL64_HDR_MAGIC) { |
| BootParamlistPtr->BootingWith32BitKernel = TRUE; |
| } |
| } |
| |
| return EFI_SUCCESS; |
| } |
| |
| STATIC EFI_STATUS |
| CheckMDTPStatus (CHAR16 *PartitionName, BootInfo *Info) |
| { |
| EFI_STATUS Status = EFI_SUCCESS; |
| BOOLEAN MdtpActive = FALSE; |
| CHAR8 StrPartition[MAX_GPT_NAME_SIZE]; |
| CHAR8 PartitionNameAscii[MAX_GPT_NAME_SIZE]; |
| UINT32 PartitionNameLen; |
| QCOM_MDTP_PROTOCOL *MdtpProtocol; |
| MDTP_VB_EXTERNAL_PARTITION ExternalPartition; |
| |
| SetMem ((VOID *)StrPartition, MAX_GPT_NAME_SIZE, 0); |
| SetMem ((VOID *)PartitionNameAscii, MAX_GPT_NAME_SIZE, 0); |
| |
| if (FixedPcdGetBool (EnableMdtpSupport)) { |
| Status = IsMdtpActive (&MdtpActive); |
| if (EFI_ERROR (Status)) { |
| DEBUG ((EFI_D_ERROR, "Failed to get activation state for MDTP, " |
| "Status=%r. Considering MDTP as active and " |
| "continuing \n", |
| Status)); |
| |
| if (Status != EFI_NOT_FOUND) { |
| MdtpActive = TRUE; |
| } |
| } |
| |
| if (MdtpActive) { |
| /* If MDTP is Active and Dm-Verity Mode is not Enforcing, Block */ |
| if (!IsEnforcing ()) { |
| DEBUG ((EFI_D_ERROR, |
| "ERROR: MDTP is active and verity mode is not enforcing \n")); |
| return EFI_NOT_STARTED; |
| } |
| /* If MDTP is Active and Device is in unlocked State, Block */ |
| if (IsUnlocked ()) { |
| DEBUG ((EFI_D_ERROR, |
| "ERROR: MDTP is active and DEVICE is unlocked \n")); |
| return EFI_NOT_STARTED; |
| } |
| } |
| } |
| |
| UnicodeStrToAsciiStr (PartitionName, PartitionNameAscii); |
| PartitionNameLen = AsciiStrLen (PartitionNameAscii); |
| if (Info->MultiSlotBoot) |
| PartitionNameLen -= (MAX_SLOT_SUFFIX_SZ - 1); |
| AsciiStrnCpyS (StrPartition, MAX_GPT_NAME_SIZE, "/", AsciiStrLen ("/")); |
| AsciiStrnCatS (StrPartition, MAX_GPT_NAME_SIZE, PartitionNameAscii, |
| PartitionNameLen); |
| |
| if (FixedPcdGetBool (EnableMdtpSupport)) { |
| Status = gBS->LocateProtocol (&gQcomMdtpProtocolGuid, NULL, |
| (VOID **)&MdtpProtocol); |
| |
| if (Status != EFI_NOT_FOUND) { |
| if (EFI_ERROR (Status)) { |
| DEBUG ((EFI_D_ERROR, "Failed in locating MDTP protocol, Status=%r\n", |
| Status)); |
| return Status; |
| } |
| |
| AsciiStrnCpyS (ExternalPartition.PartitionName, MAX_PARTITION_NAME_LEN, |
| StrPartition, AsciiStrLen (StrPartition)); |
| Status = MdtpProtocol->MdtpBootState (MdtpProtocol, &ExternalPartition); |
| |
| if (EFI_ERROR (Status)) { |
| /* MdtpVerify should always handle errors internally, so when returned |
| * back to the caller, |
| * the return value is expected to be success only. |
| * Therfore, we don't expect any error status here. */ |
| DEBUG ((EFI_D_ERROR, "MDTP verification failed, Status=%r\n", Status)); |
| return Status; |
| } |
| } |
| |
| else |
| DEBUG ( |
| (EFI_D_ERROR, "Failed to locate MDTP protocol, Status=%r\n", Status)); |
| } |
| |
| return Status; |
| } |
| |
| STATIC EFI_STATUS |
| ApplyOverlay (BootParamlist *BootParamlistPtr, |
| VOID *AppendedDtHdr, |
| struct fdt_entry_node *DtsList) |
| { |
| VOID *FinalDtbHdr = AppendedDtHdr; |
| VOID *TmpDtbHdr = NULL; |
| UINT64 ApplyDTStartTime = GetTimerCountms (); |
| |
| if (BootParamlistPtr == NULL || |
| AppendedDtHdr == NULL) { |
| DEBUG ((EFI_D_ERROR, "ApplyOverlay: Invalid input parameters\n")); |
| return EFI_INVALID_PARAMETER; |
| } |
| if (DtsList == NULL) { |
| DEBUG ((EFI_D_VERBOSE, "ApplyOverlay: Overlay DT is NULL\n")); |
| goto out; |
| } |
| |
| if (!pre_overlay_malloc ()) { |
| DEBUG ((EFI_D_ERROR, |
| "ApplyOverlay: Unable to Allocate Pre Buffer for Overlay\n")); |
| return EFI_OUT_OF_RESOURCES; |
| } |
| |
| TmpDtbHdr = ufdt_install_blob (AppendedDtHdr, fdt_totalsize (AppendedDtHdr)); |
| if (!TmpDtbHdr) { |
| DEBUG ((EFI_D_ERROR, "ApplyOverlay: Install blob failed\n")); |
| return EFI_NOT_FOUND; |
| } |
| |
| FinalDtbHdr = ufdt_apply_multi_overlay (TmpDtbHdr, |
| fdt_totalsize (TmpDtbHdr), |
| DtsList); |
| DeleteDtList (&DtsList); |
| if (!FinalDtbHdr) { |
| DEBUG ((EFI_D_ERROR, "ApplyOverlay: ufdt apply overlay failed\n")); |
| return EFI_NOT_FOUND; |
| } |
| |
| out: |
| if ((BootParamlistPtr->RamdiskLoadAddr - |
| BootParamlistPtr->DeviceTreeLoadAddr) < |
| fdt_totalsize (FinalDtbHdr)) { |
| DEBUG ((EFI_D_ERROR, |
| "ApplyOverlay: After overlay DTB size exceeded than supported\n")); |
| return EFI_UNSUPPORTED; |
| } |
| /* If DeviceTreeLoadAddr == AppendedDtHdr |
| CopyMem will not copy Source Buffer to Destination Buffer |
| and return Destination BUffer. |
| */ |
| gBS->CopyMem ((VOID *)BootParamlistPtr->DeviceTreeLoadAddr, |
| FinalDtbHdr, |
| fdt_totalsize (FinalDtbHdr)); |
| post_overlay_free (); |
| DEBUG ((EFI_D_INFO, "Apply Overlay total time: %lu ms \n", |
| GetTimerCountms () - ApplyDTStartTime)); |
| return EFI_SUCCESS; |
| } |
| |
| STATIC UINT32 |
| GetNumberOfPages (UINT32 ImageSize, UINT32 PageSize) |
| { |
| return (ImageSize + PageSize - 1) / PageSize; |
| } |
| |
| STATIC EFI_STATUS |
| DTBImgCheckAndAppendDT (BootInfo *Info, BootParamlist *BootParamlistPtr) |
| { |
| VOID *SingleDtHdr = NULL; |
| VOID *NextDtHdr = NULL; |
| VOID *BoardDtb = NULL; |
| VOID *SocDtb = NULL; |
| VOID *OverrideDtb = NULL; |
| VOID *Dtb; |
| BOOLEAN DtboCheckNeeded = FALSE; |
| BOOLEAN DtboImgInvalid = FALSE; |
| struct fdt_entry_node *DtsList = NULL; |
| EFI_STATUS Status; |
| UINT32 HeaderVersion = 0; |
| struct boot_img_hdr_v1 *BootImgHdrV1; |
| struct boot_img_hdr_v2 *BootImgHdrV2; |
| vendor_boot_img_hdr_v3 *VendorBootImgHdrV3; |
| UINT32 NumHeaderPages; |
| UINT32 NumKernelPages; |
| UINT32 NumSecondPages; |
| UINT32 NumRamdiskPages; |
| UINT32 NumVendorRamdiskPages; |
| UINT32 NumRecoveryDtboPages; |
| VOID* ImageBuffer = NULL; |
| UINT32 ImageSize = 0; |
| |
| if (Info == NULL || |
| BootParamlistPtr == NULL) { |
| DEBUG ((EFI_D_ERROR, "Invalid input parameters\n")); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| ImageBuffer = BootParamlistPtr->ImageBuffer + |
| BootParamlistPtr->PageSize + |
| BootParamlistPtr->PatchedKernelHdrSize; |
| ImageSize = BootParamlistPtr->KernelSize; |
| HeaderVersion = Info->HeaderVersion; |
| |
| if (HeaderVersion > BOOT_HEADER_VERSION_ONE) { |
| BootImgHdrV1 = (struct boot_img_hdr_v1 *) |
| ((UINT64) BootParamlistPtr->ImageBuffer + |
| BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET); |
| BootImgHdrV2 = (struct boot_img_hdr_v2 *) |
| ((UINT64) BootParamlistPtr->ImageBuffer + |
| BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET + |
| BOOT_IMAGE_HEADER_V2_OFFSET); |
| |
| NumHeaderPages = 1; |
| NumKernelPages = |
| GetNumberOfPages (BootParamlistPtr->KernelSize, |
| BootParamlistPtr->PageSize); |
| NumRamdiskPages = |
| GetNumberOfPages (BootParamlistPtr->RamdiskSize, |
| BootParamlistPtr->PageSize); |
| NumSecondPages = |
| GetNumberOfPages (BootParamlistPtr->SecondSize, |
| BootParamlistPtr->PageSize); |
| |
| if (HeaderVersion == BOOT_HEADER_VERSION_TWO) { |
| NumRecoveryDtboPages = |
| GetNumberOfPages (BootImgHdrV1->recovery_dtbo_size, |
| BootParamlistPtr->PageSize); |
| BootParamlistPtr->DtbOffset = BootParamlistPtr->PageSize * |
| (NumHeaderPages + NumKernelPages + NumRamdiskPages + |
| NumSecondPages + NumRecoveryDtboPages); |
| ImageSize = BootImgHdrV2->dtb_size + BootParamlistPtr->DtbOffset; |
| ImageBuffer = BootParamlistPtr->ImageBuffer; |
| } else { |
| VendorBootImgHdrV3 = BootParamlistPtr->VendorImageBuffer; |
| |
| NumVendorRamdiskPages = GetNumberOfPages ( |
| BootParamlistPtr->VendorRamdiskSize, |
| BootParamlistPtr->PageSize); |
| BootParamlistPtr->DtbOffset = BootParamlistPtr->PageSize * |
| (NumHeaderPages + NumVendorRamdiskPages); |
| ImageSize = VendorBootImgHdrV3->dtb_size + |
| BootParamlistPtr->DtbOffset; |
| |
| // DTB is a part of vendor_boot image |
| ImageBuffer = BootParamlistPtr->VendorImageBuffer; |
| } |
| } |
| DtboImgInvalid = LoadAndValidateDtboImg (Info, BootParamlistPtr); |
| if (!DtboImgInvalid) { |
| // appended device tree |
| Dtb = DeviceTreeAppended (ImageBuffer, |
| ImageSize, |
| BootParamlistPtr->DtbOffset, |
| (VOID *)BootParamlistPtr->DeviceTreeLoadAddr); |
| if (!Dtb) { |
| if (BootParamlistPtr->DtbOffset >= ImageSize) { |
| DEBUG ((EFI_D_ERROR, "Dtb offset goes beyond the image size\n")); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| SingleDtHdr = (BootParamlistPtr->ImageBuffer + |
| BootParamlistPtr->DtbOffset); |
| |
| if (!fdt_check_header (SingleDtHdr)) { |
| if ((ImageSize - BootParamlistPtr->DtbOffset) < |
| fdt_totalsize (SingleDtHdr)) { |
| DEBUG ((EFI_D_ERROR, "Dtb offset goes beyond the image size\n")); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| NextDtHdr = |
| (VOID *)((uintptr_t)SingleDtHdr + fdt_totalsize (SingleDtHdr)); |
| if (!fdt_check_header (NextDtHdr)) { |
| DEBUG ((EFI_D_VERBOSE, "Not the single appended DTB\n")); |
| return EFI_NOT_FOUND; |
| } |
| |
| DEBUG ((EFI_D_VERBOSE, "Single appended DTB found\n")); |
| if (CHECK_ADD64 (BootParamlistPtr->DeviceTreeLoadAddr, |
| fdt_totalsize (SingleDtHdr))) { |
| DEBUG ((EFI_D_ERROR, |
| "Integer Overflow: in single dtb header addition\n")); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| gBS->CopyMem ((VOID *)BootParamlistPtr->DeviceTreeLoadAddr, |
| SingleDtHdr, fdt_totalsize (SingleDtHdr)); |
| } else { |
| DEBUG ((EFI_D_ERROR, "Error: Device Tree blob not found\n")); |
| return EFI_NOT_FOUND; |
| } |
| } |
| } else { |
| /*It is the case of DTB overlay Get the Soc specific dtb */ |
| SocDtb = GetSocDtb (ImageBuffer, |
| ImageSize, |
| BootParamlistPtr->DtbOffset, |
| (VOID *)BootParamlistPtr->DeviceTreeLoadAddr); |
| |
| if (!SocDtb) { |
| DEBUG ((EFI_D_ERROR, |
| "Error: Appended Soc Device Tree blob not found\n")); |
| return EFI_NOT_FOUND; |
| } |
| |
| /*Check do we really need to gothrough DTBO or not*/ |
| DtboCheckNeeded = GetDtboNeeded (); |
| if (DtboCheckNeeded == TRUE) { |
| BoardDtb = GetBoardDtb (Info, BootParamlistPtr->DtboImgBuffer); |
| if (!BoardDtb) { |
| DEBUG ((EFI_D_ERROR, "Error: Board Dtbo blob not found\n")); |
| return EFI_NOT_FOUND; |
| } |
| |
| if (!AppendToDtList (&DtsList, |
| (fdt64_t)BoardDtb, |
| fdt_totalsize (BoardDtb))) { |
| DEBUG ((EFI_D_ERROR, |
| "Unable to Allocate buffer for Overlay DT\n")); |
| DeleteDtList (&DtsList); |
| return EFI_OUT_OF_RESOURCES; |
| } |
| } |
| |
| /* If hypervisor boot info is present, append dtbo info passed from hyp */ |
| if (IsVmEnabled ()) { |
| if (BootParamlistPtr->HypDtboBaseAddr == NULL) { |
| DEBUG ((EFI_D_ERROR, "Error: HypOverlay DT is NULL\n")); |
| return EFI_NOT_FOUND; |
| } |
| |
| for (UINT32 i = 0; i < BootParamlistPtr->NumHypDtbos; i++) { |
| /* Flag the invalid dtbos and overlay the valid ones */ |
| if (!BootParamlistPtr->HypDtboBaseAddr[i] || |
| fdt_check_header ((VOID *)BootParamlistPtr->HypDtboBaseAddr[i])) { |
| DEBUG ((EFI_D_ERROR, "HypInfo: Not overlaying hyp dtbo" |
| "Dtbo :%d is null or Bad DT header\n", i)); |
| continue; |
| } |
| |
| if (!AppendToDtList (&DtsList, |
| (fdt64_t)BootParamlistPtr->HypDtboBaseAddr[i], |
| fdt_totalsize (BootParamlistPtr->HypDtboBaseAddr[i]))) { |
| DEBUG ((EFI_D_ERROR, |
| "Unable to Allocate buffer for HypOverlay DT num: %d\n", i)); |
| DeleteDtList (&DtsList); |
| return EFI_OUT_OF_RESOURCES; |
| } |
| } |
| } |
| |
| // Only enabled to debug builds. |
| if (!TargetBuildVariantUser ()) { |
| Status = GetOvrdDtb (&OverrideDtb); |
| if (Status == EFI_SUCCESS && |
| OverrideDtb && |
| !AppendToDtList (&DtsList, |
| (fdt64_t)OverrideDtb, |
| fdt_totalsize (OverrideDtb))) { |
| DEBUG ((EFI_D_ERROR, |
| "Unable to allocate buffer for Override DT\n")); |
| DeleteDtList (&DtsList); |
| return EFI_OUT_OF_RESOURCES; |
| } |
| } |
| |
| Status = ApplyOverlay (BootParamlistPtr, |
| SocDtb, |
| DtsList); |
| if (Status != EFI_SUCCESS) { |
| DEBUG ((EFI_D_ERROR, "Error: Dtb overlay failed\n")); |
| return Status; |
| } |
| } |
| return EFI_SUCCESS; |
| } |
| |
| STATIC EFI_STATUS |
| GZipPkgCheck (BootParamlist *BootParamlistPtr) |
| { |
| UINT32 OutLen = 0; |
| UINT64 OutAvaiLen = 0; |
| struct kernel64_hdr *Kptr = NULL; |
| UINT64 DecompressStartTime; |
| |
| if (BootParamlistPtr == NULL) { |
| |
| DEBUG ((EFI_D_ERROR, "Invalid input parameters\n")); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| if (BootParamlistPtr->BootingWithGzipPkgKernel) { |
| OutAvaiLen = BootParamlistPtr->DeviceTreeLoadAddr - |
| BootParamlistPtr->KernelLoadAddr; |
| |
| if (OutAvaiLen > MAX_UINT32) { |
| DEBUG ((EFI_D_ERROR, |
| "Integer Overflow: the length of decompressed data = %u\n", |
| OutAvaiLen)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| DecompressStartTime = GetTimerCountms (); |
| if (decompress ( |
| (UINT8 *)(BootParamlistPtr->ImageBuffer + |
| BootParamlistPtr->PageSize), // Read blob using BlockIo |
| BootParamlistPtr->KernelSize, // Blob size |
| (UINT8 *)BootParamlistPtr->KernelLoadAddr, // Load address, allocated |
| (UINT32)OutAvaiLen, // Allocated Size |
| &BootParamlistPtr->DtbOffset, &OutLen)) { |
| DEBUG ((EFI_D_ERROR, "Decompressing kernel image failed!!!\n")); |
| return RETURN_OUT_OF_RESOURCES; |
| } |
| |
| if (OutLen <= sizeof (struct kernel64_hdr *)) { |
| DEBUG ((EFI_D_ERROR, |
| "Decompress kernel size is smaller than image header size\n")); |
| return RETURN_OUT_OF_RESOURCES; |
| } |
| Kptr = (Kernel64Hdr *) BootParamlistPtr->KernelLoadAddr; |
| DEBUG ((EFI_D_INFO, "Decompressing kernel image total time: %lu ms\n", |
| GetTimerCountms () - DecompressStartTime)); |
| } else { |
| Kptr = (struct kernel64_hdr *)(BootParamlistPtr->ImageBuffer |
| + BootParamlistPtr->PageSize); |
| /* Patch kernel support only for 64-bit */ |
| if (BootParamlistPtr->BootingWithPatchedKernel) { |
| DEBUG ((EFI_D_VERBOSE, "Patched kernel detected\n")); |
| |
| /* The size of the kernel is stored at start of kernel image + 16 |
| * The dtb would start just after the kernel */ |
| gBS->CopyMem ((VOID *)&BootParamlistPtr->DtbOffset, |
| (VOID *) (BootParamlistPtr->ImageBuffer + |
| BootParamlistPtr->PageSize + |
| sizeof (PATCHED_KERNEL_MAGIC) - 1), |
| sizeof (BootParamlistPtr->DtbOffset)); |
| |
| BootParamlistPtr->PatchedKernelHdrSize = PATCHED_KERNEL_HEADER_SIZE; |
| Kptr = (struct kernel64_hdr *)((VOID *)Kptr + |
| BootParamlistPtr->PatchedKernelHdrSize); |
| } |
| |
| if (Kptr->magic_64 != KERNEL64_HDR_MAGIC) { |
| if (BootParamlistPtr->KernelSize <= |
| DTB_OFFSET_LOCATION_IN_ARCH32_KERNEL_HDR) { |
| DEBUG ((EFI_D_ERROR, "DTB offset goes beyond kernel size.\n")); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| gBS->CopyMem ((VOID *)&BootParamlistPtr->DtbOffset, |
| ((VOID *)Kptr + DTB_OFFSET_LOCATION_IN_ARCH32_KERNEL_HDR), |
| sizeof (BootParamlistPtr->DtbOffset)); |
| } |
| gBS->CopyMem ((VOID *)BootParamlistPtr->KernelLoadAddr, (VOID *)Kptr, |
| BootParamlistPtr->KernelSize); |
| } |
| |
| if (Kptr->magic_64 != KERNEL64_HDR_MAGIC) { |
| /* For GZipped 32-bit Kernel */ |
| BootParamlistPtr->BootingWith32BitKernel = TRUE; |
| } else { |
| if (Kptr->ImageSize > |
| (BootParamlistPtr->DeviceTreeLoadAddr - |
| BootParamlistPtr->KernelLoadAddr)) { |
| DEBUG ((EFI_D_ERROR, |
| "DTB header can get corrupted due to runtime kernel size\n")); |
| return RETURN_OUT_OF_RESOURCES; |
| } |
| } |
| return EFI_SUCCESS; |
| } |
| |
| STATIC EFI_STATUS |
| LoadAddrAndDTUpdate (BootInfo *Info, BootParamlist *BootParamlistPtr) |
| { |
| EFI_STATUS Status; |
| UINT64 RamdiskLoadAddr; |
| UINT64 RamdiskEndAddr = 0; |
| UINT32 TotalRamdiskSize; |
| |
| if (BootParamlistPtr == NULL) { |
| DEBUG ((EFI_D_ERROR, "Invalid input parameters\n")); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| RamdiskLoadAddr = BootParamlistPtr->RamdiskLoadAddr; |
| |
| TotalRamdiskSize = BootParamlistPtr->RamdiskSize + |
| BootParamlistPtr->VendorRamdiskSize; |
| |
| if (RamdiskEndAddr - RamdiskLoadAddr < TotalRamdiskSize) { |
| DEBUG ((EFI_D_ERROR, "Error: Ramdisk size is over the limit\n")); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| if (CHECK_ADD64 ((UINT64)BootParamlistPtr->ImageBuffer, |
| BootParamlistPtr->RamdiskOffset)) { |
| DEBUG ((EFI_D_ERROR, "Integer Overflow: ImageBuffer=%u, " |
| "RamdiskOffset=%u\n", |
| BootParamlistPtr->ImageBuffer, |
| BootParamlistPtr->RamdiskOffset)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| Status = UpdateDeviceTree ((VOID *)BootParamlistPtr->DeviceTreeLoadAddr, |
| BootParamlistPtr->FinalCmdLine, |
| (VOID *)RamdiskLoadAddr, TotalRamdiskSize, |
| BootParamlistPtr->BootingWith32BitKernel); |
| if (Status != EFI_SUCCESS) { |
| DEBUG ((EFI_D_ERROR, "Device Tree update failed Status:%r\n", Status)); |
| return Status; |
| } |
| |
| /* If the boot-image version is greater than 2, place the vendor-ramdisk |
| * first in the memory, and then place ramdisk. |
| * This concatination would result in an overlay for .gzip and .cpio formats. |
| */ |
| if (Info->HeaderVersion >= BOOT_HEADER_VERSION_THREE) { |
| gBS->CopyMem ((VOID *)RamdiskLoadAddr, |
| BootParamlistPtr->VendorImageBuffer + |
| BootParamlistPtr->PageSize, |
| BootParamlistPtr->VendorRamdiskSize); |
| |
| RamdiskLoadAddr += BootParamlistPtr->VendorRamdiskSize; |
| } |
| |
| gBS->CopyMem ((CHAR8 *)RamdiskLoadAddr, |
| BootParamlistPtr->ImageBuffer + |
| BootParamlistPtr->RamdiskOffset, |
| BootParamlistPtr->RamdiskSize); |
| |
| if (BootParamlistPtr->BootingWith32BitKernel) { |
| if (CHECK_ADD64 (BootParamlistPtr->KernelLoadAddr, |
| BootParamlistPtr->KernelSizeActual)) { |
| DEBUG ((EFI_D_ERROR, "Integer Overflow: while Kernel image copy\n")); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| if (BootParamlistPtr->KernelLoadAddr + |
| BootParamlistPtr->KernelSizeActual > |
| BootParamlistPtr->DeviceTreeLoadAddr) { |
| DEBUG ((EFI_D_ERROR, "Kernel size is over the limit\n")); |
| return EFI_INVALID_PARAMETER; |
| } |
| gBS->CopyMem ((CHAR8 *)BootParamlistPtr->KernelLoadAddr, |
| BootParamlistPtr->ImageBuffer + |
| BootParamlistPtr->PageSize, |
| BootParamlistPtr->KernelSizeActual); |
| } |
| |
| return EFI_SUCCESS; |
| } |
| |
| STATIC EFI_STATUS |
| CatCmdLine (BootParamlist *BootParamlistPtr, |
| boot_img_hdr_v3 *BootImgHdrV3, |
| vendor_boot_img_hdr_v3 *VendorBootImgHdrV3) |
| { |
| UINTN MaxCmdLineLen = BOOT_ARGS_SIZE + |
| BOOT_EXTRA_ARGS_SIZE + VENDOR_BOOT_ARGS_SIZE; |
| |
| BootParamlistPtr->CmdLine = AllocateZeroPool (MaxCmdLineLen); |
| if (!BootParamlistPtr->CmdLine) { |
| DEBUG ((EFI_D_ERROR, |
| "CatCmdLine: Failed to allocate memory for cmdline\n")); |
| return EFI_OUT_OF_RESOURCES; |
| } |
| |
| /* Place the vendor_boot image cmdline first so that the cmdline |
| * from boot image takes precedence in case of duplicates. |
| */ |
| AsciiStrCpyS (BootParamlistPtr->CmdLine, MaxCmdLineLen, |
| (CONST CHAR8 *)VendorBootImgHdrV3->cmdline); |
| AsciiStrCatS (BootParamlistPtr->CmdLine, MaxCmdLineLen, " "); |
| AsciiStrCatS (BootParamlistPtr->CmdLine, MaxCmdLineLen, |
| (CONST CHAR8 *)BootImgHdrV3->cmdline); |
| |
| return EFI_SUCCESS; |
| } |
| |
| STATIC EFI_STATUS |
| UpdateBootParamsSizeAndCmdLine (BootInfo *Info, BootParamlist *BootParamlistPtr) |
| { |
| EFI_STATUS Status = EFI_SUCCESS; |
| UINTN VendorBootImgSize; |
| boot_img_hdr_v3 *BootImgHdrV3; |
| vendor_boot_img_hdr_v3 *VendorBootImgHdrV3; |
| |
| if (Info->HeaderVersion < BOOT_HEADER_VERSION_THREE) { |
| BootParamlistPtr->KernelSize = |
| ((boot_img_hdr *)(BootParamlistPtr->ImageBuffer))->kernel_size; |
| BootParamlistPtr->RamdiskSize = |
| ((boot_img_hdr *)(BootParamlistPtr->ImageBuffer))->ramdisk_size; |
| BootParamlistPtr->SecondSize = |
| ((boot_img_hdr *)(BootParamlistPtr->ImageBuffer))->second_size; |
| BootParamlistPtr->PageSize = |
| ((boot_img_hdr *)(BootParamlistPtr->ImageBuffer))->page_size; |
| BootParamlistPtr->CmdLine = (CHAR8 *)&(((boot_img_hdr *) |
| (BootParamlistPtr->ImageBuffer))->cmdline[0]); |
| BootParamlistPtr->CmdLine[BOOT_ARGS_SIZE - 1] = '\0'; |
| |
| return EFI_SUCCESS; |
| } |
| |
| BootImgHdrV3 = BootParamlistPtr->ImageBuffer; |
| |
| Status = GetImage (Info, (VOID **)&VendorBootImgHdrV3, |
| &VendorBootImgSize, "vendor_boot"); |
| if (Status != EFI_SUCCESS) { |
| DEBUG ((EFI_D_ERROR, |
| "UpdateBootParamsSizeAndCmdLine: Failed to find vendor_boot image\n")); |
| return Status; |
| } |
| |
| BootParamlistPtr->VendorImageBuffer = VendorBootImgHdrV3; |
| BootParamlistPtr->VendorImageSize = VendorBootImgSize; |
| BootParamlistPtr->KernelSize = BootImgHdrV3->kernel_size; |
| BootParamlistPtr->RamdiskSize = BootImgHdrV3->ramdisk_size; |
| BootParamlistPtr->VendorRamdiskSize = |
| VendorBootImgHdrV3->vendor_ramdisk_size; |
| BootParamlistPtr->PageSize = VendorBootImgHdrV3->page_size; |
| BootParamlistPtr->SecondSize = 0; |
| |
| Status = CatCmdLine (BootParamlistPtr, BootImgHdrV3, VendorBootImgHdrV3); |
| if (Status != EFI_SUCCESS) { |
| DEBUG ((EFI_D_ERROR, |
| "UpdateBootParamsSizeAndCmdLine: Failed to cat cmdline\n")); |
| return Status; |
| } |
| |
| return EFI_SUCCESS; |
| } |
| |
| EFI_STATUS |
| BootLinux (BootInfo *Info) |
| { |
| |
| EFI_STATUS Status; |
| CHAR16 *PartitionName = NULL; |
| BOOLEAN Recovery = FALSE; |
| BOOLEAN AlarmBoot = FALSE; |
| |
| LINUX_KERNEL LinuxKernel; |
| LINUX_KERNEL32 LinuxKernel32; |
| UINT32 RamdiskSizeActual = 0; |
| UINT32 SecondSizeActual = 0; |
| |
| /*Boot Image header information variables*/ |
| CHAR8 FfbmStr[FFBM_MODE_BUF_SIZE] = {'\0'}; |
| BOOLEAN IsModeSwitch = FALSE; |
| |
| BootParamlist BootParamlistPtr = {0}; |
| |
| if (Info == NULL) { |
| DEBUG ((EFI_D_ERROR, "BootLinux: invalid parameter Info\n")); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| if (IsVmEnabled ()) { |
| Status = CheckAndSetVmData (&BootParamlistPtr); |
| if (Status != EFI_SUCCESS) { |
| DEBUG ((EFI_D_ERROR, "Failed to update HypData!! Status:%r\n", Status)); |
| return Status; |
| } |
| } |
| |
| PartitionName = Info->Pname; |
| Recovery = Info->BootIntoRecovery; |
| AlarmBoot = Info->BootReasonAlarm; |
| |
| if (!StrnCmp (PartitionName, (CONST CHAR16 *)L"boot", |
| StrLen ((CONST CHAR16 *)L"boot"))) { |
| Status = GetFfbmCommand (FfbmStr, FFBM_MODE_BUF_SIZE); |
| if (Status != EFI_SUCCESS) { |
| DEBUG ((EFI_D_VERBOSE, "No Ffbm cookie found, ignore: %r\n", Status)); |
| FfbmStr[0] = '\0'; |
| } |
| } |
| |
| Status = GetImage (Info, |
| &BootParamlistPtr.ImageBuffer, |
| (UINTN *)&BootParamlistPtr.ImageSize, |
| ((!Info->MultiSlotBoot || |
| IsDynamicPartitionSupport ()) && |
| (Recovery && |
| !IsBuildUseRecoveryAsBoot ()))? |
| "recovery" : "boot"); |
| if (Status != EFI_SUCCESS || |
| BootParamlistPtr.ImageBuffer == NULL || |
| BootParamlistPtr.ImageSize <= 0) { |
| DEBUG ((EFI_D_ERROR, "BootLinux: Get%aImage failed!\n", |
| (!Info->MultiSlotBoot && |
| (Recovery && |
| !IsBuildUseRecoveryAsBoot ()))? "Recovery" : "Boot")); |
| return EFI_NOT_STARTED; |
| } |
| /* Find if MDTP is enabled and Active */ |
| |
| Status = CheckMDTPStatus (PartitionName, Info); |
| if (Status != EFI_SUCCESS) { |
| return Status; |
| } |
| |
| Info->HeaderVersion = ((boot_img_hdr *) |
| (BootParamlistPtr.ImageBuffer))->header_version; |
| |
| Status = UpdateBootParamsSizeAndCmdLine (Info, &BootParamlistPtr); |
| if (Status != EFI_SUCCESS) { |
| return Status; |
| } |
| |
| // Retrive Base Memory Address from Ram Partition Table |
| Status = BaseMem (&BootParamlistPtr.BaseMemory); |
| if (Status != EFI_SUCCESS) { |
| DEBUG ((EFI_D_ERROR, "Base memory not found!!! Status:%r\n", Status)); |
| return Status; |
| } |
| |
| Status = UpdateKernelModeAndPkg (&BootParamlistPtr); |
| if (Status != EFI_SUCCESS) { |
| return Status; |
| } |
| |
| Status = UpdateBootParams (&BootParamlistPtr); |
| if (Status != EFI_SUCCESS) { |
| return Status; |
| } |
| SetandGetLoadAddr (&BootParamlistPtr, LOAD_ADDR_NONE); |
| Status = GZipPkgCheck (&BootParamlistPtr); |
| if (Status != EFI_SUCCESS) { |
| return Status; |
| } |
| |
| /*Finds out the location of device tree image and ramdisk image within the |
| *boot image |
| *Kernel, Ramdisk and Second sizes all rounded to page |
| *The offset and the LOCAL_ROUND_TO_PAGE function is written in a way that it |
| *is done the same in LK*/ |
| BootParamlistPtr.KernelSizeActual = LOCAL_ROUND_TO_PAGE ( |
| BootParamlistPtr.KernelSize, |
| BootParamlistPtr.PageSize); |
| RamdiskSizeActual = LOCAL_ROUND_TO_PAGE (BootParamlistPtr.RamdiskSize, |
| BootParamlistPtr.PageSize); |
| SecondSizeActual = LOCAL_ROUND_TO_PAGE (BootParamlistPtr.SecondSize, |
| BootParamlistPtr.PageSize); |
| |
| /*Offsets are the location of the images within the boot image*/ |
| |
| BootParamlistPtr.RamdiskOffset = ADD_OF (BootParamlistPtr.PageSize, |
| BootParamlistPtr.KernelSizeActual); |
| if (!BootParamlistPtr.RamdiskOffset) { |
| DEBUG ((EFI_D_ERROR, "Integer Overflow: PageSize=%u, KernelSizeActual=%u\n", |
| BootParamlistPtr.PageSize, BootParamlistPtr.KernelSizeActual)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| DEBUG ((EFI_D_VERBOSE, "Kernel Load Address: 0x%x\n", |
| BootParamlistPtr.KernelLoadAddr)); |
| DEBUG ((EFI_D_VERBOSE, "Kernel Size Actual: 0x%x\n", |
| BootParamlistPtr.KernelSizeActual)); |
| DEBUG ((EFI_D_VERBOSE, "Second Size Actual: 0x%x\n", SecondSizeActual)); |
| DEBUG ((EFI_D_VERBOSE, "Ramdisk Load Address: 0x%x\n", |
| BootParamlistPtr.RamdiskLoadAddr)); |
| DEBUG ((EFI_D_VERBOSE, "Ramdisk Size Actual: 0x%x\n", RamdiskSizeActual)); |
| DEBUG ((EFI_D_VERBOSE, "Ramdisk Offset: 0x%x\n", |
| BootParamlistPtr.RamdiskOffset)); |
| DEBUG ( |
| (EFI_D_VERBOSE, "Device Tree Load Address: 0x%x\n", |
| BootParamlistPtr.DeviceTreeLoadAddr)); |
| |
| if (AsciiStrStr (BootParamlistPtr.CmdLine, "root=")) { |
| BootDevImage = TRUE; |
| } |
| |
| Status = DTBImgCheckAndAppendDT (Info, &BootParamlistPtr); |
| if (Status != EFI_SUCCESS) { |
| return Status; |
| } |
| |
| /* Updates the command line from boot image, appends device serial no., |
| * baseband information, etc. |
| * Called before ShutdownUefiBootServices as it uses some boot service |
| * functions |
| */ |
| Status = UpdateCmdLine (BootParamlistPtr.CmdLine, FfbmStr, Recovery, |
| AlarmBoot, Info->VBCmdLine, &BootParamlistPtr.FinalCmdLine, |
| Info->HeaderVersion); |
| if (EFI_ERROR (Status)) { |
| DEBUG ((EFI_D_ERROR, "Error updating cmdline. Device Error %r\n", Status)); |
| return Status; |
| } |
| |
| Status = LoadAddrAndDTUpdate (Info, &BootParamlistPtr); |
| if (Status != EFI_SUCCESS) { |
| return Status; |
| } |
| |
| FreeVerifiedBootResource (Info); |
| |
| /* Free the boot logo blt buffer before starting kernel */ |
| FreeBootLogoBltBuffer (); |
| if (BootParamlistPtr.BootingWith32BitKernel) { |
| Status = gBS->LocateProtocol (&gQcomScmModeSwithProtocolGuid, NULL, |
| (VOID **)&pQcomScmModeSwitchProtocol); |
| if (!EFI_ERROR (Status)) |
| IsModeSwitch = TRUE; |
| } |
| |
| DEBUG ((EFI_D_INFO, "\nShutting Down UEFI Boot Services: %lu ms\n", |
| GetTimerCountms ())); |
| /*Shut down UEFI boot services*/ |
| Status = ShutdownUefiBootServices (); |
| if (EFI_ERROR (Status)) { |
| DEBUG ((EFI_D_ERROR, |
| "ERROR: Can not shutdown UEFI boot services. Status=0x%X\n", |
| Status)); |
| goto Exit; |
| } |
| |
| PreparePlatformHardware (); |
| |
| BootStatsSetTimeStamp (BS_KERNEL_ENTRY); |
| |
| if (IsVmEnabled ()) { |
| DisableHypUartUsageForLogging (); |
| } |
| |
| // |
| // Start the Linux Kernel |
| // |
| |
| if (BootParamlistPtr.BootingWith32BitKernel) { |
| if (IsModeSwitch) { |
| Status = SwitchTo32bitModeBooting ( |
| (UINT64)BootParamlistPtr.KernelLoadAddr, |
| (UINT64)BootParamlistPtr.DeviceTreeLoadAddr); |
| if (EFI_ERROR (Status)) { |
| goto Exit; |
| } |
| } |
| |
| // Booting into 32 bit kernel. |
| LinuxKernel32 = (LINUX_KERNEL32) (UINT64)BootParamlistPtr.KernelLoadAddr; |
| LinuxKernel32 (0, 0, (UINTN)BootParamlistPtr.DeviceTreeLoadAddr); |
| |
| // Should never reach here. After life support is not available |
| DEBUG ((EFI_D_ERROR, "After Life support not available\n")); |
| goto Exit; |
| } |
| |
| LinuxKernel = (LINUX_KERNEL) (UINT64)BootParamlistPtr.KernelLoadAddr; |
| LinuxKernel ((UINT64)BootParamlistPtr.DeviceTreeLoadAddr, 0, 0, 0); |
| |
| // Kernel should never exit |
| // After Life services are not provided |
| |
| Exit: |
| // Only be here if we fail to start Linux |
| CpuDeadLoop (); |
| return EFI_NOT_STARTED; |
| } |
| |
| /** |
| Check image header |
| @param[in] ImageHdrBuffer Supplies the address where a pointer to the image |
| header buffer. |
| @param[in] ImageHdrSize Supplies the address where a pointer to the image |
| header size. |
| @param[in] VendorImageHdrBuffer Supplies the address where a pointer to |
| the image header buffer. |
| @param[in] VendorImageHdrSize Supplies the address where a pointer to |
| the image header size. |
| @param[out] ImageSizeActual The Pointer for image actual size. |
| @param[out] PageSize The Pointer for page size.. |
| @retval EFI_SUCCESS Check image header successfully. |
| @retval other Failed to check image header. |
| **/ |
| EFI_STATUS |
| CheckImageHeader (VOID *ImageHdrBuffer, |
| UINT32 ImageHdrSize, |
| VOID *VendorImageHdrBuffer, |
| UINT32 VendorImageHdrSize, |
| UINT32 *ImageSizeActual, |
| UINT32 *PageSize, |
| BOOLEAN BootIntoRecovery) |
| { |
| EFI_STATUS Status = EFI_SUCCESS; |
| |
| struct boot_img_hdr_v2 *BootImgHdrV2; |
| boot_img_hdr_v3 *BootImgHdrV3; |
| vendor_boot_img_hdr_v3 *VendorBootImgHdrV3; |
| |
| UINT32 KernelSizeActual = 0; |
| UINT32 DtSizeActual = 0; |
| UINT32 RamdiskSizeActual = 0; |
| UINT32 VendorRamdiskSizeActual = 0; |
| |
| // Boot Image header information variables |
| UINT32 HeaderVersion = 0; |
| UINT32 KernelSize = 0; |
| UINT32 RamdiskSize = 0; |
| UINT32 VendorRamdiskSize = 0; |
| UINT32 SecondSize = 0; |
| UINT32 DtSize = 0; |
| UINT32 tempImgSize = 0; |
| |
| if (CompareMem ((VOID *)((boot_img_hdr *)(ImageHdrBuffer))->magic, BOOT_MAGIC, |
| BOOT_MAGIC_SIZE)) { |
| DEBUG ((EFI_D_ERROR, "Invalid boot image header\n")); |
| return EFI_NO_MEDIA; |
| } |
| |
| HeaderVersion = ((boot_img_hdr *)(ImageHdrBuffer))->header_version; |
| if (HeaderVersion < BOOT_HEADER_VERSION_THREE) { |
| KernelSize = ((boot_img_hdr *)(ImageHdrBuffer))->kernel_size; |
| RamdiskSize = ((boot_img_hdr *)(ImageHdrBuffer))->ramdisk_size; |
| SecondSize = ((boot_img_hdr *)(ImageHdrBuffer))->second_size; |
| *PageSize = ((boot_img_hdr *)(ImageHdrBuffer))->page_size; |
| } else { |
| if (CompareMem ((VOID *)((vendor_boot_img_hdr_v3 *) |
| (VendorImageHdrBuffer))->magic, |
| VENDOR_BOOT_MAGIC, VENDOR_BOOT_MAGIC_SIZE)) { |
| DEBUG ((EFI_D_ERROR, "Invalid vendor_boot image header\n")); |
| return EFI_NO_MEDIA; |
| } |
| |
| BootImgHdrV3 = ImageHdrBuffer; |
| VendorBootImgHdrV3 = VendorImageHdrBuffer; |
| |
| KernelSize = BootImgHdrV3->kernel_size; |
| RamdiskSize = BootImgHdrV3->ramdisk_size; |
| VendorRamdiskSize = VendorBootImgHdrV3->vendor_ramdisk_size; |
| *PageSize = VendorBootImgHdrV3->page_size; |
| DtSize = VendorBootImgHdrV3->dtb_size; |
| |
| if (*PageSize > BOOT_IMG_MAX_PAGE_SIZE) { |
| DEBUG ((EFI_D_ERROR, "Invalid vendor-img pagesize. " |
| "MAX: %u. PageSize: %u and VendorImageHdrSize: %u\n", |
| BOOT_IMG_MAX_PAGE_SIZE, *PageSize, VendorImageHdrSize)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| VendorRamdiskSizeActual = ROUND_TO_PAGE (VendorRamdiskSize, *PageSize - 1); |
| if (VendorRamdiskSize && |
| !VendorRamdiskSizeActual) { |
| DEBUG ((EFI_D_ERROR, "Integer Overflow: Vendor Ramdisk Size = %u\n", |
| RamdiskSize)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| } |
| |
| if (!KernelSize || !*PageSize) { |
| DEBUG ((EFI_D_ERROR, "Invalid image Sizes\n")); |
| DEBUG ( |
| (EFI_D_ERROR, "KernelSize: %u, PageSize=%u\n", KernelSize, *PageSize)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| if ((*PageSize != ImageHdrSize) && (*PageSize > BOOT_IMG_MAX_PAGE_SIZE)) { |
| DEBUG ((EFI_D_ERROR, "Invalid image pagesize\n")); |
| DEBUG ((EFI_D_ERROR, "MAX: %u. PageSize: %u and ImageHdrSize: %u\n", |
| BOOT_IMG_MAX_PAGE_SIZE, *PageSize, ImageHdrSize)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| KernelSizeActual = ROUND_TO_PAGE (KernelSize, *PageSize - 1); |
| if (!KernelSizeActual) { |
| DEBUG ((EFI_D_ERROR, "Integer Overflow: Kernel Size = %u\n", KernelSize)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| RamdiskSizeActual = ROUND_TO_PAGE (RamdiskSize, *PageSize - 1); |
| if (RamdiskSize && !RamdiskSizeActual) { |
| DEBUG ((EFI_D_ERROR, "Integer Overflow: Ramdisk Size = %u\n", RamdiskSize)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| if (HeaderVersion == BOOT_HEADER_VERSION_TWO) { |
| BootImgHdrV2 = (struct boot_img_hdr_v2 *) |
| ((UINT64) ImageHdrBuffer + |
| BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET + |
| BOOT_IMAGE_HEADER_V2_OFFSET); |
| |
| DtSize = BootImgHdrV2->dtb_size; |
| } |
| |
| // DT size doesn't apply to header versions 0 and 1 |
| if (HeaderVersion >= BOOT_HEADER_VERSION_TWO) { |
| DtSizeActual = ROUND_TO_PAGE (DtSize, *PageSize - 1); |
| if (DtSize && |
| !DtSizeActual) { |
| DEBUG ((EFI_D_ERROR, "Integer Overflow: dt Size = %u\n", DtSize)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| } |
| |
| *ImageSizeActual = ADD_OF (*PageSize, KernelSizeActual); |
| if (!*ImageSizeActual) { |
| DEBUG ((EFI_D_ERROR, "Integer Overflow: Actual Kernel size = %u\n", |
| KernelSizeActual)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| tempImgSize = *ImageSizeActual; |
| *ImageSizeActual = ADD_OF (*ImageSizeActual, RamdiskSizeActual); |
| if (!*ImageSizeActual) { |
| DEBUG ((EFI_D_ERROR, |
| "Integer Overflow: ImgSizeActual=%u, RamdiskActual=%u\n", |
| tempImgSize, RamdiskSizeActual)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| tempImgSize = *ImageSizeActual; |
| |
| /* |
| * As the DTB is not not a part of boot-images with header versions greater |
| * than two, ignore considering its size for calculating the total image size |
| */ |
| if (HeaderVersion < BOOT_HEADER_VERSION_THREE) { |
| *ImageSizeActual = ADD_OF (*ImageSizeActual, DtSizeActual); |
| if (!*ImageSizeActual) { |
| DEBUG ((EFI_D_ERROR, "Integer Overflow: ImgSizeActual=%u," |
| " DtSizeActual=%u\n", tempImgSize, DtSizeActual)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| } |
| |
| if (BootIntoRecovery && |
| HeaderVersion > BOOT_HEADER_VERSION_ZERO && |
| HeaderVersion < BOOT_HEADER_VERSION_THREE) { |
| |
| struct boot_img_hdr_v1 *Hdr1 = |
| (struct boot_img_hdr_v1 *) (ImageHdrBuffer + sizeof (boot_img_hdr)); |
| UINT32 RecoveryDtboActual = 0; |
| |
| if (HeaderVersion == BOOT_HEADER_VERSION_ONE) { |
| if ((Hdr1->header_size != |
| sizeof (struct boot_img_hdr_v1) + sizeof (boot_img_hdr))) { |
| DEBUG ((EFI_D_ERROR, |
| "Invalid boot image header: %d\n", Hdr1->header_size)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| } |
| else { |
| if ((Hdr1->header_size != |
| BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET + |
| BOOT_IMAGE_HEADER_V2_OFFSET + |
| sizeof (struct boot_img_hdr_v2))) { |
| DEBUG ((EFI_D_ERROR, |
| "Invalid boot image header: %d\n", Hdr1->header_size)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| } |
| RecoveryDtboActual = ROUND_TO_PAGE (Hdr1->recovery_dtbo_size, |
| *PageSize - 1); |
| |
| if (RecoveryDtboActual > DTBO_MAX_SIZE_ALLOWED) { |
| DEBUG ((EFI_D_ERROR, "Recovery Dtbo Size too big %x, Allowed size %x\n", |
| RecoveryDtboActual, DTBO_MAX_SIZE_ALLOWED)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| if (CHECK_ADD64 (Hdr1->recovery_dtbo_offset, RecoveryDtboActual)) { |
| DEBUG ((EFI_D_ERROR, "Integer Overflow: RecoveryDtboOffset=%u " |
| "RecoveryDtboActual=%u\n", |
| Hdr1->recovery_dtbo_offset, RecoveryDtboActual)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| tempImgSize = *ImageSizeActual; |
| *ImageSizeActual = ADD_OF (*ImageSizeActual, RecoveryDtboActual); |
| if (!*ImageSizeActual) { |
| DEBUG ((EFI_D_ERROR, "Integer Overflow: ImgSizeActual=%u," |
| " RecoveryDtboActual=%u\n", tempImgSize, RecoveryDtboActual)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| } |
| DEBUG ((EFI_D_VERBOSE, "Boot Image Header Info...\n")); |
| DEBUG ((EFI_D_VERBOSE, "Image Header version : 0x%x\n", HeaderVersion)); |
| DEBUG ((EFI_D_VERBOSE, "Kernel Size 1 : 0x%x\n", KernelSize)); |
| DEBUG ((EFI_D_VERBOSE, "Kernel Size 2 : 0x%x\n", SecondSize)); |
| DEBUG ((EFI_D_VERBOSE, "Ramdisk Size : 0x%x\n", RamdiskSize)); |
| DEBUG ((EFI_D_VERBOSE, "DTB Size : 0x%x\n", DtSize)); |
| |
| if (HeaderVersion >= BOOT_HEADER_VERSION_THREE) { |
| DEBUG ((EFI_D_VERBOSE, "Vendor Ramdisk Size : 0x%x\n", |
| VendorRamdiskSize)); |
| } |
| |
| return Status; |
| } |
| |
| /** |
| Load image header from partition |
| @param[in] Pname Partition name. |
| @param[out] ImageHdrBuffer Supplies the address where a pointer to the image |
| buffer. |
| @param[out] ImageHdrSize The Pointer for image actual size. |
| @retval EFI_SUCCESS Load image from partition successfully. |
| @retval other Failed to Load image from partition. |
| **/ |
| EFI_STATUS |
| LoadImageHeader (CHAR16 *Pname, VOID **ImageHdrBuffer, UINT32 *ImageHdrSize) |
| { |
| if (ImageHdrBuffer == NULL) { |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| if (!ADD_OF (BOOT_IMG_MAX_PAGE_SIZE, ALIGNMENT_MASK_4KB - 1)) { |
| DEBUG ((EFI_D_ERROR, "Integer Overflow: in ALIGNMENT_MASK_4KB addition\n")); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| *ImageHdrBuffer = |
| AllocatePages (ALIGN_PAGES (BOOT_IMG_MAX_PAGE_SIZE, ALIGNMENT_MASK_4KB)); |
| if (!*ImageHdrBuffer) { |
| DEBUG ((EFI_D_ERROR, "Failed to allocate for Boot image Hdr\n")); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| *ImageHdrSize = BOOT_IMG_MAX_PAGE_SIZE; |
| return LoadImageFromPartition (*ImageHdrBuffer, ImageHdrSize, Pname); |
| } |
| |
| /** |
| Load image from partition |
| @param[in] Pname Partition name. |
| @param[in] ImageBuffer Supplies the address where a pointer to the image |
| buffer. |
| @param[in] ImageSizeActual Actual size of the Image. |
| @param[in] PageSize The page size |
| @retval EFI_SUCCESS Load image from partition successfully. |
| @retval other Failed to Load image from partition. |
| **/ |
| EFI_STATUS |
| LoadImage (CHAR16 *Pname, VOID **ImageBuffer, |
| UINT32 ImageSizeActual, UINT32 PageSize) |
| { |
| EFI_STATUS Status = EFI_SUCCESS; |
| UINT32 ImageSize = 0; |
| |
| // Check for invalid ImageBuffer |
| if (ImageBuffer == NULL) { |
| return EFI_INVALID_PARAMETER; |
| } else { |
| *ImageBuffer = NULL; |
| } |
| |
| ImageSize = |
| ADD_OF (ROUND_TO_PAGE (ImageSizeActual, (PageSize - 1)), PageSize); |
| if (!ImageSize) { |
| DEBUG ((EFI_D_ERROR, "Integer Overflow: ImgSize=%u\n", ImageSizeActual)); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| if (!ADD_OF (ImageSize, ALIGNMENT_MASK_4KB - 1)) { |
| DEBUG ((EFI_D_ERROR, "Integer Overflow: in ALIGNMENT_MASK_4KB addition\n")); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| *ImageBuffer = AllocatePages (ALIGN_PAGES (ImageSize, ALIGNMENT_MASK_4KB)); |
| if (!*ImageBuffer) { |
| DEBUG ((EFI_D_ERROR, "No resources available for ImageBuffer\n")); |
| return EFI_OUT_OF_RESOURCES; |
| } |
| |
| BootStatsSetTimeStamp (BS_KERNEL_LOAD_START); |
| Status = LoadImageFromPartition (*ImageBuffer, &ImageSize, Pname); |
| BootStatsSetTimeStamp (BS_KERNEL_LOAD_DONE); |
| |
| if (Status != EFI_SUCCESS) { |
| DEBUG ((EFI_D_ERROR, "Failed Kernel Size : 0x%x\n", ImageSize)); |
| return Status; |
| } |
| |
| return Status; |
| } |
| |
| EFI_STATUS |
| GetImage (CONST BootInfo *Info, |
| VOID **ImageBuffer, |
| UINTN *ImageSize, |
| CHAR8 *ImageName) |
| { |
| if (Info == NULL || ImageBuffer == NULL || ImageSize == NULL || |
| ImageName == NULL) { |
| DEBUG ((EFI_D_ERROR, "GetImage: invalid parameters\n")); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| for (UINTN LoadedIndex = 0; LoadedIndex < Info->NumLoadedImages; |
| LoadedIndex++) { |
| if (!AsciiStrnCmp (Info->Images[LoadedIndex].Name, ImageName, |
| AsciiStrLen (ImageName))) { |
| *ImageBuffer = Info->Images[LoadedIndex].ImageBuffer; |
| *ImageSize = Info->Images[LoadedIndex].ImageSize; |
| return EFI_SUCCESS; |
| } |
| } |
| return EFI_NOT_FOUND; |
| } |
| |
| /* Return Build variant */ |
| #ifdef USER_BUILD_VARIANT |
| BOOLEAN TargetBuildVariantUser (VOID) |
| { |
| return TRUE; |
| } |
| #else |
| BOOLEAN TargetBuildVariantUser (VOID) |
| { |
| return FALSE; |
| } |
| #endif |
| |
| #ifdef ENABLE_LE_VARIANT |
| BOOLEAN IsLEVariant (VOID) |
| { |
| return TRUE; |
| } |
| #else |
| BOOLEAN IsLEVariant (VOID) |
| { |
| return FALSE; |
| } |
| #endif |
| |
| #ifdef BUILD_SYSTEM_ROOT_IMAGE |
| BOOLEAN IsBuildAsSystemRootImage (VOID) |
| { |
| return TRUE; |
| } |
| #else |
| BOOLEAN IsBuildAsSystemRootImage (VOID) |
| { |
| return FALSE; |
| } |
| #endif |
| |
| #ifdef BUILD_USES_RECOVERY_AS_BOOT |
| BOOLEAN IsBuildUseRecoveryAsBoot (VOID) |
| { |
| return TRUE; |
| } |
| #else |
| BOOLEAN IsBuildUseRecoveryAsBoot (VOID) |
| { |
| return FALSE; |
| } |
| #endif |
| |
| VOID |
| ResetBootDevImage (VOID) |
| { |
| BootDevImage = FALSE; |
| } |
| |
| VOID |
| SetBootDevImage (VOID) |
| { |
| BootDevImage = TRUE; |
| } |
| |
| BOOLEAN IsBootDevImage (VOID) |
| { |
| return BootDevImage; |
| } |
| |
| #ifdef AB_RETRYCOUNT_DISABLE |
| BOOLEAN IsABRetryCountDisabled (VOID) |
| { |
| return TRUE; |
| } |
| #else |
| BOOLEAN IsABRetryCountDisabled (VOID) |
| { |
| return FALSE; |
| } |
| #endif |
| |
| #if DYNAMIC_PARTITION_SUPPORT |
| BOOLEAN IsDynamicPartitionSupport (VOID) |
| { |
| return TRUE; |
| } |
| #else |
| BOOLEAN IsDynamicPartitionSupport (VOID) |
| { |
| return FALSE; |
| } |
| #endif |
| |
| #if VIRTUAL_AB_OTA |
| BOOLEAN IsVirtualAbOtaSupported (VOID) |
| { |
| return TRUE; |
| } |
| #else |
| BOOLEAN IsVirtualAbOtaSupported (VOID) |
| { |
| return FALSE; |
| } |
| #endif |
| |
| #if NAND_SQUASHFS_SUPPORT |
| BOOLEAN IsNANDSquashFsSupport (VOID) |
| { |
| return TRUE; |
| } |
| #else |
| BOOLEAN IsNANDSquashFsSupport (VOID) |
| { |
| return FALSE; |
| } |
| #endif |
| |
| #if TARGET_BOARD_TYPE_AUTO |
| BOOLEAN IsEnableDisplayMenuFlagSupported (VOID) |
| { |
| return FALSE; |
| } |
| #else |
| BOOLEAN IsEnableDisplayMenuFlagSupported (VOID) |
| { |
| return TRUE; |
| } |
| #endif |
| |
| #ifdef ENABLE_SYSTEMD_BOOTSLOT |
| BOOLEAN IsSystemdBootslotEnabled (VOID) |
| { |
| return TRUE; |
| } |
| #else |
| BOOLEAN IsSystemdBootslotEnabled (VOID) |
| { |
| return FALSE; |
| } |
| #endif |