]> git.proxmox.com Git - mirror_edk2.git/blob - IntelFrameworkModulePkg/Universal/Acpi/AcpiS3SaveDxe/AcpiS3Save.c
IntelFrameworkModulePkg: Add AcpiS3SaveDxe driver
[mirror_edk2.git] / IntelFrameworkModulePkg / Universal / Acpi / 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) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
6
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions
9 of the BSD License which accompanies this distribution. The
10 full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
12
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15
16 **/
17
18 #include <PiDxe.h>
19 #include <Library/BaseLib.h>
20 #include <Library/BaseMemoryLib.h>
21 #include <Library/UefiBootServicesTableLib.h>
22 #include <Library/UefiRuntimeServicesTableLib.h>
23 #include <Library/LockBoxLib.h>
24 #include <Library/PcdLib.h>
25 #include <Library/DebugLib.h>
26 #include <Guid/AcpiVariableCompatibility.h>
27 #include <Guid/AcpiS3Context.h>
28 #include <Guid/Acpi.h>
29 #include <Protocol/AcpiS3Save.h>
30 #include <IndustryStandard/Acpi.h>
31
32 #include "AcpiS3Save.h"
33
34 /**
35 Hook point for AcpiVariableThunkPlatform for InstallAcpiS3Save.
36 **/
37 VOID
38 InstallAcpiS3SaveThunk (
39 VOID
40 );
41
42 /**
43 Hook point for AcpiVariableThunkPlatform for S3Ready.
44
45 @param AcpiS3Context ACPI s3 context
46 **/
47 VOID
48 S3ReadyThunkPlatform (
49 IN ACPI_S3_CONTEXT *AcpiS3Context
50 );
51
52 UINTN mLegacyRegionSize;
53
54 EFI_ACPI_S3_SAVE_PROTOCOL mS3Save = {
55 LegacyGetS3MemorySize,
56 S3Ready,
57 };
58
59 EFI_GUID mAcpiS3IdtrProfileGuid = {
60 0xdea652b0, 0xd587, 0x4c54, 0xb5, 0xb4, 0xc6, 0x82, 0xe7, 0xa0, 0xaa, 0x3d
61 };
62
63 /**
64 Allocate EfiACPIMemoryNVS below 4G memory address.
65
66 This function allocates EfiACPIMemoryNVS below 4G memory address.
67
68 @param Size Size of memory to allocate.
69
70 @return Allocated address for output.
71
72 **/
73 VOID*
74 AllocateAcpiNvsMemoryBelow4G (
75 IN UINTN Size
76 )
77 {
78 UINTN Pages;
79 EFI_PHYSICAL_ADDRESS Address;
80 EFI_STATUS Status;
81 VOID* Buffer;
82
83 Pages = EFI_SIZE_TO_PAGES (Size);
84 Address = 0xffffffff;
85
86 Status = gBS->AllocatePages (
87 AllocateMaxAddress,
88 EfiACPIMemoryNVS,
89 Pages,
90 &Address
91 );
92 ASSERT_EFI_ERROR (Status);
93
94 Buffer = (VOID *) (UINTN) Address;
95 ZeroMem (Buffer, Size);
96
97 return Buffer;
98 }
99
100 /**
101 To find Facs in Acpi tables.
102
103 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
104 in the table.
105
106 @param AcpiTableGuid The guid used to find ACPI table in UEFI ConfigurationTable.
107
108 @return Facs table pointer.
109 **/
110 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *
111 FindAcpiFacsTableByAcpiGuid (
112 IN EFI_GUID *AcpiTableGuid
113 )
114 {
115 EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp;
116 EFI_ACPI_DESCRIPTION_HEADER *Rsdt;
117 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;
118 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
119 UINTN Index;
120 UINT32 Data32;
121 Rsdp = NULL;
122 Rsdt = NULL;
123 Fadt = NULL;
124 //
125 // found ACPI table RSD_PTR from system table
126 //
127 for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
128 if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), AcpiTableGuid)) {
129 //
130 // A match was found.
131 //
132 Rsdp = gST->ConfigurationTable[Index].VendorTable;
133 break;
134 }
135 }
136
137 if (Rsdp == NULL) {
138 return NULL;
139 }
140
141 Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN) Rsdp->RsdtAddress;
142 if (Rsdt == NULL || Rsdt->Signature != EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {
143 return NULL;
144 }
145
146 for (Index = sizeof (EFI_ACPI_DESCRIPTION_HEADER); Index < Rsdt->Length; Index = Index + sizeof (UINT32)) {
147
148 Data32 = *(UINT32 *) ((UINT8 *) Rsdt + Index);
149 Fadt = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *) (UINT32 *) (UINTN) Data32;
150 if (Fadt->Header.Signature == EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
151 break;
152 }
153 }
154
155 if (Fadt == NULL || Fadt->Header.Signature != EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
156 return NULL;
157 }
158
159 Facs = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)Fadt->FirmwareCtrl;
160
161 return Facs;
162 }
163
164 /**
165 To find Facs in Acpi tables.
166
167 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
168 in the table.
169
170 @return Facs table pointer.
171 **/
172 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *
173 FindAcpiFacsTable (
174 VOID
175 )
176 {
177 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
178
179 Facs = FindAcpiFacsTableByAcpiGuid (&gEfiAcpi20TableGuid);
180 if (Facs != NULL) {
181 return Facs;
182 }
183
184 return FindAcpiFacsTableByAcpiGuid (&gEfiAcpi10TableGuid);
185 }
186
187 /**
188 Allocates and fills in the Page Directory and Page Table Entries to
189 establish a 1:1 Virtual to Physical mapping.
190 If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1
191 virtual to physical mapping page table.
192 If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.
193
194 @return the 1:1 Virtual to Physical identity mapping page table base address.
195
196 **/
197 EFI_PHYSICAL_ADDRESS
198 S3CreateIdentityMappingPageTables (
199 VOID
200 )
201 {
202 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
203 UINT32 RegEax;
204 UINT8 PhysicalAddressBits;
205 EFI_PHYSICAL_ADDRESS PageAddress;
206 UINTN IndexOfPml4Entries;
207 UINTN IndexOfPdpEntries;
208 UINTN IndexOfPageDirectoryEntries;
209 UINTN NumberOfPml4EntriesNeeded;
210 UINTN NumberOfPdpEntriesNeeded;
211 PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;
212 PAGE_MAP_AND_DIRECTORY_POINTER *PageMap;
213 PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry;
214 PAGE_TABLE_ENTRY *PageDirectoryEntry;
215 EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress;
216 UINTN TotalPageTableSize;
217
218 //
219 // Get physical address bits supported.
220 //
221 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
222 if (RegEax >= 0x80000008) {
223 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
224 PhysicalAddressBits = (UINT8) RegEax;
225 } else {
226 PhysicalAddressBits = 36;
227 }
228
229 //
230 // Calculate the table entries needed.
231 //
232 if (PhysicalAddressBits <= 39 ) {
233 NumberOfPml4EntriesNeeded = 1;
234 NumberOfPdpEntriesNeeded = (UINTN)LShiftU64 (1, (PhysicalAddressBits - 30));
235 } else {
236 NumberOfPml4EntriesNeeded = (UINTN)LShiftU64 (1, (PhysicalAddressBits - 39));
237 NumberOfPdpEntriesNeeded = 512;
238 }
239
240 //
241 // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs.
242 //
243 TotalPageTableSize = (UINTN)(1 + NumberOfPml4EntriesNeeded + NumberOfPml4EntriesNeeded * NumberOfPdpEntriesNeeded);
244 DEBUG ((EFI_D_ERROR, "TotalPageTableSize - %x pages\n", TotalPageTableSize));
245
246 //
247 // By architecture only one PageMapLevel4 exists - so lets allocate storgage for it.
248 //
249 S3NvsPageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateAcpiNvsMemoryBelow4G (EFI_PAGES_TO_SIZE(TotalPageTableSize));
250 ASSERT (S3NvsPageTableAddress != 0);
251 PageMap = (PAGE_MAP_AND_DIRECTORY_POINTER *)(UINTN)S3NvsPageTableAddress;
252 S3NvsPageTableAddress += SIZE_4KB;
253
254 PageMapLevel4Entry = PageMap;
255 PageAddress = 0;
256 for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) {
257 //
258 // Each PML4 entry points to a page of Page Directory Pointer entires.
259 // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
260 //
261 PageDirectoryPointerEntry = (PAGE_MAP_AND_DIRECTORY_POINTER *)(UINTN)S3NvsPageTableAddress;
262 S3NvsPageTableAddress += SIZE_4KB;
263 //
264 // Make a PML4 Entry
265 //
266 PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry;
267 PageMapLevel4Entry->Bits.ReadWrite = 1;
268 PageMapLevel4Entry->Bits.Present = 1;
269
270 for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
271 //
272 // Each Directory Pointer entries points to a page of Page Directory entires.
273 // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
274 //
275 PageDirectoryEntry = (PAGE_TABLE_ENTRY *)(UINTN)S3NvsPageTableAddress;
276 S3NvsPageTableAddress += SIZE_4KB;
277
278 //
279 // Fill in a Page Directory Pointer Entries
280 //
281 PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry;
282 PageDirectoryPointerEntry->Bits.ReadWrite = 1;
283 PageDirectoryPointerEntry->Bits.Present = 1;
284
285 for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += 0x200000) {
286 //
287 // Fill in the Page Directory entries
288 //
289 PageDirectoryEntry->Uint64 = (UINT64)PageAddress;
290 PageDirectoryEntry->Bits.ReadWrite = 1;
291 PageDirectoryEntry->Bits.Present = 1;
292 PageDirectoryEntry->Bits.MustBe1 = 1;
293 }
294 }
295 }
296 return (EFI_PHYSICAL_ADDRESS) (UINTN) PageMap;
297 } else {
298 //
299 // If DXE is running 32-bit mode, no need to establish page table.
300 //
301 return (EFI_PHYSICAL_ADDRESS) 0;
302 }
303 }
304
305 /**
306 Gets the buffer of legacy memory below 1 MB
307 This function is to get the buffer in legacy memory below 1MB that is required during S3 resume.
308
309 @param This A pointer to the EFI_ACPI_S3_SAVE_PROTOCOL instance.
310 @param Size The returned size of legacy memory below 1 MB.
311
312 @retval EFI_SUCCESS Size is successfully returned.
313 @retval EFI_INVALID_PARAMETER The pointer Size is NULL.
314
315 **/
316 EFI_STATUS
317 EFIAPI
318 LegacyGetS3MemorySize (
319 IN EFI_ACPI_S3_SAVE_PROTOCOL *This,
320 OUT UINTN *Size
321 )
322 {
323 if (Size == NULL) {
324 return EFI_INVALID_PARAMETER;
325 }
326
327 *Size = mLegacyRegionSize;
328 return EFI_SUCCESS;
329 }
330
331 /**
332 Prepares all information that is needed in the S3 resume boot path.
333
334 Allocate the resources or prepare informations and save in ACPI variable set for S3 resume boot path
335
336 @param This A pointer to the EFI_ACPI_S3_SAVE_PROTOCOL instance.
337 @param LegacyMemoryAddress The base address of legacy memory.
338
339 @retval EFI_NOT_FOUND Some necessary information cannot be found.
340 @retval EFI_SUCCESS All information was saved successfully.
341 @retval EFI_OUT_OF_RESOURCES Resources were insufficient to save all the information.
342 @retval EFI_INVALID_PARAMETER The memory range is not located below 1 MB.
343
344 **/
345 EFI_STATUS
346 EFIAPI
347 S3Ready (
348 IN EFI_ACPI_S3_SAVE_PROTOCOL *This,
349 IN VOID *LegacyMemoryAddress
350 )
351 {
352 EFI_STATUS Status;
353 EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer;
354 ACPI_S3_CONTEXT *AcpiS3Context;
355 STATIC BOOLEAN AlreadyEntered;
356 IA32_DESCRIPTOR *Idtr;
357 IA32_IDT_GATE_DESCRIPTOR *IdtGate;
358
359 DEBUG ((EFI_D_INFO, "S3Ready!\n"));
360
361 //
362 // Platform may invoke AcpiS3Save->S3Save() before ExitPmAuth, because we need save S3 information there, while BDS ReadyToBoot may invoke it again.
363 // So if 2nd S3Save() is triggered later, we need ignore it.
364 //
365 if (AlreadyEntered) {
366 return EFI_SUCCESS;
367 }
368 AlreadyEntered = TRUE;
369
370 AcpiS3Context = AllocateAcpiNvsMemoryBelow4G (sizeof(*AcpiS3Context));
371 ASSERT (AcpiS3Context != NULL);
372 AcpiS3ContextBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)AcpiS3Context;
373
374 //
375 // Get ACPI Table because we will save its position to variable
376 //
377 AcpiS3Context->AcpiFacsTable = (EFI_PHYSICAL_ADDRESS)(UINTN)FindAcpiFacsTable ();
378 ASSERT (AcpiS3Context->AcpiFacsTable != 0);
379
380 IdtGate = AllocateAcpiNvsMemoryBelow4G (sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 + sizeof(IA32_DESCRIPTOR));
381 Idtr = (IA32_DESCRIPTOR *)(IdtGate + 0x100);
382 Idtr->Base = (UINTN)IdtGate;
383 Idtr->Limit = (UINT16)(sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 - 1);
384 AcpiS3Context->IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)Idtr;
385
386 Status = SaveLockBox (
387 &mAcpiS3IdtrProfileGuid,
388 (VOID *)(UINTN)Idtr,
389 (UINTN)sizeof(IA32_DESCRIPTOR)
390 );
391 ASSERT_EFI_ERROR (Status);
392
393 Status = SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
394 ASSERT_EFI_ERROR (Status);
395
396 //
397 // Allocate page table
398 //
399 AcpiS3Context->S3NvsPageTableAddress = S3CreateIdentityMappingPageTables ();
400
401 //
402 // Allocate stack
403 //
404 AcpiS3Context->BootScriptStackSize = PcdGet32 (PcdS3BootScriptStackSize);
405 AcpiS3Context->BootScriptStackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateAcpiNvsMemoryBelow4G (PcdGet32 (PcdS3BootScriptStackSize));
406 ASSERT (AcpiS3Context->BootScriptStackBase != 0);
407
408 //
409 // Allocate a code buffer < 4G for S3 debug to load external code
410 //
411 AcpiS3Context->S3DebugBufferAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateAcpiNvsMemoryBelow4G (EFI_PAGE_SIZE);
412
413 DEBUG((EFI_D_INFO, "AcpiS3Context: AcpiFacsTable is 0x%8x\n", AcpiS3Context->AcpiFacsTable));
414 DEBUG((EFI_D_INFO, "AcpiS3Context: IdtrProfile is 0x%8x\n", AcpiS3Context->IdtrProfile));
415 DEBUG((EFI_D_INFO, "AcpiS3Context: S3NvsPageTableAddress is 0x%8x\n", AcpiS3Context->S3NvsPageTableAddress));
416 DEBUG((EFI_D_INFO, "AcpiS3Context: S3DebugBufferAddress is 0x%8x\n", AcpiS3Context->S3DebugBufferAddress));
417
418 Status = SaveLockBox (
419 &gEfiAcpiVariableGuid,
420 &AcpiS3ContextBuffer,
421 sizeof(AcpiS3ContextBuffer)
422 );
423 ASSERT_EFI_ERROR (Status);
424
425 Status = SaveLockBox (
426 &gEfiAcpiS3ContextGuid,
427 (VOID *)(UINTN)AcpiS3Context,
428 (UINTN)sizeof(*AcpiS3Context)
429 );
430 ASSERT_EFI_ERROR (Status);
431
432 Status = SetLockBoxAttributes (&gEfiAcpiS3ContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
433 ASSERT_EFI_ERROR (Status);
434
435 if (FeaturePcdGet(PcdFrameworkCompatibilitySupport)) {
436 S3ReadyThunkPlatform (AcpiS3Context);
437 }
438
439 return EFI_SUCCESS;
440 }
441
442 /**
443 The Driver Entry Point.
444
445 The function is the driver Entry point which will produce AcpiS3SaveProtocol.
446
447 @param ImageHandle A handle for the image that is initializing this driver
448 @param SystemTable A pointer to the EFI system table
449
450 @retval EFI_SUCCESS: Driver initialized successfully
451 @retval EFI_LOAD_ERROR: Failed to Initialize or has been loaded
452 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
453
454 **/
455 EFI_STATUS
456 EFIAPI
457 InstallAcpiS3Save (
458 IN EFI_HANDLE ImageHandle,
459 IN EFI_SYSTEM_TABLE *SystemTable
460 )
461 {
462 EFI_STATUS Status;
463
464 if (!FeaturePcdGet(PcdPlatformCsmSupport)) {
465 //
466 // More memory for no CSM tip, because GDT need relocation
467 //
468 mLegacyRegionSize = 0x250;
469 } else {
470 mLegacyRegionSize = 0x100;
471 }
472
473 if (FeaturePcdGet(PcdFrameworkCompatibilitySupport)) {
474 InstallAcpiS3SaveThunk ();
475 }
476
477 Status = gBS->InstallProtocolInterface (
478 &ImageHandle,
479 &gEfiAcpiS3SaveProtocolGuid,
480 EFI_NATIVE_INTERFACE,
481 &mS3Save
482 );
483 ASSERT_EFI_ERROR (Status);
484 return Status;
485 }