]> git.proxmox.com Git - mirror_edk2.git/commitdiff
ArmPkg/AArch64Mmu: disable MMU during page table manipulations
authorArd Biesheuvel <ard.biesheuvel@linaro.org>
Mon, 11 Apr 2016 13:47:24 +0000 (15:47 +0200)
committerArd Biesheuvel <ard.biesheuvel@linaro.org>
Thu, 14 Apr 2016 16:01:52 +0000 (18:01 +0200)
On ARM, manipulating live page tables is cumbersome since the architecture
mandates the use of break-before-make, i.e., replacing a block entry with
a table entry requires an intermediate step via an invalid entry, or TLB
conflicts may occur.

Since it is not generally feasible to decide in the page table manipulation
routines whether such an invalid entry will result in those routines
themselves to become unavailable, use a function that is callable with
the MMU off (i.e., a leaf function that does not access the stack) to
perform the change of a block entry into a table entry.

Note that the opposite should never occur, i.e., table entries are never
coalesced into block entries.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
ArmPkg/Include/Library/ArmLib.h
ArmPkg/Library/ArmLib/AArch64/AArch64Lib.inf
ArmPkg/Library/ArmLib/AArch64/AArch64LibConstructor.c [new file with mode: 0644]
ArmPkg/Library/ArmLib/AArch64/AArch64Mmu.c
ArmPkg/Library/ArmLib/AArch64/AArch64Support.S

index 15f610d82e1dfc5c4c2fb8c32fe517b85b12e964..1689f0072db640747f0ca0205ffb1a2009f401e4 100644 (file)
@@ -613,4 +613,10 @@ ArmClearMemoryRegionReadOnly (
   IN  UINT64                    Length\r
   );\r
 \r
+VOID\r
+ArmReplaceLiveTranslationEntry (\r
+  IN  UINT64  *Entry,\r
+  IN  UINT64  Value\r
+  );\r
+\r
 #endif // __ARM_LIB__\r
index dd585dea91fb7cebceb41138798072c3386fb26f..58684e8492f24eb576ef281ae1f3e265c2f35f21 100644 (file)
   INF_VERSION                    = 0x00010005\r
   BASE_NAME                      = AArch64Lib\r
   FILE_GUID                      = ef20ddf5-b334-47b3-94cf-52ff44c29138\r
-  MODULE_TYPE                    = DXE_DRIVER\r
+  MODULE_TYPE                    = BASE\r
   VERSION_STRING                 = 1.0\r
   LIBRARY_CLASS                  = ArmLib\r
+  CONSTRUCTOR                    = AArch64LibConstructor\r
 \r
 [Sources.AARCH64]\r
   AArch64Lib.c\r
@@ -31,6 +32,7 @@
 \r
   ../Common/AArch64/ArmLibSupport.S\r
   ../Common/ArmLib.c\r
+  AArch64LibConstructor.c\r
 \r
 [Packages]\r
   ArmPkg/ArmPkg.dec\r
@@ -38,6 +40,7 @@
 \r
 [LibraryClasses]\r
   MemoryAllocationLib\r
+  CacheMaintenanceLib\r
 \r
 [Protocols]\r
   gEfiCpuArchProtocolGuid\r
diff --git a/ArmPkg/Library/ArmLib/AArch64/AArch64LibConstructor.c b/ArmPkg/Library/ArmLib/AArch64/AArch64LibConstructor.c
new file mode 100644 (file)
index 0000000..d2d0d3c
--- /dev/null
@@ -0,0 +1,36 @@
+#/* @file\r
+#\r
+#  Copyright (c) 2016, Linaro Limited. All rights reserved.\r
+#\r
+#  This program and the accompanying materials\r
+#  are licensed and made available under the terms and conditions of the BSD License\r
+#  which accompanies this distribution.  The full text of the license may be found at\r
+#  http://opensource.org/licenses/bsd-license.php\r
+#\r
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+#*/\r
+\r
+#include <Base.h>\r
+\r
+#include <Library/ArmLib.h>\r
+#include <Library/CacheMaintenanceLib.h>\r
+\r
+RETURN_STATUS\r
+EFIAPI\r
+AArch64LibConstructor (\r
+  VOID\r
+  )\r
+{\r
+  extern UINT32 ArmReplaceLiveTranslationEntrySize;\r
+\r
+  //\r
+  // The ArmReplaceLiveTranslationEntry () helper function may be invoked\r
+  // with the MMU off so we have to ensure that it gets cleaned to the PoC\r
+  //\r
+  WriteBackDataCacheRange (ArmReplaceLiveTranslationEntry,\r
+    ArmReplaceLiveTranslationEntrySize);\r
+\r
+  return RETURN_SUCCESS;\r
+}\r
index 918957d4ff4a3a236c1bbc1ca12920bef77b6695..48ca8271849cc3c3215d0c79d9bfe0bf47a96e69 100644 (file)
@@ -167,6 +167,20 @@ GetRootTranslationTableInfo (
   }\r
 }\r
 \r
