2 This driver is used for SMM S3 support for the bootloader that
4 The payload would save SMM rebase info to SMM communication area.
5 The bootloader is expected to rebase the SMM and trigger SMI by
6 writting 0xB2 port with given value from SMM communication area.
7 The paylaod SMM handler got chance to restore regs in S3 path.
9 Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
10 SPDX-License-Identifier: BSD-2-Clause-Patent
14 #include <BlSupportSmm.h>
16 PLD_S3_COMMUNICATION mPldS3Hob
;
17 EFI_SMRAM_HOB_DESCRIPTOR_BLOCK
*mSmramHob
= NULL
;
18 PLD_SMM_REGISTERS
*mSmmRegisterHob
= NULL
;;
19 UINT64 mSmmFeatureControl
= 0;
22 Save SMM rebase and SMI handler information to SMM communication area
24 The function detects SMM communication region for boot loader, if it is detected, it
25 will save SMM rebase information and S3 SMI handler information to SMM communication
26 region. Bootloader should consume these information in S3 path to restore smm base,
27 and write the 0xB2 port to trigger SMI so that payload could resume S3 registers.
29 @param[in] BlSwSmiHandlerInput Value written to 0xB2 to trigger SMI handler.
31 @retval EFI_SUCCESS Save SMM info success.
32 @retval Others Failed to save SMM info.
36 IN UINT8 BlSwSmiHandlerInput
40 EFI_PROCESSOR_INFORMATION ProcessorInfo
;
41 EFI_MP_SERVICES_PROTOCOL
*MpService
;
42 CPU_SMMBASE
*SmmBaseInfo
;
43 PLD_TO_BL_SMM_INFO
*PldSmmInfo
;
46 PldSmmInfo
= (PLD_TO_BL_SMM_INFO
*)(UINTN
)mPldS3Hob
.CommBuffer
.PhysicalStart
;
47 PldSmmInfo
->Header
.Header
.HobLength
= (UINT16
)(sizeof (PLD_TO_BL_SMM_INFO
) + gSmst
->NumberOfCpus
* sizeof (CPU_SMMBASE
));
48 for (Index
= 0; Index
< mSmramHob
->NumberOfSmmReservedRegions
; Index
++) {
49 if ((mPldS3Hob
.CommBuffer
.PhysicalStart
>= mSmramHob
->Descriptor
[Index
].PhysicalStart
) &&
50 (mPldS3Hob
.CommBuffer
.PhysicalStart
< mSmramHob
->Descriptor
[Index
].PhysicalStart
+ mSmramHob
->Descriptor
[Index
].PhysicalSize
)) {
54 if (Index
== mSmramHob
->NumberOfSmmReservedRegions
) {
59 // Make sure the dedicated region for SMM info communication whose attribute is "allocated" (i.e., excluded from SMM memory service)
61 if ((mSmramHob
->Descriptor
[Index
].RegionState
& EFI_ALLOCATED
) == 0) {
62 DEBUG ((DEBUG_ERROR
, "SMM communication region not set to EFI_ALLOCATED\n"));
63 return EFI_INVALID_PARAMETER
;
66 if (((UINTN
)PldSmmInfo
+ PldSmmInfo
->Header
.Header
.HobLength
) > (mSmramHob
->Descriptor
[Index
].PhysicalStart
+ mSmramHob
->Descriptor
[Index
].PhysicalSize
)) {
67 DEBUG ((DEBUG_ERROR
, "SMM communication buffer (0x%x) is too small.\n", (UINTN
)PldSmmInfo
+ PldSmmInfo
->Header
.Header
.HobLength
));
68 return EFI_BUFFER_TOO_SMALL
;
71 CopyGuid (&PldSmmInfo
->Header
.Name
, &gS3CommunicationGuid
);
72 PldSmmInfo
->Header
.Header
.HobType
= EFI_HOB_TYPE_GUID_EXTENSION
;
73 PldSmmInfo
->S3Info
.SwSmiTriggerValue
= BlSwSmiHandlerInput
;
76 // Save APIC ID and SMM base
78 Status
= gBS
->LocateProtocol (&gEfiMpServiceProtocolGuid
, NULL
, (VOID
**)&MpService
);
79 if (EFI_ERROR(Status
)) {
82 PldSmmInfo
->S3Info
.CpuCount
= (UINT32
)gSmst
->NumberOfCpus
;
83 SmmBaseInfo
= &PldSmmInfo
->S3Info
.SmmBase
[0];
84 for (Index
= 0; Index
< gSmst
->NumberOfCpus
; Index
++) {
85 Status
= MpService
->GetProcessorInfo (MpService
, Index
, &ProcessorInfo
);
86 if (EFI_ERROR(Status
)) {
90 SmmBaseInfo
->ApicId
= (UINT32
)(UINTN
)ProcessorInfo
.ProcessorId
;
91 SmmBaseInfo
->SmmBase
= (UINT32
)(UINTN
)gSmst
->CpuSaveState
[Index
] - SMRAM_SAVE_STATE_MAP_OFFSET
;
92 DEBUG ((DEBUG_INFO
, "CPU%d ID:%02X Base: %08X\n", Index
, SmmBaseInfo
->ApicId
, SmmBaseInfo
->SmmBase
));
101 Get specified SMI register based on given register ID
103 @param[in] Id The register ID to get.
105 @retval NULL The register is not found
109 PLD_GENERIC_REGISTER
*
116 for (Index
= 0; Index
< mSmmRegisterHob
->Count
; Index
++) {
117 if (mSmmRegisterHob
->Registers
[Index
].Id
== Id
) {
118 return &mSmmRegisterHob
->Registers
[Index
];
125 Set SMM SMI Global enable lock
133 PLD_GENERIC_REGISTER
*SmiLockReg
;
135 DEBUG ((DEBUG_ERROR
, "LockSmiGlobalEn .....\n"));
137 SmiLockReg
= GetRegisterById (REGISTER_ID_SMI_GBL_EN_LOCK
);
138 if (SmiLockReg
== NULL
) {
139 DEBUG ((DEBUG_ERROR
, "SMI global enable lock reg not found.\n"));
144 // Set SMM SMI lock in S3 path
146 if ((SmiLockReg
->Address
.AccessSize
== EFI_ACPI_3_0_DWORD
) &&
147 (SmiLockReg
->Address
.Address
!= 0) &&
148 (SmiLockReg
->Address
.RegisterBitWidth
== 1) &&
149 (SmiLockReg
->Address
.AddressSpaceId
== EFI_ACPI_3_0_SYSTEM_MEMORY
) &&
150 (SmiLockReg
->Value
== 1)) {
151 DEBUG ((DEBUG_ERROR
, "LockSmiGlobalEn ....is locked\n"));
153 MmioOr32 ((UINT32
)SmiLockReg
->Address
.Address
, 1 << SmiLockReg
->Address
.RegisterBitOffset
);
155 DEBUG ((DEBUG_ERROR
, "Unexpected SMM SMI lock register, need enhancement here.\n"));
160 Check and set SMM feature lock bit and code check enable bit
170 if (mSmmFeatureControl
!= 0) {
174 mSmmFeatureControl
= AsmReadMsr64(MSR_SMM_FEATURE_CONTROL
);
175 if ((mSmmFeatureControl
& 0x5) != 0x5) {
177 // Set Lock bit [BIT0] for this register and SMM code check enable bit [BIT2]
179 AsmWriteMsr64 (MSR_SMM_FEATURE_CONTROL
, mSmmFeatureControl
| 0x5);
181 mSmmFeatureControl
= AsmReadMsr64(MSR_SMM_FEATURE_CONTROL
);
187 Function to program SMRR base and mask.
189 @param[in] ProcedureArgument Pointer to SMRR_BASE_MASK structure.
193 IN VOID
*ProcedureArgument
196 if (ProcedureArgument
!= NULL
) {
197 AsmWriteMsr64 (MSR_IA32_SMRR_PHYSBASE
, ((SMRR_BASE_MASK
*)ProcedureArgument
)->Base
);
198 AsmWriteMsr64 (MSR_IA32_SMRR_PHYSMASK
, ((SMRR_BASE_MASK
*)ProcedureArgument
)->Mask
);
212 SMRR_BASE_MASK Arguments
;
217 if ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSBASE
) != 0) && ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSMASK
) & BIT11
) != 0)) {
221 SmmBase
= (UINT32
)(UINTN
)mSmramHob
->Descriptor
[0].PhysicalStart
;
222 SmmSize
= (UINT32
)(UINTN
)mSmramHob
->Descriptor
[0].PhysicalSize
;
223 if ((mSmramHob
->NumberOfSmmReservedRegions
> 2) || (mSmramHob
->NumberOfSmmReservedRegions
== 0)) {
224 DEBUG ((DEBUG_ERROR
, "%d SMM ranges are not supported.\n", mSmramHob
->NumberOfSmmReservedRegions
));
226 } else if (mSmramHob
->NumberOfSmmReservedRegions
== 2) {
227 if ((mSmramHob
->Descriptor
[1].PhysicalStart
+ mSmramHob
->Descriptor
[1].PhysicalSize
) == SmmBase
){
228 SmmBase
= (UINT32
)(UINTN
)mSmramHob
->Descriptor
[1].PhysicalStart
;
229 } else if (mSmramHob
->Descriptor
[1].PhysicalStart
!= (SmmBase
+ SmmSize
)) {
230 DEBUG ((DEBUG_ERROR
, "Two SMM regions are not continous.\n"));
233 SmmSize
+= (UINT32
)(UINTN
)mSmramHob
->Descriptor
[1].PhysicalSize
;
236 if ((SmmBase
== 0) || (SmmSize
< SIZE_4KB
)) {
237 DEBUG ((DEBUG_ERROR
, "Invalid SMM range.\n"));
242 // SMRR size must be of length 2^n
243 // SMRR base alignment cannot be less than SMRR length
245 if ((SmmSize
!= GetPowerOfTwo32 (SmmSize
)) || ((SmmBase
& ~(SmmSize
- 1)) != SmmBase
)) {
246 DEBUG ((DEBUG_ERROR
, " Invalid SMM range.\n"));
251 // Calculate smrr base, mask and pass them as arguments.
253 Arguments
.Base
= (SmmSize
| MTRR_CACHE_WRITE_BACK
);
254 Arguments
.Mask
= (~(SmmSize
- 1) & EFI_MSR_SMRR_MASK
);
257 // Set SMRR valid bit
259 Arguments
.Mask
|= BIT11
;
262 // Program smrr base and mask on BSP first and then on APs
265 for (Index
= 0; Index
< gSmst
->NumberOfCpus
; Index
++) {
266 if (Index
!= gSmst
->CurrentlyExecutingCpu
) {
267 Status
= gSmst
->SmmStartupThisAp (SetSmrr
, Index
, (VOID
*)&Arguments
);
268 if (EFI_ERROR(Status
)) {
269 DEBUG ((DEBUG_ERROR
, "Programming SMRR on AP# %d status: %r\n", Index
, Status
));
277 Software SMI callback for restoring SMRR base and mask in S3 path.
279 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
280 @param[in] Context Points to an optional handler context which was specified when the
281 handler was registered.
282 @param[in, out] CommBuffer A pointer to a collection of data in memory that will
283 be conveyed from a non-SMM environment into an SMM environment.
284 @param[in, out] CommBufferSize The size of the CommBuffer.
286 @retval EFI_SUCCESS The interrupt was handled successfully.
292 IN EFI_HANDLE DispatchHandle
,
293 IN CONST VOID
*Context
,
294 IN OUT VOID
*CommBuffer
,
295 IN OUT UINTN
*CommBufferSize
299 SmmFeatureLockOnS3 ();
307 Lock SMI in this SMM ready to lock event.
309 @param Protocol Points to the protocol's unique identifier
310 @param Interface Points to the interface instance
311 @param Handle The handle on which the interface was installed
313 @retval EFI_SUCCESS SmmEventCallback runs successfully
314 @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
318 BlSupportSmmReadyToLockCallback (
319 IN CONST EFI_GUID
*Protocol
,
333 The driver's entry point.
335 @param[in] ImageHandle The firmware allocated handle for the EFI image.
336 @param[in] SystemTable A pointer to the EFI System Table.
338 @retval EFI_SUCCESS The entry point is executed successfully.
339 @retval Others Some error occurs when executing this entry point.
345 IN EFI_HANDLE ImageHandle
,
346 IN EFI_SYSTEM_TABLE
*SystemTable
350 EFI_SMM_SW_DISPATCH2_PROTOCOL
*SwDispatch
;
351 EFI_SMM_SW_REGISTER_CONTEXT SwContext
;
353 EFI_HOB_GUID_TYPE
*GuidHob
;
358 // Get SMM S3 communication hob and save it
360 GuidHob
= GetFirstGuidHob (&gS3CommunicationGuid
);
361 if (GuidHob
!= NULL
) {
362 SmmHob
= (VOID
*) (GET_GUID_HOB_DATA(GuidHob
));
363 CopyMem (&mPldS3Hob
, SmmHob
, GET_GUID_HOB_DATA_SIZE(GuidHob
));
365 return EFI_NOT_FOUND
;
368 if (mPldS3Hob
.PldAcpiS3Enable
) {
369 // Other drivers will take care of S3.
374 // Get smram hob and save it
376 GuidHob
= GetFirstGuidHob (&gEfiSmmSmramMemoryGuid
);
377 if (GuidHob
!= NULL
) {
378 SmmHob
= (VOID
*) (GET_GUID_HOB_DATA(GuidHob
));
379 mSmramHob
= AllocatePool (GET_GUID_HOB_DATA_SIZE(GuidHob
));
380 if (mSmramHob
== NULL
) {
381 return EFI_OUT_OF_RESOURCES
;
383 CopyMem (mSmramHob
, SmmHob
, GET_GUID_HOB_DATA_SIZE(GuidHob
));
385 return EFI_NOT_FOUND
;
389 // Get SMM register hob and save it
391 GuidHob
= GetFirstGuidHob (&gSmmRegisterInfoGuid
);
392 if (GuidHob
!= NULL
) {
393 SmmHob
= (VOID
*) (GET_GUID_HOB_DATA(GuidHob
));
394 mSmmRegisterHob
= AllocatePool (GET_GUID_HOB_DATA_SIZE(GuidHob
));
395 if (mSmmRegisterHob
== NULL
) {
396 return EFI_OUT_OF_RESOURCES
;
398 CopyMem (mSmmRegisterHob
, SmmHob
, GET_GUID_HOB_DATA_SIZE(GuidHob
));
400 return EFI_NOT_FOUND
;
404 // Get the Sw dispatch protocol and register SMI handler.
406 Status
= gSmst
->SmmLocateProtocol (&gEfiSmmSwDispatch2ProtocolGuid
, NULL
, (VOID
**)&SwDispatch
);
407 if (EFI_ERROR (Status
)) {
410 SwContext
.SwSmiInputValue
= (UINTN
) -1;
411 Status
= SwDispatch
->Register (SwDispatch
, BlSwSmiHandler
, &SwContext
, &SwHandle
);
412 if (EFI_ERROR (Status
)) {
413 DEBUG ((DEBUG_ERROR
, "Registering S3 smi handler failed: %r\n", Status
));
418 // Register SMM ready to lock callback
420 Status
= gSmst
->SmmRegisterProtocolNotify (
421 &gEfiSmmReadyToLockProtocolGuid
,
422 BlSupportSmmReadyToLockCallback
,
425 ASSERT_EFI_ERROR (Status
);
427 SaveSmmInfoForS3 ((UINT8
)SwContext
.SwSmiInputValue
);