378 lines
12 KiB
C
378 lines
12 KiB
C
/** @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 <Uefi.h>
|
|
#include <ChipsetAccess.h>
|
|
#include <SmiTable.h>
|
|
#include <ChipsetSmiTable.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/IoLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/PcdLib.h>
|
|
#include "BiosRegionLockInfo.h"
|
|
#include "BiosRegionLockHelpFun.h"
|
|
#include <Library/MmPciLib.h>
|
|
#include <Library/TimerLib.h>
|
|
#include <Library/SpiCommonLib.h>
|
|
#include <Library/S3BootScriptLib.h>
|
|
#include <Library/PciSegmentLib.h>
|
|
#include <Register/PchRegs.h>
|
|
#include <Register/SpiRegs.h>
|
|
#include <PchReservedResources.h>
|
|
#include <Library/HobLib.h>
|
|
//[-start-191225-IB16740000-add]// for PCI_DEVICE_NUMBER_PCH_LPC & PCI_FUNCTION_NUMBER_PCH_XHCI define
|
|
#include <PchBdfAssignment.h>
|
|
//[-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;
|
|
}
|
|
|