/** @file Unit tests the SplitTable() ImagePropertiesRecordLib Logic Copyright (C) Microsoft Corporation. SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #define UNIT_TEST_APP_NAME "Image Properties Record Lib Unit Test" #define UNIT_TEST_APP_VERSION "1.0" #define NEXT_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \ ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) + (Size))) // The starting memory map will contain 6 entries #define NUMBER_OF_MEMORY_MAP_DESCRIPTORS 6 // Each memory map descriptor will be the sizeof(EFI_MEMORY_DESCRIPTOR) instead of a nonstandard size // to catch pointer math issues #define DESCRIPTOR_SIZE sizeof(EFI_MEMORY_DESCRIPTOR) // Each memory map descriptor will describe 12 pages #define BASE_DESCRIPTOR_NUMBER_OF_PAGES 0x0C // The size, in bytes, of each memory map descriptor range #define BASE_DESCRIPTOR_ENTRY_SIZE (EFI_PAGES_TO_SIZE(BASE_DESCRIPTOR_NUMBER_OF_PAGES)) // MACRO to get the starting address of a descriptor's described range based on the index of that descriptor #define BASE_DESCRIPTOR_START_ADDRESS(DescriptorNumber) (DescriptorNumber * BASE_DESCRIPTOR_ENTRY_SIZE) // Virtual start must be zero #define BASE_DESCRIPTOR_VIRTUAL_START 0x0 // Size of the default memory map #define BASE_MEMORY_MAP_SIZE (NUMBER_OF_MEMORY_MAP_DESCRIPTORS * DESCRIPTOR_SIZE) // Number of images in each test case #define NUMBER_OF_IMAGES_TO_SPLIT 3 // Maximum number of descriptors required for each image (None->Data->Code->Data->Code->Data->None) #define MAX_DESCRIPTORS_PER_IMAGE 7 // Number of unused additional descriptors in the starting memory map buffer which is used by the // SplitTable() logic #define NUMBER_OF_ADDITIONAL_DESCRIPTORS (NUMBER_OF_IMAGES_TO_SPLIT * MAX_DESCRIPTORS_PER_IMAGE) // Size of the memory map with enough space for the starting descriptors and the split descriptors #define SPLIT_MEMORY_MAP_SIZE (BASE_MEMORY_MAP_SIZE + (NUMBER_OF_ADDITIONAL_DESCRIPTORS * DESCRIPTOR_SIZE)) typedef enum { SectionTypeCode, SectionTypeData, SectionTypeNotFound } SECTION_TYPE; typedef struct { EFI_MEMORY_DESCRIPTOR *MemoryMap; LIST_ENTRY ImageList; } IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT; EFI_MEMORY_DESCRIPTOR BaseMemoryMap[] = { { EfiConventionalMemory, // Type BASE_DESCRIPTOR_START_ADDRESS (0), // PhysicalStart BASE_DESCRIPTOR_VIRTUAL_START, // VirtualStart BASE_DESCRIPTOR_NUMBER_OF_PAGES, // Number of Pages 0 // Attribute }, { EfiConventionalMemory, // Type BASE_DESCRIPTOR_START_ADDRESS (1), // PhysicalStart BASE_DESCRIPTOR_VIRTUAL_START, // VirtualStart BASE_DESCRIPTOR_NUMBER_OF_PAGES, // Number of Pages 0 // Attribute }, { EfiConventionalMemory, // Type BASE_DESCRIPTOR_START_ADDRESS (2), // PhysicalStart BASE_DESCRIPTOR_VIRTUAL_START, // VirtualStart BASE_DESCRIPTOR_NUMBER_OF_PAGES, // Number of Pages 0 // Attribute }, { EfiConventionalMemory, // Type BASE_DESCRIPTOR_START_ADDRESS (3), // PhysicalStart BASE_DESCRIPTOR_VIRTUAL_START, // VirtualStart BASE_DESCRIPTOR_NUMBER_OF_PAGES, // Number of Pages 0 // Attribute }, { EfiConventionalMemory, // Type BASE_DESCRIPTOR_START_ADDRESS (4), // PhysicalStart BASE_DESCRIPTOR_VIRTUAL_START, // VirtualStart BASE_DESCRIPTOR_NUMBER_OF_PAGES, // Number of Pages 0 // Attribute }, { EfiConventionalMemory, // Type BASE_DESCRIPTOR_START_ADDRESS (5), // PhysicalStart BASE_DESCRIPTOR_VIRTUAL_START, // VirtualStart BASE_DESCRIPTOR_NUMBER_OF_PAGES, // Number of Pages 0 // Attribute } }; /** Returns a bitmap where one bit is set for each section in the image list. For example, if there are 3 images and each image 3 sections the returned bitmap will be 111111111. @param[in] ImageRecordList A list of IMAGE_PROPERTIES_RECORD entries @retval A bitmap such that the most significant bit is the number of sections in all images and every bit between 0 -> MSB is set **/ STATIC UINT64 GetImageSectionBitmap ( IN LIST_ENTRY *ImageRecordList ) { IMAGE_PROPERTIES_RECORD *ImageRecord; IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection; LIST_ENTRY *ImageRecordLink; LIST_ENTRY *ImageRecordCodeSectionLink; EFI_PHYSICAL_ADDRESS SectionBase; UINT64 ReturnBitmap; UINT64 Shift; if (ImageRecordList == NULL) { return 0; } ReturnBitmap = 0; Shift = 0; // Walk through each image record for (ImageRecordLink = ImageRecordList->ForwardLink; ImageRecordLink != ImageRecordList; ImageRecordLink = ImageRecordLink->ForwardLink) { ImageRecord = CR ( ImageRecordLink, IMAGE_PROPERTIES_RECORD, Link, IMAGE_PROPERTIES_RECORD_SIGNATURE ); SectionBase = ImageRecord->ImageBase; // Walk through each code entry for (ImageRecordCodeSectionLink = ImageRecord->CodeSegmentList.ForwardLink; ImageRecordCodeSectionLink != &ImageRecord->CodeSegmentList; ImageRecordCodeSectionLink = ImageRecordCodeSectionLink->ForwardLink) { ImageRecordCodeSection = CR ( ImageRecordCodeSectionLink, IMAGE_PROPERTIES_RECORD_CODE_SECTION, Link, IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE ); // Check for data region before the code section base if (SectionBase < ImageRecordCodeSection->CodeSegmentBase) { ReturnBitmap |= LShiftU64 (1, Shift++); } // Code section ReturnBitmap |= LShiftU64 (1, Shift++); SectionBase = ImageRecordCodeSection->CodeSegmentBase + ImageRecordCodeSection->CodeSegmentSize; } // Check for data region after the previous code section if (SectionBase < (ImageRecord->ImageBase + ImageRecord->ImageSize)) { ReturnBitmap |= LShiftU64 (1, Shift++); } } return ReturnBitmap; } /** Searches the input image list for a section which exactly matches the memory range Buffer -> Buffer + Length. @param[in] Buffer Start Address to check @param[in] Length Length to check @param[out] Type The type of the section which corresponds with the memory range Buffer -> Buffer + Length (Code or Data) or SectionTypeNotFound if no image section matches the memory range @param[in] ImageRecordList A list of IMAGE_PROPERTIES_RECORD entries to check against the memory range Buffer -> Buffer + Length @retval A bitmap with a single bit set (1 << Shift) where Shift corresponds with the number of sections inspected in the image list before arriving at the section matching the memory range Buffer -> Buffer + Length **/ STATIC UINT64 MatchDescriptorToImageSection ( IN EFI_PHYSICAL_ADDRESS Buffer, IN UINT64 Length, OUT SECTION_TYPE *Type, IN LIST_ENTRY *ImageRecordList ) { IMAGE_PROPERTIES_RECORD *ImageRecord; IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection; LIST_ENTRY *ImageRecordLink; LIST_ENTRY *ImageRecordCodeSectionLink; EFI_PHYSICAL_ADDRESS SectionBase; UINT8 Shift; Shift = 0; if (ImageRecordList == NULL) { return 1; } // Walk through each image record for (ImageRecordLink = ImageRecordList->ForwardLink; ImageRecordLink != ImageRecordList; ImageRecordLink = ImageRecordLink->ForwardLink) { ImageRecord = CR ( ImageRecordLink, IMAGE_PROPERTIES_RECORD, Link, IMAGE_PROPERTIES_RECORD_SIGNATURE ); SectionBase = ImageRecord->ImageBase; // Walk through each code entry for (ImageRecordCodeSectionLink = ImageRecord->CodeSegmentList.ForwardLink; ImageRecordCodeSectionLink != &ImageRecord->CodeSegmentList; ImageRecordCodeSectionLink = ImageRecordCodeSectionLink->ForwardLink) { ImageRecordCodeSection = CR ( ImageRecordCodeSectionLink, IMAGE_PROPERTIES_RECORD_CODE_SECTION, Link, IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE ); if (SectionBase < ImageRecordCodeSection->CodeSegmentBase) { // Check the data region before the code section base if ((Buffer == SectionBase) && (Length == ImageRecordCodeSection->CodeSegmentBase - SectionBase)) { *Type = SectionTypeData; return LShiftU64 (1, Shift); } Shift++; } // Check the code region if ((Buffer == ImageRecordCodeSection->CodeSegmentBase) && (Length == ImageRecordCodeSection->CodeSegmentSize)) { *Type = SectionTypeCode; return LShiftU64 (1, Shift); } Shift++; SectionBase = ImageRecordCodeSection->CodeSegmentBase + ImageRecordCodeSection->CodeSegmentSize; } // Check the data region after the code section if (SectionBase < (ImageRecord->ImageBase + ImageRecord->ImageSize)) { if ((Buffer == SectionBase) && (Length == (ImageRecord->ImageBase + ImageRecord->ImageSize) - SectionBase)) { *Type = SectionTypeData; return LShiftU64 (1, Shift); } Shift++; } } // No image sections match *Type = SectionTypeNotFound; return 0; } /** Walks through the input memory map and checks that every memory descriptor with an attribute matches an image in ImageRecordList. @param[in] MemoryMapSize The size, in bytes, of the memory map @param[in] MemoryMap A pointer to the buffer containing the memory map @param[in] ImageRecordList A list of IMAGE_PROPERTIES_RECORD entries @retval TRUE if all memory descriptors with attributes match an image section and have the correct attributes **/ STATIC BOOLEAN IsMemoryMapValid ( IN UINTN MemoryMapSize, IN EFI_MEMORY_DESCRIPTOR *MemoryMap, IN LIST_ENTRY *ImageRecordList ) { UINT64 ImageSectionsBitmap; UINT64 ReturnSectionBitmask; UINT64 NumberOfDescriptors; UINT8 Index; SECTION_TYPE Type; Index = 0; NumberOfDescriptors = MemoryMapSize / DESCRIPTOR_SIZE; UT_ASSERT_EQUAL (MemoryMapSize % DESCRIPTOR_SIZE, 0); UT_ASSERT_NOT_NULL (MemoryMap); UT_ASSERT_NOT_NULL (ImageRecordList); // The returned bitmap will have one bit is set for each section in the image list. // If there are 3 images and 3 sections each image, the resulting bitmap will // be 0000000000000000000000000000000000000000000000000000000111111111. Flipping that bitmap // results in 1111111111111111111111111111111111111111111111111111111000000000. The return value // of each iteration through MatchDescriptorToImageSection() is one set bit corrosponding to the number // of sections before finding the section which matched the descriptor memory range which we // OR with ImageSectionsBitmap. If, at the end of the loop, every bit in ImageSectionsBitmap is set, // we must have matched every image in ImageRecordList with a descriptor in the memory map which has // nonzero attributes. ImageSectionsBitmap = ~GetImageSectionBitmap (ImageRecordList); // For each descriptor in the memory map for ( ; Index < NumberOfDescriptors; Index++) { if (MemoryMap[Index].Attribute != 0) { ReturnSectionBitmask = MatchDescriptorToImageSection ( MemoryMap[Index].PhysicalStart, EFI_PAGES_TO_SIZE (MemoryMap[Index].NumberOfPages), &Type, ImageRecordList ); // Make sure the attributes of the descriptor match the returned section type. // DATA sections should have execution protection and CODE sections should have // write protection. if ((Type == SectionTypeNotFound) || ((Type == SectionTypeData) && (MemoryMap[Index].Attribute == EFI_MEMORY_RP)) || ((Type == SectionTypeCode) && (MemoryMap[Index].Attribute == EFI_MEMORY_XP))) { return FALSE; } // If the bit associated with image found has already been set, then there must be a duplicate // in the memory map meaning it is invalid. UT_ASSERT_EQUAL (ImageSectionsBitmap & ReturnSectionBitmask, 0); ImageSectionsBitmap |= ReturnSectionBitmask; } } // If every bit in ImageSectionsBitmap is set, the return value will be TRUE return !(~ImageSectionsBitmap); } /** Separate the image sections in the memory map and run a check to ensure the output is valid. @param[in] Context Context containing the memory map and image record pointers @retval TRUE if the memory map is split correctly **/ STATIC BOOLEAN SeparateAndCheck ( IN IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *Context ) { UINTN MemoryMapSize; MemoryMapSize = BASE_MEMORY_MAP_SIZE; // Separate the memory map so each image section has its own descriptor SplitTable ( &MemoryMapSize, Context->MemoryMap, DESCRIPTOR_SIZE, &Context->ImageList, NUMBER_OF_ADDITIONAL_DESCRIPTORS ); // Ensure the updated memory map is valid return IsMemoryMapValid (MemoryMapSize, Context->MemoryMap, &Context->ImageList); } /** Test the case where the image range contains multiple code sections and does not perfectly align with the existing memory descriptor. @param[in] Context Context containing the memory map and image record pointers @retval UNIT_TEST_PASSED The Unit test has completed and the test case was successful. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI MaxOutAdditionalDescriptors ( IN UNIT_TEST_CONTEXT Context ) { IMAGE_PROPERTIES_RECORD *Image1; IMAGE_PROPERTIES_RECORD *Image2; IMAGE_PROPERTIES_RECORD *Image3; IMAGE_PROPERTIES_RECORD_CODE_SECTION *CodeSectionInImage1; IMAGE_PROPERTIES_RECORD_CODE_SECTION *CodeSectionInImage2; IMAGE_PROPERTIES_RECORD_CODE_SECTION *CodeSectionInImage3; IMAGE_PROPERTIES_RECORD_CODE_SECTION *AddCodeSectionInImage1; IMAGE_PROPERTIES_RECORD_CODE_SECTION *AddCodeSectionInImage2; IMAGE_PROPERTIES_RECORD_CODE_SECTION *AddCodeSectionInImage3; IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *TestContext; TestContext = (IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *)Context; Image1 = CR (TestContext->ImageList.ForwardLink, IMAGE_PROPERTIES_RECORD, Link, IMAGE_PROPERTIES_RECORD_SIGNATURE); Image2 = CR (Image1->Link.ForwardLink, IMAGE_PROPERTIES_RECORD, Link, IMAGE_PROPERTIES_RECORD_SIGNATURE); Image3 = CR (Image2->Link.ForwardLink, IMAGE_PROPERTIES_RECORD, Link, IMAGE_PROPERTIES_RECORD_SIGNATURE); CodeSectionInImage1 = CR (Image1->CodeSegmentList.ForwardLink, IMAGE_PROPERTIES_RECORD_CODE_SECTION, Link, IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE); CodeSectionInImage2 = CR (Image2->CodeSegmentList.ForwardLink, IMAGE_PROPERTIES_RECORD_CODE_SECTION, Link, IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE); CodeSectionInImage3 = CR (Image3->CodeSegmentList.ForwardLink, IMAGE_PROPERTIES_RECORD_CODE_SECTION, Link, IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE); /////////////// // Descriptor 1 /////////////// // | | | | | | | | // | 4K PAGE | DATA | CODE | DATA | CODE | DATA | 4K PAGE * 5 | // | | | | | | | | Image1->ImageBase = BASE_DESCRIPTOR_START_ADDRESS (1) + EFI_PAGE_SIZE; Image1->ImageSize = BASE_DESCRIPTOR_ENTRY_SIZE - EFI_PAGE_SIZE - EFI_PAGE_SIZE; Image1->CodeSegmentCount = 2; CodeSectionInImage1->CodeSegmentBase = Image1->ImageBase + EFI_PAGE_SIZE; CodeSectionInImage1->CodeSegmentSize = EFI_PAGE_SIZE; TestContext->MemoryMap[1].Type = EfiBootServicesCode; AddCodeSectionInImage1 = AllocateZeroPool (sizeof (IMAGE_PROPERTIES_RECORD_CODE_SECTION)); AddCodeSectionInImage1->Signature = IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE; AddCodeSectionInImage1->CodeSegmentBase = CodeSectionInImage1->CodeSegmentBase + CodeSectionInImage1->CodeSegmentSize + EFI_PAGE_SIZE; AddCodeSectionInImage1->CodeSegmentSize = EFI_PAGE_SIZE; InsertTailList (&Image1->CodeSegmentList, &AddCodeSectionInImage1->Link); /////////////// // Descriptor 2 /////////////// // | | | | | | | | // | 4K PAGE | DATA | CODE | DATA | CODE | DATA | 4K PAGE * 5 | // | | | | | | | | Image2->ImageBase = BASE_DESCRIPTOR_START_ADDRESS (2) + EFI_PAGE_SIZE; Image2->ImageSize = BASE_DESCRIPTOR_ENTRY_SIZE - EFI_PAGE_SIZE - EFI_PAGE_SIZE; Image2->CodeSegmentCount = 2; CodeSectionInImage2->CodeSegmentBase = Image2->ImageBase + EFI_PAGE_SIZE; CodeSectionInImage2->CodeSegmentSize = EFI_PAGE_SIZE; TestContext->MemoryMap[2].Type = EfiLoaderCode; AddCodeSectionInImage2 = AllocateZeroPool (sizeof (IMAGE_PROPERTIES_RECORD_CODE_SECTION)); AddCodeSectionInImage2->Signature = IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE; AddCodeSectionInImage2->CodeSegmentBase = CodeSectionInImage2->CodeSegmentBase + CodeSectionInImage2->CodeSegmentSize + EFI_PAGE_SIZE; AddCodeSectionInImage2->CodeSegmentSize = EFI_PAGE_SIZE; InsertTailList (&Image2->CodeSegmentList, &AddCodeSectionInImage2->Link); /////////////// // Descriptor 3 /////////////// // | | | | | | | | // | 4K PAGE | DATA | CODE | DATA | CODE | DATA | 4K PAGE * 5 | // | | | | | | | | Image3->ImageBase = BASE_DESCRIPTOR_START_ADDRESS (3) + EFI_PAGE_SIZE; Image3->ImageSize = BASE_DESCRIPTOR_ENTRY_SIZE - EFI_PAGE_SIZE - EFI_PAGE_SIZE; Image3->CodeSegmentCount = 2; CodeSectionInImage3->CodeSegmentBase = Image3->ImageBase + EFI_PAGE_SIZE; CodeSectionInImage3->CodeSegmentSize = EFI_PAGE_SIZE; TestContext->MemoryMap[3].Type = EfiRuntimeServicesCode; AddCodeSectionInImage3 = AllocateZeroPool (sizeof (IMAGE_PROPERTIES_RECORD_CODE_SECTION)); AddCodeSectionInImage3->Signature = IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE; AddCodeSectionInImage3->CodeSegmentBase = CodeSectionInImage3->CodeSegmentBase + CodeSectionInImage3->CodeSegmentSize + EFI_PAGE_SIZE; AddCodeSectionInImage3->CodeSegmentSize = EFI_PAGE_SIZE; InsertTailList (&Image3->CodeSegmentList, &AddCodeSectionInImage3->Link); UT_ASSERT_TRUE (SeparateAndCheck (TestContext)); return UNIT_TEST_PASSED; } /** Test the case where multiple image ranges lie within an existing memory descriptor. @param[in] Context Context containing the memory map and image record pointers @retval UNIT_TEST_PASSED The Unit test has completed and the test case was successful. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI MultipleImagesInOneDescriptor ( IN UNIT_TEST_CONTEXT Context ) { IMAGE_PROPERTIES_RECORD *Image1; IMAGE_PROPERTIES_RECORD *Image2; IMAGE_PROPERTIES_RECORD *Image3; IMAGE_PROPERTIES_RECORD_CODE_SECTION *CodeSectionInImage1; IMAGE_PROPERTIES_RECORD_CODE_SECTION *CodeSectionInImage2; IMAGE_PROPERTIES_RECORD_CODE_SECTION *CodeSectionInImage3; IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *TestContext; TestContext = (IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *)Context; Image1 = CR (TestContext->ImageList.ForwardLink, IMAGE_PROPERTIES_RECORD, Link, IMAGE_PROPERTIES_RECORD_SIGNATURE); Image2 = CR (Image1->Link.ForwardLink, IMAGE_PROPERTIES_RECORD, Link, IMAGE_PROPERTIES_RECORD_SIGNATURE); Image3 = CR (Image2->Link.ForwardLink, IMAGE_PROPERTIES_RECORD, Link, IMAGE_PROPERTIES_RECORD_SIGNATURE); CodeSectionInImage1 = CR (Image1->CodeSegmentList.ForwardLink, IMAGE_PROPERTIES_RECORD_CODE_SECTION, Link, IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE); CodeSectionInImage2 = CR (Image2->CodeSegmentList.ForwardLink, IMAGE_PROPERTIES_RECORD_CODE_SECTION, Link, IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE); CodeSectionInImage3 = CR (Image3->CodeSegmentList.ForwardLink, IMAGE_PROPERTIES_RECORD_CODE_SECTION, Link, IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE); /////////////// // Descriptor 1 /////////////// // | | | | | | | | | | | | | // | 4K PAGE | DATA | CODE | DATA | 4K PAGE | DATA | CODE | DATA | DATA | CODE | DATA | 4K PAGE | // | | | | | | | | | | | | | Image1->ImageBase = BASE_DESCRIPTOR_START_ADDRESS (1) + EFI_PAGE_SIZE; Image1->ImageSize = EFI_PAGES_TO_SIZE (3); Image1->CodeSegmentCount = 1; CodeSectionInImage1->CodeSegmentBase = Image1->ImageBase + EFI_PAGE_SIZE; CodeSectionInImage1->CodeSegmentSize = EFI_PAGE_SIZE; TestContext->MemoryMap[1].Type = EfiBootServicesCode; Image2->ImageBase = Image1->ImageBase + Image1->ImageSize + EFI_PAGE_SIZE; Image2->ImageSize = EFI_PAGES_TO_SIZE (3); Image2->CodeSegmentCount = 1; CodeSectionInImage2->CodeSegmentBase = Image2->ImageBase + EFI_PAGE_SIZE; CodeSectionInImage2->CodeSegmentSize = EFI_PAGE_SIZE; Image3->ImageBase = Image2->ImageBase + Image2->ImageSize; Image3->ImageSize = EFI_PAGES_TO_SIZE (3); Image3->CodeSegmentCount = 1; CodeSectionInImage3->CodeSegmentBase = Image3->ImageBase + EFI_PAGE_SIZE; CodeSectionInImage3->CodeSegmentSize = EFI_PAGE_SIZE; UT_ASSERT_TRUE (SeparateAndCheck (TestContext)); return UNIT_TEST_PASSED; } /** Test the case where all image ranges do not fit perfectly within an existing memory descriptor. @param[in] Context Context containing the memory map and image record pointers @retval UNIT_TEST_PASSED The Unit test has completed and the test case was successful. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI ImagesDontFitDescriptors ( IN UNIT_TEST_CONTEXT Context ) { IMAGE_PROPERTIES_RECORD *Image1; IMAGE_PROPERTIES_RECORD *Image2; IMAGE_PROPERTIES_RECORD *Image3; IMAGE_PROPERTIES_RECORD_CODE_SECTION *CodeSectionInImage1; IMAGE_PROPERTIES_RECORD_CODE_SECTION *CodeSectionInImage2; IMAGE_PROPERTIES_RECORD_CODE_SECTION *CodeSectionInImage3; IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *TestContext; TestContext = (IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *)Context; Image1 = CR (TestContext->ImageList.ForwardLink, IMAGE_PROPERTIES_RECORD, Link, IMAGE_PROPERTIES_RECORD_SIGNATURE); Image2 = CR (Image1->Link.ForwardLink, IMAGE_PROPERTIES_RECORD, Link, IMAGE_PROPERTIES_RECORD_SIGNATURE); Image3 = CR (Image2->Link.ForwardLink, IMAGE_PROPERTIES_RECORD, Link, IMAGE_PROPERTIES_RECORD_SIGNATURE); CodeSectionInImage1 = CR (Image1->CodeSegmentList.ForwardLink, IMAGE_PROPERTIES_RECORD_CODE_SECTION, Link, IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE); CodeSectionInImage2 = CR (Image2->CodeSegmentList.ForwardLink, IMAGE_PROPERTIES_RECORD_CODE_SECTION, Link, IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE); CodeSectionInImage3 = CR (Image3->CodeSegmentList.ForwardLink, IMAGE_PROPERTIES_RECORD_CODE_SECTION, Link, IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE); /////////////// // Descriptor 1 /////////////// // | | | | | // | 4K PAGE | DATA | CODE * 2 | DATA * 8 | // | | | | | Image1->ImageBase = BASE_DESCRIPTOR_START_ADDRESS (1) + EFI_PAGE_SIZE; Image1->ImageSize = BASE_DESCRIPTOR_ENTRY_SIZE - EFI_PAGE_SIZE; Image1->CodeSegmentCount = 1; CodeSectionInImage1->CodeSegmentBase = Image1->ImageBase + EFI_PAGE_SIZE; CodeSectionInImage1->CodeSegmentSize = EFI_PAGES_TO_SIZE (2); TestContext->MemoryMap[1].Type = EfiBootServicesCode; /////////////// // Descriptor 3 /////////////// // | | | | | // | DATA | CODE * 3 | DATA * 7 | 4K PAGE | // | | | | | Image2->ImageBase = BASE_DESCRIPTOR_START_ADDRESS (3); Image2->ImageSize = BASE_DESCRIPTOR_ENTRY_SIZE - EFI_PAGE_SIZE; Image2->CodeSegmentCount = 1; CodeSectionInImage2->CodeSegmentBase = Image2->ImageBase + EFI_PAGE_SIZE; CodeSectionInImage2->CodeSegmentSize = EFI_PAGES_TO_SIZE (3); TestContext->MemoryMap[3].Type = EfiLoaderCode; /////////////// // Descriptor 4 /////////////// // | | | | | | // | 4K PAGE | DATA | CODE * 2 | DATA * 7 | 4K PAGE | // | | | | | | Image3->ImageBase = BASE_DESCRIPTOR_START_ADDRESS (4) + EFI_PAGE_SIZE; Image3->ImageSize = BASE_DESCRIPTOR_ENTRY_SIZE - EFI_PAGE_SIZE - EFI_PAGE_SIZE; Image3->CodeSegmentCount = 1; CodeSectionInImage3->CodeSegmentBase = Image3->ImageBase + EFI_PAGE_SIZE; CodeSectionInImage3->CodeSegmentSize = EFI_PAGES_TO_SIZE (2); TestContext->MemoryMap[4].Type = EfiRuntimeServicesCode; UT_ASSERT_TRUE (SeparateAndCheck (TestContext)); return UNIT_TEST_PASSED; } /** Test the case where all image ranges fit perfectly within an existing memory descriptor. @param[in] Context Context containing the memory map and image record pointers @retval UNIT_TEST_PASSED The Unit test has completed and the test case was successful. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI ImagesFitDescriptors ( IN UNIT_TEST_CONTEXT Context ) { IMAGE_PROPERTIES_RECORD *Image1; IMAGE_PROPERTIES_RECORD *Image2; IMAGE_PROPERTIES_RECORD *Image3; IMAGE_PROPERTIES_RECORD_CODE_SECTION *CodeSectionInImage1; IMAGE_PROPERTIES_RECORD_CODE_SECTION *CodeSectionInImage2; IMAGE_PROPERTIES_RECORD_CODE_SECTION *CodeSectionInImage3; IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *TestContext; TestContext = (IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *)Context; Image1 = CR (TestContext->ImageList.ForwardLink, IMAGE_PROPERTIES_RECORD, Link, IMAGE_PROPERTIES_RECORD_SIGNATURE); Image2 = CR (Image1->Link.ForwardLink, IMAGE_PROPERTIES_RECORD, Link, IMAGE_PROPERTIES_RECORD_SIGNATURE); Image3 = CR (Image2->Link.ForwardLink, IMAGE_PROPERTIES_RECORD, Link, IMAGE_PROPERTIES_RECORD_SIGNATURE); CodeSectionInImage1 = CR (Image1->CodeSegmentList.ForwardLink, IMAGE_PROPERTIES_RECORD_CODE_SECTION, Link, IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE); CodeSectionInImage2 = CR (Image2->CodeSegmentList.ForwardLink, IMAGE_PROPERTIES_RECORD_CODE_SECTION, Link, IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE); CodeSectionInImage3 = CR (Image3->CodeSegmentList.ForwardLink, IMAGE_PROPERTIES_RECORD_CODE_SECTION, Link, IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE); /////////////// // Descriptor 1 /////////////// // | | | | // | DATA | CODE * 3 | DATA * 8 | // | | | | Image1->ImageBase = BASE_DESCRIPTOR_START_ADDRESS (1); Image1->ImageSize = BASE_DESCRIPTOR_ENTRY_SIZE; Image1->CodeSegmentCount = 1; CodeSectionInImage1->CodeSegmentBase = Image1->ImageBase + EFI_PAGE_SIZE; CodeSectionInImage1->CodeSegmentSize = EFI_PAGES_TO_SIZE (3); TestContext->MemoryMap[1].Type = EfiBootServicesCode; /////////////// // Descriptor 2 /////////////// // | | | | // | DATA | CODE * 4 | DATA * 7 | // | | | | Image2->ImageBase = BASE_DESCRIPTOR_START_ADDRESS (2); Image2->ImageSize = BASE_DESCRIPTOR_ENTRY_SIZE; Image2->CodeSegmentCount = 1; CodeSectionInImage2->CodeSegmentBase = Image2->ImageBase + EFI_PAGE_SIZE; CodeSectionInImage2->CodeSegmentSize = EFI_PAGES_TO_SIZE (4); TestContext->MemoryMap[2].Type = EfiLoaderCode; /////////////// // Descriptor 3 /////////////// // | | | | // | DATA | CODE * 3 | DATA * 8 | // | | | | Image3->ImageBase = BASE_DESCRIPTOR_START_ADDRESS (3); Image3->ImageSize = BASE_DESCRIPTOR_ENTRY_SIZE; Image3->CodeSegmentCount = 1; CodeSectionInImage3->CodeSegmentBase = Image3->ImageBase + EFI_PAGE_SIZE; CodeSectionInImage3->CodeSegmentSize = EFI_PAGES_TO_SIZE (3); TestContext->MemoryMap[3].Type = EfiRuntimeServicesCode; UT_ASSERT_TRUE (SeparateAndCheck (TestContext)); return UNIT_TEST_PASSED; } /** Free all allocated memory. @param[in] Context Context containing the memory map and image record pointers **/ VOID EFIAPI TestCleanup ( IN UNIT_TEST_CONTEXT Context ) { IMAGE_PROPERTIES_RECORD *ImageRecord; IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection; LIST_ENTRY *ImageRecordLink; LIST_ENTRY *CodeSegmentListHead; IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *TestContext; TestContext = (IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *)Context; ImageRecordLink = &TestContext->ImageList; while (!IsListEmpty (ImageRecordLink)) { ImageRecord = CR ( ImageRecordLink->ForwardLink, IMAGE_PROPERTIES_RECORD, Link, IMAGE_PROPERTIES_RECORD_SIGNATURE ); CodeSegmentListHead = &ImageRecord->CodeSegmentList; while (!IsListEmpty (CodeSegmentListHead)) { ImageRecordCodeSection = CR ( CodeSegmentListHead->ForwardLink, IMAGE_PROPERTIES_RECORD_CODE_SECTION, Link, IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE ); RemoveEntryList (&ImageRecordCodeSection->Link); FreePool (ImageRecordCodeSection); } RemoveEntryList (&ImageRecord->Link); FreePool (ImageRecord); } if (TestContext->MemoryMap != NULL) { FreePool (TestContext->MemoryMap); } } /** Create a generic image list with the proper signatures which will be customized for each test and allocate the default memory map. @param[out] TestContext Context which will be passed to the test cases **/ STATIC VOID CreateBaseContextEntry ( OUT IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *TestContext ) { IMAGE_PROPERTIES_RECORD *Image1; IMAGE_PROPERTIES_RECORD *Image2; IMAGE_PROPERTIES_RECORD *Image3; IMAGE_PROPERTIES_RECORD_CODE_SECTION *CodeSectionInImage1; IMAGE_PROPERTIES_RECORD_CODE_SECTION *CodeSectionInImage2; IMAGE_PROPERTIES_RECORD_CODE_SECTION *CodeSectionInImage3; InitializeListHead (&TestContext->ImageList); Image1 = AllocateZeroPool (sizeof (IMAGE_PROPERTIES_RECORD)); CodeSectionInImage1 = AllocateZeroPool (sizeof (IMAGE_PROPERTIES_RECORD_CODE_SECTION)); Image1->Signature = IMAGE_PROPERTIES_RECORD_SIGNATURE; CodeSectionInImage1->Signature = IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE; InitializeListHead (&Image1->CodeSegmentList); InsertTailList (&TestContext->ImageList, &Image1->Link); InsertTailList (&Image1->CodeSegmentList, &CodeSectionInImage1->Link); Image2 = AllocateZeroPool (sizeof (IMAGE_PROPERTIES_RECORD)); CodeSectionInImage2 = AllocateZeroPool (sizeof (IMAGE_PROPERTIES_RECORD_CODE_SECTION)); Image2->Signature = IMAGE_PROPERTIES_RECORD_SIGNATURE; CodeSectionInImage2->Signature = IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE; InitializeListHead (&Image2->CodeSegmentList); InsertTailList (&TestContext->ImageList, &Image2->Link); InsertTailList (&Image2->CodeSegmentList, &CodeSectionInImage2->Link); Image3 = AllocateZeroPool (sizeof (IMAGE_PROPERTIES_RECORD)); CodeSectionInImage3 = AllocateZeroPool (sizeof (IMAGE_PROPERTIES_RECORD_CODE_SECTION)); Image3->Signature = IMAGE_PROPERTIES_RECORD_SIGNATURE; CodeSectionInImage3->Signature = IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE; InitializeListHead (&Image3->CodeSegmentList); InsertTailList (&TestContext->ImageList, &Image3->Link); InsertTailList (&Image3->CodeSegmentList, &CodeSectionInImage3->Link); TestContext->MemoryMap = AllocateZeroPool (SPLIT_MEMORY_MAP_SIZE); CopyMem (TestContext->MemoryMap, &BaseMemoryMap, BASE_MEMORY_MAP_SIZE); return; } /** Initialze the unit test framework, suite, and unit tests. @retval EFI_SUCCESS All test cases were dispatched. @retval EFI_OUT_OF_RESOURCES There are not enough resources available to initialize the unit tests. **/ STATIC EFI_STATUS EFIAPI UnitTestingEntry ( VOID ) { EFI_STATUS Status; UNIT_TEST_FRAMEWORK_HANDLE Framework; UNIT_TEST_SUITE_HANDLE ImagePropertiesRecordTests; IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *Context1; IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *Context2; IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *Context3; IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *Context4; DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION)); Framework = NULL; Context1 = (IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *)AllocateZeroPool (sizeof (IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT)); Context2 = (IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *)AllocateZeroPool (sizeof (IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT)); Context3 = (IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *)AllocateZeroPool (sizeof (IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT)); Context4 = (IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT *)AllocateZeroPool (sizeof (IMAGE_PROPERTIES_RECORD_HOST_TEST_CONTEXT)); CreateBaseContextEntry (Context1); CreateBaseContextEntry (Context2); CreateBaseContextEntry (Context3); CreateBaseContextEntry (Context4); // // Start setting up the test framework for running the tests. // Status = InitUnitTestFramework (&Framework, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status)); goto EXIT; } // // Populate the Unit Test Suite. // Status = CreateUnitTestSuite (&ImagePropertiesRecordTests, Framework, "Image Properties Record Tests", "ImagePropertiesRecordLib.SplitTable", NULL, NULL); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for the Image Properties Record Tests\n")); Status = EFI_OUT_OF_RESOURCES; goto EXIT; } // // --------------Suite-----------Description--------------Name----------Function--------Pre---Post-------------------Context----------- // AddTestCase (ImagePropertiesRecordTests, "All images fit perfectly into existing descriptors", "ImagesFitDescriptors", ImagesFitDescriptors, NULL, TestCleanup, Context1); AddTestCase (ImagePropertiesRecordTests, "All images don't fit perfectly into existing descriptors", "ImagesDontFitDescriptors", ImagesDontFitDescriptors, NULL, TestCleanup, Context2); AddTestCase (ImagePropertiesRecordTests, "All Images are contined In single descriptor", "MultipleImagesInOneDescriptor", MultipleImagesInOneDescriptor, NULL, TestCleanup, Context3); AddTestCase (ImagePropertiesRecordTests, "Multiple code sections each image", "MaxOutAdditionalDescriptors", MaxOutAdditionalDescriptors, NULL, TestCleanup, Context4); // // Execute the tests. // Status = RunAllTestSuites (Framework); EXIT: if (Framework) { FreeUnitTestFramework (Framework); } return Status; } /// /// Avoid ECC error for function name that starts with lower case letter /// #define ImagePropertiesRecordLibUnitTestMain main /** Standard POSIX C entry point for host based unit test execution. @param[in] Argc Number of arguments @param[in] Argv Array of pointers to arguments @retval 0 Success @retval other Error **/ INT32 ImagePropertiesRecordLibUnitTestMain ( IN INT32 Argc, IN CHAR8 *Argv[] ) { return UnitTestingEntry (); }