forked from xt-sys/exectos
		
	
		
			
				
	
	
		
			924 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			924 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /**
 | |
|  * PROJECT:         ExectOS
 | |
|  * COPYRIGHT:       See COPYING.md in the top level directory
 | |
|  * FILE:            xtldr/modules/pecoff/pecoff.cc
 | |
|  * DESCRIPTION:     Basic PE/COFF executable file format support module
 | |
|  * DEVELOPERS:      Rafal Kupiec <belliash@codingworkshop.eu.org>
 | |
|  */
 | |
| 
 | |
| #include <pecoff.hh>
 | |
| 
 | |
| 
 | |
| /* PE/COFF_O module information */
 | |
| MODULE_AUTHOR(L"Rafal Kupiec <belliash@codingworkshop.eu.org>");
 | |
| MODULE_DESCRIPTION(L"Basic PE/COFF executable file format support");
 | |
| MODULE_LICENSE(L"GPLv3");
 | |
| MODULE_VERSION(L"0.1");
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Returns the address of the entry point.
 | |
|  *
 | |
|  * @param ImagePointer
 | |
|  *        Supplies a pointer to the PE/COFF context structure representing the loaded image.
 | |
|  *
 | |
|  * @param EntryPoint
 | |
|  *        Supplies a pointer to the memory area where address of the image entry point will be stored.
 | |
|  *
 | |
|  * @return This routine returns a status code.
 | |
|  *
 | |
|  * @since XT 1.0
 | |
|  */
 | |
| XTCDECL
 | |
| EFI_STATUS
 | |
| PeCoff::GetEntryPoint(IN PVOID ImagePointer,
 | |
|                       OUT PVOID *EntryPoint)
 | |
