]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/ScriptExecute.c
MdeModulePkg: Supporting S3 in 64bit PEI
[mirror_edk2.git] / MdeModulePkg / Universal / Acpi / BootScriptExecutorDxe / ScriptExecute.c
... / ...
CommitLineData
1/** @file\r
2 This is the code for Boot Script Executer module.\r
3\r
4 This driver is dispatched by Dxe core and the driver will reload itself to ACPI reserved memory\r
5 in the entry point. The functionality is to interpret and restore the S3 boot script\r
6\r
7Copyright (c) 2006 - 2022, Intel Corporation. All rights reserved.<BR>\r
8Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\r
9\r
10SPDX-License-Identifier: BSD-2-Clause-Patent\r
11\r
12**/\r
13\r
14#include "ScriptExecute.h"\r
15\r
16EFI_GUID mBootScriptExecutorImageGuid = {\r
17 0x9a8d3433, 0x9fe8, 0x42b6, { 0x87, 0xb, 0x1e, 0x31, 0xc8, 0x4e, 0xbe, 0x3b }\r
18};\r
19\r
20BOOLEAN mPage1GSupport = FALSE;\r
21UINT64 mAddressEncMask = 0;\r
22\r
23/**\r
24 Entry function of Boot script exector. This function will be executed in\r
25 S3 boot path.\r
26 This function should not return, because it is invoked by switch stack.\r
27\r
28 @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT\r
29 @param PeiS3ResumeState a pointer to a structure of PEI_S3_RESUME_STATE\r
30\r
31 @retval EFI_INVALID_PARAMETER - OS waking vector not found\r
32 @retval EFI_UNSUPPORTED - something wrong when we resume to OS\r
33**/\r
34EFI_STATUS\r
35EFIAPI\r
36S3BootScriptExecutorEntryFunction (\r
37 IN ACPI_S3_CONTEXT *AcpiS3Context,\r
38 IN PEI_S3_RESUME_STATE *PeiS3ResumeState\r
39 )\r
40{\r
41 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;\r
42 EFI_STATUS Status;\r
43 UINTN TempStackTop;\r
44 UINTN TempStack[0x10];\r
45 UINTN AsmTransferControl16Address;\r
46 IA32_DESCRIPTOR IdtDescriptor;\r
47\r
48 //\r
49 // Disable interrupt of Debug timer, since new IDT table cannot handle it.\r
50 //\r
51 SaveAndSetDebugTimerInterrupt (FALSE);\r
52\r
53 AsmReadIdtr (&IdtDescriptor);\r
54 //\r
55 // Restore IDT for debug\r
56 //\r
57 SetIdtEntry (AcpiS3Context);\r
58\r
59 //\r
60 // Initialize Debug Agent to support source level debug in S3 path, it will disable interrupt and Debug Timer.\r
61 //\r
62 InitializeDebugAgent (DEBUG_AGENT_INIT_S3, (VOID *)&IdtDescriptor, NULL);\r
63\r
64 //\r
65 // Because not install BootScriptExecute PPI(used just in this module), So just pass NULL\r
66 // for that parameter.\r
67 //\r
68 Status = S3BootScriptExecute ();\r
69\r
70 //\r
71 // If invalid script table or opcode in S3 boot script table.\r
72 //\r
73 ASSERT_EFI_ERROR (Status);\r
74\r
75 if (EFI_ERROR (Status)) {\r
76 CpuDeadLoop ();\r
77 return Status;\r
78 }\r
79\r
80 AsmWbinvd ();\r
81\r
82 //\r
83 // Get ACPI Table Address\r
84 //\r
85 Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)((UINTN)(AcpiS3Context->AcpiFacsTable));\r
86\r
87 //\r
88 // We need turn back to S3Resume - install boot script done ppi and report status code on S3resume.\r
89 //\r
90 if (PeiS3ResumeState != 0) {\r
91 //\r
92 // Need report status back to S3ResumePeim.\r
93 // If boot script execution is failed, S3ResumePeim wil report the error status code.\r
94 //\r
95 PeiS3ResumeState->ReturnStatus = (UINT64)(UINTN)Status;\r
96 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
97 //\r
98 // X64 DXE to IA32 PEI S3 Resume\r
99 //\r
100 DEBUG ((DEBUG_INFO, "Call AsmDisablePaging64() to return to S3 Resume in PEI Phase\n"));\r
101 PeiS3ResumeState->AsmTransferControl = (EFI_PHYSICAL_ADDRESS)(UINTN)AsmTransferControl32;\r
102\r
103 if ((Facs != NULL) &&\r
104 (Facs->Signature == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) &&\r
105 (Facs->FirmwareWakingVector != 0))\r
106 {\r
107 //\r
108 // more step needed - because relative address is handled differently between X64 and IA32.\r
109 //\r
110 AsmTransferControl16Address = (UINTN)AsmTransferControl16;\r
111 AsmFixAddress16 = (UINT32)AsmTransferControl16Address;\r
112 AsmJmpAddr32 = (UINT32)((Facs->FirmwareWakingVector & 0xF) | ((Facs->FirmwareWakingVector & 0xFFFF0) << 12));\r
113 }\r
114\r
115 AsmDisablePaging64 (\r
116 PeiS3ResumeState->ReturnCs,\r
117 (UINT32)PeiS3ResumeState->ReturnEntryPoint,\r
118 (UINT32)(UINTN)AcpiS3Context,\r
119 (UINT32)(UINTN)PeiS3ResumeState,\r
120 (UINT32)PeiS3ResumeState->ReturnStackPointer\r
121 );\r
122 } else {\r
123 //\r
124 // IA32 DXE to IA32 PEI S3 Resume / X64 DXE to X64 PEI S3 Resume\r
125 //\r
126 DEBUG ((DEBUG_INFO, "Call SwitchStack() to return to S3 Resume in PEI Phase\n"));\r
127 PeiS3ResumeState->AsmTransferControl = (EFI_PHYSICAL_ADDRESS)(UINTN)AsmTransferControl;\r
128\r
129 SwitchStack (\r
130 (SWITCH_STACK_ENTRY_POINT)(UINTN)PeiS3ResumeState->ReturnEntryPoint,\r
131 (VOID *)(UINTN)AcpiS3Context,\r
132 (VOID *)(UINTN)PeiS3ResumeState,\r
133 (VOID *)(UINTN)PeiS3ResumeState->ReturnStackPointer\r
134 );\r
135 }\r
136\r
137 //\r
138 // Never run to here\r
139 //\r
140 CpuDeadLoop ();\r
141 return EFI_UNSUPPORTED;\r
142 }\r
143\r
144 //\r
145 // S3ResumePeim does not provide a way to jump back to itself, so resume to OS here directly\r
146 //\r
147 if (Facs->XFirmwareWakingVector != 0) {\r
148 //\r
149 // Switch to native waking vector\r
150 //\r
151 TempStackTop = (UINTN)&TempStack + sizeof (TempStack);\r
152 if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) &&\r
153 ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0) &&\r
154 ((Facs->OspmFlags & EFI_ACPI_4_0_OSPM_64BIT_WAKE__F) != 0))\r
155 {\r
156 //\r
157 // X64 long mode waking vector\r
158 //\r
159 DEBUG ((DEBUG_INFO, "Transfer from 64bit DXE to 64bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector));\r
160 if (sizeof (UINTN) == sizeof (UINT64)) {\r
161 //\r
162 // 64bit DXE calls to 64bit OS S3 waking vector\r
163 //\r
164 SwitchStack (\r
165 (SWITCH_STACK_ENTRY_POINT)(UINTN)Facs->XFirmwareWakingVector,\r
166 NULL,\r
167 NULL,\r
168 (VOID *)(UINTN)TempStackTop\r
169 );\r
170 } else {\r
171 // Unsupported for 32bit DXE, 64bit OS vector\r
172 DEBUG ((DEBUG_ERROR, "Unsupported for 32bit DXE transfer to 64bit OS waking vector!\r\n"));\r
173 ASSERT (FALSE);\r
174 }\r
175 } else {\r
176 //\r
177 // IA32 protected mode waking vector (Page disabled)\r
178 //\r
179 DEBUG ((DEBUG_INFO, "Transfer to 32bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector));\r
180 if (sizeof (UINTN) == sizeof (UINT64)) {\r
181 //\r
182 // 64bit DXE calls to 32bit OS S3 waking vector\r
183 //\r
184 AsmDisablePaging64 (\r
185 0x10,\r
186 (UINT32)Facs->XFirmwareWakingVector,\r
187 0,\r
188 0,\r
189 (UINT32)TempStackTop\r
190 );\r
191 } else {\r
192 //\r
193 // 32bit DXE calls to 32bit OS S3 waking vector\r
194 //\r
195 SwitchStack (\r
196 (SWITCH_STACK_ENTRY_POINT)(UINTN)Facs->XFirmwareWakingVector,\r
197 NULL,\r
198 NULL,\r
199 (VOID *)(UINTN)TempStackTop\r
200 );\r
201 }\r
202 }\r
203 } else {\r
204 //\r
205 // 16bit Realmode waking vector\r
206 //\r
207 DEBUG ((DEBUG_INFO, "Transfer to 16bit OS waking vector - %x\r\n", (UINTN)Facs->FirmwareWakingVector));\r
208 AsmTransferControl (Facs->FirmwareWakingVector, 0x0);\r
209 }\r
210\r
211 //\r
212 // Never run to here\r
213 //\r
214 CpuDeadLoop ();\r
215 return EFI_UNSUPPORTED;\r
216}\r
217\r
218/**\r
219 Register image to memory profile.\r
220\r
221 @param FileName File name of the image.\r
222 @param ImageBase Image base address.\r
223 @param ImageSize Image size.\r
224 @param FileType File type of the image.\r
225\r
226**/\r
227VOID\r
228RegisterMemoryProfileImage (\r
229 IN EFI_GUID *FileName,\r
230 IN PHYSICAL_ADDRESS ImageBase,\r
231 IN UINT64 ImageSize,\r
232 IN EFI_FV_FILETYPE FileType\r
233 )\r
234{\r
235 EFI_STATUS Status;\r
236 EDKII_MEMORY_PROFILE_PROTOCOL *ProfileProtocol;\r
237 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FilePath;\r
238 UINT8 TempBuffer[sizeof (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH) + sizeof (EFI_DEVICE_PATH_PROTOCOL)];\r
239\r
240 if ((PcdGet8 (PcdMemoryProfilePropertyMask) & BIT0) != 0) {\r
241 FilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)TempBuffer;\r
242 Status = gBS->LocateProtocol (&gEdkiiMemoryProfileGuid, NULL, (VOID **)&ProfileProtocol);\r
243 if (!EFI_ERROR (Status)) {\r
244 EfiInitializeFwVolDevicepathNode (FilePath, FileName);\r
245 SetDevicePathEndNode (FilePath + 1);\r
246\r
247 Status = ProfileProtocol->RegisterImage (\r
248 ProfileProtocol,\r
249 (EFI_DEVICE_PATH_PROTOCOL *)FilePath,\r
250 ImageBase,\r
251 ImageSize,\r
252 FileType\r
253 );\r
254 }\r
255 }\r
256}\r
257\r
258/**\r
259 This is the Event notification function to reload BootScriptExecutor image\r
260 to RESERVED mem and save it to LockBox.\r
261\r
262 @param Event Pointer to this event\r
263 @param Context Event handler private data\r
264 **/\r
265VOID\r
266EFIAPI\r
267ReadyToLockEventNotify (\r
268 IN EFI_EVENT Event,\r
269 IN VOID *Context\r
270 )\r
271{\r
272 EFI_STATUS Status;\r
273 VOID *Interface;\r
274 UINT8 *Buffer;\r
275 UINTN BufferSize;\r
276 EFI_HANDLE NewImageHandle;\r
277 UINTN Pages;\r
278 EFI_PHYSICAL_ADDRESS FfsBuffer;\r
279 PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;\r
280 EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc;\r
281\r
282 Status = gBS->LocateProtocol (&gEfiDxeSmmReadyToLockProtocolGuid, NULL, &Interface);\r
283 if (EFI_ERROR (Status)) {\r
284 return;\r
285 }\r
286\r
287 //\r
288 // A workaround: Here we install a dummy handle\r
289 //\r
290 NewImageHandle = NULL;\r
291 Status = gBS->InstallProtocolInterface (\r
292 &NewImageHandle,\r
293 &gEfiCallerIdGuid,\r
294 EFI_NATIVE_INTERFACE,\r
295 NULL\r
296 );\r
297 ASSERT_EFI_ERROR (Status);\r
298\r
299 //\r
300 // Reload BootScriptExecutor image itself to RESERVED mem\r
301 //\r
302 Status = GetSectionFromAnyFv (\r
303 &gEfiCallerIdGuid,\r
304 EFI_SECTION_PE32,\r
305 0,\r
306 (VOID **)&Buffer,\r
307 &BufferSize\r
308 );\r
309 ASSERT_EFI_ERROR (Status);\r
310 ImageContext.Handle = Buffer;\r
311 ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;\r
312 //\r
313 // Get information about the image being loaded\r
314 //\r
315 Status = PeCoffLoaderGetImageInfo (&ImageContext);\r
316 ASSERT_EFI_ERROR (Status);\r
317 if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) {\r
318 Pages = EFI_SIZE_TO_PAGES ((UINTN)(ImageContext.ImageSize + ImageContext.SectionAlignment));\r
319 } else {\r
320 Pages = EFI_SIZE_TO_PAGES ((UINTN)ImageContext.ImageSize);\r
321 }\r
322\r
323 FfsBuffer = 0xFFFFFFFF;\r
324 Status = gBS->AllocatePages (\r
325 AllocateMaxAddress,\r
326 EfiReservedMemoryType,\r
327 Pages,\r
328 &FfsBuffer\r
329 );\r
330 ASSERT_EFI_ERROR (Status);\r
331\r
332 //\r
333 // Make sure that the buffer can be used to store code.\r
334 //\r
335 Status = gDS->GetMemorySpaceDescriptor (FfsBuffer, &MemDesc);\r
336 if (!EFI_ERROR (Status) && ((MemDesc.Attributes & EFI_MEMORY_XP) != 0)) {\r
337 gDS->SetMemorySpaceAttributes (\r
338 FfsBuffer,\r
339 EFI_PAGES_TO_SIZE (Pages),\r
340 MemDesc.Attributes & (~EFI_MEMORY_XP)\r
341 );\r
342 }\r
343\r
344 ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer;\r
345 //\r
346 // Align buffer on section boundary\r
347 //\r
348 ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;\r
349 ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)ImageContext.SectionAlignment - 1);\r
350 //\r
351 // Load the image to our new buffer\r
352 //\r
353 Status = PeCoffLoaderLoadImage (&ImageContext);\r
354 ASSERT_EFI_ERROR (Status);\r
355\r
356 //\r
357 // Relocate the image in our new buffer\r
358 //\r
359 Status = PeCoffLoaderRelocateImage (&ImageContext);\r
360 ASSERT_EFI_ERROR (Status);\r
361\r
362 //\r
363 // Free the buffer allocated by ReadSection since the image has been relocated in the new buffer\r
364 //\r
365 gBS->FreePool (Buffer);\r
366\r
367 //\r
368 // Flush the instruction cache so the image data is written before we execute it\r
369 //\r
370 InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize);\r
371\r
372 RegisterMemoryProfileImage (\r
373 &gEfiCallerIdGuid,\r
374 ImageContext.ImageAddress,\r
375 ImageContext.ImageSize,\r
376 EFI_FV_FILETYPE_DRIVER\r
377 );\r
378\r
379 Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint))(NewImageHandle, gST);\r
380 ASSERT_EFI_ERROR (Status);\r
381\r
382 //\r
383 // Additional step for BootScript integrity\r
384 // Save BootScriptExecutor image\r
385 //\r
386 Status = SaveLockBox (\r
387 &mBootScriptExecutorImageGuid,\r
388 (VOID *)(UINTN)ImageContext.ImageAddress,\r
389 (UINTN)ImageContext.ImageSize\r
390 );\r
391 ASSERT_EFI_ERROR (Status);\r
392\r
393 Status = SetLockBoxAttributes (&mBootScriptExecutorImageGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);\r
394 ASSERT_EFI_ERROR (Status);\r
395\r
396 gBS->CloseEvent (Event);\r
397}\r
398\r
399/**\r
400 Entrypoint of Boot script exector driver, this function will be executed in\r
401 normal boot phase and invoked by DXE dispatch.\r
402\r
403 @param[in] ImageHandle The firmware allocated handle for the EFI image.\r
404 @param[in] SystemTable A pointer to the EFI System Table.\r
405\r
406 @retval EFI_SUCCESS The entry point is executed successfully.\r
407 @retval other Some error occurs when executing this entry point.\r
408**/\r
409EFI_STATUS\r
410EFIAPI\r
411BootScriptExecutorEntryPoint (\r
412 IN EFI_HANDLE ImageHandle,\r
413 IN EFI_SYSTEM_TABLE *SystemTable\r
414 )\r
415{\r
416 UINTN BufferSize;\r
417 UINTN Pages;\r
418 BOOT_SCRIPT_EXECUTOR_VARIABLE *EfiBootScriptExecutorVariable;\r
419 EFI_PHYSICAL_ADDRESS BootScriptExecutorBuffer;\r
420 EFI_STATUS Status;\r
421 VOID *DevicePath;\r
422 EFI_EVENT ReadyToLockEvent;\r
423 VOID *Registration;\r
424 UINT32 RegEax;\r
425 UINT32 RegEdx;\r
426\r
427 if (!PcdGetBool (PcdAcpiS3Enable)) {\r
428 return EFI_UNSUPPORTED;\r
429 }\r
430\r
431 //\r
432 // Make sure AddressEncMask is contained to smallest supported address field.\r
433 //\r
434 mAddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;\r
435\r
436 //\r
437 // Test if the gEfiCallerIdGuid of this image is already installed. if not, the entry\r
438 // point is loaded by DXE code which is the first time loaded. or else, it is already\r
439 // be reloaded be itself.This is a work-around\r
440 //\r
441 Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &DevicePath);\r
442 if (EFI_ERROR (Status)) {\r
443 //\r
444 // Create ReadyToLock event to reload BootScriptExecutor image\r
445 // to RESERVED mem and save it to LockBox.\r
446 //\r
447 ReadyToLockEvent = EfiCreateProtocolNotifyEvent (\r
448 &gEfiDxeSmmReadyToLockProtocolGuid,\r
449 TPL_NOTIFY,\r
450 ReadyToLockEventNotify,\r
451 NULL,\r
452 &Registration\r
453 );\r
454 ASSERT (ReadyToLockEvent != NULL);\r
455 } else {\r
456 //\r
457 // the entry point is invoked after reloading. following code only run in RESERVED mem\r
458 //\r
459 if (PcdGetBool (PcdUse1GPageTable)) {\r
460 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
461 if (RegEax >= 0x80000001) {\r
462 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);\r
463 if ((RegEdx & BIT26) != 0) {\r
464 mPage1GSupport = TRUE;\r
465 }\r
466 }\r
467 }\r
468\r
469 BufferSize = sizeof (BOOT_SCRIPT_EXECUTOR_VARIABLE);\r
470\r
471 BootScriptExecutorBuffer = 0xFFFFFFFF;\r
472 Pages = EFI_SIZE_TO_PAGES (BufferSize);\r
473 Status = gBS->AllocatePages (\r
474 AllocateMaxAddress,\r
475 EfiReservedMemoryType,\r
476 Pages,\r
477 &BootScriptExecutorBuffer\r
478 );\r
479 ASSERT_EFI_ERROR (Status);\r
480\r
481 EfiBootScriptExecutorVariable = (BOOT_SCRIPT_EXECUTOR_VARIABLE *)(UINTN)BootScriptExecutorBuffer;\r
482 EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = (UINTN)S3BootScriptExecutorEntryFunction;\r
483\r
484 Status = SaveLockBox (\r
485 &gEfiBootScriptExecutorVariableGuid,\r
486 &BootScriptExecutorBuffer,\r
487 sizeof (BootScriptExecutorBuffer)\r
488 );\r
489 ASSERT_EFI_ERROR (Status);\r
490\r
491 //\r
492 // Additional step for BootScript integrity\r
493 // Save BootScriptExecutor context\r
494 //\r
495 Status = SaveLockBox (\r
496 &gEfiBootScriptExecutorContextGuid,\r
497 EfiBootScriptExecutorVariable,\r
498 sizeof (*EfiBootScriptExecutorVariable)\r
499 );\r
500 ASSERT_EFI_ERROR (Status);\r
501\r
502 Status = SetLockBoxAttributes (&gEfiBootScriptExecutorContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);\r
503 ASSERT_EFI_ERROR (Status);\r
504 }\r
505\r
506 return EFI_SUCCESS;\r
507}\r