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