VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c

Last change on this file was 99404, checked in by vboxsync, 2 years ago

Devices/EFI/FirmwareNew: Update to edk2-stable202302 and make it build, bugref:4643

  • Property svn:eol-style set to native
File size: 12.6 KB
Line 
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
16PLD_S3_COMMUNICATION mPldS3Hob;
17EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *mSmramHob = NULL;
18PLD_SMM_REGISTERS *mSmmRegisterHob = NULL;
19UINT64 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**/
34EFI_STATUS
35SaveSmmInfoForS3 (
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 {
52 break;
53 }
54 }
55
56 if (Index == mSmramHob->NumberOfSmmReservedRegions) {
57 return EFI_NOT_FOUND;
58 }
59
60 //
61 // Make sure the dedicated region for SMM info communication whose attribute is "allocated" (i.e., excluded from SMM memory service)
62 //
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;
66 }
67
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;
71 }
72
73 CopyGuid (&PldSmmInfo->Header.Name, &gS3CommunicationGuid);
74 PldSmmInfo->Header.Header.HobType = EFI_HOB_TYPE_GUID_EXTENSION;
75 PldSmmInfo->S3Info.SwSmiTriggerValue = BlSwSmiHandlerInput;
76
77 //
78 // Save APIC ID and SMM base
79 //
80 Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpService);
81 if (EFI_ERROR (Status)) {
82 return Status;
83 }
84
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)) {
90 return Status;
91 }
92
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));
96 SmmBaseInfo++;
97 }
98
99 return EFI_SUCCESS;
100}
101
102/**
103 Get specified SMI register based on given register ID
104
105 @param[in] Id The register ID to get.
106
107 @retval NULL The register is not found
108 @return smi register
109
110**/
111PLD_GENERIC_REGISTER *
112GetRegisterById (
113 UINT64 Id
114 )
115{
116 UINT32 Index;
117
118 for (Index = 0; Index < mSmmRegisterHob->Count; Index++) {
119 if (mSmmRegisterHob->Registers[Index].Id == Id) {
120 return &mSmmRegisterHob->Registers[Index];
121 }
122 }
123
124 return NULL;
125}
126
127/**
128 Set SMM SMI Global enable lock
129
130**/
131VOID
132LockSmiGlobalEn (
133 VOID
134 )
135{
136 PLD_GENERIC_REGISTER *SmiLockReg;
137
138 DEBUG ((DEBUG_ERROR, "LockSmiGlobalEn .....\n"));
139
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"));
143 return;
144 }
145
146 //
147 // Set SMM SMI lock in S3 path
148 //
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))
154 {
155 DEBUG ((DEBUG_ERROR, "LockSmiGlobalEn ....is locked\n"));
156
157 MmioOr32 ((UINT32)SmiLockReg->Address.Address, 1 << SmiLockReg->Address.RegisterBitOffset);
158 } else {
159 DEBUG ((DEBUG_ERROR, "Unexpected SMM SMI lock register, need enhancement here.\n"));
160 }
161}
162
163/**
164 Check and set SMM feature lock bit and code check enable bit
165 in S3 path.
166
167**/
168VOID
169SmmFeatureLockOnS3 (
170 VOID
171 )
172{
173 if (mSmmFeatureControl != 0) {
174 return;
175 }
176
177 mSmmFeatureControl = AsmReadMsr64 (MSR_SMM_FEATURE_CONTROL);
178 if ((mSmmFeatureControl & 0x5) != 0x5) {
179 //
180 // Set Lock bit [BIT0] for this register and SMM code check enable bit [BIT2]
181 //
182 AsmWriteMsr64 (MSR_SMM_FEATURE_CONTROL, mSmmFeatureControl | 0x5);
183 }
184
185 mSmmFeatureControl = AsmReadMsr64 (MSR_SMM_FEATURE_CONTROL);
186}
187
188/**
189 Function to program SMRR base and mask.
190
191 @param[in] ProcedureArgument Pointer to SMRR_BASE_MASK structure.
192**/
193VOID
194EFIAPI
195SetSmrr (
196 IN VOID *ProcedureArgument
197 )
198{
199 if (ProcedureArgument != NULL) {
200 AsmWriteMsr64 (MSR_IA32_SMRR_PHYSBASE, ((SMRR_BASE_MASK *)ProcedureArgument)->Base);
201 AsmWriteMsr64 (MSR_IA32_SMRR_PHYSMASK, ((SMRR_BASE_MASK *)ProcedureArgument)->Mask);
202 }
203}
204
205/**
206 Set SMRR in S3 path.
207
208**/
209VOID
210SetSmrrOnS3 (
211 VOID
212 )
213{
214 EFI_STATUS Status;
215 SMRR_BASE_MASK Arguments;
216 UINTN Index;
217 UINT32 SmmBase;
218 UINT32 SmmSize;
219
220 if ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSBASE) != 0) && ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSMASK) & BIT11) != 0)) {
221 return;
222 }
223
224 SmmBase = (UINT32)(UINTN)mSmramHob->Descriptor[0].PhysicalStart;
225 SmmSize = (UINT32)(UINTN)mSmramHob->Descriptor[0].PhysicalSize;
226 if ((mSmramHob->NumberOfSmmReservedRegions > 2) || (mSmramHob->NumberOfSmmReservedRegions == 0)) {
227 DEBUG ((DEBUG_ERROR, "%d SMM ranges are not supported.\n", mSmramHob->NumberOfSmmReservedRegions));
228 return;
229 } else if (mSmramHob->NumberOfSmmReservedRegions == 2) {
230 if ((mSmramHob->Descriptor[1].PhysicalStart + mSmramHob->Descriptor[1].PhysicalSize) == SmmBase) {
231 SmmBase = (UINT32)(UINTN)mSmramHob->Descriptor[1].PhysicalStart;
232 } else if (mSmramHob->Descriptor[1].PhysicalStart != (SmmBase + SmmSize)) {
233 DEBUG ((DEBUG_ERROR, "Two SMM regions are not continous.\n"));
234 return;
235 }
236
237 SmmSize += (UINT32)(UINTN)mSmramHob->Descriptor[1].PhysicalSize;
238 }
239
240 if ((SmmBase == 0) || (SmmSize < SIZE_4KB)) {
241 DEBUG ((DEBUG_ERROR, "Invalid SMM range.\n"));
242 return;
243 }
244
245 //
246 // SMRR size must be of length 2^n
247 // SMRR base alignment cannot be less than SMRR length
248 //
249 if ((SmmSize != GetPowerOfTwo32 (SmmSize)) || ((SmmBase & ~(SmmSize - 1)) != SmmBase)) {
250 DEBUG ((DEBUG_ERROR, " Invalid SMM range.\n"));
251 return;
252 }
253
254 //
255 // Calculate smrr base, mask and pass them as arguments.
256 //
257 Arguments.Base = (SmmSize | MTRR_CACHE_WRITE_BACK);
258 Arguments.Mask = (~(SmmSize - 1) & EFI_MSR_SMRR_MASK);
259
260 //
261 // Set SMRR valid bit
262 //
263 Arguments.Mask |= BIT11;
264
265 //
266 // Program smrr base and mask on BSP first and then on APs
267 //
268 SetSmrr (&Arguments);
269 for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
270 if (Index != gSmst->CurrentlyExecutingCpu) {
271 Status = gSmst->SmmStartupThisAp (SetSmrr, Index, (VOID *)&Arguments);
272 if (EFI_ERROR (Status)) {
273 DEBUG ((DEBUG_ERROR, "Programming SMRR on AP# %d status: %r\n", Index, Status));
274 }
275 }
276 }
277}
278
279/**
280 Software SMI callback for restoring SMRR base and mask in S3 path.
281
282 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
283 @param[in] Context Points to an optional handler context which was specified when the
284 handler was registered.
285 @param[in, out] CommBuffer A pointer to a collection of data in memory that will
286 be conveyed from a non-SMM environment into an SMM environment.
287 @param[in, out] CommBufferSize The size of the CommBuffer.
288
289 @retval EFI_SUCCESS The interrupt was handled successfully.
290
291**/
292EFI_STATUS
293EFIAPI
294BlSwSmiHandler (
295 IN EFI_HANDLE DispatchHandle,
296 IN CONST VOID *Context,
297 IN OUT VOID *CommBuffer,
298 IN OUT UINTN *CommBufferSize
299 )
300{
301 SetSmrrOnS3 ();
302 SmmFeatureLockOnS3 ();
303 LockSmiGlobalEn ();
304
305 return EFI_SUCCESS;
306}
307
308/**
309 Lock SMI in this SMM ready to lock event.
310
311 @param Protocol Points to the protocol's unique identifier
312 @param Interface Points to the interface instance
313 @param Handle The handle on which the interface was installed
314
315 @retval EFI_SUCCESS SmmEventCallback runs successfully
316 @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
317 **/
318EFI_STATUS
319EFIAPI
320BlSupportSmmReadyToLockCallback (
321 IN CONST EFI_GUID *Protocol,
322 IN VOID *Interface,
323 IN EFI_HANDLE Handle
324 )
325{
326 //
327 // Set SMM SMI lock
328 //
329 LockSmiGlobalEn ();
330 return EFI_SUCCESS;
331}
332
333/**
334 The driver's entry point.
335
336 @param[in] ImageHandle The firmware allocated handle for the EFI image.
337 @param[in] SystemTable A pointer to the EFI System Table.
338
339 @retval EFI_SUCCESS The entry point is executed successfully.
340 @retval Others Some error occurs when executing this entry point.
341
342**/
343EFI_STATUS
344EFIAPI
345BlSupportSmm (
346 IN EFI_HANDLE ImageHandle,
347 IN EFI_SYSTEM_TABLE *SystemTable
348 )
349{
350 EFI_STATUS Status;
351 EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch;
352 EFI_SMM_SW_REGISTER_CONTEXT SwContext;
353 EFI_HANDLE SwHandle;
354 EFI_HOB_GUID_TYPE *GuidHob;
355 VOID *SmmHob;
356 VOID *Registration;
357
358 //
359 // Get SMM S3 communication hob and save it
360 //
361 GuidHob = GetFirstGuidHob (&gS3CommunicationGuid);
362 if (GuidHob != NULL) {
363 SmmHob = (VOID *)(GET_GUID_HOB_DATA (GuidHob));
364 CopyMem (&mPldS3Hob, SmmHob, GET_GUID_HOB_DATA_SIZE (GuidHob));
365 } else {
366 return EFI_NOT_FOUND;
367 }
368
369 if (mPldS3Hob.PldAcpiS3Enable) {
370 // Other drivers will take care of S3.
371 return EFI_SUCCESS;
372 }
373
374 //
375 // Get smram hob and save it
376 //
377 GuidHob = GetFirstGuidHob (&gEfiSmmSmramMemoryGuid);
378 if (GuidHob != NULL) {
379 SmmHob = (VOID *)(GET_GUID_HOB_DATA (GuidHob));
380 mSmramHob = AllocatePool (GET_GUID_HOB_DATA_SIZE (GuidHob));
381 if (mSmramHob == NULL) {
382 return EFI_OUT_OF_RESOURCES;
383 }
384
385 CopyMem (mSmramHob, SmmHob, GET_GUID_HOB_DATA_SIZE (GuidHob));
386 } else {
387 return EFI_NOT_FOUND;
388 }
389
390 //
391 // Get SMM register hob and save it
392 //
393 GuidHob = GetFirstGuidHob (&gSmmRegisterInfoGuid);
394 if (GuidHob != NULL) {
395 SmmHob = (VOID *)(GET_GUID_HOB_DATA (GuidHob));
396 mSmmRegisterHob = AllocatePool (GET_GUID_HOB_DATA_SIZE (GuidHob));
397 if (mSmmRegisterHob == NULL) {
398 return EFI_OUT_OF_RESOURCES;
399 }
400
401 CopyMem (mSmmRegisterHob, SmmHob, GET_GUID_HOB_DATA_SIZE (GuidHob));
402 } else {
403 return EFI_NOT_FOUND;
404 }
405
406 //
407 // Get the Sw dispatch protocol and register SMI handler.
408 //
409 Status = gSmst->SmmLocateProtocol (&gEfiSmmSwDispatch2ProtocolGuid, NULL, (VOID **)&SwDispatch);
410 if (EFI_ERROR (Status)) {
411 return Status;
412 }
413
414 SwContext.SwSmiInputValue = (UINTN)-1;
415 Status = SwDispatch->Register (SwDispatch, BlSwSmiHandler, &SwContext, &SwHandle);
416 if (EFI_ERROR (Status)) {
417 DEBUG ((DEBUG_ERROR, "Registering S3 smi handler failed: %r\n", Status));
418 return Status;
419 }
420
421 //
422 // Register SMM ready to lock callback
423 //
424 Status = gSmst->SmmRegisterProtocolNotify (
425 &gEfiSmmReadyToLockProtocolGuid,
426 BlSupportSmmReadyToLockCallback,
427 &Registration
428 );
429 ASSERT_EFI_ERROR (Status);
430
431 SaveSmmInfoForS3 ((UINT8)SwContext.SwSmiInputValue);
432
433 return EFI_SUCCESS;
434}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette