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
S3BootScriptExecutorEntryFunction(IN ACPI_S3_CONTEXT * AcpiS3Context,IN PEI_S3_RESUME_STATE * PeiS3ResumeState)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
RegisterMemoryProfileImage(IN EFI_GUID * FileName,IN PHYSICAL_ADDRESS ImageBase,IN UINT64 ImageSize,IN EFI_FV_FILETYPE FileType)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
ReadyToLockEventNotify(IN EFI_EVENT Event,IN VOID * Context)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
BootScriptExecutorEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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 
498