/** @file PCH Port 61h bit-4 toggling in SMM IO Trap Copyright (c) 2017, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "PchInitSmm.h" GLOBAL_REMOVE_IF_UNREFERENCED EFI_SMM_CPU_PROTOCOL *mSmmCpu; ///< To read and write save state GLOBAL_REMOVE_IF_UNREFERENCED EFI_HANDLE mPchIoTrapHandle; ///< Pause and resume via EFI_HANDLE GLOBAL_REMOVE_IF_UNREFERENCED PCH_SMM_IO_TRAP_CONTROL_PROTOCOL *mPchSmmIoTrapControl; ///< To pause and resume IO trap GLOBAL_REMOVE_IF_UNREFERENCED UINTN mInternalState = 0; ///< To store the toggle value for each CPU threads /** This SMI handle will pause the IO trap in the beginning @param[in] DispatchHandle - The handle of this callback, obtained when registering @param[in] DispatchContext - Pointer to the EFI_SMM_IO_TRAP_DISPATCH_CALLBACK_CONTEXT @retval None **/ VOID PchIoTrapPort61hSmiCallback ( IN EFI_HANDLE DispatchHandle, IN CONST VOID *CallbackContext, IN OUT VOID *CommBuffer, IN OUT UINTN *CommBufferSize ) { EFI_STATUS Status; UINTN CpuIndex; UINTN Mask; UINT8 ReadValue, WriteValue; EFI_SMM_SAVE_STATE_IO_INFO IoInfo; // // Disabling the IO Trap, prevent SMI re-enter because of reading port 61h in handler itself // Status = mPchSmmIoTrapControl->Pause ( mPchSmmIoTrapControl, mPchIoTrapHandle ); ASSERT_EFI_ERROR (Status); // // Detects the CPU which causes the IO Trap // for (CpuIndex = 0; CpuIndex < gSmst->NumberOfCpus; CpuIndex++) { Status = mSmmCpu->ReadSaveState ( mSmmCpu, sizeof (EFI_SMM_SAVE_STATE_IO_INFO), EFI_SMM_SAVE_STATE_REGISTER_IO, CpuIndex, &IoInfo ); if (Status == EFI_SUCCESS) { // // Make sure the IO trap is byte read, port input only, and it is port 0x61 // if ((IoInfo.IoWidth == EFI_SMM_SAVE_STATE_IO_WIDTH_UINT8) && (IoInfo.IoType == EFI_SMM_SAVE_STATE_IO_TYPE_INPUT) && (IoInfo.IoPort == 0x61)) { // // Read from Port 61h, toggle bit4 based on the internal state // ReadValue = IoRead8 (0x61); Mask = (UINTN) (1 << CpuIndex); mInternalState ^= Mask; WriteValue = (mInternalState & Mask) ? (ReadValue | 0x10) : (ReadValue & ~0x10); Status = mSmmCpu->WriteSaveState ( mSmmCpu, sizeof (UINT8), EFI_SMM_SAVE_STATE_REGISTER_RAX, CpuIndex, &WriteValue ); ASSERT_EFI_ERROR (Status); } //if the iotrap is the one we want } //if iotrap happens for this thread } //for (iterate for all threads) // // Resume the IO Trap // Port 61h bit-4 emulation will be disabled after exit boot service // The disabling mechanism is actually to not resume the trap. // This means, even after exit boot service, this handler will be called once // before it is disabled // if (mPchNvsArea->ExitBootServicesFlag == 0) { Status = mPchSmmIoTrapControl->Resume ( mPchSmmIoTrapControl, mPchIoTrapHandle ); ASSERT_EFI_ERROR (Status); } else { DEBUG ((DEBUG_INFO, "Port61H trap has been disabled. \n")); } } /** Locate required protocol and register the IO trap in this entry point @param[in] ImageHandle - Handle for the image of this driver @param[in] SystemTable - Pointer to the EFI System Table @retval EFI_SUCCESS - PCH SMM handler was installed **/ EFI_STATUS EFIAPI InstallIoTrapPort61h ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_SMM_IO_TRAP_REGISTER_CONTEXT PchIoTrapContext; DEBUG ((DEBUG_INFO, "InstallIoTrapPort61h()\n")); if (mPchConfigHob->Port61hSmm.Enable == 0) { return EFI_SUCCESS; } // // Locate the protocol to read and write save state // Status = gSmst->SmmLocateProtocol (&gEfiSmmCpuProtocolGuid, NULL, (VOID **) &mSmmCpu); ASSERT_EFI_ERROR (Status); // // Locate the protocol for pause and resume // Status = gSmst->SmmLocateProtocol (&gPchSmmIoTrapControlGuid, NULL, (VOID **) &mPchSmmIoTrapControl); ASSERT_EFI_ERROR (Status); // // Configure trap type, in ECP, MergeDisable should be TRUE for pause and resume to work // PchIoTrapContext.Type = ReadTrap; PchIoTrapContext.Length = 1; PchIoTrapContext.Address = 0x61; Status = mPchIoTrap->Register ( mPchIoTrap, (EFI_SMM_HANDLER_ENTRY_POINT2) PchIoTrapPort61hSmiCallback, &PchIoTrapContext, &mPchIoTrapHandle ); ASSERT_EFI_ERROR (Status); return EFI_SUCCESS; }