+STATIC\r
+VOID\r
+ReplaceLiveEntry (\r
+  IN  UINT64  *Entry,\r
+  IN  UINT64  Value\r
+  )\r
+{\r
+  if (!ArmMmuEnabled ()) {\r
+    *Entry = Value;\r
+  } else {\r
+    ArmReplaceLiveTranslationEntry (Entry, Value);\r
+  }\r
+}\r
+\r
 STATIC\r
 VOID\r
 LookupAddresstoRootTable (\r
@@ -330,7 +344,8 @@ GetBlockEntryListFromAddress (
         }\r
 \r
         // Fill the BlockEntry with the new TranslationTable\r
-        *BlockEntry = ((UINTN)TranslationTable & TT_ADDRESS_MASK_DESCRIPTION_TABLE) | TableAttributes | TT_TYPE_TABLE_ENTRY;\r
+        ReplaceLiveEntry (BlockEntry,\r
+          ((UINTN)TranslationTable & TT_ADDRESS_MASK_DESCRIPTION_TABLE) | TableAttributes | TT_TYPE_TABLE_ENTRY);\r
       }\r
     } else {\r
       if (IndexLevel != PageLevel) {\r
index 1a3023b794878f91ace57b24b7d0d88f377a7faf..43f7a795acecac5c1d11412cb21cf55d9b1c19a3 100644 (file)
@@ -56,6 +56,8 @@ GCC_ASM_EXPORT (ArmReadIdPfr1)
 GCC_ASM_EXPORT (ArmWriteHcr)\r
 GCC_ASM_EXPORT (ArmReadHcr)\r
 GCC_ASM_EXPORT (ArmReadCurrentEL)\r
+GCC_ASM_EXPORT (ArmReplaceLiveTranslationEntry)\r
+GCC_ASM_EXPORT (ArmReplaceLiveTranslationEntrySize)\r
 \r
 .set CTRL_M_BIT,      (1 << 0)\r
 .set CTRL_A_BIT,      (1 << 1)\r
@@ -481,4 +483,64 @@ ASM_PFX(ArmReadCurrentEL):
   mrs   x0, CurrentEL\r
   ret\r
 \r
+\r
+  .macro __replace_entry, el\r
+\r
+  // disable the MMU\r
+  mrs   x8, sctlr_el\el\r
+  bic   x9, x8, #CTRL_M_BIT\r
+  msr   sctlr_el\el, x9\r
+  isb\r
+\r
+  // write updated entry\r
+  str   x1, [x0]\r
+\r
+  // invalidate again to get rid of stale clean cachelines that may\r
+  // have been filled speculatively since the last invalidate\r
+  dmb   sy\r
+  dc    ivac, x0\r
+\r
+  // flush the TLBs\r
+  .if   \el == 1\r
+  tlbi  vmalle1\r
+  .else\r
+  tlbi  alle\el\r
+  .endif\r
+  dsb   sy\r
+\r
+  // re-enable the MMU\r
+  msr   sctlr_el\el, x8\r
+  isb\r
+  .endm\r
+\r
+//VOID\r
+//ArmReplaceLiveTranslationEntry (\r
+//  IN  UINT64  *Entry,\r
+//  IN  UINT64  Value\r
+//  )\r
+ASM_PFX(ArmReplaceLiveTranslationEntry):\r
+\r
+  // disable interrupts\r
+  mrs   x2, daif\r
+  msr   daifset, #0xf\r
+  isb\r
+\r
+  // clean and invalidate first so that we don't clobber\r
+  // adjacent entries that are dirty in the caches\r
+  dc    civac, x0\r
+  dsb   ish\r
+\r
+  EL1_OR_EL2_OR_EL3(x3)\r
+1:__replace_entry 1\r
+  b     4f\r
+2:__replace_entry 2\r
+  b     4f\r
+3:__replace_entry 3\r
+\r
+4:msr   daif, x2\r
+  ret\r
+\r
+ASM_PFX(ArmReplaceLiveTranslationEntrySize):\r
+  .long   . - ArmReplaceLiveTranslationEntry\r
+\r
 ASM_FUNCTION_REMOVE_IF_UNREFERENCED\r