]> git.proxmox.com Git - mirror_edk2.git/blob - QuarkPlatformPkg/Acpi/Dxe/BootScriptExecutorDxe/ScriptExecute.c
QuarkPlatformPkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / QuarkPlatformPkg / Acpi / Dxe / 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 NVS memory
5 in the entry point. The functionality is to interpret and restore the S3 boot script
6
7 Copyright (c) 2013-2016 Intel Corporation.
8
9 SPDX-License-Identifier: BSD-2-Clause-Patent
10
11 **/
12
13 #include "ScriptExecute.h"
14
15 #pragma pack(1)
16 typedef union {
17 struct {
18 UINT32 LimitLow : 16;
19 UINT32 BaseLow : 16;
20 UINT32 BaseMid : 8;
21 UINT32 Type : 4;
22 UINT32 System : 1;
23 UINT32 Dpl : 2;
24 UINT32 Present : 1;
25 UINT32 LimitHigh : 4;
26 UINT32 Software : 1;
27 UINT32 Reserved : 1;
28 UINT32 DefaultSize : 1;
29 UINT32 Granularity : 1;
30 UINT32 BaseHigh : 8;
31 } Bits;
32 UINT64 Uint64;
33 } IA32_GDT;
34
35 #pragma pack()
36
37 EFI_GUID mBootScriptExecutorImageGuid = {
38 0x9a8d3433, 0x9fe8, 0x42b6, {0x87, 0xb, 0x1e, 0x31, 0xc8, 0x4e, 0xbe, 0x3b}
39 };
40
41 //
42 // Global Descriptor Table (GDT)
43 //
44 GLOBAL_REMOVE_IF_UNREFERENCED IA32_GDT mGdtEntries[] = {
45 /* selector { Global Segment Descriptor } */
46 /* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
47 /* 0x08 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
48 /* 0x10 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 0, 1, 1, 0}},
49 /* 0x18 */ {{0xFFFF, 0, 0, 0x3, 1, 0, 1, 0xF, 0, 0, 1, 1, 0}},
50 /* 0x20 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
51 /* 0x28 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 0, 0, 1, 0}},
52 /* 0x30 */ {{0xFFFF, 0, 0, 0x3, 1, 0, 1, 0xF, 0, 0, 0, 1, 0}},
53 /* 0x38 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 1, 0, 1, 0}},
54 /* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
55 };
56
57 //
58 // IA32 Gdt register
59 //
60 GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt = {
61 sizeof (mGdtEntries) - 1,
62 (UINTN) mGdtEntries
63 };
64
65 /**
66 Entry function of Boot script exector. This function will be executed in
67 S3 boot path.
68 This function should not return, because it is invoked by switch stack.
69
70 @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT
71 @param PeiS3ResumeState a pointer to a structure of PEI_S3_RESUME_STATE
72
73 @retval EFI_INVALID_PARAMETER - OS waking vector not found
74 @retval EFI_UNSUPPORTED - something wrong when we resume to OS
75 **/
76 EFI_STATUS
77 EFIAPI
78 S3BootScriptExecutorEntryFunction (
79 IN ACPI_S3_CONTEXT *AcpiS3Context,
80 IN PEI_S3_RESUME_STATE *PeiS3ResumeState
81 )
82 {
83 EFI_STATUS Status;
84
85 //
86 // Disable interrupt of Debug timer, since new IDT table cannot handle it.
87 //
88 SaveAndSetDebugTimerInterrupt (FALSE);
89
90 //
91 // Restore IDT for debug
92 //
93 SetIdtEntry (AcpiS3Context);
94
95 //
96 // Initialize Debug Agent to support source level debug in S3 path.
97 //
98 InitializeDebugAgent (DEBUG_AGENT_INIT_S3, NULL, NULL);
99
100 //
101 // Because not install BootScriptExecute PPI(used just in this module), So just pass NULL
102 // for that parameter.
103 //
104 Status = S3BootScriptExecute ();
105
106 AsmWbinvd ();
107
108 //
109 // We need turn back to S3Resume - install boot script done ppi and report status code on S3resume.
110 //
111 if (PeiS3ResumeState != 0) {
112 //
113 // Need report status back to S3ResumePeim.
114 // If boot script execution is failed, S3ResumePeim wil report the error status code.
115 //
116 PeiS3ResumeState->ReturnStatus = (UINT64)(UINTN)Status;
117 //
118 // IA32 S3 Resume
119 //
120 DEBUG ((EFI_D_INFO, "Call SwitchStack() to return to S3 Resume in PEI Phase\n"));
121 PeiS3ResumeState->AsmTransferControl = (EFI_PHYSICAL_ADDRESS)(UINTN)PlatformTransferControl16;
122
123 SwitchStack (
124 (SWITCH_STACK_ENTRY_POINT)(UINTN)PeiS3ResumeState->ReturnEntryPoint,
125 (VOID *)(UINTN)AcpiS3Context,
126 (VOID *)(UINTN)PeiS3ResumeState,
127 (VOID *)(UINTN)PeiS3ResumeState->ReturnStackPointer
128 );
129
130 //
131 // Never run to here
132 //
133 CpuDeadLoop();
134 return EFI_UNSUPPORTED;
135 }
136
137 //
138 // Never run to here
139 //
140 CpuDeadLoop();
141 return EFI_UNSUPPORTED;
142 }
143 /**
144 Entrypoint of Boot script exector driver, this function will be executed in
145 normal boot phase and invoked by DXE dispatch.
146
147 @param[in] ImageHandle The firmware allocated handle for the EFI image.
148 @param[in] SystemTable A pointer to the EFI System Table.
149
150 @retval EFI_SUCCESS The entry point is executed successfully.
151 @retval other Some error occurs when executing this entry point.
152 **/
153 EFI_STATUS
154 EFIAPI
155 BootScriptExecutorEntryPoint (
156 IN EFI_HANDLE ImageHandle,
157 IN EFI_SYSTEM_TABLE *SystemTable
158 )
159 {
160 UINT8 *Buffer;
161 UINTN BufferSize;
162 UINTN Pages;
163 EFI_PHYSICAL_ADDRESS FfsBuffer;
164 PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
165 BOOT_SCRIPT_EXECUTOR_VARIABLE *EfiBootScriptExecutorVariable;
166 EFI_PHYSICAL_ADDRESS BootScriptExecutorBuffer;
167 EFI_STATUS Status;
168 VOID *DevicePath;
169 EFI_HANDLE NewImageHandle;
170
171 //
172 // Test if the gEfiCallerIdGuid of this image is already installed. if not, the entry
173 // point is loaded by DXE code which is the first time loaded. or else, it is already
174 // be reloaded be itself.This is a work-around
175 //
176 Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &DevicePath);
177 if (EFI_ERROR (Status)) {
178
179 //
180 // This is the first-time loaded by DXE core. reload itself to NVS mem
181 //
182 //
183 // A workarouond: Here we install a dummy handle
184 //
185 NewImageHandle = NULL;
186 Status = gBS->InstallProtocolInterface (
187 &NewImageHandle,
188 &gEfiCallerIdGuid,
189 EFI_NATIVE_INTERFACE,
190 NULL
191 );
192
193 Status = GetSectionFromAnyFv (
194 &gEfiCallerIdGuid,
195 EFI_SECTION_PE32,
196 0,
197 (VOID **) &Buffer,
198 &BufferSize
199 );
200 ImageContext.Handle = Buffer;
201 ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
202 //
203 // Get information about the image being loaded
204 //
205 Status = PeCoffLoaderGetImageInfo (&ImageContext);
206 if (EFI_ERROR (Status)) {
207 return Status;
208 }
209 Pages = EFI_SIZE_TO_PAGES(BufferSize + ImageContext.SectionAlignment);
210 FfsBuffer = 0xFFFFFFFF;
211 Status = gBS->AllocatePages (
212 AllocateMaxAddress,
213 EfiACPIMemoryNVS,
214 Pages,
215 &FfsBuffer
216 );
217 if (EFI_ERROR (Status)) {
218 return EFI_OUT_OF_RESOURCES;
219 }
220 ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer;
221 //
222 // Align buffer on section boundary
223 //
224 ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;
225 ImageContext.ImageAddress &= ~(ImageContext.SectionAlignment - 1);
226 //
227 // Load the image to our new buffer
228 //
229 Status = PeCoffLoaderLoadImage (&ImageContext);
230 if (EFI_ERROR (Status)) {
231 gBS->FreePages (FfsBuffer, Pages);
232 return Status;
233 }
234
235 //
236 // Relocate the image in our new buffer
237 //
238 Status = PeCoffLoaderRelocateImage (&ImageContext);
239
240 if (EFI_ERROR (Status)) {
241 PeCoffLoaderUnloadImage (&ImageContext);
242 gBS->FreePages (FfsBuffer, Pages);
243 return Status;
244 }
245 //
246 // Flush the instruction cache so the image data is written before we execute it
247 //
248 InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize);
249 Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, SystemTable);
250 if (EFI_ERROR (Status)) {
251 gBS->FreePages (FfsBuffer, Pages);
252 return Status;
253 }
254 //
255 // Additional step for BootScript integrity
256 // Save BootScriptExecutor image
257 //
258 Status = SaveLockBox (
259 &mBootScriptExecutorImageGuid,
260 (VOID *)(UINTN)ImageContext.ImageAddress,
261 (UINTN)ImageContext.ImageSize
262 );
263 ASSERT_EFI_ERROR (Status);
264
265 Status = SetLockBoxAttributes (&mBootScriptExecutorImageGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
266 ASSERT_EFI_ERROR (Status);
267
268 } else {
269 //
270 // the entry point is invoked after reloading. following code only run in ACPI NVS
271 //
272 BufferSize = sizeof (BOOT_SCRIPT_EXECUTOR_VARIABLE);
273
274 BootScriptExecutorBuffer = 0xFFFFFFFF;
275 Pages = EFI_SIZE_TO_PAGES(BufferSize);
276 Status = gBS->AllocatePages (
277 AllocateMaxAddress,
278 EfiACPIMemoryNVS,
279 Pages,
280 &BootScriptExecutorBuffer
281 );
282 if (EFI_ERROR (Status)) {
283 return EFI_OUT_OF_RESOURCES;
284 }
285
286 EfiBootScriptExecutorVariable = (BOOT_SCRIPT_EXECUTOR_VARIABLE *)(UINTN)BootScriptExecutorBuffer;
287 EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = (UINTN) S3BootScriptExecutorEntryFunction ;
288
289 Status = SaveLockBox (
290 &gEfiBootScriptExecutorVariableGuid,
291 &BootScriptExecutorBuffer,
292 sizeof(BootScriptExecutorBuffer)
293 );
294 ASSERT_EFI_ERROR (Status);
295
296 //
297 // Additional step for BootScript integrity
298 // Save BootScriptExecutor context
299 //
300 Status = SaveLockBox (
301 &gEfiBootScriptExecutorContextGuid,
302 EfiBootScriptExecutorVariable,
303 sizeof(*EfiBootScriptExecutorVariable)
304 );
305 ASSERT_EFI_ERROR (Status);
306
307 Status = SetLockBoxAttributes (&gEfiBootScriptExecutorContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
308 ASSERT_EFI_ERROR (Status);
309
310 }
311
312 return EFI_SUCCESS;
313 }
314
315 /**
316 Platform specific mechanism to transfer control to 16bit OS waking vector
317
318 @param[in] AcpiWakingVector The 16bit OS waking vector
319 @param[in] AcpiLowMemoryBase A buffer under 1M which could be used during the transfer
320
321 **/
322 VOID
323 PlatformTransferControl16 (
324 IN UINT32 AcpiWakingVector,
325 IN UINT32 AcpiLowMemoryBase
326 )
327 {
328 UINT32 NewValue;
329 UINT64 BaseAddress;
330 UINT64 SmramLength;
331 UINTN Index;
332
333 DEBUG (( EFI_D_INFO, "PlatformTransferControl - Entry\r\n"));
334
335 //
336 // Need to make sure the GDT is loaded with values that support long mode and real mode.
337 //
338 AsmWriteGdtr (&mGdt);
339
340 //
341 // Disable eSram block (this will also clear/zero eSRAM)
342 // We only use eSRAM in the PEI phase. Disable now that we are resuming the OS
343 //
344 NewValue = QNCPortRead (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_ESRAMPGCTRL_BLOCK);
345 NewValue |= BLOCK_DISABLE_PG;
346 QNCPortWrite (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_ESRAMPGCTRL_BLOCK, NewValue);
347
348 //
349 // Update HMBOUND to top of DDR3 memory and LOCK
350 // We disabled eSRAM so now we move HMBOUND down to top of DDR3
351 //
352 QNCGetTSEGMemoryRange (&BaseAddress, &SmramLength);
353 NewValue = (UINT32)(BaseAddress + SmramLength);
354 DEBUG ((EFI_D_INFO,"Locking HMBOUND at: = 0x%8x\n",NewValue));
355 QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QUARK_NC_HOST_BRIDGE_HMBOUND_REG, (NewValue | HMBOUND_LOCK));
356
357 //
358 // Lock all IMR regions now that HMBOUND is locked
359 //
360 for (Index = (QUARK_NC_MEMORY_MANAGER_IMR0+QUARK_NC_MEMORY_MANAGER_IMRXL); Index <= (QUARK_NC_MEMORY_MANAGER_IMR7+QUARK_NC_MEMORY_MANAGER_IMRXL); Index += 4) {
361 NewValue = QNCPortRead (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, Index);
362 NewValue |= IMR_LOCK;
363 QNCPortWrite (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, Index, NewValue);
364 }
365
366 //
367 // Call ASM routine to switch to real mode and jump to 16bit OS waking vector
368 //
369 AsmTransferControl(AcpiWakingVector, 0);
370
371 //
372 // Never run to here
373 //
374 CpuDeadLoop();
375 }
376
377
378
379