/** @file Linux image load library Copyright (c) 2011 - 2020, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #define ACPI_RSDP_CMDLINE_STR "acpi_rsdp=" /** Jump to kernel entry point. @param[in] KernelStart Pointer to kernel entry point. @param[in] KernelBootParams Pointer to boot parameter structure. **/ VOID EFIAPI JumpToKernel ( IN VOID *KernelStart, IN VOID *KernelBootParams ); /** Jump to kernel 64-bit entry point. @param[in] KernelStart Pointer to kernel 64-bit entry point. @param[in] KernelBootParams Pointer to boot parameter structure. **/ VOID EFIAPI JumpToKernel64 ( IN VOID *KernelStart, IN VOID *KernelBootParams ); /** Return the memory map info HOB data. @retval Pointer to the memory map info hob. NULL if HOB is not found. **/ STATIC MEMORY_MAP_INFO * EFIAPI GetMemoryMapInfo ( VOID ) { EFI_HOB_GUID_TYPE *GuidHob; GuidHob = GetNextGuidHob (&gLoaderMemoryMapInfoGuid, GetHobList()); if (GuidHob == NULL) { ASSERT (GuidHob); return NULL; } return (MEMORY_MAP_INFO *)GET_GUID_HOB_DATA (GuidHob); } /** Dump kernel boot parameters. @param[in] Bp Pointer to boot parameter structure. **/ VOID DumpLinuxBootParams ( IN BOOT_PARAMS *Bp ) { IN SETUP_HEADER *Header; if (Bp == NULL) { return; } Header = &Bp->Hdr; DEBUG ((DEBUG_INFO, "\n\n============ KERNEL SETUP ============\n")); DEBUG ((DEBUG_INFO, "SetupSectorss: 0x%x\n", Header->SetupSectorss)); DEBUG ((DEBUG_INFO, "RootFlags: 0x%x\n", Header->RootFlags)); DEBUG ((DEBUG_INFO, "SysSize: 0x%x\n", Header->SysSize)); DEBUG ((DEBUG_INFO, "RamSize: 0x%x\n", Header->RamSize)); DEBUG ((DEBUG_INFO, "VideoMode: 0x%x\n", Header->VideoMode)); DEBUG ((DEBUG_INFO, "RootDev: 0x%x\n", Header->RootDev)); DEBUG ((DEBUG_INFO, "Signature: 0x%x\n", Header->Signature)); DEBUG ((DEBUG_INFO, "Jump: 0x%x\n", Header->Jump)); DEBUG ((DEBUG_INFO, "Header: 0x%x\n", Header->Header)); DEBUG ((DEBUG_INFO, "Version: 0x%x\n", Header->Version)); DEBUG ((DEBUG_INFO, "SuSwitch: 0x%x\n", Header->SuSwitch)); DEBUG ((DEBUG_INFO, "SetupSeg: 0x%x\n", Header->SetupSeg)); DEBUG ((DEBUG_INFO, "StartSys: 0x%x\n", Header->StartSys)); DEBUG ((DEBUG_INFO, "KernalVer: 0x%x\n", Header->KernalVer)); DEBUG ((DEBUG_INFO, "LoaderId: 0x%x\n", Header->LoaderId)); DEBUG ((DEBUG_INFO, "LoadFlags: 0x%x\n", Header->LoadFlags)); DEBUG ((DEBUG_INFO, "MoveSize: 0x%x\n", Header->MoveSize)); DEBUG ((DEBUG_INFO, "Code32Start: 0x%x\n", Header->Code32Start)); DEBUG ((DEBUG_INFO, "BootsectorKludge: 0x%x\n", Header->BootsectorKludge)); DEBUG ((DEBUG_INFO, "HeapEnd: 0x%x\n", Header->HeapEnd)); DEBUG ((DEBUG_INFO, "ExtLoaderVer: 0x%x\n", Header->ExtLoaderVer)); DEBUG ((DEBUG_INFO, "ExtLoaderType: 0x%x\n", Header->ExtLoaderType)); DEBUG ((DEBUG_INFO, "CmdlineSize: 0x%x\n", Header->CmdlineSize)); DEBUG ((DEBUG_INFO, "CmdLinePtr: 0x%x\n", Header->CmdLinePtr)); DEBUG ((DEBUG_INFO, "Cmd Args: %a\n", (CHAR8 *)(UINTN)Header->CmdLinePtr)); DEBUG ((DEBUG_INFO, "RamDiskStart: 0x%x\n", Header->RamDiskStart)); DEBUG ((DEBUG_INFO, "RamDisklen: 0x%x\n", Header->RamDisklen)); DEBUG ((DEBUG_INFO, "RamDiskMax: 0x%x\n", Header->RamDiskMax)); DEBUG ((DEBUG_INFO, "KernelAlignment: 0x%x\n", Header->KernelAlignment)); DEBUG ((DEBUG_INFO, "RelocatableKernel: 0x%x\n", Header->RelocatableKernel)); DEBUG ((DEBUG_INFO, "MinAlignment: 0x%x\n", Header->MinAlignment)); DEBUG ((DEBUG_INFO, "XloadFlags: 0x%x\n", Header->XloadFlags)); DEBUG ((DEBUG_INFO, "HardwareSubarch: 0x%x\n", Header->HardwareSubarch)); DEBUG ((DEBUG_INFO, "HardwareSubarchData: 0x%x\n", Header->HardwareSubarchData)); DEBUG ((DEBUG_INFO, "PayloadOffset: 0x%x\n", Header->PayloadOffset)); DEBUG ((DEBUG_INFO, "PayloadLength: 0x%x\n", Header->PayloadLength)); DEBUG ((DEBUG_INFO, "SetupData: 0x%x\n", Header->SetupData)); DEBUG ((DEBUG_INFO, "PrefAddress: 0x%x\n", Header->PrefAddress)); DEBUG ((DEBUG_INFO, "InitSize: 0x%x\n", Header->InitSize)); DEBUG ((DEBUG_INFO, "HandoverOffset: 0x%x\n\n", Header->HandoverOffset)); } /** Return kernel boot parameters. @retval Pointer to boot parameter structure. **/ BOOT_PARAMS * EFIAPI GetLinuxBootParams ( VOID ) { BOOT_PARAMS *Bp; Bp = (BOOT_PARAMS *) (UINTN)BOOT_PARAMS_BASE; return Bp; } /** Check if the image is a bootable Linux image. @param[in] ImageBase Memory address of an image @retval TRUE Image is a bootable kernel image @retval FALSE Not a bootable kernel image **/ BOOLEAN EFIAPI IsBzImage ( IN CONST VOID *ImageBase ) { BOOT_PARAMS *Bp; Bp = (BOOT_PARAMS *) ImageBase; if (Bp == NULL) { return FALSE; } // Check boot sector Signature if ((Bp->Hdr.Signature != 0xAA55) || (Bp->Hdr.Header != SETUP_HDR)) { DEBUG ((DEBUG_ERROR, "This image is not in bzimage format\n")); return FALSE; } DEBUG ((DEBUG_INFO, "Found bzimage Signature\n")); return TRUE; } /** Load linux kernel image to specified address and setup boot parameters. @param[in] KernelBase Memory address of an kernel image. @param[in] InitRdBase Memory address of an InitRd image. @param[in] InitRdLen InitRd image size. @param[in] CmdLineBase Memory address of command line buffer. @param[in] CmdLineLen Command line buffer size. @retval EFI_INVALID_PARAMETER Input parameters are not valid. @retval EFI_UNSUPPORTED Unsupported binary type. @retval EFI_SUCCESS Kernel is loaded successfully. **/ EFI_STATUS EFIAPI LoadBzImage ( IN CONST VOID *KernelBase, IN CONST VOID *InitRdBase, IN UINT32 InitRdLen, IN CONST VOID *CmdLineBase, IN UINT32 CmdLineLen ) { BOOT_PARAMS *Bp; BOOT_PARAMS *BaseBp; VOID *KernelBuf; UINT32 BootParamSize; UINTN KernelSize; VOID CONST *ImageBase; ImageBase = KernelBase; if (ImageBase == NULL) { return EFI_INVALID_PARAMETER; } if (!IsBzImage (ImageBase)) { return EFI_UNSUPPORTED; } BaseBp = (BOOT_PARAMS *) ImageBase; Bp = GetLinuxBootParams (); ZeroMem ((VOID *)Bp, sizeof (BOOT_PARAMS)); CopyMem (&Bp->Hdr, &BaseBp->Hdr, sizeof (SETUP_HEADER)); if (Bp->Hdr.SetupSectorss != 0) { BootParamSize = (Bp->Hdr.SetupSectorss + 1) * 512; } else { BootParamSize = 5 * 512; } KernelBuf = (VOID *) (UINTN)LINUX_KERNEL_BASE; KernelSize = (UINTN)Bp->Hdr.SysSize * 16; CopyMem (KernelBuf, (UINT8 *)ImageBase + BootParamSize, KernelSize); // // Update boot params // Bp->Hdr.LoaderId = 0xff; Bp->Hdr.CmdLinePtr = (UINT32)(UINTN)CmdLineBase; Bp->Hdr.CmdlineSize = CmdLineLen; Bp->Hdr.RamDiskStart = (UINT32)(UINTN)InitRdBase; Bp->Hdr.RamDisklen = InitRdLen; return EFI_SUCCESS; } /** Update linux kernel boot parameters. @param[in] Bp BootParams address to be updated **/ VOID EFIAPI UpdateLinuxBootParams ( IN BOOT_PARAMS *Bp ) { EFI_HOB_GUID_TYPE *GuidHob; EFI_PEI_GRAPHICS_INFO_HOB *GfxInfoHob; UINTN MemoryMapSize; E820_ENTRY *E820Entry; MEMORY_MAP_INFO *MapInfo; UINTN Index; EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *GfxMode; SYSTEM_TABLE_INFO *SystemTableInfo; CHAR8 ParamValue[64]; if (Bp == NULL) { return; } // // Get graphics data // GuidHob = GetFirstGuidHob (&gEfiGraphicsInfoHobGuid); if (GuidHob != NULL) { GfxInfoHob = (EFI_PEI_GRAPHICS_INFO_HOB *)GET_GUID_HOB_DATA (GuidHob); ZeroMem (&Bp->ScreenInfo, sizeof (Bp->ScreenInfo)); GfxMode = &GfxInfoHob->GraphicsMode; if (GfxMode->PixelFormat == PixelRedGreenBlueReserved8BitPerColor) { Bp->ScreenInfo.RedPos = 0; Bp->ScreenInfo.BluePos = 16; } else if (GfxMode->PixelFormat == PixelBlueGreenRedReserved8BitPerColor) { Bp->ScreenInfo.RedPos = 16; Bp->ScreenInfo.BluePos = 0; } else { // Unsupported format GfxMode = NULL; } if (GfxMode != NULL) { Bp->ScreenInfo.OrigVideoIsVGA = VIDEO_TYPE_EFI; Bp->ScreenInfo.LfbLinelength = (UINT16) (GfxMode->PixelsPerScanLine * 4); Bp->ScreenInfo.LfbDepth = 32; Bp->ScreenInfo.LfbBase = (UINT32)(UINTN)GfxInfoHob->FrameBufferBase; Bp->ScreenInfo.ExtLfbBase = (UINT32)RShiftU64 (GfxInfoHob->FrameBufferBase, 32); Bp->ScreenInfo.Capabilities |= VIDEO_CAPABILITY_64BIT_BASE; Bp->ScreenInfo.LfbWidth = (UINT16)GfxMode->HorizontalResolution; Bp->ScreenInfo.LfbHeight = (UINT16)GfxMode->VerticalResolution; Bp->ScreenInfo.Pages = 1; Bp->ScreenInfo.RedSize = 8; Bp->ScreenInfo.GreenSize = 8; Bp->ScreenInfo.BlueSize = 8; Bp->ScreenInfo.RsvdSize = 8; Bp->ScreenInfo.GreenPos = 8; Bp->ScreenInfo.RsvdPos = 24; } } // Get memory map E820Entry = &Bp->E820Map[0]; MemoryMapSize = (UINTN)ARRAY_SIZE (Bp->E820Map); MapInfo = GetMemoryMapInfo (); for (Index = 0; Index < MapInfo->Count && Index < MemoryMapSize; Index++) { E820Entry->Type = (UINT32)MapInfo->Entry[Index].Type; E820Entry->Addr = MapInfo->Entry[Index].Base; E820Entry->Size = MapInfo->Entry[Index].Size; E820Entry++; } Bp->E820Entries = (UINT8)MapInfo->Count; // // Append acpi_rsdp only if it does not exist in the kernel command line // to allow a user override acpi_rdsp kernel parameter. // To check the existence, simply search for "acpi_rsdp=" string since it's // case-sensitive with the immediate '=' trailing according to kernel spec. // GuidHob = GetFirstGuidHob (&gLoaderSystemTableInfoGuid); if (GuidHob != NULL) { SystemTableInfo = (SYSTEM_TABLE_INFO *)GET_GUID_HOB_DATA (GuidHob); Bp->AcpiRsdpAddr = SystemTableInfo->AcpiTableBase; if (Bp->Hdr.Version < 0x020E) { // Bp.AcpiRsdpAddr was only added in Linux boot protocol 2.14 // For old version, just append "acpi_rsdp=" instead. if (AsciiStrStr ((CHAR8 *)(UINTN)Bp->Hdr.CmdLinePtr, (CHAR8 *)ACPI_RSDP_CMDLINE_STR) == NULL) { AsciiSPrint (ParamValue, sizeof (ParamValue), " %a0x%lx", ACPI_RSDP_CMDLINE_STR, SystemTableInfo->AcpiTableBase); AsciiStrCatS ((CHAR8 *)(UINTN)Bp->Hdr.CmdLinePtr, CMDLINE_LENGTH_MAX, ParamValue); Bp->Hdr.CmdlineSize = (UINT32)AsciiStrLen ((CHAR8 *)(UINTN)Bp->Hdr.CmdLinePtr); } } } } /** Load linux kernel image to specified address and setup boot parameters. @param[in] HobList HOB list pointer. @param[in] Params Extra parameters. Not used for now. **/ VOID EFIAPI LinuxBoot ( IN VOID *HobList, IN VOID *Params ) { BOOT_PARAMS *Bp; UINTN KernelStart; Bp = GetLinuxBootParams (); if (Bp != NULL) { UpdateLinuxBootParams (Bp); KernelStart = (UINTN)Bp->Hdr.Code32Start; if (!IsLongModeEnabled ()) { if (IsLongModeSupported () && ((Bp->Hdr.XloadFlags & BIT0) == BIT0)) { KernelStart += 0x200; DEBUG ((DEBUG_INFO, "Switch to LongMode and jump to 64-bit kernel entrypoint ...\n")); JumpToLongMode ((UINT64)(UINTN)JumpToKernel64, (UINT64)KernelStart, (UINT64)(UINTN)Bp, (UINT64)(UINTN)HobList); } else { JumpToKernel ((VOID *)KernelStart, Bp); } } else { if ((Bp->Hdr.XloadFlags & BIT0) == BIT0) { DEBUG ((DEBUG_INFO, "Jump to 64-bit kernel entrypoint ...\n")); KernelStart += 0x200; JumpToKernel ((VOID *)KernelStart, Bp); } else { // In long mode already, but kernel is not 64-bit DEBUG ((DEBUG_ERROR, "Unsupported kernel mode !\n")); } } } CpuDeadLoop (); }