| {
 | |
|     PPECOFF_IMAGE_CONTEXT Image;
 | |
| 
 | |
|     /* Get PE/COFF image pointer*/
 | |
|     Image = (PPECOFF_IMAGE_CONTEXT)ImagePointer;
 | |
| 
 | |
|     /* Validate input data */
 | |
|     if(!Image || !Image->PeHeader)
 | |
|     {
 | |
|         /* Invalid parameter passed */
 | |
|         return STATUS_EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     /* Check PE/COFF image type */
 | |
|     if(Image->PeHeader->OptionalHeader32.Magic == PECOFF_IMAGE_PE_OPTIONAL_HDR64_MAGIC)
 | |
|     {
 | |
|         /* Get entry point from 64-bit optional header */
 | |
|         *EntryPoint = (PUCHAR)Image->VirtualAddress + Image->PeHeader->OptionalHeader64.AddressOfEntryPoint;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         /* Get entry point from 32-bit optional header */
 | |
|         *EntryPoint = (PUCHAR)Image->VirtualAddress + Image->PeHeader->OptionalHeader32.AddressOfEntryPoint;
 | |
|     }
 | |
| 
 | |
|     /* Return success */
 | |
|     return STATUS_EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns the size of the loaded PE/COFF file.
 | |
|  *
 | |
|  * @param ImagePointer
 | |
|  *        Supplies a pointer to the PE/COFF context structure representing the loaded image.
 | |
|  *
 | |
|  * @param ImageSize
 | |
|  *        Supplies a pointer to the memory area where file size will be stored.
 | |
|  *
 | |
|  * @return This routine returns a status code.
 | |
|  *
 | |
|  * @since XT 1.0
 | |
|  */
 | |
| XTCDECL
 | |
| EFI_STATUS
 | |
| PeCoff::GetFileSize(IN PVOID ImagePointer,
 | |
|                     OUT PULONGLONG FileSize)
 | |
| {
 | |
|     PPECOFF_IMAGE_CONTEXT Image;
 | |
| 
 | |
|     /* Get PE/COFF image pointer*/
 | |
|     Image = (PPECOFF_IMAGE_CONTEXT)ImagePointer;
 | |
| 
 | |
|     /* Validate input data */
 | |
|     if(!Image || !Image->ImageSize)
 | |
|     {
 | |
|         /* Invalid parameter passed */
 | |
|         return STATUS_EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     /* Get image size and return success */
 | |
|     *FileSize = Image->FileSize;
 | |
|     return STATUS_EFI_NOT_FOUND;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns the size of the loaded PE/COFF image.
 | |
|  *
 | |
|  * @param ImagePointer
 | |
|  *        Supplies a pointer to the PE/COFF context structure representing the loaded image.
 | |
|  *
 | |
|  * @param ImageSize
 | |
|  *        Supplies a pointer to the memory area where image size will be stored.
 | |
|  *
 | |
|  * @return This routine returns a status code.
 | |
|  *
 | |
|  * @since XT 1.0
 | |
|  */
 | |
| XTCDECL
 | |
| EFI_STATUS
 | |
| PeCoff::GetImageSize(IN PVOID ImagePointer,
 | |
|                      OUT PUINT ImageSize)
 | |
| {
 | |
|     PPECOFF_IMAGE_CONTEXT Image;
 | |
| 
 | |
|     /* Get PE/COFF image pointer*/
 | |
|     Image = (PPECOFF_IMAGE_CONTEXT)ImagePointer;
 | |
| 
 | |
|     /* Validate input data */
 | |
|     if(!Image || !Image->ImageSize)
 | |
|     {
 | |
|         /* Invalid parameter passed */
 | |
|         return STATUS_EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     /* Get image size and return success */
 | |
|     *ImageSize = Image->ImageSize;
 | |
|     return STATUS_EFI_NOT_FOUND;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns the machine type of the PE/COFF image.
 | |
|  *
 | |
|  * @param ImagePointer
 | |
|  *        Supplies a pointer to the PE/COFF context structure representing the loaded image.
 | |
|  *
 | |
|  * @param MachineType
 | |
|  *        Supplies a pointer to the memory area where a value defined for the 'machine' field will be stored.
 | |
|  *
 | |
|  * @return This routine returns a status code.
 | |
|  *
 | |
|  * @since XT 1.0
 | |
|  */
 | |
| XTCDECL
 | |
| EFI_STATUS
 | |
| PeCoff::GetMachineType(IN PVOID ImagePointer,
 | |
|                        OUT PUSHORT MachineType)
 | |
| {
 | |
|     PPECOFF_IMAGE_CONTEXT Image;
 | |
| 
 | |
|     /* Get PE/COFF image pointer*/
 | |
|     Image = (PPECOFF_IMAGE_CONTEXT)ImagePointer;
 | |
| 
 | |
|     /* Validate input data */
 | |
|     if(!Image || !Image->PeHeader)
 | |
|     {
 | |
|         /* Invalid parameter passed */
 | |
|         return STATUS_EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     /* Get image machine type and return success */
 | |
|     *MachineType = Image->PeHeader->FileHeader.Machine;
 | |
|     return STATUS_EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns an address to the specified section in the PE/COFF image.
 | |
|  *
 | |
|  * @param ImagePointer
 | |
|  *        Supplies a pointer to the PE/COFF context structure representing the loaded image.
 | |
|  *
 | |
|  * @param SectionName
 | |
|  *        Supplies a name of the requested section.
 | |
|  *
 | |
|  * @param RawData
 | |
|  *        Supplies a pointer to the memory area where the address of the requested section will be stored.
 | |
|  *
 | |
|  * @return This routine returns a status code.
 | |
|  *
 | |
|  * @since XT 1.0
 | |
|  */
 | |
| XTCDECL
 | |
| EFI_STATUS
 | |
| PeCoff::GetSection(IN PVOID ImagePointer,
 | |
|                    IN PCHAR SectionName,
 | |
|                    OUT PULONG *RawData)
 | |
| {
 | |
|     PPECOFF_IMAGE_SECTION_HEADER SectionHeader;
 | |
|     PPECOFF_IMAGE_CONTEXT Image;
 | |
|     SIZE_T SectionNameLength;
 | |
|     USHORT SectionIndex;
 | |
| 
 | |
|     /* Get PE/COFF image pointer*/
 | |
|     Image = (PPECOFF_IMAGE_CONTEXT)ImagePointer;
 | |
| 
 | |
|     /* Validate input data */
 | |
|     if(!Image || !Image->PeHeader)
 | |
|     {
 | |
|         /* Invalid parameter passed */
 | |
|         return STATUS_EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     /* Check PE/COFF image type */
 | |
|     if(Image->PeHeader->OptionalHeader32.Magic == PECOFF_IMAGE_PE_OPTIONAL_HDR64_MAGIC)
 | |
|     {
 | |
|         /* Find section header in 64-bit optional header */
 | |
|         SectionHeader = (PPECOFF_IMAGE_SECTION_HEADER)((PUCHAR)&Image->PeHeader->OptionalHeader64 +
 | |
|                                                        Image->PeHeader->FileHeader.SizeOfOptionalHeader);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         /* Find section header in 32-bit optional header */
 | |
|         SectionHeader = (PPECOFF_IMAGE_SECTION_HEADER)((PUCHAR)&Image->PeHeader->OptionalHeader32 +
 | |
|                                                        Image->PeHeader->FileHeader.SizeOfOptionalHeader);
 | |
|     }
 | |
| 
 | |
|     /* Get section name length */
 | |
|     SectionNameLength = XtLdrProtocol->String.Length(SectionName, 0);
 | |
| 
 | |
|     /* Iterate through all image sections */
 | |
|     for(SectionIndex = 0; SectionIndex < Image->PeHeader->FileHeader.NumberOfSections; SectionIndex++)
 | |
|     {
 | |
|         /* Check section name */
 | |
|         if(XtLdrProtocol->String.Compare((PCHAR)SectionHeader[SectionIndex].Name, SectionName, SectionNameLength) == 0)
 | |
|         {
 | |
|             /* Store section address and return */
 | |
|             *RawData = (PULONG)((PUCHAR)Image->Data + SectionHeader[SectionIndex].PointerToRawData);
 | |
|             return STATUS_EFI_SUCCESS;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Section not found if reached here */
 | |
|     return STATUS_EFI_NOT_FOUND;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns an information about subsystem that is required to run PE/COFF image.
 | |
|  *
 | |
|  * @param ImagePointer
 | |
|  *        Supplies a pointer to the PE/COFF context structure representing the loaded image.
 | |
|  *
 | |
|  * @param SubSystem
 | |
|  *        Supplies a pointer to the memory area storing a value defined for the 'subsystem' field of the image.
 | |
|  *
 | |
|  * @return This routine returns a status code.
 | |
|  *
 | |
|  * @since XT 1.0
 | |
|  */
 | |
| XTCDECL
 | |
| EFI_STATUS
 | |
| PeCoff::GetSubSystem(IN PVOID ImagePointer,
 | |
|                      OUT PUSHORT SubSystem)
 | |
| {
 | |
|     PPECOFF_IMAGE_CONTEXT Image;
 | |
| 
 | |
|     /* Get PE/COFF image pointer*/
 | |
|     Image = (PPECOFF_IMAGE_CONTEXT)ImagePointer;
 | |
| 
 | |
|     /* Validate input data */
 | |
|     if(!Image || !Image->PeHeader)
 | |
|     {
 | |
|         /* Invalid parameter passed */
 | |
|         return STATUS_EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     /* Check PE/COFF image type */
 | |
|     if(Image->PeHeader->OptionalHeader32.Magic == PECOFF_IMAGE_PE_OPTIONAL_HDR64_MAGIC)
 | |
|     {
 | |
|         /* Get image subsystem from 64-bit optional header */
 | |
|         *SubSystem = Image->PeHeader->OptionalHeader64.Subsystem;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         /* Get image subsystem from 32-bit optional header */
 | |
|         *SubSystem = Image->PeHeader->OptionalHeader32.Subsystem;
 | |
|     }
 | |
| 
 | |
|     /* Return success */
 | |
|     return STATUS_EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns an information about major image version.
 | |
|  *
 | |
|  * @param ImagePointer
 | |
|  *        Supplies a pointer to the PE/COFF context structure representing the loaded image.
 | |
|  *
 | |
|  * @param Version
 | |
|  *        Supplies a pointer to the memory area storing a major image version.
 | |
|  *
 | |
|  * @return This routine returns a status code.
 | |
|  *
 | |
|  * @since XT 1.0
 | |
|  */
 | |
| XTCDECL
 | |
| EFI_STATUS
 | |
| PeCoff::GetVersion(IN PVOID ImagePointer,
 | |
|                    OUT PUSHORT Version)
 | |
| {
 | |
|     PPECOFF_IMAGE_CONTEXT Image;
 | |
| 
 | |
|     /* Get PE/COFF image pointer*/
 | |
|     Image = (PPECOFF_IMAGE_CONTEXT)ImagePointer;
 | |
| 
 | |
|     /* Validate input data */
 | |
|     if(!Image || !Image->PeHeader)
 | |
|     {
 | |
|         /* Invalid parameter passed */
 | |
|         return STATUS_EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     /* Check PE/COFF image type */
 | |
|     if(Image->PeHeader->OptionalHeader32.Magic == PECOFF_IMAGE_PE_OPTIONAL_HDR64_MAGIC)
 | |
|     {
 | |
|         /* Get image major version from 64-bit optional header */
 | |
|         *Version = Image->PeHeader->OptionalHeader64.MajorImageVersion;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         /* Get image major version from 32-bit optional header */
 | |
|         *Version = Image->PeHeader->OptionalHeader32.MajorImageVersion;
 | |
|     }
 | |
| 
 | |
|     /* Return success */
 | |
|     return STATUS_EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Initializes PECOFF module by opening XTLDR protocol and installing PECOFF protocol.
 | |
|  *
 | |
|  * @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
 | |
|  */
 | |
| XTCDECL
 | |
| EFI_STATUS
 | |
| PeCoff::InitializeModule(IN EFI_HANDLE ImageHandle,
 | |
|                          IN PEFI_SYSTEM_TABLE SystemTable)
 | |
| {
 | |
|     EFI_GUID Guid = XT_PECOFF_IMAGE_PROTOCOL_GUID;
 | |
|     EFI_STATUS Status;
 | |
| 
 | |
|     /* Open the XTLDR protocol */
 | |
|     Status = BlGetXtLdrProtocol(SystemTable, ImageHandle, &XtLdrProtocol);
 | |
|     if(Status != STATUS_EFI_SUCCESS)
 | |
|     {
 | |
|         /* Failed to open loader protocol */
 | |
|         return STATUS_EFI_PROTOCOL_ERROR;
 | |
|     }
 | |
| 
 | |
|     /* Set routines available via PE/COFF image protocol */
 | |
|     PeProtocol.GetEntryPoint = GetEntryPoint;
 | |
|     PeProtocol.GetFileSize = GetFileSize;
 | |
|     PeProtocol.GetImageSize = GetImageSize;
 | |
|     PeProtocol.GetMachineType = GetMachineType;
 | |
|     PeProtocol.GetSection = GetSection;
 | |
|     PeProtocol.GetSubSystem = GetSubSystem;
 | |
|     PeProtocol.GetVersion = GetVersion;
 | |
|     PeProtocol.LoadImage = LoadImage;
 | |
|     PeProtocol.RelocateImage = RelocateImage;
 | |
|     PeProtocol.UnloadImage = UnloadImage;
 | |
|     PeProtocol.VerifyImage = VerifyImage;
 | |
| 
 | |
|     /* Register PE/COFF protocol */
 | |
|     return XtLdrProtocol->Protocol.Install(&PeProtocol, &Guid);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Loads a PE/COFF image file.
 | |
|  *
 | |
|  * @param FileHandle
 | |
|  *        The handle of the opened portable executable (PE) file.
 | |
|  *
 | |
|  * @param MemoryType
 | |
|  *        Supplies the type of memory to be assigned to the memory descriptor.
 | |
|  *
 | |
|  * @param VirtualAddress
 | |
|  *        Optional virtual address pointing to the memory area where PE/COFF file will be loaded.
 | |
|  *
 | |
|  * @param Image
 | |
|  *        Supplies pointer to the memory area where loaded PE/COFF image context will be stored.
 | |
|  *
 | |
|  * @return This routine returns a status code.
 | |
|  *
 | |
|  * @since XT 1.0
 | |
|  */
 | |
| XTCDECL
 | |
| EFI_STATUS
 | |
| PeCoff::LoadImage(IN PEFI_FILE_HANDLE FileHandle,
 | |
|                   IN LOADER_MEMORY_TYPE MemoryType,
 | |
|                   IN PVOID VirtualAddress,
 | |
|                   OUT PVOID *ImagePointer)
 | |
| {
 | |
|     EFI_GUID FileInfoGuid = EFI_FILE_INFO_PROTOCOL_GUID;
 | |
|     PPECOFF_IMAGE_SECTION_HEADER SectionHeader;
 | |
|     PPECOFF_IMAGE_CONTEXT ImageData;
 | |
|     EFI_PHYSICAL_ADDRESS Address;
 | |
|     PEFI_FILE_INFO FileInfo;
 | |
|     UINT_PTR ReadSize;
 | |
|     EFI_STATUS Status;
 | |
|     UINT SectionSize;
 | |
|     SIZE_T Pages;
 | |
|     PUCHAR Data;
 | |
|     UINT Index;
 | |
| 
 | |
|     /* Set required size for getting file information */
 | |
|     ReadSize = sizeof(EFI_FILE_INFO) + 32;
 | |
| 
 | |
|     /* Allocate necessary amount of memory */
 | |
|     Status = XtLdrProtocol->Memory.AllocatePool(ReadSize, (PVOID *)&FileInfo);
 | |
|     if(Status != STATUS_EFI_SUCCESS)
 | |
|     {
 | |
|         /* Memory allocation failure */
 | |
|         XtLdrProtocol->Debug.Print(L"ERROR: Memory allocation failure (Status Code: 0x%zX)\n", Status);
 | |
|         return Status;
 | |
|     }
 | |
| 
 | |
|     /* First attempt to get file information */
 | |
|     Status = FileHandle->GetInfo(FileHandle, &FileInfoGuid, &ReadSize, FileInfo);
 | |
|     if(Status == STATUS_EFI_BUFFER_TOO_SMALL)
 | |
|     {
 | |
|         /* Buffer it too small, but EFI tells the required size, let's reallocate */
 | |
|         XtLdrProtocol->Memory.FreePool(&FileInfo);
 | |
|         Status = XtLdrProtocol->Memory.AllocatePool(ReadSize, (PVOID *)&FileInfo);
 | |
|         if(Status != STATUS_EFI_SUCCESS)
 | |
|         {
 | |
|             /* Memory allocation failure */
 | |
|             XtLdrProtocol->Debug.Print(L"ERROR: Memory allocation failure (Status Code: 0x%zX)\n", Status);
 | |
|             return Status;
 | |
|         }
 | |
| 
 | |
|         /* Second attempt to get file information */
 | |
|         Status = FileHandle->GetInfo(FileHandle, &FileInfoGuid, &ReadSize, FileInfo);
 | |
|     }
 | |
|     if(Status != STATUS_EFI_SUCCESS)
 | |
|     {
 | |
|         /* Unable to get file information */
 | |
|         XtLdrProtocol->Debug.Print(L"ERROR: Failed to get PE/COFF file information (Status Code: 0x%zX)\n", Status);
 | |
|         return Status;
 | |
|     }
 | |
| 
 | |
|     /* Allocate memory for storing image data */
 | |
|     Status = XtLdrProtocol->Memory.AllocatePool(sizeof(PECOFF_IMAGE_CONTEXT), (PVOID *)&ImageData);
 | |
|     if(Status != STATUS_EFI_SUCCESS)
 | |
|     {
 | |
|         /* Memory allocation failure */
 | |
|         XtLdrProtocol->Debug.Print(L"ERROR: Memory allocation failure (Status Code: 0x%zX)\n", Status);
 | |
|         return Status;
 | |
|     }
 | |
| 
 | |
|     /* Store file size and memory type, nullify data and free up memory */
 | |
|     ImageData->Data = NULLPTR;
 | |
|     ImageData->FileSize = FileInfo->FileSize;
 | |
|     ImageData->MemoryType = MemoryType;
 | |
|     XtLdrProtocol->Memory.FreePool(FileInfo);
 | |
| 
 | |
|     /* Calculate number of pages */
 | |
|     Pages = EFI_SIZE_TO_PAGES(ImageData->FileSize);
 | |
| 
 | |
|     /* Allocate pages */
 | |
|     Status = XtLdrProtocol->Memory.AllocatePages(AllocateAnyPages, Pages, &Address);
 | |
|     if(Status != STATUS_EFI_SUCCESS)
 | |
|     {
 | |
|         /* Pages allocation failure */
 | |
|         XtLdrProtocol->Debug.Print(L"ERROR: Pages allocation failure (Status Code: 0x%zX)\n", Status);
 | |
|         XtLdrProtocol->Memory.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 */
 | |
|         XtLdrProtocol->Debug.Print(L"ERROR: Failed to read PE/COFF image file (Status Code: 0x%zX)\n", Status);
 | |
|         XtLdrProtocol->Memory.FreePages(Pages, (EFI_PHYSICAL_ADDRESS)(UINT_PTR)Data);
 | |
|         XtLdrProtocol->Memory.FreePool(ImageData);
 | |
|         return Status;
 | |
|     }
 | |
| 
 | |
|     /* Extract DOS and PE headers */
 | |
|     ImageData->DosHeader = (PPECOFF_IMAGE_DOS_HEADER)Data;
 | |
|     ImageData->PeHeader = (PPECOFF_IMAGE_PE_HEADER)((PUCHAR)Data + ImageData->DosHeader->PeHeaderOffset);
 | |
| 
 | |
|     /* Validate headers */
 | |
|     Status = PeCoff::VerifyImage(ImageData);
 | |
|     if(Status != STATUS_EFI_SUCCESS)
 | |
|     {
 | |
|         /* Header validation failed, probably broken or invalid PE/COFF image */
 | |
|         XtLdrProtocol->Debug.Print(L"ERROR: Invalid PE/COFF image headers (Status Code: 0x%zX)\n", Status);
 | |
|         XtLdrProtocol->Memory.FreePages(Pages, (EFI_PHYSICAL_ADDRESS)(UINT_PTR)Data);
 | |
|         XtLdrProtocol->Memory.FreePool(ImageData);
 | |
|         return Status;
 | |
|     }
 | |
| 
 | |
|     /* Make sure image is executable */
 | |
|     if (!(ImageData->PeHeader->FileHeader.Characteristics & PECOFF_IMAGE_FILE_EXECUTABLE_IMAGE))
 | |
|     {
 | |
|         /* Loaded image is not executable */
 | |
|         XtLdrProtocol->Debug.Print(L"ERROR: Non-executable PE/COFF image loaded\n");
 | |
|         XtLdrProtocol->Memory.FreePages(Pages, (EFI_PHYSICAL_ADDRESS)(UINT_PTR)Data);
 | |
|         XtLdrProtocol->Memory.FreePool(ImageData);
 | |
|         return STATUS_EFI_LOAD_ERROR;
 | |
|     }
 | |
| 
 | |
|     /* Store image size depending on the PE/COFF image type */
 | |
|     if(ImageData->PeHeader->OptionalHeader32.Magic == PECOFF_IMAGE_PE_OPTIONAL_HDR64_MAGIC)
 | |
|     {
 | |
|         /* Store 64-bit image size */
 | |
|         ImageData->ImageSize = ImageData->PeHeader->OptionalHeader64.SizeOfImage;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         /* Store 32-bit image size */
 | |
|         ImageData->ImageSize = ImageData->PeHeader->OptionalHeader32.SizeOfImage;
 | |
|     }
 | |
| 
 | |
|     /* Calculate number of image pages */
 | |
|     ImageData->ImagePages = EFI_SIZE_TO_PAGES(ImageData->ImageSize);
 | |
| 
 | |
|     /* Allocate image pages */
 | |
|     Status = XtLdrProtocol->Memory.AllocatePages(AllocateAnyPages, ImageData->ImagePages, &Address);
 | |
|     if(Status != STATUS_EFI_SUCCESS)
 | |
|     {
 | |
|         /* Pages reallocation failure */
 | |
|         XtLdrProtocol->Debug.Print(L"ERROR: Pages reallocation failure (Status Code: 0x%zX)\n", Status);
 | |
|         XtLdrProtocol->Memory.FreePool(ImageData);
 | |
|         return Status;
 | |
|     }
 | |
| 
 | |
|     /* Store image data and virtual address */
 | |
|     ImageData->Data = (PUCHAR)(UINT_PTR)Address;
 | |
|     ImageData->PhysicalAddress = (PVOID)(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;
 | |
|     }
 | |
| 
 | |
|     /* Check the PE/COFF image type */
 | |
|     if(ImageData->PeHeader->OptionalHeader32.Magic == PECOFF_IMAGE_PE_OPTIONAL_HDR64_MAGIC)
 | |
|     {
 | |
|         /* Copy all PE32+ sections */
 | |
|         XtLdrProtocol->Memory.CopyMemory(ImageData->Data, Data, ImageData->PeHeader->OptionalHeader64.SizeOfHeaders);
 | |
| 
 | |
|         /* Find PE32+ section header */
 | |
|         SectionHeader = (PPECOFF_IMAGE_SECTION_HEADER)((PUCHAR)&ImageData->PeHeader->OptionalHeader64 +
 | |
|                                                        ImageData->PeHeader->FileHeader.SizeOfOptionalHeader);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         /* Copy all PE32 sections */
 | |
|         XtLdrProtocol->Memory.CopyMemory(ImageData->Data, Data, ImageData->PeHeader->OptionalHeader64.SizeOfHeaders);
 | |
| 
 | |
|         /* Find PE32 section header */
 | |
|         SectionHeader = (PPECOFF_IMAGE_SECTION_HEADER)((PUCHAR)&ImageData->PeHeader->OptionalHeader64 +
 | |
|                                                        ImageData->PeHeader->FileHeader.SizeOfOptionalHeader);
 | |
|     }
 | |
| 
 | |
|     /* Load each section into memory */
 | |
|     for(Index = 0; Index < ImageData->PeHeader->FileHeader.NumberOfSections; Index++)
 | |
|     {
 | |
|         /* 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 */
 | |
|             XtLdrProtocol->Memory.CopyMemory((PUCHAR)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 */
 | |
|             XtLdrProtocol->Memory.ZeroMemory((PUCHAR)ImageData->Data + SectionHeader[Index].VirtualAddress + SectionSize,
 | |
|                                              SectionHeader[Index].Misc.VirtualSize - SectionSize);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Free pages */
 | |
|     XtLdrProtocol->Memory.FreePages((EFI_PHYSICAL_ADDRESS)(UINT_PTR)Data, Pages);
 | |
| 
 | |
|     /* Perform relocation fixups */
 | |
|     Status = PeCoff::RelocateLoadedImage(ImageData);
 | |
|     if(Status != STATUS_EFI_SUCCESS)
 | |
|     {
 | |
|         /* Failed to relocate image */
 | |
|         XtLdrProtocol->Debug.Print(L"ERROR: PE/COFF image relocation failed (Status Code: 0x%zX)\n", Status);
 | |
|         return Status;
 | |
|     }
 | |
| 
 | |
|     /* Store image data */
 | |
|     *ImagePointer = ImageData;
 | |
| 
 | |
|     /* Return SUCCESS */
 | |
|     return STATUS_EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Relocates PE/COFF image to the specified address.
 | |
|  *
 | |
|  * @param ImagePointer
 | |
|  *        Supplies a pointer to the PE/COFF context structure representing the loaded image.
 | |
|  *
 | |
|  * @param Address
 | |
|  *        Specifies destination address of memory region, where image should be relocated.
 | |
|  *
 | |
|  * @return This routine returns a status code.
 | |
|  *
 | |
|  * @since XT 1.0
 | |
|  */
 | |
| XTCDECL
 | |
| EFI_STATUS
 | |
| PeCoff::RelocateImage(IN PVOID ImagePointer,
 | |
|                       IN EFI_VIRTUAL_ADDRESS Address)
 | |
| {
 | |
|     PPECOFF_IMAGE_CONTEXT Image;
 | |
|     ULONGLONG ImageBase, OldVirtualAddress;
 | |
|     EFI_STATUS Status;
 | |
| 
 | |
|     /* Get PE/COFF image pointer*/
 | |
|     Image = (PPECOFF_IMAGE_CONTEXT)ImagePointer;
 | |
| 
 | |
|     /* Store original virtual address */
 | |
|     OldVirtualAddress = (UINT_PTR)Image->VirtualAddress;
 | |
| 
 | |
|     /* Check PE/COFF image type */
 | |
|     if(Image->PeHeader->OptionalHeader32.Magic == PECOFF_IMAGE_PE_OPTIONAL_HDR64_MAGIC)
 | |
|     {
 | |
|         /* This is 64-bit PE32+, store its image base address */
 | |
|         ImageBase = Image->PeHeader->OptionalHeader64.ImageBase;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         /* This is 32-bit PE32, store its image base address */
 | |
|         ImageBase = Image->PeHeader->OptionalHeader32.ImageBase;
 | |
|     }
 | |
| 
 | |
|     /* Overwrite virtual address and relocate image once again */
 | |
|     Image->VirtualAddress = (PVOID)(Address - OldVirtualAddress + ImageBase);
 | |
|     Status = PeCoff::RelocateLoadedImage(Image);
 | |
|     if(Status != STATUS_EFI_SUCCESS)
 | |
|     {
 | |
|         /* Relocation failed */
 | |
|         return Status;
 | |
|     }
 | |
| 
 | |
|     /* Store new image virtual address */
 | |
|     Image->VirtualAddress = (PVOID)Address;
 | |
| 
 | |
|     /* Return success */
 | |
|     return STATUS_EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Relocates a loaded PE/COFF image.
 | |
|  *
 | |
|  * @param Image
 | |
|  *        Supplies a pointer to the PE/COFF context structure representing the loaded image.
 | |
|  *
 | |
|  * @return This routine returns a status code.
 | |
|  *
 | |
|  * @since XT 1.0
 | |
|  */
 | |
| XTCDECL
 | |
| EFI_STATUS
 | |
| PeCoff::RelocateLoadedImage(IN PPECOFF_IMAGE_CONTEXT Image)
 | |
| {
 | |
|     PPECOFF_IMAGE_BASE_RELOCATION RelocationDir, RelocationEnd;
 | |
|     PPECOFF_IMAGE_DATA_DIRECTORY DataDirectory;
 | |
|     USHORT Offset, Type, Count;
 | |
|     PUSHORT TypeOffset;
 | |
|     ULONGLONG ImageBase;
 | |
|     PUINT Address;
 | |
|     PULONGLONG LongPtr;
 | |
|     PUINT ShortPtr;
 | |
| 
 | |
|     /* Make sure image is not stripped */
 | |
|     if(Image->PeHeader->FileHeader.Characteristics & PECOFF_IMAGE_FILE_RELOCS_STRIPPED)
 | |
|     {
 | |
|         /* No relocation information found */
 | |
|         XtLdrProtocol->Debug.Print(L"WARNING: PE/COFF image is stripped and contains no information about relocations\n");
 | |
|         return STATUS_EFI_SUCCESS;
 | |
|     }
 | |
| 
 | |
|     /* Check PE/COFF image type */
 | |
|     if(Image->PeHeader->OptionalHeader32.Magic == PECOFF_IMAGE_PE_OPTIONAL_HDR64_MAGIC)
 | |
|     {
 | |
|         /* Set relocation data directory and image base address */
 | |
|         DataDirectory = &Image->PeHeader->OptionalHeader64.DataDirectory[PECOFF_IMAGE_DIRECTORY_ENTRY_BASERELOC];
 | |
|         ImageBase = Image->PeHeader->OptionalHeader64.ImageBase;
 | |
| 
 | |
|         /* Check if loaded 64-bit PE32+ image should be relocated */
 | |
|         if(Image->PeHeader->OptionalHeader64.NumberOfRvaAndSizes <= PECOFF_IMAGE_DIRECTORY_ENTRY_BASERELOC ||
 | |
|         DataDirectory->VirtualAddress == 0 || DataDirectory->Size < sizeof(PECOFF_IMAGE_BASE_RELOCATION))
 | |
|         {
 | |
|             /* No need to relocate the image */
 | |
|             return STATUS_EFI_SUCCESS;
 | |
|         }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         /* Check if loaded 32-bit PE32 image should be relocated */
 | |
|         /* Set relocation data directory and image base address */
 | |
|         DataDirectory = &Image->PeHeader->OptionalHeader32.DataDirectory[PECOFF_IMAGE_DIRECTORY_ENTRY_BASERELOC];
 | |
|         ImageBase = Image->PeHeader->OptionalHeader32.ImageBase;
 | |
| 
 | |
|         if(Image->PeHeader->OptionalHeader32.NumberOfRvaAndSizes <= PECOFF_IMAGE_DIRECTORY_ENTRY_BASERELOC ||
 | |
|         DataDirectory->VirtualAddress == 0 || DataDirectory->Size < sizeof(PECOFF_IMAGE_BASE_RELOCATION))
 | |
|         {
 | |
|             /* No need to relocate the image */
 | |
|             return STATUS_EFI_SUCCESS;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Set relocation pointers */
 | |
|     RelocationDir = (PPECOFF_IMAGE_BASE_RELOCATION)((ULONG_PTR)Image->Data + DataDirectory->VirtualAddress);
 | |
|     RelocationEnd = (PPECOFF_IMAGE_BASE_RELOCATION)((ULONG_PTR)RelocationDir + DataDirectory->Size);
 | |
| 
 | |
|     /* Do relocations */
 | |
|     while(RelocationDir < RelocationEnd && RelocationDir->SizeOfBlock > 0)
 | |
|     {
 | |
|         /* Calculate number of relocations needed, address and type offset */
 | |
|         Count = (RelocationDir->SizeOfBlock - sizeof(PECOFF_IMAGE_BASE_RELOCATION)) / sizeof(USHORT);
 | |
|         Address = (PUINT)((PUCHAR)Image->Data + RelocationDir->VirtualAddress);
 | |
|         TypeOffset = (PUSHORT)((PUCHAR)RelocationDir + sizeof(PECOFF_IMAGE_BASE_RELOCATION));
 | |
| 
 | |
|         /* Do relocations */
 | |
|         while(Count--)
 | |
|         {
 | |
|             /* Calculate offset and relocation type */
 | |
|             Offset = *TypeOffset & 0xFFF;
 | |
|             Type = *TypeOffset >> 12;
 | |
| 
 | |
|             /* Check if end of the loaded address reached */
 | |
|             if((PVOID)(PUSHORT)(Address + Offset) >= (PUCHAR)Image->Data + Image->ImageSize)
 | |
|             {
 | |
|                 /* Do not relocate after the end of loaded image */
 | |
|                 break;
 | |
|             }
 | |
| 
 | |
|             /* Make sure we are not going to relocate into .reloc section */
 | |
|             if((ULONG_PTR)(Address + Offset) < (ULONG_PTR)RelocationDir ||
 | |
|                (ULONG_PTR)(Address + Offset) >= (ULONG_PTR)RelocationEnd)
 | |
|             {
 | |
|                 /* Apply relocation fixup */
 | |
|                 switch (Type)
 | |
|                 {
 | |
|                     case PECOFF_IMAGE_REL_BASED_ABSOLUTE:
 | |
|                         /* No relocation required */
 | |
|                         break;
 | |
|                     case PECOFF_IMAGE_REL_BASED_DIR64:
 | |
|                         /* 64-bit relocation */
 | |
|                         LongPtr = (PULONGLONG)((PUCHAR)Address + Offset);
 | |
|                         *LongPtr = *LongPtr - ImageBase + (UINT_PTR)Image->VirtualAddress;
 | |
|                         break;
 | |
|                     case PECOFF_IMAGE_REL_BASED_HIGHLOW:
 | |
|                         /* 32-bit relocation of hight and low half of address */
 | |
|                         ShortPtr = (PUINT)((PUCHAR)Address + Offset);
 | |
|                         *ShortPtr = *ShortPtr - ImageBase + (UINT_PTR)Image->VirtualAddress;
 | |
|                         break;
 | |
|                     default:
 | |
|                         /* Unknown or unsupported relocation type */
 | |
|                         return STATUS_EFI_UNSUPPORTED;
 | |
|                 }
 | |
|             }
 | |
|             /* Increment the type offset */
 | |
|             TypeOffset++;
 | |
|         }
 | |
| 
 | |
|         /* Next relocation */
 | |
|         RelocationDir = (PPECOFF_IMAGE_BASE_RELOCATION)((PUCHAR)RelocationDir + RelocationDir->SizeOfBlock);
 | |
|     }
 | |
| 
 | |
|     /* Return SUCCESS */
 | |
|     return STATUS_EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Unloads a PE/COFF image file and frees allocated memory.
 | |
|  *
 | |
|  * @param ImagePointer
 | |
|  *        Supplies a pointer to the PE/COFF context structure representing the loaded image.
 | |
|  *
 | |
|  * @return This routine returns a status code.
 | |
|  *
 | |
|  * @since XT 1.0
 | |
|  */
 | |
| XTCDECL
 | |
| EFI_STATUS
 | |
| PeCoff::UnloadImage(IN PVOID ImagePointer)
 | |
| {
 | |
|     PPECOFF_IMAGE_CONTEXT Image;
 | |
|     EFI_STATUS Status;
 | |
| 
 | |
|     /* Get PE/COFF image pointer*/
 | |
|     Image = (PPECOFF_IMAGE_CONTEXT)ImagePointer;
 | |
| 
 | |
|     /* Validate input data */
 | |
|     if(!Image || !Image->Data)
 | |
|     {
 | |
|         /* Invalid parameter passed */
 | |
|         return STATUS_EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     /* Free memory allocated for the image */
 | |
|     Status = XtLdrProtocol->Memory.FreePages(Image->ImagePages, (EFI_PHYSICAL_ADDRESS)(UINT_PTR)Image->Data);
 | |
|     Status |= XtLdrProtocol->Memory.FreePool(Image);
 | |
| 
 | |
|     /* Return status */
 | |
|     return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Validates a PE/COFF image headers.
 | |
|  *
 | |
|  * @param ImagePointer
 | |
|  *        Supplies a pointer to the PE/COFF context structure representing the loaded image.
 | |
|  *
 | |
|  * @return This routine returns a status code.
 | |
|  *
 | |
|  * @since XT 1.0
 | |
|  */
 | |
| XTCDECL
 | |
| EFI_STATUS
 | |
| PeCoff::VerifyImage(IN PVOID ImagePointer)
 | |
| {
 | |
|     PPECOFF_IMAGE_CONTEXT Image;
 | |
| 
 | |
|     /* Get PE/COFF image pointer*/
 | |
|     Image = (PPECOFF_IMAGE_CONTEXT)ImagePointer;
 | |
| 
 | |
|     /* Validate input data */
 | |
|     if(!Image || !Image->PeHeader)
 | |
|     {
 | |
|         /* Invalid parameter passed */
 | |
|         return STATUS_EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     /* Validate file size */
 | |
|     if(Image->FileSize < sizeof(PECOFF_IMAGE_DOS_HEADER))
 | |
|     {
 | |
|         /* PE/COFF image shorter than DOS header, return error*/
 | |
|         return STATUS_EFI_END_OF_FILE;
 | |
|     }
 | |
| 
 | |
|     /* Validate DOS header */
 | |
|     if(Image->DosHeader->Magic != PECOFF_IMAGE_DOS_SIGNATURE)
 | |
|     {
 | |
|         /* Invalid DOS signature, return error */
 | |
|         return STATUS_EFI_INCOMPATIBLE_VERSION;
 | |
|     }
 | |
| 
 | |
|     /* Validate PE header */
 | |
|     if(Image->PeHeader->Signature != PECOFF_IMAGE_NT_SIGNATURE &&
 | |
|        Image->PeHeader->Signature != PECOFF_IMAGE_XT_SIGNATURE)
 | |
|     {
 | |
|         /* Invalid PE signature, return error */
 | |
|         return STATUS_EFI_INCOMPATIBLE_VERSION;
 | |
|     }
 | |
| 
 | |
|     /* Validate optional header */
 | |
|     if(Image->PeHeader->OptionalHeader32.Magic != PECOFF_IMAGE_PE_OPTIONAL_HDR32_MAGIC &&
 | |
|        Image->PeHeader->OptionalHeader64.Magic != PECOFF_IMAGE_PE_OPTIONAL_HDR64_MAGIC)
 | |
|     {
 | |
|         /* Invalid optional header signature, return error */
 | |
|         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 a status code.
 | |
|  *
 | |
|  * @since XT 1.0
 | |
|  */
 | |
| XTCDECL
 | |
| EFI_STATUS
 | |
| XtLdrModuleMain(IN EFI_HANDLE ImageHandle,
 | |
|                 IN PEFI_SYSTEM_TABLE SystemTable)
 | |
| {
 | |
|     /* Initialize PECOFF module */
 | |
|     return PeCoff::InitializeModule(ImageHandle, SystemTable);
 | |
| }
 |