diff options
Diffstat (limited to 'shim.c')
| -rw-r--r-- | shim.c | 643 |
1 files changed, 357 insertions, 286 deletions
@@ -1,234 +1,174 @@ +/* + * shim - trivial UEFI first-stage bootloader + * + * Copyright 2012 Red Hat, Inc <mjg@redhat.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Significant portions of this code are derived from Tianocore + * (http://tianocore.sf.net) and are Copyright 2009-2012 Intel + * Corporation. + */ + #include <efi.h> #include <efilib.h> #include <Library/BaseCryptLib.h> #include "PeImage.h" +#define SECOND_STAGE L"grub.efi" + static EFI_SYSTEM_TABLE *systab; static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table); +/* + * The vendor certificate used for validating the second stage loader + */ + #include "cert.h" -static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, - int *grubsize) +/* + * Perform basic bounds checking of the intra-image pointers + */ +static void *ImageAddress (void *image, int size, unsigned int address) { - 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; - CHAR16 *Dir; - EFI_HANDLE device; - unsigned int buffersize = sizeof(EFI_FILE_INFO); - int i; - - 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; - - 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; - } - - FilePath = (FILEPATH_DEVICE_PATH *)li->FilePath; + if (address > size) + return NULL; - efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, - FilePath->PathName, EFI_FILE_MODE_READ, - 0); + return image + address; +} - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to open %s - %lx\n", FilePath->PathName, - efi_status); - return efi_status; - } +/* + * Perform the actual relocation + */ +static 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; - fileinfo = AllocatePool(buffersize); + context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)grubdata; - if (!fileinfo) { - Print(L"Unable to allocate info buffer\n"); - return EFI_OUT_OF_RESOURCES; + if (context->NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) { + Print(L"Image has no relocation entry\n"); + return EFI_UNSUPPORTED; } - efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id, - &buffersize, fileinfo); + RelocBase = ImageAddress(grubdata, size, context->RelocDir->VirtualAddress); + RelocBaseEnd = ImageAddress(grubdata, size, context->RelocDir->VirtualAddress + context->RelocDir->Size - 1); - if (efi_status == EFI_BUFFER_TOO_SMALL) { - fileinfo = AllocatePool(buffersize); - if (!fileinfo) { - Print(L"Unable to allocate info buffer\n"); - return EFI_OUT_OF_RESOURCES; - } - efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, - &file_info_id, &buffersize, - fileinfo); + if (!RelocBase || !RelocBaseEnd) { + Print(L"Reloc table overflows binary\n"); + return EFI_UNSUPPORTED; } - if (efi_status != EFI_SUCCESS) { - Print(L"Unable to get file info\n"); - return efi_status; - } + Adjust = (UINT64)grubdata - context->ImageAddress; - efi_status = uefi_call_wrapper(grub->Close, 1, grub); + while (RelocBase < RelocBaseEnd) { + Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION)); + RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock); - if (fileinfo->Attribute & EFI_FILE_DIRECTORY) { - Dir = FilePath->PathName; - } else { - for (i=StrLen(FilePath->PathName); i > 0; i--) { - if (FilePath->PathName[i] == '\\') - break; + if ((void *)RelocEnd < grubdata || (void *)RelocEnd > ImageEnd) { + Print(L"Reloc entry overflows binary\n"); + return EFI_UNSUPPORTED; } - if (i) { - Dir = AllocatePool(i * 2); - CopyMem(Dir, FilePath->PathName, i * 2); - Dir[i] = '\0'; - } else { - Dir = AllocatePool(2); - Dir[0] = '\0'; + FixupBase = ImageAddress(grubdata, size, RelocBase->VirtualAddress); + if (!FixupBase) { + Print(L"Invalid fixupbase\n"); + return EFI_UNSUPPORTED; } - } - PathName = AllocatePool(StrLen(Dir) + StrLen(L"grub.efi")); - - if (!PathName) - return EFI_LOAD_ERROR; - - StrCpy(PathName, Dir); + while (Reloc < RelocEnd) { + Fixup = FixupBase + (*Reloc & 0xFFF); + switch ((*Reloc) >> 12) { + case EFI_IMAGE_REL_BASED_ABSOLUTE: + break; - StrCat(PathName, L"grub.efi"); + 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; - efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, PathName, - EFI_FILE_MODE_READ, 0); + 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; - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to open %s - %lx\n", PathName, efi_status); - return efi_status; - } + 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; - efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id, - &buffersize, fileinfo); + 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; - if (efi_status == EFI_BUFFER_TOO_SMALL) { - fileinfo = AllocatePool(buffersize); - if (!fileinfo) { - Print(L"Unable to allocate info buffer\n"); - return EFI_OUT_OF_RESOURCES; + default: + Print(L"Unknown relocation\n"); + return EFI_UNSUPPORTED; + } + Reloc += 1; } - 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); - - if (!*grubdata) { - Print(L"Unable to allocate file buffer\n"); - return EFI_OUT_OF_RESOURCES; - } - - efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize, - *grubdata); - - if (efi_status != EFI_SUCCESS) { - Print(L"Unexpected return from initial read: %x, buffersize %x\n", efi_status, buffersize); - return efi_status; - } - - if (efi_status != EFI_SUCCESS) { - Print(L"Unable to read grub\n"); - return efi_status; - } - - *grubsize = buffersize; - - return EFI_SUCCESS; -} - -static 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->PEHdr = PEHdr; - context->ImageAddress = PEHdr->Pe32Plus.OptionalHeader.ImageBase; - 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)); - context->SecDir = (EFI_IMAGE_DATA_DIRECTORY *) &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; - - if (context->SecDir->VirtualAddress >= context->ImageSize) { - Print(L"Malformed security header\n"); - return EFI_INVALID_PARAMETER; - } - - if (context->SecDir->Size == 0) { - Print(L"Empty security header\n"); - return EFI_INVALID_PARAMETER; + RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd; } return EFI_SUCCESS; } -static void *ImageAddress (void *image, int size, unsigned int address) -{ - if (address > size) - return NULL; - - return image + address; -} - -static EFI_STATUS verify_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, char *grubdata, int grubsize) +/* + * Check that the signature is valid and matches the binary + */ +static EFI_STATUS verify_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, + char *grubdata, int grubsize) { unsigned int size = grubsize; unsigned int ctxsize; @@ -237,7 +177,7 @@ static EFI_STATUS verify_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, char *grub EFI_STATUS status = EFI_ACCESS_DENIED; char *hashbase; unsigned int hashsize; - WIN_CERTIFICATE_EFI_PKCS *cer; + WIN_CERTIFICATE_EFI_PKCS *cert; unsigned int SumOfBytesHashed, SumOfSectionBytes; unsigned int index, pos; EFI_IMAGE_SECTION_HEADER *Section; @@ -246,13 +186,18 @@ static EFI_STATUS verify_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, char *grub cert = ImageAddress (grubdata, size, context->SecDir->VirtualAddress); - if (cer->Hdr.wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA) { + if (!cert) { + Print(L"Certificate located outside the image\n"); + return EFI_INVALID_PARAMETER; + } + + if (cert->Hdr.wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA) { Print(L"Unsupported certificate type %x\n", cert->Hdr.wCertificateType); return EFI_UNSUPPORTED; } - /* Check which kind of hash */ + /* FIXME: Check which kind of hash */ ctxsize = Sha256GetContextSize(); ctx = AllocatePool(ctxsize); @@ -300,7 +245,7 @@ static EFI_STATUS verify_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, char *grub goto done; } - /* Sort sections and hash SizeOfRawData of each section */ + /* Sort sections */ SumOfBytesHashed = context->PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders; Section = (EFI_IMAGE_SECTION_HEADER *) ( @@ -348,6 +293,11 @@ static EFI_STATUS verify_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, char *grub hashbase = ImageAddress(grubdata, size, Section->PointerToRawData); hashsize = (unsigned int) Section->SizeOfRawData; + if (!hashbase) { + Print(L"Malformed section header\n"); + return EFI_INVALID_PARAMETER; + } + if (!(Sha256Update(ctx, hashbase, hashsize))) { Print(L"Unable to generate hash\n"); status = EFI_OUT_OF_RESOURCES; @@ -377,10 +327,10 @@ static EFI_STATUS verify_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, char *grub goto done; } - if (!AuthenticodeVerify(WinCertificate->CertData, - context->SecDir->Size - sizeof(WinCertificate->Hdr), - cert, sizeof(cert), hash, - SHA256_DIGEST_SIZE)) { + if (!AuthenticodeVerify(cert->CertData, + context->SecDir->Size - sizeof(cert->Hdr), + vendor_cert, sizeof(cert), hash, + SHA256_DIGEST_SIZE)) { Print(L"Invalid signature\n"); status = EFI_ACCESS_DENIED; } else { @@ -394,107 +344,60 @@ done: return status; } -static EFI_STATUS relocate_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, void *grubdata) +/* + * Read the binary header and grab appropriate information from it + */ +static EFI_STATUS read_header(void *grubdata, + PE_COFF_LOADER_IMAGE_CONTEXT *context) { - 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; + EFI_IMAGE_DOS_HEADER *DosHdr = grubdata; + EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = grubdata; - context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)grubdata; + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) + PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)grubdata + DosHdr->e_lfanew); - if (context->NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) { - Print(L"Image has no relocation entry\n"); + if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) { + Print(L"Unsupported image type\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"); + if (PEHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) { + Print(L"Unsupported image - Relocations have been stripped\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; + if (PEHdr->Pe32.OptionalHeader.Magic != EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + Print(L"Only 64-bit images supported\n"); + return EFI_UNSUPPORTED; + } - 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; + context->PEHdr = PEHdr; + context->ImageAddress = PEHdr->Pe32Plus.OptionalHeader.ImageBase; + 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)); + context->SecDir = (EFI_IMAGE_DATA_DIRECTORY *) &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; - 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; + if (context->SecDir->VirtualAddress >= context->ImageSize) { + Print(L"Malformed security header\n"); + return EFI_INVALID_PARAMETER; + } - default: - Print(L"Unknown relocation\n"); - return EFI_UNSUPPORTED; - } - Reloc += 1; - } - RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd; + if (context->SecDir->Size == 0) { + Print(L"Empty security header\n"); + return EFI_INVALID_PARAMETER; } return EFI_SUCCESS; - } +/* + * Once the image has been loaded it needs to be validated and relocated + */ static EFI_STATUS handle_grub (void *grubdata, int grubsize) { EFI_STATUS efi_status; @@ -566,6 +469,174 @@ static EFI_STATUS handle_grub (void *grubdata, int grubsize) return EFI_SUCCESS; } +/* + * Locate the second stage bootloader and read it into a buffer + */ +static 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; + CHAR16 *Dir; + EFI_HANDLE device; + unsigned int buffersize = sizeof(EFI_FILE_INFO); + int i; + + 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; + + 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; + } + + FilePath = (FILEPATH_DEVICE_PATH *)li->FilePath; + + efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, + FilePath->PathName, EFI_FILE_MODE_READ, + 0); + + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to open %s - %lx\n", FilePath->PathName, + efi_status); + return efi_status; + } + + fileinfo = AllocatePool(buffersize); + + if (!fileinfo) { + Print(L"Unable to allocate info buffer\n"); + return EFI_OUT_OF_RESOURCES; + } + + efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id, + &buffersize, fileinfo); + + if (efi_status == EFI_BUFFER_TOO_SMALL) { + fileinfo = AllocatePool(buffersize); + if (!fileinfo) { + Print(L"Unable to allocate info buffer\n"); + return EFI_OUT_OF_RESOURCES; + } + 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; + } + + efi_status = uefi_call_wrapper(grub->Close, 1, grub); + + if (fileinfo->Attribute & EFI_FILE_DIRECTORY) { + Dir = FilePath->PathName; + } else { + for (i=StrLen(FilePath->PathName); i > 0; i--) { + if (FilePath->PathName[i] == '\\') + break; + } + + if (i) { + Dir = AllocatePool(i * 2); + CopyMem(Dir, FilePath->PathName, i * 2); + Dir[i] = '\0'; + } else { + Dir = AllocatePool(2); + Dir[0] = '\0'; + } + } + + PathName = AllocatePool(StrLen(Dir) + StrLen(SECOND_STAGE)); + + if (!PathName) + return EFI_LOAD_ERROR; + + StrCpy(PathName, Dir); + + StrCat(PathName, SECOND_STAGE); + + 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 %s - %lx\n", PathName, efi_status); + 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); + if (!fileinfo) { + Print(L"Unable to allocate info buffer\n"); + return EFI_OUT_OF_RESOURCES; + } + 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); + + if (!*grubdata) { + Print(L"Unable to allocate file buffer\n"); + return EFI_OUT_OF_RESOURCES; + } + + efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize, + *grubdata); + + if (efi_status != EFI_SUCCESS) { + Print(L"Unexpected return from initial read: %x, buffersize %x\n", efi_status, buffersize); + 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 efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) { EFI_STATUS efi_status; |
