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