]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Acpi/S3SaveStateDxe/AcpiS3ContextSave.c
MdeModulePkg S3SaveStateDxe: Move S3Ready() functional code from AcpiS3SaveDxe
[mirror_edk2.git] / MdeModulePkg / Universal / Acpi / S3SaveStateDxe / AcpiS3ContextSave.c
1 /** @file
2 This is the implementation to save ACPI S3 Context.
3
4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
5
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions
8 of the BSD License which accompanies this distribution. The
9 full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15 **/
16
17 #include <PiDxe.h>
18 #include <Library/BaseLib.h>
19 #include <Library/BaseMemoryLib.h>
20 #include <Library/UefiBootServicesTableLib.h>
21 #include <Library/HobLib.h>
22 #include <Library/LockBoxLib.h>
23 #include <Library/PcdLib.h>
24 #include <Library/DebugLib.h>
25 #include <Guid/AcpiS3Context.h>
26 #include <Guid/Acpi.h>
27 #include <IndustryStandard/Acpi.h>
28 #include <Protocol/LockBox.h>
29
30 //
31 // 8 extra pages for PF handler.
32 //
33 #define EXTRA_PAGE_TABLE_PAGES 8
34
35 EFI_GUID mAcpiS3IdtrProfileGuid = {
36 0xdea652b0, 0xd587, 0x4c54, { 0xb5, 0xb4, 0xc6, 0x82, 0xe7, 0xa0, 0xaa, 0x3d }
37 };
38
39 /**
40 Allocate memory below 4G memory address.
41
42 This function allocates memory below 4G memory address.
43
44 @param MemoryType Memory type of memory to allocate.
45 @param Size Size of memory to allocate.
46
47 @return Allocated address for output.
48
49 **/
50 VOID*
51 AllocateMemoryBelow4G (
52 IN EFI_MEMORY_TYPE MemoryType,
53 IN UINTN Size
54 )
55 {
56 UINTN Pages;
57 EFI_PHYSICAL_ADDRESS Address;
58 EFI_STATUS Status;
59 VOID* Buffer;
60
61 Pages = EFI_SIZE_TO_PAGES (Size);
62 Address = 0xffffffff;
63
64 Status = gBS->AllocatePages (
65 AllocateMaxAddress,
66 MemoryType,
67 Pages,
68 &Address
69 );
70 ASSERT_EFI_ERROR (Status);
71
72 Buffer = (VOID *) (UINTN) Address;
73 ZeroMem (Buffer, Size);
74
75 return Buffer;
76 }
77
78 /**
79
80 This function scan ACPI table in RSDT.
81
82 @param Rsdt ACPI RSDT
83 @param Signature ACPI table signature
84
85 @return ACPI table
86
87 **/
88 VOID *
89 ScanTableInRSDT (
90 IN EFI_ACPI_DESCRIPTION_HEADER *Rsdt,
91 IN UINT32 Signature
92 )
93 {
94 UINTN Index;
95 UINT32 EntryCount;
96 UINT32 *EntryPtr;
97 EFI_ACPI_DESCRIPTION_HEADER *Table;
98
99 if (Rsdt == NULL) {
100 return NULL;
101 }
102
103 EntryCount = (Rsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / sizeof(UINT32);
104
105 EntryPtr = (UINT32 *)(Rsdt + 1);
106 for (Index = 0; Index < EntryCount; Index ++, EntryPtr ++) {
107 Table = (EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)(*EntryPtr));
108 if (Table->Signature == Signature) {
109 return Table;
110 }
111 }
112
113 return NULL;
114 }
115
116 /**
117
118 This function scan ACPI table in XSDT.
119
120 @param Xsdt ACPI XSDT
121 @param Signature ACPI table signature
122
123 @return ACPI table
124
125 **/
126 VOID *
127 ScanTableInXSDT (
128 IN EFI_ACPI_DESCRIPTION_HEADER *Xsdt,
129 IN UINT32 Signature
130 )
131 {
132 UINTN Index;
133 UINT32 EntryCount;
134 UINT64 EntryPtr;
135 UINTN BasePtr;
136 EFI_ACPI_DESCRIPTION_HEADER *Table;
137
138 if (Xsdt == NULL) {
139 return NULL;
140 }
141
142 EntryCount = (Xsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / sizeof(UINT64);
143
144 BasePtr = (UINTN)(Xsdt + 1);
145 for (Index = 0; Index < EntryCount; Index ++) {
146 CopyMem (&EntryPtr, (VOID *)(BasePtr + Index * sizeof(UINT64)), sizeof(UINT64));
147 Table = (EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)(EntryPtr));
148 if (Table->Signature == Signature) {
149 return Table;
150 }
151 }
152
153 return NULL;
154 }
155
156 /**
157 To find Facs in FADT.
158
159 @param Fadt FADT table pointer
160
161 @return Facs table pointer.
162 **/
163 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *
164 FindAcpiFacsFromFadt (
165 IN EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt
166 )
167 {
168 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
169 UINT64 Data64;
170
171 if (Fadt == NULL) {
172 return NULL;
173 }
174
175 if (Fadt->Header.Revision < EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION) {
176 Facs = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)Fadt->FirmwareCtrl;
177 } else {
178 if (Fadt->FirmwareCtrl != 0) {
179 Facs = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)Fadt->FirmwareCtrl;
180 } else {
181 CopyMem (&Data64, &Fadt->XFirmwareCtrl, sizeof(UINT64));
182 Facs = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)Data64;
183 }
184 }
185 return Facs;
186 }
187
188 /**
189 To find Facs in Acpi tables.
190
191 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
192 in the table.
193
194 @param AcpiTableGuid The guid used to find ACPI table in UEFI ConfigurationTable.
195
196 @return Facs table pointer.
197 **/
198 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *
199 FindAcpiFacsTableByAcpiGuid (
200 IN EFI_GUID *AcpiTableGuid
201 )
202 {
203 EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp;
204 EFI_ACPI_DESCRIPTION_HEADER *Rsdt;
205 EFI_ACPI_DESCRIPTION_HEADER *Xsdt;
206 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;
207 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
208 UINTN Index;
209
210 Rsdp = NULL;
211 //
212 // found ACPI table RSD_PTR from system table
213 //
214 for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
215 if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), AcpiTableGuid)) {
216 //
217 // A match was found.
218 //
219 Rsdp = gST->ConfigurationTable[Index].VendorTable;
220 break;
221 }
222 }
223
224 if (Rsdp == NULL) {
225 return NULL;
226 }
227
228 //
229 // Search XSDT
230 //
231 if (Rsdp->Revision >= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION) {
232 Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN) Rsdp->XsdtAddress;
233 Fadt = ScanTableInXSDT (Xsdt, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE);
234 if (Fadt != NULL) {
235 Facs = FindAcpiFacsFromFadt (Fadt);
236 if (Facs != NULL) {
237 return Facs;
238 }
239 }
240 }
241
242 //
243 // Search RSDT
244 //
245 Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN) Rsdp->RsdtAddress;
246 Fadt = ScanTableInRSDT (Rsdt, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE);
247 if (Fadt != NULL) {
248 Facs = FindAcpiFacsFromFadt (Fadt);
249 if (Facs != NULL) {
250 return Facs;
251 }
252 }
253
254 return NULL;
255 }
256
257 /**
258 To find Facs in Acpi tables.
259
260 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
261 in the table.
262
263 @return Facs table pointer.
264 **/
265 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *
266 FindAcpiFacsTable (
267 VOID
268 )
269 {
270 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
271
272 Facs = FindAcpiFacsTableByAcpiGuid (&gEfiAcpi20TableGuid);
273 if (Facs != NULL) {
274 return Facs;
275 }
276
277 return FindAcpiFacsTableByAcpiGuid (&gEfiAcpi10TableGuid);
278 }
279
280 /**
281 The function will check if long mode waking vector is supported.
282
283 @param[in] Facs Pointer to FACS table.
284
285 @retval TRUE Long mode waking vector is supported.
286 @retval FALSE Long mode waking vector is not supported.
287
288 **/
289 BOOLEAN
290 IsLongModeWakingVectorSupport (
291 IN EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs
292 )
293 {
294 if ((Facs == NULL) ||
295 (Facs->Signature != EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) ) {
296 //
297 // Something wrong with FACS.
298 //
299 return FALSE;
300 }
301 if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) &&
302 ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0)) {
303 //
304 // BIOS supports 64bit waking vector.
305 //
306 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
307 return TRUE;
308 }
309 }
310 return FALSE;
311 }
312
313 /**
314 Allocates page table buffer.
315
316 @param[in] LongModeWakingVectorSupport Support long mode waking vector or not.
317
318 If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1
319 virtual to physical mapping page table when long mode waking vector is supported, otherwise
320 create 4G page table when long mode waking vector is not supported and let PF handler to
321 handle > 4G request.
322 If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.
323
324 @return Page table base address.
325
326 **/
327 EFI_PHYSICAL_ADDRESS
328 S3AllocatePageTablesBuffer (
329 IN BOOLEAN LongModeWakingVectorSupport
330 )
331 {
332 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
333 UINTN ExtraPageTablePages;
334 UINT32 RegEax;
335 UINT32 RegEdx;
336 UINT8 PhysicalAddressBits;
337 UINT32 NumberOfPml4EntriesNeeded;
338 UINT32 NumberOfPdpEntriesNeeded;
339 EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress;
340 UINTN TotalPageTableSize;
341 VOID *Hob;
342 BOOLEAN Page1GSupport;
343
344 Page1GSupport = FALSE;
345 if (PcdGetBool(PcdUse1GPageTable)) {
346 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
347 if (RegEax >= 0x80000001) {
348 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
349 if ((RegEdx & BIT26) != 0) {
350 Page1GSupport = TRUE;
351 }
352 }
353 }
354
355 //
356 // Get physical address bits supported.
357 //
358 Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
359 if (Hob != NULL) {
360 PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
361 } else {
362 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
363 if (RegEax >= 0x80000008) {
364 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
365 PhysicalAddressBits = (UINT8) RegEax;
366 } else {
367 PhysicalAddressBits = 36;
368 }
369 }
370
371 //
372 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
373 //
374 ASSERT (PhysicalAddressBits <= 52);
375 if (PhysicalAddressBits > 48) {
376 PhysicalAddressBits = 48;
377 }
378
379 ExtraPageTablePages = 0;
380 if (!LongModeWakingVectorSupport) {
381 //
382 // Create 4G page table when BIOS does not support long mode waking vector,
383 // and let PF handler to handle > 4G request.
384 //
385 PhysicalAddressBits = 32;
386 ExtraPageTablePages = EXTRA_PAGE_TABLE_PAGES;
387 }
388
389 //
390 // Calculate the table entries needed.
391 //
392 if (PhysicalAddressBits <= 39 ) {
393 NumberOfPml4EntriesNeeded = 1;
394 NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
395 } else {
396 NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
397 NumberOfPdpEntriesNeeded = 512;
398 }
399
400 //
401 // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs.
402 //
403 if (!Page1GSupport) {
404 TotalPageTableSize = (UINTN)(1 + NumberOfPml4EntriesNeeded + NumberOfPml4EntriesNeeded * NumberOfPdpEntriesNeeded);
405 } else {
406 TotalPageTableSize = (UINTN)(1 + NumberOfPml4EntriesNeeded);
407 }
408
409 TotalPageTableSize += ExtraPageTablePages;
410 DEBUG ((EFI_D_ERROR, "AcpiS3ContextSave TotalPageTableSize - 0x%x pages\n", TotalPageTableSize));
411
412 //
413 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
414 //
415 S3NvsPageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGES_TO_SIZE(TotalPageTableSize));
416 ASSERT (S3NvsPageTableAddress != 0);
417 return S3NvsPageTableAddress;
418 } else {
419 //
420 // If DXE is running 32-bit mode, no need to establish page table.
421 //
422 return (EFI_PHYSICAL_ADDRESS) 0;
423 }
424 }
425
426 /**
427 Callback function executed when the EndOfDxe event group is signaled.
428
429 @param[in] Event Event whose notification function is being invoked.
430 @param[in] Context The pointer to the notification function's context, which
431 is implementation-dependent.
432 **/
433 VOID
434 EFIAPI
435 AcpiS3ContextSaveOnEndOfDxe (
436 IN EFI_EVENT Event,
437 IN VOID *Context
438 )
439 {
440 EFI_STATUS Status;
441 EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer;
442 ACPI_S3_CONTEXT *AcpiS3Context;
443 IA32_DESCRIPTOR *Idtr;
444 IA32_IDT_GATE_DESCRIPTOR *IdtGate;
445 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
446 VOID *Interface;
447
448 DEBUG ((EFI_D_INFO, "AcpiS3ContextSave!\n"));
449
450 Status = gBS->LocateProtocol (&gEfiLockBoxProtocolGuid, NULL, &Interface);
451 if (EFI_ERROR (Status)) {
452 DEBUG ((EFI_D_INFO | EFI_D_WARN, "ACPI S3 context can't be saved without LockBox!\n"));
453 goto Done;
454 }
455
456 AcpiS3Context = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(*AcpiS3Context));
457 ASSERT (AcpiS3Context != NULL);
458 AcpiS3ContextBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)AcpiS3Context;
459
460 //
461 // Get ACPI Table because we will save its position to variable
462 //
463 Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) FindAcpiFacsTable ();
464 AcpiS3Context->AcpiFacsTable = (EFI_PHYSICAL_ADDRESS) (UINTN) Facs;
465 ASSERT (AcpiS3Context->AcpiFacsTable != 0);
466
467 IdtGate = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 + sizeof(IA32_DESCRIPTOR));
468 Idtr = (IA32_DESCRIPTOR *)(IdtGate + 0x100);
469 Idtr->Base = (UINTN)IdtGate;
470 Idtr->Limit = (UINT16)(sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 - 1);
471 AcpiS3Context->IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)Idtr;
472
473 Status = SaveLockBox (
474 &mAcpiS3IdtrProfileGuid,
475 (VOID *)(UINTN)Idtr,
476 (UINTN)sizeof(IA32_DESCRIPTOR)
477 );
478 ASSERT_EFI_ERROR (Status);
479
480 Status = SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
481 ASSERT_EFI_ERROR (Status);
482
483 //
484 // Allocate page table
485 //
486 AcpiS3Context->S3NvsPageTableAddress = S3AllocatePageTablesBuffer (IsLongModeWakingVectorSupport (Facs));
487
488 //
489 // Allocate stack
490 //
491 AcpiS3Context->BootScriptStackSize = PcdGet32 (PcdS3BootScriptStackSize);
492 AcpiS3Context->BootScriptStackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, PcdGet32 (PcdS3BootScriptStackSize));
493 ASSERT (AcpiS3Context->BootScriptStackBase != 0);
494
495 //
496 // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it.
497 //
498 AcpiS3Context->S3DebugBufferAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGE_SIZE);
499 SetMem ((VOID *)(UINTN)AcpiS3Context->S3DebugBufferAddress, EFI_PAGE_SIZE, 0xff);
500
501 DEBUG((EFI_D_INFO, "AcpiS3Context: AcpiFacsTable is 0x%8x\n", AcpiS3Context->AcpiFacsTable));
502 DEBUG((EFI_D_INFO, "AcpiS3Context: IdtrProfile is 0x%8x\n", AcpiS3Context->IdtrProfile));
503 DEBUG((EFI_D_INFO, "AcpiS3Context: S3NvsPageTableAddress is 0x%8x\n", AcpiS3Context->S3NvsPageTableAddress));
504 DEBUG((EFI_D_INFO, "AcpiS3Context: S3DebugBufferAddress is 0x%8x\n", AcpiS3Context->S3DebugBufferAddress));
505 DEBUG((EFI_D_INFO, "AcpiS3Context: BootScriptStackBase is 0x%8x\n", AcpiS3Context->BootScriptStackBase));
506 DEBUG((EFI_D_INFO, "AcpiS3Context: BootScriptStackSize is 0x%8x\n", AcpiS3Context->BootScriptStackSize));
507
508 Status = SaveLockBox (
509 &gEfiAcpiVariableGuid,
510 &AcpiS3ContextBuffer,
511 sizeof(AcpiS3ContextBuffer)
512 );
513 ASSERT_EFI_ERROR (Status);
514
515 Status = SaveLockBox (
516 &gEfiAcpiS3ContextGuid,
517 (VOID *)(UINTN)AcpiS3Context,
518 (UINTN)sizeof(*AcpiS3Context)
519 );
520 ASSERT_EFI_ERROR (Status);
521
522 Status = SetLockBoxAttributes (&gEfiAcpiS3ContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
523 ASSERT_EFI_ERROR (Status);
524
525 Done:
526 //
527 // Close the event, deregistering the callback and freeing resources.
528 //
529 Status = gBS->CloseEvent (Event);
530 ASSERT_EFI_ERROR (Status);
531 }
532