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