ArmPkg/ArmMmuLib: use a pool allocation for the root table

Currently, we allocate a full page for the root translation table, even
if the configured translation only requires two entries (16 bytes) for
the root level, which happens to be the case for a 40 bit VA. Likewise,
for a 36-bit VA space, the root table only needs 16 entries of 8 bytes
each, adding up to 128 bytes.

So switch to a pool allocation for the root table if we can, but take into
account that the architecture requires it to be naturally aligned to its
size, i.e., a 64 byte table requires 64 byte alignment, whereas pool
allocations in general are only guaranteed to be aligned to 8 bytes.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Leif Lindholm <leif.lindholm@linaro.org>
diff --git a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
index 1ff584e..57e789f 100644
--- a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
+++ b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
@@ -553,12 +553,14 @@
   )

 {

   VOID*                         TranslationTable;

+  VOID*                         TranslationTableBuffer;

   UINT32                        TranslationTableAttribute;

   ARM_MEMORY_REGION_DESCRIPTOR *MemoryTableEntry;

   UINT64                        MaxAddress;

   UINT64                        TopAddress;

   UINTN                         T0SZ;

   UINTN                         RootTableEntryCount;

+  UINTN                         RootTableEntrySize;

   UINT64                        TCR;

   RETURN_STATUS                 Status;

 

@@ -638,8 +640,19 @@
   // Set TCR

   ArmSetTCR (TCR);

 

-  // Allocate pages for translation table

-  TranslationTable = AllocatePages (1);

+  // Allocate pages for translation table. Pool allocations are 8 byte aligned,

+  // but we may require a higher alignment based on the size of the root table.

+  RootTableEntrySize = RootTableEntryCount * sizeof(UINT64);

+  if (RootTableEntrySize < EFI_PAGE_SIZE / 2) {

+    TranslationTableBuffer = AllocatePool (2 * RootTableEntrySize - 8);

+    //

+    // Naturally align the root table. Preserves possible NULL value

+    //

+    TranslationTable = (VOID *)((UINTN)(TranslationTableBuffer - 1) | (RootTableEntrySize - 1)) + 1;

+  } else {

+    TranslationTable = AllocatePages (1);

+    TranslationTableBuffer = NULL;

+  }

   if (TranslationTable == NULL) {

     return RETURN_OUT_OF_RESOURCES;

   }

@@ -653,10 +666,10 @@
   }

 

   if (TranslationTableSize != NULL) {

-    *TranslationTableSize = RootTableEntryCount * sizeof(UINT64);

+    *TranslationTableSize = RootTableEntrySize;

   }

 

-  ZeroMem (TranslationTable, RootTableEntryCount * sizeof(UINT64));

+  ZeroMem (TranslationTable, RootTableEntrySize);

 

   // Disable MMU and caches. ArmDisableMmu() also invalidates the TLBs

   ArmDisableMmu ();

@@ -716,7 +729,11 @@
   return RETURN_SUCCESS;

 

 FREE_TRANSLATION_TABLE:

-  FreePages (TranslationTable, 1);

+  if (TranslationTableBuffer != NULL) {

+    FreePool (TranslationTableBuffer);

+  } else {

+    FreePages (TranslationTable, 1);

+  }

   return Status;

 }