| /** @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%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 (( | |
| 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 >= 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 ((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; | |
| } | |