/** @file Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include "TestPointInternal.h" GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID mTestPointSmmCommunciationGuid = TEST_POINT_SMM_COMMUNICATION_GUID; EFI_STATUS TestPointCheckSmrr ( VOID ); EFI_STATUS TestPointDumpSmmLoadedImage ( VOID ); EFI_STATUS TestPointCheckSmmMemAttribute ( VOID ); EFI_STATUS TestPointCheckSmmPaging ( VOID ); EFI_STATUS TestPointCheckSmmCommunicationBuffer ( IN EFI_MEMORY_DESCRIPTOR *UefiMemoryMap, IN UINTN UefiMemoryMapSize, IN UINTN UefiDescriptorSize, IN EFI_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable ); VOID TestPointDumpGcd ( OUT EFI_GCD_MEMORY_SPACE_DESCRIPTOR **GcdMemoryMap, OPTIONAL OUT UINTN *GcdMemoryMapNumberOfDescriptors, OPTIONAL OUT EFI_GCD_IO_SPACE_DESCRIPTOR **GcdIoMap, OPTIONAL OUT UINTN *GcdIoMapNumberOfDescriptors, OPTIONAL IN BOOLEAN DumpPrint ); VOID TestPointDumpUefiMemoryMap ( OUT EFI_MEMORY_DESCRIPTOR **UefiMemoryMap, OPTIONAL OUT UINTN *UefiMemoryMapSize, OPTIONAL OUT UINTN *UefiDescriptorSize, OPTIONAL IN BOOLEAN DumpPrint ); GLOBAL_REMOVE_IF_UNREFERENCED EFI_MEMORY_DESCRIPTOR *mUefiMemoryMap; GLOBAL_REMOVE_IF_UNREFERENCED UINTN mUefiMemoryMapSize; GLOBAL_REMOVE_IF_UNREFERENCED UINTN mUefiDescriptorSize; GLOBAL_REMOVE_IF_UNREFERENCED EFI_GCD_MEMORY_SPACE_DESCRIPTOR *mGcdMemoryMap; GLOBAL_REMOVE_IF_UNREFERENCED EFI_GCD_IO_SPACE_DESCRIPTOR *mGcdIoMap; GLOBAL_REMOVE_IF_UNREFERENCED UINTN mGcdMemoryMapNumberOfDescriptors; GLOBAL_REMOVE_IF_UNREFERENCED UINTN mGcdIoMapNumberOfDescriptors; EFI_MEMORY_ATTRIBUTES_TABLE *mUefiMemoryAttributesTable; GLOBAL_REMOVE_IF_UNREFERENCED ADAPTER_INFO_PLATFORM_TEST_POINT_STRUCT mTestPointStruct = { PLATFORM_TEST_POINT_VERSION, PLATFORM_TEST_POINT_ROLE_PLATFORM_IBV, {TEST_POINT_IMPLEMENTATION_ID_PLATFORM_SMM}, TEST_POINT_FEATURE_SIZE, {0}, // FeaturesImplemented {0}, // FeaturesVerified 0, }; GLOBAL_REMOVE_IF_UNREFERENCED UINT8 mFeatureImplemented[TEST_POINT_FEATURE_SIZE]; /** This service verifies SMRR configuration at the End of DXE. Test subject: SMRR. Test overview: Verify SMRR is aligned and SMRR matches SMRAM_INFO. Reporting mechanism: Set ADAPTER_INFO_PLATFORM_TEST_POINT_STRUCT. Dumps SMRR and SMRAM_INFO. @retval EFI_SUCCESS The test point check was performed successfully. @retval EFI_UNSUPPORTED The test point check is not supported on this platform. **/ EFI_STATUS EFIAPI TestPointSmmEndOfDxeSmrrFunctional ( VOID ) { EFI_STATUS Status; BOOLEAN Result; if ((mFeatureImplemented[TEST_POINT_INDEX_BYTE6_SMM] & TEST_POINT_BYTE6_SMM_END_OF_DXE_SMRR_FUNCTIONAL) == 0) { return EFI_SUCCESS; } DEBUG ((DEBUG_INFO, "======== TestPointSmmEndOfDxeSmrrFunctional - Enter\n")); Result = TRUE; Status = TestPointCheckSmrr (); if (EFI_ERROR(Status)) { Result = FALSE; } if (Result) { TestPointLibSetFeaturesVerified ( PLATFORM_TEST_POINT_ROLE_PLATFORM_IBV, NULL, TEST_POINT_INDEX_BYTE6_SMM, TEST_POINT_BYTE6_SMM_END_OF_DXE_SMRR_FUNCTIONAL ); } DEBUG ((DEBUG_INFO, "======== TestPointSmmEndOfDxe - Exit\n")); return EFI_SUCCESS; } /** This service verifies the validity of the SMM memory atttribute table at SMM Ready To Lock. Test subject: SMM memory attribute table. Test overview: Verify the SMM memory attribute table is reported. Verify image code/data is consistent with the SMM memory attribute table. Verify the GDT/IDT/PageTable is RO, data is NX, and code is RO. Reporting mechanism: Set ADAPTER_INFO_PLATFORM_TEST_POINT_STRUCT. Dumps the SMM memory attribute table and SMM image information. @retval EFI_SUCCESS The test point check was performed successfully. @retval EFI_UNSUPPORTED The test point check is not supported on this platform. **/ EFI_STATUS EFIAPI TestPointSmmReadyToLockSmmMemoryAttributeTableFunctional ( VOID ) { EFI_STATUS Status; BOOLEAN Result; if ((mFeatureImplemented[TEST_POINT_INDEX_BYTE6_SMM] & TEST_POINT_BYTE6_SMM_READY_TO_LOCK_SMM_MEMORY_ATTRIBUTE_TABLE_FUNCTIONAL) == 0) { return EFI_SUCCESS; } DEBUG ((DEBUG_INFO, "======== TestPointSmmReadyToLock - Enter\n")); Result = TRUE; TestPointDumpSmmLoadedImage (); Status = TestPointCheckSmmMemAttribute (); if (EFI_ERROR(Status)) { Result = FALSE; } if (Result) { TestPointLibSetFeaturesVerified ( PLATFORM_TEST_POINT_ROLE_PLATFORM_IBV, NULL, TEST_POINT_INDEX_BYTE6_SMM, TEST_POINT_BYTE6_SMM_READY_TO_LOCK_SMM_MEMORY_ATTRIBUTE_TABLE_FUNCTIONAL ); } DEBUG ((DEBUG_INFO, "======== TestPointSmmReadyToLock - Exit\n")); return EFI_SUCCESS; } /** This service verifies the security of SMM communication buffers at SMM Ready To Lock. Test subject: SMM communication buffer. Test overview: Verify only CommBuffer and MMIO are mapped in the page table. Reporting mechanism: Dumps the memory map and GCD map at SmmReadyToLock and checks at SmmReadyToBoot. @retval EFI_SUCCESS The test point check was performed successfully. @retval EFI_UNSUPPORTED The test point check is not supported on this platform. **/ EFI_STATUS EFIAPI TestPointSmmReadyToLockSecureSmmCommunicationBuffer ( VOID ) { EFI_STATUS Status; EFI_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable; UINTN MemoryAttributesTableSize; if ((mFeatureImplemented[TEST_POINT_INDEX_BYTE6_SMM] & TEST_POINT_BYTE6_SMM_READY_TO_LOCK_SECURE_SMM_COMMUNICATION_BUFFER) == 0) { return EFI_SUCCESS; } DEBUG ((DEBUG_INFO, "======== TestPointSmmReadyToLockSecureSmmCommunicationBuffer - Enter\n")); // // Collect information here, because it is last chance to access outside SMRAM. // TestPointDumpUefiMemoryMap (&mUefiMemoryMap, &mUefiMemoryMapSize, &mUefiDescriptorSize, TRUE); TestPointDumpGcd (&mGcdMemoryMap, &mGcdMemoryMapNumberOfDescriptors, &mGcdIoMap, &mGcdIoMapNumberOfDescriptors, TRUE); Status = EfiGetSystemConfigurationTable (&gEfiMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable); if (!EFI_ERROR (Status)) { MemoryAttributesTableSize = sizeof(EFI_MEMORY_ATTRIBUTES_TABLE) + MemoryAttributesTable->DescriptorSize * MemoryAttributesTable->NumberOfEntries; mUefiMemoryAttributesTable = AllocateCopyPool (MemoryAttributesTableSize, MemoryAttributesTable); ASSERT (mUefiMemoryAttributesTable != NULL); } // // Defer the validation to TestPointSmmReadyToBootSecureSmmCommunicationBuffer, because page table setup later. // DEBUG ((DEBUG_INFO, "======== TestPointSmmReadyToLockSecureSmmCommunicationBuffer - Exit\n")); return EFI_SUCCESS; } /** This service verifies the validity of the SMM page table at SMM Ready To Boot. Test subject: SMM page table. Test overview: Verify the SMM page table matches the SMM memory attribute table. Reporting mechanism: Set ADAPTER_INFO_PLATFORM_TEST_POINT_STRUCT. Reports an error message upon checking. @retval EFI_SUCCESS The test point check was performed successfully. @retval EFI_UNSUPPORTED The test point check is not supported on this platform. **/ EFI_STATUS EFIAPI TestPointSmmReadyToBootSmmPageProtection ( VOID ) { EFI_STATUS Status; BOOLEAN Result; if ((mFeatureImplemented[TEST_POINT_INDEX_BYTE6_SMM] & TEST_POINT_BYTE6_SMM_READY_TO_BOOT_SMM_PAGE_LEVEL_PROTECTION) == 0) { return EFI_SUCCESS; } DEBUG ((DEBUG_INFO, "======== TestPointSmmReadyToBootSmmPageProtection - Enter\n")); Result = TRUE; Status = TestPointCheckSmmPaging (); if (EFI_ERROR(Status)) { Result = FALSE; } if (Result) { TestPointLibSetFeaturesVerified ( PLATFORM_TEST_POINT_ROLE_PLATFORM_IBV, NULL, TEST_POINT_INDEX_BYTE6_SMM, TEST_POINT_BYTE6_SMM_READY_TO_BOOT_SMM_PAGE_LEVEL_PROTECTION ); } if (mUefiMemoryMap != NULL) { Result = TRUE; Status = TestPointCheckSmmCommunicationBuffer (mUefiMemoryMap, mUefiMemoryMapSize, mUefiDescriptorSize, mUefiMemoryAttributesTable); if (EFI_ERROR(Status)) { Result = FALSE; } if (Result) { TestPointLibSetFeaturesVerified ( PLATFORM_TEST_POINT_ROLE_PLATFORM_IBV, NULL, TEST_POINT_INDEX_BYTE6_SMM, TEST_POINT_BYTE6_SMM_READY_TO_LOCK_SECURE_SMM_COMMUNICATION_BUFFER ); } } DEBUG ((DEBUG_INFO, "======== TestPointSmmReadyToBootSmmPageProtection - Exit\n")); return EFI_SUCCESS; } /** Dispatch function for a Software SMI handler. Caution: This function may receive untrusted input. Communicate buffer and buffer size are external input, so this function will do basic validation. @param CommBuffer A pointer to a collection of data in memory that will be conveyed from a non-SMM environment into an SMM environment. @param CommBufferSize The size of the CommBuffer. @retval EFI_SUCCESS Command is handled successfully. **/ EFI_STATUS TestPointSmmReadyToBootSmmPageProtectionHandler ( IN OUT VOID *CommBuffer OPTIONAL, IN OUT UINTN *CommBufferSize OPTIONAL ) { EFI_STATUS Status; BOOLEAN Result; TEST_POINT_SMM_COMMUNICATION_UEFI_GCD_MAP_INFO *CommData; UINTN TempCommBufferSize; if ((mFeatureImplemented[TEST_POINT_INDEX_BYTE6_SMM] & TEST_POINT_BYTE6_SMM_READY_TO_BOOT_SMM_PAGE_LEVEL_PROTECTION) == 0) { return EFI_SUCCESS; } DEBUG ((DEBUG_INFO, "======== TestPointSmmReadyToBootSmmPageProtectionHandler - Enter\n")); TempCommBufferSize = *CommBufferSize; if (TempCommBufferSize < sizeof(TEST_POINT_SMM_COMMUNICATION_UEFI_GCD_MAP_INFO)) { DEBUG((DEBUG_ERROR, "TestPointSmmReadyToBootSmmPageProtectionHandler: SMM communication buffer size invalid!\n")); return EFI_SUCCESS; } if (!SmmIsBufferOutsideSmmValid((UINTN)CommBuffer, TempCommBufferSize)) { DEBUG((DEBUG_ERROR, "TestPointSmmReadyToBootSmmPageProtectionHandler: SMM communication buffer in SMRAM or overflow!\n")); return EFI_SUCCESS; } DEBUG ((DEBUG_INFO, "TempCommBufferSize - 0x%x\n", TempCommBufferSize)); CommData = AllocateCopyPool (TempCommBufferSize, CommBuffer); if (CommData == NULL) { DEBUG((DEBUG_ERROR, "TestPointSmmReadyToBootSmmPageProtectionHandler: SMM communication buffer size too big!\n")); return EFI_SUCCESS; } if (CommData->UefiMemoryMapOffset != sizeof(TEST_POINT_SMM_COMMUNICATION_UEFI_GCD_MAP_INFO)) { DEBUG((DEBUG_ERROR, "TestPointSmmReadyToBootSmmPageProtectionHandler: UefiMemoryMapOffset invalid!\n")); goto Done; } if (CommData->UefiMemoryMapSize >= TempCommBufferSize - CommData->UefiMemoryMapOffset) { DEBUG((DEBUG_ERROR, "TestPointSmmReadyToBootSmmPageProtectionHandler: UefiMemoryMapSize invalid!\n")); goto Done; } if (CommData->GcdMemoryMapOffset != CommData->UefiMemoryMapOffset + CommData->UefiMemoryMapSize) { DEBUG((DEBUG_ERROR, "TestPointSmmReadyToBootSmmPageProtectionHandler: GcdMemoryMapOffset invalid!\n")); goto Done; } if (CommData->GcdMemoryMapSize >= TempCommBufferSize - CommData->GcdMemoryMapOffset) { DEBUG((DEBUG_ERROR, "TestPointSmmReadyToBootSmmPageProtectionHandler: GcdMemoryMapSize invalid!\n")); goto Done; } if (CommData->GcdIoMapOffset != CommData->GcdMemoryMapOffset + CommData->GcdMemoryMapSize) { DEBUG((DEBUG_ERROR, "TestPointSmmReadyToBootSmmPageProtectionHandler: GcdIoMapOffset invalid!\n")); goto Done; } if (CommData->GcdIoMapSize >= TempCommBufferSize - CommData->GcdIoMapOffset) { DEBUG((DEBUG_ERROR, "TestPointSmmReadyToBootSmmPageProtectionHandler: GcdIoMapSize invalid!\n")); goto Done; } if (CommData->UefiMemoryAttributeTableOffset != CommData->GcdIoMapOffset + CommData->GcdIoMapSize) { DEBUG((DEBUG_ERROR, "TestPointSmmReadyToBootSmmPageProtectionHandler: UefiMemoryAttributeTableOffset invalid!\n")); goto Done; } if (CommData->UefiMemoryAttributeTableSize != TempCommBufferSize - CommData->UefiMemoryAttributeTableOffset) { DEBUG((DEBUG_ERROR, "TestPointSmmReadyToBootSmmPageProtectionHandler: UefiMemoryAttributeTableSize invalid!\n")); goto Done; } if (CommData->UefiMemoryMapSize != 0) { // // The SpeculationBarrier() call here is to ensure the previous range/content // checks for the CommBuffer (copied in to CommData) have been completed before // calling into TestPointCheckSmmCommunicationBuffer(). // SpeculationBarrier (); Result = TRUE; Status = TestPointCheckSmmCommunicationBuffer ( (EFI_MEMORY_DESCRIPTOR *)(UINTN)((UINTN)CommData + CommData->UefiMemoryMapOffset), (UINTN)CommData->UefiMemoryMapSize, mUefiDescriptorSize, (CommData->UefiMemoryAttributeTableSize != 0) ? (EFI_MEMORY_ATTRIBUTES_TABLE *)(UINTN)((UINTN)CommData + CommData->UefiMemoryAttributeTableOffset) : NULL ); if (EFI_ERROR(Status)) { Result = FALSE; } if (Result) { TestPointLibSetFeaturesVerified ( PLATFORM_TEST_POINT_ROLE_PLATFORM_IBV, NULL, TEST_POINT_INDEX_BYTE6_SMM, TEST_POINT_BYTE6_SMM_READY_TO_LOCK_SECURE_SMM_COMMUNICATION_BUFFER ); } else { TestPointLibClearFeaturesVerified ( PLATFORM_TEST_POINT_ROLE_PLATFORM_IBV, NULL, TEST_POINT_INDEX_BYTE6_SMM, TEST_POINT_BYTE6_SMM_READY_TO_LOCK_SECURE_SMM_COMMUNICATION_BUFFER ); } } Done: FreePool (CommData); DEBUG ((DEBUG_INFO, "======== TestPointSmmReadyToBootSmmPageProtectionHandler - Exit\n")); return EFI_SUCCESS; } /** Dispatch function for a Software SMI handler. Caution: This function may receive untrusted input. Communicate buffer and buffer size are external input, so this function will do basic validation. @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). @param Context Points to an optional handler context which was specified when the handler was registered. @param CommBuffer A pointer to a collection of data in memory that will be conveyed from a non-SMM environment into an SMM environment. @param CommBufferSize The size of the CommBuffer. @retval EFI_SUCCESS Command is handled successfully. **/ EFI_STATUS EFIAPI TestPointSmmHandler ( IN EFI_HANDLE DispatchHandle, IN CONST VOID *Context OPTIONAL, IN OUT VOID *CommBuffer OPTIONAL, IN OUT UINTN *CommBufferSize OPTIONAL ) { TEST_POINT_SMM_COMMUNICATION_HEADER CommData; UINTN TempCommBufferSize; // // If input is invalid, stop processing this SMI // if (CommBuffer == NULL || CommBufferSize == NULL) { return EFI_SUCCESS; } TempCommBufferSize = *CommBufferSize; if (TempCommBufferSize < sizeof(TEST_POINT_SMM_COMMUNICATION_HEADER)) { DEBUG((DEBUG_ERROR, "TestPointSmmHandler: SMM communication buffer size invalid!\n")); return EFI_SUCCESS; } CopyMem (&CommData, CommBuffer, sizeof(CommData)); if (CommData.Version != TEST_POINT_SMM_COMMUNICATION_VERSION) { DEBUG((DEBUG_ERROR, "TestPointSmmHandler: SMM communication Version invalid!\n")); return EFI_SUCCESS; } switch (CommData.FuncId) { case TEST_POINT_SMM_COMMUNICATION_FUNC_ID_UEFI_GCD_MAP_INFO: return TestPointSmmReadyToBootSmmPageProtectionHandler (CommBuffer, CommBufferSize); } return EFI_SUCCESS; } /** This service verifies the system state within SMM after Exit Boot Services is invoked. @retval EFI_SUCCESS The test point check was performed successfully. **/ EFI_STATUS EFIAPI TestPointSmmExitBootServices ( VOID ) { DEBUG ((DEBUG_INFO, "======== TestPointSmmExitBootServices - Enter\n")); DEBUG ((DEBUG_INFO, "======== TestPointSmmExitBootServices - Exit\n")); return EFI_SUCCESS; } /** Register SMM Test Point handler. **/ VOID RegisterSmmTestPointHandler ( VOID ) { EFI_STATUS Status; EFI_HANDLE DispatchHandle; Status = gSmst->SmiHandlerRegister ( TestPointSmmHandler, &mTestPointSmmCommunciationGuid, &DispatchHandle ); ASSERT_EFI_ERROR (Status); } /** Initialize feature data. @param[in] Role The test point role being requested. **/ VOID InitData ( IN UINT32 Role ) { EFI_STATUS Status; ASSERT (PcdGetSize(PcdTestPointIbvPlatformFeature) == sizeof(mFeatureImplemented)); CopyMem (mFeatureImplemented, PcdGetPtr(PcdTestPointIbvPlatformFeature), sizeof(mFeatureImplemented)); mTestPointStruct.Role = Role; CopyMem (mTestPointStruct.FeaturesImplemented, mFeatureImplemented, sizeof(mFeatureImplemented)); Status = TestPointLibSetTable ( &mTestPointStruct, sizeof(mTestPointStruct) ); if (EFI_ERROR (Status)) { if (Status != EFI_ALREADY_STARTED) { ASSERT_EFI_ERROR (Status); } } } /** The library constructuor. The function does the necessary initialization work for this library instance. @param[in] ImageHandle The firmware allocated handle for the UEFI image. @param[in] SystemTable A pointer to the EFI system table. @retval EFI_SUCCESS The function always return EFI_SUCCESS. **/ EFI_STATUS EFIAPI SmmTestPointCheckLibConstructor ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { InitData (PLATFORM_TEST_POINT_ROLE_PLATFORM_IBV); RegisterSmmTestPointHandler (); return EFI_SUCCESS; }