]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/ScriptExecute.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[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 - 2019, 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 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 // more step needed - because relative address is handled differently between X64 and IA32.
108 //
109 AsmTransferControl16Address = (UINTN)AsmTransferControl16;
110 AsmFixAddress16 = (UINT32)AsmTransferControl16Address;
111 AsmJmpAddr32 = (UINT32)((Facs->FirmwareWakingVector & 0xF) | ((Facs->FirmwareWakingVector & 0xFFFF0) << 12));
112 }
113
114 AsmDisablePaging64 (
115 PeiS3ResumeState->ReturnCs,
116 (UINT32)PeiS3ResumeState->ReturnEntryPoint,
117 (UINT32)(UINTN)AcpiS3Context,
118 (UINT32)(UINTN)PeiS3ResumeState,
119 (UINT32)PeiS3ResumeState->ReturnStackPointer
120 );
121 } else {
122 //
123 // IA32 S3 Resume
124 //
125 DEBUG ((DEBUG_INFO, "Call SwitchStack() to return to S3 Resume in PEI Phase\n"));
126 PeiS3ResumeState->AsmTransferControl = (EFI_PHYSICAL_ADDRESS)(UINTN)AsmTransferControl;
127
128 SwitchStack (
129 (SWITCH_STACK_ENTRY_POINT)(UINTN)PeiS3ResumeState->ReturnEntryPoint,
130 (VOID *)(UINTN)AcpiS3Context,
131 (VOID *)(UINTN)PeiS3ResumeState,
132 (VOID *)(UINTN)PeiS3ResumeState->ReturnStackPointer
133 );
134 }
135
136 //
137 // Never run to here
138 //
139 CpuDeadLoop();
140 return EFI_UNSUPPORTED;
141 }
142
143 //
144 // S3ResumePeim does not provide a way to jump back to itself, so resume to OS here directly
145 //
146 if (Facs->XFirmwareWakingVector != 0) {
147 //
148 // Switch to native waking vector
149 //
150 TempStackTop = (UINTN)&TempStack + sizeof(TempStack);
151 if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) &&
152 ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0) &&
153 ((Facs->OspmFlags & EFI_ACPI_4_0_OSPM_64BIT_WAKE__F) != 0)) {
154 //
155 // X64 long mode waking vector
156 //
157 DEBUG ((DEBUG_INFO, "Transfer to 64bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector));
158 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
159 SwitchStack (
160 (SWITCH_STACK_ENTRY_POINT)(UINTN)Facs->XFirmwareWakingVector,
161 NULL,
162 NULL,
163 (VOID *)(UINTN)TempStackTop
164 );
165 } else {
166 // Unsupported for 32bit DXE, 64bit OS vector
167 DEBUG (( EFI_D_ERROR, "Unsupported for 32bit DXE transfer to 64bit OS waking vector!\r\n"));
168 ASSERT (FALSE);
169 }
170 } else {
171 //
172 // IA32 protected mode waking vector (Page disabled)
173 //
174 DEBUG ((DEBUG_INFO, "Transfer to 32bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector));
175 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
176 AsmDisablePaging64 (
177 0x10,
178 (UINT32)Facs->XFirmwareWakingVector,
179 0,
180 0,
181 (UINT32)TempStackTop
182 );
183 } else {
184 SwitchStack (
185 (SWITCH_STACK_ENTRY_POINT)(UINTN)Facs->XFirmwareWakingVector,
186 NULL,
187 NULL,
188 (VOID *)(UINTN)TempStackTop
189 );
190 }
191 }
192 } else {
193 //
194 // 16bit Realmode waking vector
195 //
196 DEBUG ((DEBUG_INFO, "Transfer to 16bit OS waking vector - %x\r\n", (UINTN)Facs->FirmwareWakingVector));
197 AsmTransferControl (Facs->FirmwareWakingVector, 0x0);
198 }
199
200 //
201 // Never run to here
202 //
203 CpuDeadLoop();
204 return EFI_UNSUPPORTED;
205 }
206
207 /**
208 Register image to memory profile.
209
210 @param FileName File name of the image.
211 @param ImageBase Image base address.
212 @param ImageSize Image size.
213 @param FileType File type of the image.
214
215 **/
216 VOID
217 RegisterMemoryProfileImage (
218 IN EFI_GUID *FileName,
219 IN PHYSICAL_ADDRESS ImageBase,
220 IN UINT64 ImageSize,
221 IN EFI_FV_FILETYPE FileType
222 )
223 {
224 EFI_STATUS Status;
225 EDKII_MEMORY_PROFILE_PROTOCOL *ProfileProtocol;
226 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FilePath;
227 UINT8 TempBuffer[sizeof (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH) + sizeof (EFI_DEVICE_PATH_PROTOCOL)];
228
229 if ((PcdGet8 (PcdMemoryProfilePropertyMask) & BIT0) != 0) {
230
231 FilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)TempBuffer;
232 Status = gBS->LocateProtocol (&gEdkiiMemoryProfileGuid, NULL, (VOID **) &ProfileProtocol);
233 if (!EFI_ERROR (Status)) {
234 EfiInitializeFwVolDevicepathNode (FilePath, FileName);
235 SetDevicePathEndNode (FilePath + 1);
236
237 Status = ProfileProtocol->RegisterImage (
238 ProfileProtocol,
239 (EFI_DEVICE_PATH_PROTOCOL *) FilePath,
240 ImageBase,
241 ImageSize,
242 FileType
243 );
244 }
245 }
246 }
247
248 /**
249 This is the Event notification function to reload BootScriptExecutor image
250 to RESERVED mem and save it to LockBox.
251
252 @param Event Pointer to this event
253 @param Context Event handler private data
254 **/
255 VOID
256 EFIAPI
257 ReadyToLockEventNotify (
258 IN EFI_EVENT Event,
259 IN VOID *Context
260 )
261 {
262 EFI_STATUS Status;
263 VOID *Interface;
264 UINT8 *Buffer;
265 UINTN BufferSize;
266 EFI_HANDLE NewImageHandle;
267 UINTN Pages;
268 EFI_PHYSICAL_ADDRESS FfsBuffer;
269 PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
270 EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc;
271
272 Status = gBS->LocateProtocol (&gEfiDxeSmmReadyToLockProtocolGuid, NULL, &Interface);
273 if (EFI_ERROR (Status)) {
274 return;
275 }
276
277 //
278 // A workaround: Here we install a dummy handle
279 //
280 NewImageHandle = NULL;
281 Status = gBS->InstallProtocolInterface (
282 &NewImageHandle,
283 &gEfiCallerIdGuid,
284 EFI_NATIVE_INTERFACE,
285 NULL
286 );
287 ASSERT_EFI_ERROR (Status);
288
289 //
290 // Reload BootScriptExecutor image itself to RESERVED mem
291 //
292 Status = GetSectionFromAnyFv (
293 &gEfiCallerIdGuid,
294 EFI_SECTION_PE32,
295 0,
296 (VOID **) &Buffer,
297 &BufferSize
298 );
299 ASSERT_EFI_ERROR (Status);
300 ImageContext.Handle = Buffer;
301 ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
302 //
303 // Get information about the image being loaded
304 //
305 Status = PeCoffLoaderGetImageInfo (&ImageContext);
306 ASSERT_EFI_ERROR (Status);
307 if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) {
308 Pages = EFI_SIZE_TO_PAGES ((UINTN) (ImageContext.ImageSize + ImageContext.SectionAlignment));
309 } else {
310 Pages = EFI_SIZE_TO_PAGES ((UINTN) ImageContext.ImageSize);
311 }
312 FfsBuffer = 0xFFFFFFFF;
313 Status = gBS->AllocatePages (
314 AllocateMaxAddress,
315 EfiReservedMemoryType,
316 Pages,
317 &FfsBuffer
318 );
319 ASSERT_EFI_ERROR (Status);
320
321 //
322 // Make sure that the buffer can be used to store code.
323 //
324 Status = gDS->GetMemorySpaceDescriptor (FfsBuffer, &MemDesc);
325 if (!EFI_ERROR (Status) && (MemDesc.Attributes & EFI_MEMORY_XP) != 0) {
326 gDS->SetMemorySpaceAttributes (
327 FfsBuffer,
328 EFI_PAGES_TO_SIZE (Pages),
329 MemDesc.Attributes & (~EFI_MEMORY_XP)
330 );
331 }
332
333 ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer;
334 //
335 // Align buffer on section boundary
336 //
337 ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;
338 ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)ImageContext.SectionAlignment - 1);
339 //
340 // Load the image to our new buffer
341 //
342 Status = PeCoffLoaderLoadImage (&ImageContext);
343 ASSERT_EFI_ERROR (Status);
344
345 //
346 // Relocate the image in our new buffer
347 //
348 Status = PeCoffLoaderRelocateImage (&ImageContext);
349 ASSERT_EFI_ERROR (Status);
350
351 //
352 // Free the buffer allocated by ReadSection since the image has been relocated in the new buffer
353 //
354 gBS->FreePool (Buffer);
355
356 //
357 // Flush the instruction cache so the image data is written before we execute it
358 //
359 InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize);
360
361 RegisterMemoryProfileImage (
362 &gEfiCallerIdGuid,
363 ImageContext.ImageAddress,
364 ImageContext.ImageSize,
365 EFI_FV_FILETYPE_DRIVER
366 );
367
368 Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, gST);
369 ASSERT_EFI_ERROR (Status);
370
371 //
372 // Additional step for BootScript integrity
373 // Save BootScriptExecutor image
374 //
375 Status = SaveLockBox (
376 &mBootScriptExecutorImageGuid,
377 (VOID *)(UINTN)ImageContext.ImageAddress,
378 (UINTN)ImageContext.ImageSize
379 );
380 ASSERT_EFI_ERROR (Status);
381
382 Status = SetLockBoxAttributes (&mBootScriptExecutorImageGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
383 ASSERT_EFI_ERROR (Status);
384
385 gBS->CloseEvent (Event);
386 }
387
388 /**
389 Entrypoint of Boot script exector driver, this function will be executed in
390 normal boot phase and invoked by DXE dispatch.
391
392 @param[in] ImageHandle The firmware allocated handle for the EFI image.
393 @param[in] SystemTable A pointer to the EFI System Table.
394
395 @retval EFI_SUCCESS The entry point is executed successfully.
396 @retval other Some error occurs when executing this entry point.
397 **/
398 EFI_STATUS
399 EFIAPI
400 BootScriptExecutorEntryPoint (
401 IN EFI_HANDLE ImageHandle,
402 IN EFI_SYSTEM_TABLE *SystemTable
403 )
404 {
405 UINTN BufferSize;
406 UINTN Pages;
407 BOOT_SCRIPT_EXECUTOR_VARIABLE *EfiBootScriptExecutorVariable;
408 EFI_PHYSICAL_ADDRESS BootScriptExecutorBuffer;
409 EFI_STATUS Status;
410 VOID *DevicePath;
411 EFI_EVENT ReadyToLockEvent;
412 VOID *Registration;
413 UINT32 RegEax;
414 UINT32 RegEdx;
415
416 if (!PcdGetBool (PcdAcpiS3Enable)) {
417 return EFI_UNSUPPORTED;
418 }
419
420 //
421 // Make sure AddressEncMask is contained to smallest supported address field.
422 //
423 mAddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;
424
425 //
426 // Test if the gEfiCallerIdGuid of this image is already installed. if not, the entry
427 // point is loaded by DXE code which is the first time loaded. or else, it is already
428 // be reloaded be itself.This is a work-around
429 //
430 Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &DevicePath);
431 if (EFI_ERROR (Status)) {
432 //
433 // Create ReadyToLock event to reload BootScriptExecutor image
434 // to RESERVED mem and save it to LockBox.
435 //
436 ReadyToLockEvent = EfiCreateProtocolNotifyEvent (
437 &gEfiDxeSmmReadyToLockProtocolGuid,
438 TPL_NOTIFY,
439 ReadyToLockEventNotify,
440 NULL,
441 &Registration
442 );
443 ASSERT (ReadyToLockEvent != NULL);
444 } else {
445 //
446 // the entry point is invoked after reloading. following code only run in RESERVED mem
447 //
448 if (PcdGetBool(PcdUse1GPageTable)) {
449 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
450 if (RegEax >= 0x80000001) {
451 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
452 if ((RegEdx & BIT26) != 0) {
453 mPage1GSupport = TRUE;
454 }
455 }
456 }
457
458 BufferSize = sizeof (BOOT_SCRIPT_EXECUTOR_VARIABLE);
459
460 BootScriptExecutorBuffer = 0xFFFFFFFF;
461 Pages = EFI_SIZE_TO_PAGES(BufferSize);
462 Status = gBS->AllocatePages (
463 AllocateMaxAddress,
464 EfiReservedMemoryType,
465 Pages,
466 &BootScriptExecutorBuffer
467 );
468 ASSERT_EFI_ERROR (Status);
469
470 EfiBootScriptExecutorVariable = (BOOT_SCRIPT_EXECUTOR_VARIABLE *)(UINTN)BootScriptExecutorBuffer;
471 EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = (UINTN) S3BootScriptExecutorEntryFunction ;
472
473 Status = SaveLockBox (
474 &gEfiBootScriptExecutorVariableGuid,
475 &BootScriptExecutorBuffer,
476 sizeof(BootScriptExecutorBuffer)
477 );
478 ASSERT_EFI_ERROR (Status);
479
480 //
481 // Additional step for BootScript integrity
482 // Save BootScriptExecutor context
483 //
484 Status = SaveLockBox (
485 &gEfiBootScriptExecutorContextGuid,
486 EfiBootScriptExecutorVariable,
487 sizeof(*EfiBootScriptExecutorVariable)
488 );
489 ASSERT_EFI_ERROR (Status);
490
491 Status = SetLockBoxAttributes (&gEfiBootScriptExecutorContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
492 ASSERT_EFI_ERROR (Status);
493 }
494
495 return EFI_SUCCESS;
496 }
497