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