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