diff --git a/UnitTestFrameworkPkg/Include/Library/HostMemoryAllocationBelowAddressLib.h b/UnitTestFrameworkPkg/Include/Library/HostMemoryAllocationBelowAddressLib.h
new file mode 100644
index 0000000000000..38bba3f48fa95
--- /dev/null
+++ b/UnitTestFrameworkPkg/Include/Library/HostMemoryAllocationBelowAddressLib.h
@@ -0,0 +1,93 @@
+/** @file
+ HostMemoryAllocationBelowAddressLib class
+
+ Copyright (c) 2024, Intel Corporation. All rights reserved.
+ 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 VOID *MaximumAddress,
+ IN UINTN Length
+ );
+
+/**
+ Free memory allocated with AllocateMemoryHostAllocatePoolBelowAddress().
+
+ @param[in] Address Pointer to buffer previously allocated with
+ HostAllocatePoolBelowAddress().
+
+ @retval TRUE The buffer was freed.
+ @retval FALSE The buffer could not be freed..
+**/
+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 VOID *MaximumAddress,
+ IN UINTN Pages,
+ IN UINTN 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 0000000000000..bb00000de0388
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/AllocateBelowAddress.c
@@ -0,0 +1,382 @@
+/** @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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#if defined (_WIN32) || defined (_WIN64)
+ #include "WinInclude.h"
+#elif defined (__linux__)
+ #include
+ #include
+ #include
+ #include
+#else
+ #error Unsupported target
+#endif
+
+#include
+#include
+
+///
+/// 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_32 ('P', 'A', 'B', 'E')
+
+///
+/// Structure placed immediately before an aligned allocation to store the
+/// information required to free the entire allocated buffer.
+///
+typedef struct {
+ UINT32 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_32 ('P', 'H', 'D', 'B')
+
+///
+/// Structure placed immediately before an pool allocation to store the
+/// information required to free the entire allocated buffer.
+///
+typedef struct {
+ UINT32 Signature;
+ UINT32 TotalSize;
+} POOL_HEAD_BELOW_ADDRESS;
+
+//
+// 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] MinimumAddress The address above which the memory allocation must
+ be performed.
+ @param[in] Length The size, in bytes, of the memory allocation.
+ @param[in] PageSize The page size to which the memory allocation must
+ be aligned.
+
+ @retval !NULL Pointer to the allocated memory.
+ @retval NULL The memory allocation failed.
+**/
+static
+VOID *
+HostAllocateBufferInRange (
+ UINTN MaximumAddress,
+ UINTN MinimumAddress,
+ UINTN Length,
+ UINTN PageSize
+ )
+{
+ UINTN Address;
+ VOID *AllocatedAddress;
+
+ for (Address = MaximumAddress; Address >= MinimumAddress; Address -= PageSize) {
+ #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 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 VOID *MaximumAddress,
+ IN UINTN Length
+ )
+{
+ VOID *AllocatedAddress;
+ POOL_HEAD_BELOW_ADDRESS *PoolHead;
+
+ if (mPageSize == 0) {
+ mPageSize = HostGetPageSize ();
+ if (mPageSize == 0) {
+ return NULL;
+ }
+ }
+
+ if ((Length == 0) || (Length >= (UINTN)MaximumAddress)) {
+ return NULL;
+ }
+
+ Length += sizeof (POOL_HEAD_BELOW_ADDRESS);
+
+ AllocatedAddress = HostAllocateBufferInRange (
+ ((UINTN)MaximumAddress - Length) & ~(mPageSize - 1),
+ BASE_64KB,
+ Length,
+ mPageSize
+ );
+ if (AllocatedAddress == NULL) {
+ return NULL;
+ }
+
+ DEBUG_CLEAR_MEMORY (AllocatedAddress, Length);
+ PoolHead = (POOL_HEAD_BELOW_ADDRESS *)AllocatedAddress;
+ PoolHead->Signature = POOL_HEAD_BELOW_ADDRESS_PRIVATE_SIGNATURE;
+ PoolHead->TotalSize = (UINT32)(Length);
+ return (VOID *)(PoolHead + 1);
+}
+
+/**
+ Free memory allocated with HostAllocatePoolBelowAddress().
+
+ @param[in] Buffer Pointer to buffer previously allocated with
+ HostAllocatePoolBelowAddress().
+
+ @retval TRUE The buffer was freed.
+ @retval FALSE The buffer could not be freed..
+**/
+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));
+
+ Length = 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 VOID *MaximumAddress,
+ IN UINTN Pages,
+ IN UINTN Alignment
+ )
+{
+ PAGE_HEAD_BELOW_ADDRESS PageHead;
+ PAGE_HEAD_BELOW_ADDRESS *PageHeadPtr;
+ UINTN AlignmentMask;
+
+ if (mPageSize == 0) {
+ mPageSize = HostGetPageSize ();
+ if (mPageSize == 0) {
+ return NULL;
+ }
+ }
+
+ if ((Pages == 0) || (EFI_PAGES_TO_SIZE (Pages) >= (UINTN)MaximumAddress)) {
+ return NULL;
+ }
+
+ ASSERT ((Alignment & (Alignment - 1)) == 0);
+
+ if (Alignment < SIZE_4KB) {
+ Alignment = SIZE_4KB;
+ }
+
+ AlignmentMask = Alignment - 1;
+
+ //
+ // We need reserve Alignment pages for PAGE_HEAD_BELOW_ADDRESS, as meta data.
+ //
+ PageHead.Signature = PAGE_HEAD_BELOW_ADDRESS_PRIVATE_SIGNATURE;
+ PageHead.TotalPages = Pages + EFI_SIZE_TO_PAGES (Alignment) * 2;
+ PageHead.AlignedPages = Pages;
+ PageHead.AllocatedBuffer = HostAllocateBufferInRange (
+ ((UINTN)MaximumAddress - EFI_PAGES_TO_SIZE (PageHead.TotalPages)) & ~(mPageSize - 1),
+ BASE_64KB,
+ EFI_PAGES_TO_SIZE (PageHead.TotalPages),
+ mPageSize
+ );
+ if (PageHead.AllocatedBuffer == NULL) {
+ return NULL;
+ }
+
+ DEBUG_CLEAR_MEMORY (PageHead.AllocatedBuffer, EFI_PAGES_TO_SIZE (PageHead.TotalPages));
+
+ 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 + Alignment);
+ }
+
+ PageHeadPtr = (VOID *)((UINTN)PageHead.AlignedBuffer - sizeof (PAGE_HEAD_BELOW_ADDRESS));
+ memcpy (PageHeadPtr, &PageHead, sizeof (PAGE_HEAD_BELOW_ADDRESS));
+
+ 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 44ec3fd517222..1443600013e22 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 0000000000000..1197018282c40
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/WinInclude.h
@@ -0,0 +1,23 @@
+/** @file
+ Include windows.h addressing conflicts with forced include of Base.h
+
+ Copyright (c) 2024, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#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
+
+#pragma warning (pop)
+
+#undef GUID
+#undef _LIST_ENTRY
+#undef LIST_ENTRY
+#define VOID void
diff --git a/UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/SampleGoogleTest.cpp b/UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/SampleGoogleTest.cpp
index f3643dc864dea..d435ba411e9fc 100644
--- a/UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/SampleGoogleTest.cpp
+++ b/UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/SampleGoogleTest.cpp
@@ -13,6 +13,7 @@ extern "C" {
#include
#include
#include
+ #include
}
/**
@@ -433,6 +434,176 @@ 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 ((VOID *)BASE_4GB, 0);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Length == Maximum Address always fails
+ //
+ Buffer1 = HostAllocatePoolBelowAddress ((VOID *)BASE_4GB, SIZE_4GB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Length > Maximum Address always fails
+ //
+ Buffer1 = HostAllocatePoolBelowAddress ((VOID *)BASE_4GB, SIZE_8GB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Maximum Address <= 64KB always fails
+ //
+ Buffer1 = HostAllocatePoolBelowAddress ((VOID *)NULL, SIZE_4KB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Maximum Address <= 64KB always fails
+ //
+ Buffer1 = HostAllocatePoolBelowAddress ((VOID *)BASE_64KB, SIZE_4KB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Allocation of 4KB buffer below 4GB must succeed
+ //
+ Buffer1 = HostAllocatePoolBelowAddress ((VOID *)BASE_4GB, 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 ((VOID *)BASE_4GB, 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 (Buffer1));
+ 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 ((VOID *)BASE_4GB, 0, SIZE_4KB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Length == Maximum Address always fails
+ //
+ Buffer1 = HostAllocateAlignedPagesBelowAddress ((VOID *)BASE_4GB, EFI_SIZE_TO_PAGES (SIZE_4GB), SIZE_4KB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Length > Maximum Address always fails
+ //
+ Buffer1 = HostAllocateAlignedPagesBelowAddress ((VOID *)BASE_4GB, EFI_SIZE_TO_PAGES (SIZE_8GB), SIZE_4KB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Maximum Address <= 64KB always fails
+ //
+ Buffer1 = HostAllocateAlignedPagesBelowAddress ((VOID *)NULL, EFI_SIZE_TO_PAGES (SIZE_4KB), SIZE_4KB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Maximum Address <= 64KB always fails
+ //
+ Buffer1 = HostAllocateAlignedPagesBelowAddress ((VOID *)BASE_64KB, EFI_SIZE_TO_PAGES (SIZE_4KB), SIZE_4KB);
+ ASSERT_EQ (Buffer1, (VOID *)NULL);
+
+ //
+ // Allocation of 4KB buffer below 4GB must succeed
+ //
+ Buffer1 = HostAllocateAlignedPagesBelowAddress ((VOID *)BASE_4GB, 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 ((VOID *)BASE_4GB, 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 cf51b21e00d45..970ba900b820a 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 ef0a148d48764..0553af4edc370 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 f7cd035adce3e..2e6b0d28c69ac 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