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
7Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
8
9This program and the accompanying materials
10are licensed and made available under the terms and conditions of the BSD License
11which accompanies this distribution.  The full text of the license may be found at
12http://opensource.org/licenses/bsd-license.php
13
14THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
15WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16
17**/
18
19#include "ScriptExecute.h"
20
21EFI_GUID              mBootScriptExecutorImageGuid = {
22  0x9a8d3433, 0x9fe8, 0x42b6, { 0x87, 0xb, 0x1e, 0x31, 0xc8, 0x4e, 0xbe, 0x3b }
23};
24
25BOOLEAN               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**/
38EFI_STATUS
39EFIAPI
40S3BootScriptExecutorEntryFunction (
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**/
220VOID
221RegisterMemoryProfileImage (
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 **/
259VOID
260EFIAPI
261ReadyToLockEventNotify (
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  if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) {
311    Pages = EFI_SIZE_TO_PAGES ((UINTN) (ImageContext.ImageSize + ImageContext.SectionAlignment));
312  } else {
313    Pages = EFI_SIZE_TO_PAGES ((UINTN) ImageContext.ImageSize);
314  }
315  FfsBuffer = 0xFFFFFFFF;
316  Status = gBS->AllocatePages (
317                  AllocateMaxAddress,
318                  EfiReservedMemoryType,
319                  Pages,
320                  &FfsBuffer
321                  );
322  ASSERT_EFI_ERROR (Status);
323  ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer;
324  //
325  // Align buffer on section boundary
326  //
327  ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;
328  ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)(ImageContext.SectionAlignment - 1));
329  //
330  // Load the image to our new buffer
331  //
332  Status = PeCoffLoaderLoadImage (&ImageContext);
333  ASSERT_EFI_ERROR (Status);
334
335  //
336  // Relocate the image in our new buffer
337  //
338  Status = PeCoffLoaderRelocateImage (&ImageContext);
339  ASSERT_EFI_ERROR (Status);
340
341  //
342  // Free the buffer allocated by ReadSection since the image has been relocated in the new buffer
343  //
344  gBS->FreePool (Buffer);
345
346  //
347  // Flush the instruction cache so the image data is written before we execute it
348  //
349  InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize);
350
351  RegisterMemoryProfileImage (
352    &gEfiCallerIdGuid,
353    ImageContext.ImageAddress,
354    ImageContext.ImageSize,
355    EFI_FV_FILETYPE_DRIVER
356    );
357
358  Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, gST);
359  ASSERT_EFI_ERROR (Status);
360
361  //
362  // Additional step for BootScript integrity
363  // Save BootScriptExecutor image
364  //
365  Status = SaveLockBox (
366             &mBootScriptExecutorImageGuid,
367             (VOID *)(UINTN)ImageContext.ImageAddress,
368             (UINTN)ImageContext.ImageSize
369             );
370  ASSERT_EFI_ERROR (Status);
371
372  Status = SetLockBoxAttributes (&mBootScriptExecutorImageGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
373  ASSERT_EFI_ERROR (Status);
374
375  gBS->CloseEvent (Event);
376}
377
378/**
379  Entrypoint of Boot script exector driver, this function will be executed in
380  normal boot phase and invoked by DXE dispatch.
381
382  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
383  @param[in] SystemTable    A pointer to the EFI System Table.
384
385  @retval EFI_SUCCESS       The entry point is executed successfully.
386  @retval other             Some error occurs when executing this entry point.
387**/
388EFI_STATUS
389EFIAPI
390BootScriptExecutorEntryPoint (
391  IN EFI_HANDLE           ImageHandle,
392  IN EFI_SYSTEM_TABLE     *SystemTable
393  )
394{
395  UINTN                                         BufferSize;
396  UINTN                                         Pages;
397  BOOT_SCRIPT_EXECUTOR_VARIABLE                 *EfiBootScriptExecutorVariable;
398  EFI_PHYSICAL_ADDRESS                          BootScriptExecutorBuffer;
399  EFI_STATUS                                    Status;
400  VOID                                          *DevicePath;
401  EFI_EVENT                                     ReadyToLockEvent;
402  VOID                                          *Registration;
403  UINT32                                        RegEax;
404  UINT32                                        RegEdx;
405
406  if (!PcdGetBool (PcdAcpiS3Enable)) {
407    return EFI_UNSUPPORTED;
408  }
409
410  //
411  // Test if the gEfiCallerIdGuid of this image is already installed. if not, the entry
412  // point is loaded by DXE code which is the first time loaded. or else, it is already
413  // be reloaded be itself.This is a work-around
414  //
415  Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &DevicePath);
416  if (EFI_ERROR (Status)) {
417      //
418      // Create ReadyToLock event to reload BootScriptExecutor image
419      // to RESERVED mem and save it to LockBox.
420      //
421      ReadyToLockEvent = EfiCreateProtocolNotifyEvent  (
422                           &gEfiDxeSmmReadyToLockProtocolGuid,
423                           TPL_NOTIFY,
424                           ReadyToLockEventNotify,
425                           NULL,
426                           &Registration
427                           );
428      ASSERT (ReadyToLockEvent != NULL);
429    } else {
430      //
431      // the entry point is invoked after reloading. following code only run in RESERVED mem
432      //
433      if (PcdGetBool(PcdUse1GPageTable)) {
434        AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
435        if (RegEax >= 0x80000001) {
436          AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
437          if ((RegEdx & BIT26) != 0) {
438            mPage1GSupport = TRUE;
439          }
440        }
441      }
442
443      BufferSize = sizeof (BOOT_SCRIPT_EXECUTOR_VARIABLE);
444
445      BootScriptExecutorBuffer = 0xFFFFFFFF;
446      Pages = EFI_SIZE_TO_PAGES(BufferSize);
447      Status = gBS->AllocatePages (
448                      AllocateMaxAddress,
449                      EfiReservedMemoryType,
450                      Pages,
451                      &BootScriptExecutorBuffer
452                      );
453      ASSERT_EFI_ERROR (Status);
454
455      EfiBootScriptExecutorVariable = (BOOT_SCRIPT_EXECUTOR_VARIABLE *)(UINTN)BootScriptExecutorBuffer;
456      EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = (UINTN) S3BootScriptExecutorEntryFunction ;
457
458      Status = SaveLockBox (
459                 &gEfiBootScriptExecutorVariableGuid,
460                 &BootScriptExecutorBuffer,
461                 sizeof(BootScriptExecutorBuffer)
462                 );
463      ASSERT_EFI_ERROR (Status);
464
465      //
466      // Additional step for BootScript integrity
467      // Save BootScriptExecutor context
468      //
469      Status = SaveLockBox (
470                 &gEfiBootScriptExecutorContextGuid,
471                 EfiBootScriptExecutorVariable,
472                 sizeof(*EfiBootScriptExecutorVariable)
473                 );
474      ASSERT_EFI_ERROR (Status);
475
476      Status = SetLockBoxAttributes (&gEfiBootScriptExecutorContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
477      ASSERT_EFI_ERROR (Status);
478    }
479
480    return EFI_SUCCESS;
481}
482
483