/** @file PnpSmm driver initialization ;****************************************************************************** ;* Copyright (c) 2012 - 2021, Insyde Software Corp. All Rights Reserved. ;* ;* You may not reproduce, distribute, publish, display, perform, modify, adapt, ;* transmit, broadcast, present, recite, release, license or otherwise exploit ;* any part of this publication in any form, by any means, without the prior ;* written permission of Insyde Software Corporation. ;* ;****************************************************************************** */ #include "PnpSmm.h" #include #include #include // for GPNV #include #include #define FixedPcdGetPtrSize(TokenName) (UINTN)_PCD_PATCHABLE_##TokenName##_SIZE UINT8 *mSmmSMBIOSPnpBuffer = NULL; EFI_SMM_SYSTEM_TABLE2 *mSmst; EFI_SMM_FW_BLOCK_SERVICE_PROTOCOL *mSmmFwBlockService; H2O_IHISI_PROTOCOL *mH2OIhisiPnp = NULL; PLATFORM_GPNV_MAP_BUFFER GPNVMapBuffer; PLATFORM_GPNV_HEADER *GPNVHeader = NULL; UINTN UpdateableStringCount; DMI_UPDATABLE_STRING *mUpdatableStrings; UINTN OemGPNVHandleCount = 0; BOOLEAN mIsOemGPNVMap = FALSE; BOOLEAN mLegacyBoot = FALSE; BOOLEAN mSmmSmbiosEndOfDxe = FALSE; typedef struct { UINT8 Type; BOOLEAN Updatable; UINTN StringNum; UINT8 StringOffset[1]; } MODIFY_UPDATABLE_INFO; /** 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. **/ EFI_STATUS EFIAPI SmbiosSmmEndOfDxe ( IN CONST EFI_GUID *Protocol, IN VOID *Interface, IN EFI_HANDLE Handle ) { mSmmSmbiosEndOfDxe = TRUE; return EFI_SUCCESS; } /** This callback fucntion gets the SMBIOS table entry point and pass into SmmSmbiosPnpHandler. @param[In] Event Event type @param[In] Context Context for the event **/ VOID EFIAPI GetSmbiosEntryCallback ( IN EFI_EVENT Event, IN VOID *Cotext ) { EFI_STATUS Status; EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateBufHeader; SMM_SMBIOS_PNP_COMMUNICATE_HEADER *SmmSMBIOSPnpHeader; EFI_SMM_COMMUNICATION_PROTOCOL *SmmCommunication; EFI_PHYSICAL_ADDRESS SMBIOSTableEntryAddress; EFI_PHYSICAL_ADDRESS SMBIOSTableEntryAddress64Bit; SMM_SMBIOS_PNP_ADDRESS *SmmSMBIOSPnpAddress; UINTN CommSize; gBS->CloseEvent (Event); // // Initial Communication buffer // SmmCommunicateBufHeader = (EFI_SMM_COMMUNICATE_HEADER *) mSmmSMBIOSPnpBuffer; CopyGuid (&SmmCommunicateBufHeader->HeaderGuid, &gH2OSmmPnPCommunicationGuid); SmmCommunicateBufHeader->MessageLength = (SMM_SMBIOS_PNP_COMM_BUFF_SIZE - OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data)); SmmSMBIOSPnpHeader = (SMM_SMBIOS_PNP_COMMUNICATE_HEADER *) SmmCommunicateBufHeader->Data; SmmSMBIOSPnpHeader->Function = SMM_COMM_SMBIOS_PNP_ENTRY_SET; SmmSMBIOSPnpAddress = (SMM_SMBIOS_PNP_ADDRESS *)SmmSMBIOSPnpHeader->Data; // // Need to pass SMBIOS table entry point into SmmSmbiosPnpHandler. // Status = EFI_SUCCESS; SMBIOSTableEntryAddress = 0; SMBIOSTableEntryAddress64Bit = 0; if (((PcdGet16 (PcdSmbiosVersion) >> 8) < 0x3) || (((PcdGet16 (PcdSmbiosVersion) >> 8) >= 0x3) && ((PcdGet32 (PcdSmbiosEntryPointProvideMethod) & BIT0) == BIT0))) { Status = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID **) &SMBIOSTableEntryAddress); } ASSERT_EFI_ERROR (Status); if (((PcdGet16 (PcdSmbiosVersion) >> 8) >= 0x3) && ((PcdGet32 (PcdSmbiosEntryPointProvideMethod) & BIT1) == BIT1)) { Status = EfiGetSystemConfigurationTable (&gEfiSmbios3TableGuid, (VOID **) &SMBIOSTableEntryAddress64Bit); } ASSERT_EFI_ERROR (Status); SmmSMBIOSPnpAddress->Address = SMBIOSTableEntryAddress; SmmSMBIOSPnpAddress->Address64Bit = SMBIOSTableEntryAddress64Bit; Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &SmmCommunication); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { return; } CommSize = SMM_SMBIOS_PNP_COMM_BUFF_SIZE; Status = SmmCommunication->Communicate (SmmCommunication, mSmmSMBIOSPnpBuffer, &CommSize); ASSERT_EFI_ERROR (Status); } EFI_STATUS FillPlatformGPNVMapBuffer ( IN OEM_GPNV_MAP *GPNVMap ) { UINTN Index; if (OemGPNVHandleCount >= FixedPcdGet16 (PcdDefaultGpnvMapBufferSize)) { return EFI_INVALID_PARAMETER; } for (Index = 0; Index < OemGPNVHandleCount; Index++) { GPNVMapBuffer.PlatFormGPNVMap[Index].Handle = GPNVMap->Handle; GPNVMapBuffer.PlatFormGPNVMap[Index].MinGPNVSize = GPNVMap->GPNVSize; GPNVMapBuffer.PlatFormGPNVMap[Index].GPNVSize = GPNVMap->GPNVSize; GPNVMapBuffer.PlatFormGPNVMap[Index].BaseAddress = GPNVMap->GPNVBaseAddress; ++GPNVMap; } return EFI_SUCCESS; } /** SMM Legacy boot callback to indicate gLegacyBootProtocolGuid event is signaled @retval EFI_SUCCESS: Locate EFI_SMM_SW_DISPATCH2_PROTOCOL success @retval Others EFI_SMM_SW_DISPATCH2_PROTOCOL not installed **/ STATIC EFI_STATUS EFIAPI SmmLegacyBootCallback ( IN CONST EFI_GUID *Protocol, IN VOID *Interface, IN EFI_HANDLE Handle ) { mLegacyBoot = TRUE; return EFI_SUCCESS; } /** Entry point for SmmPnp drivers. @param[In] ImageHandle EFI_HANDLE @param[In] SystemTable EFI_SYSTEM_TABLE @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR Fail **/ EFI_STATUS EFIAPI SmmPnpInit ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_HANDLE SwHandle; EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch; EFI_SMM_SW_REGISTER_CONTEXT SwContext; EFI_EVENT SmbiosGetTableEvent; DMI_UPDATABLE_STRING *TempStringTable; OEM_GPNV_MAP *OemGPNVMap; EFI_SMM_BASE2_PROTOCOL *SmmBase; BOOLEAN InSmm; BOOLEAN NeedFreePnpTable; EFI_PCD_PROTOCOL *Pcd = NULL; EFI_HANDLE DispatchHandle; VOID *Registration; VOID *LegacyBootRegistration; EFI_HANDLE Handle; SmmBase = NULL; TempStringTable = NULL; Status = gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID **)&SmmBase); InSmm = FALSE; if (!EFI_ERROR (Status)) { SmmBase->InSmm (SmmBase, &InSmm); } if (InSmm) { Status = SmmBase->GetSmstLocation (SmmBase, &mSmst); if (EFI_ERROR (Status)) { return Status; } Status = mSmst->SmmLocateProtocol (&gEfiSmmFwBlockServiceProtocolGuid, NULL, (VOID **)&mSmmFwBlockService); // // Perfrom DMI reclaim if needed // Status = mSmst->SmmRegisterProtocolNotify ( &gEfiSmmEndOfDxeProtocolGuid, SmbiosSmmEndOfDxe, &Registration ); if (!EFI_ERROR(Status)) { DmiReclaimBeforeEndOfDxe(); } UpdateableStringCount = 0; mUpdatableStrings = NULL; // // OemServices (Smm) // NeedFreePnpTable = TRUE; DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices Call: OemSvcSmmInstallPnpStringTable \n")); Status = OemSvcSmmInstallPnpStringTable ( &UpdateableStringCount, &TempStringTable ); DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices OemSvcSmmInstallPnpStringTable Status: %r\n", Status)); if (Status == EFI_UNSUPPORTED) { Status = gBS->LocateProtocol (&gEfiPcdProtocolGuid, NULL, (VOID **) &Pcd); if (EFI_ERROR(Status)) { return Status; } NeedFreePnpTable = FALSE; TempStringTable = PcdGetPtr (PcdSmbiosUpdatableStringTable); UpdateableStringCount = FixedPcdGetPtrSize (PcdSmbiosUpdatableStringTable) / sizeof (DMI_UPDATABLE_STRING); } if ((TempStringTable != NULL) && (UpdateableStringCount != 0)) { mUpdatableStrings = AllocateRuntimePool (UpdateableStringCount * sizeof (DMI_UPDATABLE_STRING)); if (mUpdatableStrings != NULL) { CopyMem (mUpdatableStrings, TempStringTable, UpdateableStringCount * sizeof (DMI_UPDATABLE_STRING)); } if (NeedFreePnpTable) { FreePool (TempStringTable); } } // // OemServices (Smm) // OemGPNVHandleCount = 0; OemGPNVMap = NULL; mIsOemGPNVMap = FALSE; DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices Call: OemSvcSmmInstallPnpGpnvTable \n")); Status = OemSvcSmmInstallPnpGpnvTable (&OemGPNVHandleCount, &OemGPNVMap); DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices OemSvcSmmInstallPnpGpnvTable Status: %r\n", Status)); if (Status == EFI_MEDIA_CHANGED && OemGPNVMap != NULL) { Status = FillPlatformGPNVMapBuffer (OemGPNVMap); ASSERT_EFI_ERROR (Status); if (!EFI_ERROR (Status)) { mIsOemGPNVMap = TRUE; } } Status = mSmst->SmmRegisterProtocolNotify ( &gEdkiiSmmLegacyBootProtocolGuid, SmmLegacyBootCallback, &LegacyBootRegistration ); ASSERT_EFI_ERROR (Status); // // Get the Sw dispatch protocol // Status = mSmst->SmmLocateProtocol (&gEfiSmmSwDispatch2ProtocolGuid, NULL, (VOID **)&SwDispatch); ASSERT_EFI_ERROR (Status); // // Register SMBIOS call SMI function // SwContext.SwSmiInputValue = SMM_PnP_BIOS_CALL; SwHandle = NULL; Status = SwDispatch->Register (SwDispatch, PnPBiosCallback, &SwContext, &SwHandle); ASSERT_EFI_ERROR (Status); Status = gBS->LocateProtocol ( &gEfiCallerIdGuid, NULL, (VOID **) &mSmmSMBIOSPnpBuffer ); // // Register SMBIOS PnP SMI Communication handler // Status = mSmst->SmiHandlerRegister ( SmmSmbiosPnpHandler, &gH2OSmmPnPCommunicationGuid, &DispatchHandle ); ASSERT_EFI_ERROR (Status); } else { // // This event will be triggered after SMBIOS table entry point is ready. // So, we can get the address of entry point and save it. // Status = EfiCreateEventReadyToBootEx ( TPL_CALLBACK - 1, GetSmbiosEntryCallback, NULL, &SmbiosGetTableEvent ); mSmmSMBIOSPnpBuffer = AllocateReservedPool (SMM_SMBIOS_PNP_COMM_BUFF_SIZE); if (mSmmSMBIOSPnpBuffer == NULL) { return EFI_OUT_OF_RESOURCES; } Handle = NULL; Status = gBS->InstallMultipleProtocolInterfaces ( &Handle, &gEfiCallerIdGuid, mSmmSMBIOSPnpBuffer, NULL ); if (EFI_ERROR (Status)) { return Status; } } return EFI_SUCCESS; } /** Check the input memory buffer whether lies within the command buffer.. @param[in] Buffer Pointer to the first byte in a buffer. @param[in] BufferSize Unsigned integer that specifies the size of the Buffer, in bytes. @retval TURE Buffer lies within the command buffer. @retval FALSE Buffer lies outside the command buffer. **/ BOOLEAN EFIAPI BufferInCmdBufferPnp ( IN VOID *Buffer, IN UINTN BufferSize ) { EFI_STATUS Status; if (mLegacyBoot) { return TRUE; } if (mH2OIhisiPnp == NULL) { Status = mSmst->SmmLocateProtocol ( &gH2OIhisiProtocolGuid, NULL, (VOID **) &mH2OIhisiPnp ); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { return TRUE; } } return mH2OIhisiPnp->BufferInCmdBuffer((VOID *) Buffer, BufferSize); } /** Check the input memory buffer is whether overlap the SMRAM ranges. @param[in] Buffer The pointer to the buffer to be checked. @param[in] BufferSize The size in bytes of the input buffer @retval TURE The buffer overlaps SMRAM ranges. @retval FALSE The buffer doesn't overlap SMRAM ranges. **/ BOOLEAN EFIAPI BufferOverlapSmramPnp ( IN VOID *Buffer, IN UINTN BufferSize ) { EFI_STATUS Status; if (mH2OIhisiPnp == NULL) { Status = mSmst->SmmLocateProtocol ( &gH2OIhisiProtocolGuid, NULL, (VOID **) &mH2OIhisiPnp ); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { return TRUE; } } if (mH2OIhisiPnp->BufferOverlapSmram((VOID *) Buffer, BufferSize)) { return TRUE; } return FALSE; }