--- /dev/null
+/** @file\r
+ Instance of SMM IO check library.\r
+\r
+ SMM IO check library library implementation. This library consumes GCD to collect all valid\r
+ IO space defined by a platform.\r
+ A platform may have its own SmmIoLib instance to exclude more IO space.\r
+\r
+ Copyright (c) 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
+ 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
+\r
+#include <PiSmm.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/SmmServicesTableLib.h>\r
+#include <Library/HobLib.h>\r
+#include <Library/DxeServicesTableLib.h>\r
+#include <Protocol/SmmReadyToLock.h>\r
+#include <Protocol/SmmEndOfDxe.h>\r
+\r
+EFI_GCD_MEMORY_SPACE_DESCRIPTOR *mSmmIoLibGcdMemSpace = NULL;\r
+UINTN mSmmIoLibGcdMemNumberOfDesc = 0;\r
+\r
+EFI_PHYSICAL_ADDRESS mSmmIoLibInternalMaximumSupportMemAddress = 0;\r
+\r
+VOID *mSmmIoLibRegistrationEndOfDxe;\r
+VOID *mSmmIoLibRegistrationReadyToLock;\r
+\r
+BOOLEAN mSmmIoLibReadyToLock = FALSE;\r
+\r
+/**\r
+ Calculate and save the maximum support address.\r
+\r
+**/\r
+VOID\r
+SmmIoLibInternalCalculateMaximumSupportAddress (\r
+ VOID\r
+ )\r
+{\r
+ VOID *Hob;\r
+ UINT32 RegEax;\r
+ UINT8 MemPhysicalAddressBits;\r
+\r
+ //\r
+ // Get physical address bits supported.\r
+ //\r
+ Hob = GetFirstHob (EFI_HOB_TYPE_CPU);\r
+ if (Hob != NULL) {\r
+ MemPhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;\r
+ } else {\r
+ AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
+ if (RegEax >= 0x80000008) {\r
+ AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);\r
+ MemPhysicalAddressBits = (UINT8) RegEax;\r
+ } else {\r
+ MemPhysicalAddressBits = 36;\r
+ }\r
+ }\r
+ //\r
+ // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.\r
+ //\r
+ ASSERT (MemPhysicalAddressBits <= 52);\r
+ if (MemPhysicalAddressBits > 48) {\r
+ MemPhysicalAddressBits = 48;\r
+ }\r
+\r
+ //\r
+ // Save the maximum support address in one global variable\r
+ //\r
+ mSmmIoLibInternalMaximumSupportMemAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, MemPhysicalAddressBits) - 1);\r
+ DEBUG ((DEBUG_INFO, "mSmmIoLibInternalMaximumSupportMemAddress = 0x%lx\n", mSmmIoLibInternalMaximumSupportMemAddress));\r
+}\r
+\r
+/**\r
+ This function check if the MMIO resource is valid per processor architecture and\r
+ valid per platform design.\r
+\r
+ @param BaseAddress The MMIO start address to be checked.\r
+ @param Length The MMIO length to be checked.\r
+ @param Owner A GUID representing the owner of the resource.\r
+ This GUID may be used by producer to correlate the device ownership of the resource.\r
+ NULL means no specific owner.\r
+\r
+ @retval TRUE This MMIO resource is valid per processor architecture and valid per platform design.\r
+ @retval FALSE This MMIO resource is not valid per processor architecture or valid per platform design.\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+SmmIsMmioValid (\r
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
+ IN UINT64 Length,\r
+ IN EFI_GUID *Owner OPTIONAL\r
+ )\r
+{\r
+ UINTN Index;\r
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Desc;\r
+ BOOLEAN InValidRegion;\r
+\r
+ //\r
+ // Check override.\r
+ // NOTE: (B:0->L:4G) is invalid for IA32, but (B:1->L:4G-1)/(B:4G-1->L:1) is valid.\r
+ //\r
+ if ((Length > mSmmIoLibInternalMaximumSupportMemAddress) ||\r
+ (BaseAddress > mSmmIoLibInternalMaximumSupportMemAddress) ||\r
+ ((Length != 0) && (BaseAddress > (mSmmIoLibInternalMaximumSupportMemAddress - (Length - 1)))) ) {\r
+ //\r
+ // Overflow happen\r
+ //\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "SmmIsMmioValid: Overflow: BaseAddress (0x%lx) - Length (0x%lx), MaximumSupportMemAddress (0x%lx)\n",\r
+ BaseAddress,\r
+ Length,\r
+ mSmmIoLibInternalMaximumSupportMemAddress\r
+ ));\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Check override for valid MMIO region\r
+ //\r
+ if (mSmmIoLibReadyToLock) {\r
+ InValidRegion = FALSE;\r
+ for (Index = 0; Index < mSmmIoLibGcdMemNumberOfDesc; Index ++) {\r
+ Desc = &mSmmIoLibGcdMemSpace[Index];\r
+ if ((Desc->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) &&\r
+ (BaseAddress >= Desc->BaseAddress) &&\r
+ ((BaseAddress + Length) <= (Desc->BaseAddress + Desc->Length))) {\r
+ InValidRegion = TRUE;\r
+ }\r
+ }\r
+\r
+ if (!InValidRegion) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "SmmIsMmioValid: Not in valid MMIO region: BaseAddress (0x%lx) - Length (0x%lx)\n",\r
+ BaseAddress,\r
+ Length\r
+ ));\r
+ return FALSE;\r
+ }\r
+ }\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Merge continous entries whose type is EfiGcdMemoryTypeMemoryMappedIo.\r
+\r
+ @param[in, out] GcdMemoryMap A pointer to the buffer in which firmware places\r
+ the current GCD memory map.\r
+ @param[in, out] NumberOfDescriptors A pointer to the number of the\r
+ GcdMemoryMap buffer. On input, this is the number of\r
+ the current GCD memory map. On output,\r
+ it is the number of new GCD memory map after merge.\r
+**/\r
+STATIC\r
+VOID\r
+MergeGcdMmioEntry (\r
+ IN OUT EFI_GCD_MEMORY_SPACE_DESCRIPTOR *GcdMemoryMap,\r
+ IN OUT UINTN *NumberOfDescriptors\r
+ )\r
+{\r
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR *GcdMemoryMapEntry;\r
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR *GcdMemoryMapEnd;\r
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR *NewGcdMemoryMapEntry;\r
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR *NextGcdMemoryMapEntry;\r
+\r
+ GcdMemoryMapEntry = GcdMemoryMap;\r
+ NewGcdMemoryMapEntry = GcdMemoryMap;\r
+ GcdMemoryMapEnd = (EFI_GCD_MEMORY_SPACE_DESCRIPTOR *) ((UINT8 *) GcdMemoryMap + (*NumberOfDescriptors) * sizeof(EFI_GCD_MEMORY_SPACE_DESCRIPTOR));\r
+ while ((UINTN)GcdMemoryMapEntry < (UINTN)GcdMemoryMapEnd) {\r
+ CopyMem (NewGcdMemoryMapEntry, GcdMemoryMapEntry, sizeof(EFI_GCD_MEMORY_SPACE_DESCRIPTOR));\r
+ NextGcdMemoryMapEntry = GcdMemoryMapEntry + 1;\r
+\r
+ do {\r
+ if (((UINTN)NextGcdMemoryMapEntry < (UINTN)GcdMemoryMapEnd) &&\r
+ (GcdMemoryMapEntry->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) && (NextGcdMemoryMapEntry->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) &&\r
+ ((GcdMemoryMapEntry->BaseAddress + GcdMemoryMapEntry->Length) == NextGcdMemoryMapEntry->BaseAddress)) {\r
+ GcdMemoryMapEntry->Length += NextGcdMemoryMapEntry->Length;\r
+ if (NewGcdMemoryMapEntry != GcdMemoryMapEntry) {\r
+ NewGcdMemoryMapEntry->Length += NextGcdMemoryMapEntry->Length;\r
+ }\r
+\r
+ NextGcdMemoryMapEntry = NextGcdMemoryMapEntry + 1;\r
+ continue;\r
+ } else {\r
+ GcdMemoryMapEntry = NextGcdMemoryMapEntry - 1;\r
+ break;\r
+ }\r
+ } while (TRUE);\r
+\r
+ GcdMemoryMapEntry = GcdMemoryMapEntry + 1;\r
+ NewGcdMemoryMapEntry = NewGcdMemoryMapEntry + 1;\r
+ }\r
+\r
+ *NumberOfDescriptors = ((UINTN)NewGcdMemoryMapEntry - (UINTN)GcdMemoryMap) / sizeof(EFI_GCD_MEMORY_SPACE_DESCRIPTOR);\r
+\r
+ return ;\r
+}\r
+\r
+/**\r
+ Notification for SMM EndOfDxe protocol.\r
+\r
+ @param[in] Protocol Points to the protocol's unique identifier.\r
+ @param[in] Interface Points to the interface instance.\r
+ @param[in] Handle The handle on which the interface was installed.\r
+\r
+ @retval EFI_SUCCESS Notification runs successfully.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmmIoLibInternalEndOfDxeNotify (\r
+ IN CONST EFI_GUID *Protocol,\r
+ IN VOID *Interface,\r
+ IN EFI_HANDLE Handle\r
+ )\r
+{\r
+ UINTN NumberOfDescriptors;\r
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemSpaceMap;\r
+ EFI_STATUS Status;\r
+\r
+ Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemSpaceMap);\r
+ if (!EFI_ERROR (Status)) {\r
+\r
+ MergeGcdMmioEntry (MemSpaceMap, &NumberOfDescriptors);\r
+\r
+ mSmmIoLibGcdMemSpace = AllocateCopyPool (NumberOfDescriptors * sizeof (EFI_GCD_MEMORY_SPACE_DESCRIPTOR), MemSpaceMap);\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (EFI_ERROR (Status)) {\r
+ gBS->FreePool (MemSpaceMap);\r
+ return Status;\r
+ }\r
+\r
+ mSmmIoLibGcdMemNumberOfDesc = NumberOfDescriptors;\r
+ gBS->FreePool (MemSpaceMap);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Notification for SMM ReadyToLock protocol.\r
+\r
+ @param[in] Protocol Points to the protocol's unique identifier.\r
+ @param[in] Interface Points to the interface instance.\r
+ @param[in] Handle The handle on which the interface was installed.\r
+\r
+ @retval EFI_SUCCESS Notification runs successfully.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmmIoLibInternalReadyToLockNotify (\r
+ IN CONST EFI_GUID *Protocol,\r
+ IN VOID *Interface,\r
+ IN EFI_HANDLE Handle\r
+ )\r
+{\r
+ mSmmIoLibReadyToLock = TRUE;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ The constructor function initializes the Smm IO library\r
+\r
+ @param ImageHandle The firmware allocated handle for the EFI image.\r
+ @param SystemTable A pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmmIoLibConstructor (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Calculate and save maximum support address\r
+ //\r
+ SmmIoLibInternalCalculateMaximumSupportAddress ();\r
+\r
+ //\r
+ // Register EndOfDxe to get GCD resource map\r
+ //\r
+ Status = gSmst->SmmRegisterProtocolNotify (&gEfiSmmEndOfDxeProtocolGuid, SmmIoLibInternalEndOfDxeNotify, &mSmmIoLibRegistrationEndOfDxe);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // Register ready to lock so that we can know when to check valid resource region\r
+ //\r
+ Status = gSmst->SmmRegisterProtocolNotify (&gEfiSmmReadyToLockProtocolGuid, SmmIoLibInternalReadyToLockNotify, &mSmmIoLibRegistrationReadyToLock);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ The destructor function frees resource used in the Smm IO library\r
+\r
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.\r
+ @param[in] SystemTable A pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The deconstructor always returns EFI_SUCCESS.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmmIoLibDestructor (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ gSmst->SmmRegisterProtocolNotify (&gEfiSmmEndOfDxeProtocolGuid, NULL, &mSmmIoLibRegistrationEndOfDxe);\r
+ gSmst->SmmRegisterProtocolNotify (&gEfiSmmReadyToLockProtocolGuid, NULL, &mSmmIoLibRegistrationReadyToLock);\r
+ return EFI_SUCCESS;\r
+}\r