/** * PROJECT: ExectOS * COPYRIGHT: See COPYING.md in the top level directory * FILE: xtldr/modules/pecoff/pecoff.c * DESCRIPTION: PE/COFF executable file format support * DEVELOPERS: Rafal Kupiec */ #include /* EFI Image Handle */ EFI_HANDLE EfiImageHandle; /* EFI System Table */ PEFI_SYSTEM_TABLE EfiSystemTable; /* EFI XT Loader Protocol */ PXT_BOOT_LOADER_PROTOCOL EfiXtLdrProtocol; /* XTOS PE/COFF Image Protocol */ XT_PECOFF_IMAGE_PROTOCOL XtPeCoffProtocol; /** * Loads a PE/COFF image file. * * @param FileHandle * The handle of the opened portable executable (PE) file. * * @param VirtualAddress * Optional virtual address pointing to the memory area where PE/COFF file will be loaded. * * @return This routine returns status code. * * @since XT 1.0 */ EFI_STATUS PeLoadImage(IN PEFI_FILE_HANDLE FileHandle, IN PVOID VirtualAddress, OUT PPECOFF_IMAGE_CONTEXT *Image) { EFI_GUID FileInfoGuid = EFI_FILE_INFO_PROTOCOL_GUID; PPECOFF_IMAGE_SECTION_HEADER SectionHeader; PPECOFF_IMAGE_PE_HEADER PeHeader; PPECOFF_IMAGE_CONTEXT ImageData; EFI_PHYSICAL_ADDRESS Address; PEFI_FILE_INFO FileInfo; SIZE_T Size, Pages; EFI_STATUS Status; UINT_PTR ReadSize; UINT SectionSize; PUCHAR Data; UINT Index; /* Set required size for getting file information */ Size = sizeof(EFI_FILE_INFO) + 32; /* Allocate necessary amount of memory */ Status = EfiXtLdrProtocol->AllocatePool(Size, (PVOID *)&FileInfo); if(Status != STATUS_EFI_SUCCESS) { /* Memory allocation failure */ EfiXtLdrProtocol->DbgPrint(L"ERROR: Memory pool allocation failure\n"); return Status; } /* First attempt to get file information */ Status = FileHandle->GetInfo(FileHandle, &FileInfoGuid, &Size, FileInfo); if(Status == STATUS_EFI_BUFFER_TOO_SMALL) { /* Buffer it too small, but EFI tells the required size, let's reallocate */ EfiXtLdrProtocol->FreePool(&FileInfo); Status = EfiXtLdrProtocol->AllocatePool(Size, (PVOID *)&FileInfo); if(Status != STATUS_EFI_SUCCESS) { /* Memory allocation failure */ EfiXtLdrProtocol->DbgPrint(L"ERROR: Memory pool allocation failure\n"); return Status; } /* Second attempt to get file information */ Status = FileHandle->GetInfo(FileHandle, &FileInfoGuid, &Size, FileInfo); } if(Status != STATUS_EFI_SUCCESS) { /* Unable to get file information */ EfiXtLdrProtocol->DbgPrint(L"ERROR: Failed to get file information\n"); return Status; } /* Allocate memory for storing image data */ Status = EfiXtLdrProtocol->AllocatePool(sizeof(PECOFF_IMAGE_CONTEXT), (PVOID *)&ImageData); if(Status != STATUS_EFI_SUCCESS) { /* Memory allocation failure */ EfiXtLdrProtocol->DbgPrint(L"ERROR: Memory pool allocation failure\n"); return Status; } /* Store file size and free memory */ ImageData->FileSize = FileInfo->FileSize; EfiXtLdrProtocol->FreePool(FileInfo); /* Calculate number of pages */ Pages = EFI_SIZE_TO_PAGES(ImageData->FileSize); /* Allocate pages */ Status = EfiXtLdrProtocol->AllocatePages(Pages, &Address); if(Status != STATUS_EFI_SUCCESS) { /* Pages allocation failure */ EfiXtLdrProtocol->DbgPrint(L"ERROR: Pages allocation failure\n"); EfiXtLdrProtocol->FreePool(ImageData); return Status; } /* Read PE/COFF image */ ReadSize = Pages * EFI_PAGE_SIZE; Data = (PUCHAR)(UINT_PTR)Address; Status = FileHandle->Read(FileHandle, &ReadSize, Data); if(Status != STATUS_EFI_SUCCESS) { /* Failed to read data */ EfiXtLdrProtocol->DbgPrint(L"ERROR: Unable to read PE/COFF image file\n"); EfiXtLdrProtocol->FreePages(Pages, (EFI_PHYSICAL_ADDRESS)(UINT_PTR)Data); EfiXtLdrProtocol->FreePool(ImageData); return Status; } /* Read and validate headers */ Status = PepReadImageHeader(Data, ImageData->FileSize, &PeHeader); if(Status != STATUS_EFI_SUCCESS) { /* Header validation failed, probably broken or invalid PE/COFF image */ EfiXtLdrProtocol->DbgPrint(L"ERROR: PE/COFF image validation failed\n"); EfiXtLdrProtocol->FreePages(Pages, (EFI_PHYSICAL_ADDRESS)(UINT_PTR)Data); EfiXtLdrProtocol->FreePool(ImageData); return Status; } /* Store image size and calculate number of image pages */ ImageData->ImageSize = PeHeader->OptionalHeader.SizeOfImage; ImageData->ImagePages = EFI_SIZE_TO_PAGES(ImageData->ImageSize); /* Allocate image pages */ Status = EfiXtLdrProtocol->AllocatePages(ImageData->ImagePages, &Address); if(Status != STATUS_EFI_SUCCESS) { /* Pages reallocation failure */ EfiXtLdrProtocol->DbgPrint(L"ERROR: Pages reallocation failure\n"); EfiXtLdrProtocol->FreePool(ImageData); return Status; } /* Store image data and base address */ ImageData->Data = (PUINT8)(UINT_PTR)Address; if(VirtualAddress) { /* Virtual address passed to this routine */ ImageData->VirtualAddress = VirtualAddress; } else { /* Virtual address not specified, use physical address */ ImageData->VirtualAddress = (PVOID)(UINT_PTR)Address; } /* Copy all sections */ RtlCopyMemory(ImageData->Data, Data, PeHeader->OptionalHeader.SizeOfHeaders); /* Find section header */ SectionHeader = (PPECOFF_IMAGE_SECTION_HEADER)((PUCHAR)&PeHeader->OptionalHeader + PeHeader->FileHeader.SizeOfOptionalHeader); /* Load each section into memory */ for(Index = 0; Index < PeHeader->FileHeader.NumberOfSections; Index++) { EfiXtLdrProtocol->EfiPrint(L"TEST: %lx\n", SectionHeader[Index].Misc.VirtualSize); /* Check section raw data size and section virtual size */ if(SectionHeader[Index].SizeOfRawData < SectionHeader[Index].Misc.VirtualSize) { /* Use raw data size if it is smaller than virtual size */ SectionSize = SectionHeader[Index].SizeOfRawData; } else { /* User virtual size otherwise */ SectionSize = SectionHeader[Index].Misc.VirtualSize; } /* Make sure section is available */ if(SectionSize > 0 && SectionHeader[Index].PointerToRawData != 0) { /* Copy section */ RtlCopyMemory((PUINT8)ImageData->Data + SectionHeader[Index].VirtualAddress, Data + SectionHeader[Index].PointerToRawData, SectionSize); } /* Check if raw size is shorter than virtual size */ if(SectionSize < SectionHeader[Index].Misc.VirtualSize) { /* Fill remaining space with zeroes */ RtlZeroMemory((PUINT8)ImageData->Data + SectionHeader[Index].VirtualAddress + SectionSize, SectionHeader[Index].Misc.VirtualSize - SectionSize); } } /* Free pages */ EfiXtLdrProtocol->FreePages((EFI_PHYSICAL_ADDRESS)(UINT_PTR)Data, Pages); /* Store image data */ *Image = ImageData; /* Return SUCCESS */ return STATUS_EFI_SUCCESS; } /** * Reads and validate a PE/COFF image headers * * @param ImageData * A pointer to the buffer containing PE/COFF image contents. * * @param FileSize * An input PE/COFF image file size. * * @param PeHeader * Pointer to the memory area where PE header will be saved. * * @return This routine returns a status code. * * @since XT 1.0 */ EFI_STATUS PepReadImageHeader(IN PUCHAR ImageData, IN SIZE_T FileSize, OUT PPECOFF_IMAGE_PE_HEADER *PeHeader) { PPECOFF_IMAGE_DOS_HEADER DosHeader; /* Validate file size */ if(FileSize < sizeof(PECOFF_IMAGE_DOS_HEADER)) { EfiXtLdrProtocol->DbgPrint(L"WARNING: PE/COFF image shorter than DOS header\n"); return STATUS_EFI_END_OF_FILE; } /* Validate DOS header */ DosHeader = (PPECOFF_IMAGE_DOS_HEADER)ImageData; if(DosHeader->e_magic != PECOFF_IMAGE_DOS_SIGNATURE) { EfiXtLdrProtocol->DbgPrint(L"WARNING: Invalid DOS signature found\n"); return STATUS_EFI_INCOMPATIBLE_VERSION; } /* Validate NT/XT header */ *PeHeader = (PPECOFF_IMAGE_PE_HEADER)(ImageData + DosHeader->e_lfanew); if((*PeHeader)->Signature != PECOFF_IMAGE_NT_SIGNATURE && (*PeHeader)->Signature != PECOFF_IMAGE_XT_SIGNATURE) { EfiXtLdrProtocol->DbgPrint(L"WARNING: Invalid NT/XT signature found\n"); return STATUS_EFI_INCOMPATIBLE_VERSION; } /* Validate optional header */ if((*PeHeader)->OptionalHeader.Magic != PECOFF_IMAGE_PE_OPTIONAL_HDR32_MAGIC && (*PeHeader)->OptionalHeader.Magic != PECOFF_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { EfiXtLdrProtocol->DbgPrint(L"WARNING: Invalid optional header signature found\n"); return STATUS_EFI_INCOMPATIBLE_VERSION; } /* Return SUCCESS */ return STATUS_EFI_SUCCESS; } /** * This routine is the entry point of the XT EFI boot loader module. * * @param ImageHandle * Firmware-allocated handle that identifies the image. * * @param SystemTable * Provides the EFI system table. * * @return This routine returns status code. * * @since XT 1.0 */ EFI_STATUS BlXtLdrModuleMain(EFI_HANDLE ImageHandle, PEFI_SYSTEM_TABLE SystemTable) { EFI_GUID Guid = XT_PECOFF_IMAGE_PROTOCOL_GUID; EFI_HANDLE Handle = NULL; EFI_STATUS Status; /* Set the system table and image handle */ EfiImageHandle = ImageHandle; EfiSystemTable = SystemTable; /* Open the XTLDR protocol */ Status = BlGetXtLoaderProtocol(&EfiXtLdrProtocol); if(Status != STATUS_EFI_SUCCESS) { /* Failed to open loader protocol */ return STATUS_EFI_PROTOCOL_ERROR; } /* Set routines available via PE/COFF image protocol */ XtPeCoffProtocol.Load = PeLoadImage; /* Register PE/COFF protocol */ return EfiSystemTable->BootServices->InstallProtocolInterface(&Handle, &Guid, EFI_NATIVE_INTERFACE, &XtPeCoffProtocol); }