/** @file
This is the Common driver that initializes the Intel PCH.
Copyright (c) 2019 Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include "PchInit.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//
// Module variables
//
GLOBAL_REMOVE_IF_UNREFERENCED PCH_CONFIG_HOB *mPchConfigHob;
GLOBAL_REMOVE_IF_UNREFERENCED SI_CONFIG_HOB_DATA *mSiConfigHobData;
//
// EFI_EVENT
//
GLOBAL_REMOVE_IF_UNREFERENCED EFI_EVENT mHeciEvent;
/**
Common PchInit Module Entry Point
**/
VOID
PchInitEntryPointCommon (
VOID
)
{
EFI_PEI_HOB_POINTERS HobPtr;
DEBUG ((DEBUG_INFO, "PchInitEntryPointCommon() Start\n"));
//
// Get PCH Config HOB.
//
HobPtr.Guid = GetFirstGuidHob (&gPchConfigHobGuid);
ASSERT (HobPtr.Guid != NULL);
mPchConfigHob = (PCH_CONFIG_HOB *) GET_GUID_HOB_DATA (HobPtr.Guid);
//
// Get Silicon Config data HOB
//
HobPtr.Guid = GetFirstGuidHob (&gSiConfigHobGuid);
ASSERT (HobPtr.Guid != NULL);
mSiConfigHobData = (SI_CONFIG_HOB_DATA *) GET_GUID_HOB_DATA (HobPtr.Guid);
DEBUG ((DEBUG_INFO, "PchInitEntryPointCommon() End\n"));
return;
}
/**
Lock SPI register before boot
**/
VOID
LockSpiConfiguration (
VOID
)
{
UINTN Index;
UINT16 Data16;
UINT16 Data16And;
UINT16 Data16Or;
UINT32 Data32;
UINT32 DlockValue;
UINT64 PciSpiRegBase;
UINT32 PchSpiBar0;
UINT32 Timer;
PciSpiRegBase = PCI_SEGMENT_LIB_ADDRESS (
DEFAULT_PCI_SEGMENT_NUMBER_PCH,
DEFAULT_PCI_BUS_NUMBER_PCH,
PCI_DEVICE_NUMBER_PCH_SPI,
PCI_FUNCTION_NUMBER_PCH_SPI,
0
);
//
// Check for SPI controller presence before programming
//
if (PciSegmentRead32 (PciSpiRegBase + PCI_VENDOR_ID_OFFSET) == 0xFFFF) {
return;
}
//
// Make sure SPI BAR0 has fixed address before writing to boot script.
// The same base address is set in PEI and will be used during resume.
//
PchSpiBar0 = PCH_SPI_BASE_ADDRESS;
PciSegmentAnd8 (PciSpiRegBase + PCI_COMMAND_OFFSET, (UINT8) ~EFI_PCI_COMMAND_MEMORY_SPACE);
PciSegmentWrite32 (PciSpiRegBase + R_SPI_CFG_BAR0, PchSpiBar0);
PciSegmentOr8 (PciSpiRegBase + PCI_COMMAND_OFFSET, EFI_PCI_COMMAND_MEMORY_SPACE);
//
// Program the Flash Protection Range Register based on policy
//
DlockValue = MmioRead32 (PchSpiBar0 + R_SPI_MEM_DLOCK);
for (Index = 0; Index < PCH_FLASH_PROTECTED_RANGES; ++Index) {
if ((mPchConfigHob->ProtectRange[Index].WriteProtectionEnable ||
mPchConfigHob->ProtectRange[Index].ReadProtectionEnable) != TRUE) {
continue;
}
//
// Proceed to program the register after ensure it is enabled
//
Data32 = 0;
Data32 |= (mPchConfigHob->ProtectRange[Index].WriteProtectionEnable == TRUE) ? B_SPI_MEM_PRX_WPE : 0;
Data32 |= (mPchConfigHob->ProtectRange[Index].ReadProtectionEnable == TRUE) ? B_SPI_MEM_PRX_RPE : 0;
Data32 |= ((UINT32) mPchConfigHob->ProtectRange[Index].ProtectedRangeLimit << N_SPI_MEM_PRX_PRL) & B_SPI_MEM_PRX_PRL_MASK;
Data32 |= ((UINT32) mPchConfigHob->ProtectRange[Index].ProtectedRangeBase << N_SPI_MEM_PRX_PRB) & B_SPI_MEM_PRX_PRB_MASK;
DEBUG ((DEBUG_INFO, "Protected range %d: 0x%08x \n", Index, Data32));
DlockValue |= (UINT32) (B_SPI_MEM_DLOCK_PR0LOCKDN << Index);
MmioWrite32 ((UINTN) (PchSpiBar0 + (R_SPI_MEM_PR0 + (Index * S_SPI_MEM_PRX))), Data32);
S3BootScriptSaveMemWrite (
S3BootScriptWidthUint32,
(UINTN) (PchSpiBar0 + (R_SPI_MEM_PR0 + (Index * S_SPI_MEM_PRX))),
1,
(VOID *) (UINTN) (PchSpiBar0 + (R_SPI_MEM_PR0 + (Index * S_SPI_MEM_PRX)))
);
}
//
// Program DLOCK register
//
MmioWrite32 ((UINTN) (PchSpiBar0 + R_SPI_MEM_DLOCK), DlockValue);
S3BootScriptSaveMemWrite (
S3BootScriptWidthUint32,
(UINTN) (PchSpiBar0 + R_SPI_MEM_DLOCK),
1,
(VOID *) (UINTN) (PchSpiBar0 + R_SPI_MEM_DLOCK)
);
///
/// PCH BIOS Spec Section 3.6 Flash Security Recommendation
/// In PCH SPI controller the BIOS should set the Flash Configuration Lock-Down bit
/// (SPI_BAR0 + 04[15]) at end of post. When set to 1, those Flash Program Registers
/// that are locked down by this FLOCKDN bit cannot be written.
/// Please refer to the EDS for which program registers are impacted.
/// Additionally BIOS must program SPI_BAR0 + 0x04 BIT11 (WRSDIS) to disable Write Status in HW sequencing
///
//
// Ensure there is no pending SPI trasaction before setting lock bits
//
Timer = 0;
while (MmioRead16 (PchSpiBar0 + R_SPI_MEM_HSFSC) & B_SPI_MEM_HSFSC_SCIP) {
if (Timer > SPI_WAIT_TIME) {
//
// SPI transaction is pending too long at this point, exit with error.
//
DEBUG ((DEBUG_ERROR, "SPI Cycle timeout\n"));
ASSERT (FALSE);
break;
}
MicroSecondDelay (SPI_WAIT_PERIOD);
Timer += SPI_WAIT_PERIOD;
}
Data16And = B_SPI_MEM_HSFSC_SCIP;
Data16 = 0;
S3BootScriptSaveMemPoll (
S3BootScriptWidthUint16,
PchSpiBar0 + R_SPI_MEM_HSFSC,
&Data16And,
&Data16,
SPI_WAIT_PERIOD,
SPI_WAIT_TIME / SPI_WAIT_PERIOD
);
//
// Clear any outstanding status
//
Data16Or = B_SPI_MEM_HSFSC_SAF_DLE
| B_SPI_MEM_HSFSC_SAF_ERROR
| B_SPI_MEM_HSFSC_AEL
| B_SPI_MEM_HSFSC_FCERR
| B_SPI_MEM_HSFSC_FDONE;
Data16And = 0xFFFF;
MmioAndThenOr16 (PchSpiBar0 + R_SPI_MEM_HSFSC, Data16And, Data16Or);
S3BootScriptSaveMemReadWrite (
S3BootScriptWidthUint16,
PchSpiBar0 + R_SPI_MEM_HSFSC,
&Data16Or,
&Data16And
);
//
// Set WRSDIS
//
Data16Or = B_SPI_MEM_HSFSC_WRSDIS;
Data16And = 0xFFFF;
MmioAndThenOr16 (PchSpiBar0 + R_SPI_MEM_HSFSC, Data16And, Data16Or);
S3BootScriptSaveMemReadWrite (
S3BootScriptWidthUint16,
PchSpiBar0 + R_SPI_MEM_HSFSC,
&Data16Or,
&Data16And
);
//
// Set FLOCKDN
//
Data16Or = B_SPI_MEM_HSFSC_FLOCKDN;
Data16And = 0xFFFF;
MmioAndThenOr16 (PchSpiBar0 + R_SPI_MEM_HSFSC, Data16And, Data16Or);
S3BootScriptSaveMemReadWrite (
S3BootScriptWidthUint16,
PchSpiBar0 + R_SPI_MEM_HSFSC,
&Data16Or,
&Data16And
);
///
/// SPI Flash Programming Guide Section 5.5.2 Vendor Component Lock
/// It is strongly recommended that BIOS sets the Vendor Component Lock (VCL) bits. VCL applies
/// the lock to both VSCC0 and VSCC1 even if VSCC0 is not used. Without the VCL bits set, it is
/// possible to make Host/GbE VSCC register(s) changes in that can cause undesired host and
/// integrated GbE Serial Flash functionality.
///
MmioOr32 ((UINTN) (PchSpiBar0 + R_SPI_MEM_SFDP0_VSCC0), B_SPI_MEM_SFDP0_VSCC0_VCL);
S3BootScriptSaveMemWrite (
S3BootScriptWidthUint32,
(UINTN) (PchSpiBar0 + R_SPI_MEM_SFDP0_VSCC0),
1,
(VOID *) (UINTN) (PchSpiBar0 + R_SPI_MEM_SFDP0_VSCC0)
);
}
/**
Process all the lock downs
**/
VOID
ProcessAllLocks (
VOID
)
{
UINT8 Data8;
UINT16 Data16And;
UINT16 Data16Or;
UINT32 Data32And;
UINT32 Data32Or;
UINT64 PciLpcRegBase;
UINT16 TcoBase;
UINT64 PciSpiRegBase;
PciLpcRegBase = PCI_SEGMENT_LIB_ADDRESS (
DEFAULT_PCI_SEGMENT_NUMBER_PCH,
DEFAULT_PCI_BUS_NUMBER_PCH,
PCI_DEVICE_NUMBER_PCH_LPC,
PCI_FUNCTION_NUMBER_PCH_LPC,
0
);
PciSpiRegBase = PCI_SEGMENT_LIB_ADDRESS (
DEFAULT_PCI_SEGMENT_NUMBER_PCH,
DEFAULT_PCI_BUS_NUMBER_PCH,
PCI_DEVICE_NUMBER_PCH_SPI,
PCI_FUNCTION_NUMBER_PCH_SPI,
0
);
PchTcoBaseGet (&TcoBase);
//
// Lock function disable (ST and NST PG) register fields.
//
PmcLockFunctionDisableConfigWithS3BootScript ();
///
/// PCH BWG Additional PCH DMI and OP-DMI Programming Steps
/// Lock DMI.
///
PchDmiSetLockWithS3BootScript ();
//
// Lock SPI register before boot.
//
LockSpiConfiguration ();
///
/// Additional Power Management Programming
/// Step 3
/// Lock configuration after stretch and ACPI base programming completed.
///
PmcLockSlpSxStretchingPolicyWithS3BootScript ();
//
// Set BiosLock.
//
if (mPchConfigHob->LockDown.BiosLock == TRUE) {
BiosLockEnable ();
}
///
/// PCH BIOS Spec Section 3.6 Flash Security Recommendation
/// BIOS also needs to set the BIOS Interface Lock Down bit in multiple locations
/// (PCR[DMI] + 274Ch[0], LPC/eSPI PCI offset DCh[7] and SPI PCI offset DCh[7]).
/// Setting these bits will prevent writes to the Top Swap bit (under their respective locations)
/// and the Boot BIOS Straps. Enabling this bit will mitigate malicious software
/// attempts to replace the system BIOS option ROM with its own code.
///
if (mPchConfigHob->LockDown.BiosInterface == TRUE) {
//
// LPC
//
PciSegmentOr8 ((UINT64) (PciLpcRegBase + R_LPC_CFG_BC), (UINT32) B_LPC_CFG_BC_BILD);
S3BootScriptSaveMemWrite (
S3BootScriptWidthUint8,
PcdGet64 (PcdPciExpressBaseAddress) + PciLpcRegBase + R_LPC_CFG_BC,
1,
(VOID *) (UINTN) (PcdGet64 (PcdPciExpressBaseAddress) + PciLpcRegBase + R_LPC_CFG_BC)
);
//
// Reads back for posted write to take effect
//
Data8 = PciSegmentRead8 ((UINTN) (PciLpcRegBase + R_LPC_CFG_BC));
S3BootScriptSaveMemPoll (
S3BootScriptWidthUint8,
PcdGet64 (PcdPciExpressBaseAddress) + PciLpcRegBase + R_LPC_CFG_BC,
&Data8, // BitMask
&Data8, // BitValue
1, // Duration
1 // LoopTimes
);
//
// SPI
//
PciSegmentOr8 ((UINT64) (PciSpiRegBase + R_SPI_CFG_BC), (UINT32) B_SPI_CFG_BC_BILD);
S3BootScriptSaveMemWrite (
S3BootScriptWidthUint8,
PcdGet64 (PcdPciExpressBaseAddress) + PciSpiRegBase + R_SPI_CFG_BC,
1,
(VOID *) (UINTN) (PcdGet64 (PcdPciExpressBaseAddress) + PciSpiRegBase + R_SPI_CFG_BC)
);
//
// Reads back for posted write to take effect
//
Data8 = PciSegmentRead8 ((UINT64) (PciSpiRegBase + R_SPI_CFG_BC));
S3BootScriptSaveMemPoll (
S3BootScriptWidthUint8,
PcdGet64 (PcdPciExpressBaseAddress) + PciSpiRegBase + R_SPI_CFG_BC,
&Data8, // BitMask
&Data8, // BitValue
1, // Duration
1 // LoopTimes
);
///
/// Set BIOS interface Lock-Down
///
PchDmiSetBiosLockDownWithS3BootScript ();
}
///
/// PCH BIOS Spec on using RTC RAM
/// Regardless of BUC.TS being updated or not, BIOS must set RC.BILD bit PCR[RTC] + 3400h[31] before exit
/// For Data integrity protection, set RTC Memory locks (Upper 128 Byte Lock and
/// Lower 128 Byte Lock) at PCR[RTC] + 3400h[4] and PCR[RTC] + 3400h[3].
/// Note once locked bytes 0x38 - 0x3F in each of the Upper and Lower Byte blocks, respectively,
/// cannot be unlocked until next reset.
///
Data32And = 0xFFFFFFFF;
Data32Or = 0x0;
if (mPchConfigHob->LockDown.BiosInterface == TRUE) {
Data32Or = B_RTC_PCR_CONF_BILD;
}
if (mPchConfigHob->LockDown.RtcMemoryLock == TRUE) {
Data32Or |= (B_RTC_PCR_CONF_UCMOS_LOCK | B_RTC_PCR_CONF_LCMOS_LOCK);
}
PchPcrAndThenOr32 (
PID_RTC_HOST, R_RTC_PCR_CONF,
Data32And,
Data32Or
);
PCH_PCR_BOOT_SCRIPT_READ_WRITE (
S3BootScriptWidthUint32,
PID_RTC_HOST, R_RTC_PCR_CONF,
&Data32Or,
&Data32And
);
///
/// Remove access to RTC PCRs
///
Data32And = (UINT32)~(BIT0);
Data32Or = 0;
PchPcrAndThenOr32 (
PID_RTC_HOST, R_RTC_PCR_PG1_AC_LO,
Data32And,
Data32Or
);
PCH_PCR_BOOT_SCRIPT_READ_WRITE (
S3BootScriptWidthUint32,
PID_RTC_HOST, R_RTC_PCR_PG1_AC_LO,
&Data32Or,
&Data32And
);
PchPcrAndThenOr32 (
PID_RTC_HOST, R_RTC_PCR_PG1_CP_LO,
Data32And,
Data32Or
);
PCH_PCR_BOOT_SCRIPT_READ_WRITE (
S3BootScriptWidthUint32,
PID_RTC_HOST, R_RTC_PCR_PG1_CP_LO,
&Data32Or,
&Data32And
);
//
// Lock Down TCO
//
Data16And = 0xFFFF;
Data16Or = B_TCO_IO_TCO1_CNT_LOCK;
IoOr16 (TcoBase + R_TCO_IO_TCO1_CNT, Data16Or);
S3BootScriptSaveIoReadWrite (
S3BootScriptWidthUint16,
(UINTN) (TcoBase + R_TCO_IO_TCO1_CNT),
&Data16Or, // Data to be ORed
&Data16And // Data to be ANDed
);
///
/// PCH BIOS Spec Section 5.15.1 Additional Chipset Initialization
/// Step 1
/// Lock PMC Set Strap Message Interface
///
PmcLockSetStrapMsgInterfaceWithS3BootScript ();
//
// Lock Down PMC
//
PmcLockWithS3BootScript ();
}
/**
Set eSPI BME bit
**/
VOID
ConfigureEspiBme (
VOID
)
{
UINT64 EspiPciBase;
EspiPciBase = PCI_SEGMENT_LIB_ADDRESS (
DEFAULT_PCI_SEGMENT_NUMBER_PCH,
DEFAULT_PCI_BUS_NUMBER_PCH,
PCI_DEVICE_NUMBER_PCH_LPC,
PCI_FUNCTION_NUMBER_PCH_LPC,
0
);
if (PciSegmentRead16 (EspiPciBase + PCI_VENDOR_ID_OFFSET) == 0xFFFF) {
return;
}
if ((PciSegmentRead32 (EspiPciBase + R_ESPI_CFG_PCBC) & B_ESPI_CFG_PCBC_ESPI_EN) == 0) {
return;
}
//
// Refer to PCH BWG.
// To enable eSPI bus mastering BIOS must enable BME in eSPI controller
// and also set BME bit in the respective slave devices through Configuration
// and Capabilities register of each slave using Get_Configuration and Set_Configuration functionality.
//
// NOTE: The setting is also done in PEI, but might be cleared by PCI bus during PCI enumeration.
// Therefore, reeable it after PCI enumeration done.
//
if (mPchConfigHob->Espi.BmeMasterSlaveEnabled == TRUE) {
PciSegmentOr8 (EspiPciBase + PCI_COMMAND_OFFSET, EFI_PCI_COMMAND_BUS_MASTER);
}
}
/**
Common PCH initialization before Boot Sript Table is closed
**/
VOID
PchOnPciEnumCompleteCommon (
VOID
)
{
UINT32 Data32Or;
UINT32 Data32And;
BOOLEAN ResetStatus;
DEBUG ((DEBUG_INFO, "PchOnPciEnumCompleteCommon() Start\n"));
if (SiScheduleResetIsRequired ()) {
ResetStatus = SiScheduleResetPerformReset ();
ASSERT (!ResetStatus);
}
ProcessAllLocks ();
//
// Perform remaining configuration for PCH SATA on End of DXE
//
ConfigurePchSataOnEndOfDxe ();
//
// PSTHCTL (0xD00h[2]) = 1, PSTH IOSF Primary Trunk Clock Gating Enable (PSTHIOSFPTCGE)
//
Data32And = 0xFFFFFFFF;
Data32Or = B_PSTH_PCR_PSTHIOSFPTCGE;
PchPcrAndThenOr32 (PID_PSTH, R_PSTH_PCR_PSTHCTL, Data32And, Data32Or);
PCH_PCR_BOOT_SCRIPT_READ_WRITE (
S3BootScriptWidthUint32,
PID_PSTH, R_PSTH_PCR_PSTHCTL,
&Data32Or,
&Data32And
);
//
// Set eSPI BME after PCI enumeration
//
ConfigureEspiBme ();
///
/// Clear Global Reset Status, Power Failure and Host Reset Status bits
///
PmcClearGlobalResetStatus ();
PmcClearPowerFailureStatus ();
PmcClearHostResetStatus ();
DEBUG ((DEBUG_INFO, "PchOnPciEnumCompleteCommon() End\n"));
}