/** @file This is the Common driver that initializes the Intel PCH. Copyright (c) 2021, 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 #include #include #include #include // // Module variables // GLOBAL_REMOVE_IF_UNREFERENCED PCH_CONFIG_HOB *mPchConfigHob; // // 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); 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 = SpiPciCfgBase (); // // Check for SPI controller presence before programming // if (PciSegmentRead16 (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); // // Locking for security reasons only if Extended BIOS Range Decode is supported // if (IsExtendedBiosRangeDecodeSupported ()) { // // Before setting FLOCKDN lock Extended BIOS Range configuration // All configuration of this feature shall be done already at this moment // PciSegmentOr32 (PciSpiRegBase + R_SPI_CFG_BC, BIT28); S3BootScriptSavePciCfgWrite ( S3BootScriptWidthUint32, (UINTN) PciSpiRegBase + R_SPI_CFG_BC, 1, (VOID *) (UINTN) (PciSpiRegBase + R_SPI_CFG_BC) ); } // // 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) ); } /** Set HD Audio PME bit **/ VOID ConfigureHdAudioPme ( VOID ) { UINT64 HdaPciBase; HdaPciBase = HdaPciCfgBase (); if (PciSegmentRead16 (HdaPciBase + PCI_VENDOR_ID_OFFSET) == 0xFFFF) { return; } /// /// PME Enable for Audio controller /// if (mPchConfigHob->HdAudio.Pme == TRUE) { PciSegmentOr32 (HdaPciBase + R_HDA_CFG_PCS, (UINT32) B_HDA_CFG_PCS_PMEE); } } /** Set eSPI BME bit **/ VOID ConfigureEspiBme ( VOID ) { UINT64 EspiPciBase; EspiPciBase = EspiPciCfgBase (); 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); } }