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