In one of the next patches we'll turn PcdQ35TsegMbytes into a dynamic PCD, to be set by PlatformPei. Jordan suggested to use gEfiPeiMemoryDiscoveredPpiGuid as SmmAccessPei's DEPEX for making sure that PlatformPei sets the PCD before SmmAccessPei consumes it. (PlatformPei installs the permanent PEI RAM.) Such a DEPEX is supposed to mirror physical firmware, where anything related to SMRAM cannot run before said platform's physical RAM is discovered (signaled by the presence of gEfiPeiMemoryDiscoveredPpiGuid). Introduce the InitQ35TsegMbytes() function and the "mQ35TsegMbytes" extern variable to "SmramInternal.h" and "SmramInternal.c": - Both SmmAccess modules (PEIM and DXE driver) are supposed to call InitQ35TsegMbytes() in their respective entry point functions, saving PcdQ35TsegMbytes into "mQ35TsegMbytes". This way dynamic PCD fetches can be kept out of PEI_SMM_ACCESS_PPI and EFI_SMM_ACCESS2_PROTOCOL member functions later (when we add support for extended TSEG size). - We can thus replace the current PcdQ35TsegMbytes fetches in SmmAccessPei's entry point function as well, with reads from "mQ35TsegMbytes". Cc: Jordan Justen <jordan.l.justen@intel.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
206 lines
5.9 KiB
C
206 lines
5.9 KiB
C
/** @file
|
|
|
|
Functions and types shared by the SMM accessor PEI and DXE modules.
|
|
|
|
Copyright (C) 2015, Red Hat, Inc.
|
|
|
|
This program and the accompanying materials are licensed and made available
|
|
under the terms and conditions of the BSD License which accompanies this
|
|
distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
|
|
WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include <Guid/AcpiS3Context.h>
|
|
#include <IndustryStandard/Q35MchIch9.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/PcdLib.h>
|
|
#include <Library/PciLib.h>
|
|
|
|
#include "SmramInternal.h"
|
|
|
|
//
|
|
// The value of PcdQ35TsegMbytes is saved into this variable at module startup.
|
|
//
|
|
UINT16 mQ35TsegMbytes;
|
|
|
|
/**
|
|
Save PcdQ35TsegMbytes into mQ35TsegMbytes.
|
|
**/
|
|
VOID
|
|
InitQ35TsegMbytes (
|
|
VOID
|
|
)
|
|
{
|
|
mQ35TsegMbytes = PcdGet16 (PcdQ35TsegMbytes);
|
|
}
|
|
|
|
/**
|
|
Read the MCH_SMRAM and ESMRAMC registers, and update the LockState and
|
|
OpenState fields in the PEI_SMM_ACCESS_PPI / EFI_SMM_ACCESS2_PROTOCOL object,
|
|
from the D_LCK and T_EN bits.
|
|
|
|
PEI_SMM_ACCESS_PPI and EFI_SMM_ACCESS2_PROTOCOL member functions can rely on
|
|
the LockState and OpenState fields being up-to-date on entry, and they need
|
|
to restore the same invariant on exit, if they touch the bits in question.
|
|
|
|
@param[out] LockState Reflects the D_LCK bit on output; TRUE iff SMRAM is
|
|
locked.
|
|
@param[out] OpenState Reflects the inverse of the T_EN bit on output; TRUE
|
|
iff SMRAM is open.
|
|
**/
|
|
VOID
|
|
GetStates (
|
|
OUT BOOLEAN *LockState,
|
|
OUT BOOLEAN *OpenState
|
|
)
|
|
{
|
|
UINT8 SmramVal, EsmramcVal;
|
|
|
|
SmramVal = PciRead8 (DRAMC_REGISTER_Q35 (MCH_SMRAM));
|
|
EsmramcVal = PciRead8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC));
|
|
|
|
*LockState = !!(SmramVal & MCH_SMRAM_D_LCK);
|
|
*OpenState = !(EsmramcVal & MCH_ESMRAMC_T_EN);
|
|
}
|
|
|
|
//
|
|
// The functions below follow the PEI_SMM_ACCESS_PPI and
|
|
// EFI_SMM_ACCESS2_PROTOCOL member declarations. The PeiServices and This
|
|
// pointers are removed (TSEG doesn't depend on them), and so is the
|
|
// DescriptorIndex parameter (TSEG doesn't support range-wise locking).
|
|
//
|
|
// The LockState and OpenState members that are common to both
|
|
// PEI_SMM_ACCESS_PPI and EFI_SMM_ACCESS2_PROTOCOL are taken and updated in
|
|
// isolation from the rest of the (non-shared) members.
|
|
//
|
|
|
|
EFI_STATUS
|
|
SmramAccessOpen (
|
|
OUT BOOLEAN *LockState,
|
|
OUT BOOLEAN *OpenState
|
|
)
|
|
{
|
|
//
|
|
// Open TSEG by clearing T_EN.
|
|
//
|
|
PciAnd8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC),
|
|
(UINT8)((~(UINT32)MCH_ESMRAMC_T_EN) & 0xff));
|
|
|
|
GetStates (LockState, OpenState);
|
|
if (!*OpenState) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
SmramAccessClose (
|
|
OUT BOOLEAN *LockState,
|
|
OUT BOOLEAN *OpenState
|
|
)
|
|
{
|
|
//
|
|
// Close TSEG by setting T_EN.
|
|
//
|
|
PciOr8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC), MCH_ESMRAMC_T_EN);
|
|
|
|
GetStates (LockState, OpenState);
|
|
if (*OpenState) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
SmramAccessLock (
|
|
OUT BOOLEAN *LockState,
|
|
IN OUT BOOLEAN *OpenState
|
|
)
|
|
{
|
|
if (*OpenState) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
//
|
|
// Close & lock TSEG by setting T_EN and D_LCK.
|
|
//
|
|
PciOr8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC), MCH_ESMRAMC_T_EN);
|
|
PciOr8 (DRAMC_REGISTER_Q35 (MCH_SMRAM), MCH_SMRAM_D_LCK);
|
|
|
|
GetStates (LockState, OpenState);
|
|
if (*OpenState || !*LockState) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
SmramAccessGetCapabilities (
|
|
IN BOOLEAN LockState,
|
|
IN BOOLEAN OpenState,
|
|
IN OUT UINTN *SmramMapSize,
|
|
IN OUT EFI_SMRAM_DESCRIPTOR *SmramMap
|
|
)
|
|
{
|
|
UINTN OriginalSize;
|
|
UINT32 TsegMemoryBaseMb, TsegMemoryBase;
|
|
UINT64 CommonRegionState;
|
|
UINT8 TsegSizeBits;
|
|
|
|
OriginalSize = *SmramMapSize;
|
|
*SmramMapSize = DescIdxCount * sizeof *SmramMap;
|
|
if (OriginalSize < *SmramMapSize) {
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// Read the TSEG Memory Base register.
|
|
//
|
|
TsegMemoryBaseMb = PciRead32 (DRAMC_REGISTER_Q35 (MCH_TSEGMB));
|
|
TsegMemoryBase = (TsegMemoryBaseMb >> MCH_TSEGMB_MB_SHIFT) << 20;
|
|
|
|
//
|
|
// Precompute the region state bits that will be set for all regions.
|
|
//
|
|
CommonRegionState = (OpenState ? EFI_SMRAM_OPEN : EFI_SMRAM_CLOSED) |
|
|
(LockState ? EFI_SMRAM_LOCKED : 0) |
|
|
EFI_CACHEABLE;
|
|
|
|
//
|
|
// The first region hosts an SMM_S3_RESUME_STATE object. It is located at the
|
|
// start of TSEG. We round up the size to whole pages, and we report it as
|
|
// EFI_ALLOCATED, so that the SMM_CORE stays away from it.
|
|
//
|
|
SmramMap[DescIdxSmmS3ResumeState].PhysicalStart = TsegMemoryBase;
|
|
SmramMap[DescIdxSmmS3ResumeState].CpuStart = TsegMemoryBase;
|
|
SmramMap[DescIdxSmmS3ResumeState].PhysicalSize =
|
|
EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (sizeof (SMM_S3_RESUME_STATE)));
|
|
SmramMap[DescIdxSmmS3ResumeState].RegionState =
|
|
CommonRegionState | EFI_ALLOCATED;
|
|
|
|
//
|
|
// Get the TSEG size bits from the ESMRAMC register.
|
|
//
|
|
TsegSizeBits = PciRead8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC)) &
|
|
MCH_ESMRAMC_TSEG_MASK;
|
|
|
|
//
|
|
// The second region is the main one, following the first.
|
|
//
|
|
SmramMap[DescIdxMain].PhysicalStart =
|
|
SmramMap[DescIdxSmmS3ResumeState].PhysicalStart +
|
|
SmramMap[DescIdxSmmS3ResumeState].PhysicalSize;
|
|
SmramMap[DescIdxMain].CpuStart = SmramMap[DescIdxMain].PhysicalStart;
|
|
SmramMap[DescIdxMain].PhysicalSize =
|
|
(TsegSizeBits == MCH_ESMRAMC_TSEG_8MB ? SIZE_8MB :
|
|
TsegSizeBits == MCH_ESMRAMC_TSEG_2MB ? SIZE_2MB :
|
|
SIZE_1MB) - SmramMap[DescIdxSmmS3ResumeState].PhysicalSize;
|
|
SmramMap[DescIdxMain].RegionState = CommonRegionState;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|