+/** @file\r
+\r
+Copyright (c) 2016, 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
+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 "PiSmmCpuDxeSmm.h"\r
+\r
+#define NEXT_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \\r
+ ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) + (Size)))\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
+ {Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},\r
+};\r
+\r
+/**\r
+ Return page table base.\r
+\r
+ @return page table base.\r
+**/\r
+UINTN\r
+GetPageTableBase (\r
+ VOID\r
+ )\r
+{\r
+ return (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);\r
+}\r
+\r
+/**\r
+ Return length according to page attributes.\r
+\r
+ @param[in] PageAttributes The page attribute of the page entry.\r
+\r
+ @return The length of page entry.\r
+**/\r
+UINTN\r
+PageAttributeToLength (\r
+ IN PAGE_ATTRIBUTE PageAttribute\r
+ )\r
+{\r
+ UINTN Index;\r
+ for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {\r
+ if (PageAttribute == mPageAttributeTable[Index].Attribute) {\r
+ return (UINTN)mPageAttributeTable[Index].Length;\r
+ }\r
+ }\r
+ return 0;\r
+}\r
+\r
+/**\r
+ Return address mask according to page attributes.\r
+\r
+ @param[in] PageAttributes The page attribute of the page entry.\r
+\r
+ @return The address mask of page entry.\r
+**/\r
+UINTN\r
+PageAttributeToMask (\r
+ IN PAGE_ATTRIBUTE PageAttribute\r
+ )\r
+{\r
+ UINTN Index;\r
+ for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {\r
+ if (PageAttribute == mPageAttributeTable[Index].Attribute) {\r
+ return (UINTN)mPageAttributeTable[Index].AddressMask;\r
+ }\r
+ }\r
+ return 0;\r
+}\r
+\r
+/**\r
+ Return page table entry to match the address.\r
+\r
+ @param[in] Address The address to be checked.\r
+ @param[out] PageAttributes The page attribute of the page entry.\r
+\r
+ @return The page entry.\r
+**/\r
+VOID *\r
+GetPageTableEntry (\r
+ IN PHYSICAL_ADDRESS Address,\r
+ OUT PAGE_ATTRIBUTE *PageAttribute\r
+ )\r
+{\r
+ UINTN Index1;\r
+ UINTN Index2;\r
+ UINTN Index3;\r
+ UINTN Index4;\r
+ UINT64 *L1PageTable;\r
+ UINT64 *L2PageTable;\r
+ UINT64 *L3PageTable;\r
+ UINT64 *L4PageTable;\r
+\r
+ Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK;\r
+ Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK;\r
+ Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK;\r
+ Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK;\r
+\r
+ if (sizeof(UINTN) == sizeof(UINT64)) {\r
+ L4PageTable = (UINT64 *)GetPageTableBase ();\r
+ if (L4PageTable[Index4] == 0) {\r
+ *PageAttribute = PageNone;\r
+ return NULL;\r
+ }\r
+\r
+ L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & PAGING_4K_ADDRESS_MASK_64);\r
+ } else {\r
+ L3PageTable = (UINT64 *)GetPageTableBase ();\r
+ }\r
+ if (L3PageTable[Index3] == 0) {\r
+ *PageAttribute = PageNone;\r
+ return NULL;\r
+ }\r
+ if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {\r
+ // 1G\r
+ *PageAttribute = Page1G;\r
+ return &L3PageTable[Index3];\r
+ }\r
+\r
+ L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & PAGING_4K_ADDRESS_MASK_64);\r
+ if (L2PageTable[Index2] == 0) {\r
+ *PageAttribute = PageNone;\r
+ return NULL;\r
+ }\r
+ if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {\r
+ // 2M\r
+ *PageAttribute = Page2M;\r
+ return &L2PageTable[Index2];\r
+ }\r
+\r
+ // 4k\r
+ L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & PAGING_4K_ADDRESS_MASK_64);\r
+ if ((L1PageTable[Index1] == 0) && (Address != 0)) {\r
+ *PageAttribute = PageNone;\r
+ return NULL;\r
+ }\r
+ *PageAttribute = Page4K;\r
+ return &L1PageTable[Index1];\r
+}\r
+\r
+/**\r
+ Return memory attributes of page entry.\r
+\r
+ @param[in] PageEntry The page entry.\r
+\r
+ @return Memory attributes of page entry.\r
+**/\r
+UINT64\r
+GetAttributesFromPageEntry (\r
+ IN UINT64 *PageEntry\r
+ )\r
+{\r
+ UINT64 Attributes;\r
+ Attributes = 0;\r
+ if ((*PageEntry & IA32_PG_P) == 0) {\r
+ Attributes |= EFI_MEMORY_RP;\r
+ }\r
+ if ((*PageEntry & IA32_PG_RW) == 0) {\r
+ Attributes |= EFI_MEMORY_RO;\r
+ }\r
+ if ((*PageEntry & IA32_PG_NX) != 0) {\r
+ Attributes |= EFI_MEMORY_XP;\r
+ }\r
+ return Attributes;\r
+}\r
+\r
+/**\r
+ Modify memory attributes of page entry.\r
+\r
+ @param[in] PageEntry The page entry.\r
+ @param[in] Attributes The bit mask of attributes to modify for the memory region.\r
+ @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.\r
+ @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.\r
+**/\r
+VOID\r
+ConvertPageEntryAttribute (\r
+ IN UINT64 *PageEntry,\r
+ IN UINT64 Attributes,\r
+ IN BOOLEAN IsSet,\r
+ OUT BOOLEAN *IsModified\r
+ )\r
+{\r
+ UINT64 CurrentPageEntry;\r
+ UINT64 NewPageEntry;\r
+\r
+ CurrentPageEntry = *PageEntry;\r
+ NewPageEntry = CurrentPageEntry;\r
+ if ((Attributes & EFI_MEMORY_RP) != 0) {\r
+ if (IsSet) {\r
+ NewPageEntry &= ~(UINT64)IA32_PG_P;\r
+ } else {\r
+ NewPageEntry |= IA32_PG_P;\r
+ }\r
+ }\r
+ if ((Attributes & EFI_MEMORY_RO) != 0) {\r
+ if (IsSet) {\r
+ NewPageEntry &= ~(UINT64)IA32_PG_RW;\r
+ } else {\r
+ NewPageEntry |= IA32_PG_RW;\r
+ }\r
+ }\r
+ if ((Attributes & EFI_MEMORY_XP) != 0) {\r
+ if (IsSet) {\r
+ NewPageEntry |= IA32_PG_NX;\r
+ } else {\r
+ NewPageEntry &= ~IA32_PG_NX;\r
+ }\r
+ }\r
+ *PageEntry = NewPageEntry;\r
+ if (CurrentPageEntry != NewPageEntry) {\r
+ *IsModified = TRUE;\r
+ DEBUG ((DEBUG_VERBOSE, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry));\r
+ DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry));\r
+ } else {\r
+ *IsModified = FALSE;\r
+ }\r
+}\r
+\r
+/**\r
+ This function returns if there is need to split page entry.\r
+\r
+ @param[in] BaseAddress The base address to be checked.\r
+ @param[in] Length The length to be checked.\r
+ @param[in] PageEntry The page entry to be checked.\r
+ @param[in] PageAttribute The page attribute of the page entry.\r
+\r
+ @retval SplitAttributes on if there is need to split page entry.\r
+**/\r
+PAGE_ATTRIBUTE\r
+NeedSplitPage (\r
+ IN PHYSICAL_ADDRESS BaseAddress,\r
+ IN UINT64 Length,\r
+ IN UINT64 *PageEntry,\r
+ IN PAGE_ATTRIBUTE PageAttribute\r
+ )\r
+{\r
+ UINT64 PageEntryLength;\r
+\r
+ PageEntryLength = PageAttributeToLength (PageAttribute);\r
+\r
+ if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) {\r
+ return PageNone;\r
+ }\r
+\r
+ if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) {\r
+ return Page4K;\r
+ }\r
+\r
+ return Page2M;\r
+}\r
+\r
+/**\r
+ This function splits one page entry to small page entries.\r
+\r
+ @param[in] PageEntry The page entry to be splitted.\r
+ @param[in] PageAttribute The page attribute of the page entry.\r
+ @param[in] SplitAttribute How to split the page entry.\r
+\r
+ @retval RETURN_SUCCESS The page entry is splitted.\r
+ @retval RETURN_UNSUPPORTED The page entry does not support to be splitted.\r
+ @retval RETURN_OUT_OF_RESOURCES No resource to split page entry.\r
+**/\r
+RETURN_STATUS\r
+SplitPage (\r
+ IN UINT64 *PageEntry,\r
+ IN PAGE_ATTRIBUTE PageAttribute,\r
+ IN PAGE_ATTRIBUTE SplitAttribute\r
+ )\r
+{\r
+ UINT64 BaseAddress;\r
+ UINT64 *NewPageEntry;\r
+ UINTN Index;\r
+\r
+ ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);\r
+\r
+ if (PageAttribute == Page2M) {\r
+ //\r
+ // Split 2M to 4K\r
+ //\r
+ ASSERT (SplitAttribute == Page4K);\r
+ if (SplitAttribute == Page4K) {\r
+ NewPageEntry = AllocatePageTableMemory (1);\r
+ DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));\r
+ if (NewPageEntry == NULL) {\r
+ return RETURN_OUT_OF_RESOURCES;\r
+ }\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
+ }\r
+ (*PageEntry) = (UINT64)(UINTN)NewPageEntry + ((*PageEntry) & PAGE_PROGATE_BITS);\r
+ return RETURN_SUCCESS;\r
+ } else {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+ } else if (PageAttribute == Page1G) {\r
+ //\r
+ // Split 1G to 2M\r
+ // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.\r
+ //\r
+ ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K);\r
+ if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) {\r
+ NewPageEntry = AllocatePageTableMemory (1);\r
+ DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));\r
+ if (NewPageEntry == NULL) {\r
+ return RETURN_OUT_OF_RESOURCES;\r
+ }\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
+ }\r
+ (*PageEntry) = (UINT64)(UINTN)NewPageEntry + ((*PageEntry) & PAGE_PROGATE_BITS);\r
+ return RETURN_SUCCESS;\r
+ } else {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+ } else {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+}\r
+\r
+/**\r
+ This function modifies the page attributes for the memory region specified by BaseAddress and\r
+ Length from their current attributes to the attributes specified by Attributes.\r
+\r
+ Caller should make sure BaseAddress and Length is at page boundary.\r
+\r
+ @param[in] BaseAddress The physical address that is the start address of a memory region.\r
+ @param[in] Length The size in bytes of the memory region.\r
+ @param[in] Attributes The bit mask of attributes to modify for the memory region.\r
+ @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.\r
+ @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.\r
+ @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.\r
+\r
+ @retval RETURN_SUCCESS The attributes were modified for the memory region.\r
+ @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by\r
+ BaseAddress and Length cannot be modified.\r
+ @retval RETURN_INVALID_PARAMETER Length is zero.\r
+ Attributes specified an illegal combination of attributes that\r
+ cannot be set together.\r
+ @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
+ the memory resource range.\r
+ @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory\r
+ resource range specified by BaseAddress and Length.\r
+ The bit mask of attributes is not support for the memory resource\r
+ range specified by BaseAddress and Length.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+ConvertMemoryPageAttributes (\r
+ IN PHYSICAL_ADDRESS BaseAddress,\r
+ IN UINT64 Length,\r
+ IN UINT64 Attributes,\r
+ IN BOOLEAN IsSet,\r
+ OUT BOOLEAN *IsSplitted, OPTIONAL\r
+ OUT BOOLEAN *IsModified OPTIONAL\r
+ )\r
+{\r
+ UINT64 *PageEntry;\r
+ PAGE_ATTRIBUTE PageAttribute;\r
+ UINTN PageEntryLength;\r
+ PAGE_ATTRIBUTE SplitAttribute;\r
+ RETURN_STATUS Status;\r
+ BOOLEAN IsEntryModified;\r
+\r
+ ASSERT (Attributes != 0);\r
+ ASSERT ((Attributes & ~(EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP)) == 0);\r
+\r
+ ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);\r
+ ASSERT ((Length & (SIZE_4KB - 1)) == 0);\r
+\r
+ if (Length == 0) {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+// DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));\r
+\r
+ if (IsSplitted != NULL) {\r
+ *IsSplitted = FALSE;\r
+ }\r
+ if (IsModified != NULL) {\r
+ *IsModified = FALSE;\r
+ }\r
+\r
+ //\r
+ // Below logic is to check 2M/4K page to make sure we donot waist memory.\r
+ //\r
+ while (Length != 0) {\r
+ PageEntry = GetPageTableEntry (BaseAddress, &PageAttribute);\r
+ if (PageEntry == NULL) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+ PageEntryLength = PageAttributeToLength (PageAttribute);\r
+ SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute);\r
+ if (SplitAttribute == PageNone) {\r
+ ConvertPageEntryAttribute (PageEntry, Attributes, IsSet, &IsEntryModified);\r
+ if (IsEntryModified) {\r
+ if (IsModified != NULL) {\r
+ *IsModified = TRUE;\r
+ }\r
+ }\r
+ //\r
+ // Convert success, move to next\r
+ //\r
+ BaseAddress += PageEntryLength;\r
+ Length -= PageEntryLength;\r
+ } else {\r
+ Status = SplitPage (PageEntry, PageAttribute, SplitAttribute);\r
+ if (RETURN_ERROR (Status)) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+ if (IsSplitted != NULL) {\r
+ *IsSplitted = TRUE;\r
+ }\r
+ if (IsModified != NULL) {\r
+ *IsModified = TRUE;\r
+ }\r
+ //\r
+ // Just split current page\r
+ // Convert success in next around\r
+ //\r
+ }\r
+ }\r
+\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+/**\r
+ FlushTlb on current processor.\r
+\r
+ @param[in,out] Buffer Pointer to private data buffer.\r
+**/\r
+VOID\r
+EFIAPI\r
+FlushTlbOnCurrentProcessor (\r
+ IN OUT VOID *Buffer\r
+ )\r
+{\r
+ CpuFlushTlb ();\r
+}\r
+\r
+/**\r
+ FlushTlb for all processors.\r
+**/\r
+VOID\r
+FlushTlbForAll (\r
+ VOID\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ FlushTlbOnCurrentProcessor (NULL);\r
+\r
+ for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {\r
+ if (Index != gSmst->CurrentlyExecutingCpu) {\r
+ // Force to start up AP in blocking mode,\r
+ SmmBlockingStartupThisAp (FlushTlbOnCurrentProcessor, Index, NULL);\r
+ // Do not check return status, because AP might not be present in some corner cases.\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ This function sets the attributes for the memory region specified by BaseAddress and\r
+ Length from their current attributes to the attributes specified by Attributes.\r
+\r
+ @param[in] BaseAddress The physical address that is the start address of a memory region.\r
+ @param[in] Length The size in bytes of the memory region.\r
+ @param[in] Attributes The bit mask of attributes to set for the memory region.\r
+ @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.\r
+\r
+ @retval EFI_SUCCESS The attributes were set for the memory region.\r
+ @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
+ BaseAddress and Length cannot be modified.\r
+ @retval EFI_INVALID_PARAMETER Length is zero.\r
+ Attributes specified an illegal combination of attributes that\r
+ cannot be set together.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
+ the memory resource range.\r
+ @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
+ resource range specified by BaseAddress and Length.\r
+ The bit mask of attributes is not support for the memory resource\r
+ range specified by BaseAddress and Length.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmmSetMemoryAttributesEx (\r
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
+ IN UINT64 Length,\r
+ IN UINT64 Attributes,\r
+ OUT BOOLEAN *IsSplitted OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ BOOLEAN IsModified;\r
+\r
+ Status = ConvertMemoryPageAttributes (BaseAddress, Length, Attributes, TRUE, IsSplitted, &IsModified);\r
+ if (!EFI_ERROR(Status)) {\r
+ if (IsModified) {\r
+ //\r
+ // Flush TLB as last step\r
+ //\r
+ FlushTlbForAll();\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ This function clears the attributes for the memory region specified by BaseAddress and\r
+ Length from their current attributes to the attributes specified by Attributes.\r
+\r
+ @param[in] BaseAddress The physical address that is the start address of a memory region.\r
+ @param[in] Length The size in bytes of the memory region.\r
+ @param[in] Attributes The bit mask of attributes to clear for the memory region.\r
+ @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.\r
+\r
+ @retval EFI_SUCCESS The attributes were cleared for the memory region.\r
+ @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
+ BaseAddress and Length cannot be modified.\r
+ @retval EFI_INVALID_PARAMETER Length is zero.\r
+ Attributes specified an illegal combination of attributes that\r
+ cannot be set together.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
+ the memory resource range.\r
+ @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
+ resource range specified by BaseAddress and Length.\r
+ The bit mask of attributes is not support for the memory resource\r
+ range specified by BaseAddress and Length.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmmClearMemoryAttributesEx (\r
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
+ IN UINT64 Length,\r
+ IN UINT64 Attributes,\r
+ OUT BOOLEAN *IsSplitted OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ BOOLEAN IsModified;\r
+\r
+ Status = ConvertMemoryPageAttributes (BaseAddress, Length, Attributes, FALSE, IsSplitted, &IsModified);\r
+ if (!EFI_ERROR(Status)) {\r
+ if (IsModified) {\r
+ //\r
+ // Flush TLB as last step\r
+ //\r
+ FlushTlbForAll();\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ This function sets the attributes for the memory region specified by BaseAddress and\r
+ Length from their current attributes to the attributes specified by Attributes.\r
+\r
+ @param[in] BaseAddress The physical address that is the start address of a memory region.\r
+ @param[in] Length The size in bytes of the memory region.\r
+ @param[in] Attributes The bit mask of attributes to set for the memory region.\r
+\r
+ @retval EFI_SUCCESS The attributes were set for the memory region.\r
+ @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
+ BaseAddress and Length cannot be modified.\r
+ @retval EFI_INVALID_PARAMETER Length is zero.\r
+ Attributes specified an illegal combination of attributes that\r
+ cannot be set together.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
+ the memory resource range.\r
+ @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
+ resource range specified by BaseAddress and Length.\r
+ The bit mask of attributes is not support for the memory resource\r
+ range specified by BaseAddress and Length.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmmSetMemoryAttributes (\r
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
+ IN UINT64 Length,\r
+ IN UINT64 Attributes\r
+ )\r
+{\r
+ return SmmSetMemoryAttributesEx (BaseAddress, Length, Attributes, NULL);\r
+}\r
+\r
+/**\r
+ This function clears the attributes for the memory region specified by BaseAddress and\r
+ Length from their current attributes to the attributes specified by Attributes.\r
+\r
+ @param[in] BaseAddress The physical address that is the start address of a memory region.\r
+ @param[in] Length The size in bytes of the memory region.\r
+ @param[in] Attributes The bit mask of attributes to clear for the memory region.\r
+\r
+ @retval EFI_SUCCESS The attributes were cleared for the memory region.\r
+ @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
+ BaseAddress and Length cannot be modified.\r
+ @retval EFI_INVALID_PARAMETER Length is zero.\r
+ Attributes specified an illegal combination of attributes that\r
+ cannot be set together.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
+ the memory resource range.\r
+ @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
+ resource range specified by BaseAddress and Length.\r
+ The bit mask of attributes is not support for the memory resource\r
+ range specified by BaseAddress and Length.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmmClearMemoryAttributes (\r
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
+ IN UINT64 Length,\r
+ IN UINT64 Attributes\r
+ )\r
+{\r
+ return SmmClearMemoryAttributesEx (BaseAddress, Length, Attributes, NULL);\r
+}\r
+\r
+\r
+\r
+/**\r
+ Retrieves a pointer to the system configuration table from the SMM System Table\r
+ based on a specified GUID.\r
+\r
+ @param[in] TableGuid The pointer to table's GUID type.\r
+ @param[out] Table The pointer to the table associated with TableGuid in the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS A configuration table matching TableGuid was found.\r
+ @retval EFI_NOT_FOUND A configuration table matching TableGuid could not be found.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmmGetSystemConfigurationTable (\r
+ IN EFI_GUID *TableGuid,\r
+ OUT VOID **Table\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ ASSERT (TableGuid != NULL);\r
+ ASSERT (Table != NULL);\r
+\r
+ *Table = NULL;\r
+ for (Index = 0; Index < gSmst->NumberOfTableEntries; Index++) {\r
+ if (CompareGuid (TableGuid, &(gSmst->SmmConfigurationTable[Index].VendorGuid))) {\r
+ *Table = gSmst->SmmConfigurationTable[Index].VendorTable;\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ This function sets SMM save state buffer to be RW and XP.\r
+**/\r
+VOID\r
+PatchSmmSaveStateMap (\r
+ VOID\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINTN TileCodeSize;\r
+ UINTN TileDataSize;\r
+ UINTN TileSize;\r
+\r
+ TileCodeSize = GetSmiHandlerSize ();\r
+ TileCodeSize = ALIGN_VALUE(TileCodeSize, SIZE_4KB);\r
+ TileDataSize = sizeof (SMRAM_SAVE_STATE_MAP) + sizeof (PROCESSOR_SMM_DESCRIPTOR);\r
+ TileDataSize = ALIGN_VALUE(TileDataSize, SIZE_4KB);\r
+ TileSize = TileDataSize + TileCodeSize - 1;\r
+ TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize);\r
+\r
+ DEBUG ((DEBUG_INFO, "PatchSmmSaveStateMap:\n"));\r
+ for (Index = 0; Index < mMaxNumberOfCpus - 1; Index++) {\r
+ //\r
+ // Code\r
+ //\r
+ SmmSetMemoryAttributes (\r
+ mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,\r
+ TileCodeSize,\r
+ EFI_MEMORY_RO\r
+ );\r
+ SmmClearMemoryAttributes (\r
+ mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,\r
+ TileCodeSize,\r
+ EFI_MEMORY_XP\r
+ );\r
+\r
+ //\r
+ // Data\r
+ //\r
+ SmmClearMemoryAttributes (\r
+ mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,\r
+ TileSize - TileCodeSize,\r
+ EFI_MEMORY_RO\r
+ );\r
+ SmmSetMemoryAttributes (\r
+ mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,\r
+ TileSize - TileCodeSize,\r
+ EFI_MEMORY_XP\r
+ );\r
+ }\r
+\r
+ //\r
+ // Code\r
+ //\r
+ SmmSetMemoryAttributes (\r
+ mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,\r
+ TileCodeSize,\r
+ EFI_MEMORY_RO\r
+ );\r
+ SmmClearMemoryAttributes (\r
+ mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,\r
+ TileCodeSize,\r
+ EFI_MEMORY_XP\r
+ );\r
+\r
+ //\r
+ // Data\r
+ //\r
+ SmmClearMemoryAttributes (\r
+ mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,\r
+ SIZE_32KB - TileCodeSize,\r
+ EFI_MEMORY_RO\r
+ );\r
+ SmmSetMemoryAttributes (\r
+ mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,\r
+ SIZE_32KB - TileCodeSize,\r
+ EFI_MEMORY_XP\r
+ );\r
+}\r
+\r
+/**\r
+ This function sets GDT/IDT buffer to be RO and XP.\r
+**/\r
+VOID\r
+PatchGdtIdtMap (\r
+ VOID\r
+ )\r
+{\r
+ EFI_PHYSICAL_ADDRESS BaseAddress;\r
+ UINTN Size;\r
+\r
+ //\r
+ // GDT\r
+ //\r
+ DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - GDT:\n"));\r
+\r
+ BaseAddress = mGdtBuffer;\r
+ Size = ALIGN_VALUE(mGdtBufferSize, SIZE_4KB);\r
+ SmmSetMemoryAttributes (\r
+ BaseAddress,\r
+ Size,\r
+ EFI_MEMORY_RO\r
+ );\r
+ SmmSetMemoryAttributes (\r
+ BaseAddress,\r
+ Size,\r
+ EFI_MEMORY_XP\r
+ );\r
+\r
+ //\r
+ // IDT\r
+ //\r
+ DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - IDT:\n"));\r
+\r
+ BaseAddress = gcSmiIdtr.Base;\r
+ Size = ALIGN_VALUE(gcSmiIdtr.Limit + 1, SIZE_4KB);\r
+ SmmSetMemoryAttributes (\r
+ BaseAddress,\r
+ Size,\r
+ EFI_MEMORY_RO\r
+ );\r
+ SmmSetMemoryAttributes (\r
+ BaseAddress,\r
+ Size,\r
+ EFI_MEMORY_XP\r
+ );\r
+}\r
+\r
+/**\r
+ This function sets memory attribute according to MemoryAttributesTable.\r
+**/\r
+VOID\r
+SetMemMapAttributes (\r
+ VOID\r
+ )\r
+{\r
+ EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
+ EFI_MEMORY_DESCRIPTOR *MemoryMapStart;\r
+ UINTN MemoryMapEntryCount;\r
+ UINTN DescriptorSize;\r
+ UINTN Index;\r
+ EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;\r
+\r
+ SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);\r
+ if (MemoryAttributesTable == NULL) {\r
+ DEBUG ((DEBUG_INFO, "MemoryAttributesTable - NULL\n"));\r
+ return ;\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "MemoryAttributesTable:\n"));\r
+ DEBUG ((DEBUG_INFO, " Version - 0x%08x\n", MemoryAttributesTable->Version));\r
+ DEBUG ((DEBUG_INFO, " NumberOfEntries - 0x%08x\n", MemoryAttributesTable->NumberOfEntries));\r
+ DEBUG ((DEBUG_INFO, " DescriptorSize - 0x%08x\n", MemoryAttributesTable->DescriptorSize));\r
+\r
+ MemoryMapEntryCount = MemoryAttributesTable->NumberOfEntries;\r
+ DescriptorSize = MemoryAttributesTable->DescriptorSize;\r
+ MemoryMapStart = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1);\r
+ MemoryMap = MemoryMapStart;\r
+ for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
+ DEBUG ((DEBUG_INFO, "Entry (0x%x)\n", MemoryMap));\r
+ DEBUG ((DEBUG_INFO, " Type - 0x%x\n", MemoryMap->Type));\r
+ DEBUG ((DEBUG_INFO, " PhysicalStart - 0x%016lx\n", MemoryMap->PhysicalStart));\r
+ DEBUG ((DEBUG_INFO, " VirtualStart - 0x%016lx\n", MemoryMap->VirtualStart));\r
+ DEBUG ((DEBUG_INFO, " NumberOfPages - 0x%016lx\n", MemoryMap->NumberOfPages));\r
+ DEBUG ((DEBUG_INFO, " Attribute - 0x%016lx\n", MemoryMap->Attribute));\r
+ MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);\r
+ }\r
+\r
+ MemoryMap = MemoryMapStart;\r
+ for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
+ DEBUG ((DEBUG_VERBOSE, "SetAttribute: Memory Entry - 0x%lx, 0x%x\n", MemoryMap->PhysicalStart, MemoryMap->NumberOfPages));\r
+ switch (MemoryMap->Type) {\r
+ case EfiRuntimeServicesCode:\r
+ SmmSetMemoryAttributes (\r
+ MemoryMap->PhysicalStart,\r
+ EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),\r
+ EFI_MEMORY_RO\r
+ );\r
+ break;\r
+ case EfiRuntimeServicesData:\r
+ SmmSetMemoryAttributes (\r
+ MemoryMap->PhysicalStart,\r
+ EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),\r
+ EFI_MEMORY_XP\r
+ );\r
+ break;\r
+ default:\r
+ SmmSetMemoryAttributes (\r
+ MemoryMap->PhysicalStart,\r
+ EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),\r
+ EFI_MEMORY_XP\r
+ );\r
+ break;\r
+ }\r
+ MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);\r
+ }\r
+\r
+ PatchSmmSaveStateMap ();\r
+ PatchGdtIdtMap ();\r
+\r
+ return ;\r
+}\r