summaryrefslogtreecommitdiff
path: root/shim.c
diff options
context:
space:
mode:
authorMatthew Garrett <mjg@redhat.com>2012-04-11 13:59:55 -0400
committerMatthew Garrett <mjg@redhat.com>2012-04-11 13:59:55 -0400
commitb2fe1780947117d5c6b6b79ccf36d73d1f889977 (patch)
tree327c955373c4bc8ffe600243c4a5212cc16c997b /shim.c
downloadefi-boot-shim-b2fe1780947117d5c6b6b79ccf36d73d1f889977.tar.gz
efi-boot-shim-b2fe1780947117d5c6b6b79ccf36d73d1f889977.zip
Initial commit
Diffstat (limited to 'shim.c')
-rw-r--r--shim.c335
1 files changed, 335 insertions, 0 deletions
diff --git a/shim.c b/shim.c
new file mode 100644
index 00000000..2f5956c9
--- /dev/null
+++ b/shim.c
@@ -0,0 +1,335 @@
+/* Read path */
+/* Load real bootloader */
+/* Check signature */
+/* Relocate real bootloader */
+/* Jump to real bootloader */
+
+#include <efi.h>
+#include <efilib.h>
+#include "PeImage.h"
+
+EFI_SYSTEM_TABLE *systab;
+EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table);
+
+EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, int *grubsize)
+{
+ EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
+ EFI_GUID simple_file_system_protocol = SIMPLE_FILE_SYSTEM_PROTOCOL;
+ EFI_GUID file_info_id = EFI_FILE_INFO_ID;
+ EFI_STATUS efi_status;
+ EFI_LOADED_IMAGE *li;
+ EFI_FILE_IO_INTERFACE *drive;
+ EFI_FILE_INFO *fileinfo = NULL;
+ EFI_FILE *root, *grub;
+ FILEPATH_DEVICE_PATH *FilePath;
+ CHAR16 *PathName;
+ EFI_HANDLE device;
+ unsigned int buffersize = 0;
+
+ efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle,
+ &loaded_image_protocol, &li);
+
+ if (efi_status != EFI_SUCCESS)
+ return efi_status;
+
+ if (DevicePathType(li->FilePath) != MEDIA_DEVICE_PATH ||
+ DevicePathSubType(li->FilePath) != MEDIA_FILEPATH_DP)
+ return EFI_NOT_FOUND;
+
+ FilePath = (FILEPATH_DEVICE_PATH *)li->FilePath;
+
+ PathName = AllocatePool(StrLen(FilePath->PathName) + StrLen(L"grub.efi"));
+
+ if (!PathName)
+ return EFI_LOAD_ERROR;
+
+ StrCpy(PathName, FilePath->PathName);
+
+ StrCat(PathName, L"grub.efi");
+
+ device = li->DeviceHandle;
+
+ efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device,
+ &simple_file_system_protocol, &drive);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to find fs\n");
+ return efi_status;
+ }
+
+ efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to open fs\n");
+ return efi_status;
+ }
+
+ efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, PathName,
+ EFI_FILE_MODE_READ, 0);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to open grub\n");
+ return efi_status;
+ }
+
+ efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id,
+ &buffersize, fileinfo);
+
+ if (efi_status == EFI_BUFFER_TOO_SMALL) {
+ fileinfo = AllocatePool(buffersize);
+ efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub,
+ &file_info_id, &buffersize,
+ fileinfo);
+ }
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Unable to get file info\n");
+ return efi_status;
+ }
+
+ buffersize = fileinfo->FileSize;
+ *grubdata = AllocatePool(buffersize);
+
+ efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize,
+ *grubdata);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Unexpected return from initial read\n");
+ return efi_status;
+ }
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Unable to read grub\n");
+ return efi_status;
+ }
+
+ *grubsize = buffersize;
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS read_header(void *grubdata, PE_COFF_LOADER_IMAGE_CONTEXT *context)
+{
+ EFI_IMAGE_DOS_HEADER *DosHdr = grubdata;
+ EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = grubdata;
+
+ if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE)
+ PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)grubdata + DosHdr->e_lfanew);
+
+ if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) {
+ Print(L"Unsupported image type\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ if (PEHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) {
+ Print(L"Unsupported image - Relocations have been stripped\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ if (PEHdr->Pe32.OptionalHeader.Magic != EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ Print(L"Only 64-bit images supported\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ context->ImageAddress = PEHdr->Pe32Plus.OptionalHeader.ImageBase;
+ PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)grubdata;
+ context->ImageSize = (UINT64)PEHdr->Pe32Plus.OptionalHeader.SizeOfImage;
+ context->SizeOfHeaders = PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders;
+ context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint;
+ context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
+ context->NumberOfRvaAndSizes = PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes;
+ context->NumberOfSections = PEHdr->Pe32.FileHeader.NumberOfSections;
+ context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)((char *)PEHdr + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER));
+
+ return EFI_SUCCESS;
+}
+
+void *ImageAddress (void *image, int size, unsigned int address)
+{
+ if (address > size)
+ return 0;
+
+ return image + address;
+}
+
+EFI_STATUS relocate_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, void *grubdata)
+{
+ EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd;
+ UINT64 Adjust;
+ UINT16 *Reloc, *RelocEnd;
+ char *Fixup, *FixupBase, *FixupData = NULL;
+ UINT16 *Fixup16;
+ UINT32 *Fixup32;
+ UINT64 *Fixup64;
+ int size = context->ImageSize;
+ void *ImageEnd = (char *)grubdata + size;
+
+ if (context->NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
+ Print(L"Image has no relocation entry\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ RelocBase = ImageAddress(grubdata, size, context->RelocDir->VirtualAddress);
+ RelocBaseEnd = ImageAddress(grubdata, size, context->RelocDir->VirtualAddress + context->RelocDir->Size - 1);
+
+ if (!RelocBase || !RelocBaseEnd) {
+ Print(L"Reloc table overflows binary\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ Adjust = (UINT64)grubdata - context->ImageAddress;
+
+ while (RelocBase < RelocBaseEnd) {
+ Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
+ RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock);
+
+ if ((void *)RelocEnd < grubdata || (void *)RelocEnd > ImageEnd) {
+ Print(L"Reloc entry overflows binary\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ FixupBase = ImageAddress(grubdata, size, RelocBase->VirtualAddress);
+ if (!FixupBase) {
+ Print(L"Invalid fixupbase\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ while (Reloc < RelocEnd) {
+ Fixup = FixupBase + (*Reloc & 0xFFF);
+ switch ((*Reloc) >> 12) {
+ case EFI_IMAGE_REL_BASED_ABSOLUTE:
+ break;
+
+ case EFI_IMAGE_REL_BASED_HIGH:
+ Fixup16 = (UINT16 *) Fixup;
+ *Fixup16 = (UINT16) (*Fixup16 + ((UINT16) ((UINT32) Adjust >> 16)));
+ if (FixupData != NULL) {
+ *(UINT16 *) FixupData = *Fixup16;
+ FixupData = FixupData + sizeof (UINT16);
+ }
+ break;
+
+ case EFI_IMAGE_REL_BASED_LOW:
+ Fixup16 = (UINT16 *) Fixup;
+ *Fixup16 = (UINT16) (*Fixup16 + (UINT16) Adjust);
+ if (FixupData != NULL) {
+ *(UINT16 *) FixupData = *Fixup16;
+ FixupData = FixupData + sizeof (UINT16);
+ }
+ break;
+
+ case EFI_IMAGE_REL_BASED_HIGHLOW:
+ Fixup32 = (UINT32 *) Fixup;
+ *Fixup32 = *Fixup32 + (UINT32) Adjust;
+ if (FixupData != NULL) {
+ FixupData = ALIGN_POINTER (FixupData, sizeof (UINT32));
+ *(UINT32 *)FixupData = *Fixup32;
+ FixupData = FixupData + sizeof (UINT32);
+ }
+ break;
+
+ case EFI_IMAGE_REL_BASED_DIR64:
+ Fixup64 = (UINT64 *) Fixup;
+ *Fixup64 = *Fixup64 + (UINT64) Adjust;
+ if (FixupData != NULL) {
+ FixupData = ALIGN_POINTER (FixupData, sizeof(UINT64));
+ *(UINT64 *)(FixupData) = *Fixup64;
+ FixupData = FixupData + sizeof(UINT64);
+ }
+ break;
+
+ default:
+ Print(L"Unknown relocation\n");
+ return EFI_UNSUPPORTED;
+ }
+ Reloc += 1;
+ }
+ RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd;
+ }
+
+ return EFI_SUCCESS;
+
+}
+
+EFI_STATUS handle_grub (void *grubdata, int grubsize)
+{
+ EFI_STATUS efi_status;
+ char *buffer;
+ int i, size;
+ EFI_IMAGE_SECTION_HEADER *Section;
+ char *base, *end, *maxend = 0;
+ PE_COFF_LOADER_IMAGE_CONTEXT context;
+
+ efi_status = read_header(grubdata, &context);
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to read header\n");
+ return efi_status;
+ }
+
+ buffer = AllocatePool(context.ImageSize);
+
+ CopyMem(buffer, grubdata, context.SizeOfHeaders);
+
+ Section = context.FirstSection;
+ for (i = 0; i < context.NumberOfSections; i++) {
+ size = Section->Misc.VirtualSize;
+
+ if (size > Section->SizeOfRawData)
+ size = Section->SizeOfRawData;
+
+ base = ImageAddress (buffer, context.ImageSize, Section->VirtualAddress);
+ end = ImageAddress (buffer, context.ImageSize, Section->VirtualAddress + size - 1);
+
+ if (!base || !end) {
+ Print(L"Invalid section size\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ if (end > maxend)
+ maxend = end;
+
+ if (Section->SizeOfRawData > 0)
+ CopyMem(base, grubdata + Section->PointerToRawData, size);
+
+ if (size < Section->Misc.VirtualSize)
+ ZeroMem (base + size, Section->Misc.VirtualSize - size);
+
+ Section += 1;
+ }
+
+ efi_status = relocate_grub(&context, buffer);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Relocation failed\n");
+ return efi_status;
+ }
+
+ entry_point = (void *)(context.EntryPoint + (UINT64)buffer);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab)
+{
+ EFI_STATUS efi_status;
+ void *grubdata;
+ int grubsize;
+
+ systab = passed_systab;
+
+ InitializeLib(image_handle, systab);
+
+ efi_status = load_grub(image_handle, &grubdata, &grubsize);
+
+ if (efi_status != EFI_SUCCESS)
+ return efi_status;
+
+ efi_status = handle_grub(grubdata, grubsize);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to load grub\n");
+ return efi_status;
+ }
+
+ return uefi_call_wrapper(entry_point, 3, image_handle, passed_systab);
+}