]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c
UefiCpuPkg/PiSmmCpuDxeSmm: Fix memory protection crash
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / SmmCpuMemoryManagement.c
index 588aa270d895375dbc872d5a749523542a9f10e5..3ad5256f1e03d64b4a93bfe7eee74de80c5173c1 100644 (file)
@@ -1,6 +1,6 @@
 /** @file\r
 \r
-Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>\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
@@ -16,6 +16,13 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 #define NEXT_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \\r
   ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) + (Size)))\r
 \r
+#define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \\r
+  ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size)))\r
+\r
+EFI_MEMORY_DESCRIPTOR *mUefiMemoryMap;\r
+UINTN                 mUefiMemoryMapSize;\r
+UINTN                 mUefiDescriptorSize;\r
+\r
 PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {\r
   {Page4K,  SIZE_4KB, PAGING_4K_ADDRESS_MASK_64},\r
   {Page2M,  SIZE_2MB, PAGING_2M_ADDRESS_MASK_64},\r
@@ -112,7 +119,7 @@ GetPageTableEntry (
       return NULL;\r
     }\r
 \r
-    L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & PAGING_4K_ADDRESS_MASK_64);\r
+    L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
   } else {\r
     L3PageTable = (UINT64 *)GetPageTableBase ();\r
   }\r
@@ -126,7 +133,7 @@ GetPageTableEntry (
     return &L3PageTable[Index3];\r
   }\r
 \r
-  L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & PAGING_4K_ADDRESS_MASK_64);\r
+  L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
   if (L2PageTable[Index2] == 0) {\r
     *PageAttribute = PageNone;\r
     return NULL;\r
@@ -138,7 +145,7 @@ GetPageTableEntry (
   }\r
 \r
   // 4k\r
-  L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & PAGING_4K_ADDRESS_MASK_64);\r
+  L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
   if ((L1PageTable[Index1] == 0) && (Address != 0)) {\r
     *PageAttribute = PageNone;\r
     return NULL;\r
@@ -297,9 +304,9 @@ SplitPage (
       }\r
       BaseAddress = *PageEntry & PAGING_2M_ADDRESS_MASK_64;\r
       for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
-        NewPageEntry[Index] = BaseAddress + SIZE_4KB * Index + ((*PageEntry) & PAGE_PROGATE_BITS);\r
+        NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | mAddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
       }\r
-      (*PageEntry) = (UINT64)(UINTN)NewPageEntry + PAGE_ATTRIBUTE_BITS;\r
+      (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
       return RETURN_SUCCESS;\r
     } else {\r
       return RETURN_UNSUPPORTED;\r
@@ -318,9 +325,9 @@ SplitPage (
       }\r
       BaseAddress = *PageEntry & PAGING_1G_ADDRESS_MASK_64;\r
       for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
-        NewPageEntry[Index] = BaseAddress + SIZE_2MB * Index + IA32_PG_PS + ((*PageEntry) & PAGE_PROGATE_BITS);\r
+        NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | mAddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS);\r
       }\r
-      (*PageEntry) = (UINT64)(UINTN)NewPageEntry + PAGE_ATTRIBUTE_BITS;\r
+      (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
       return RETURN_SUCCESS;\r
     } else {\r
       return RETURN_UNSUPPORTED;\r
@@ -373,6 +380,7 @@ ConvertMemoryPageAttributes (
   PAGE_ATTRIBUTE                    SplitAttribute;\r
   RETURN_STATUS                     Status;\r
   BOOLEAN                           IsEntryModified;\r
+  EFI_PHYSICAL_ADDRESS              MaximumSupportMemAddress;\r
 \r
   ASSERT (Attributes != 0);\r
   ASSERT ((Attributes & ~(EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP)) == 0);\r
@@ -384,6 +392,17 @@ ConvertMemoryPageAttributes (
     return RETURN_INVALID_PARAMETER;\r
   }\r
 \r
+  MaximumSupportMemAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, mPhysicalAddressBits) - 1);\r
+  if (BaseAddress > MaximumSupportMemAddress) {\r
+    return RETURN_UNSUPPORTED;\r
+  }\r
+  if (Length > MaximumSupportMemAddress) {\r
+    return RETURN_UNSUPPORTED;\r
+  }\r
+  if ((Length != 0) && (BaseAddress > MaximumSupportMemAddress - (Length - 1))) {\r
+    return RETURN_UNSUPPORTED;\r
+  }\r
+\r
 //  DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));\r
 \r
   if (IsSplitted != NULL) {\r
@@ -823,3 +842,281 @@ SetMemMapAttributes (
 \r
   return ;\r
 }\r
+\r
+/**\r
+  Sort memory map entries based upon PhysicalStart, from low to high.\r
+\r
+  @param  MemoryMap              A pointer to the buffer in which firmware places\r
+                                 the current memory map.\r
+  @param  MemoryMapSize          Size, in bytes, of the MemoryMap buffer.\r
+  @param  DescriptorSize         Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.\r
+**/\r
+STATIC\r
+VOID\r
+SortMemoryMap (\r
+  IN OUT EFI_MEMORY_DESCRIPTOR  *MemoryMap,\r
+  IN UINTN                      MemoryMapSize,\r
+  IN UINTN                      DescriptorSize\r
+  )\r
+{\r
+  EFI_MEMORY_DESCRIPTOR       *MemoryMapEntry;\r
+  EFI_MEMORY_DESCRIPTOR       *NextMemoryMapEntry;\r
+  EFI_MEMORY_DESCRIPTOR       *MemoryMapEnd;\r
+  EFI_MEMORY_DESCRIPTOR       TempMemoryMap;\r
+\r
+  MemoryMapEntry = MemoryMap;\r
+  NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
+  MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);\r
+  while (MemoryMapEntry < MemoryMapEnd) {\r
+    while (NextMemoryMapEntry < MemoryMapEnd) {\r
+      if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) {\r
+        CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));\r
+        CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));\r
+        CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof(EFI_MEMORY_DESCRIPTOR));\r
+      }\r
+\r
+      NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);\r
+    }\r
+\r
+    MemoryMapEntry      = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
+    NextMemoryMapEntry  = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
+  }\r
+}\r
+\r
+/**\r
+  Return if a UEFI memory page should be marked as not present in SMM page table.\r
+  If the memory map entries type is\r
+  EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,\r
+  EfiUnusableMemory, EfiACPIReclaimMemory, return TRUE.\r
+  Or return FALSE.\r
+\r
+  @param[in]  MemoryMap              A pointer to the memory descriptor.\r
+\r
+  @return TRUE  The memory described will be marked as not present in SMM page table.\r
+  @return FALSE The memory described will not be marked as not present in SMM page table.\r
+**/\r
+BOOLEAN\r
+IsUefiPageNotPresent (\r
+  IN EFI_MEMORY_DESCRIPTOR  *MemoryMap\r
+  )\r
+{\r
+  switch (MemoryMap->Type) {\r
+  case EfiLoaderCode:\r
+  case EfiLoaderData:\r
+  case EfiBootServicesCode:\r
+  case EfiBootServicesData:\r
+  case EfiConventionalMemory:\r
+  case EfiUnusableMemory:\r
+  case EfiACPIReclaimMemory:\r
+    return TRUE;\r
+  default:\r
+    return FALSE;\r
+  }\r
+}\r
+\r
+/**\r
+  Merge continous memory map entries whose type is\r
+  EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,\r
+  EfiUnusableMemory, EfiACPIReclaimMemory, because the memory described by\r
+  these entries will be set as NOT present in SMM page table.\r
+\r
+  @param[in, out]  MemoryMap              A pointer to the buffer in which firmware places\r
+                                          the current memory map.\r
+  @param[in, out]  MemoryMapSize          A pointer to the size, in bytes, of the\r
+                                          MemoryMap buffer. On input, this is the size of\r
+                                          the current memory map.  On output,\r
+                                          it is the size of new memory map after merge.\r
+  @param[in]       DescriptorSize         Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.\r
+**/\r
+STATIC\r
+VOID\r
+MergeMemoryMapForNotPresentEntry (\r
+  IN OUT EFI_MEMORY_DESCRIPTOR  *MemoryMap,\r
+  IN OUT UINTN                  *MemoryMapSize,\r
+  IN UINTN                      DescriptorSize\r
+  )\r
+{\r
+  EFI_MEMORY_DESCRIPTOR       *MemoryMapEntry;\r
+  EFI_MEMORY_DESCRIPTOR       *MemoryMapEnd;\r
+  UINT64                      MemoryBlockLength;\r
+  EFI_MEMORY_DESCRIPTOR       *NewMemoryMapEntry;\r
+  EFI_MEMORY_DESCRIPTOR       *NextMemoryMapEntry;\r
+\r
+  MemoryMapEntry = MemoryMap;\r
+  NewMemoryMapEntry = MemoryMap;\r
+  MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + *MemoryMapSize);\r
+  while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {\r
+    CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));\r
+    NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
+\r
+    do {\r
+      MemoryBlockLength = (UINT64) (EFI_PAGES_TO_SIZE((UINTN)MemoryMapEntry->NumberOfPages));\r
+      if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) &&\r
+          IsUefiPageNotPresent(MemoryMapEntry) && IsUefiPageNotPresent(NextMemoryMapEntry) &&\r
+          ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart)) {\r
+        MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;\r
+        if (NewMemoryMapEntry != MemoryMapEntry) {\r
+          NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;\r
+        }\r
+\r
+        NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);\r
+        continue;\r
+      } else {\r
+        MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);\r
+        break;\r
+      }\r
+    } while (TRUE);\r
+\r
+    MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
+    NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize);\r
+  }\r
+\r
+  *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap;\r
+\r
+  return ;\r
+}\r
+\r
+/**\r
+  This function caches the UEFI memory map information.\r
+**/\r
+VOID\r
+GetUefiMemoryMap (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  UINTN                 MapKey;\r
+  UINT32                DescriptorVersion;\r
+  EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
+  UINTN                 UefiMemoryMapSize;\r
+\r
+  DEBUG ((DEBUG_INFO, "GetUefiMemoryMap\n"));\r
+\r
+  UefiMemoryMapSize = 0;\r
+  MemoryMap = NULL;\r
+  Status = gBS->GetMemoryMap (\r
+                  &UefiMemoryMapSize,\r
+                  MemoryMap,\r
+                  &MapKey,\r
+                  &mUefiDescriptorSize,\r
+                  &DescriptorVersion\r
+                  );\r
+  ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
+\r
+  do {\r
+    Status = gBS->AllocatePool (EfiBootServicesData, UefiMemoryMapSize, (VOID **)&MemoryMap);\r
+    ASSERT (MemoryMap != NULL);\r
+    if (MemoryMap == NULL) {\r
+      return ;\r
+    }\r
+\r
+    Status = gBS->GetMemoryMap (\r
+                    &UefiMemoryMapSize,\r
+                    MemoryMap,\r
+                    &MapKey,\r
+                    &mUefiDescriptorSize,\r
+                    &DescriptorVersion\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      gBS->FreePool (MemoryMap);\r
+      MemoryMap = NULL;\r
+    }\r
+  } while (Status == EFI_BUFFER_TOO_SMALL);\r
+\r
+  if (MemoryMap == NULL) {\r
+    return ;\r
+  }\r
+\r
+  SortMemoryMap (MemoryMap, UefiMemoryMapSize, mUefiDescriptorSize);\r
+  MergeMemoryMapForNotPresentEntry (MemoryMap, &UefiMemoryMapSize, mUefiDescriptorSize);\r
+\r
+  mUefiMemoryMapSize = UefiMemoryMapSize;\r
+  mUefiMemoryMap = AllocateCopyPool (UefiMemoryMapSize, MemoryMap);\r
+  ASSERT (mUefiMemoryMap != NULL);\r
+\r
+  gBS->FreePool (MemoryMap);\r
+}\r
+\r
+/**\r
+  This function sets UEFI memory attribute according to UEFI memory map.\r
+\r
+  The normal memory region is marked as not present, such as\r
+  EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,\r
+  EfiUnusableMemory, EfiACPIReclaimMemory.\r
+**/\r
+VOID\r
+SetUefiMemMapAttributes (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
+  UINTN                 MemoryMapEntryCount;\r
+  UINTN                 Index;\r
+\r
+  DEBUG ((DEBUG_INFO, "SetUefiMemMapAttributes\n"));\r
+\r
+  if (mUefiMemoryMap == NULL) {\r
+    DEBUG ((DEBUG_INFO, "UefiMemoryMap - NULL\n"));\r
+    return ;\r
+  }\r
+\r
+  MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;\r
+  MemoryMap = mUefiMemoryMap;\r
+  for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
+    if (IsUefiPageNotPresent(MemoryMap)) {\r
+      Status = SmmSetMemoryAttributes (\r
+                 MemoryMap->PhysicalStart,\r
+                 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),\r
+                 EFI_MEMORY_RP\r
+                 );\r
+      DEBUG ((\r
+        DEBUG_INFO,\r
+        "UefiMemory protection: 0x%lx - 0x%lx %r\n",\r
+        MemoryMap->PhysicalStart,\r
+        MemoryMap->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),\r
+        Status\r
+        ));\r
+    }\r
+    MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mUefiDescriptorSize);\r
+  }\r
+\r
+  //\r
+  // Do free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress().\r
+  //\r
+}\r
+\r
+/**\r
+  Return if the Address is forbidden as SMM communication buffer.\r
+\r
+  @param[in] Address the address to be checked\r
+\r
+  @return TRUE  The address is forbidden as SMM communication buffer.\r
+  @return FALSE The address is allowed as SMM communication buffer.\r
+**/\r
+BOOLEAN\r
+IsSmmCommBufferForbiddenAddress (\r
+  IN UINT64  Address\r
+  )\r
+{\r
+  EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
+  UINTN                 MemoryMapEntryCount;\r
+  UINTN                 Index;\r
+\r
+  if (mUefiMemoryMap == NULL) {\r
+    return FALSE;\r
+  }\r
+\r
+  MemoryMap = mUefiMemoryMap;\r
+  MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;\r
+  for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
+    if (IsUefiPageNotPresent (MemoryMap)) {\r
+      if ((Address >= MemoryMap->PhysicalStart) &&\r
+          (Address < MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages)) ) {\r
+        return TRUE;\r
+      }\r
+    }\r
+    MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mUefiDescriptorSize);\r
+  }\r
+  return FALSE;\r
+}\r