]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/AcpiS3SaveDxe/AcpiS3Save.c
8372db85bdb5a899b75fbc71c501efabcbc186ff
[mirror_edk2.git] / OvmfPkg / AcpiS3SaveDxe / AcpiS3Save.c
1 /** @file
2 This is an implementation of the ACPI S3 Save protocol. This is defined in
3 S3 boot path specification 0.9.
4
5 Copyright (c) 2014-2015, Red Hat, Inc.<BR>
6 Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
7
8 This program and the accompanying materials
9 are licensed and made available under the terms and conditions
10 of the BSD License which accompanies this distribution. The
11 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 <PiDxe.h>
20 #include <Library/BaseLib.h>
21 #include <Library/BaseMemoryLib.h>
22 #include <Library/UefiBootServicesTableLib.h>
23 #include <Library/UefiRuntimeServicesTableLib.h>
24 #include <Library/HobLib.h>
25 #include <Library/LockBoxLib.h>
26 #include <Library/PcdLib.h>
27 #include <Library/DebugLib.h>
28 #include <Library/QemuFwCfgLib.h>
29 #include <Guid/AcpiVariableCompatibility.h>
30 #include <Guid/AcpiS3Context.h>
31 #include <Guid/Acpi.h>
32 #include <Guid/EventGroup.h>
33 #include <Protocol/AcpiS3Save.h>
34 #include <Protocol/LockBox.h>
35 #include <IndustryStandard/Acpi.h>
36
37 #include "AcpiS3Save.h"
38
39 UINTN mLegacyRegionSize;
40
41 EFI_ACPI_S3_SAVE_PROTOCOL mS3Save = {
42 LegacyGetS3MemorySize,
43 S3Ready,
44 };
45
46 EFI_GUID mAcpiS3IdtrProfileGuid = {
47 0xdea652b0, 0xd587, 0x4c54, { 0xb5, 0xb4, 0xc6, 0x82, 0xe7, 0xa0, 0xaa, 0x3d }
48 };
49
50 /**
51 Allocate memory below 4G memory address.
52
53 This function allocates memory below 4G memory address.
54
55 @param MemoryType Memory type of memory to allocate.
56 @param Size Size of memory to allocate.
57
58 @return Allocated address for output.
59
60 **/
61 VOID*
62 AllocateMemoryBelow4G (
63 IN EFI_MEMORY_TYPE MemoryType,
64 IN UINTN Size
65 )
66 {
67 UINTN Pages;
68 EFI_PHYSICAL_ADDRESS Address;
69 EFI_STATUS Status;
70 VOID* Buffer;
71
72 Pages = EFI_SIZE_TO_PAGES (Size);
73 Address = 0xffffffff;
74
75 Status = gBS->AllocatePages (
76 AllocateMaxAddress,
77 MemoryType,
78 Pages,
79 &Address
80 );
81 ASSERT_EFI_ERROR (Status);
82
83 Buffer = (VOID *) (UINTN) Address;
84 ZeroMem (Buffer, Size);
85
86 return Buffer;
87 }
88
89 /**
90
91 This function scan ACPI table in RSDT.
92
93 @param Rsdt ACPI RSDT
94 @param Signature ACPI table signature
95
96 @return ACPI table
97
98 **/
99 VOID *
100 ScanTableInRSDT (
101 IN EFI_ACPI_DESCRIPTION_HEADER *Rsdt,
102 IN UINT32 Signature
103 )
104 {
105 UINTN Index;
106 UINT32 EntryCount;
107 UINT32 *EntryPtr;
108 EFI_ACPI_DESCRIPTION_HEADER *Table;
109
110 if (Rsdt == NULL) {
111 return NULL;
112 }
113
114 EntryCount = (Rsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / sizeof(UINT32);
115
116 EntryPtr = (UINT32 *)(Rsdt + 1);
117 for (Index = 0; Index < EntryCount; Index ++, EntryPtr ++) {
118 Table = (EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)(*EntryPtr));
119 if (Table->Signature == Signature) {
120 return Table;
121 }
122 }
123
124 return NULL;
125 }
126
127 /**
128
129 This function scan ACPI table in XSDT.
130
131 @param Xsdt ACPI XSDT
132 @param Signature ACPI table signature
133
134 @return ACPI table
135
136 **/
137 VOID *
138 ScanTableInXSDT (
139 IN EFI_ACPI_DESCRIPTION_HEADER *Xsdt,
140 IN UINT32 Signature
141 )
142 {
143 UINTN Index;
144 UINT32 EntryCount;
145 UINT64 EntryPtr;
146 UINTN BasePtr;
147 EFI_ACPI_DESCRIPTION_HEADER *Table;
148
149 if (Xsdt == NULL) {
150 return NULL;
151 }
152
153 EntryCount = (Xsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / sizeof(UINT64);
154
155 BasePtr = (UINTN)(Xsdt + 1);
156 for (Index = 0; Index < EntryCount; Index ++) {
157 CopyMem (&EntryPtr, (VOID *)(BasePtr + Index * sizeof(UINT64)), sizeof(UINT64));
158 Table = (EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)(EntryPtr));
159 if (Table->Signature == Signature) {
160 return Table;
161 }
162 }
163
164 return NULL;
165 }
166
167 /**
168 To find Facs in FADT.
169
170 @param Fadt FADT table pointer
171
172 @return Facs table pointer.
173 **/
174 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *
175 FindAcpiFacsFromFadt (
176 IN EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt
177 )
178 {
179 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
180 UINT64 Data64;
181
182 if (Fadt == NULL) {
183 return NULL;
184 }
185
186 if (Fadt->Header.Revision < EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION) {
187 Facs = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)Fadt->FirmwareCtrl;
188 } else {
189 if (Fadt->FirmwareCtrl != 0) {
190 Facs = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)Fadt->FirmwareCtrl;
191 } else {
192 CopyMem (&Data64, &Fadt->XFirmwareCtrl, sizeof(UINT64));
193 Facs = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)Data64;
194 }
195 }
196 return Facs;
197 }
198
199 /**
200 To find Facs in Acpi tables.
201
202 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
203 in the table.
204
205 @param AcpiTableGuid The guid used to find ACPI table in UEFI ConfigurationTable.
206
207 @return Facs table pointer.
208 **/
209 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *
210 FindAcpiFacsTableByAcpiGuid (
211 IN EFI_GUID *AcpiTableGuid
212 )
213 {
214 EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp;
215 EFI_ACPI_DESCRIPTION_HEADER *Rsdt;
216 EFI_ACPI_DESCRIPTION_HEADER *Xsdt;
217 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;
218 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
219 UINTN Index;
220
221 Rsdp = NULL;
222 //
223 // found ACPI table RSD_PTR from system table
224 //
225 for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
226 if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), AcpiTableGuid)) {
227 //
228 // A match was found.
229 //
230 Rsdp = gST->ConfigurationTable[Index].VendorTable;
231 break;
232 }
233 }
234
235 if (Rsdp == NULL) {
236 return NULL;
237 }
238
239 //
240 // Search XSDT
241 //
242 if (Rsdp->Revision >= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION) {
243 Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN) Rsdp->XsdtAddress;
244 Fadt = ScanTableInXSDT (Xsdt, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE);
245 if (Fadt != NULL) {
246 Facs = FindAcpiFacsFromFadt (Fadt);
247 if (Facs != NULL) {
248 return Facs;
249 }
250 }
251 }
252
253 //
254 // Search RSDT
255 //
256 Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN) Rsdp->RsdtAddress;
257 Fadt = ScanTableInRSDT (Rsdt, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE);
258 if (Fadt != NULL) {
259 Facs = FindAcpiFacsFromFadt (Fadt);
260 if (Facs != NULL) {
261 return Facs;
262 }
263 }
264
265 return NULL;
266 }
267
268 /**
269 To find Facs in Acpi tables.
270
271 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
272 in the table.
273
274 @return Facs table pointer.
275 **/
276 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *
277 FindAcpiFacsTable (
278 VOID
279 )
280 {
281 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
282
283 Facs = FindAcpiFacsTableByAcpiGuid (&gEfiAcpi20TableGuid);
284 if (Facs != NULL) {
285 return Facs;
286 }
287
288 return FindAcpiFacsTableByAcpiGuid (&gEfiAcpi10TableGuid);
289 }
290
291 /**
292 Allocates and fills in the Page Directory and Page Table Entries to
293 establish a 1:1 Virtual to Physical mapping.
294 If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1
295 virtual to physical mapping page table.
296 If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.
297
298 @return the 1:1 Virtual to Physical identity mapping page table base address.
299
300 **/
301 EFI_PHYSICAL_ADDRESS
302 S3CreateIdentityMappingPageTables (
303 VOID
304 )
305 {
306 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
307 UINT32 RegEax;
308 UINT32 RegEdx;
309 UINT8 PhysicalAddressBits;
310 UINT32 NumberOfPml4EntriesNeeded;
311 UINT32 NumberOfPdpEntriesNeeded;
312 EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress;
313 UINTN TotalPageTableSize;
314 VOID *Hob;
315 BOOLEAN Page1GSupport;
316
317 Page1GSupport = FALSE;
318 if (PcdGetBool(PcdUse1GPageTable)) {
319 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
320 if (RegEax >= 0x80000001) {
321 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
322 if ((RegEdx & BIT26) != 0) {
323 Page1GSupport = TRUE;
324 }
325 }
326 }
327
328 //
329 // Get physical address bits supported.
330 //
331 Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
332 if (Hob != NULL) {
333 PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
334 } else {
335 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
336 if (RegEax >= 0x80000008) {
337 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
338 PhysicalAddressBits = (UINT8) RegEax;
339 } else {
340 PhysicalAddressBits = 36;
341 }
342 }
343
344 //
345 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
346 //
347 ASSERT (PhysicalAddressBits <= 52);
348 if (PhysicalAddressBits > 48) {
349 PhysicalAddressBits = 48;
350 }
351
352 //
353 // Calculate the table entries needed.
354 //
355 if (PhysicalAddressBits <= 39 ) {
356 NumberOfPml4EntriesNeeded = 1;
357 NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
358 } else {
359 NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
360 NumberOfPdpEntriesNeeded = 512;
361 }
362
363 //
364 // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs.
365 //
366 if (!Page1GSupport) {
367 TotalPageTableSize = (UINTN)(1 + NumberOfPml4EntriesNeeded + NumberOfPml4EntriesNeeded * NumberOfPdpEntriesNeeded);
368 } else {
369 TotalPageTableSize = (UINTN)(1 + NumberOfPml4EntriesNeeded);
370 }
371 DEBUG ((EFI_D_ERROR, "TotalPageTableSize - %x pages\n", TotalPageTableSize));
372
373 //
374 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
375 //
376 S3NvsPageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGES_TO_SIZE(TotalPageTableSize));
377 ASSERT (S3NvsPageTableAddress != 0);
378 return S3NvsPageTableAddress;
379 } else {
380 //
381 // If DXE is running 32-bit mode, no need to establish page table.
382 //
383 return (EFI_PHYSICAL_ADDRESS) 0;
384 }
385 }
386
387 /**
388 Gets the buffer of legacy memory below 1 MB
389 This function is to get the buffer in legacy memory below 1MB that is required during S3 resume.
390
391 @param This A pointer to the EFI_ACPI_S3_SAVE_PROTOCOL instance.
392 @param Size The returned size of legacy memory below 1 MB.
393
394 @retval EFI_SUCCESS Size is successfully returned.
395 @retval EFI_INVALID_PARAMETER The pointer Size is NULL.
396
397 **/
398 EFI_STATUS
399 EFIAPI
400 LegacyGetS3MemorySize (
401 IN EFI_ACPI_S3_SAVE_PROTOCOL *This,
402 OUT UINTN *Size
403 )
404 {
405 ASSERT (FALSE);
406
407 if (Size == NULL) {
408 return EFI_INVALID_PARAMETER;
409 }
410
411 *Size = mLegacyRegionSize;
412 return EFI_SUCCESS;
413 }
414
415 /**
416 Prepares all information that is needed in the S3 resume boot path.
417
418 Allocate the resources or prepare informations and save in ACPI variable set for S3 resume boot path
419
420 @param This A pointer to the EFI_ACPI_S3_SAVE_PROTOCOL instance.
421 @param LegacyMemoryAddress The base address of legacy memory.
422
423 @retval EFI_NOT_FOUND Some necessary information cannot be found.
424 @retval EFI_SUCCESS All information was saved successfully.
425 @retval EFI_OUT_OF_RESOURCES Resources were insufficient to save all the information.
426 @retval EFI_INVALID_PARAMETER The memory range is not located below 1 MB.
427
428 **/
429 EFI_STATUS
430 EFIAPI
431 S3Ready (
432 IN EFI_ACPI_S3_SAVE_PROTOCOL *This,
433 IN VOID *LegacyMemoryAddress
434 )
435 {
436 EFI_STATUS Status;
437 EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer;
438 ACPI_S3_CONTEXT *AcpiS3Context;
439 STATIC BOOLEAN AlreadyEntered;
440 IA32_DESCRIPTOR *Idtr;
441 IA32_IDT_GATE_DESCRIPTOR *IdtGate;
442
443 DEBUG ((EFI_D_INFO, "S3Ready!\n"));
444
445 //
446 // Platform may invoke AcpiS3Save->S3Save() before ExitPmAuth, because we need save S3 information there, while BDS ReadyToBoot may invoke it again.
447 // So if 2nd S3Save() is triggered later, we need ignore it.
448 //
449 if (AlreadyEntered) {
450 return EFI_SUCCESS;
451 }
452 AlreadyEntered = TRUE;
453
454 ASSERT (LegacyMemoryAddress == NULL);
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 AcpiS3Context->AcpiFacsTable = (EFI_PHYSICAL_ADDRESS)(UINTN)FindAcpiFacsTable ();
464 ASSERT (AcpiS3Context->AcpiFacsTable != 0);
465
466 IdtGate = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 + sizeof(IA32_DESCRIPTOR));
467 Idtr = (IA32_DESCRIPTOR *)(IdtGate + 0x100);
468 Idtr->Base = (UINTN)IdtGate;
469 Idtr->Limit = (UINT16)(sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 - 1);
470 AcpiS3Context->IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)Idtr;
471
472 Status = SaveLockBox (
473 &mAcpiS3IdtrProfileGuid,
474 (VOID *)(UINTN)Idtr,
475 (UINTN)sizeof(IA32_DESCRIPTOR)
476 );
477 ASSERT_EFI_ERROR (Status);
478
479 Status = SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
480 ASSERT_EFI_ERROR (Status);
481
482 //
483 // Allocate page table
484 //
485 AcpiS3Context->S3NvsPageTableAddress = S3CreateIdentityMappingPageTables ();
486
487 //
488 // Allocate stack
489 //
490 AcpiS3Context->BootScriptStackSize = PcdGet32 (PcdS3BootScriptStackSize);
491 AcpiS3Context->BootScriptStackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, PcdGet32 (PcdS3BootScriptStackSize));
492 ASSERT (AcpiS3Context->BootScriptStackBase != 0);
493
494 //
495 // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it.
496 //
497 AcpiS3Context->S3DebugBufferAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGE_SIZE);
498 SetMem ((VOID *)(UINTN)AcpiS3Context->S3DebugBufferAddress, EFI_PAGE_SIZE, 0xff);
499
500 DEBUG((EFI_D_INFO, "AcpiS3Context: AcpiFacsTable is 0x%8x\n", AcpiS3Context->AcpiFacsTable));
501 DEBUG((EFI_D_INFO, "AcpiS3Context: IdtrProfile is 0x%8x\n", AcpiS3Context->IdtrProfile));
502 DEBUG((EFI_D_INFO, "AcpiS3Context: S3NvsPageTableAddress is 0x%8x\n", AcpiS3Context->S3NvsPageTableAddress));
503 DEBUG((EFI_D_INFO, "AcpiS3Context: S3DebugBufferAddress is 0x%8x\n", AcpiS3Context->S3DebugBufferAddress));
504
505 Status = SaveLockBox (
506 &gEfiAcpiVariableGuid,
507 &AcpiS3ContextBuffer,
508 sizeof(AcpiS3ContextBuffer)
509 );
510 ASSERT_EFI_ERROR (Status);
511
512 Status = SaveLockBox (
513 &gEfiAcpiS3ContextGuid,
514 (VOID *)(UINTN)AcpiS3Context,
515 (UINTN)sizeof(*AcpiS3Context)
516 );
517 ASSERT_EFI_ERROR (Status);
518
519 Status = SetLockBoxAttributes (&gEfiAcpiS3ContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
520 ASSERT_EFI_ERROR (Status);
521
522 return EFI_SUCCESS;
523 }
524
525 /**
526 Callback function executed when the EndOfDxe event group is signaled.
527
528 @param[in] Event Event whose notification function is being invoked.
529 @param[in] Context The pointer to the notification function's context, which
530 is implementation-dependent.
531 **/
532 VOID
533 EFIAPI
534 OnEndOfDxe (
535 IN EFI_EVENT Event,
536 IN VOID *Context
537 )
538 {
539 EFI_STATUS Status;
540
541 //
542 // Our S3Ready() function ignores both of its parameters, and always
543 // succeeds.
544 //
545 Status = S3Ready (
546 NULL, // This
547 NULL // LegacyMemoryAddress
548 );
549 ASSERT_EFI_ERROR (Status);
550
551 //
552 // Close the event, deregistering the callback and freeing resources.
553 //
554 Status = gBS->CloseEvent (Event);
555 ASSERT_EFI_ERROR (Status);
556 }
557
558
559 /**
560 The Driver Entry Point.
561
562 The function is the driver Entry point which will produce AcpiS3SaveProtocol.
563
564 @param ImageHandle A handle for the image that is initializing this driver
565 @param SystemTable A pointer to the EFI system table
566
567 @retval EFI_SUCCESS: Driver initialized successfully
568 @retval EFI_LOAD_ERROR: Failed to Initialize or has been loaded
569 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
570
571 **/
572 EFI_STATUS
573 EFIAPI
574 InstallAcpiS3Save (
575 IN EFI_HANDLE ImageHandle,
576 IN EFI_SYSTEM_TABLE *SystemTable
577 )
578 {
579 EFI_STATUS Status;
580 EFI_EVENT EndOfDxeEvent;
581
582 if (!QemuFwCfgS3Enabled()) {
583 return EFI_LOAD_ERROR;
584 }
585
586 if (!FeaturePcdGet(PcdPlatformCsmSupport)) {
587 //
588 // More memory for no CSM tip, because GDT need relocation
589 //
590 mLegacyRegionSize = 0x250;
591 } else {
592 mLegacyRegionSize = 0x100;
593 }
594
595 Status = gBS->InstallMultipleProtocolInterfaces (
596 &ImageHandle,
597 &gEfiAcpiS3SaveProtocolGuid, &mS3Save,
598 &gEfiLockBoxProtocolGuid, NULL,
599 NULL
600 );
601 ASSERT_EFI_ERROR (Status);
602
603 Status = gBS->CreateEventEx (
604 EVT_NOTIFY_SIGNAL,
605 TPL_CALLBACK,
606 OnEndOfDxe,
607 NULL, /* NotifyContext */
608 &gEfiEndOfDxeEventGroupGuid,
609 &EndOfDxeEvent
610 );
611 ASSERT_EFI_ERROR (Status);
612 return Status;
613 }