+\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
+\r
+/**\r
+ This function set given attributes of the memory region specified by\r
+ BaseAddress and Length.\r
+\r
+ @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.\r
+ @param BaseAddress The physical address that is the start address of\r
+ a memory region.\r
+ @param Length The size in bytes of the memory region.\r
+ @param Attributes The bit mask of attributes to set for the memory\r
+ region.\r
+\r
+ @retval EFI_SUCCESS The attributes were set for the memory region.\r
+ @retval EFI_INVALID_PARAMETER Length is zero.\r
+ Attributes specified an illegal combination of\r
+ attributes that cannot be set together.\r
+ @retval EFI_UNSUPPORTED The processor does not support one or more\r
+ bytes of the memory resource range specified\r
+ by BaseAddress and Length.\r
+ The bit mask of attributes is not support for\r
+ the memory resource range specified by\r
+ BaseAddress and Length.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EdkiiSmmSetMemoryAttributes (\r
+ IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,\r
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
+ IN UINT64 Length,\r
+ IN UINT64 Attributes\r
+ )\r
+{\r
+ return SmmSetMemoryAttributes (BaseAddress, Length, Attributes);\r
+}\r
+\r
+/**\r
+ This function clears given attributes of the memory region specified by\r
+ BaseAddress and Length.\r
+\r
+ @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.\r
+ @param BaseAddress The physical address that is the start address of\r
+ a memory region.\r
+ @param Length The size in bytes of the memory region.\r
+ @param Attributes The bit mask of attributes to set for the memory\r
+ region.\r
+\r
+ @retval EFI_SUCCESS The attributes were set for the memory region.\r
+ @retval EFI_INVALID_PARAMETER Length is zero.\r
+ Attributes specified an illegal combination of\r
+ attributes that cannot be set together.\r
+ @retval EFI_UNSUPPORTED The processor does not support one or more\r
+ bytes of the memory resource range specified\r
+ by BaseAddress and Length.\r
+ The bit mask of attributes is not support for\r
+ the memory resource range specified by\r
+ BaseAddress and Length.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EdkiiSmmClearMemoryAttributes (\r
+ IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,\r
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
+ IN UINT64 Length,\r
+ IN UINT64 Attributes\r
+ )\r
+{\r
+ return SmmClearMemoryAttributes (BaseAddress, Length, Attributes);\r
+}\r
+\r
+/**\r
+ This function retrieve the attributes of the memory region specified by\r
+ BaseAddress and Length. If different attributes are got from different part\r
+ of the memory region, EFI_NO_MAPPING will be returned.\r
+\r
+ @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.\r
+ @param BaseAddress The physical address that is the start address of\r
+ a memory region.\r
+ @param Length The size in bytes of the memory region.\r
+ @param Attributes Pointer to attributes returned.\r
+\r
+ @retval EFI_SUCCESS The attributes got for the memory region.\r
+ @retval EFI_INVALID_PARAMETER Length is zero.\r
+ Attributes is NULL.\r
+ @retval EFI_NO_MAPPING Attributes are not consistent cross the memory\r
+ region.\r
+ @retval EFI_UNSUPPORTED The processor does not support one or more\r
+ bytes of the memory resource range specified\r
+ by BaseAddress and Length.\r
+ The bit mask of attributes is not support for\r
+ the memory resource range specified by\r
+ BaseAddress and Length.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EdkiiSmmGetMemoryAttributes (\r
+ IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,\r
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
+ IN UINT64 Length,\r
+ OUT UINT64 *Attributes\r
+ )\r
+{\r
+ EFI_PHYSICAL_ADDRESS Address;\r
+ UINT64 *PageEntry;\r
+ UINT64 MemAttr;\r
+ PAGE_ATTRIBUTE PageAttr;\r
+ INT64 Size;\r
+\r
+ if (Length < SIZE_4KB || Attributes == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Size = (INT64)Length;\r
+ MemAttr = (UINT64)-1;\r
+\r
+ do {\r
+\r
+ PageEntry = GetPageTableEntry (BaseAddress, &PageAttr);\r
+ if (PageEntry == NULL || PageAttr == PageNone) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // If the memory range is cross page table boundary, make sure they\r
+ // share the same attribute. Return EFI_NO_MAPPING if not.\r
+ //\r
+ *Attributes = GetAttributesFromPageEntry (PageEntry);\r
+ if (MemAttr != (UINT64)-1 && *Attributes != MemAttr) {\r
+ return EFI_NO_MAPPING;\r
+ }\r
+\r
+ switch (PageAttr) {\r
+ case Page4K:\r
+ Address = *PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64;\r
+ Size -= (SIZE_4KB - (BaseAddress - Address));\r
+ BaseAddress += (SIZE_4KB - (BaseAddress - Address));\r
+ break;\r
+\r
+ case Page2M:\r
+ Address = *PageEntry & ~mAddressEncMask & PAGING_2M_ADDRESS_MASK_64;\r
+ Size -= SIZE_2MB - (BaseAddress - Address);\r
+ BaseAddress += SIZE_2MB - (BaseAddress - Address);\r
+ break;\r
+\r
+ case Page1G:\r
+ Address = *PageEntry & ~mAddressEncMask & PAGING_1G_ADDRESS_MASK_64;\r
+ Size -= SIZE_1GB - (BaseAddress - Address);\r
+ BaseAddress += SIZE_1GB - (BaseAddress - Address);\r
+ break;\r
+\r
+ default:\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ MemAttr = *Attributes;\r
+\r
+ } while (Size > 0);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r