diff --git a/BaseTools/ImageTool/ImageToolEmit.c b/BaseTools/ImageTool/ImageToolEmit.c index b369c469e7..4f7be6786f 100644 --- a/BaseTools/ImageTool/ImageToolEmit.c +++ b/BaseTools/ImageTool/ImageToolEmit.c @@ -187,6 +187,11 @@ ToolImageEmit ( return NULL; } + if ((Format == UefiImageFormatUe) && Xip) { + ToolImageDestruct (&ImageInfo); + return OutputFile; + } + Status = ValidateOutputFile (OutputFile, *OutputFileSize, &ImageInfo); ToolImageDestruct (&ImageInfo); diff --git a/BaseTools/ImageTool/UeEmit.c b/BaseTools/ImageTool/UeEmit.c index b945db6457..7849495cf6 100644 --- a/BaseTools/ImageTool/UeEmit.c +++ b/BaseTools/ImageTool/UeEmit.c @@ -908,6 +908,136 @@ ToolImageEmitUeFile ( return true; } +static +bool +ToolImageEmitUeXipFile ( + image_tool_dynamic_buffer *Buffer, + image_tool_image_info_t *Image + ) +{ + bool Success; + UE_HEADER UeHdr; + uint8_t AlignmentExponent; + int8_t Machine; + uint8_t Subsystem; + uint8_t LastSegmentIndex; + uint8_t NumLoadTables; + uint32_t Offset; + uint32_t UeHdrOff; + bool Chaining; + uint16_t Index; + uint64_t BaseAddress; + + assert (Image->SegmentInfo.NumSegments > 0); + + if (Image->SegmentInfo.NumSegments > UE_HEADER_NUM_SEGMENTS_MAX) { + DEBUG_RAISE (); + return false; + } + + if (Image->HiiInfo.DataSize > 0) { + DEBUG_RAISE (); + return false; + } + + BaseAddress = Image->HeaderInfo.BaseAddress; + UeHdrOff = ALIGN_VALUE(sizeof (UeHdr), Image->SegmentInfo.SegmentAlignment); + + Success = ToolImageRelocate (Image, BaseAddress + UeHdrOff, 0); + if (!Success) { + DEBUG_RAISE (); + return false; + } + + AlignmentExponent = AlignmentToExponent (Image->SegmentInfo.SegmentAlignment); + if (12 > AlignmentExponent || AlignmentExponent > 27) { + DEBUG_RAISE (); + return false; + } + + Machine = InternalGetUeMachine (Image->HeaderInfo.Machine); + if (Machine < 0) { + DEBUG_RAISE (); + return false; + } + + NumLoadTables = 0U; + Subsystem = (uint8_t)(Image->HeaderInfo.Subsystem - 10U); + LastSegmentIndex = (uint8_t)(Image->SegmentInfo.NumSegments - 1U); + Chaining = false; + + UeHdr.Magic = UE_HEADER_MAGIC; + + UeHdr.Type = (Machine << 3U) | Subsystem; + assert (UE_HEADER_SUBSYSTEM (UeHdr.Type) == Subsystem); + assert (UE_HEADER_ARCH (UeHdr.Type) == Machine); + + UeHdr.TableCounts = NumLoadTables; + UeHdr.TableCounts |= LastSegmentIndex << 3U; + assert (UE_HEADER_LAST_SEGMENT_INDEX (UeHdr.TableCounts) == LastSegmentIndex); + assert (UE_HEADER_NUM_LOAD_TABLES (UeHdr.TableCounts) == NumLoadTables); + + UeHdr.EntryPointAddress = Image->HeaderInfo.EntryPointAddress + UeHdrOff; + + UeHdr.ImageInfo = BaseAddress >> 12ULL; + UeHdr.ImageInfo |= UE_HEADER_IMAGE_INFO_XIP; + UeHdr.ImageInfo |= (uint64_t)Image->HeaderInfo.FixedAddress << 57ULL; + UeHdr.ImageInfo |= (uint64_t)Image->RelocInfo.RelocsStripped << 58ULL; + UeHdr.ImageInfo |= (uint64_t)Chaining << 59ULL; + UeHdr.ImageInfo |= (uint64_t)(AlignmentExponent - 12U) << 60ULL; + assert (UE_HEADER_BASE_ADDRESS (UeHdr.ImageInfo) == BaseAddress); + assert ((UeHdr.ImageInfo & (0xFULL << 52ULL)) == 0); + assert (((UeHdr.ImageInfo & UE_HEADER_IMAGE_INFO_FIXED_ADDRESS) != 0) == Image->HeaderInfo.FixedAddress); + assert (((UeHdr.ImageInfo & UE_HEADER_IMAGE_INFO_RELOCATION_FIXUPS_STRIPPED) != 0) == Image->RelocInfo.RelocsStripped); + assert (((UeHdr.ImageInfo & UE_HEADER_IMAGE_INFO_CHAINED_FIXUPS) != 0) == Chaining); + assert (((UeHdr.ImageInfo & UE_HEADER_IMAGE_INFO_XIP) != 0) == TRUE); + assert (UE_HEADER_SEGMENT_ALIGNMENT (UeHdr.ImageInfo) == Image->SegmentInfo.SegmentAlignment); + + Offset = ImageToolBufferAppend (Buffer, &UeHdr, sizeof (UeHdr)); + if (Offset == MAX_UINT32) { + DEBUG_RAISE (); + return false; + } + + Success = ToolImageEmitUeSegmentHeaders (Buffer, Image); + if (!Success) { + DEBUG_RAISE (); + return false; + } + + Offset = ImageToolBufferAppendReserveAlign ( + Buffer, + Image->SegmentInfo.SegmentAlignment + ); + if (Offset == MAX_UINT32) { + DEBUG_RAISE (); + return false; + } + + for (Index = 0; Index < Image->SegmentInfo.NumSegments; ++Index) { + Offset = ImageToolBufferAppend ( + Buffer, + Image->SegmentInfo.Segments[Index].Data, + Image->SegmentInfo.Segments[Index].UnpaddedSize + ); + if (Offset == MAX_UINT32) { + DEBUG_RAISE (); + return false; + } + + Offset = ImageToolBufferAppendReserve ( + Buffer, + Image->SegmentInfo.Segments[Index].ImageSize - Image->SegmentInfo.Segments[Index].UnpaddedSize + ); + if (Offset == MAX_UINT32) { + DEBUG_RAISE (); + return false; + } + } + + return true; +} + void * ToolImageEmitUe ( image_tool_image_info_t *Image, @@ -921,29 +1051,27 @@ ToolImageEmitUe ( uint32_t BaseAddressSubtrahend; void *FileBuffer; - // LCOV_EXCL_START - if (Xip) { - DEBUG_RAISE (); - return NULL; - } - // LCOV_EXCL_STOP - - Success = ToolImageStripEmptyPrefix (&BaseAddressSubtrahend, Image); - if (!Success) { - DEBUG_RAISE (); - return NULL; - } - - if (Strip) { - ToolImageStripRelocs (Image); - } + ImageToolBufferInit (&Buffer); ImageInitUnpaddedSize (Image); ToolImageEmitUePadChainedRelocs (Image); - ImageToolBufferInit (&Buffer); + if (Xip) { + Success = ToolImageEmitUeXipFile (&Buffer, Image); + } else { + Success = ToolImageStripEmptyPrefix (&BaseAddressSubtrahend, Image); + if (!Success) { + DEBUG_RAISE (); + return NULL; + } + + if (Strip) { + ToolImageStripRelocs (Image); + } + + Success = ToolImageEmitUeFile (&Buffer, Image, BaseAddressSubtrahend); + } - Success = ToolImageEmitUeFile (&Buffer, Image, BaseAddressSubtrahend); if (!Success) { DEBUG_RAISE (); ImageToolBufferFree (&Buffer); diff --git a/BaseTools/Source/C/GenFv/GenFvInternalLib.c b/BaseTools/Source/C/GenFv/GenFvInternalLib.c index 43ddc11242..587290b049 100644 --- a/BaseTools/Source/C/GenFv/GenFvInternalLib.c +++ b/BaseTools/Source/C/GenFv/GenFvInternalLib.c @@ -3622,6 +3622,9 @@ Returns: UINT8 ImageFormat; UINT32 RebasedImageSize; VOID *RebasedImage; + UINT32 FfsFileLength; + UINTN FileOffset; + EFI_FFS_INTEGRITY_CHECK *IntegrityCheck; Index = 0; Cptr = NULL; @@ -3790,6 +3793,7 @@ Returns: UefiImageFileBuffer = (VOID *) ((UINTN) CurrentPe32Section.Pe32Section + CurSecHdrSize); UefiImageFileSize = SectPeSize; + FileOffset = (UINTN)UefiImageFileBuffer - (UINTN)(*FfsFile); // // UefiImage has no reloc section. It will try to get reloc data from the original UEFI image. // @@ -3871,37 +3875,81 @@ Returns: return EFI_UNSUPPORTED; } - UefiImageFileBuffer = NULL; - UefiImageFileSize = 0; + if (ImageFormat == UefiImageFormatUe) { + FfsFileLength = GetFfsFileLength (*FfsFile) - SectPeSize + RebasedImageSize; + *FfsFile = realloc (*FfsFile, FfsFileLength); + if (*FfsFile == NULL) { + fprintf (stderr, "GenFv: Could not reallocate memory for rebased FfsFile\n"); + return EFI_OUT_OF_RESOURCES; + } + *FileSize = FfsFileLength; - if (RebasedImageSize > SectPeSize) { - Error (NULL, 0, 4001, "Invalid", "rebased file is too large (%s)", FileName); - return EFI_UNSUPPORTED; - } + memmove ( + (UINT8 *)((UINTN)(*FfsFile) + FileOffset), + RebasedImage, + RebasedImageSize + ); - memmove ( - (UINT8 *)CurrentPe32Section.Pe32Section + CurSecHdrSize, - RebasedImage, - RebasedImageSize + if (FfsHeaderSize > sizeof(EFI_FFS_FILE_HEADER)) { + ((EFI_FFS_FILE_HEADER2 *)(*FfsFile))->ExtendedSize = FfsFileLength; + } else { + (*FfsFile)->Size[0] = (UINT8)(FfsFileLength & 0x000000FF); + (*FfsFile)->Size[1] = (UINT8)((FfsFileLength & 0x0000FF00) >> 8); + (*FfsFile)->Size[2] = (UINT8)((FfsFileLength & 0x00FF0000) >> 16); + } + + // + // Recalculate the FFS header checksum. Instead of setting Header and State + // both to zero, set Header to (UINT8)(-State) so State preserves its original + // value + // + IntegrityCheck = &(*FfsFile)->IntegrityCheck; + IntegrityCheck->Checksum.Header = (UINT8) (0x100 - (*FfsFile)->State); + IntegrityCheck->Checksum.File = 0; + + IntegrityCheck->Checksum.Header = CalculateChecksum8 ( + (UINT8 *)(*FfsFile), FfsHeaderSize); + + if ((*FfsFile)->Attributes & FFS_ATTRIB_CHECKSUM) { + // + // Ffs header checksum = zero, so only need to calculate ffs body. + // + IntegrityCheck->Checksum.File = CalculateChecksum8 ( + (UINT8 *)(*FfsFile) + FfsHeaderSize, + FfsFileLength - FfsHeaderSize); + } else { + IntegrityCheck->Checksum.File = FFS_FIXED_CHECKSUM; + } + } else { + if (RebasedImageSize > SectPeSize) { + Error (NULL, 0, 4001, "Invalid", "rebased file is too large (%s)", FileName); + return EFI_UNSUPPORTED; + } + + memmove ( + (UINT8 *)CurrentPe32Section.Pe32Section + CurSecHdrSize, + RebasedImage, + RebasedImageSize ); - memset ( - (UINT8 *)CurrentPe32Section.Pe32Section + CurSecHdrSize + RebasedImageSize, - 0, - SectPeSize - RebasedImageSize + memset ( + (UINT8 *)CurrentPe32Section.Pe32Section + CurSecHdrSize + RebasedImageSize, + 0, + SectPeSize - RebasedImageSize ); - // - // Now update file checksum - // - if ((*FfsFile)->Attributes & FFS_ATTRIB_CHECKSUM) { - SavedState = (*FfsFile)->State; - (*FfsFile)->IntegrityCheck.Checksum.File = 0; - (*FfsFile)->State = 0; - (*FfsFile)->IntegrityCheck.Checksum.File = CalculateChecksum8 ( - (UINT8 *) ((UINT8 *)(*FfsFile) + FfsHeaderSize), - GetFfsFileLength (*FfsFile) - FfsHeaderSize - ); - (*FfsFile)->State = SavedState; + // + // Now update file checksum + // + if ((*FfsFile)->Attributes & FFS_ATTRIB_CHECKSUM) { + SavedState = (*FfsFile)->State; + (*FfsFile)->IntegrityCheck.Checksum.File = 0; + (*FfsFile)->State = 0; + (*FfsFile)->IntegrityCheck.Checksum.File = CalculateChecksum8 ( + (UINT8 *) ((UINT8 *)(*FfsFile) + FfsHeaderSize), + GetFfsFileLength (*FfsFile) - FfsHeaderSize + ); + (*FfsFile)->State = SavedState; + } } // @@ -3910,7 +3958,7 @@ Returns: Status = UefiImageInitializeContext ( &ImageContext, - (VOID *) ((UINTN) CurrentPe32Section.Pe32Section + CurSecHdrSize), + (VOID *) ((UINTN)(*FfsFile) + FileOffset), RebasedImageSize, UEFI_IMAGE_SOURCE_FV ); @@ -3927,6 +3975,9 @@ Returns: &ImageContext ); + UefiImageFileBuffer = NULL; + UefiImageFileSize = 0; + free (SymbolsPathCpy); } diff --git a/MdePkg/Include/IndustryStandard/UeImage.h b/MdePkg/Include/IndustryStandard/UeImage.h index f65e341d8a..3e88d972cc 100644 --- a/MdePkg/Include/IndustryStandard/UeImage.h +++ b/MdePkg/Include/IndustryStandard/UeImage.h @@ -501,7 +501,8 @@ typedef struct { /// /// [Bits 51:0] The base UEFI page of the UE image, i.e., the base address in /// 4 KiB units. - /// [Bits 56:52] Reserved for future use. Must be zero. + /// [Bits 55:52] Reserved for future use. Must be zero. + /// [Bit 56] Indicates whether the UE image is XIP /// [Bit 57] Indicates whether the UE image is designated for a fixed /// address. /// [Bit 58] Indicates whether the UE relocation table has been stripped. @@ -569,6 +570,11 @@ STATIC_ASSERT ( #define UE_HEADER_SEGMENT_ALIGNMENT(ImageInfo) \ (1U << ((UINT8)RShiftU64 (ImageInfo, 60) + 12U)) +/// +/// UE header image information bit that indicates whether the image is XIP. +/// +#define UE_HEADER_IMAGE_INFO_XIP 0x0100000000000000ULL + /// /// UE header image information bit that indicates whether the image is /// designated to be loaded to a fixed address. diff --git a/MdePkg/Include/Library/UeImageLib.h b/MdePkg/Include/Library/UeImageLib.h index b7d1f9ae43..850ca6b3df 100644 --- a/MdePkg/Include/Library/UeImageLib.h +++ b/MdePkg/Include/Library/UeImageLib.h @@ -17,6 +17,7 @@ typedef struct { UINT8 Subsystem; UINT8 Machine; BOOLEAN FixedAddress; + BOOLEAN XIP; UINT8 LastSegmentIndex; UINT32 SegmentsFileOffset; // Unused for XIP UINT32 SegmentAlignment; diff --git a/MdePkg/Library/BaseUeImageLib/UeImageLib.c b/MdePkg/Library/BaseUeImageLib/UeImageLib.c index 31a8a62088..9ace54b355 100644 --- a/MdePkg/Library/BaseUeImageLib/UeImageLib.c +++ b/MdePkg/Library/BaseUeImageLib/UeImageLib.c @@ -273,6 +273,10 @@ InternalInitializeContextLate ( return RETURN_UNSUPPORTED; } + if (Context->XIP) { + return RETURN_SUCCESS; + } + return InternalVerifyLoadTables (Context); } @@ -320,6 +324,7 @@ UeInitializeContextPostHash ( Context->FixedAddress = (UeHdr->ImageInfo & UE_HEADER_IMAGE_INFO_FIXED_ADDRESS) != 0; Context->RelocsStripped = (UeHdr->ImageInfo & UE_HEADER_IMAGE_INFO_RELOCATION_FIXUPS_STRIPPED) != 0; + Context->XIP = (UeHdr->ImageInfo & UE_HEADER_IMAGE_INFO_XIP) != 0; Context->LastSegmentIndex = LastSegmentIndex; Context->NumLoadTables = NumLoadTables;