/** @file Provide main functions of Bios Region Lock protocol ;****************************************************************************** ;* Copyright (c) 2014 - 2020, Insyde Software Corporation. All Rights Reserved. ;* ;* You may not reproduce, distribute, publish, display, perform, modify, adapt, ;* transmit, broadcast, present, recite, release, license or otherwise exploit ;* any part of this publication in any form, by any means, without the prior ;* written permission of Insyde Software Corporation. ;* ;****************************************************************************** */ #include #include #include #include #include #include #include #include #include "BiosRegionLockInfo.h" #include "BiosRegionLockHelpFun.h" #include #include #include #include #include #include #include #include #include //[-start-191225-IB16740000-add]// for PCI_DEVICE_NUMBER_PCH_LPC & PCI_FUNCTION_NUMBER_PCH_XHCI define #include //[-end-191225-IB16740000-add]/ /** This function set the BIOS region by requested type to the BIOS protect region register. @param[in] This Protocol instance pointer. @param[in] Type The BIOS Region type which need to be locked. @retval EFI_ACCESS_DENIED The BIOS protect region register has been locked. @retval EFI_INVALID_PARAMETER The input Type is invalid. @retval EFI_OUT_OF_RESOURCES All BIOS protect region registers have been set and the requested region can not be merge to existing protected region. @retval EFI_SUCCESS Set BIOS protect region register successfully */ EFI_STATUS EFIAPI SetRegionByType ( IN BIOS_REGION_LOCK_PROTOCOL *This, IN BIOS_REGION_TYPE Type ) { #if 0 UINTN Base; UINTN Length; EFI_STATUS Status; UINTN PchSpiBase; UINTN PciSpiRegBase; PciSpiRegBase = MmPciBase ( DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_SPI, PCI_FUNCTION_NUMBER_PCH_SPI ); PchSpiBase = MmioRead32 (PciSpiRegBase + R_SPI_CFG_BAR0) &~(B_SPI_CFG_BAR0_MASK); // if (PchMmRcrb16 (R_PCH_SPI_HSFS) & B_PCH_SPI_HSFS_FLOCKDN) { if (MmioRead16 (PchSpiBase + R_SPI_MEM_HSFS) & B_SPI_MEM_HSFS_FLOCKDN) { DEBUG ((DEBUG_ERROR, "SPI has been locked - Access Denied!\n")); return EFI_ACCESS_DENIED; } Status = GetBiosRegionAddress (Type, &Base, &Length); if (EFI_ERROR (Status)) { return Status; } return SetRegionByAddress (This, Base, Length); #else return EFI_UNSUPPORTED; #endif } /** This function sets the BIOS region by requested base address and legnth to the BIOS protect region register. @param[in] This Protocol instance pointer. @param[in] BaseAddress The start address of the BIOS region which need to be locked. @param[in] Length The Length of the BIOS region which need to be locked. @retval EFI_ACCESS_DENIED The BIOS protect region register has been locked. @retval EFI_OUT_OF_RESOURCES All BIOS protect region registers have been set and the requested region can not be merge to existing protected region. @retval EFI_SUCCESS Set BIOS protect region register successfully */ EFI_STATUS EFIAPI SetRegionByAddress ( IN BIOS_REGION_LOCK_PROTOCOL *This, IN UINTN BaseAddress, IN UINTN Length ) { BIOS_REGION_LOCK_INSTANCE *Private; EFI_STATUS Status; UINTN PchSpiBase; UINTN PciSpiRegBase; PciSpiRegBase = MmPciBase ( DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_SPI, PCI_FUNCTION_NUMBER_PCH_SPI ); PchSpiBase = MmioRead32 (PciSpiRegBase + R_SPI_CFG_BAR0) &~(B_SPI_CFG_BAR0_MASK); if (MmioRead32 (PchSpiBase + R_SPI_MEM_HSFSC) & B_SPI_MEM_HSFSC_FLOCKDN) { DEBUG ((DEBUG_ERROR, "SPI has been locked - Access Denied!\n")); return EFI_ACCESS_DENIED; } Private = BIOS_REGION_LOCK_INSTANCE_FROM_THIS (This); Status = MergeToBase (Private->BiosLock, BaseAddress, Length); if (EFI_ERROR (Status)) { return Status; } return EFI_SUCCESS; } /** This function removes the BIOS region by requested type from the BIOS protect region register. @param[in] This Protocol instance pointer. @param[in] Type The BIOS Region type which need to be locked. @retval EFI_ACCESS_DENIED The BIOS protect region register has been locked. @retval EFI_INVALID_PARAMETER The input Type is invalid. @retval EFI_OUT_OF_RESOURCES The BIOS protect region registers are not enough to set for all discontinuous BIOS region @retval EFI_SUCCESS Set BIOS protect region register successfully */ EFI_STATUS EFIAPI ClearRegionByType ( IN BIOS_REGION_LOCK_PROTOCOL *This, IN BIOS_REGION_TYPE Type ) { #if 0 UINTN Base; UINTN Length; EFI_STATUS Status; UINTN PchSpiBase; UINTN PciSpiRegBase; PciSpiRegBase = MmPciBase ( DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_SPI, PCI_FUNCTION_NUMBER_PCH_SPI ); PchSpiBase = MmioRead32 (PciSpiRegBase + R_SPI_CFG_BAR0) &~(B_SPI_CFG_BAR0_MASK); // if (PchMmRcrb16 (R_PCH_SPI_HSFS) & B_PCH_SPI_HSFS_FLOCKDN) { if (MmioRead16 (PchSpiBase + R_SPI_MEM_HSFS) & B_SPI_MEM_HSFS_FLOCKDN) { DEBUG ((DEBUG_ERROR, "SPI has been locked - Access Denied!\n")); return EFI_ACCESS_DENIED; } Status = GetBiosRegionAddress (Type, &Base, &Length); if (EFI_ERROR (Status)) { return Status; } return ClearRegionByAddress (This, Base, Length); #else return EFI_UNSUPPORTED; #endif } /** This function removes the BIOS region by requested base address and legnth from the BIOS protect region register. @param[in] This Protocol instance pointer. @param[in] BaseAddress The start address of the BIOS region which need to be locked. @param[in] Length The Length of the BIOS region which need to be locked. @retval EFI_ACCESS_DENIED The BIOS protect region register has been locked. @retval EFI_OUT_OF_RESOURCES The BIOS protect region registers are not enough to set for all discontinuous BIOS region @retval EFI_SUCCESS Set BIOS protect region register successfully */ EFI_STATUS EFIAPI ClearRegionByAddress ( IN BIOS_REGION_LOCK_PROTOCOL *This, IN UINTN BaseAddress, IN UINTN Length ) { BIOS_REGION_LOCK_INSTANCE *Private; EFI_STATUS Status; UINTN PchSpiBase; UINTN PciSpiRegBase; PciSpiRegBase = MmPciBase ( DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_SPI, PCI_FUNCTION_NUMBER_PCH_SPI ); PchSpiBase = MmioRead32 (PciSpiRegBase + R_SPI_CFG_BAR0) &~(B_SPI_CFG_BAR0_MASK); if (MmioRead32 (PchSpiBase + R_SPI_MEM_HSFSC) & B_SPI_MEM_HSFSC_FLOCKDN) { DEBUG ((DEBUG_ERROR, "SPI has been locked - Access Denied!\n")); return EFI_ACCESS_DENIED; } Private = BIOS_REGION_LOCK_INSTANCE_FROM_THIS (This); Status = RemoveFromBase (Private->BiosLock, BaseAddress, Length); if (EFI_ERROR (Status)) { return Status; } return EFI_SUCCESS; } /** This function locks BIOS region described by PR0 to PR5 and trigger software SMI to enable BIOS Region SMM Protection. @param[in] This Protocol instance pointer. @retval EFI_SUCCESS */ EFI_STATUS EFIAPI Lock ( IN BIOS_REGION_LOCK_PROTOCOL *This ) { BIOS_REGION_LOCK_INSTANCE *Private; EFI_STATUS Status; UINT32 PchSpiBase; UINT64 PciSpiRegBase; UINT16 Data16; UINT16 Data16And; UINT16 Data16Or; UINT32 Timer; UINT64 BootScriptPciAddress; EFI_BOOT_MODE BootMode; BootMode = GetBootModeHob (); DEBUG ((DEBUG_ERROR, " BiosRegionLock->Lock, BootMode = 0x%x \n", BootMode)); if ((BootMode == BOOT_ON_FLASH_UPDATE) || (BootMode == BOOT_IN_RECOVERY_MODE) ) { DEBUG ((DEBUG_INFO, "BOOT_ON_FLASH_UPDATE and BOOT_IN_RECOVERY_MODE mode should not lock SPI\n")); return EFI_SUCCESS; } 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 ); // // 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. // PchSpiBase = PCH_SPI_BASE_ADDRESS; PciSegmentWrite32 (PciSpiRegBase + R_SPI_CFG_BAR0, PchSpiBase); BootScriptPciAddress = S3_BOOT_SCRIPT_LIB_PCI_ADDRESS ( DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_SPI, PCI_FUNCTION_NUMBER_PCH_SPI, R_SPI_CFG_BAR0 ); S3BootScriptSavePciCfgWrite ( S3BootScriptWidthUint32, BootScriptPciAddress, 1, &PchSpiBase ); PchSpiBase = PciSegmentRead32 (PciSpiRegBase + R_SPI_CFG_BAR0) &~(B_SPI_CFG_BAR0_MASK); if (MmioRead32 (PchSpiBase + R_SPI_MEM_HSFSC) & B_SPI_MEM_HSFSC_FLOCKDN) { DEBUG ((DEBUG_ERROR, "SPI has been locked - Access Denied!\n")); return EFI_ACCESS_DENIED; } Private = BIOS_REGION_LOCK_INSTANCE_FROM_THIS (This); // // Program PR registers // Status = ProgramRegister (Private->BiosLock); ASSERT_EFI_ERROR (Status); // // Lock the Protected Range registers // if ((MmioRead32 (PchSpiBase + R_SPI_MEM_HSFSC) & B_SPI_MEM_HSFSC_FLOCKDN) == 0) { // // Ensure there is no pending SPI trasaction before setting lock bits // Timer = 0; while (MmioRead16 (PchSpiBase + 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, PchSpiBase + 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 (PchSpiBase + R_SPI_MEM_HSFSC, Data16And, Data16Or); S3BootScriptSaveMemReadWrite ( S3BootScriptWidthUint16, PchSpiBase + R_SPI_MEM_HSFSC, &Data16Or, &Data16And ); // // Set WRSDIS // Data16Or = B_SPI_MEM_HSFSC_WRSDIS; Data16And = 0xFFFF; MmioAndThenOr16 (PchSpiBase + R_SPI_MEM_HSFSC, Data16And, Data16Or); S3BootScriptSaveMemReadWrite ( S3BootScriptWidthUint16, PchSpiBase + R_SPI_MEM_HSFSC, &Data16Or, &Data16And ); // // Set FLOCKDN // Data16Or = B_SPI_MEM_HSFSC_FLOCKDN; Data16And = 0xFFFF; MmioAndThenOr16 (PchSpiBase + R_SPI_MEM_HSFSC, Data16And, Data16Or); S3BootScriptSaveMemReadWrite ( S3BootScriptWidthUint16, PchSpiBase + R_SPI_MEM_HSFSC, &Data16Or, &Data16And ); DEBUG ((DEBUG_ERROR, "SPI is locked now!\n")); } else { DEBUG ((DEBUG_ERROR, "SPI is already locked. Can not lock again!\n")); CpuDeadLoop (); } return EFI_SUCCESS; }