/** @file Copyright (c) 2017, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "SmmTestPoint.h" EFI_STATUS GetAllSmmTestPointData ( IN OUT UINTN *DataSize, IN OUT VOID *Data ) { EFI_STATUS Status; EFI_ADAPTER_INFORMATION_PROTOCOL *Aip; UINTN NoHandles; EFI_HANDLE *Handles; UINTN HandleBufSize; UINTN Index; EFI_GUID *InfoTypesBuffer; UINTN InfoTypesBufferCount; UINTN InfoTypesIndex; EFI_ADAPTER_INFORMATION_PROTOCOL *AipCandidate; VOID *InformationBlock; UINTN InformationBlockSize; UINTN TotalSize; EFI_STATUS RetStatus; TotalSize = 0; Handles = NULL; HandleBufSize = 0; Status = gSmst->SmmLocateHandle ( ByProtocol, &gEfiAdapterInformationProtocolGuid, NULL, &HandleBufSize, Handles ); if (Status != EFI_BUFFER_TOO_SMALL) { RetStatus = EFI_NOT_FOUND; goto Done ; } Handles = AllocateZeroPool (HandleBufSize); if (Handles == NULL) { RetStatus = EFI_OUT_OF_RESOURCES; goto Done ; } Status = gSmst->SmmLocateHandle ( ByProtocol, &gEfiAdapterInformationProtocolGuid, NULL, &HandleBufSize, Handles ); if (EFI_ERROR (Status)) { RetStatus = Status; goto Done ; } NoHandles = HandleBufSize / sizeof(EFI_HANDLE); RetStatus = EFI_SUCCESS; Aip = NULL; InformationBlock = NULL; InformationBlockSize = 0; for (Index = 0; Index < NoHandles; Index++) { Status = gSmst->SmmHandleProtocol ( Handles[Index], &gEfiAdapterInformationProtocolGuid, (VOID **)&Aip ); if (EFI_ERROR (Status)) { continue; } // // Check AIP // Status = Aip->GetSupportedTypes ( Aip, &InfoTypesBuffer, &InfoTypesBufferCount ); if (EFI_ERROR (Status)) { continue; } AipCandidate = NULL; for (InfoTypesIndex = 0; InfoTypesIndex < InfoTypesBufferCount; InfoTypesIndex++) { if (CompareGuid (&InfoTypesBuffer[InfoTypesIndex], &gAdapterInfoPlatformTestPointGuid)) { AipCandidate = Aip; break; } } FreePool (InfoTypesBuffer); if (AipCandidate == NULL) { continue; } // // Check Role // Aip = AipCandidate; Status = Aip->GetInformation ( Aip, &gAdapterInfoPlatformTestPointGuid, &InformationBlock, &InformationBlockSize ); if (EFI_ERROR (Status)) { continue; } if ((Data != NULL) && (TotalSize + InformationBlockSize <= *DataSize)) { CopyMem ((UINT8 *)Data + TotalSize, InformationBlock, InformationBlockSize); } else { RetStatus = EFI_BUFFER_TOO_SMALL; } TotalSize += InformationBlockSize; FreePool (InformationBlock); } Done: *DataSize = TotalSize; if (Handles != NULL) { FreePool (Handles); } return RetStatus; } /** SMM test point SMI handler to get info. @param SmiHandlerTestPointParameterGetInfo The parameter of SMM test point SMI handler get info. **/ VOID SmmTestPointSmiHandlerGetInfo ( IN SMI_HANDLER_TEST_POINT_PARAMETER_GET_INFO *SmiHandlerTestPointParameterGetInfo ) { UINTN DataSize; EFI_STATUS Status; DataSize = 0; Status = GetAllSmmTestPointData (&DataSize, NULL); if (Status == EFI_BUFFER_TOO_SMALL) { SmiHandlerTestPointParameterGetInfo->DataSize = DataSize; SmiHandlerTestPointParameterGetInfo->Header.ReturnStatus = 0; } else { SmiHandlerTestPointParameterGetInfo->DataSize = 0; SmiHandlerTestPointParameterGetInfo->Header.ReturnStatus = (UINT64)(INT64)(INTN)EFI_NOT_FOUND; } } /** Copy SMM Test Point data. @param DataBuffer The buffer to hold SMM Test Point data. @param DataSize On input, data buffer size. On output, actual data buffer size copied. @param DataOffset On input, data buffer offset to copy. On output, next time data buffer offset to copy. **/ VOID SmiHandlerTestPointCopyData ( IN VOID *InputData, IN UINTN InputDataSize, OUT VOID *DataBuffer, IN OUT UINT64 *DataSize, IN OUT UINT64 *DataOffset ) { if (*DataOffset >= InputDataSize) { *DataOffset = InputDataSize; return; } if (InputDataSize - *DataOffset < *DataSize) { *DataSize = InputDataSize - *DataOffset; } CopyMem( DataBuffer, (UINT8 *)InputData + *DataOffset, (UINTN)*DataSize ); *DataOffset = *DataOffset + *DataSize; } /** SMM test point SMI handler to get data by offset. @param SmiHandlerTestPointParameterGetDataByOffset The parameter of SMM test point SMI handler get data by offset. **/ VOID SmmTestPointSmiHandlerGetDataByOffset ( IN SMI_HANDLER_TEST_POINT_PARAMETER_GET_DATA_BY_OFFSET *SmiHandlerTestPointParameterGetDataByOffset ) { SMI_HANDLER_TEST_POINT_PARAMETER_GET_DATA_BY_OFFSET SmiHandlerTestPointGetDataByOffset; VOID *Data; UINTN DataSize; EFI_STATUS Status; Data = NULL; CopyMem ( &SmiHandlerTestPointGetDataByOffset, SmiHandlerTestPointParameterGetDataByOffset, sizeof(SmiHandlerTestPointGetDataByOffset) ); // // Sanity check // if (!SmmIsBufferOutsideSmmValid((UINTN)SmiHandlerTestPointGetDataByOffset.DataBuffer, (UINTN)SmiHandlerTestPointGetDataByOffset.DataSize)) { DEBUG((DEBUG_ERROR, "SmmTestPointSmiHandlerGetDataByOffset: SmmTestPoint get data in SMRAM or overflow!\n")); SmiHandlerTestPointParameterGetDataByOffset->Header.ReturnStatus = (UINT64)(INT64)(INTN)EFI_ACCESS_DENIED; goto Done; } DataSize = 0; Status = GetAllSmmTestPointData (&DataSize, NULL); if (Status != EFI_BUFFER_TOO_SMALL) { SmiHandlerTestPointParameterGetDataByOffset->Header.ReturnStatus = (UINT64)(INT64)(INTN)EFI_NOT_FOUND; goto Done; } Data = AllocatePool (DataSize); if (Data == NULL) { SmiHandlerTestPointParameterGetDataByOffset->Header.ReturnStatus = (UINT64)(INT64)(INTN)EFI_OUT_OF_RESOURCES; goto Done; } Status = GetAllSmmTestPointData (&DataSize, Data); if (EFI_ERROR(Status)) { SmiHandlerTestPointParameterGetDataByOffset->Header.ReturnStatus = (UINT64)(INT64)(INTN)Status; goto Done; } // // The SpeculationBarrier() call here is to ensure the previous range/content // checks for the CommBuffer have been completed before calling into // SmiHandlerTestPointCopyData(). // SpeculationBarrier (); SmiHandlerTestPointCopyData ( Data, DataSize, (VOID *)(UINTN)SmiHandlerTestPointGetDataByOffset.DataBuffer, &SmiHandlerTestPointGetDataByOffset.DataSize, &SmiHandlerTestPointGetDataByOffset.DataOffset ); CopyMem ( SmiHandlerTestPointParameterGetDataByOffset, &SmiHandlerTestPointGetDataByOffset, sizeof(SmiHandlerTestPointGetDataByOffset) ); SmiHandlerTestPointParameterGetDataByOffset->Header.ReturnStatus = 0; Done: if (Data != NULL) { FreePool (Data); } } /** 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 SmmTestPointSmiHandler ( IN EFI_HANDLE DispatchHandle, IN CONST VOID *Context OPTIONAL, IN OUT VOID *CommBuffer OPTIONAL, IN OUT UINTN *CommBufferSize OPTIONAL ) { SMI_HANDLER_TEST_POINT_PARAMETER_HEADER *SmiHandlerTestPointParameterHeader; UINTN TempCommBufferSize; DEBUG((DEBUG_ERROR, "SmmTestPointSmiHandler Enter\n")); // // If input is invalid, stop processing this SMI // if (CommBuffer == NULL || CommBufferSize == NULL) { return EFI_SUCCESS; } TempCommBufferSize = *CommBufferSize; if (TempCommBufferSize < sizeof(SMI_HANDLER_TEST_POINT_PARAMETER_HEADER)) { DEBUG((DEBUG_ERROR, "SmmTestPointSmiHandler: SMM communication buffer size invalid!\n")); return EFI_SUCCESS; } if (!SmmIsBufferOutsideSmmValid((UINTN)CommBuffer, TempCommBufferSize)) { DEBUG((DEBUG_ERROR, "SmmTestPointSmiHandler: SMM communication buffer in SMRAM or overflow!\n")); return EFI_SUCCESS; } SmiHandlerTestPointParameterHeader = (SMI_HANDLER_TEST_POINT_PARAMETER_HEADER *)((UINTN)CommBuffer); SmiHandlerTestPointParameterHeader->ReturnStatus = (UINT64)-1; switch (SmiHandlerTestPointParameterHeader->Command) { case SMI_HANDLER_TEST_POINT_COMMAND_GET_INFO: DEBUG((DEBUG_ERROR, "SmiHandlerTestPointHandlerGetInfo\n")); if (TempCommBufferSize != sizeof(SMI_HANDLER_TEST_POINT_PARAMETER_GET_INFO)) { DEBUG((DEBUG_ERROR, "SmmTestPointSmiHandler: SMM communication buffer size invalid!\n")); return EFI_SUCCESS; } SmmTestPointSmiHandlerGetInfo((SMI_HANDLER_TEST_POINT_PARAMETER_GET_INFO *)(UINTN)CommBuffer); break; case SMI_HANDLER_TEST_POINT_COMMAND_GET_DATA_BY_OFFSET: DEBUG((DEBUG_ERROR, "SmiHandlerTestPointHandlerGetDataByOffset\n")); if (TempCommBufferSize != sizeof(SMI_HANDLER_TEST_POINT_PARAMETER_GET_DATA_BY_OFFSET)) { DEBUG((DEBUG_ERROR, "SmmTestPointSmiHandler: SMM communication buffer size invalid!\n")); return EFI_SUCCESS; } SmmTestPointSmiHandlerGetDataByOffset((SMI_HANDLER_TEST_POINT_PARAMETER_GET_DATA_BY_OFFSET *)(UINTN)CommBuffer); break; default: break; } DEBUG((DEBUG_ERROR, "SmmTestPointSmiHandler Exit\n")); return EFI_SUCCESS; } /** Register SMM TestPoint handler. **/ VOID RegisterSmmTestPointSmiHandler ( VOID ) { EFI_HANDLE DispatchHandle; EFI_STATUS Status; STATIC BOOLEAN Registered = FALSE; if (Registered) { return ; } Status = gSmst->SmiHandlerRegister ( SmmTestPointSmiHandler, &gAdapterInfoPlatformTestPointGuid, &DispatchHandle ); ASSERT_EFI_ERROR (Status); Registered = TRUE; }