diff options
author | Michael D Kinney <michael.d.kinney@intel.com> | 2024-11-25 22:34:54 +0300 |
---|---|---|
committer | mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> | 2025-01-21 08:02:38 +0300 |
commit | e78fb8a366fcd39329f86ce5a095d88d9be8dcb6 (patch) | |
tree | df22af1def059dc8e2ee74e4da2cdde8e33e970a | |
parent | 5f97d5391eadc592548aa635b8b16e811df046fc (diff) | |
download | edk2-e78fb8a366fcd39329f86ce5a095d88d9be8dcb6.tar.xz |
UnitTestFrameworkPkg/MemoryAllocationLibPosix: Add allocate below address
Add HostMemoryAllocationBelowAddressLib class and implementation that
uses OS specific services to perform pool and page allocations below
a specified address in a host based unit test application execution
environment. This library class is only required for mocking buffers
that are assumed to be below a specific address by code under test.
Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com>
8 files changed, 778 insertions, 1 deletions
diff --git a/UnitTestFrameworkPkg/Include/Library/HostMemoryAllocationBelowAddressLib.h b/UnitTestFrameworkPkg/Include/Library/HostMemoryAllocationBelowAddressLib.h new file mode 100644 index 0000000000..36ba3d6298 --- /dev/null +++ b/UnitTestFrameworkPkg/Include/Library/HostMemoryAllocationBelowAddressLib.h @@ -0,0 +1,90 @@ +/** @file
+ HostMemoryAllocationBelowAddressLib class
+
+ Copyright (c) 2024, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef HOST_MEMORY_ALLOCATION_BELOW_ADDRESS_LIB_H_
+
+/**
+ Allocate memory below a specifies address.
+
+ @param[in] MaximumAddress The address below which the memory allocation must
+ be performed.
+ @param[in] Length The size, in bytes, of the memory allocation.
+
+ @retval !NULL Pointer to the allocated memory.
+ @retval NULL The memory allocation failed.
+**/
+VOID *
+EFIAPI
+HostAllocatePoolBelowAddress (
+ IN UINT64 MaximumAddress,
+ IN UINT64 Length
+ );
+
+/**
+ Free memory allocated with AllocateMemoryHostAllocatePoolBelowAddress().
+
+ @param[in] Address Pointer to buffer previously allocated with
+ HostAllocatePoolBelowAddress().
+**/
+VOID
+EFIAPI
+HostFreePoolBelowAddress (
+ IN VOID *Address
+ );
+
+/**
+ Allocates one or more 4KB pages below a specified address at a specified
+ alignment.
+
+ Allocates the number of 4KB pages specified by Pages below MaximumAddress with
+ an alignment specified by Alignment. The allocated buffer is returned. If
+ Pages is 0, then NULL is returned. If there is not enough memory below the
+ requested address at the specified alignment remaining to satisfy the request,
+ then NULL is returned.
+
+ If Alignment is not a power of two and Alignment is not zero, then ASSERT().
+ If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT().
+
+ @param[in] MaximumAddress The address below which the memory allocation must
+ @param[in] Pages The number of 4 KB pages to allocate.
+ @param[in] Alignment The requested alignment of the allocation. Must be
+ a power of two. If Alignment is zero, then byte
+ alignment is used.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+**/
+VOID *
+EFIAPI
+HostAllocateAlignedPagesBelowAddress (
+ IN UINT64 MaximumAddress,
+ IN UINTN Pages,
+ IN UINT64 Alignment
+ );
+
+/**
+ Frees one or more 4KB pages that were previously allocated with
+ HostAllocateAlignedPagesBelowAddress().
+
+ Frees the number of 4KB pages specified by Pages from the buffer specified by
+ Buffer. Buffer must have been allocated with HostAllocateAlignedPagesBelowAddress().
+ If it is not possible to free allocated pages, then this function will perform
+ no actions.
+
+ If Buffer was not allocated with HostAllocateAlignedPagesBelowAddress(), then
+ ASSERT(). If Pages is zero, then ASSERT().
+
+ @param[in] Buffer The pointer to the buffer of pages to free.
+ @param[in] Pages The number of 4 KB pages to free.
+**/
+VOID
+EFIAPI
+HostFreeAlignedPagesBelowAddress (
+ IN VOID *Buffer,
+ IN UINTN Pages
+ );
+
+#endif
diff --git a/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/AllocateBelowAddress.c b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/AllocateBelowAddress.c new file mode 100644 index 0000000000..4eb386c1ed --- /dev/null +++ b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/AllocateBelowAddress.c @@ -0,0 +1,438 @@ +/** @file
+ Instance of Memory Below Address Allocation Library based on Windows APIs
+ and Linux APIs.
+
+ Uses Windows APIs VirtualAlloc() and VirtualFree() to allocate and free memory
+ below a specified virtual address.
+
+ Uses Linux APIs mmap() and munmap() to allocate and free memory below a
+ specified virtual address.
+
+ Copyright (c) 2024, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#if defined (_WIN32) || defined (_WIN64)
+ #include "WinInclude.h"
+#elif defined (__linux__)
+ #include <sys/mman.h>
+ #include <unistd.h>
+ #include <errno.h>
+ #include <string.h>
+#else
+ #error Unsupported target
+#endif
+
+#include <Library/HostMemoryAllocationBelowAddressLib.h>
+#include <Library/DebugLib.h>
+
+///
+/// Signature for PAGE_HEAD_BELOW_ADDRESS structure
+/// Used to verify that buffer being freed was allocated by this library.
+///
+#define PAGE_HEAD_BELOW_ADDRESS_PRIVATE_SIGNATURE SIGNATURE_64 ('P', 'A', 'H', 'B', 'e', 'l', 'A', 'd')
+
+///
+/// Structure placed immediately before an aligned allocation to store the
+/// information required to free the entire allocated buffer.
+///
+typedef struct {
+ UINT64 Signature;
+ VOID *AllocatedBuffer;
+ UINTN TotalPages;
+ VOID *AlignedBuffer;
+ UINTN AlignedPages;
+} PAGE_HEAD_BELOW_ADDRESS;
+
+///
+/// Signature for POOL_HEAD_BELOW_ADDRESS structure
+/// Used to verify that buffer being freed was allocated by this library.
+///
+#define POOL_HEAD_BELOW_ADDRESS_PRIVATE_SIGNATURE SIGNATURE_64 ('P', 'O', 'H', 'B', 'e', 'l', 'A', 'd')
+
+///
+/// Structure placed immediately before an pool allocation to store the
+/// information required to free the entire allocated buffer.
+///
+typedef struct {
+ UINT64 Signature;
+ UINT64 TotalSize;
+} POOL_HEAD_BELOW_ADDRESS;
+
+//
+// Lowest address that can be allocated by this library
+//
+#define MINIMUM_ALLOCATION_ADDRESS BASE_64KB
+
+//
+// The page size of the host
+//
+static UINTN mPageSize = 0;
+
+/**
+ Use system services to get the host page size.
+
+ @return Host page size in bytes.
+**/
+static
+UINTN
+HostGetPageSize (
+ VOID
+ )
+{
+ #if defined (_WIN32) || defined (_WIN64)
+ SYSTEM_INFO SystemInfo;
+
+ GetSystemInfo (&SystemInfo);
+ return (UINTN)SystemInfo.dwPageSize;
+ #elif defined (__linux__)
+ return sysconf (_SC_PAGESIZE);
+ #else
+ return 0;
+ #endif
+}
+
+/**
+ Use system services to allocate a buffer between a minimum and maximum
+ address aligned to the requested page size.
+
+ @param[in] MaximumAddress The address below which the memory allocation must
+ be performed.
+ @param[in] Length The size, in bytes, of the memory allocation.
+
+ @retval !NULL Pointer to the allocated memory.
+ @retval NULL The memory allocation failed.
+**/
+static
+VOID *
+HostAllocateBufferInRange (
+ UINTN MaximumAddress,
+ UINTN Length
+ )
+{
+ UINTN Address;
+ VOID *AllocatedAddress;
+
+ if (mPageSize == 0) {
+ mPageSize = HostGetPageSize ();
+ if (mPageSize == 0) {
+ return NULL;
+ }
+ }
+
+ //
+ // Round maximum address down to the nearest page boundary
+ //
+ MaximumAddress &= ~(mPageSize - 1);
+
+ for (Address = MaximumAddress; Address >= MINIMUM_ALLOCATION_ADDRESS; Address -= mPageSize) {
+ #if defined (_WIN32) || defined (_WIN64)
+ AllocatedAddress = VirtualAlloc (
+ (VOID *)Address,
+ Length,
+ MEM_RESERVE | MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (AllocatedAddress != NULL) {
+ return AllocatedAddress;
+ }
+
+ #elif defined (__linux__)
+ AllocatedAddress = mmap (
+ (VOID *)Address,
+ Length,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE,
+ -1,
+ 0
+ );
+ if (AllocatedAddress != MAP_FAILED) {
+ return AllocatedAddress;
+ }
+
+ #else
+ return NULL;
+ #endif
+ }
+
+ return NULL;
+}
+
+/**
+ Use system services to free memory allocated with HostAllocateBufferInRange().
+
+ @param[in] Buffer Pointer to buffer previously allocated with
+ HostAllocateBufferInRange().
+ @param[in] Length Length, in bytes, of buffer previously allocated with
+ HostAllocateBufferInRange().
+**/
+static
+VOID
+HostFreeBufferInRange (
+ IN VOID *Buffer,
+ IN UINTN Length
+ )
+{
+ #if defined (_WIN32) || defined (_WIN64)
+ if (!VirtualFree (Buffer, 0, MEM_RELEASE)) {
+ ASSERT (FALSE);
+ }
+
+ #elif defined (__linux__)
+ if (munmap (Buffer, Length) == -1) {
+ ASSERT (FALSE);
+ }
+
+ #endif
+}
+
+/**
+ Allocate memory below a specific address.
+
+ @param[in] MaximumAddress The address below which the memory allocation must
+ be performed.
+ @param[in] Length The size, in bytes, of the memory allocation.
+
+ @retval !NULL Pointer to the allocated memory.
+ @retval NULL The memory allocation failed.
+**/
+VOID *
+EFIAPI
+HostAllocatePoolBelowAddress (
+ IN UINT64 MaximumAddress,
+ IN UINT64 Length
+ )
+{
+ VOID *AllocatedAddress;
+ POOL_HEAD_BELOW_ADDRESS *PoolHead;
+
+ if (Length == 0) {
+ return NULL;
+ }
+
+ //
+ // Limit maximum address to the largest supported virtual address
+ //
+ MaximumAddress = MIN (MaximumAddress, MAX_UINTN);
+
+ //
+ // Increase requested allocation length by the size of the pool header
+ //
+ Length += sizeof (POOL_HEAD_BELOW_ADDRESS);
+
+ //
+ // Make sure allocation length is smaller than maximum address
+ //
+ if (Length > MaximumAddress) {
+ DEBUG ((DEBUG_ERROR, "HostAllocatePoolBelowAddress: Length > MaximumAddress\n"));
+ return NULL;
+ }
+
+ //
+ // Reduce maximum address by the requested allocation length
+ //
+ MaximumAddress -= Length;
+
+ AllocatedAddress = HostAllocateBufferInRange (
+ (UINTN)MaximumAddress,
+ (UINTN)Length
+ );
+ if (AllocatedAddress == NULL) {
+ DEBUG ((DEBUG_ERROR, "HostAllocatePoolBelowAddress: HostAllocateBufferInRange failed\n"));
+ return NULL;
+ }
+
+ DEBUG_CLEAR_MEMORY (AllocatedAddress, (UINTN)Length);
+ PoolHead = (POOL_HEAD_BELOW_ADDRESS *)AllocatedAddress;
+ PoolHead->Signature = POOL_HEAD_BELOW_ADDRESS_PRIVATE_SIGNATURE;
+ PoolHead->TotalSize = Length;
+ return (VOID *)(PoolHead + 1);
+}
+
+/**
+ Free memory allocated with HostAllocatePoolBelowAddress().
+
+ @param[in] Buffer Pointer to buffer previously allocated with
+ HostAllocatePoolBelowAddress().
+**/
+VOID
+EFIAPI
+HostFreePoolBelowAddress (
+ IN VOID *Buffer
+ )
+{
+ POOL_HEAD_BELOW_ADDRESS *PoolHead;
+ UINTN Length;
+
+ ASSERT (Buffer != NULL);
+
+ PoolHead = ((POOL_HEAD_BELOW_ADDRESS *)Buffer) - 1;
+
+ ASSERT (PoolHead != NULL);
+ ASSERT (PoolHead->Signature == POOL_HEAD_BELOW_ADDRESS_PRIVATE_SIGNATURE);
+ ASSERT (PoolHead->TotalSize >= sizeof (POOL_HEAD_BELOW_ADDRESS));
+ ASSERT (PoolHead->TotalSize <= MAX_UINTN);
+
+ Length = (UINTN)PoolHead->TotalSize;
+ DEBUG_CLEAR_MEMORY (PoolHead, Length);
+
+ HostFreeBufferInRange (PoolHead, Length);
+}
+
+/**
+ Allocates one or more 4KB pages below a specified address at a specified
+ alignment.
+
+ Allocates the number of 4KB pages specified by Pages below MaximumAddress with
+ an alignment specified by Alignment. The allocated buffer is returned. If
+ Pages is 0, then NULL is returned. If there is not enough memory below the
+ requested address at the specified alignment remaining to satisfy the request,
+ then NULL is returned.
+
+ If Alignment is not a power of two and Alignment is not zero, then ASSERT().
+ If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT().
+
+ @param[in] MaximumAddress The address below which the memory allocation must
+ @param[in] Pages The number of 4 KB pages to allocate.
+ @param[in] Alignment The requested alignment of the allocation. Must be
+ a power of two. If Alignment is zero, then byte
+ alignment is used.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+**/
+VOID *
+EFIAPI
+HostAllocateAlignedPagesBelowAddress (
+ IN UINT64 MaximumAddress,
+ IN UINTN Pages,
+ IN UINT64 Alignment
+ )
+{
+ PAGE_HEAD_BELOW_ADDRESS PageHead;
+ PAGE_HEAD_BELOW_ADDRESS *PageHeadPtr;
+ UINTN AlignmentMask;
+ UINTN Length;
+
+ if (Pages == 0) {
+ return NULL;
+ }
+
+ //
+ // Make sure alignment is a power of two
+ //
+ if ((Alignment & (Alignment - 1)) != 0) {
+ DEBUG ((DEBUG_ERROR, "HostAllocateAlignedPagesBelowAddress: Alignment is not a power of two\n"));
+ return NULL;
+ }
+
+ //
+ // Make sure alignment is smaller than the largest supported virtual address
+ //
+ if (Alignment > MAX_UINTN) {
+ DEBUG ((DEBUG_ERROR, "HostAllocateAlignedPagesBelowAddress: Alignment > MAX_UINTN\n"));
+ return NULL;
+ }
+
+ //
+ // Make sure alignment is at least 4KB
+ //
+ Alignment = MAX (Alignment, SIZE_4KB);
+
+ //
+ // Initialize local page head structure
+ //
+ PageHead.Signature = PAGE_HEAD_BELOW_ADDRESS_PRIVATE_SIGNATURE;
+ PageHead.AlignedPages = Pages;
+ PageHead.TotalPages = Pages + 2 * EFI_SIZE_TO_PAGES ((UINTN)Alignment);
+
+ //
+ // Limit maximum address to the largest supported virtual address
+ //
+ MaximumAddress = MIN (MaximumAddress, MAX_UINTN);
+
+ //
+ // Make sure total page allocation fits below maximum address
+ //
+ if (PageHead.TotalPages >= EFI_SIZE_TO_PAGES (MaximumAddress)) {
+ DEBUG ((DEBUG_ERROR, "HostAllocateAlignedPagesBelowAddress: TotalPages >= MaximumAddress\n"));
+ return NULL;
+ }
+
+ //
+ // Determine the length of the allocation in bytes
+ //
+ Length = EFI_PAGES_TO_SIZE (PageHead.TotalPages);
+
+ //
+ // Reduce maximum address by the total allocation length
+ //
+ MaximumAddress -= Length;
+
+ //
+ // Allocate buffer large enough to support aligned page request
+ //
+ PageHead.AllocatedBuffer = HostAllocateBufferInRange (
+ (UINTN)MaximumAddress,
+ Length
+ );
+ if (PageHead.AllocatedBuffer == NULL) {
+ DEBUG ((DEBUG_ERROR, "HostAllocateAlignedPagesBelowAddress: HostAllocateBufferInRange failed\n"));
+ return NULL;
+ }
+
+ DEBUG_CLEAR_MEMORY (PageHead.AllocatedBuffer, Length);
+
+ AlignmentMask = ((UINTN)Alignment - 1);
+ PageHead.AlignedBuffer = (VOID *)(((UINTN)PageHead.AllocatedBuffer + AlignmentMask) & ~AlignmentMask);
+ if ((UINTN)PageHead.AlignedBuffer - (UINTN)PageHead.AllocatedBuffer < sizeof (PAGE_HEAD_BELOW_ADDRESS)) {
+ PageHead.AlignedBuffer = (VOID *)((UINTN)PageHead.AlignedBuffer + (UINTN)Alignment);
+ }
+
+ PageHeadPtr = (PAGE_HEAD_BELOW_ADDRESS *)((UINTN)PageHead.AlignedBuffer) - 1;
+ memcpy (PageHeadPtr, &PageHead, sizeof (PageHead));
+
+ return PageHead.AlignedBuffer;
+}
+
+/**
+ Frees one or more 4KB pages that were previously allocated with
+ HostAllocateAlignedPagesBelowAddress().
+
+ Frees the number of 4KB pages specified by Pages from the buffer specified by
+ Buffer. Buffer must have been allocated with HostAllocateAlignedPagesBelowAddress().
+ If it is not possible to free allocated pages, then this function will perform
+ no actions.
+
+ If Buffer was not allocated with HostAllocateAlignedPagesBelowAddress(), then
+ ASSERT(). If Pages is zero, then ASSERT().
+
+ @param[in] Buffer The pointer to the buffer of pages to free.
+ @param[in] Pages The number of 4 KB pages to free.
+**/
+VOID
+EFIAPI
+HostFreeAlignedPagesBelowAddress (
+ IN VOID *Buffer,
+ IN UINTN Pages
+ )
+{
+ PAGE_HEAD_BELOW_ADDRESS *PageHeadPtr;
+ VOID *AllocatedBuffer;
+ UINTN Length;
+
+ ASSERT (Buffer != NULL);
+
+ PageHeadPtr = ((PAGE_HEAD_BELOW_ADDRESS *)Buffer) - 1;
+
+ ASSERT (PageHeadPtr != NULL);
+ ASSERT (PageHeadPtr->Signature == PAGE_HEAD_BELOW_ADDRESS_PRIVATE_SIGNATURE);
+ ASSERT (PageHeadPtr->AlignedPages == Pages);
+ ASSERT (PageHeadPtr->AllocatedBuffer != NULL);
+
+ AllocatedBuffer = PageHeadPtr->AllocatedBuffer;
+ Length = EFI_PAGES_TO_SIZE (PageHeadPtr->TotalPages);
+
+ DEBUG_CLEAR_MEMORY (AllocatedBuffer, Length);
+
+ HostFreeBufferInRange (AllocatedBuffer, Length);
+}
diff --git a/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf index 44ec3fd517..1443600013 100644 --- a/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf +++ b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf @@ -16,12 +16,16 @@ MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
LIBRARY_CLASS = MemoryAllocationLib|HOST_APPLICATION
+ LIBRARY_CLASS = HostMemoryAllocationBelowAddressLib|HOST_APPLICATION
[Sources]
MemoryAllocationLibPosix.c
+ AllocateBelowAddress.c
+ WinInclude.h
[Packages]
MdePkg/MdePkg.dec
+ UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
[LibraryClasses]
- BaseLib
+ DebugLib
diff --git a/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/WinInclude.h b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/WinInclude.h new file mode 100644 index 0000000000..cdde46c868 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/WinInclude.h @@ -0,0 +1,28 @@ +/** @file
+ Include windows.h addressing conflicts with forced include of Base.h
+
+ Copyright (c) 2024, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef WIN_INCLUDE_H_
+#define WIN_INCLUDE_H_
+
+#define GUID _WINNT_DUP_GUID_____
+#define _LIST_ENTRY _WINNT_DUP_LIST_ENTRY_FORWARD
+#define LIST_ENTRY _WINNT_DUP_LIST_ENTRY
+#undef VOID
+
+#pragma warning (push)
+#pragma warning (disable : 4668)
+
+#include <windows.h>
+
+#pragma warning (pop)
+
+#undef GUID
+#undef _LIST_ENTRY
+#undef LIST_ENTRY
+#define VOID void
+
+#endif
diff --git a/UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/SampleGoogleTest.cpp b/UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/SampleGoogleTest.cpp index 9164f66da3..c13c66f97a 100644 --- a/UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/SampleGoogleTest.cpp +++ b/UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/SampleGoogleTest.cpp @@ -13,6 +13,7 @@ extern "C" { #include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
+ #include <Library/HostMemoryAllocationBelowAddressLib.h>
}
/**
@@ -433,6 +434,215 @@ TEST (SanitizerTests, DivideByZeroDeathTest) { EXPECT_DEATH (DivideWithNoParameterChecking (10, 0), "ERROR: AddressSanitizer: ");
}
+/**
+ Sample unit test that allocates and frees buffers below 4GB
+**/
+TEST (MemoryAllocationTests, Below4GB) {
+ VOID *Buffer1;
+ VOID *Buffer2;
+ UINT8 EmptyBuffer[0x100];
+
+ //
+ // Length 0 always fails
+ //
+ Buffer1 = HostAllocatePoolBelowAddress (BASE_4GB - 1, 0);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Length == Maximum Address always fails
+ //
+ Buffer1 = HostAllocatePoolBelowAddress (BASE_4GB - 1, SIZE_4GB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Length > Maximum Address always fails
+ //
+ Buffer1 = HostAllocatePoolBelowAddress (BASE_4GB - 1, SIZE_8GB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Maximum Address 0 always fails
+ //
+ Buffer1 = HostAllocatePoolBelowAddress (0, SIZE_4KB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Maximum Address < 64KB always fails
+ //
+ Buffer1 = HostAllocatePoolBelowAddress (BASE_64KB - 1, SIZE_4KB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Not enough memory available always fails
+ //
+ Buffer1 = HostAllocatePoolBelowAddress (BASE_128KB - 1, SIZE_64KB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Allocation of 4KB buffer below 4GB must succeed
+ //
+ Buffer1 = HostAllocatePoolBelowAddress (BASE_4GB - 1, SIZE_4KB);
+ ASSERT_NE (Buffer1, (VOID *)NULL);
+ ASSERT_LT ((UINTN)Buffer1, BASE_4GB);
+
+ //
+ // Allocated buffer must support read and write
+ //
+ *(UINT8 *)Buffer1 = 0x5A;
+ ASSERT_EQ (*(UINT8 *)Buffer1, 0x5A);
+
+ //
+ // Allocation of 1MB buffer below 4GB must succeed
+ //
+ Buffer2 = HostAllocatePoolBelowAddress (BASE_4GB - 1, SIZE_1MB);
+ ASSERT_NE (Buffer2, (VOID *)NULL);
+ ASSERT_LT ((UINTN)Buffer2, BASE_4GB);
+
+ //
+ // Allocated buffer must support read and write
+ //
+ *(UINT8 *)Buffer2 = 0x5A;
+ ASSERT_EQ (*(UINT8 *)Buffer2, 0x5A);
+
+ //
+ // Allocations must return different values
+ //
+ ASSERT_NE (Buffer1, Buffer2);
+
+ //
+ // Free buffers below 4GB must not ASSERT
+ //
+ HostFreePoolBelowAddress (Buffer1);
+ HostFreePoolBelowAddress (Buffer2);
+
+ //
+ // Expect ASSERT() tests
+ //
+ EXPECT_ANY_THROW (HostFreePoolBelowAddress (NULL));
+ EXPECT_ANY_THROW (HostFreePoolBelowAddress (EmptyBuffer + 0x80));
+ Buffer1 = AllocatePool (0x100);
+ EXPECT_ANY_THROW (HostFreePoolBelowAddress ((UINT8 *)Buffer1 + 0x80));
+ FreePool (Buffer1);
+}
+
+/**
+ Sample unit test that allocates and frees aligned pages below 4GB
+**/
+TEST (MemoryAllocationTests, AlignedBelow4GB) {
+ VOID *Buffer1;
+ VOID *Buffer2;
+ UINT8 EmptyBuffer[0x100];
+
+ //
+ // Pages 0 always fails
+ //
+ Buffer1 = HostAllocateAlignedPagesBelowAddress (BASE_4GB - 1, 0, SIZE_4KB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Alignment not a power of 2 always fails
+ //
+ Buffer1 = HostAllocateAlignedPagesBelowAddress (BASE_4GB - 1, SIZE_4KB, 5);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Alignment not a power of 2 always fails
+ //
+ Buffer1 = HostAllocateAlignedPagesBelowAddress (BASE_4GB - 1, SIZE_4KB, SIZE_16KB + 1);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Alignment larger than largest supported virtual address always fails
+ // Only applies to 32-bit architectures
+ //
+ if (sizeof (UINTN) == sizeof (UINT32)) {
+ Buffer1 = HostAllocateAlignedPagesBelowAddress (BASE_4GB - 1, SIZE_4KB, SIZE_4GB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+ }
+
+ //
+ // Length == Maximum Address always fails
+ //
+ Buffer1 = HostAllocateAlignedPagesBelowAddress (BASE_4GB - 1, EFI_SIZE_TO_PAGES (SIZE_4GB), SIZE_4KB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Length > Maximum Address always fails
+ //
+ Buffer1 = HostAllocateAlignedPagesBelowAddress (BASE_4GB - 1, EFI_SIZE_TO_PAGES (SIZE_8GB), SIZE_4KB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Alignment >= Maximum Address always fails
+ //
+ Buffer1 = HostAllocateAlignedPagesBelowAddress (BASE_4GB - 1, EFI_SIZE_TO_PAGES (SIZE_4GB), SIZE_4GB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Maximum Address 0 always fails
+ //
+ Buffer1 = HostAllocateAlignedPagesBelowAddress (0, EFI_SIZE_TO_PAGES (SIZE_4KB), SIZE_4KB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Maximum Address <= 64KB always fails
+ //
+ Buffer1 = HostAllocateAlignedPagesBelowAddress (BASE_64KB - 1, EFI_SIZE_TO_PAGES (SIZE_4KB), SIZE_4KB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Not enough memory available always fails
+ //
+ Buffer1 = HostAllocateAlignedPagesBelowAddress (BASE_128KB - 1, EFI_SIZE_TO_PAGES (SIZE_64KB), SIZE_4KB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Allocation of 4KB buffer below 4GB must succeed
+ //
+ Buffer1 = HostAllocateAlignedPagesBelowAddress (BASE_4GB - 1, EFI_SIZE_TO_PAGES (SIZE_4KB), SIZE_4KB);
+ ASSERT_NE (Buffer1, (VOID *)NULL);
+ ASSERT_LT ((UINTN)Buffer1, BASE_4GB);
+
+ //
+ // Allocated buffer must support read and write
+ //
+ *(UINT8 *)Buffer1 = 0x5A;
+ ASSERT_EQ (*(UINT8 *)Buffer1, 0x5A);
+
+ //
+ // Allocation of 1MB buffer below 4GB must succeed
+ //
+ Buffer2 = HostAllocateAlignedPagesBelowAddress (BASE_4GB - 1, EFI_SIZE_TO_PAGES (SIZE_1MB), SIZE_1MB);
+ ASSERT_NE (Buffer2, (VOID *)NULL);
+ ASSERT_LT ((UINTN)Buffer2, BASE_4GB);
+
+ //
+ // Allocated buffer must support read and write
+ //
+ *(UINT8 *)Buffer2 = 0x5A;
+ ASSERT_EQ (*(UINT8 *)Buffer2, 0x5A);
+
+ //
+ // Allocations must return different values
+ //
+ ASSERT_NE (Buffer1, Buffer2);
+
+ //
+ // Free buffers below 4GB must not ASSERT
+ //
+ HostFreeAlignedPagesBelowAddress (Buffer1, EFI_SIZE_TO_PAGES (SIZE_4KB));
+ HostFreeAlignedPagesBelowAddress (Buffer2, EFI_SIZE_TO_PAGES (SIZE_1MB));
+
+ //
+ // Expect ASSERT() tests
+ //
+ EXPECT_ANY_THROW (HostFreeAlignedPagesBelowAddress (NULL, 0));
+ EXPECT_ANY_THROW (HostFreeAlignedPagesBelowAddress (EmptyBuffer + 0x80, 1));
+ Buffer1 = AllocatePool (0x100);
+ EXPECT_ANY_THROW (HostFreeAlignedPagesBelowAddress ((UINT8 *)Buffer1 + 0x80, 1));
+ FreePool (Buffer1);
+}
+
int
main (
int argc,
diff --git a/UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml index cf51b21e00..970ba900b8 100644 --- a/UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml +++ b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml @@ -96,6 +96,7 @@ "Library/SubhookLib/subhook/**/*.*" # not going to spell check a submodule
],
"ExtendWords": [ # words to extend to the dictionary for this package
+ "noreplace",
"Pointee",
"gmock",
"GMOCK",
diff --git a/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec index ef0a148d48..0553af4edc 100644 --- a/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec +++ b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec @@ -39,6 +39,11 @@ SubhookLib|Include/Library/SubhookLib.h
FunctionMockLib|Include/Library/FunctionMockLib.h
+ ## @libraryclass Host only memory allocation library that supports allocating
+ # buffers below a specified address.
+ #
+ HostMemoryAllocationBelowAddressLib|Include/Library/HostMemoryAllocationBelowAddressLib.h
+
[LibraryClasses.Common.Private]
## @libraryclass Provides a unit test result report
#
diff --git a/UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc b/UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc index 1c28855878..27ed1df009 100644 --- a/UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc +++ b/UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc @@ -23,6 +23,7 @@ UnitTestLib|UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.inf
DebugLib|UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.inf
MemoryAllocationLib|UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf
+ HostMemoryAllocationBelowAddressLib|UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf
UefiBootServicesTableLib|UnitTestFrameworkPkg/Library/UnitTestUefiBootServicesTableLib/UnitTestUefiBootServicesTableLib.inf
PeiServicesTablePointerLib|UnitTestFrameworkPkg/Library/UnitTestPeiServicesTablePointerLib/UnitTestPeiServicesTablePointerLib.inf
NULL|UnitTestFrameworkPkg/Library/UnitTestDebugAssertLib/UnitTestDebugAssertLibHost.inf
|