/** @file | |
Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#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 ((EFI_D_INFO, "Cleared kernel setup 0-0x1f1, 0x%x-0x1000\n", 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 (( | |
EFI_D_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 >= (sizeof (Bp->e820_map) / sizeof (Bp->e820_map[0]))) { | |
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 ((EFI_D_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 ((EFI_D_INFO, "Jumping to kernel\n")); | |
DisableInterrupts (); | |
SetLinuxDescriptorTables (); | |
JumpToKernel (Kernel, (VOID*) KernelSetup); | |
return EFI_SUCCESS; | |
} | |