/** @file | |
Unit tests the SplitTable() ImagePropertiesRecordLib Logic | |
Copyright (C) Microsoft Corporation. | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdarg.h> | |
#include <stddef.h> | |
#include <setjmp.h> | |
#include <cmocka.h> | |
#include <Uefi.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/UnitTestLib.h> | |
#include <Library/ImagePropertiesRecordLib.h> | |
#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 (); | |
} |