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