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