/** @file A PEIM with the following responsibilities: - verify & configure the Q35 TSEG in the entry point, - provide SMRAM access by producing PEI_SMM_ACCESS_PPI This PEIM runs from RAM, so we can write to variables with static storage duration. Copyright (C) 2013, 2015, Red Hat, Inc.
Copyright (c) 2010 - 2024, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include "SmramInternal.h" // // PEI_SMM_ACCESS_PPI implementation. // /** Opens the SMRAM area to be accessible by a PEIM driver. This function "opens" SMRAM so that it is visible while not inside of SMM. The function should return EFI_UNSUPPORTED if the hardware does not support hiding of SMRAM. The function should return EFI_DEVICE_ERROR if the SMRAM configuration is locked. @param PeiServices General purpose services available to every PEIM. @param This The pointer to the SMM Access Interface. @param DescriptorIndex The region of SMRAM to Open. @retval EFI_SUCCESS The region was successfully opened. @retval EFI_DEVICE_ERROR The region could not be opened because locked by chipset. @retval EFI_INVALID_PARAMETER The descriptor index was out of bounds. **/ STATIC EFI_STATUS EFIAPI SmmAccessPeiOpen ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_SMM_ACCESS_PPI *This, IN UINTN DescriptorIndex ) { EFI_HOB_GUID_TYPE *GuidHob; EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *DescriptorBlock; // // Get the number of regions in the system that can be usable for SMRAM // GuidHob = GetFirstGuidHob (&gEfiSmmSmramMemoryGuid); DescriptorBlock = GET_GUID_HOB_DATA (GuidHob); ASSERT (DescriptorBlock); if (DescriptorIndex >= DescriptorBlock->NumberOfSmmReservedRegions) { return EFI_INVALID_PARAMETER; } // // According to current practice, DescriptorIndex is not considered at all, // beyond validating it. // return SmramAccessOpen (&This->LockState, &This->OpenState); } /** Inhibits access to the SMRAM. This function "closes" SMRAM so that it is not visible while outside of SMM. The function should return EFI_UNSUPPORTED if the hardware does not support hiding of SMRAM. @param PeiServices General purpose services available to every PEIM. @param This The pointer to the SMM Access Interface. @param DescriptorIndex The region of SMRAM to Close. @retval EFI_SUCCESS The region was successfully closed. @retval EFI_DEVICE_ERROR The region could not be closed because locked by chipset. @retval EFI_INVALID_PARAMETER The descriptor index was out of bounds. **/ STATIC EFI_STATUS EFIAPI SmmAccessPeiClose ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_SMM_ACCESS_PPI *This, IN UINTN DescriptorIndex ) { EFI_HOB_GUID_TYPE *GuidHob; EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *DescriptorBlock; // // Get the number of regions in the system that can be usable for SMRAM // GuidHob = GetFirstGuidHob (&gEfiSmmSmramMemoryGuid); DescriptorBlock = GET_GUID_HOB_DATA (GuidHob); ASSERT (DescriptorBlock); if (DescriptorIndex >= DescriptorBlock->NumberOfSmmReservedRegions) { return EFI_INVALID_PARAMETER; } // // According to current practice, DescriptorIndex is not considered at all, // beyond validating it. // return SmramAccessClose (&This->LockState, &This->OpenState); } /** Inhibits access to the SMRAM. This function prohibits access to the SMRAM region. This function is usually implemented such that it is a write-once operation. @param PeiServices General purpose services available to every PEIM. @param This The pointer to the SMM Access Interface. @param DescriptorIndex The region of SMRAM to Close. @retval EFI_SUCCESS The region was successfully locked. @retval EFI_DEVICE_ERROR The region could not be locked because at least one range is still open. @retval EFI_INVALID_PARAMETER The descriptor index was out of bounds. **/ STATIC EFI_STATUS EFIAPI SmmAccessPeiLock ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_SMM_ACCESS_PPI *This, IN UINTN DescriptorIndex ) { EFI_HOB_GUID_TYPE *GuidHob; EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *DescriptorBlock; // // Get the number of regions in the system that can be usable for SMRAM // GuidHob = GetFirstGuidHob (&gEfiSmmSmramMemoryGuid); DescriptorBlock = GET_GUID_HOB_DATA (GuidHob); ASSERT (DescriptorBlock); if (DescriptorIndex >= DescriptorBlock->NumberOfSmmReservedRegions) { return EFI_INVALID_PARAMETER; } // // According to current practice, DescriptorIndex is not considered at all, // beyond validating it. // return SmramAccessLock (&This->LockState, &This->OpenState); } /** Queries the memory controller for the possible regions that will support SMRAM. @param PeiServices General purpose services available to every PEIM. @param This The pointer to the SmmAccessPpi Interface. @param SmramMapSize The pointer to the variable containing size of the buffer to contain the description information. @param SmramMap The buffer containing the data describing the Smram region descriptors. @retval EFI_BUFFER_TOO_SMALL The user did not provide a sufficient buffer. @retval EFI_SUCCESS The user provided a sufficiently-sized buffer. **/ STATIC EFI_STATUS EFIAPI SmmAccessPeiGetCapabilities ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_SMM_ACCESS_PPI *This, IN OUT UINTN *SmramMapSize, IN OUT EFI_SMRAM_DESCRIPTOR *SmramMap ) { return SmramAccessGetCapabilities ( SmramMapSize, SmramMap ); } // // LockState and OpenState will be filled in by the entry point. // STATIC PEI_SMM_ACCESS_PPI mAccess = { &SmmAccessPeiOpen, &SmmAccessPeiClose, &SmmAccessPeiLock, &SmmAccessPeiGetCapabilities }; STATIC EFI_PEI_PPI_DESCRIPTOR mPpiList[] = { { EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gPeiSmmAccessPpiGuid, &mAccess } }; // // Utility functions. // STATIC UINT8 CmosRead8 ( IN UINT8 Index ) { IoWrite8 (0x70, Index); return IoRead8 (0x71); } STATIC UINT32 GetSystemMemorySizeBelow4gb ( VOID ) { UINT32 Cmos0x34; UINT32 Cmos0x35; Cmos0x34 = CmosRead8 (0x34); Cmos0x35 = CmosRead8 (0x35); return ((Cmos0x35 << 8 | Cmos0x34) << 16) + SIZE_16MB; } // // Entry point of this driver. // EFI_STATUS EFIAPI SmmAccessPeiEntryPoint ( IN EFI_PEI_FILE_HANDLE FileHandle, IN CONST EFI_PEI_SERVICES **PeiServices ) { UINT16 HostBridgeDevId; UINT8 EsmramcVal; UINT8 RegMask8; UINT32 TopOfLowRam, TopOfLowRamMb; // // This module should only be included if SMRAM support is required. // ASSERT (FeaturePcdGet (PcdSmmSmramRequire)); // // Verify if we're running on a Q35 machine type. // HostBridgeDevId = PciRead16 (OVMF_HOSTBRIDGE_DID); if (HostBridgeDevId != INTEL_Q35_MCH_DEVICE_ID) { DEBUG (( DEBUG_ERROR, "%a: no SMRAM with host bridge DID=0x%04x; only " "DID=0x%04x (Q35) is supported\n", __func__, HostBridgeDevId, INTEL_Q35_MCH_DEVICE_ID )); goto WrongConfig; } // // Confirm if QEMU supports SMRAM. // // With no support for it, the ESMRAMC (Extended System Management RAM // Control) register reads as zero. If there is support, the cache-enable // bits are hard-coded as 1 by QEMU. // EsmramcVal = PciRead8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC)); RegMask8 = MCH_ESMRAMC_SM_CACHE | MCH_ESMRAMC_SM_L1 | MCH_ESMRAMC_SM_L2; if ((EsmramcVal & RegMask8) != RegMask8) { DEBUG (( DEBUG_ERROR, "%a: this Q35 implementation lacks SMRAM\n", __func__ )); goto WrongConfig; } TopOfLowRam = GetSystemMemorySizeBelow4gb (); ASSERT ((TopOfLowRam & (SIZE_1MB - 1)) == 0); TopOfLowRamMb = TopOfLowRam >> 20; // // Some of the following registers are no-ops for QEMU at the moment, but it // is recommended to set them correctly, since the ESMRAMC that we ultimately // care about is in the same set of registers. // // First, we disable the integrated VGA, and set both the GTT Graphics Memory // Size and the Graphics Mode Select memory pre-allocation fields to zero. // This takes just one write to the Graphics Control Register. // PciWrite16 (DRAMC_REGISTER_Q35 (MCH_GGC), MCH_GGC_IVD); // // Set Top of Low Usable DRAM. // PciWrite16 ( DRAMC_REGISTER_Q35 (MCH_TOLUD), (UINT16)(TopOfLowRamMb << MCH_TOLUD_MB_SHIFT) ); // // Given the zero graphics memory sizes configured above, set the // graphics-related stolen memory bases to the same as TOLUD. // PciWrite32 ( DRAMC_REGISTER_Q35 (MCH_GBSM), TopOfLowRamMb << MCH_GBSM_MB_SHIFT ); PciWrite32 ( DRAMC_REGISTER_Q35 (MCH_BGSM), TopOfLowRamMb << MCH_BGSM_MB_SHIFT ); // // Set TSEG Memory Base. // InitQ35TsegMbytes (); PciWrite32 ( DRAMC_REGISTER_Q35 (MCH_TSEGMB), (TopOfLowRamMb - mQ35TsegMbytes) << MCH_TSEGMB_MB_SHIFT ); // // Set TSEG size, and disable TSEG visibility outside of SMM. Note that the // T_EN bit has inverse meaning; when T_EN is set, then TSEG visibility is // *restricted* to SMM. // EsmramcVal &= ~(UINT32)MCH_ESMRAMC_TSEG_MASK; EsmramcVal |= mQ35TsegMbytes == 8 ? MCH_ESMRAMC_TSEG_8MB : mQ35TsegMbytes == 2 ? MCH_ESMRAMC_TSEG_2MB : mQ35TsegMbytes == 1 ? MCH_ESMRAMC_TSEG_1MB : MCH_ESMRAMC_TSEG_EXT; EsmramcVal |= MCH_ESMRAMC_T_EN; PciWrite8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC), EsmramcVal); // // TSEG should be closed (see above), but unlocked, initially. Set G_SMRAME // (Global SMRAM Enable) too, as both D_LCK and T_EN depend on it. // PciAndThenOr8 ( DRAMC_REGISTER_Q35 (MCH_SMRAM), (UINT8)((~(UINT32)MCH_SMRAM_D_LCK) & 0xff), MCH_SMRAM_G_SMRAME ); GetStates (&mAccess.LockState, &mAccess.OpenState); // // SmramAccessLock() depends on "mQ35SmramAtDefaultSmbase"; init the latter // just before exposing the former via PEI_SMM_ACCESS_PPI.Lock(). // InitQ35SmramAtDefaultSmbase (); // // We're done. The next step should succeed, but even if it fails, we can't // roll back the above BuildGuidHob() allocation, because PEI doesn't support // releasing memory. // return PeiServicesInstallPpi (mPpiList); WrongConfig: // // We really don't want to continue in this case. // ASSERT (FALSE); CpuDeadLoop (); return EFI_UNSUPPORTED; }