]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Acpi/S3SaveStateDxe/AcpiS3ContextSave.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Universal / Acpi / S3SaveStateDxe / AcpiS3ContextSave.c
CommitLineData
bd890a73
SZ
1/** @file\r
2 This is the implementation to save ACPI S3 Context.\r
3\r
d1102dba 4Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
bd890a73 5\r
9d510e61 6SPDX-License-Identifier: BSD-2-Clause-Patent\r
bd890a73
SZ
7\r
8**/\r
9\r
10#include <PiDxe.h>\r
11#include <Library/BaseLib.h>\r
12#include <Library/BaseMemoryLib.h>\r
13#include <Library/UefiBootServicesTableLib.h>\r
14#include <Library/HobLib.h>\r
15#include <Library/LockBoxLib.h>\r
16#include <Library/PcdLib.h>\r
17#include <Library/DebugLib.h>\r
2ad0581b 18#include <Library/UefiLib.h>\r
bd890a73 19#include <Guid/AcpiS3Context.h>\r
bd890a73
SZ
20#include <IndustryStandard/Acpi.h>\r
21#include <Protocol/LockBox.h>\r
22\r
23//\r
24// 8 extra pages for PF handler.\r
25//\r
26#define EXTRA_PAGE_TABLE_PAGES 8\r
27\r
28EFI_GUID mAcpiS3IdtrProfileGuid = {\r
29 0xdea652b0, 0xd587, 0x4c54, { 0xb5, 0xb4, 0xc6, 0x82, 0xe7, 0xa0, 0xaa, 0x3d }\r
30};\r
31\r
32/**\r
33 Allocate memory below 4G memory address.\r
34\r
35 This function allocates memory below 4G memory address.\r
36\r
37 @param MemoryType Memory type of memory to allocate.\r
38 @param Size Size of memory to allocate.\r
d1102dba 39\r
bd890a73
SZ
40 @return Allocated address for output.\r
41\r
42**/\r
43VOID*\r
44AllocateMemoryBelow4G (\r
45 IN EFI_MEMORY_TYPE MemoryType,\r
46 IN UINTN Size\r
47 )\r
48{\r
49 UINTN Pages;\r
50 EFI_PHYSICAL_ADDRESS Address;\r
51 EFI_STATUS Status;\r
52 VOID* Buffer;\r
53\r
54 Pages = EFI_SIZE_TO_PAGES (Size);\r
55 Address = 0xffffffff;\r
56\r
57 Status = gBS->AllocatePages (\r
58 AllocateMaxAddress,\r
59 MemoryType,\r
60 Pages,\r
61 &Address\r
62 );\r
63 ASSERT_EFI_ERROR (Status);\r
64\r
65 Buffer = (VOID *) (UINTN) Address;\r
66 ZeroMem (Buffer, Size);\r
67\r
68 return Buffer;\r
69}\r
70\r
bd890a73
SZ
71/**\r
72 The function will check if long mode waking vector is supported.\r
73\r
74 @param[in] Facs Pointer to FACS table.\r
75\r
76 @retval TRUE Long mode waking vector is supported.\r
77 @retval FALSE Long mode waking vector is not supported.\r
78\r
79**/\r
80BOOLEAN\r
81IsLongModeWakingVectorSupport (\r
82 IN EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs\r
83 )\r
84{\r
85 if ((Facs == NULL) ||\r
86 (Facs->Signature != EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) ) {\r
87 //\r
88 // Something wrong with FACS.\r
89 //\r
90 return FALSE;\r
91 }\r
92 if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) &&\r
93 ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0)) {\r
94 //\r
95 // BIOS supports 64bit waking vector.\r
96 //\r
97 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
98 return TRUE;\r
99 }\r
100 }\r
101 return FALSE;\r
102}\r
103\r
104/**\r
105 Allocates page table buffer.\r
106\r
107 @param[in] LongModeWakingVectorSupport Support long mode waking vector or not.\r
108\r
d1102dba 109 If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1\r
bd890a73
SZ
110 virtual to physical mapping page table when long mode waking vector is supported, otherwise\r
111 create 4G page table when long mode waking vector is not supported and let PF handler to\r
112 handle > 4G request.\r
d1102dba
LG
113 If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.\r
114\r
115 @return Page table base address.\r
bd890a73
SZ
116\r
117**/\r
118EFI_PHYSICAL_ADDRESS\r
119S3AllocatePageTablesBuffer (\r
120 IN BOOLEAN LongModeWakingVectorSupport\r
121 )\r
d1102dba 122{\r
bd890a73
SZ
123 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
124 UINTN ExtraPageTablePages;\r
125 UINT32 RegEax;\r
126 UINT32 RegEdx;\r
127 UINT8 PhysicalAddressBits;\r
128 UINT32 NumberOfPml4EntriesNeeded;\r
129 UINT32 NumberOfPdpEntriesNeeded;\r
130 EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress;\r
131 UINTN TotalPageTableSize;\r
132 VOID *Hob;\r
133 BOOLEAN Page1GSupport;\r
134\r
135 Page1GSupport = FALSE;\r
136 if (PcdGetBool(PcdUse1GPageTable)) {\r
137 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
138 if (RegEax >= 0x80000001) {\r
139 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);\r
140 if ((RegEdx & BIT26) != 0) {\r
141 Page1GSupport = TRUE;\r
142 }\r
143 }\r
144 }\r
145\r
146 //\r
147 // Get physical address bits supported.\r
148 //\r
149 Hob = GetFirstHob (EFI_HOB_TYPE_CPU);\r
150 if (Hob != NULL) {\r
151 PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;\r
152 } else {\r
153 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
154 if (RegEax >= 0x80000008) {\r
155 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);\r
156 PhysicalAddressBits = (UINT8) RegEax;\r
157 } else {\r
158 PhysicalAddressBits = 36;\r
159 }\r
160 }\r
161\r
162 //\r
163 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.\r
164 //\r
165 ASSERT (PhysicalAddressBits <= 52);\r
166 if (PhysicalAddressBits > 48) {\r
167 PhysicalAddressBits = 48;\r
168 }\r
169\r
170 ExtraPageTablePages = 0;\r
171 if (!LongModeWakingVectorSupport) {\r
172 //\r
173 // Create 4G page table when BIOS does not support long mode waking vector,\r
174 // and let PF handler to handle > 4G request.\r
175 //\r
176 PhysicalAddressBits = 32;\r
177 ExtraPageTablePages = EXTRA_PAGE_TABLE_PAGES;\r
178 }\r
179\r
180 //\r
181 // Calculate the table entries needed.\r
182 //\r
183 if (PhysicalAddressBits <= 39 ) {\r
184 NumberOfPml4EntriesNeeded = 1;\r
185 NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));\r
186 } else {\r
187 NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));\r
188 NumberOfPdpEntriesNeeded = 512;\r
189 }\r
190\r
191 //\r
192 // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs.\r
193 //\r
194 if (!Page1GSupport) {\r
16f69227 195 TotalPageTableSize = 1 + NumberOfPml4EntriesNeeded + NumberOfPml4EntriesNeeded * NumberOfPdpEntriesNeeded;\r
bd890a73 196 } else {\r
16f69227 197 TotalPageTableSize = 1 + NumberOfPml4EntriesNeeded;\r
bd890a73
SZ
198 }\r
199\r
200 TotalPageTableSize += ExtraPageTablePages;\r
558f58e3 201 DEBUG ((DEBUG_INFO, "AcpiS3ContextSave TotalPageTableSize - 0x%x pages\n", TotalPageTableSize));\r
bd890a73
SZ
202\r
203 //\r
204 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.\r
205 //\r
206 S3NvsPageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGES_TO_SIZE(TotalPageTableSize));\r
207 ASSERT (S3NvsPageTableAddress != 0);\r
208 return S3NvsPageTableAddress;\r
209 } else {\r
210 //\r
211 // If DXE is running 32-bit mode, no need to establish page table.\r
212 //\r
d1102dba 213 return (EFI_PHYSICAL_ADDRESS) 0;\r
bd890a73
SZ
214 }\r
215}\r
216\r
217/**\r
218 Callback function executed when the EndOfDxe event group is signaled.\r
219\r
220 @param[in] Event Event whose notification function is being invoked.\r
221 @param[in] Context The pointer to the notification function's context, which\r
222 is implementation-dependent.\r
223**/\r
224VOID\r
225EFIAPI\r
226AcpiS3ContextSaveOnEndOfDxe (\r
227 IN EFI_EVENT Event,\r
228 IN VOID *Context\r
229 )\r
230{\r
231 EFI_STATUS Status;\r
232 EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer;\r
233 ACPI_S3_CONTEXT *AcpiS3Context;\r
234 IA32_DESCRIPTOR *Idtr;\r
235 IA32_IDT_GATE_DESCRIPTOR *IdtGate;\r
236 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;\r
237 VOID *Interface;\r
238\r
239 DEBUG ((EFI_D_INFO, "AcpiS3ContextSave!\n"));\r
240\r
241 Status = gBS->LocateProtocol (&gEfiLockBoxProtocolGuid, NULL, &Interface);\r
242 if (EFI_ERROR (Status)) {\r
243 DEBUG ((EFI_D_INFO | EFI_D_WARN, "ACPI S3 context can't be saved without LockBox!\n"));\r
244 goto Done;\r
245 }\r
246\r
247 AcpiS3Context = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(*AcpiS3Context));\r
248 ASSERT (AcpiS3Context != NULL);\r
249 AcpiS3ContextBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)AcpiS3Context;\r
250\r
251 //\r
252 // Get ACPI Table because we will save its position to variable\r
253 //\r
2ad0581b
SZ
254 Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) EfiLocateFirstAcpiTable (\r
255 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE\r
256 );\r
bd890a73
SZ
257 AcpiS3Context->AcpiFacsTable = (EFI_PHYSICAL_ADDRESS) (UINTN) Facs;\r
258 ASSERT (AcpiS3Context->AcpiFacsTable != 0);\r
259\r
260 IdtGate = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 + sizeof(IA32_DESCRIPTOR));\r
261 Idtr = (IA32_DESCRIPTOR *)(IdtGate + 0x100);\r
262 Idtr->Base = (UINTN)IdtGate;\r
263 Idtr->Limit = (UINT16)(sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 - 1);\r
264 AcpiS3Context->IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)Idtr;\r
265\r
266 Status = SaveLockBox (\r
267 &mAcpiS3IdtrProfileGuid,\r
268 (VOID *)(UINTN)Idtr,\r
269 (UINTN)sizeof(IA32_DESCRIPTOR)\r
270 );\r
271 ASSERT_EFI_ERROR (Status);\r
272\r
273 Status = SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);\r
274 ASSERT_EFI_ERROR (Status);\r
275\r
276 //\r
277 // Allocate page table\r
278 //\r
279 AcpiS3Context->S3NvsPageTableAddress = S3AllocatePageTablesBuffer (IsLongModeWakingVectorSupport (Facs));\r
280\r
281 //\r
282 // Allocate stack\r
283 //\r
284 AcpiS3Context->BootScriptStackSize = PcdGet32 (PcdS3BootScriptStackSize);\r
285 AcpiS3Context->BootScriptStackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, PcdGet32 (PcdS3BootScriptStackSize));\r
286 ASSERT (AcpiS3Context->BootScriptStackBase != 0);\r
287\r
288 //\r
289 // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it.\r
290 //\r
291 AcpiS3Context->S3DebugBufferAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGE_SIZE);\r
292 SetMem ((VOID *)(UINTN)AcpiS3Context->S3DebugBufferAddress, EFI_PAGE_SIZE, 0xff);\r
293\r
294 DEBUG((EFI_D_INFO, "AcpiS3Context: AcpiFacsTable is 0x%8x\n", AcpiS3Context->AcpiFacsTable));\r
295 DEBUG((EFI_D_INFO, "AcpiS3Context: IdtrProfile is 0x%8x\n", AcpiS3Context->IdtrProfile));\r
296 DEBUG((EFI_D_INFO, "AcpiS3Context: S3NvsPageTableAddress is 0x%8x\n", AcpiS3Context->S3NvsPageTableAddress));\r
297 DEBUG((EFI_D_INFO, "AcpiS3Context: S3DebugBufferAddress is 0x%8x\n", AcpiS3Context->S3DebugBufferAddress));\r
298 DEBUG((EFI_D_INFO, "AcpiS3Context: BootScriptStackBase is 0x%8x\n", AcpiS3Context->BootScriptStackBase));\r
299 DEBUG((EFI_D_INFO, "AcpiS3Context: BootScriptStackSize is 0x%8x\n", AcpiS3Context->BootScriptStackSize));\r
300\r
301 Status = SaveLockBox (\r
302 &gEfiAcpiVariableGuid,\r
303 &AcpiS3ContextBuffer,\r
304 sizeof(AcpiS3ContextBuffer)\r
305 );\r
306 ASSERT_EFI_ERROR (Status);\r
307\r
308 Status = SaveLockBox (\r
309 &gEfiAcpiS3ContextGuid,\r
310 (VOID *)(UINTN)AcpiS3Context,\r
311 (UINTN)sizeof(*AcpiS3Context)\r
312 );\r
313 ASSERT_EFI_ERROR (Status);\r
314\r
315 Status = SetLockBoxAttributes (&gEfiAcpiS3ContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);\r
316 ASSERT_EFI_ERROR (Status);\r
317\r
318Done:\r
319 //\r
320 // Close the event, deregistering the callback and freeing resources.\r
321 //\r
322 Status = gBS->CloseEvent (Event);\r
323 ASSERT_EFI_ERROR (Status);\r
324}\r
325\r