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
))
56 if (Index
== mSmramHob
->NumberOfSmmReservedRegions
) {
61 // Make sure the dedicated region for SMM info communication whose attribute is "allocated" (i.e., excluded from SMM memory service)
63 if ((mSmramHob
->Descriptor
[Index
].RegionState
& EFI_ALLOCATED
) == 0) {
64 DEBUG ((DEBUG_ERROR
, "SMM communication region not set to EFI_ALLOCATED\n"));
65 return EFI_INVALID_PARAMETER
;
68 if (((UINTN
)PldSmmInfo
+ PldSmmInfo
->Header
.Header
.HobLength
) > (mSmramHob
->Descriptor
[Index
].PhysicalStart
+ mSmramHob
->Descriptor
[Index
].PhysicalSize
)) {
69 DEBUG ((DEBUG_ERROR
, "SMM communication buffer (0x%x) is too small.\n", (UINTN
)PldSmmInfo
+ PldSmmInfo
->Header
.Header
.HobLength
));
70 return EFI_BUFFER_TOO_SMALL
;
73 CopyGuid (&PldSmmInfo
->Header
.Name
, &gS3CommunicationGuid
);
74 PldSmmInfo
->Header
.Header
.HobType
= EFI_HOB_TYPE_GUID_EXTENSION
;
75 PldSmmInfo
->S3Info
.SwSmiTriggerValue
= BlSwSmiHandlerInput
;
78 // Save APIC ID and SMM base
80 Status
= gBS
->LocateProtocol (&gEfiMpServiceProtocolGuid
, NULL
, (VOID
**)&MpService
);
81 if (EFI_ERROR (Status
)) {
85 PldSmmInfo
->S3Info
.CpuCount
= (UINT32
)gSmst
->NumberOfCpus
;
86 SmmBaseInfo
= &PldSmmInfo
->S3Info
.SmmBase
[0];
87 for (Index
= 0; Index
< gSmst
->NumberOfCpus
; Index
++) {
88 Status
= MpService
->GetProcessorInfo (MpService
, Index
, &ProcessorInfo
);
89 if (EFI_ERROR (Status
)) {
93 SmmBaseInfo
->ApicId
= (UINT32
)(UINTN
)ProcessorInfo
.ProcessorId
;
94 SmmBaseInfo
->SmmBase
= (UINT32
)(UINTN
)gSmst
->CpuSaveState
[Index
] - SMRAM_SAVE_STATE_MAP_OFFSET
;
95 DEBUG ((DEBUG_INFO
, "CPU%d ID:%02X Base: %08X\n", Index
, SmmBaseInfo
->ApicId
, SmmBaseInfo
->SmmBase
));
103 Get specified SMI register based on given register ID
105 @param[in] Id The register ID to get.
107 @retval NULL The register is not found
111 PLD_GENERIC_REGISTER
*
118 for (Index
= 0; Index
< mSmmRegisterHob
->Count
; Index
++) {
119 if (mSmmRegisterHob
->Registers
[Index
].Id
== Id
) {
120 return &mSmmRegisterHob
->Registers
[Index
];
128 Set SMM SMI Global enable lock
136 PLD_GENERIC_REGISTER
*SmiLockReg
;
138 DEBUG ((DEBUG_ERROR
, "LockSmiGlobalEn .....\n"));
140 SmiLockReg
= GetRegisterById (REGISTER_ID_SMI_GBL_EN_LOCK
);
141 if (SmiLockReg
== NULL
) {
142 DEBUG ((DEBUG_ERROR
, "SMI global enable lock reg not found.\n"));
147 // Set SMM SMI lock in S3 path
149 if ((SmiLockReg
->Address
.AccessSize
== EFI_ACPI_3_0_DWORD
) &&
150 (SmiLockReg
->Address
.Address
!= 0) &&
151 (SmiLockReg
->Address
.RegisterBitWidth
== 1) &&
152 (SmiLockReg
->Address
.AddressSpaceId
== EFI_ACPI_3_0_SYSTEM_MEMORY
) &&
153 (SmiLockReg
->Value
== 1))
155 DEBUG ((DEBUG_ERROR
, "LockSmiGlobalEn ....is locked\n"));
157 MmioOr32 ((UINT32
)SmiLockReg
->Address
.Address
, 1 << SmiLockReg
->Address
.RegisterBitOffset
);
159 DEBUG ((DEBUG_ERROR
, "Unexpected SMM SMI lock register, need enhancement here.\n"));
164 Check and set SMM feature lock bit and code check enable bit
173 if (mSmmFeatureControl
!= 0) {
177 mSmmFeatureControl
= AsmReadMsr64 (MSR_SMM_FEATURE_CONTROL
);
178 if ((mSmmFeatureControl
& 0x5) != 0x5) {
180 // Set Lock bit [BIT0] for this register and SMM code check enable bit [BIT2]
182 AsmWriteMsr64 (MSR_SMM_FEATURE_CONTROL
, mSmmFeatureControl
| 0x5);
185 mSmmFeatureControl
= AsmReadMsr64 (MSR_SMM_FEATURE_CONTROL
);
189 Function to program SMRR base and mask.
191 @param[in] ProcedureArgument Pointer to SMRR_BASE_MASK structure.
195 IN VOID
*ProcedureArgument
198 if (ProcedureArgument
!= NULL
) {
199 AsmWriteMsr64 (MSR_IA32_SMRR_PHYSBASE
, ((SMRR_BASE_MASK
*)ProcedureArgument
)->Base
);
200 AsmWriteMsr64 (MSR_IA32_SMRR_PHYSMASK
, ((SMRR_BASE_MASK
*)ProcedureArgument
)->Mask
);
214 SMRR_BASE_MASK Arguments
;
219 if ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSBASE
) != 0) && ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSMASK
) & BIT11
) != 0)) {
223 SmmBase
= (UINT32
)(UINTN
)mSmramHob
->Descriptor
[0].PhysicalStart
;
224 SmmSize
= (UINT32
)(UINTN
)mSmramHob
->Descriptor
[0].PhysicalSize
;
225 if ((mSmramHob
->NumberOfSmmReservedRegions
> 2) || (mSmramHob
->NumberOfSmmReservedRegions
== 0)) {
226 DEBUG ((DEBUG_ERROR
, "%d SMM ranges are not supported.\n", mSmramHob
->NumberOfSmmReservedRegions
));
228 } else if (mSmramHob
->NumberOfSmmReservedRegions
== 2) {
229 if ((mSmramHob
->Descriptor
[1].PhysicalStart
+ mSmramHob
->Descriptor
[1].PhysicalSize
) == SmmBase
) {
230 SmmBase
= (UINT32
)(UINTN
)mSmramHob
->Descriptor
[1].PhysicalStart
;
231 } else if (mSmramHob
->Descriptor
[1].PhysicalStart
!= (SmmBase
+ SmmSize
)) {
232 DEBUG ((DEBUG_ERROR
, "Two SMM regions are not continous.\n"));
236 SmmSize
+= (UINT32
)(UINTN
)mSmramHob
->Descriptor
[1].PhysicalSize
;
239 if ((SmmBase
== 0) || (SmmSize
< SIZE_4KB
)) {
240 DEBUG ((DEBUG_ERROR
, "Invalid SMM range.\n"));
245 // SMRR size must be of length 2^n
246 // SMRR base alignment cannot be less than SMRR length
248 if ((SmmSize
!= GetPowerOfTwo32 (SmmSize
)) || ((SmmBase
& ~(SmmSize
- 1)) != SmmBase
)) {
249 DEBUG ((DEBUG_ERROR
, " Invalid SMM range.\n"));
254 // Calculate smrr base, mask and pass them as arguments.
256 Arguments
.Base
= (SmmSize
| MTRR_CACHE_WRITE_BACK
);
257 Arguments
.Mask
= (~(SmmSize
- 1) & EFI_MSR_SMRR_MASK
);
260 // Set SMRR valid bit
262 Arguments
.Mask
|= BIT11
;
265 // Program smrr base and mask on BSP first and then on APs
267 SetSmrr (&Arguments
);
268 for (Index
= 0; Index
< gSmst
->NumberOfCpus
; Index
++) {
269 if (Index
!= gSmst
->CurrentlyExecutingCpu
) {
270 Status
= gSmst
->SmmStartupThisAp (SetSmrr
, Index
, (VOID
*)&Arguments
);
271 if (EFI_ERROR (Status
)) {
272 DEBUG ((DEBUG_ERROR
, "Programming SMRR on AP# %d status: %r\n", Index
, Status
));
279 Software SMI callback for restoring SMRR base and mask in S3 path.
281 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
282 @param[in] Context Points to an optional handler context which was specified when the
283 handler was registered.
284 @param[in, out] CommBuffer A pointer to a collection of data in memory that will
285 be conveyed from a non-SMM environment into an SMM environment.
286 @param[in, out] CommBufferSize The size of the CommBuffer.
288 @retval EFI_SUCCESS The interrupt was handled successfully.
294 IN EFI_HANDLE DispatchHandle
,
295 IN CONST VOID
*Context
,
296 IN OUT VOID
*CommBuffer
,
297 IN OUT UINTN
*CommBufferSize
301 SmmFeatureLockOnS3 ();
308 Lock SMI in this SMM ready to lock event.
310 @param Protocol Points to the protocol's unique identifier
311 @param Interface Points to the interface instance
312 @param Handle The handle on which the interface was installed
314 @retval EFI_SUCCESS SmmEventCallback runs successfully
315 @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
319 BlSupportSmmReadyToLockCallback (
320 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
;
384 CopyMem (mSmramHob
, SmmHob
, GET_GUID_HOB_DATA_SIZE (GuidHob
));
386 return EFI_NOT_FOUND
;
390 // Get SMM register hob and save it
392 GuidHob
= GetFirstGuidHob (&gSmmRegisterInfoGuid
);
393 if (GuidHob
!= NULL
) {
394 SmmHob
= (VOID
*)(GET_GUID_HOB_DATA (GuidHob
));
395 mSmmRegisterHob
= AllocatePool (GET_GUID_HOB_DATA_SIZE (GuidHob
));
396 if (mSmmRegisterHob
== NULL
) {
397 return EFI_OUT_OF_RESOURCES
;
400 CopyMem (mSmmRegisterHob
, SmmHob
, GET_GUID_HOB_DATA_SIZE (GuidHob
));
402 return EFI_NOT_FOUND
;
406 // Get the Sw dispatch protocol and register SMI handler.
408 Status
= gSmst
->SmmLocateProtocol (&gEfiSmmSwDispatch2ProtocolGuid
, NULL
, (VOID
**)&SwDispatch
);
409 if (EFI_ERROR (Status
)) {
413 SwContext
.SwSmiInputValue
= (UINTN
)-1;
414 Status
= SwDispatch
->Register (SwDispatch
, BlSwSmiHandler
, &SwContext
, &SwHandle
);
415 if (EFI_ERROR (Status
)) {
416 DEBUG ((DEBUG_ERROR
, "Registering S3 smi handler failed: %r\n", Status
));
421 // Register SMM ready to lock callback
423 Status
= gSmst
->SmmRegisterProtocolNotify (
424 &gEfiSmmReadyToLockProtocolGuid
,
425 BlSupportSmmReadyToLockCallback
,
428 ASSERT_EFI_ERROR (Status
);
430 SaveSmmInfoForS3 ((UINT8
)SwContext
.SwSmiInputValue
);