EFI_STATUS EFIAPI LoadPeCoffImage ( IN VOID *PeCoffImage, OUT EFI_PHYSICAL_ADDRESS *ImageAddress, OUT UINT64 *ImageSize, OUT EFI_PHYSICAL_ADDRESS *EntryPoint ) { RETURN_STATUS Status; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; VOID *Buffer; ZeroMem (&ImageContext, sizeof (ImageContext)); ImageContext.Handle = PeCoffImage; ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; Status = PeCoffLoaderGetImageInfo (&ImageContext); ASSERT_EFI_ERROR (Status); // // Allocate Memory for the image // Buffer = AllocatePages (EFI_SIZE_TO_PAGES((UINT32)ImageContext.ImageSize)); ASSERT (Buffer != 0); ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer; // // Load the image to our new buffer // Status = PeCoffLoaderLoadImage (&ImageContext); ASSERT_EFI_ERROR (Status); // // Relocate the image in our new buffer // Status = PeCoffLoaderRelocateImage (&ImageContext); ASSERT_EFI_ERROR (Status); *ImageAddress = ImageContext.ImageAddress; *ImageSize = ImageContext.ImageSize; *EntryPoint = ImageContext.EntryPoint; // // Flush not needed for all architectures. We could have a processor specific // function in this library that does the no-op if needed. // InvalidateInstructionCacheRange ((VOID *)(UINTN)*ImageAddress, (UINTN)*ImageSize); return Status; }
/*---------------------------------------------------------------------------------------*/ EFI_STATUS LoadExeFileInMemory ( IN EFI_GUID *File, IN EFI_SECTION_TYPE SectionType, IN EFI_PHYSICAL_ADDRESS Memory, OUT UINTN *ImageSize, OUT EFI_IMAGE_ENTRY_POINT *ExeEntryPoint ) { EFI_STATUS Status; VOID *SourceBuffer; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; *ExeEntryPoint = NULL; SourceBuffer = NULL; *ImageSize = 0; // Look for SMM foundation FFS FV Status = GetSectionFromAnyFv (File, SectionType, 0, (VOID **) &SourceBuffer, ImageSize); if (EFI_ERROR (Status)) { return (Status); } ImageContext.Handle = SourceBuffer; ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; // Gather SMM foundation Coff image information Status = PeCoffLoaderGetImageInfo (&ImageContext); if (EFI_ERROR (Status)) { FreePool (SourceBuffer); return Status; } ImageContext.ImageAddress = Memory; ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; ImageContext.ImageAddress &= ~(ImageContext.SectionAlignment - 1); // Load SMM foundation FFS in SMM space Status = PeCoffLoaderLoadImage (&ImageContext); if (!EFI_ERROR (Status)) { // // Relocate the image in our new buffer // Status = PeCoffLoaderRelocateImage (&ImageContext); if (!EFI_ERROR (Status)) { *ExeEntryPoint = (EFI_IMAGE_ENTRY_POINT)(UINTN)ImageContext.EntryPoint; //Status = (*ExeEntryPoint) (NULL, gST); } } FreePool (SourceBuffer); return Status; }
RETURN_STATUS EFIAPI SecPeCoffGetEntryPoint ( IN VOID *Pe32Data, IN OUT VOID **EntryPoint ) { EFI_STATUS Status; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; ZeroMem (&ImageContext, sizeof (ImageContext)); ImageContext.Handle = Pe32Data; ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE) SecImageRead; Status = PeCoffLoaderGetImageInfo (&ImageContext); if (EFI_ERROR (Status)) { return Status; } if (ImageContext.ImageAddress != (UINTN)Pe32Data) { // // Relocate image to match the address where it resides // ImageContext.ImageAddress = (UINTN)Pe32Data; Status = PeCoffLoaderLoadImage (&ImageContext); if (EFI_ERROR (Status)) { return Status; } Status = PeCoffLoaderRelocateImage (&ImageContext); if (EFI_ERROR (Status)) { return Status; } } else { // // Or just return image entry point // ImageContext.PdbPointer = PeCoffLoaderGetPdbPointer (Pe32Data); Status = PeCoffLoaderGetEntryPoint (Pe32Data, EntryPoint); if (EFI_ERROR (Status)) { return Status; } ImageContext.EntryPoint = (UINTN)*EntryPoint; } // On Unix a dlopen is done that will change the entry point SecPeCoffRelocateImageExtraAction (&ImageContext); *EntryPoint = (VOID *)(UINTN)ImageContext.EntryPoint; return Status; }
RETURN_STATUS EFIAPI SecPeCoffGetEntryPoint ( IN VOID *Pe32Data, IN OUT VOID **EntryPoint ) { EFI_STATUS Status; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; ZeroMem (&ImageContext, sizeof (ImageContext)); ImageContext.Handle = Pe32Data; ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE) SecImageRead; Status = PeCoffLoaderGetImageInfo (&ImageContext); if (EFI_ERROR (Status)) { return Status; } // // Allocate space in NT (not emulator) memory with ReadWrite and Execute attribute. // Extra space is for alignment // ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) VirtualAlloc (NULL, (SIZE_T) (ImageContext.ImageSize + (ImageContext.SectionAlignment * 2)), MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (ImageContext.ImageAddress == 0) { return EFI_OUT_OF_RESOURCES; } // // Align buffer on section boundary // ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)ImageContext.SectionAlignment - 1); Status = PeCoffLoaderLoadImage (&ImageContext); if (EFI_ERROR (Status)) { return Status; } Status = PeCoffLoaderRelocateImage (&ImageContext); if (EFI_ERROR (Status)) { return Status; } *EntryPoint = (VOID *)(UINTN)ImageContext.EntryPoint; return EFI_SUCCESS; }
/** Loads an EFI image into SMRAM. @param DriverEntry EFI_SMM_DRIVER_ENTRY instance @return EFI_STATUS **/ EFI_STATUS EFIAPI SmmLoadImage ( IN OUT EFI_SMM_DRIVER_ENTRY *DriverEntry ) { UINT32 AuthenticationStatus; UINTN FilePathSize; VOID *Buffer; UINTN Size; UINTN PageCount; EFI_GUID *NameGuid; EFI_STATUS Status; EFI_STATUS SecurityStatus; EFI_HANDLE DeviceHandle; EFI_PHYSICAL_ADDRESS DstBuffer; EFI_DEVICE_PATH_PROTOCOL *FilePath; EFI_DEVICE_PATH_PROTOCOL *OriginalFilePath; EFI_DEVICE_PATH_PROTOCOL *HandleFilePath; EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; Buffer = NULL; Size = 0; Fv = DriverEntry->Fv; NameGuid = &DriverEntry->FileName; FilePath = DriverEntry->FvFileDevicePath; OriginalFilePath = FilePath; HandleFilePath = FilePath; DeviceHandle = NULL; SecurityStatus = EFI_SUCCESS; Status = EFI_SUCCESS; AuthenticationStatus = 0; // // Try to get the image device handle by checking the match protocol. // Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &HandleFilePath, &DeviceHandle); if (EFI_ERROR(Status)) { return Status; } // // If the Security Architectural Protocol has not been located yet, then attempt to locate it // if (mSecurity == NULL) { gBS->LocateProtocol (&gEfiSecurityArchProtocolGuid, NULL, (VOID**)&mSecurity); } // // Verify the Authentication Status through the Security Architectural Protocol // if ((mSecurity != NULL) && (OriginalFilePath != NULL)) { SecurityStatus = mSecurity->FileAuthenticationState ( mSecurity, AuthenticationStatus, OriginalFilePath ); if (EFI_ERROR (SecurityStatus) && SecurityStatus != EFI_SECURITY_VIOLATION) { Status = SecurityStatus; return Status; } } // // Pull out just the file portion of the DevicePath for the LoadedImage FilePath // FilePath = OriginalFilePath; Status = gBS->HandleProtocol (DeviceHandle, &gEfiDevicePathProtocolGuid, (VOID **)&HandleFilePath); if (!EFI_ERROR (Status)) { FilePathSize = GetDevicePathSize (HandleFilePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL); FilePath = (EFI_DEVICE_PATH_PROTOCOL *) (((UINT8 *)FilePath) + FilePathSize ); } // // Try reading PE32 section firstly // Status = Fv->ReadSection ( Fv, NameGuid, EFI_SECTION_PE32, 0, &Buffer, &Size, &AuthenticationStatus ); if (EFI_ERROR (Status)) { // // Try reading TE section secondly // Buffer = NULL; Size = 0; Status = Fv->ReadSection ( Fv, NameGuid, EFI_SECTION_TE, 0, &Buffer, &Size, &AuthenticationStatus ); } if (EFI_ERROR (Status)) { if (Buffer != NULL) { Status = gBS->FreePool (Buffer); } return Status; } // // Initialize ImageContext // ImageContext.Handle = Buffer; ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; // // Get information about the image being loaded // Status = PeCoffLoaderGetImageInfo (&ImageContext); if (EFI_ERROR (Status)) { if (Buffer != NULL) { Status = gBS->FreePool (Buffer); } return Status; } // // if Loading module at Fixed Address feature is enabled, then cut out a memory range started from TESG BASE // to hold the Smm driver code // if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0) { // // Get the fixed loading address assigned by Build tool // Status = GetPeCoffImageFixLoadingAssignedAddress (&ImageContext); if (!EFI_ERROR (Status)) { // // Since the memory range to load Smm core alreay been cut out, so no need to allocate and free this range // following statements is to bypass SmmFreePages // PageCount = 0; DstBuffer = (UINTN)gLoadModuleAtFixAddressSmramBase; } else { DEBUG ((EFI_D_INFO|EFI_D_LOAD, "LOADING MODULE FIXED ERROR: Failed to load module at fixed address. \n")); // // allocate the memory to load the SMM driver // PageCount = (UINTN)EFI_SIZE_TO_PAGES((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment); DstBuffer = (UINTN)(-1); Status = SmmAllocatePages ( AllocateMaxAddress, EfiRuntimeServicesCode, PageCount, &DstBuffer ); if (EFI_ERROR (Status)) { if (Buffer != NULL) { Status = gBS->FreePool (Buffer); } return Status; } ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer; } } else { PageCount = (UINTN)EFI_SIZE_TO_PAGES((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment); DstBuffer = (UINTN)(-1); Status = SmmAllocatePages ( AllocateMaxAddress, EfiRuntimeServicesCode, PageCount, &DstBuffer ); if (EFI_ERROR (Status)) { if (Buffer != NULL) { Status = gBS->FreePool (Buffer); } return Status; } ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer; } // // Align buffer on section boundry // ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; ImageContext.ImageAddress &= ~(ImageContext.SectionAlignment - 1); // // Load the image to our new buffer // Status = PeCoffLoaderLoadImage (&ImageContext); if (EFI_ERROR (Status)) { if (Buffer != NULL) { Status = gBS->FreePool (Buffer); } SmmFreePages (DstBuffer, PageCount); return Status; } // // Relocate the image in our new buffer // Status = PeCoffLoaderRelocateImage (&ImageContext); if (EFI_ERROR (Status)) { if (Buffer != NULL) { Status = gBS->FreePool (Buffer); } SmmFreePages (DstBuffer, PageCount); return Status; } // // Flush the instruction cache so the image data are written before we execute it // InvalidateInstructionCacheRange ((VOID *)(UINTN) ImageContext.ImageAddress, (UINTN) ImageContext.ImageSize); // // Save Image EntryPoint in DriverEntry // DriverEntry->ImageEntryPoint = ImageContext.EntryPoint; DriverEntry->ImageBuffer = DstBuffer; DriverEntry->NumberOfPage = PageCount; // // Allocate a Loaded Image Protocol in EfiBootServicesData // Status = gBS->AllocatePool (EfiBootServicesData, sizeof (EFI_LOADED_IMAGE_PROTOCOL), (VOID **)&DriverEntry->LoadedImage); if (EFI_ERROR (Status)) { if (Buffer != NULL) { Status = gBS->FreePool (Buffer); } SmmFreePages (DstBuffer, PageCount); return Status; } // // Fill in the remaining fields of the Loaded Image Protocol instance. // Note: ImageBase is an SMRAM address that can not be accessed outside of SMRAM if SMRAM window is closed. // DriverEntry->LoadedImage->Revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION; DriverEntry->LoadedImage->ParentHandle = gSmmCorePrivate->SmmIplImageHandle; DriverEntry->LoadedImage->SystemTable = gST; DriverEntry->LoadedImage->DeviceHandle = DeviceHandle; // // Make an EfiBootServicesData buffer copy of FilePath // Status = gBS->AllocatePool (EfiBootServicesData, GetDevicePathSize (FilePath), (VOID **)&DriverEntry->LoadedImage->FilePath); if (EFI_ERROR (Status)) { if (Buffer != NULL) { Status = gBS->FreePool (Buffer); } SmmFreePages (DstBuffer, PageCount); return Status; } CopyMem (DriverEntry->LoadedImage->FilePath, FilePath, GetDevicePathSize (FilePath)); DriverEntry->LoadedImage->ImageBase = (VOID *)(UINTN)DriverEntry->ImageBuffer; DriverEntry->LoadedImage->ImageSize = ImageContext.ImageSize; DriverEntry->LoadedImage->ImageCodeType = EfiRuntimeServicesCode; DriverEntry->LoadedImage->ImageDataType = EfiRuntimeServicesData; // // Create a new image handle in the UEFI handle database for the SMM Driver // DriverEntry->ImageHandle = NULL; Status = gBS->InstallMultipleProtocolInterfaces ( &DriverEntry->ImageHandle, &gEfiLoadedImageProtocolGuid, DriverEntry->LoadedImage, NULL ); // // Print the load address and the PDB file name if it is available // DEBUG_CODE_BEGIN (); UINTN Index; UINTN StartIndex; CHAR8 EfiFileName[256]; DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Loading SMM driver at 0x%11p EntryPoint=0x%11p ", (VOID *)(UINTN) ImageContext.ImageAddress, FUNCTION_ENTRY_POINT (ImageContext.EntryPoint))); // // Print Module Name by Pdb file path. // Windows and Unix style file path are all trimmed correctly. // if (ImageContext.PdbPointer != NULL) { StartIndex = 0; for (Index = 0; ImageContext.PdbPointer[Index] != 0; Index++) { if ((ImageContext.PdbPointer[Index] == '\\') || (ImageContext.PdbPointer[Index] == '/')) { StartIndex = Index + 1; } } // // Copy the PDB file name to our temporary string, and replace .pdb with .efi // The PDB file name is limited in the range of 0~255. // If the length is bigger than 255, trim the redudant characters to avoid overflow in array boundary. // for (Index = 0; Index < sizeof (EfiFileName) - 4; Index++) { EfiFileName[Index] = ImageContext.PdbPointer[Index + StartIndex]; if (EfiFileName[Index] == 0) { EfiFileName[Index] = '.'; } if (EfiFileName[Index] == '.') { EfiFileName[Index + 1] = 'e'; EfiFileName[Index + 2] = 'f'; EfiFileName[Index + 3] = 'i'; EfiFileName[Index + 4] = 0; break; } } if (Index == sizeof (EfiFileName) - 4) { EfiFileName[Index] = 0; } DEBUG ((DEBUG_INFO | DEBUG_LOAD, "%a", EfiFileName)); // &Image->ImageContext.PdbPointer[StartIndex])); } DEBUG ((DEBUG_INFO | DEBUG_LOAD, "\n")); DEBUG_CODE_END (); // // Free buffer allocated by Fv->ReadSection. // // The UEFI Boot Services FreePool() function must be used because Fv->ReadSection // used the UEFI Boot Services AllocatePool() function // Status = gBS->FreePool(Buffer); return Status; }
/** Relocate this image under 4G memory for IPF. @param ImageHandle Handle of driver image. @param SystemTable Pointer to system table. @retval EFI_SUCCESS Image successfully relocated. @retval EFI_ABORTED Failed to relocate image. **/ EFI_STATUS RelocateImageUnder4GIfNeeded ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; UINTN NumberOfPages; EFI_PHYSICAL_ADDRESS LoadedImageBase; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; EFI_PHYSICAL_ADDRESS MemoryAddress; EFI_HANDLE NewImageHandle; Status = gBS->HandleProtocol ( ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID *) &LoadedImage ); if (!EFI_ERROR (Status)) { LoadedImageBase = (EFI_PHYSICAL_ADDRESS) (UINTN) LoadedImage->ImageBase; if (LoadedImageBase > 0xffffffff) { NumberOfPages = (UINTN) (DivU64x32(LoadedImage->ImageSize, EFI_PAGE_SIZE) + 1); // // Allocate buffer below 4GB here // Status = AllocateLegacyMemory ( AllocateMaxAddress, 0x7FFFFFFF, NumberOfPages, // do we have to convert this to pages?? &MemoryAddress ); if (EFI_ERROR (Status)) { return Status; } ZeroMem (&ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT)); ImageContext.Handle = (VOID *)(UINTN)LoadedImageBase; ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; // // Get information about the image being loaded // Status = PeCoffLoaderGetImageInfo (&ImageContext); if (EFI_ERROR (Status)) { return Status; } ImageContext.ImageAddress = (PHYSICAL_ADDRESS)MemoryAddress; // // Align buffer on section boundary // ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; ImageContext.ImageAddress &= ~((PHYSICAL_ADDRESS)ImageContext.SectionAlignment - 1); // // Load the image to our new buffer // Status = PeCoffLoaderLoadImage (&ImageContext); if (EFI_ERROR (Status)) { gBS->FreePages (MemoryAddress, NumberOfPages); return Status; } // // Relocate the image in our new buffer // Status = PeCoffLoaderRelocateImage (&ImageContext); if (EFI_ERROR (Status)) { gBS->FreePages (MemoryAddress, NumberOfPages); return Status; } // // Create a new handle with gEfiCallerIdGuid to be used as the ImageHandle fore the reloaded image // NewImageHandle = NULL; Status = gBS->InstallProtocolInterface ( &NewImageHandle, &gEfiCallerIdGuid, EFI_NATIVE_INTERFACE, NULL ); // // Flush the instruction cache so the image data is written before we execute it // InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, SystemTable); if (EFI_ERROR (Status)) { gBS->FreePages (MemoryAddress, NumberOfPages); return Status; } // // return error directly the BS will unload this image // return EFI_ABORTED; } } return EFI_SUCCESS; }
/** Measure PE image into TPM log based on the authenticode image hashing in PE/COFF Specification 8.0 Appendix A. Caution: This function may receive untrusted input. PE/COFF image is external input, so this function will validate its data structure within this image buffer before use. Notes: PE/COFF image is checked by BasePeCoffLib PeCoffLoaderGetImageInfo(). @param[in] PCRIndex TPM PCR index @param[in] ImageAddress Start address of image buffer. @param[in] ImageSize Image size @param[out] DigestList Digeest list of this image. @retval EFI_SUCCESS Successfully measure image. @retval EFI_OUT_OF_RESOURCES No enough resource to measure image. @retval other error value **/ EFI_STATUS MeasurePeImageAndExtend ( IN UINT32 PCRIndex, IN EFI_PHYSICAL_ADDRESS ImageAddress, IN UINTN ImageSize, OUT TPML_DIGEST_VALUES *DigestList ) { EFI_STATUS Status; EFI_IMAGE_DOS_HEADER *DosHdr; UINT32 PeCoffHeaderOffset; EFI_IMAGE_SECTION_HEADER *Section; UINT8 *HashBase; UINTN HashSize; UINTN SumOfBytesHashed; EFI_IMAGE_SECTION_HEADER *SectionHeader; UINTN Index; UINTN Pos; UINT16 Magic; EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; UINT32 NumberOfRvaAndSizes; UINT32 CertSize; HASH_HANDLE HashHandle; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; HashHandle = 0xFFFFFFFF; // Know bad value Status = EFI_UNSUPPORTED; SectionHeader = NULL; // // Check PE/COFF image // ZeroMem (&ImageContext, sizeof (ImageContext)); ImageContext.Handle = (VOID *) (UINTN) ImageAddress; mTrEEDxeImageSize = ImageSize; ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE) TrEEDxeImageRead; // // Get information about the image being loaded // Status = PeCoffLoaderGetImageInfo (&ImageContext); if (EFI_ERROR (Status)) { // // The information can't be got from the invalid PeImage // DEBUG ((DEBUG_INFO, "TreeDxe: PeImage invalid. Cannot retrieve image information.\n")); goto Finish; } DosHdr = (EFI_IMAGE_DOS_HEADER *) (UINTN) ImageAddress; PeCoffHeaderOffset = 0; if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { PeCoffHeaderOffset = DosHdr->e_lfanew; } Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *) (UINTN) ImageAddress + PeCoffHeaderOffset); if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) { Status = EFI_UNSUPPORTED; goto Finish; } // // PE/COFF Image Measurement // // NOTE: The following codes/steps are based upon the authenticode image hashing in // PE/COFF Specification 8.0 Appendix A. // // // 1. Load the image header into memory. // 2. Initialize a SHA hash context. Status = HashStart (&HashHandle); if (EFI_ERROR (Status)) { goto Finish; } // // Measuring PE/COFF Image Header; // But CheckSum field and SECURITY data directory (certificate) are excluded // if (Hdr.Pe32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 && Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value // in the PE/COFF Header. If the MachineType is Itanium(IA64) and the // Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC // then override the magic value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC // Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC; } else { // // Get the magic value from the PE/COFF Optional Header // Magic = Hdr.Pe32->OptionalHeader.Magic; } // // 3. Calculate the distance from the base of the image header to the image checksum address. // 4. Hash the image header from its base to beginning of the image checksum. // HashBase = (UINT8 *) (UINTN) ImageAddress; if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset // NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes; HashSize = (UINTN) ((UINT8 *)(&Hdr.Pe32->OptionalHeader.CheckSum) - HashBase); } else { // // Use PE32+ offset // NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes; HashSize = (UINTN) ((UINT8 *)(&Hdr.Pe32Plus->OptionalHeader.CheckSum) - HashBase); } Status = HashUpdate (HashHandle, HashBase, HashSize); if (EFI_ERROR (Status)) { goto Finish; } // // 5. Skip over the image checksum (it occupies a single ULONG). // if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) { // // 6. Since there is no Cert Directory in optional header, hash everything // from the end of the checksum to the end of image header. // if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset. // HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32); HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageAddress); } else { // // Use PE32+ offset. // HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32); HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageAddress); } if (HashSize != 0) { Status = HashUpdate (HashHandle, HashBase, HashSize); if (EFI_ERROR (Status)) { goto Finish; } } } else { // // 7. Hash everything from the end of the checksum to the start of the Cert Directory. // if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset // HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32); HashSize = (UINTN) ((UINT8 *)(&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase); } else { // // Use PE32+ offset // HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32); HashSize = (UINTN) ((UINT8 *)(&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase); } if (HashSize != 0) { Status = HashUpdate (HashHandle, HashBase, HashSize); if (EFI_ERROR (Status)) { goto Finish; } } // // 8. Skip over the Cert Directory. (It is sizeof(IMAGE_DATA_DIRECTORY) bytes.) // 9. Hash everything from the end of the Cert Directory to the end of image header. // if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset // HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageAddress); } else { // // Use PE32+ offset // HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageAddress); } if (HashSize != 0) { Status = HashUpdate (HashHandle, HashBase, HashSize); if (EFI_ERROR (Status)) { goto Finish; } } } // // 10. Set the SUM_OF_BYTES_HASHED to the size of the header // if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset // SumOfBytesHashed = Hdr.Pe32->OptionalHeader.SizeOfHeaders; } else { // // Use PE32+ offset // SumOfBytesHashed = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders; } // // 11. Build a temporary table of pointers to all the IMAGE_SECTION_HEADER // structures in the image. The 'NumberOfSections' field of the image // header indicates how big the table should be. Do not include any // IMAGE_SECTION_HEADERs in the table whose 'SizeOfRawData' field is zero. // SectionHeader = (EFI_IMAGE_SECTION_HEADER *) AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * Hdr.Pe32->FileHeader.NumberOfSections); if (SectionHeader == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Finish; } // // 12. Using the 'PointerToRawData' in the referenced section headers as // a key, arrange the elements in the table in ascending order. In other // words, sort the section headers according to the disk-file offset of // the section. // Section = (EFI_IMAGE_SECTION_HEADER *) ( (UINT8 *) (UINTN) ImageAddress + PeCoffHeaderOffset + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER) + Hdr.Pe32->FileHeader.SizeOfOptionalHeader ); for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) { Pos = Index; while ((Pos > 0) && (Section->PointerToRawData < SectionHeader[Pos - 1].PointerToRawData)) { CopyMem (&SectionHeader[Pos], &SectionHeader[Pos - 1], sizeof(EFI_IMAGE_SECTION_HEADER)); Pos--; } CopyMem (&SectionHeader[Pos], Section, sizeof(EFI_IMAGE_SECTION_HEADER)); Section += 1; } // // 13. Walk through the sorted table, bring the corresponding section // into memory, and hash the entire section (using the 'SizeOfRawData' // field in the section header to determine the amount of data to hash). // 14. Add the section's 'SizeOfRawData' to SUM_OF_BYTES_HASHED . // 15. Repeat steps 13 and 14 for all the sections in the sorted table. // for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) { Section = (EFI_IMAGE_SECTION_HEADER *) &SectionHeader[Index]; if (Section->SizeOfRawData == 0) { continue; } HashBase = (UINT8 *) (UINTN) ImageAddress + Section->PointerToRawData; HashSize = (UINTN) Section->SizeOfRawData; Status = HashUpdate (HashHandle, HashBase, HashSize); if (EFI_ERROR (Status)) { goto Finish; } SumOfBytesHashed += HashSize; } // // 16. If the file size is greater than SUM_OF_BYTES_HASHED, there is extra // data in the file that needs to be added to the hash. This data begins // at file offset SUM_OF_BYTES_HASHED and its length is: // FileSize - (CertDirectory->Size) // if (ImageSize > SumOfBytesHashed) { HashBase = (UINT8 *) (UINTN) ImageAddress + SumOfBytesHashed; if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) { CertSize = 0; } else { if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset. // CertSize = Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size; } else { // // Use PE32+ offset. // CertSize = Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size; } } if (ImageSize > CertSize + SumOfBytesHashed) { HashSize = (UINTN) (ImageSize - CertSize - SumOfBytesHashed); Status = HashUpdate (HashHandle, HashBase, HashSize); if (EFI_ERROR (Status)) { goto Finish; } } else if (ImageSize < CertSize + SumOfBytesHashed) { Status = EFI_UNSUPPORTED; goto Finish; } } // // 17. Finalize the SHA hash. // Status = HashCompleteAndExtend (HashHandle, PCRIndex, NULL, 0, DigestList); if (EFI_ERROR (Status)) { goto Finish; } Finish: if (SectionHeader != NULL) { FreePool (SectionHeader); } return Status; }
/** Load the SMM Core image into SMRAM and executes the SMM Core from SMRAM. @param[in] SmramRange Descriptor for the range of SMRAM to reload the currently executing image. @param[in] Context Context to pass into SMM Core @return EFI_STATUS **/ EFI_STATUS ExecuteSmmCoreFromSmram ( IN EFI_SMRAM_DESCRIPTOR *SmramRange, IN VOID *Context ) { EFI_STATUS Status; VOID *SourceBuffer; UINTN SourceSize; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; UINTN PageCount; EFI_PHYSICAL_ADDRESS DestinationBuffer; EFI_IMAGE_ENTRY_POINT EntryPoint; // // Search all Firmware Volumes for a PE/COFF image in a file of type SMM_CORE // Status = GetSectionFromAnyFvByFileType ( EFI_FV_FILETYPE_SMM_CORE, 0, EFI_SECTION_PE32, 0, &SourceBuffer, &SourceSize ); if (EFI_ERROR (Status)) { return Status; } // // Initilize ImageContext // ImageContext.Handle = SourceBuffer; ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; // // Get information about the image being loaded // Status = PeCoffLoaderGetImageInfo (&ImageContext); if (EFI_ERROR (Status)) { return Status; } // // if Loading module at Fixed Address feature is enabled, the SMM core driver will be loaded to // the address assigned by build tool. // if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0) { // // Get the fixed loading address assigned by Build tool // Status = GetPeCoffImageFixLoadingAssignedAddress (&ImageContext); if (!EFI_ERROR (Status)) { // // Since the memory range to load SMM CORE will be cut out in SMM core, so no need to allocate and free this range // PageCount = 0; } else { DEBUG ((EFI_D_INFO, "LOADING MODULE FIXED ERROR: Loading module at fixed address at address failed\n")); // // Allocate memory for the image being loaded from the EFI_SRAM_DESCRIPTOR // specified by SmramRange // PageCount = (UINTN)EFI_SIZE_TO_PAGES((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment); ASSERT ((SmramRange->PhysicalSize & EFI_PAGE_MASK) == 0); ASSERT (SmramRange->PhysicalSize > EFI_PAGES_TO_SIZE (PageCount)); SmramRange->PhysicalSize -= EFI_PAGES_TO_SIZE (PageCount); DestinationBuffer = SmramRange->CpuStart + SmramRange->PhysicalSize; // // Align buffer on section boundry // ImageContext.ImageAddress = DestinationBuffer; } } else { // // Allocate memory for the image being loaded from the EFI_SRAM_DESCRIPTOR // specified by SmramRange // PageCount = (UINTN)EFI_SIZE_TO_PAGES((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment); ASSERT ((SmramRange->PhysicalSize & EFI_PAGE_MASK) == 0); ASSERT (SmramRange->PhysicalSize > EFI_PAGES_TO_SIZE (PageCount)); SmramRange->PhysicalSize -= EFI_PAGES_TO_SIZE (PageCount); DestinationBuffer = SmramRange->CpuStart + SmramRange->PhysicalSize; // // Align buffer on section boundry // ImageContext.ImageAddress = DestinationBuffer; } ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; ImageContext.ImageAddress &= ~(ImageContext.SectionAlignment - 1); // // Print debug message showing SMM Core load address. // DEBUG ((DEBUG_INFO, "SMM IPL loading SMM Core at SMRAM address %p\n", (VOID *)(UINTN)ImageContext.ImageAddress)); // // Load the image to our new buffer // Status = PeCoffLoaderLoadImage (&ImageContext); if (!EFI_ERROR (Status)) { // // Relocate the image in our new buffer // Status = PeCoffLoaderRelocateImage (&ImageContext); if (!EFI_ERROR (Status)) { // // Flush the instruction cache so the image data are written before we execute it // InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); // // Print debug message showing SMM Core entry point address. // DEBUG ((DEBUG_INFO, "SMM IPL calling SMM Core at SMRAM address %p\n", (VOID *)(UINTN)ImageContext.EntryPoint)); // // Execute image // EntryPoint = (EFI_IMAGE_ENTRY_POINT)(UINTN)ImageContext.EntryPoint; Status = EntryPoint ((EFI_HANDLE)Context, gST); } } // // If the load operation, relocate operation, or the image execution return an // error, then free memory allocated from the EFI_SRAM_DESCRIPTOR specified by // SmramRange // if (EFI_ERROR (Status)) { SmramRange->PhysicalSize += EFI_PAGES_TO_SIZE (PageCount); } // // Always free memory allocted by GetFileBufferByFilePath () // FreePool (SourceBuffer); return Status; }
/** Loads and relocates a PE/COFF image into memory. If the image is not relocatable, it will not be loaded into memory and be loaded as XIP image. @param Pe32Data - The base address of the PE/COFF file that is to be loaded and relocated @param ImageAddress - The base address of the relocated PE/COFF image @param ImageSize - The size of the relocated PE/COFF image @param EntryPoint - The entry point of the relocated PE/COFF image @retval EFI_SUCCESS The file was loaded and relocated @retval EFI_OUT_OF_RESOURCES There was not enough memory to load and relocate the PE/COFF file @retval EFI_WARN_BUFFER_TOO_SMALL There is not enough heap to allocate the requested size. This will not prevent the XIP image from being invoked. **/ EFI_STATUS LoadAndRelocatePeCoffImage ( IN VOID *Pe32Data, OUT EFI_PHYSICAL_ADDRESS *ImageAddress, OUT UINT64 *ImageSize, OUT EFI_PHYSICAL_ADDRESS *EntryPoint ) { EFI_STATUS Status; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; PEI_CORE_INSTANCE *Private; UINT64 AlignImageSize; BOOLEAN IsXipImage; EFI_STATUS ReturnStatus; Private = PEI_CORE_INSTANCE_FROM_PS_THIS (GetPeiServicesTablePointer ()); ReturnStatus = EFI_SUCCESS; IsXipImage = FALSE; ZeroMem (&ImageContext, sizeof (ImageContext)); ImageContext.Handle = Pe32Data; Status = GetImageReadFunction (&ImageContext); ASSERT_EFI_ERROR (Status); Status = PeCoffLoaderGetImageInfo (&ImageContext); if (EFI_ERROR (Status)) { return Status; } // // XIP image that ImageAddress is same to Image handle. // if (ImageContext.ImageAddress == (EFI_PHYSICAL_ADDRESS)(UINTN) Pe32Data) { IsXipImage = TRUE; } // // When Image has no reloc section, it can't be relocated into memory. // if (ImageContext.RelocationsStripped && (Private->PeiMemoryInstalled) && (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME || PcdGetBool (PcdShadowPeimOnS3Boot))) { DEBUG ((EFI_D_INFO|EFI_D_LOAD, "The image at 0x%08x without reloc section can't be loaded into memory\n", (UINTN) Pe32Data)); } // // Set default base address to current image address. // ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Pe32Data; // // Allocate Memory for the image when memory is ready, boot mode is not S3, and image is relocatable. // if ((!ImageContext.RelocationsStripped) && (Private->PeiMemoryInstalled) && (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME || PcdGetBool (PcdShadowPeimOnS3Boot))) { // // Allocate more buffer to avoid buffer overflow. // if (ImageContext.IsTeImage) { AlignImageSize = ImageContext.ImageSize + ((EFI_TE_IMAGE_HEADER *) Pe32Data)->StrippedSize - sizeof (EFI_TE_IMAGE_HEADER); } else { AlignImageSize = ImageContext.ImageSize; } if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) { AlignImageSize += ImageContext.SectionAlignment; } if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0 && (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME)) { Status = GetPeCoffImageFixLoadingAssignedAddress(&ImageContext, Private); if (EFI_ERROR (Status)){ DEBUG ((EFI_D_INFO|EFI_D_LOAD, "LOADING MODULE FIXED ERROR: Failed to load module at fixed address. \n")); // // The PEIM is not assiged valid address, try to allocate page to load it. // ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) AllocatePages (EFI_SIZE_TO_PAGES ((UINT32) AlignImageSize)); } } else { ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) AllocatePages (EFI_SIZE_TO_PAGES ((UINT32) AlignImageSize)); } if (ImageContext.ImageAddress != 0) { // // Adjust the Image Address to make sure it is section alignment. // if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) { ImageContext.ImageAddress = (ImageContext.ImageAddress + ImageContext.SectionAlignment - 1) & ~((UINTN)ImageContext.SectionAlignment - 1); } // // Fix alignment requirement when Load IPF TeImage into memory. // Skip the reserved space for the stripped PeHeader when load TeImage into memory. // if (ImageContext.IsTeImage) { ImageContext.ImageAddress = ImageContext.ImageAddress + ((EFI_TE_IMAGE_HEADER *) Pe32Data)->StrippedSize - sizeof (EFI_TE_IMAGE_HEADER); } } else { // // No enough memory resource. // if (IsXipImage) { // // XIP image can still be invoked. // ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Pe32Data; ReturnStatus = EFI_WARN_BUFFER_TOO_SMALL; } else { // // Non XIP image can't be loaded because no enough memory is allocated. // ASSERT (FALSE); return EFI_OUT_OF_RESOURCES; } } } // // Load the image to our new buffer // Status = PeCoffLoaderLoadImage (&ImageContext); if (EFI_ERROR (Status)) { return Status; } // // Relocate the image in our new buffer // Status = PeCoffLoaderRelocateImage (&ImageContext); if (EFI_ERROR (Status)) { return Status; } // // Flush the instruction cache so the image data is written before we execute it // if (ImageContext.ImageAddress != (EFI_PHYSICAL_ADDRESS)(UINTN) Pe32Data) { InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); } *ImageAddress = ImageContext.ImageAddress; *ImageSize = ImageContext.ImageSize; *EntryPoint = ImageContext.EntryPoint; return ReturnStatus; }
/** Entrypoint of Boot script exector driver, this function will be executed in normal boot phase and invoked by DXE dispatch. @param[in] ImageHandle The firmware allocated handle for the EFI image. @param[in] SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS The entry point is executed successfully. @retval other Some error occurs when executing this entry point. **/ EFI_STATUS EFIAPI BootScriptExecutorEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { UINT8 *Buffer; UINTN BufferSize; UINTN Pages; EFI_PHYSICAL_ADDRESS FfsBuffer; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; BOOT_SCRIPT_EXECUTOR_VARIABLE *EfiBootScriptExecutorVariable; EFI_PHYSICAL_ADDRESS BootScriptExecutorBuffer; EFI_STATUS Status; VOID *DevicePath; EFI_HANDLE NewImageHandle; // // Test if the gEfiCallerIdGuid of this image is already installed. if not, the entry // point is loaded by DXE code which is the first time loaded. or else, it is already // be reloaded be itself.This is a work-around // Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &DevicePath); if (EFI_ERROR (Status)) { // // This is the first-time loaded by DXE core. reload itself to NVS mem // // // A workarouond: Here we install a dummy handle // NewImageHandle = NULL; Status = gBS->InstallProtocolInterface ( &NewImageHandle, &gEfiCallerIdGuid, EFI_NATIVE_INTERFACE, NULL ); Status = GetSectionFromAnyFv ( &gEfiCallerIdGuid, EFI_SECTION_PE32, 0, (VOID **) &Buffer, &BufferSize ); ImageContext.Handle = Buffer; ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; // // Get information about the image being loaded // Status = PeCoffLoaderGetImageInfo (&ImageContext); if (EFI_ERROR (Status)) { return Status; } Pages = EFI_SIZE_TO_PAGES(BufferSize + ImageContext.SectionAlignment); FfsBuffer = 0xFFFFFFFF; Status = gBS->AllocatePages ( AllocateMaxAddress, EfiACPIMemoryNVS, Pages, &FfsBuffer ); if (EFI_ERROR (Status)) { return EFI_OUT_OF_RESOURCES; } ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer; // // Align buffer on section boundry // ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; ImageContext.ImageAddress &= ~(ImageContext.SectionAlignment - 1); // // Load the image to our new buffer // Status = PeCoffLoaderLoadImage (&ImageContext); if (EFI_ERROR (Status)) { gBS->FreePages (FfsBuffer, Pages); return Status; } // // Relocate the image in our new buffer // Status = PeCoffLoaderRelocateImage (&ImageContext); if (EFI_ERROR (Status)) { PeCoffLoaderUnloadImage (&ImageContext); gBS->FreePages (FfsBuffer, Pages); return Status; } // // Flush the instruction cache so the image data is written before we execute it // InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, SystemTable); if (EFI_ERROR (Status)) { gBS->FreePages (FfsBuffer, Pages); return Status; } // // Additional step for BootScript integrity // Save BootScriptExecutor image // Status = SaveLockBox ( &mBootScriptExecutorImageGuid, (VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize ); ASSERT_EFI_ERROR (Status); Status = SetLockBoxAttributes (&mBootScriptExecutorImageGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); ASSERT_EFI_ERROR (Status); } else { // // the entry point is invoked after reloading. following code only run in ACPI NVS // BufferSize = sizeof (BOOT_SCRIPT_EXECUTOR_VARIABLE); BootScriptExecutorBuffer = 0xFFFFFFFF; Pages = EFI_SIZE_TO_PAGES(BufferSize); Status = gBS->AllocatePages ( AllocateMaxAddress, EfiACPIMemoryNVS, Pages, &BootScriptExecutorBuffer ); if (EFI_ERROR (Status)) { return EFI_OUT_OF_RESOURCES; } EfiBootScriptExecutorVariable = (BOOT_SCRIPT_EXECUTOR_VARIABLE *)(UINTN)BootScriptExecutorBuffer; EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = (UINTN) S3BootScriptExecutorEntryFunction ; Status = SaveLockBox ( &gEfiBootScriptExecutorVariableGuid, &BootScriptExecutorBuffer, sizeof(BootScriptExecutorBuffer) ); ASSERT_EFI_ERROR (Status); // // Additional step for BootScript integrity // Save BootScriptExecutor context // Status = SaveLockBox ( &gEfiBootScriptExecutorContextGuid, EfiBootScriptExecutorVariable, sizeof(*EfiBootScriptExecutorVariable) ); ASSERT_EFI_ERROR (Status); Status = SetLockBoxAttributes (&gEfiBootScriptExecutorContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); ASSERT_EFI_ERROR (Status); } return EFI_SUCCESS; }
/** Relocate this image under 4G memory. @param ImageHandle Handle of driver image. @param SystemTable Pointer to system table. @retval EFI_SUCCESS Image successfully relocated. @retval EFI_ABORTED Failed to relocate image. **/ EFI_STATUS RelocateImageUnder4GIfNeeded ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; UINT8 *Buffer; UINTN BufferSize; EFI_HANDLE NewImageHandle; UINTN Pages; EFI_PHYSICAL_ADDRESS FfsBuffer; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; VOID *Interface; // // If it is already <4G, no need do relocate // if ((UINTN)RelocateImageUnder4GIfNeeded < 0xFFFFFFFF) { return EFI_SUCCESS; } // // If locate gEfiCallerIdGuid success, it means 2nd entry. // Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &Interface); if (!EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "FspNotifyDxe - 2nd entry\n")); return EFI_SUCCESS; } DEBUG ((DEBUG_INFO, "FspNotifyDxe - 1st entry\n")); // // Here we install a dummy handle // NewImageHandle = NULL; Status = gBS->InstallProtocolInterface ( &NewImageHandle, &gEfiCallerIdGuid, EFI_NATIVE_INTERFACE, NULL ); ASSERT_EFI_ERROR (Status); // // Reload image itself to <4G mem // Status = GetSectionFromAnyFv ( &gEfiCallerIdGuid, EFI_SECTION_PE32, 0, (VOID **) &Buffer, &BufferSize ); ASSERT_EFI_ERROR (Status); ImageContext.Handle = Buffer; ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; // // Get information about the image being loaded // Status = PeCoffLoaderGetImageInfo (&ImageContext); ASSERT_EFI_ERROR (Status); if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) { Pages = EFI_SIZE_TO_PAGES ((UINTN) (ImageContext.ImageSize + ImageContext.SectionAlignment)); } else { Pages = EFI_SIZE_TO_PAGES ((UINTN) ImageContext.ImageSize); } FfsBuffer = 0xFFFFFFFF; Status = gBS->AllocatePages ( AllocateMaxAddress, EfiBootServicesCode, Pages, &FfsBuffer ); ASSERT_EFI_ERROR (Status); ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer; // // Align buffer on section boundry // ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)(ImageContext.SectionAlignment - 1)); // // Load the image to our new buffer // Status = PeCoffLoaderLoadImage (&ImageContext); ASSERT_EFI_ERROR (Status); // // Relocate the image in our new buffer // Status = PeCoffLoaderRelocateImage (&ImageContext); ASSERT_EFI_ERROR (Status); // // Free the buffer allocated by ReadSection since the image has been relocated in the new buffer // gBS->FreePool (Buffer); // // Flush the instruction cache so the image data is written before we execute it // InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); DEBUG ((DEBUG_INFO, "Loading driver at 0x%08x EntryPoint=0x%08x\n", (UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.EntryPoint)); Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, gST); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Error: Image at 0x%08x start failed: %r\n", ImageContext.ImageAddress, Status)); gBS->FreePages (FfsBuffer, Pages); } // // return error to unload >4G copy, if we already relocate itself to <4G. // return EFI_ALREADY_STARTED; }
/** Loads and relocates a PE/COFF image into memory. If the image is not relocatable, it will not be loaded into memory and be loaded as XIP image. @param FileHandle - Pointer to the FFS file header of the image. @param Pe32Data - The base address of the PE/COFF file that is to be loaded and relocated @param ImageAddress - The base address of the relocated PE/COFF image @param ImageSize - The size of the relocated PE/COFF image @param EntryPoint - The entry point of the relocated PE/COFF image @retval EFI_SUCCESS The file was loaded and relocated @retval EFI_OUT_OF_RESOURCES There was not enough memory to load and relocate the PE/COFF file @retval EFI_WARN_BUFFER_TOO_SMALL There is not enough heap to allocate the requested size. This will not prevent the XIP image from being invoked. **/ EFI_STATUS LoadAndRelocatePeCoffImage ( IN EFI_PEI_FILE_HANDLE FileHandle, IN VOID *Pe32Data, OUT EFI_PHYSICAL_ADDRESS *ImageAddress, OUT UINT64 *ImageSize, OUT EFI_PHYSICAL_ADDRESS *EntryPoint ) { EFI_STATUS Status; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; PEI_CORE_INSTANCE *Private; UINT64 AlignImageSize; BOOLEAN IsXipImage; EFI_STATUS ReturnStatus; BOOLEAN IsS3Boot; BOOLEAN IsPeiModule; BOOLEAN IsRegisterForShadow; EFI_FV_FILE_INFO FileInfo; Private = PEI_CORE_INSTANCE_FROM_PS_THIS (GetPeiServicesTablePointer ()); ReturnStatus = EFI_SUCCESS; IsXipImage = FALSE; ZeroMem (&ImageContext, sizeof (ImageContext)); ImageContext.Handle = Pe32Data; ImageContext.ImageRead = PeiImageRead; Status = PeCoffLoaderGetImageInfo (&ImageContext); if (EFI_ERROR (Status)) { return Status; } // // Initilize local IsS3Boot and IsRegisterForShadow variable // IsS3Boot = FALSE; if (Private->HobList.HandoffInformationTable->BootMode == BOOT_ON_S3_RESUME) { IsS3Boot = TRUE; } IsRegisterForShadow = FALSE; if ((Private->CurrentFileHandle == FileHandle) && (Private->Fv[Private->CurrentPeimFvCount].PeimState[Private->CurrentPeimCount] == PEIM_STATE_REGISTER_FOR_SHADOW)) { IsRegisterForShadow = TRUE; } // // XIP image that ImageAddress is same to Image handle. // if (ImageContext.ImageAddress == (EFI_PHYSICAL_ADDRESS)(UINTN) Pe32Data) { IsXipImage = TRUE; } // // Get file type first // Status = PeiServicesFfsGetFileInfo (FileHandle, &FileInfo); ASSERT_EFI_ERROR (Status); // // Check whether the file type is PEI module. // IsPeiModule = FALSE; if (FileInfo.FileType == EFI_FV_FILETYPE_PEI_CORE || FileInfo.FileType == EFI_FV_FILETYPE_PEIM || FileInfo.FileType == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER) { IsPeiModule = TRUE; } // // When Image has no reloc section, it can't be relocated into memory. // if (ImageContext.RelocationsStripped && (Private->PeiMemoryInstalled) && ((!IsPeiModule) || (!IsS3Boot && (PcdGetBool (PcdShadowPeimOnBoot) || IsRegisterForShadow)) || (IsS3Boot && PcdGetBool (PcdShadowPeimOnS3Boot)))) { DEBUG ((EFI_D_INFO|EFI_D_LOAD, "The image at 0x%08x without reloc section can't be loaded into memory\n", (UINTN) Pe32Data)); } // // Set default base address to current image address. // ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Pe32Data; // // Allocate Memory for the image when memory is ready, and image is relocatable. // On normal boot, PcdShadowPeimOnBoot decides whether load PEIM or PeiCore into memory. // On S3 boot, PcdShadowPeimOnS3Boot decides whether load PEIM or PeiCore into memory. // if ((!ImageContext.RelocationsStripped) && (Private->PeiMemoryInstalled) && ((!IsPeiModule) || (!IsS3Boot && (PcdGetBool (PcdShadowPeimOnBoot) || IsRegisterForShadow)) || (IsS3Boot && PcdGetBool (PcdShadowPeimOnS3Boot)))) { // // Allocate more buffer to avoid buffer overflow. // if (ImageContext.IsTeImage) { AlignImageSize = ImageContext.ImageSize + ((EFI_TE_IMAGE_HEADER *) Pe32Data)->StrippedSize - sizeof (EFI_TE_IMAGE_HEADER); } else { AlignImageSize = ImageContext.ImageSize; } if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) { AlignImageSize += ImageContext.SectionAlignment; } if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0 && (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME)) { Status = GetPeCoffImageFixLoadingAssignedAddress(&ImageContext, Private); if (EFI_ERROR (Status)){ DEBUG ((EFI_D_INFO|EFI_D_LOAD, "LOADING MODULE FIXED ERROR: Failed to load module at fixed address. \n")); // // The PEIM is not assiged valid address, try to allocate page to load it. // Status = PeiServicesAllocatePages (EfiBootServicesCode, EFI_SIZE_TO_PAGES ((UINT32) AlignImageSize), &ImageContext.ImageAddress); } } else { Status = PeiServicesAllocatePages (EfiBootServicesCode, EFI_SIZE_TO_PAGES ((UINT32) AlignImageSize), &ImageContext.ImageAddress); } if (!EFI_ERROR (Status)) { // // Adjust the Image Address to make sure it is section alignment. // if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) { ImageContext.ImageAddress = (ImageContext.ImageAddress + ImageContext.SectionAlignment - 1) & ~((UINTN)ImageContext.SectionAlignment - 1); } // // Fix alignment requirement when Load IPF TeImage into memory. // Skip the reserved space for the stripped PeHeader when load TeImage into memory. // if (ImageContext.IsTeImage) { ImageContext.ImageAddress = ImageContext.ImageAddress + ((EFI_TE_IMAGE_HEADER *) Pe32Data)->StrippedSize - sizeof (EFI_TE_IMAGE_HEADER); } } else { // // No enough memory resource. // if (IsXipImage) { // // XIP image can still be invoked. // ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Pe32Data; ReturnStatus = EFI_WARN_BUFFER_TOO_SMALL; } else { // // Non XIP image can't be loaded because no enough memory is allocated. // ASSERT (FALSE); return EFI_OUT_OF_RESOURCES; } } } // // Load the image to our new buffer // Status = PeCoffLoaderLoadImage (&ImageContext); if (EFI_ERROR (Status)) { if (ImageContext.ImageError == IMAGE_ERROR_INVALID_SECTION_ALIGNMENT) { DEBUG ((DEBUG_ERROR, "PEIM Image Address 0x%11p doesn't meet with section alignment 0x%x.\n", (VOID*)(UINTN)ImageContext.ImageAddress, ImageContext.SectionAlignment)); } return Status; } // // Relocate the image in our new buffer // Status = PeCoffLoaderRelocateImage (&ImageContext); if (EFI_ERROR (Status)) { return Status; } // // Flush the instruction cache so the image data is written before we execute it // if (ImageContext.ImageAddress != (EFI_PHYSICAL_ADDRESS)(UINTN) Pe32Data) { InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); } *ImageAddress = ImageContext.ImageAddress; *ImageSize = ImageContext.ImageSize; *EntryPoint = ImageContext.EntryPoint; return ReturnStatus; }
/** Loads and relocates a PE/COFF image into memory. If the image is not relocatable, it will not be loaded into memory and be loaded as XIP image. @param Pe32Data - The base address of the PE/COFF file that is to be loaded and relocated @param ImageAddress - The base address of the relocated PE/COFF image @param ImageSize - The size of the relocated PE/COFF image @param EntryPoint - The entry point of the relocated PE/COFF image @retval EFI_SUCCESS The file was loaded and relocated @retval EFI_OUT_OF_RESOURCES There was not enough memory to load and relocate the PE/COFF file **/ EFI_STATUS LoadAndRelocatePeCoffImage ( IN VOID *Pe32Data, OUT EFI_PHYSICAL_ADDRESS *ImageAddress, OUT UINT64 *ImageSize, OUT EFI_PHYSICAL_ADDRESS *EntryPoint ) { EFI_STATUS Status; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; PEI_CORE_INSTANCE *Private; Private = PEI_CORE_INSTANCE_FROM_PS_THIS (GetPeiServicesTablePointer ()); ZeroMem (&ImageContext, sizeof (ImageContext)); ImageContext.Handle = Pe32Data; Status = GetImageReadFunction (&ImageContext); ASSERT_EFI_ERROR (Status); Status = PeCoffLoaderGetImageInfo (&ImageContext); if (EFI_ERROR (Status)) { return Status; } // // When Image has no reloc section, it can't be relocated into memory. // if (ImageContext.RelocationsStripped && (Private->PeiMemoryInstalled) && (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME)) { DEBUG ((EFI_D_INFO|EFI_D_LOAD, "The image at 0x%08x without reloc section can't be loaded into memory\n", (UINTN) Pe32Data)); } // // Set default base address to current image address. // ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Pe32Data; // // Allocate Memory for the image when memory is ready, boot mode is not S3, and image is relocatable. // if ((!ImageContext.RelocationsStripped) && (Private->PeiMemoryInstalled) && (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME)) { if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0) { Status = GetPeCoffImageFixLoadingAssignedAddress(&ImageContext, Private); if (EFI_ERROR (Status)){ DEBUG ((EFI_D_INFO|EFI_D_LOAD, "LOADING MODULE FIXED ERROR: Failed to load module at fixed address. \n")); // // The PEIM is not assiged valid address, try to allocate page to load it. // ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) AllocatePages (EFI_SIZE_TO_PAGES ((UINT32) ImageContext.ImageSize)); } } else { ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) AllocatePages (EFI_SIZE_TO_PAGES ((UINT32) ImageContext.ImageSize)); } ASSERT (ImageContext.ImageAddress != 0); if (ImageContext.ImageAddress == 0) { return EFI_OUT_OF_RESOURCES; } // // Skip the reserved space for the stripped PeHeader when load TeImage into memory. // if (ImageContext.IsTeImage) { ImageContext.ImageAddress = ImageContext.ImageAddress + ((EFI_TE_IMAGE_HEADER *) Pe32Data)->StrippedSize - sizeof (EFI_TE_IMAGE_HEADER); } } // // Load the image to our new buffer // Status = PeCoffLoaderLoadImage (&ImageContext); if (EFI_ERROR (Status)) { return Status; } // // Relocate the image in our new buffer // Status = PeCoffLoaderRelocateImage (&ImageContext); if (EFI_ERROR (Status)) { return Status; } // // Flush the instruction cache so the image data is written before we execute it // if ((!ImageContext.RelocationsStripped) && (Private->PeiMemoryInstalled) && (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME)) { InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); } *ImageAddress = ImageContext.ImageAddress; *ImageSize = ImageContext.ImageSize; *EntryPoint = ImageContext.EntryPoint; return EFI_SUCCESS; }
/** This routine is entry point of ScriptSave driver. @param ImageHandle Handle for this drivers loaded image protocol. @param SystemTable EFI system table. @retval EFI_OUT_OF_RESOURCES No enough resource @retval EFI_SUCCESS Succesfully installed the ScriptSave driver. @retval other Errors occured. **/ EFI_STATUS EFIAPI InitializeScriptSaveOnS3SaveState ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { UINT8 *Buffer; UINTN BufferSize; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; BOOT_SCRIPT_THUNK_DATA *BootScriptThunkData; EFI_STATUS Status; VOID *DevicePath; EFI_PHYSICAL_ADDRESS MemoryAddress; UINTN PageNumber; EFI_HANDLE NewImageHandle; // // Test if the gEfiCallerIdGuid of this image is already installed. if not, the entry // point is loaded by DXE code which is the first time loaded. or else, it is already // be reloaded be itself.This is a work-around // Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &DevicePath); if (EFI_ERROR (Status)) { // // This is the first-time loaded by DXE core. reload itself to RESERVED mem // // // A workaround: Here we install a dummy handle // NewImageHandle = NULL; Status = gBS->InstallProtocolInterface ( &NewImageHandle, &gEfiCallerIdGuid, EFI_NATIVE_INTERFACE, NULL ); ASSERT_EFI_ERROR (Status); Status = GetSectionFromAnyFv ( &gEfiCallerIdGuid, EFI_SECTION_PE32, 0, (VOID **) &Buffer, &BufferSize ); ASSERT_EFI_ERROR (Status); ImageContext.Handle = Buffer; ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; // // Get information about the image being loaded // Status = PeCoffLoaderGetImageInfo (&ImageContext); ASSERT_EFI_ERROR (Status); MemoryAddress = SIZE_4GB - 1; if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) { PageNumber = EFI_SIZE_TO_PAGES ((UINTN) (ImageContext.ImageSize + ImageContext.SectionAlignment)); } else { PageNumber = EFI_SIZE_TO_PAGES ((UINTN) ImageContext.ImageSize); } Status = gBS->AllocatePages ( AllocateMaxAddress, EfiReservedMemoryType, PageNumber, &MemoryAddress ); ASSERT_EFI_ERROR (Status); ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)MemoryAddress; // // Align buffer on section boundry // ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; ImageContext.ImageAddress &= ~(ImageContext.SectionAlignment - 1); // // Load the image to our new buffer // Status = PeCoffLoaderLoadImage (&ImageContext); ASSERT_EFI_ERROR (Status); // // Relocate the image in our new buffer // Status = PeCoffLoaderRelocateImage (&ImageContext); ASSERT_EFI_ERROR (Status); // // Free the buffer allocated by ReadSection since the image has been relocated in the new buffer // gBS->FreePool (Buffer); // // Flush the instruction cache so the image data is written before we execute it // InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); RegisterMemoryProfileImage ( &gEfiCallerIdGuid, ImageContext.ImageAddress, ImageContext.ImageSize, EFI_FV_FILETYPE_DRIVER ); Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, SystemTable); ASSERT_EFI_ERROR (Status); // // Additional step for BootScriptThunk integrity // // // Allocate BootScriptThunkData // BootScriptThunkData = AllocatePool (sizeof (BOOT_SCRIPT_THUNK_DATA)); ASSERT (BootScriptThunkData != NULL); BootScriptThunkData->BootScriptThunkBase = ImageContext.ImageAddress; BootScriptThunkData->BootScriptThunkLength = ImageContext.ImageSize; // // Set BootScriptThunkData // PcdSet64 (BootScriptThunkDataPtr, (UINT64)(UINTN)BootScriptThunkData); return EFI_SUCCESS; } else { // // the entry point is invoked after reloading. following code only run in RESERVED mem // // // Locate and cache PI S3 Save State Protocol. // Status = gBS->LocateProtocol ( &gEfiS3SaveStateProtocolGuid, NULL, (VOID **) &mS3SaveState ); ASSERT_EFI_ERROR (Status); return gBS->InstallProtocolInterface ( &mHandle, &gEfiBootScriptSaveProtocolGuid, EFI_NATIVE_INTERFACE, &mS3ScriptSave ); } }
/** This is the Event notification function to reload BootScriptExecutor image to RESERVED mem and save it to LockBox. @param Event Pointer to this event @param Context Event handler private data **/ VOID EFIAPI ReadyToLockEventNotify ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_STATUS Status; VOID *Interface; UINT8 *Buffer; UINTN BufferSize; EFI_HANDLE NewImageHandle; UINTN Pages; EFI_PHYSICAL_ADDRESS FfsBuffer; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; Status = gBS->LocateProtocol (&gEfiDxeSmmReadyToLockProtocolGuid, NULL, &Interface); if (EFI_ERROR (Status)) { return; } // // A workaround: Here we install a dummy handle // NewImageHandle = NULL; Status = gBS->InstallProtocolInterface ( &NewImageHandle, &gEfiCallerIdGuid, EFI_NATIVE_INTERFACE, NULL ); ASSERT_EFI_ERROR (Status); // // Reload BootScriptExecutor image itself to RESERVED mem // Status = GetSectionFromAnyFv ( &gEfiCallerIdGuid, EFI_SECTION_PE32, 0, (VOID **) &Buffer, &BufferSize ); ASSERT_EFI_ERROR (Status); ImageContext.Handle = Buffer; ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; // // Get information about the image being loaded // Status = PeCoffLoaderGetImageInfo (&ImageContext); ASSERT_EFI_ERROR (Status); if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) { Pages = EFI_SIZE_TO_PAGES ((UINTN) (ImageContext.ImageSize + ImageContext.SectionAlignment)); } else { Pages = EFI_SIZE_TO_PAGES ((UINTN) ImageContext.ImageSize); } FfsBuffer = 0xFFFFFFFF; Status = gBS->AllocatePages ( AllocateMaxAddress, EfiReservedMemoryType, Pages, &FfsBuffer ); ASSERT_EFI_ERROR (Status); ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer; // // Align buffer on section boundry // ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)(ImageContext.SectionAlignment - 1)); // // Load the image to our new buffer // Status = PeCoffLoaderLoadImage (&ImageContext); ASSERT_EFI_ERROR (Status); // // Relocate the image in our new buffer // Status = PeCoffLoaderRelocateImage (&ImageContext); ASSERT_EFI_ERROR (Status); // // Free the buffer allocated by ReadSection since the image has been relocated in the new buffer // gBS->FreePool (Buffer); // // Flush the instruction cache so the image data is written before we execute it // InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); RegisterMemoryProfileImage ( &gEfiCallerIdGuid, ImageContext.ImageAddress, ImageContext.ImageSize, EFI_FV_FILETYPE_DRIVER ); Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, gST); ASSERT_EFI_ERROR (Status); // // Additional step for BootScript integrity // Save BootScriptExecutor image // Status = SaveLockBox ( &mBootScriptExecutorImageGuid, (VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize ); ASSERT_EFI_ERROR (Status); Status = SetLockBoxAttributes (&mBootScriptExecutorImageGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); ASSERT_EFI_ERROR (Status); gBS->CloseEvent (Event); }
/** The security handler is used to abstract platform-specific policy from the DXE core response to an attempt to use a file that returns a given status for the authentication check from the section extraction protocol. The possible responses in a given SAP implementation may include locking flash upon failure to authenticate, attestation logging for all signed drivers, and other exception operations. The File parameter allows for possible logging within the SAP of the driver. If File is NULL, then EFI_INVALID_PARAMETER is returned. If the file specified by File with an authentication status specified by AuthenticationStatus is safe for the DXE Core to use, then EFI_SUCCESS is returned. If the file specified by File with an authentication status specified by AuthenticationStatus is not safe for the DXE Core to use under any circumstances, then EFI_ACCESS_DENIED is returned. If the file specified by File with an authentication status specified by AuthenticationStatus is not safe for the DXE Core to use right now, but it might be possible to use it at a future time, then EFI_SECURITY_VIOLATION is returned. @param[in] AuthenticationStatus This is the authentication status returned from the securitymeasurement services for the input file. @param[in] File This is a pointer to the device path of the file that is being dispatched. This will optionally be used for logging. @param[in] FileBuffer File buffer matches the input file device path. @param[in] FileSize Size of File buffer matches the input file device path. @param[in] BootPolicy A boot policy that was used to call LoadImage() UEFI service. @retval EFI_SUCCESS The file specified by DevicePath and non-NULL FileBuffer did authenticate, and the platform policy dictates that the DXE Foundation may use the file. @retval other error value **/ EFI_STATUS EFIAPI DxeTpmMeasureBootHandler ( IN UINT32 AuthenticationStatus, IN CONST EFI_DEVICE_PATH_PROTOCOL *File, IN VOID *FileBuffer, IN UINTN FileSize, IN BOOLEAN BootPolicy ) { EFI_TCG_PROTOCOL *TcgProtocol; EFI_STATUS Status; TCG_EFI_BOOT_SERVICE_CAPABILITY ProtocolCapability; UINT32 TCGFeatureFlags; EFI_PHYSICAL_ADDRESS EventLogLocation; EFI_PHYSICAL_ADDRESS EventLogLastEntry; EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; EFI_DEVICE_PATH_PROTOCOL *OrigDevicePathNode; EFI_HANDLE Handle; EFI_HANDLE TempHandle; BOOLEAN ApplicationRequired; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol; EFI_PHYSICAL_ADDRESS FvAddress; UINT32 Index; Status = gBS->LocateProtocol (&gEfiTcgProtocolGuid, NULL, (VOID **) &TcgProtocol); if (EFI_ERROR (Status)) { // // TCG protocol is not installed. So, TPM is not present. // Don't do any measurement, and directly return EFI_SUCCESS. // return EFI_SUCCESS; } ProtocolCapability.Size = (UINT8) sizeof (ProtocolCapability); Status = TcgProtocol->StatusCheck ( TcgProtocol, &ProtocolCapability, &TCGFeatureFlags, &EventLogLocation, &EventLogLastEntry ); if (EFI_ERROR (Status) || ProtocolCapability.TPMDeactivatedFlag) { // // TPM device doesn't work or activate. // return EFI_SUCCESS; } // // Copy File Device Path // OrigDevicePathNode = DuplicateDevicePath (File); // // 1. Check whether this device path support BlockIo protocol. // Is so, this device path may be a GPT device path. // DevicePathNode = OrigDevicePathNode; Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &DevicePathNode, &Handle); if (!EFI_ERROR (Status) && !mMeasureGptTableFlag) { // // Find the gpt partion on the given devicepath // DevicePathNode = OrigDevicePathNode; ASSERT (DevicePathNode != NULL); while (!IsDevicePathEnd (DevicePathNode)) { // // Find the Gpt partition // if (DevicePathType (DevicePathNode) == MEDIA_DEVICE_PATH && DevicePathSubType (DevicePathNode) == MEDIA_HARDDRIVE_DP) { // // Check whether it is a gpt partition or not // if (((HARDDRIVE_DEVICE_PATH *) DevicePathNode)->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER && ((HARDDRIVE_DEVICE_PATH *) DevicePathNode)->SignatureType == SIGNATURE_TYPE_GUID) { // // Change the partition device path to its parent device path (disk) and get the handle. // DevicePathNode->Type = END_DEVICE_PATH_TYPE; DevicePathNode->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; DevicePathNode = OrigDevicePathNode; Status = gBS->LocateDevicePath ( &gEfiDiskIoProtocolGuid, &DevicePathNode, &Handle ); if (!EFI_ERROR (Status)) { // // Measure GPT disk. // Status = TcgMeasureGptTable (TcgProtocol, Handle); if (!EFI_ERROR (Status)) { // // GPT disk check done. // mMeasureGptTableFlag = TRUE; } } FreePool (OrigDevicePathNode); OrigDevicePathNode = DuplicateDevicePath (File); ASSERT (OrigDevicePathNode != NULL); break; } } DevicePathNode = NextDevicePathNode (DevicePathNode); } } // // 2. Measure PE image. // ApplicationRequired = FALSE; // // Check whether this device path support FVB protocol. // DevicePathNode = OrigDevicePathNode; Status = gBS->LocateDevicePath (&gEfiFirmwareVolumeBlockProtocolGuid, &DevicePathNode, &Handle); if (!EFI_ERROR (Status)) { // // Don't check FV image, and directly return EFI_SUCCESS. // It can be extended to the specific FV authentication according to the different requirement. // if (IsDevicePathEnd (DevicePathNode)) { return EFI_SUCCESS; } // // The PE image from unmeasured Firmware volume need be measured // The PE image from measured Firmware volume will be mearsured according to policy below. // If it is driver, do not measure // If it is application, still measure. // ApplicationRequired = TRUE; if (mCacheMeasuredHandle != Handle && mMeasuredHobData != NULL) { // // Search for Root FV of this PE image // TempHandle = Handle; do { Status = gBS->HandleProtocol( TempHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID**)&FvbProtocol ); TempHandle = FvbProtocol->ParentHandle; } while (!EFI_ERROR(Status) && FvbProtocol->ParentHandle != NULL); // // Search in measured FV Hob // Status = FvbProtocol->GetPhysicalAddress(FvbProtocol, &FvAddress); if (EFI_ERROR(Status)){ return Status; } ApplicationRequired = FALSE; for (Index = 0; Index < mMeasuredHobData->Num; Index++) { if(mMeasuredHobData->MeasuredFvBuf[Index].BlobBase == FvAddress) { // // Cache measured FV for next measurement // mCacheMeasuredHandle = Handle; ApplicationRequired = TRUE; break; } } } } // // File is not found. // if (FileBuffer == NULL) { Status = EFI_SECURITY_VIOLATION; goto Finish; } mImageSize = FileSize; mFileBuffer = FileBuffer; // // Measure PE Image // DevicePathNode = OrigDevicePathNode; ZeroMem (&ImageContext, sizeof (ImageContext)); ImageContext.Handle = (VOID *) FileBuffer; ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE) DxeTpmMeasureBootLibImageRead; // // Get information about the image being loaded // Status = PeCoffLoaderGetImageInfo (&ImageContext); if (EFI_ERROR (Status)) { // // The information can't be got from the invalid PeImage // goto Finish; } // // Measure only application if Application flag is set // Measure drivers and applications if Application flag is not set // if ((!ApplicationRequired) || (ApplicationRequired && ImageContext.ImageType == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION)) { // // Print the image path to be measured. // DEBUG_CODE_BEGIN (); CHAR16 *ToText; ToText = ConvertDevicePathToText ( DevicePathNode, FALSE, TRUE ); if (ToText != NULL) { DEBUG ((DEBUG_INFO, "The measured image path is %s.\n", ToText)); FreePool (ToText); } DEBUG_CODE_END (); // // Measure PE image into TPM log. // Status = TcgMeasurePeImage ( TcgProtocol, (EFI_PHYSICAL_ADDRESS) (UINTN) FileBuffer, FileSize, (UINTN) ImageContext.ImageAddress, ImageContext.ImageType, DevicePathNode ); } // // Done, free the allocated resource. // Finish: if (OrigDevicePathNode != NULL) { FreePool (OrigDevicePathNode); } return Status; }
EFI_STATUS GetImageContext ( IN EFI_FFS_FILE_HEADER *FfsHeader, OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext ) { EFI_STATUS Status; UINTN ParsedLength; UINTN SectionSize; UINTN SectionLength; EFI_COMMON_SECTION_HEADER *Section; VOID *EfiImage; UINTN ImageAddress; EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *DebugEntry; VOID *CodeViewEntryPointer; Section = (EFI_COMMON_SECTION_HEADER *)(FfsHeader + 1); SectionSize = *(UINT32 *)(FfsHeader->Size) & 0x00FFFFFF; SectionSize -= sizeof (EFI_FFS_FILE_HEADER); ParsedLength = 0; EfiImage = NULL; while (ParsedLength < SectionSize) { if ((Section->Type == EFI_SECTION_PE32) || (Section->Type == EFI_SECTION_TE)) { EfiImage = (EFI_IMAGE_OPTIONAL_HEADER_UNION*)(Section + 1); break; } // // Size is 24 bits wide so mask upper 8 bits. // SectionLength is adjusted it is 4 byte aligned. // Go to the next section // SectionLength = *(UINT32 *)Section->Size & 0x00FFFFFF; SectionLength = GET_OCCUPIED_SIZE (SectionLength, 4); ASSERT (SectionLength != 0); ParsedLength += SectionLength; Section = (EFI_COMMON_SECTION_HEADER *)((UINT8 *)Section + SectionLength); } if (EfiImage == NULL) { return EFI_NOT_FOUND; } // Initialize the Image Context ZeroMem (ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT)); ImageContext->Handle = EfiImage; ImageContext->ImageRead = PeCoffLoaderImageReadFromMemory; Status = PeCoffLoaderGetImageInfo (ImageContext); if (!EFI_ERROR(Status) && ((VOID*)ImageContext->DebugDirectoryEntryRva != NULL)) { ImageAddress = ImageContext->ImageAddress; if (ImageContext->IsTeImage) { ImageAddress += sizeof (EFI_TE_IMAGE_HEADER) - ((EFI_TE_IMAGE_HEADER*)EfiImage)->StrippedSize; } DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY*)(ImageAddress + ImageContext->DebugDirectoryEntryRva); if (DebugEntry->Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) { CodeViewEntryPointer = (VOID *) (ImageAddress + (UINTN) DebugEntry->RVA); switch (* (UINT32 *) CodeViewEntryPointer) { case CODEVIEW_SIGNATURE_NB10: ImageContext->PdbPointer = (CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY); break; case CODEVIEW_SIGNATURE_RSDS: ImageContext->PdbPointer = (CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY); break; case CODEVIEW_SIGNATURE_MTOC: ImageContext->PdbPointer = (CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY); break; default: break; } } } return Status; }
/** Relocate this image to reserved memory. @param ImageHandle Handle of driver image. @param SystemTable Pointer to system table. @retval EFI_SUCCESS Image successfully relocated. @retval EFI_ABORTED Failed to relocate image. **/ EFI_STATUS RelocateImageToReserved ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; EFI_STATUS Status; UINT8 *Buffer; UINTN BufferSize; EFI_HANDLE NewImageHandle; UINTN Pages; EFI_PHYSICAL_ADDRESS FfsBuffer; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; VOID *Interface; // // If locate gEfiCallerIdGuid success, it means 2nd entry. // Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &Interface); if (!EFI_ERROR (Status)) { DEBUG ((EFI_D_INFO, "StmService - 2nd entry\n")); return EFI_SUCCESS; } DEBUG ((EFI_D_INFO, "StmService - 1st entry\n")); // // Reload image itself to <4G mem // Status = GetSectionFromAnyFv ( &gEfiCallerIdGuid, EFI_SECTION_PE32, 0, (VOID **) &Buffer, &BufferSize ); ASSERT_EFI_ERROR (Status); ImageContext.Handle = Buffer; ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; // // Get information about the image being loaded // Status = PeCoffLoaderGetImageInfo (&ImageContext); ASSERT_EFI_ERROR (Status); if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) { Pages = EFI_SIZE_TO_PAGES ((UINTN) (ImageContext.ImageSize + ImageContext.SectionAlignment)); } else { Pages = EFI_SIZE_TO_PAGES ((UINTN) ImageContext.ImageSize); } FfsBuffer = 0xFFFFFFFF; Status = gBS->AllocatePages ( AllocateMaxAddress, EfiReservedMemoryType, Pages, &FfsBuffer ); ASSERT_EFI_ERROR (Status); ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer; // // Align buffer on section boundry // ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)(ImageContext.SectionAlignment - 1)); // // Load the image to our new buffer // Status = PeCoffLoaderLoadImage (&ImageContext); ASSERT_EFI_ERROR (Status); // // Relocate the image in our new buffer // Status = PeCoffLoaderRelocateImage (&ImageContext); ASSERT_EFI_ERROR (Status); // // Free the buffer allocated by ReadSection since the image has been relocated in the new buffer // gBS->FreePool (Buffer); // // Flush the instruction cache so the image data is written before we execute it // InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); // // install loaded image // LoadedImage = AllocateZeroPool (sizeof(EFI_LOADED_IMAGE_PROTOCOL)); ASSERT (LoadedImage != NULL); LoadedImage->Revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION; LoadedImage->ParentHandle = NULL; LoadedImage->SystemTable = gST; LoadedImage->DeviceHandle = NULL; LoadedImage->LoadOptionsSize = 0; LoadedImage->LoadOptions = NULL; LoadedImage->ImageBase = (VOID *)(UINTN)ImageContext.ImageAddress; LoadedImage->ImageSize = ImageContext.ImageSize; LoadedImage->ImageCodeType = EfiReservedMemoryType; LoadedImage->ImageDataType = EfiReservedMemoryType; LoadedImage->Unload = NULL; NewImageHandle = NULL; Status = gBS->InstallMultipleProtocolInterfaces ( &NewImageHandle, &gEfiCallerIdGuid, LoadedImage, NULL ); ASSERT_EFI_ERROR (Status); DEBUG ((EFI_D_INFO, "NewImageHandle - 0x%x\n", NewImageHandle)); // // Call entrypoint // DEBUG ((EFI_D_INFO, "Loading driver at 0x%08x EntryPoint=0x%08x\n", (UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.EntryPoint)); Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, gST); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Error: Image at 0x%08x start failed: %r\n", ImageContext.ImageAddress, Status)); gBS->FreePages (FfsBuffer, Pages); } // // return error to unload DXE copy, if we already relocate itself to reserved memory. // return EFI_ACCESS_DENIED; }