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