]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/ScriptExecute.c
6c1c304a150d5efd10bd5e4273c5ad169d1a1a3b
[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 This is the Event notification function to reload BootScriptExecutor image
213 to RESERVED mem and save it to LockBox.
214
215 @param Event Pointer to this event
216 @param Context Event handler private data
217 **/
218 VOID
219 EFIAPI
220 ReadyToLockEventNotify (
221 IN EFI_EVENT Event,
222 IN VOID *Context
223 )
224 {
225 EFI_STATUS Status;
226 VOID *Interface;
227 UINT8 *Buffer;
228 UINTN BufferSize;
229 EFI_HANDLE NewImageHandle;
230 UINTN Pages;
231 EFI_PHYSICAL_ADDRESS FfsBuffer;
232 PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
233
234 Status = gBS->LocateProtocol (&gEfiDxeSmmReadyToLockProtocolGuid, NULL, &Interface);
235 if (EFI_ERROR (Status)) {
236 return;
237 }
238
239 //
240 // A workaround: Here we install a dummy handle
241 //
242 NewImageHandle = NULL;
243 Status = gBS->InstallProtocolInterface (
244 &NewImageHandle,
245 &gEfiCallerIdGuid,
246 EFI_NATIVE_INTERFACE,
247 NULL
248 );
249 ASSERT_EFI_ERROR (Status);
250
251 //
252 // Reload BootScriptExecutor image itself to RESERVED mem
253 //
254 Status = GetSectionFromAnyFv (
255 &gEfiCallerIdGuid,
256 EFI_SECTION_PE32,
257 0,
258 (VOID **) &Buffer,
259 &BufferSize
260 );
261 ASSERT_EFI_ERROR (Status);
262 ImageContext.Handle = Buffer;
263 ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
264 //
265 // Get information about the image being loaded
266 //
267 Status = PeCoffLoaderGetImageInfo (&ImageContext);
268 ASSERT_EFI_ERROR (Status);
269 Pages = EFI_SIZE_TO_PAGES(BufferSize + ImageContext.SectionAlignment);
270 FfsBuffer = 0xFFFFFFFF;
271 Status = gBS->AllocatePages (
272 AllocateMaxAddress,
273 EfiReservedMemoryType,
274 Pages,
275 &FfsBuffer
276 );
277 ASSERT_EFI_ERROR (Status);
278 ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer;
279 //
280 // Align buffer on section boundry
281 //
282 ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;
283 ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)(ImageContext.SectionAlignment - 1));
284 //
285 // Load the image to our new buffer
286 //
287 Status = PeCoffLoaderLoadImage (&ImageContext);
288 ASSERT_EFI_ERROR (Status);
289
290 //
291 // Relocate the image in our new buffer
292 //
293 Status = PeCoffLoaderRelocateImage (&ImageContext);
294 ASSERT_EFI_ERROR (Status);
295
296 //
297 // Free the buffer allocated by ReadSection since the image has been relocated in the new buffer
298 //
299 gBS->FreePool (Buffer);
300
301 //
302 // Flush the instruction cache so the image data is written before we execute it
303 //
304 InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize);
305 Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, gST);
306 ASSERT_EFI_ERROR (Status);
307
308 //
309 // Additional step for BootScript integrity
310 // Save BootScriptExecutor image
311 //
312 Status = SaveLockBox (
313 &mBootScriptExecutorImageGuid,
314 (VOID *)(UINTN)ImageContext.ImageAddress,
315 (UINTN)ImageContext.ImageSize
316 );
317 ASSERT_EFI_ERROR (Status);
318
319 Status = SetLockBoxAttributes (&mBootScriptExecutorImageGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
320 ASSERT_EFI_ERROR (Status);
321
322 gBS->CloseEvent (Event);
323 }
324
325 /**
326 Entrypoint of Boot script exector driver, this function will be executed in
327 normal boot phase and invoked by DXE dispatch.
328
329 @param[in] ImageHandle The firmware allocated handle for the EFI image.
330 @param[in] SystemTable A pointer to the EFI System Table.
331
332 @retval EFI_SUCCESS The entry point is executed successfully.
333 @retval other Some error occurs when executing this entry point.
334 **/
335 EFI_STATUS
336 EFIAPI
337 BootScriptExecutorEntryPoint (
338 IN EFI_HANDLE ImageHandle,
339 IN EFI_SYSTEM_TABLE *SystemTable
340 )
341 {
342 UINTN BufferSize;
343 UINTN Pages;
344 BOOT_SCRIPT_EXECUTOR_VARIABLE *EfiBootScriptExecutorVariable;
345 EFI_PHYSICAL_ADDRESS BootScriptExecutorBuffer;
346 EFI_STATUS Status;
347 VOID *DevicePath;
348 EFI_EVENT ReadyToLockEvent;
349 VOID *Registration;
350 UINT32 RegEax;
351 UINT32 RegEdx;
352
353 //
354 // Test if the gEfiCallerIdGuid of this image is already installed. if not, the entry
355 // point is loaded by DXE code which is the first time loaded. or else, it is already
356 // be reloaded be itself.This is a work-around
357 //
358 Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &DevicePath);
359 if (EFI_ERROR (Status)) {
360 //
361 // Create ReadyToLock event to reload BootScriptExecutor image
362 // to RESERVED mem and save it to LockBox.
363 //
364 ReadyToLockEvent = EfiCreateProtocolNotifyEvent (
365 &gEfiDxeSmmReadyToLockProtocolGuid,
366 TPL_NOTIFY,
367 ReadyToLockEventNotify,
368 NULL,
369 &Registration
370 );
371 ASSERT (ReadyToLockEvent != NULL);
372 } else {
373 //
374 // the entry point is invoked after reloading. following code only run in RESERVED mem
375 //
376 if (PcdGetBool(PcdUse1GPageTable)) {
377 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
378 if (RegEax >= 0x80000001) {
379 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
380 if ((RegEdx & BIT26) != 0) {
381 mPage1GSupport = TRUE;
382 }
383 }
384 }
385
386 BufferSize = sizeof (BOOT_SCRIPT_EXECUTOR_VARIABLE);
387
388 BootScriptExecutorBuffer = 0xFFFFFFFF;
389 Pages = EFI_SIZE_TO_PAGES(BufferSize);
390 Status = gBS->AllocatePages (
391 AllocateMaxAddress,
392 EfiReservedMemoryType,
393 Pages,
394 &BootScriptExecutorBuffer
395 );
396 ASSERT_EFI_ERROR (Status);
397
398 EfiBootScriptExecutorVariable = (BOOT_SCRIPT_EXECUTOR_VARIABLE *)(UINTN)BootScriptExecutorBuffer;
399 EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = (UINTN) S3BootScriptExecutorEntryFunction ;
400
401 Status = SaveLockBox (
402 &gEfiBootScriptExecutorVariableGuid,
403 &BootScriptExecutorBuffer,
404 sizeof(BootScriptExecutorBuffer)
405 );
406 ASSERT_EFI_ERROR (Status);
407
408 //
409 // Additional step for BootScript integrity
410 // Save BootScriptExecutor context
411 //
412 Status = SaveLockBox (
413 &gEfiBootScriptExecutorContextGuid,
414 EfiBootScriptExecutorVariable,
415 sizeof(*EfiBootScriptExecutorVariable)
416 );
417 ASSERT_EFI_ERROR (Status);
418
419 Status = SetLockBoxAttributes (&gEfiBootScriptExecutorContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
420 ASSERT_EFI_ERROR (Status);
421 }
422
423 return EFI_SUCCESS;
424 }
425