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