/** @file Copyright (c) 2011 - 2019 Intel Corporation. All rights reserved.
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 ((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 >= (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; }