]> git.proxmox.com Git - mirror_edk2.git/blob - UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c
UefiPayloadPkg: Add bootloader SMM support module
[mirror_edk2.git] / UefiPayloadPkg / BlSupportSmm / BlSupportSmm.c
1 /** @file
2 This driver is used for SMM S3 support for the bootloader that
3 doesn't support SMM.
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.
8
9 Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
10 SPDX-License-Identifier: BSD-2-Clause-Patent
11
12 **/
13
14 #include <BlSupportSmm.h>
15
16 PLD_S3_COMMUNICATION mPldS3Hob;
17 EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *mSmramHob = NULL;
18 PLD_SMM_REGISTERS *mSmmRegisterHob = NULL;;
19 UINT64 mSmmFeatureControl = 0;
20
21 /**
22 Save SMM rebase and SMI handler information to SMM communication area
23
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.
28
29 @param[in] BlSwSmiHandlerInput Value written to 0xB2 to trigger SMI handler.
30
31 @retval EFI_SUCCESS Save SMM info success.
32 @retval Others Failed to save SMM info.
33 **/
34 EFI_STATUS
35 SaveSmmInfoForS3 (
36 IN UINT8 BlSwSmiHandlerInput
37 )
38 {
39 EFI_STATUS Status;
40 EFI_PROCESSOR_INFORMATION ProcessorInfo;
41 EFI_MP_SERVICES_PROTOCOL *MpService;
42 CPU_SMMBASE *SmmBaseInfo;
43 PLD_TO_BL_SMM_INFO *PldSmmInfo;
44 UINTN Index;
45
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)) {
51 break;
52 }
53 }
54 if (Index == mSmramHob->NumberOfSmmReservedRegions) {
55 return EFI_NOT_FOUND;
56 }
57
58 //
59 // Make sure the dedicated region for SMM info communication whose attribute is "allocated" (i.e., excluded from SMM memory service)
60 //
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;
64 }
65
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;
69 }
70
71 CopyGuid (&PldSmmInfo->Header.Name, &gS3CommunicationGuid);
72 PldSmmInfo->Header.Header.HobType = EFI_HOB_TYPE_GUID_EXTENSION;
73 PldSmmInfo->S3Info.SwSmiTriggerValue = BlSwSmiHandlerInput;
74
75 //
76 // Save APIC ID and SMM base
77 //
78 Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpService);
79 if (EFI_ERROR(Status)) {
80 return Status;
81 }
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)) {
87 return Status;
88 }
89
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));
93 SmmBaseInfo++;
94 }
95
96 return EFI_SUCCESS;
97 }
98
99
100 /**
101 Get specified SMI register based on given register ID
102
103 @param[in] Id The register ID to get.
104
105 @retval NULL The register is not found
106 @return smi register
107
108 **/
109 PLD_GENERIC_REGISTER *
110 GetRegisterById (
111 UINT64 Id
112 )
113 {
114 UINT32 Index;
115
116 for (Index = 0; Index < mSmmRegisterHob->Count; Index++) {
117 if (mSmmRegisterHob->Registers[Index].Id == Id) {
118 return &mSmmRegisterHob->Registers[Index];
119 }
120 }
121 return NULL;
122 }
123
124 /**
125 Set SMM SMI Global enable lock
126
127 **/
128 VOID
129 LockSmiGlobalEn (
130 VOID
131 )
132 {
133 PLD_GENERIC_REGISTER *SmiLockReg;
134
135 DEBUG ((DEBUG_ERROR, "LockSmiGlobalEn .....\n"));
136
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"));
140 return;
141 }
142
143 //
144 // Set SMM SMI lock in S3 path
145 //
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"));
152
153 MmioOr32 ((UINT32)SmiLockReg->Address.Address, 1 << SmiLockReg->Address.RegisterBitOffset);
154 } else {
155 DEBUG ((DEBUG_ERROR, "Unexpected SMM SMI lock register, need enhancement here.\n"));
156 }
157 }
158
159 /**
160 Check and set SMM feature lock bit and code check enable bit
161 in S3 path.
162
163 **/
164 VOID
165 SmmFeatureLockOnS3 (
166 VOID
167 )
168 {
169
170 if (mSmmFeatureControl != 0) {
171 return;
172 }
173
174 mSmmFeatureControl = AsmReadMsr64(MSR_SMM_FEATURE_CONTROL);
175 if ((mSmmFeatureControl & 0x5) != 0x5) {
176 //
177 // Set Lock bit [BIT0] for this register and SMM code check enable bit [BIT2]
178 //
179 AsmWriteMsr64 (MSR_SMM_FEATURE_CONTROL, mSmmFeatureControl | 0x5);
180 }
181 mSmmFeatureControl = AsmReadMsr64(MSR_SMM_FEATURE_CONTROL);
182 }
183
184
185
186 /**
187 Function to program SMRR base and mask.
188
189 @param[in] ProcedureArgument Pointer to SMRR_BASE_MASK structure.
190 **/
191 VOID
192 SetSmrr (
193 IN VOID *ProcedureArgument
194 )
195 {
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);
199 }
200 }
201
202 /**
203 Set SMRR in S3 path.
204
205 **/
206 VOID
207 SetSmrrOnS3 (
208 VOID
209 )
210 {
211 EFI_STATUS Status;
212 SMRR_BASE_MASK Arguments;
213 UINTN Index;
214 UINT32 SmmBase;
215 UINT32 SmmSize;
216
217 if ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSBASE) != 0) && ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSMASK) & BIT11) != 0)) {
218 return;
219 }
220
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));
225 return;
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"));
231 return;
232 }
233 SmmSize += (UINT32)(UINTN)mSmramHob->Descriptor[1].PhysicalSize;
234 }
235
236 if ((SmmBase == 0) || (SmmSize < SIZE_4KB)) {
237 DEBUG ((DEBUG_ERROR, "Invalid SMM range.\n"));
238 return ;
239 }
240
241 //
242 // SMRR size must be of length 2^n
243 // SMRR base alignment cannot be less than SMRR length
244 //
245 if ((SmmSize != GetPowerOfTwo32 (SmmSize)) || ((SmmBase & ~(SmmSize - 1)) != SmmBase)) {
246 DEBUG ((DEBUG_ERROR, " Invalid SMM range.\n"));
247 return ;
248 }
249
250 //
251 // Calculate smrr base, mask and pass them as arguments.
252 //
253 Arguments.Base = (SmmSize | MTRR_CACHE_WRITE_BACK);
254 Arguments.Mask = (~(SmmSize - 1) & EFI_MSR_SMRR_MASK);
255
256 //
257 // Set SMRR valid bit
258 //
259 Arguments.Mask |= BIT11;
260
261 //
262 // Program smrr base and mask on BSP first and then on APs
263 //
264 SetSmrr(&Arguments);
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));
270 }
271 }
272 }
273 }
274
275
276 /**
277 Software SMI callback for restoring SMRR base and mask in S3 path.
278
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.
285
286 @retval EFI_SUCCESS The interrupt was handled successfully.
287
288 **/
289 EFI_STATUS
290 EFIAPI
291 BlSwSmiHandler (
292 IN EFI_HANDLE DispatchHandle,
293 IN CONST VOID *Context,
294 IN OUT VOID *CommBuffer,
295 IN OUT UINTN *CommBufferSize
296 )
297 {
298 SetSmrrOnS3 ();
299 SmmFeatureLockOnS3 ();
300 LockSmiGlobalEn ();
301
302 return EFI_SUCCESS;
303 }
304
305
306 /**
307 Lock SMI in this SMM ready to lock event.
308
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
312
313 @retval EFI_SUCCESS SmmEventCallback runs successfully
314 @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
315 **/
316 EFI_STATUS
317 EFIAPI
318 BlSupportSmmReadyToLockCallback (
319 IN CONST EFI_GUID *Protocol,
320 IN VOID *Interface,
321 IN EFI_HANDLE Handle
322 )
323 {
324 //
325 // Set SMM SMI lock
326 //
327 LockSmiGlobalEn ();
328 return EFI_SUCCESS;
329 }
330
331
332 /**
333 The driver's entry point.
334
335 @param[in] ImageHandle The firmware allocated handle for the EFI image.
336 @param[in] SystemTable A pointer to the EFI System Table.
337
338 @retval EFI_SUCCESS The entry point is executed successfully.
339 @retval Others Some error occurs when executing this entry point.
340
341 **/
342 EFI_STATUS
343 EFIAPI
344 BlSupportSmm (
345 IN EFI_HANDLE ImageHandle,
346 IN EFI_SYSTEM_TABLE *SystemTable
347 )
348 {
349 EFI_STATUS Status;
350 EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch;
351 EFI_SMM_SW_REGISTER_CONTEXT SwContext;
352 EFI_HANDLE SwHandle;
353 EFI_HOB_GUID_TYPE *GuidHob;
354 VOID *SmmHob;
355 VOID *Registration;
356
357 //
358 // Get SMM S3 communication hob and save it
359 //
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));
364 } else {
365 return EFI_NOT_FOUND;
366 }
367
368 if (mPldS3Hob.PldAcpiS3Enable) {
369 // Other drivers will take care of S3.
370 return EFI_SUCCESS;
371 }
372
373 //
374 // Get smram hob and save it
375 //
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;
382 }
383 CopyMem (mSmramHob, SmmHob, GET_GUID_HOB_DATA_SIZE(GuidHob));
384 } else {
385 return EFI_NOT_FOUND;
386 }
387
388 //
389 // Get SMM register hob and save it
390 //
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;
397 }
398 CopyMem (mSmmRegisterHob, SmmHob, GET_GUID_HOB_DATA_SIZE(GuidHob));
399 } else {
400 return EFI_NOT_FOUND;
401 }
402
403 //
404 // Get the Sw dispatch protocol and register SMI handler.
405 //
406 Status = gSmst->SmmLocateProtocol (&gEfiSmmSwDispatch2ProtocolGuid, NULL, (VOID**)&SwDispatch);
407 if (EFI_ERROR (Status)) {
408 return Status;
409 }
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));
414 return Status;
415 }
416
417 //
418 // Register SMM ready to lock callback
419 //
420 Status = gSmst->SmmRegisterProtocolNotify (
421 &gEfiSmmReadyToLockProtocolGuid,
422 BlSupportSmmReadyToLockCallback,
423 &Registration
424 );
425 ASSERT_EFI_ERROR (Status);
426
427 SaveSmmInfoForS3 ((UINT8)SwContext.SwSmiInputValue);
428
429 return EFI_SUCCESS;
430 }
431