mirror of https://github.com/acidanthera/audk.git
670 lines
16 KiB
C
670 lines
16 KiB
C
/** @file
|
|
|
|
Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.<BR>
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "LoadLinuxLib.h"
|
|
|
|
/**
|
|
A simple check of the kernel setup image
|
|
|
|
An assumption is made that the size of the data is at least the
|
|
size of struct boot_params.
|
|
|
|
@param[in] KernelSetup - The kernel setup image
|
|
|
|
@retval EFI_SUCCESS - The kernel setup looks valid and supported
|
|
@retval EFI_INVALID_PARAMETER - KernelSetup was NULL
|
|
@retval EFI_UNSUPPORTED - The kernel setup is not valid or supported
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BasicKernelSetupCheck (
|
|
IN VOID *KernelSetup
|
|
)
|
|
{
|
|
return LoadLinuxCheckKernelSetup (KernelSetup, sizeof (struct boot_params));
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
LoadLinuxCheckKernelSetup (
|
|
IN VOID *KernelSetup,
|
|
IN UINTN KernelSetupSize
|
|
)
|
|
{
|
|
struct boot_params *Bp;
|
|
|
|
if (KernelSetup == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (KernelSetupSize < sizeof (*Bp)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
Bp = (struct boot_params *)KernelSetup;
|
|
|
|
if ((Bp->hdr.signature != 0xAA55) || // Check boot sector signature
|
|
(Bp->hdr.header != SETUP_HDR) ||
|
|
(Bp->hdr.version < 0x205) || // We only support relocatable kernels
|
|
(!Bp->hdr.relocatable_kernel)
|
|
)
|
|
{
|
|
return EFI_UNSUPPORTED;
|
|
} else {
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
UINTN
|
|
EFIAPI
|
|
LoadLinuxGetKernelSize (
|
|
IN VOID *KernelSetup,
|
|
IN UINTN KernelSize
|
|
)
|
|
{
|
|
struct boot_params *Bp;
|
|
|
|
if (EFI_ERROR (BasicKernelSetupCheck (KernelSetup))) {
|
|
return 0;
|
|
}
|
|
|
|
Bp = (struct boot_params *)KernelSetup;
|
|
|
|
if (Bp->hdr.version > 0x20a) {
|
|
return Bp->hdr.init_size;
|
|
} else {
|
|
//
|
|
// Add extra size for kernel decompression
|
|
//
|
|
return 3 * KernelSize;
|
|
}
|
|
}
|
|
|
|
VOID *
|
|
EFIAPI
|
|
LoadLinuxAllocateKernelSetupPages (
|
|
IN UINTN Pages
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PHYSICAL_ADDRESS Address;
|
|
|
|
Address = BASE_1GB;
|
|
Status = gBS->AllocatePages (
|
|
AllocateMaxAddress,
|
|
EfiLoaderData,
|
|
Pages,
|
|
&Address
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
return (VOID *)(UINTN)Address;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
LoadLinuxInitializeKernelSetup (
|
|
IN VOID *KernelSetup
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN SetupEnd;
|
|
struct boot_params *Bp;
|
|
|
|
Status = BasicKernelSetupCheck (KernelSetup);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Bp = (struct boot_params *)KernelSetup;
|
|
|
|
SetupEnd = 0x202 + (Bp->hdr.jump & 0xff);
|
|
|
|
//
|
|
// Clear all but the setup_header
|
|
//
|
|
ZeroMem (KernelSetup, 0x1f1);
|
|
ZeroMem (((UINT8 *)KernelSetup) + SetupEnd, 4096 - SetupEnd);
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"Cleared kernel setup 0-0x1f1, 0x%Lx-0x1000\n",
|
|
(UINT64)SetupEnd
|
|
));
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
VOID *
|
|
EFIAPI
|
|
LoadLinuxAllocateKernelPages (
|
|
IN VOID *KernelSetup,
|
|
IN UINTN Pages
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PHYSICAL_ADDRESS KernelAddress;
|
|
UINT32 Loop;
|
|
struct boot_params *Bp;
|
|
|
|
if (EFI_ERROR (BasicKernelSetupCheck (KernelSetup))) {
|
|
return NULL;
|
|
}
|
|
|
|
Bp = (struct boot_params *)KernelSetup;
|
|
|
|
for (Loop = 1; Loop < 512; Loop++) {
|
|
KernelAddress = MultU64x32 (
|
|
2 * Bp->hdr.kernel_alignment,
|
|
Loop
|
|
);
|
|
Status = gBS->AllocatePages (
|
|
AllocateAddress,
|
|
EfiLoaderData,
|
|
Pages,
|
|
&KernelAddress
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
return (VOID *)(UINTN)KernelAddress;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
VOID *
|
|
EFIAPI
|
|
LoadLinuxAllocateCommandLinePages (
|
|
IN UINTN Pages
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PHYSICAL_ADDRESS Address;
|
|
|
|
Address = 0xa0000;
|
|
Status = gBS->AllocatePages (
|
|
AllocateMaxAddress,
|
|
EfiLoaderData,
|
|
Pages,
|
|
&Address
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
return (VOID *)(UINTN)Address;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
VOID *
|
|
EFIAPI
|
|
LoadLinuxAllocateInitrdPages (
|
|
IN VOID *KernelSetup,
|
|
IN UINTN Pages
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PHYSICAL_ADDRESS Address;
|
|
|
|
struct boot_params *Bp;
|
|
|
|
if (EFI_ERROR (BasicKernelSetupCheck (KernelSetup))) {
|
|
return NULL;
|
|
}
|
|
|
|
Bp = (struct boot_params *)KernelSetup;
|
|
|
|
Address = (EFI_PHYSICAL_ADDRESS)(UINTN)Bp->hdr.ramdisk_max;
|
|
Status = gBS->AllocatePages (
|
|
AllocateMaxAddress,
|
|
EfiLoaderData,
|
|
Pages,
|
|
&Address
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
return (VOID *)(UINTN)Address;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
SetupLinuxMemmap (
|
|
IN OUT struct boot_params *Bp
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 TmpMemoryMap[1];
|
|
UINTN MapKey;
|
|
UINTN DescriptorSize;
|
|
UINT32 DescriptorVersion;
|
|
UINTN MemoryMapSize;
|
|
EFI_MEMORY_DESCRIPTOR *MemoryMap;
|
|
EFI_MEMORY_DESCRIPTOR *MemoryMapPtr;
|
|
UINTN Index;
|
|
struct efi_info *Efi;
|
|
struct e820_entry *LastE820;
|
|
struct e820_entry *E820;
|
|
UINTN E820EntryCount;
|
|
EFI_PHYSICAL_ADDRESS LastEndAddr;
|
|
|
|
//
|
|
// Get System MemoryMapSize
|
|
//
|
|
MemoryMapSize = sizeof (TmpMemoryMap);
|
|
Status = gBS->GetMemoryMap (
|
|
&MemoryMapSize,
|
|
(EFI_MEMORY_DESCRIPTOR *)TmpMemoryMap,
|
|
&MapKey,
|
|
&DescriptorSize,
|
|
&DescriptorVersion
|
|
);
|
|
ASSERT (Status == EFI_BUFFER_TOO_SMALL);
|
|
//
|
|
// Enlarge space here, because we will allocate pool now.
|
|
//
|
|
MemoryMapSize += EFI_PAGE_SIZE;
|
|
Status = gBS->AllocatePool (
|
|
EfiLoaderData,
|
|
MemoryMapSize,
|
|
(VOID **)&MemoryMap
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// Get System MemoryMap
|
|
//
|
|
Status = gBS->GetMemoryMap (
|
|
&MemoryMapSize,
|
|
MemoryMap,
|
|
&MapKey,
|
|
&DescriptorSize,
|
|
&DescriptorVersion
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
LastE820 = NULL;
|
|
E820 = &Bp->e820_map[0];
|
|
E820EntryCount = 0;
|
|
LastEndAddr = 0;
|
|
MemoryMapPtr = MemoryMap;
|
|
for (Index = 0; Index < (MemoryMapSize / DescriptorSize); Index++) {
|
|
UINTN E820Type = 0;
|
|
|
|
if (MemoryMap->NumberOfPages == 0) {
|
|
continue;
|
|
}
|
|
|
|
switch (MemoryMap->Type) {
|
|
case EfiReservedMemoryType:
|
|
case EfiRuntimeServicesCode:
|
|
case EfiRuntimeServicesData:
|
|
case EfiMemoryMappedIO:
|
|
case EfiMemoryMappedIOPortSpace:
|
|
case EfiPalCode:
|
|
E820Type = E820_RESERVED;
|
|
break;
|
|
|
|
case EfiUnusableMemory:
|
|
E820Type = E820_UNUSABLE;
|
|
break;
|
|
|
|
case EfiACPIReclaimMemory:
|
|
E820Type = E820_ACPI;
|
|
break;
|
|
|
|
case EfiLoaderCode:
|
|
case EfiLoaderData:
|
|
case EfiBootServicesCode:
|
|
case EfiBootServicesData:
|
|
case EfiConventionalMemory:
|
|
E820Type = E820_RAM;
|
|
break;
|
|
|
|
case EfiACPIMemoryNVS:
|
|
E820Type = E820_NVS;
|
|
break;
|
|
|
|
default:
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"Invalid EFI memory descriptor type (0x%x)!\n",
|
|
MemoryMap->Type
|
|
));
|
|
continue;
|
|
}
|
|
|
|
if ((LastE820 != NULL) &&
|
|
(LastE820->type == (UINT32)E820Type) &&
|
|
(MemoryMap->PhysicalStart == LastEndAddr))
|
|
{
|
|
LastE820->size += EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages);
|
|
LastEndAddr += EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages);
|
|
} else {
|
|
if (E820EntryCount >= ARRAY_SIZE (Bp->e820_map)) {
|
|
break;
|
|
}
|
|
|
|
E820->type = (UINT32)E820Type;
|
|
E820->addr = MemoryMap->PhysicalStart;
|
|
E820->size = EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages);
|
|
LastE820 = E820;
|
|
LastEndAddr = E820->addr + E820->size;
|
|
E820++;
|
|
E820EntryCount++;
|
|
}
|
|
|
|
//
|
|
// Get next item
|
|
//
|
|
MemoryMap = (EFI_MEMORY_DESCRIPTOR *)((UINTN)MemoryMap + DescriptorSize);
|
|
}
|
|
|
|
Bp->e820_entries = (UINT8)E820EntryCount;
|
|
|
|
Efi = &Bp->efi_info;
|
|
Efi->efi_systab = (UINT32)(UINTN)gST;
|
|
Efi->efi_memdesc_size = (UINT32)DescriptorSize;
|
|
Efi->efi_memdesc_version = DescriptorVersion;
|
|
Efi->efi_memmap = (UINT32)(UINTN)MemoryMapPtr;
|
|
Efi->efi_memmap_size = (UINT32)MemoryMapSize;
|
|
#ifdef MDE_CPU_IA32
|
|
Efi->efi_loader_signature = SIGNATURE_32 ('E', 'L', '3', '2');
|
|
#else
|
|
Efi->efi_systab_hi = (UINT32)(((UINT64)(UINTN)gST) >> 32);
|
|
Efi->efi_memmap_hi = (UINT32)(((UINT64)(UINTN)MemoryMapPtr) >> 32);
|
|
Efi->efi_loader_signature = SIGNATURE_32 ('E', 'L', '6', '4');
|
|
#endif
|
|
|
|
gBS->ExitBootServices (gImageHandle, MapKey);
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
LoadLinuxSetCommandLine (
|
|
IN OUT VOID *KernelSetup,
|
|
IN CHAR8 *CommandLine
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
struct boot_params *Bp;
|
|
|
|
Status = BasicKernelSetupCheck (KernelSetup);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Bp = (struct boot_params *)KernelSetup;
|
|
|
|
Bp->hdr.cmd_line_ptr = (UINT32)(UINTN)CommandLine;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
LoadLinuxSetInitrd (
|
|
IN OUT VOID *KernelSetup,
|
|
IN VOID *Initrd,
|
|
IN UINTN InitrdSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
struct boot_params *Bp;
|
|
|
|
Status = BasicKernelSetupCheck (KernelSetup);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Bp = (struct boot_params *)KernelSetup;
|
|
|
|
Bp->hdr.ramdisk_start = (UINT32)(UINTN)Initrd;
|
|
Bp->hdr.ramdisk_len = (UINT32)InitrdSize;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC VOID
|
|
FindBits (
|
|
unsigned long Mask,
|
|
UINT8 *Pos,
|
|
UINT8 *Size
|
|
)
|
|
{
|
|
UINT8 First, Len;
|
|
|
|
First = 0;
|
|
Len = 0;
|
|
|
|
if (Mask) {
|
|
while (!(Mask & 0x1)) {
|
|
Mask = Mask >> 1;
|
|
First++;
|
|
}
|
|
|
|
while (Mask & 0x1) {
|
|
Mask = Mask >> 1;
|
|
Len++;
|
|
}
|
|
}
|
|
|
|
*Pos = First;
|
|
*Size = Len;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
SetupGraphicsFromGop (
|
|
struct screen_info *Si,
|
|
EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop
|
|
)
|
|
{
|
|
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
|
|
EFI_STATUS Status;
|
|
UINTN Size;
|
|
|
|
Status = Gop->QueryMode (Gop, Gop->Mode->Mode, &Size, &Info);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
/* We found a GOP */
|
|
|
|
/* EFI framebuffer */
|
|
Si->orig_video_isVGA = 0x70;
|
|
|
|
Si->orig_x = 0;
|
|
Si->orig_y = 0;
|
|
Si->orig_video_page = 0;
|
|
Si->orig_video_mode = 0;
|
|
Si->orig_video_cols = 0;
|
|
Si->orig_video_lines = 0;
|
|
Si->orig_video_ega_bx = 0;
|
|
Si->orig_video_points = 0;
|
|
|
|
Si->lfb_base = (UINT32)Gop->Mode->FrameBufferBase;
|
|
Si->lfb_size = (UINT32)Gop->Mode->FrameBufferSize;
|
|
Si->lfb_width = (UINT16)Info->HorizontalResolution;
|
|
Si->lfb_height = (UINT16)Info->VerticalResolution;
|
|
Si->pages = 1;
|
|
Si->vesapm_seg = 0;
|
|
Si->vesapm_off = 0;
|
|
|
|
if (Info->PixelFormat == PixelRedGreenBlueReserved8BitPerColor) {
|
|
Si->lfb_depth = 32;
|
|
Si->red_size = 8;
|
|
Si->red_pos = 0;
|
|
Si->green_size = 8;
|
|
Si->green_pos = 8;
|
|
Si->blue_size = 8;
|
|
Si->blue_pos = 16;
|
|
Si->rsvd_size = 8;
|
|
Si->rsvd_pos = 24;
|
|
Si->lfb_linelength = (UINT16)(Info->PixelsPerScanLine * 4);
|
|
} else if (Info->PixelFormat == PixelBlueGreenRedReserved8BitPerColor) {
|
|
Si->lfb_depth = 32;
|
|
Si->red_size = 8;
|
|
Si->red_pos = 16;
|
|
Si->green_size = 8;
|
|
Si->green_pos = 8;
|
|
Si->blue_size = 8;
|
|
Si->blue_pos = 0;
|
|
Si->rsvd_size = 8;
|
|
Si->rsvd_pos = 24;
|
|
Si->lfb_linelength = (UINT16)(Info->PixelsPerScanLine * 4);
|
|
} else if (Info->PixelFormat == PixelBitMask) {
|
|
FindBits (
|
|
Info->PixelInformation.RedMask,
|
|
&Si->red_pos,
|
|
&Si->red_size
|
|
);
|
|
FindBits (
|
|
Info->PixelInformation.GreenMask,
|
|
&Si->green_pos,
|
|
&Si->green_size
|
|
);
|
|
FindBits (
|
|
Info->PixelInformation.BlueMask,
|
|
&Si->blue_pos,
|
|
&Si->blue_size
|
|
);
|
|
FindBits (
|
|
Info->PixelInformation.ReservedMask,
|
|
&Si->rsvd_pos,
|
|
&Si->rsvd_size
|
|
);
|
|
Si->lfb_depth = Si->red_size + Si->green_size +
|
|
Si->blue_size + Si->rsvd_size;
|
|
Si->lfb_linelength = (UINT16)((Info->PixelsPerScanLine * Si->lfb_depth) / 8);
|
|
} else {
|
|
Si->lfb_depth = 4;
|
|
Si->red_size = 0;
|
|
Si->red_pos = 0;
|
|
Si->green_size = 0;
|
|
Si->green_pos = 0;
|
|
Si->blue_size = 0;
|
|
Si->blue_pos = 0;
|
|
Si->rsvd_size = 0;
|
|
Si->rsvd_pos = 0;
|
|
Si->lfb_linelength = Si->lfb_width / 2;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
SetupGraphics (
|
|
IN OUT struct boot_params *Bp
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE *HandleBuffer;
|
|
UINTN HandleCount;
|
|
UINTN Index;
|
|
EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
|
|
|
|
ZeroMem ((VOID *)&Bp->screen_info, sizeof (Bp->screen_info));
|
|
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiGraphicsOutputProtocolGuid,
|
|
NULL,
|
|
&HandleCount,
|
|
&HandleBuffer
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
for (Index = 0; Index < HandleCount; Index++) {
|
|
Status = gBS->HandleProtocol (
|
|
HandleBuffer[Index],
|
|
&gEfiGraphicsOutputProtocolGuid,
|
|
(VOID *)&Gop
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
Status = SetupGraphicsFromGop (&Bp->screen_info, Gop);
|
|
if (!EFI_ERROR (Status)) {
|
|
FreePool (HandleBuffer);
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
FreePool (HandleBuffer);
|
|
}
|
|
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
SetupLinuxBootParams (
|
|
IN OUT struct boot_params *Bp
|
|
)
|
|
{
|
|
SetupGraphics (Bp);
|
|
|
|
SetupLinuxMemmap (Bp);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
LoadLinux (
|
|
IN VOID *Kernel,
|
|
IN OUT VOID *KernelSetup
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
struct boot_params *Bp;
|
|
|
|
Status = BasicKernelSetupCheck (KernelSetup);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Bp = (struct boot_params *)KernelSetup;
|
|
|
|
if ((Bp->hdr.version < 0x205) || !Bp->hdr.relocatable_kernel) {
|
|
//
|
|
// We only support relocatable kernels
|
|
//
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
InitLinuxDescriptorTables ();
|
|
|
|
Bp->hdr.code32_start = (UINT32)(UINTN)Kernel;
|
|
if ((Bp->hdr.version >= 0x20c) && Bp->hdr.handover_offset &&
|
|
(Bp->hdr.xloadflags & ((sizeof (UINTN) == 4) ? BIT2 : BIT3)))
|
|
{
|
|
DEBUG ((DEBUG_INFO, "Jumping to kernel EFI handover point at ofs %x\n", Bp->hdr.handover_offset));
|
|
|
|
DisableInterrupts ();
|
|
JumpToUefiKernel ((VOID *)gImageHandle, (VOID *)gST, KernelSetup, Kernel);
|
|
}
|
|
|
|
//
|
|
// Old kernels without EFI handover protocol
|
|
//
|
|
SetupLinuxBootParams (KernelSetup);
|
|
|
|
DEBUG ((DEBUG_INFO, "Jumping to kernel\n"));
|
|
DisableInterrupts ();
|
|
SetLinuxDescriptorTables ();
|
|
JumpToKernel (Kernel, (VOID *)KernelSetup);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|