123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- /** @file
- This driver is used for SMM S3 support for the bootloader that
- doesn't support SMM.
- The payload would save SMM rebase info to SMM communication area.
- The bootloader is expected to rebase the SMM and trigger SMI by
- writting 0xB2 port with given value from SMM communication area.
- The paylaod SMM handler got chance to restore regs in S3 path.
- Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include <BlSupportSmm.h>
- PLD_S3_COMMUNICATION mPldS3Hob;
- EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *mSmramHob = NULL;
- PLD_SMM_REGISTERS *mSmmRegisterHob = NULL;
- UINT64 mSmmFeatureControl = 0;
- /**
- Save SMM rebase and SMI handler information to SMM communication area
- The function detects SMM communication region for boot loader, if it is detected, it
- will save SMM rebase information and S3 SMI handler information to SMM communication
- region. Bootloader should consume these information in S3 path to restore smm base,
- and write the 0xB2 port to trigger SMI so that payload could resume S3 registers.
- @param[in] BlSwSmiHandlerInput Value written to 0xB2 to trigger SMI handler.
- @retval EFI_SUCCESS Save SMM info success.
- @retval Others Failed to save SMM info.
- **/
- EFI_STATUS
- SaveSmmInfoForS3 (
- IN UINT8 BlSwSmiHandlerInput
- )
- {
- EFI_STATUS Status;
- EFI_PROCESSOR_INFORMATION ProcessorInfo;
- EFI_MP_SERVICES_PROTOCOL *MpService;
- CPU_SMMBASE *SmmBaseInfo;
- PLD_TO_BL_SMM_INFO *PldSmmInfo;
- UINTN Index;
- PldSmmInfo = (PLD_TO_BL_SMM_INFO *)(UINTN)mPldS3Hob.CommBuffer.PhysicalStart;
- PldSmmInfo->Header.Header.HobLength = (UINT16)(sizeof (PLD_TO_BL_SMM_INFO) + gSmst->NumberOfCpus * sizeof (CPU_SMMBASE));
- for (Index = 0; Index < mSmramHob->NumberOfSmmReservedRegions; Index++) {
- if ((mPldS3Hob.CommBuffer.PhysicalStart >= mSmramHob->Descriptor[Index].PhysicalStart) &&
- (mPldS3Hob.CommBuffer.PhysicalStart < mSmramHob->Descriptor[Index].PhysicalStart + mSmramHob->Descriptor[Index].PhysicalSize))
- {
- break;
- }
- }
- if (Index == mSmramHob->NumberOfSmmReservedRegions) {
- return EFI_NOT_FOUND;
- }
- //
- // Make sure the dedicated region for SMM info communication whose attribute is "allocated" (i.e., excluded from SMM memory service)
- //
- if ((mSmramHob->Descriptor[Index].RegionState & EFI_ALLOCATED) == 0) {
- DEBUG ((DEBUG_ERROR, "SMM communication region not set to EFI_ALLOCATED\n"));
- return EFI_INVALID_PARAMETER;
- }
- if (((UINTN)PldSmmInfo + PldSmmInfo->Header.Header.HobLength) > (mSmramHob->Descriptor[Index].PhysicalStart + mSmramHob->Descriptor[Index].PhysicalSize)) {
- DEBUG ((DEBUG_ERROR, "SMM communication buffer (0x%x) is too small.\n", (UINTN)PldSmmInfo + PldSmmInfo->Header.Header.HobLength));
- return EFI_BUFFER_TOO_SMALL;
- }
- CopyGuid (&PldSmmInfo->Header.Name, &gS3CommunicationGuid);
- PldSmmInfo->Header.Header.HobType = EFI_HOB_TYPE_GUID_EXTENSION;
- PldSmmInfo->S3Info.SwSmiTriggerValue = BlSwSmiHandlerInput;
- //
- // Save APIC ID and SMM base
- //
- Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpService);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- PldSmmInfo->S3Info.CpuCount = (UINT32)gSmst->NumberOfCpus;
- SmmBaseInfo = &PldSmmInfo->S3Info.SmmBase[0];
- for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
- Status = MpService->GetProcessorInfo (MpService, Index, &ProcessorInfo);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- SmmBaseInfo->ApicId = (UINT32)(UINTN)ProcessorInfo.ProcessorId;
- SmmBaseInfo->SmmBase = (UINT32)(UINTN)gSmst->CpuSaveState[Index] - SMRAM_SAVE_STATE_MAP_OFFSET;
- DEBUG ((DEBUG_INFO, "CPU%d ID:%02X Base: %08X\n", Index, SmmBaseInfo->ApicId, SmmBaseInfo->SmmBase));
- SmmBaseInfo++;
- }
- return EFI_SUCCESS;
- }
- /**
- Get specified SMI register based on given register ID
- @param[in] Id The register ID to get.
- @retval NULL The register is not found
- @return smi register
- **/
- PLD_GENERIC_REGISTER *
- GetRegisterById (
- UINT64 Id
- )
- {
- UINT32 Index;
- for (Index = 0; Index < mSmmRegisterHob->Count; Index++) {
- if (mSmmRegisterHob->Registers[Index].Id == Id) {
- return &mSmmRegisterHob->Registers[Index];
- }
- }
- return NULL;
- }
- /**
- Set SMM SMI Global enable lock
- **/
- VOID
- LockSmiGlobalEn (
- VOID
- )
- {
- PLD_GENERIC_REGISTER *SmiLockReg;
- DEBUG ((DEBUG_ERROR, "LockSmiGlobalEn .....\n"));
- SmiLockReg = GetRegisterById (REGISTER_ID_SMI_GBL_EN_LOCK);
- if (SmiLockReg == NULL) {
- DEBUG ((DEBUG_ERROR, "SMI global enable lock reg not found.\n"));
- return;
- }
- //
- // Set SMM SMI lock in S3 path
- //
- if ((SmiLockReg->Address.AccessSize == EFI_ACPI_3_0_DWORD) &&
- (SmiLockReg->Address.Address != 0) &&
- (SmiLockReg->Address.RegisterBitWidth == 1) &&
- (SmiLockReg->Address.AddressSpaceId == EFI_ACPI_3_0_SYSTEM_MEMORY) &&
- (SmiLockReg->Value == 1))
- {
- DEBUG ((DEBUG_ERROR, "LockSmiGlobalEn ....is locked\n"));
- MmioOr32 ((UINT32)SmiLockReg->Address.Address, 1 << SmiLockReg->Address.RegisterBitOffset);
- } else {
- DEBUG ((DEBUG_ERROR, "Unexpected SMM SMI lock register, need enhancement here.\n"));
- }
- }
- /**
- Check and set SMM feature lock bit and code check enable bit
- in S3 path.
- **/
- VOID
- SmmFeatureLockOnS3 (
- VOID
- )
- {
- if (mSmmFeatureControl != 0) {
- return;
- }
- mSmmFeatureControl = AsmReadMsr64 (MSR_SMM_FEATURE_CONTROL);
- if ((mSmmFeatureControl & 0x5) != 0x5) {
- //
- // Set Lock bit [BIT0] for this register and SMM code check enable bit [BIT2]
- //
- AsmWriteMsr64 (MSR_SMM_FEATURE_CONTROL, mSmmFeatureControl | 0x5);
- }
- mSmmFeatureControl = AsmReadMsr64 (MSR_SMM_FEATURE_CONTROL);
- }
- /**
- Function to program SMRR base and mask.
- @param[in] ProcedureArgument Pointer to SMRR_BASE_MASK structure.
- **/
- VOID
- EFIAPI
- SetSmrr (
- IN VOID *ProcedureArgument
- )
- {
- if (ProcedureArgument != NULL) {
- AsmWriteMsr64 (MSR_IA32_SMRR_PHYSBASE, ((SMRR_BASE_MASK *)ProcedureArgument)->Base);
- AsmWriteMsr64 (MSR_IA32_SMRR_PHYSMASK, ((SMRR_BASE_MASK *)ProcedureArgument)->Mask);
- }
- }
- /**
- Set SMRR in S3 path.
- **/
- VOID
- SetSmrrOnS3 (
- VOID
- )
- {
- EFI_STATUS Status;
- SMRR_BASE_MASK Arguments;
- UINTN Index;
- UINT32 SmmBase;
- UINT32 SmmSize;
- if ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSBASE) != 0) && ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSMASK) & BIT11) != 0)) {
- return;
- }
- SmmBase = (UINT32)(UINTN)mSmramHob->Descriptor[0].PhysicalStart;
- SmmSize = (UINT32)(UINTN)mSmramHob->Descriptor[0].PhysicalSize;
- if ((mSmramHob->NumberOfSmmReservedRegions > 2) || (mSmramHob->NumberOfSmmReservedRegions == 0)) {
- DEBUG ((DEBUG_ERROR, "%d SMM ranges are not supported.\n", mSmramHob->NumberOfSmmReservedRegions));
- return;
- } else if (mSmramHob->NumberOfSmmReservedRegions == 2) {
- if ((mSmramHob->Descriptor[1].PhysicalStart + mSmramHob->Descriptor[1].PhysicalSize) == SmmBase) {
- SmmBase = (UINT32)(UINTN)mSmramHob->Descriptor[1].PhysicalStart;
- } else if (mSmramHob->Descriptor[1].PhysicalStart != (SmmBase + SmmSize)) {
- DEBUG ((DEBUG_ERROR, "Two SMM regions are not continous.\n"));
- return;
- }
- SmmSize += (UINT32)(UINTN)mSmramHob->Descriptor[1].PhysicalSize;
- }
- if ((SmmBase == 0) || (SmmSize < SIZE_4KB)) {
- DEBUG ((DEBUG_ERROR, "Invalid SMM range.\n"));
- return;
- }
- //
- // SMRR size must be of length 2^n
- // SMRR base alignment cannot be less than SMRR length
- //
- if ((SmmSize != GetPowerOfTwo32 (SmmSize)) || ((SmmBase & ~(SmmSize - 1)) != SmmBase)) {
- DEBUG ((DEBUG_ERROR, " Invalid SMM range.\n"));
- return;
- }
- //
- // Calculate smrr base, mask and pass them as arguments.
- //
- Arguments.Base = (SmmSize | MTRR_CACHE_WRITE_BACK);
- Arguments.Mask = (~(SmmSize - 1) & EFI_MSR_SMRR_MASK);
- //
- // Set SMRR valid bit
- //
- Arguments.Mask |= BIT11;
- //
- // Program smrr base and mask on BSP first and then on APs
- //
- SetSmrr (&Arguments);
- for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
- if (Index != gSmst->CurrentlyExecutingCpu) {
- Status = gSmst->SmmStartupThisAp (SetSmrr, Index, (VOID *)&Arguments);
- if (EFI_ERROR (Status)) {
- DEBUG ((DEBUG_ERROR, "Programming SMRR on AP# %d status: %r\n", Index, Status));
- }
- }
- }
- }
- /**
- Software SMI callback for restoring SMRR base and mask in S3 path.
- @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
- @param[in] Context Points to an optional handler context which was specified when the
- handler was registered.
- @param[in, out] CommBuffer A pointer to a collection of data in memory that will
- be conveyed from a non-SMM environment into an SMM environment.
- @param[in, out] CommBufferSize The size of the CommBuffer.
- @retval EFI_SUCCESS The interrupt was handled successfully.
- **/
- EFI_STATUS
- EFIAPI
- BlSwSmiHandler (
- IN EFI_HANDLE DispatchHandle,
- IN CONST VOID *Context,
- IN OUT VOID *CommBuffer,
- IN OUT UINTN *CommBufferSize
- )
- {
- SetSmrrOnS3 ();
- SmmFeatureLockOnS3 ();
- LockSmiGlobalEn ();
- return EFI_SUCCESS;
- }
- /**
- Lock SMI in this SMM ready to lock event.
- @param Protocol Points to the protocol's unique identifier
- @param Interface Points to the interface instance
- @param Handle The handle on which the interface was installed
- @retval EFI_SUCCESS SmmEventCallback runs successfully
- @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
- **/
- EFI_STATUS
- EFIAPI
- BlSupportSmmReadyToLockCallback (
- IN CONST EFI_GUID *Protocol,
- IN VOID *Interface,
- IN EFI_HANDLE Handle
- )
- {
- //
- // Set SMM SMI lock
- //
- LockSmiGlobalEn ();
- return EFI_SUCCESS;
- }
- /**
- The driver's entry point.
- @param[in] ImageHandle The firmware allocated handle for the EFI image.
- @param[in] SystemTable A pointer to the EFI System Table.
- @retval EFI_SUCCESS The entry point is executed successfully.
- @retval Others Some error occurs when executing this entry point.
- **/
- EFI_STATUS
- EFIAPI
- BlSupportSmm (
- IN EFI_HANDLE ImageHandle,
- IN EFI_SYSTEM_TABLE *SystemTable
- )
- {
- EFI_STATUS Status;
- EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch;
- EFI_SMM_SW_REGISTER_CONTEXT SwContext;
- EFI_HANDLE SwHandle;
- EFI_HOB_GUID_TYPE *GuidHob;
- VOID *SmmHob;
- VOID *Registration;
- //
- // Get SMM S3 communication hob and save it
- //
- GuidHob = GetFirstGuidHob (&gS3CommunicationGuid);
- if (GuidHob != NULL) {
- SmmHob = (VOID *)(GET_GUID_HOB_DATA (GuidHob));
- CopyMem (&mPldS3Hob, SmmHob, GET_GUID_HOB_DATA_SIZE (GuidHob));
- } else {
- return EFI_NOT_FOUND;
- }
- if (mPldS3Hob.PldAcpiS3Enable) {
- // Other drivers will take care of S3.
- return EFI_SUCCESS;
- }
- //
- // Get smram hob and save it
- //
- GuidHob = GetFirstGuidHob (&gEfiSmmSmramMemoryGuid);
- if (GuidHob != NULL) {
- SmmHob = (VOID *)(GET_GUID_HOB_DATA (GuidHob));
- mSmramHob = AllocatePool (GET_GUID_HOB_DATA_SIZE (GuidHob));
- if (mSmramHob == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- CopyMem (mSmramHob, SmmHob, GET_GUID_HOB_DATA_SIZE (GuidHob));
- } else {
- return EFI_NOT_FOUND;
- }
- //
- // Get SMM register hob and save it
- //
- GuidHob = GetFirstGuidHob (&gSmmRegisterInfoGuid);
- if (GuidHob != NULL) {
- SmmHob = (VOID *)(GET_GUID_HOB_DATA (GuidHob));
- mSmmRegisterHob = AllocatePool (GET_GUID_HOB_DATA_SIZE (GuidHob));
- if (mSmmRegisterHob == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- CopyMem (mSmmRegisterHob, SmmHob, GET_GUID_HOB_DATA_SIZE (GuidHob));
- } else {
- return EFI_NOT_FOUND;
- }
- //
- // Get the Sw dispatch protocol and register SMI handler.
- //
- Status = gSmst->SmmLocateProtocol (&gEfiSmmSwDispatch2ProtocolGuid, NULL, (VOID **)&SwDispatch);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- SwContext.SwSmiInputValue = (UINTN)-1;
- Status = SwDispatch->Register (SwDispatch, BlSwSmiHandler, &SwContext, &SwHandle);
- if (EFI_ERROR (Status)) {
- DEBUG ((DEBUG_ERROR, "Registering S3 smi handler failed: %r\n", Status));
- return Status;
- }
- //
- // Register SMM ready to lock callback
- //
- Status = gSmst->SmmRegisterProtocolNotify (
- &gEfiSmmReadyToLockProtocolGuid,
- BlSupportSmmReadyToLockCallback,
- &Registration
- );
- ASSERT_EFI_ERROR (Status);
- SaveSmmInfoForS3 ((UINT8)SwContext.SwSmiInputValue);
- return EFI_SUCCESS;
- }
|