]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
ArmVirtPkg, OvmfPkg: retire QemuFwCfgS3Enabled() from QemuFwCfgLib
[mirror_edk2.git] / OvmfPkg / AcpiPlatformDxe / QemuFwCfgAcpi.c
CommitLineData
14b0faad
JJ
1/** @file\r
2 OVMF ACPI support using QEMU's fw-cfg interface\r
3\r
4 Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>\r
5 Copyright (C) 2012-2014, Red Hat, Inc.\r
6\r
ab63766b
LE
7 This program and the accompanying materials are licensed and made available\r
8 under the terms and conditions of the BSD License which accompanies this\r
9 distribution. The full text of the license may be found at\r
14b0faad
JJ
10 http://opensource.org/licenses/bsd-license.php\r
11\r
ab63766b
LE
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
13 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14b0faad
JJ
14\r
15**/\r
16\r
17#include "AcpiPlatform.h"\r
18#include "QemuLoader.h"\r
19#include <Library/BaseMemoryLib.h>\r
20#include <Library/MemoryAllocationLib.h>\r
21#include <Library/QemuFwCfgLib.h>\r
687f7521 22#include <Library/QemuFwCfgS3Lib.h>\r
14b0faad
JJ
23#include <Library/DxeServicesTableLib.h>\r
24#include <Library/PcdLib.h>\r
25#include <Library/OrderedCollectionLib.h>\r
26#include <IndustryStandard/Acpi.h>\r
27\r
28\r
29//\r
30// The user structure for the ordered collection that will track the fw_cfg\r
31// blobs under processing.\r
32//\r
33typedef struct {\r
34 UINT8 File[QEMU_LOADER_FNAME_SIZE]; // NUL-terminated name of the fw_cfg\r
35 // blob. This is the ordering / search\r
36 // key.\r
37 UINTN Size; // The number of bytes in this blob.\r
38 UINT8 *Base; // Pointer to the blob data.\r
39 BOOLEAN HostsOnlyTableData; // TRUE iff the blob has been found to\r
40 // only contain data that is directly\r
41 // part of ACPI tables.\r
42} BLOB;\r
43\r
44\r
45/**\r
46 Compare a standalone key against a user structure containing an embedded key.\r
47\r
48 @param[in] StandaloneKey Pointer to the bare key.\r
49\r
50 @param[in] UserStruct Pointer to the user structure with the embedded\r
51 key.\r
52\r
53 @retval <0 If StandaloneKey compares less than UserStruct's key.\r
54\r
55 @retval 0 If StandaloneKey compares equal to UserStruct's key.\r
56\r
57 @retval >0 If StandaloneKey compares greater than UserStruct's key.\r
58**/\r
59STATIC\r
60INTN\r
61EFIAPI\r
62BlobKeyCompare (\r
63 IN CONST VOID *StandaloneKey,\r
64 IN CONST VOID *UserStruct\r
65 )\r
66{\r
67 CONST BLOB *Blob;\r
68\r
69 Blob = UserStruct;\r
70 return AsciiStrCmp (StandaloneKey, (CONST CHAR8 *)Blob->File);\r
71}\r
72\r
73\r
74/**\r
75 Comparator function for two user structures.\r
76\r
77 @param[in] UserStruct1 Pointer to the first user structure.\r
78\r
79 @param[in] UserStruct2 Pointer to the second user structure.\r
80\r
81 @retval <0 If UserStruct1 compares less than UserStruct2.\r
82\r
83 @retval 0 If UserStruct1 compares equal to UserStruct2.\r
84\r
85 @retval >0 If UserStruct1 compares greater than UserStruct2.\r
86**/\r
87STATIC\r
88INTN\r
89EFIAPI\r
90BlobCompare (\r
91 IN CONST VOID *UserStruct1,\r
92 IN CONST VOID *UserStruct2\r
93 )\r
94{\r
95 CONST BLOB *Blob1;\r
96\r
97 Blob1 = UserStruct1;\r
98 return BlobKeyCompare (Blob1->File, UserStruct2);\r
99}\r
100\r
101\r
102/**\r
103 Process a QEMU_LOADER_ALLOCATE command.\r
104\r
105 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to process.\r
106\r
107 @param[in,out] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
108 structures created thus far.\r
109\r
110 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been\r
111 allocated for the blob contents, and the\r
112 contents have been saved. A BLOB object (user\r
113 structure) has been allocated from pool memory,\r
114 referencing the blob contents. The BLOB user\r
115 structure has been linked into Tracker.\r
116\r
117 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in\r
118 Allocate, or the Allocate command references a\r
119 file that is already known by Tracker.\r
120\r
121 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in\r
122 Allocate.\r
123\r
124 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.\r
125\r
126 @return Error codes from QemuFwCfgFindFile() and\r
127 gBS->AllocatePages().\r
128**/\r
129STATIC\r
130EFI_STATUS\r
131EFIAPI\r
132ProcessCmdAllocate (\r
133 IN CONST QEMU_LOADER_ALLOCATE *Allocate,\r
134 IN OUT ORDERED_COLLECTION *Tracker\r
135 )\r
136{\r
137 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
138 UINTN FwCfgSize;\r
139 EFI_STATUS Status;\r
140 UINTN NumPages;\r
141 EFI_PHYSICAL_ADDRESS Address;\r
142 BLOB *Blob;\r
143\r
144 if (Allocate->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
145 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
146 return EFI_PROTOCOL_ERROR;\r
147 }\r
148\r
149 if (Allocate->Alignment > EFI_PAGE_SIZE) {\r
150 DEBUG ((EFI_D_ERROR, "%a: unsupported alignment 0x%x\n", __FUNCTION__,\r
151 Allocate->Alignment));\r
152 return EFI_UNSUPPORTED;\r
153 }\r
154\r
155 Status = QemuFwCfgFindFile ((CHAR8 *)Allocate->File, &FwCfgItem, &FwCfgSize);\r
156 if (EFI_ERROR (Status)) {\r
157 DEBUG ((EFI_D_ERROR, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__,\r
158 Allocate->File, Status));\r
159 return Status;\r
160 }\r
161\r
162 NumPages = EFI_SIZE_TO_PAGES (FwCfgSize);\r
163 Address = 0xFFFFFFFF;\r
164 Status = gBS->AllocatePages (AllocateMaxAddress, EfiACPIMemoryNVS, NumPages,\r
165 &Address);\r
166 if (EFI_ERROR (Status)) {\r
167 return Status;\r
168 }\r
169\r
170 Blob = AllocatePool (sizeof *Blob);\r
171 if (Blob == NULL) {\r
172 Status = EFI_OUT_OF_RESOURCES;\r
173 goto FreePages;\r
174 }\r
175 CopyMem (Blob->File, Allocate->File, QEMU_LOADER_FNAME_SIZE);\r
176 Blob->Size = FwCfgSize;\r
177 Blob->Base = (VOID *)(UINTN)Address;\r
178 Blob->HostsOnlyTableData = TRUE;\r
179\r
180 Status = OrderedCollectionInsert (Tracker, NULL, Blob);\r
181 if (Status == RETURN_ALREADY_STARTED) {\r
182 DEBUG ((EFI_D_ERROR, "%a: duplicated file \"%a\"\n", __FUNCTION__,\r
183 Allocate->File));\r
184 Status = EFI_PROTOCOL_ERROR;\r
185 }\r
186 if (EFI_ERROR (Status)) {\r
187 goto FreeBlob;\r
188 }\r
189\r
190 QemuFwCfgSelectItem (FwCfgItem);\r
191 QemuFwCfgReadBytes (FwCfgSize, Blob->Base);\r
192 ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size);\r
193\r
194 DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "\r
195 "Address=0x%Lx\n", __FUNCTION__, Allocate->File, Allocate->Alignment,\r
196 Allocate->Zone, (UINT64)Blob->Size, (UINT64)(UINTN)Blob->Base));\r
197 return EFI_SUCCESS;\r
198\r
199FreeBlob:\r
200 FreePool (Blob);\r
201\r
202FreePages:\r
203 gBS->FreePages (Address, NumPages);\r
204\r
205 return Status;\r
206}\r
207\r
208\r
209/**\r
210 Process a QEMU_LOADER_ADD_POINTER command.\r
211\r
212 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.\r
213\r
214 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
215 structures created thus far.\r
216\r
217 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in\r
218 AddPointer, or the AddPointer command references\r
219 a file unknown to Tracker, or the pointer to\r
220 relocate has invalid location, size, or value, or\r
221 the relocated pointer value is not representable\r
222 in the given pointer size.\r
223\r
224 @retval EFI_SUCCESS The pointer field inside the pointer blob has\r
225 been relocated.\r
226**/\r
227STATIC\r
228EFI_STATUS\r
229EFIAPI\r
230ProcessCmdAddPointer (\r
231 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,\r
232 IN CONST ORDERED_COLLECTION *Tracker\r
233 )\r
234{\r
235 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;\r
236 BLOB *Blob, *Blob2;\r
237 UINT8 *PointerField;\r
238 UINT64 PointerValue;\r
239\r
240 if (AddPointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' ||\r
241 AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
242 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
243 return EFI_PROTOCOL_ERROR;\r
244 }\r
245\r
246 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);\r
247 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);\r
248 if (TrackerEntry == NULL || TrackerEntry2 == NULL) {\r
249 DEBUG ((EFI_D_ERROR, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",\r
250 __FUNCTION__, AddPointer->PointerFile, AddPointer->PointeeFile));\r
251 return EFI_PROTOCOL_ERROR;\r
252 }\r
253\r
254 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
255 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);\r
256 if ((AddPointer->PointerSize != 1 && AddPointer->PointerSize != 2 &&\r
257 AddPointer->PointerSize != 4 && AddPointer->PointerSize != 8) ||\r
258 Blob->Size < AddPointer->PointerSize ||\r
259 Blob->Size - AddPointer->PointerSize < AddPointer->PointerOffset) {\r
260 DEBUG ((EFI_D_ERROR, "%a: invalid pointer location or size in \"%a\"\n",\r
261 __FUNCTION__, AddPointer->PointerFile));\r
262 return EFI_PROTOCOL_ERROR;\r
263 }\r
264\r
265 PointerField = Blob->Base + AddPointer->PointerOffset;\r
266 PointerValue = 0;\r
267 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);\r
268 if (PointerValue >= Blob2->Size) {\r
269 DEBUG ((EFI_D_ERROR, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__,\r
270 AddPointer->PointerFile));\r
271 return EFI_PROTOCOL_ERROR;\r
272 }\r
273\r
274 //\r
275 // The memory allocation system ensures that the address of the byte past the\r
276 // last byte of any allocated object is expressible (no wraparound).\r
277 //\r
278 ASSERT ((UINTN)Blob2->Base <= MAX_ADDRESS - Blob2->Size);\r
279\r
280 PointerValue += (UINT64)(UINTN)Blob2->Base;\r
a3be3b65
LE
281 if (AddPointer->PointerSize < 8 &&\r
282 RShiftU64 (PointerValue, AddPointer->PointerSize * 8) != 0) {\r
14b0faad
JJ
283 DEBUG ((EFI_D_ERROR, "%a: relocated pointer value unrepresentable in "\r
284 "\"%a\"\n", __FUNCTION__, AddPointer->PointerFile));\r
285 return EFI_PROTOCOL_ERROR;\r
286 }\r
287\r
288 CopyMem (PointerField, &PointerValue, AddPointer->PointerSize);\r
289\r
290 DEBUG ((EFI_D_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "\r
291 "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__,\r
292 AddPointer->PointerFile, AddPointer->PointeeFile,\r
293 AddPointer->PointerOffset, AddPointer->PointerSize));\r
294 return EFI_SUCCESS;\r
295}\r
296\r
297\r
298/**\r
299 Process a QEMU_LOADER_ADD_CHECKSUM command.\r
300\r
301 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.\r
302\r
303 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
304 structures created thus far.\r
305\r
306 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in\r
307 AddChecksum, or the AddChecksum command\r
308 references a file unknown to Tracker, or the\r
309 range to checksum is invalid.\r
310\r
311 @retval EFI_SUCCESS The requested range has been checksummed.\r
312**/\r
313STATIC\r
314EFI_STATUS\r
315EFIAPI\r
316ProcessCmdAddChecksum (\r
317 IN CONST QEMU_LOADER_ADD_CHECKSUM *AddChecksum,\r
318 IN CONST ORDERED_COLLECTION *Tracker\r
319 )\r
320{\r
321 ORDERED_COLLECTION_ENTRY *TrackerEntry;\r
322 BLOB *Blob;\r
323\r
324 if (AddChecksum->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
325 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
326 return EFI_PROTOCOL_ERROR;\r
327 }\r
328\r
329 TrackerEntry = OrderedCollectionFind (Tracker, AddChecksum->File);\r
330 if (TrackerEntry == NULL) {\r
331 DEBUG ((EFI_D_ERROR, "%a: invalid blob reference \"%a\"\n", __FUNCTION__,\r
332 AddChecksum->File));\r
333 return EFI_PROTOCOL_ERROR;\r
334 }\r
335\r
336 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
337 if (Blob->Size <= AddChecksum->ResultOffset ||\r
338 Blob->Size < AddChecksum->Length ||\r
339 Blob->Size - AddChecksum->Length < AddChecksum->Start) {\r
340 DEBUG ((EFI_D_ERROR, "%a: invalid checksum range in \"%a\"\n",\r
341 __FUNCTION__, AddChecksum->File));\r
342 return EFI_PROTOCOL_ERROR;\r
343 }\r
344\r
345 Blob->Base[AddChecksum->ResultOffset] = CalculateCheckSum8 (\r
346 Blob->Base + AddChecksum->Start,\r
347 AddChecksum->Length\r
348 );\r
349 DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "\r
350 "Length=0x%x\n", __FUNCTION__, AddChecksum->File,\r
351 AddChecksum->ResultOffset, AddChecksum->Start, AddChecksum->Length));\r
352 return EFI_SUCCESS;\r
353}\r
354\r
355\r
9965cbd4
LE
356/**\r
357 Process a QEMU_LOADER_WRITE_POINTER command.\r
358\r
359 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.\r
360\r
361 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
362 structures created thus far.\r
363\r
df73df13
LE
364 @param[in,out] S3Context The S3_CONTEXT object capturing the fw_cfg actions\r
365 of successfully processed QEMU_LOADER_WRITE_POINTER\r
366 commands, to be replayed at S3 resume. S3Context\r
367 may be NULL if S3 is disabled.\r
368\r
9965cbd4
LE
369 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in\r
370 WritePointer. Or, the WritePointer command\r
371 references a file unknown to Tracker or the\r
372 fw_cfg directory. Or, the pointer object to\r
373 rewrite has invalid location, size, or initial\r
374 relative value. Or, the pointer value to store\r
375 does not fit in the given pointer size.\r
376\r
377 @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg\r
df73df13
LE
378 file has been written. If S3Context is not NULL,\r
379 then WritePointer has been condensed into\r
380 S3Context.\r
381\r
382 @return Error codes propagated from\r
383 SaveCondensedWritePointerToS3Context(). The\r
384 pointer object inside the writeable fw_cfg file\r
385 has not been written.\r
9965cbd4
LE
386**/\r
387STATIC\r
388EFI_STATUS\r
389ProcessCmdWritePointer (\r
390 IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer,\r
df73df13
LE
391 IN CONST ORDERED_COLLECTION *Tracker,\r
392 IN OUT S3_CONTEXT *S3Context OPTIONAL\r
9965cbd4
LE
393 )\r
394{\r
395 RETURN_STATUS Status;\r
396 FIRMWARE_CONFIG_ITEM PointerItem;\r
397 UINTN PointerItemSize;\r
398 ORDERED_COLLECTION_ENTRY *PointeeEntry;\r
399 BLOB *PointeeBlob;\r
400 UINT64 PointerValue;\r
401\r
402 if (WritePointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' ||\r
403 WritePointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
404 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
405 return EFI_PROTOCOL_ERROR;\r
406 }\r
407\r
408 Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,\r
409 &PointerItem, &PointerItemSize);\r
410 PointeeEntry = OrderedCollectionFind (Tracker, WritePointer->PointeeFile);\r
411 if (RETURN_ERROR (Status) || PointeeEntry == NULL) {\r
412 DEBUG ((DEBUG_ERROR,\r
413 "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",\r
414 __FUNCTION__, WritePointer->PointerFile, WritePointer->PointeeFile));\r
415 return EFI_PROTOCOL_ERROR;\r
416 }\r
417\r
418 if ((WritePointer->PointerSize != 1 && WritePointer->PointerSize != 2 &&\r
419 WritePointer->PointerSize != 4 && WritePointer->PointerSize != 8) ||\r
420 (PointerItemSize < WritePointer->PointerSize) ||\r
421 (PointerItemSize - WritePointer->PointerSize <\r
422 WritePointer->PointerOffset)) {\r
423 DEBUG ((DEBUG_ERROR, "%a: invalid pointer location or size in \"%a\"\n",\r
424 __FUNCTION__, WritePointer->PointerFile));\r
425 return EFI_PROTOCOL_ERROR;\r
426 }\r
427\r
428 PointeeBlob = OrderedCollectionUserStruct (PointeeEntry);\r
429 PointerValue = WritePointer->PointeeOffset;\r
430 if (PointerValue >= PointeeBlob->Size) {\r
431 DEBUG ((DEBUG_ERROR, "%a: invalid PointeeOffset\n", __FUNCTION__));\r
432 return EFI_PROTOCOL_ERROR;\r
433 }\r
434\r
435 //\r
436 // The memory allocation system ensures that the address of the byte past the\r
437 // last byte of any allocated object is expressible (no wraparound).\r
438 //\r
439 ASSERT ((UINTN)PointeeBlob->Base <= MAX_ADDRESS - PointeeBlob->Size);\r
440\r
441 PointerValue += (UINT64)(UINTN)PointeeBlob->Base;\r
a3be3b65
LE
442 if (WritePointer->PointerSize < 8 &&\r
443 RShiftU64 (PointerValue, WritePointer->PointerSize * 8) != 0) {\r
9965cbd4
LE
444 DEBUG ((DEBUG_ERROR, "%a: pointer value unrepresentable in \"%a\"\n",\r
445 __FUNCTION__, WritePointer->PointerFile));\r
446 return EFI_PROTOCOL_ERROR;\r
447 }\r
448\r
df73df13
LE
449 //\r
450 // If S3 is enabled, we have to capture the below fw_cfg actions in condensed\r
451 // form, to be replayed during S3 resume.\r
452 //\r
453 if (S3Context != NULL) {\r
454 EFI_STATUS SaveStatus;\r
455\r
456 SaveStatus = SaveCondensedWritePointerToS3Context (\r
457 S3Context,\r
458 (UINT16)PointerItem,\r
459 WritePointer->PointerSize,\r
460 WritePointer->PointerOffset,\r
461 PointerValue\r
462 );\r
463 if (EFI_ERROR (SaveStatus)) {\r
464 return SaveStatus;\r
465 }\r
466 }\r
467\r
9965cbd4
LE
468 QemuFwCfgSelectItem (PointerItem);\r
469 QemuFwCfgSkipBytes (WritePointer->PointerOffset);\r
470 QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);\r
471\r
472 //\r
473 // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob\r
474 // as unreleasable, for the case when the whole linker/loader script is\r
475 // handled successfully.\r
476 //\r
477 PointeeBlob->HostsOnlyTableData = FALSE;\r
478\r
479 DEBUG ((DEBUG_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "\r
480 "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n", __FUNCTION__,\r
481 WritePointer->PointerFile, WritePointer->PointeeFile,\r
482 WritePointer->PointerOffset, WritePointer->PointeeOffset,\r
483 WritePointer->PointerSize));\r
484 return EFI_SUCCESS;\r
485}\r
486\r
487\r
488/**\r
489 Undo a QEMU_LOADER_WRITE_POINTER command.\r
490\r
491 This function revokes (zeroes out) a guest memory reference communicated to\r
492 QEMU earlier. The caller is responsible for invoking this function only on\r
493 such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed\r
494 by ProcessCmdWritePointer().\r
495\r
496 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.\r
497**/\r
498STATIC\r
499VOID\r
500UndoCmdWritePointer (\r
501 IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer\r
502 )\r
503{\r
504 RETURN_STATUS Status;\r
505 FIRMWARE_CONFIG_ITEM PointerItem;\r
506 UINTN PointerItemSize;\r
507 UINT64 PointerValue;\r
508\r
509 Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,\r
510 &PointerItem, &PointerItemSize);\r
511 ASSERT_RETURN_ERROR (Status);\r
512\r
513 PointerValue = 0;\r
514 QemuFwCfgSelectItem (PointerItem);\r
515 QemuFwCfgSkipBytes (WritePointer->PointerOffset);\r
516 QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);\r
517\r
518 DEBUG ((DEBUG_VERBOSE,\r
519 "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__,\r
520 WritePointer->PointerFile, WritePointer->PointerOffset,\r
521 WritePointer->PointerSize));\r
522}\r
523\r
524\r
14b0faad
JJ
525//\r
526// We'll be saving the keys of installed tables so that we can roll them back\r
527// in case of failure. 128 tables should be enough for anyone (TM).\r
528//\r
529#define INSTALLED_TABLES_MAX 128\r
530\r
531/**\r
532 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte\r
533 array is an ACPI table, and if so, install it.\r
534\r
535 This function assumes that the entire QEMU linker/loader command file has\r
8c0b0b34 536 been processed successfully in a prior first pass.\r
14b0faad
JJ
537\r
538 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.\r
539\r
540 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
541 structures.\r
542\r
543 @param[in] AcpiProtocol The ACPI table protocol used to install tables.\r
544\r
545 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN\r
546 elements, allocated by the caller. On output,\r
547 the function will have stored (appended) the\r
548 AcpiProtocol-internal key of the ACPI table that\r
549 the function has installed, if the AddPointer\r
550 command identified an ACPI table that is\r
551 different from RSDT and XSDT.\r
552\r
553 @param[in,out] NumInstalled On input, the number of entries already used in\r
554 InstalledKey; it must be in [0,\r
555 INSTALLED_TABLES_MAX] inclusive. On output, the\r
556 parameter is incremented if the AddPointer\r
557 command identified an ACPI table that is\r
558 different from RSDT and XSDT.\r
559\r
560 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on\r
561 input.\r
562\r
563 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI\r
564 table different from RSDT and XSDT, but there\r
565 was no more room in InstalledKey.\r
566\r
567 @retval EFI_SUCCESS AddPointer has been processed. Either an ACPI\r
568 table different from RSDT and XSDT has been\r
569 installed (reflected by InstalledKey and\r
570 NumInstalled), or RSDT or XSDT has been\r
571 identified but not installed, or the fw_cfg\r
572 blob pointed-into by AddPointer has been\r
573 marked as hosting something else than just\r
574 direct ACPI table contents.\r
575\r
576 @return Error codes returned by\r
577 AcpiProtocol->InstallAcpiTable().\r
578**/\r
579STATIC\r
580EFI_STATUS\r
581EFIAPI\r
582Process2ndPassCmdAddPointer (\r
583 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,\r
584 IN CONST ORDERED_COLLECTION *Tracker,\r
585 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
586 IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX],\r
587 IN OUT INT32 *NumInstalled\r
588 )\r
589{\r
590 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry;\r
591 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry2;\r
592 CONST BLOB *Blob;\r
593 BLOB *Blob2;\r
594 CONST UINT8 *PointerField;\r
595 UINT64 PointerValue;\r
596 UINTN Blob2Remaining;\r
597 UINTN TableSize;\r
598 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;\r
599 CONST EFI_ACPI_DESCRIPTION_HEADER *Header;\r
600 EFI_STATUS Status;\r
601\r
602 if (*NumInstalled < 0 || *NumInstalled > INSTALLED_TABLES_MAX) {\r
603 return EFI_INVALID_PARAMETER;\r
604 }\r
605\r
606 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);\r
607 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);\r
608 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
609 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);\r
610 PointerField = Blob->Base + AddPointer->PointerOffset;\r
611 PointerValue = 0;\r
612 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);\r
613\r
614 //\r
615 // We assert that PointerValue falls inside Blob2's contents. This is ensured\r
616 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().\r
617 //\r
618 Blob2Remaining = (UINTN)Blob2->Base;\r
619 ASSERT(PointerValue >= Blob2Remaining);\r
620 Blob2Remaining += Blob2->Size;\r
621 ASSERT (PointerValue < Blob2Remaining);\r
622\r
623 Blob2Remaining -= (UINTN) PointerValue;\r
624 DEBUG ((EFI_D_VERBOSE, "%a: checking for ACPI header in \"%a\" at 0x%Lx "\r
625 "(remaining: 0x%Lx): ", __FUNCTION__, AddPointer->PointeeFile,\r
626 PointerValue, (UINT64)Blob2Remaining));\r
627\r
628 TableSize = 0;\r
629\r
630 //\r
631 // To make our job simple, the FACS has a custom header. Sigh.\r
632 //\r
633 if (sizeof *Facs <= Blob2Remaining) {\r
634 Facs = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)PointerValue;\r
635\r
636 if (Facs->Length >= sizeof *Facs &&\r
637 Facs->Length <= Blob2Remaining &&\r
638 Facs->Signature ==\r
639 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) {\r
640 DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n",\r
641 (CONST CHAR8 *)&Facs->Signature, Facs->Length));\r
642 TableSize = Facs->Length;\r
643 }\r
644 }\r
645\r
646 //\r
647 // check for the uniform tables\r
648 //\r
649 if (TableSize == 0 && sizeof *Header <= Blob2Remaining) {\r
650 Header = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)PointerValue;\r
651\r
652 if (Header->Length >= sizeof *Header &&\r
653 Header->Length <= Blob2Remaining &&\r
654 CalculateSum8 ((CONST UINT8 *)Header, Header->Length) == 0) {\r
655 //\r
656 // This looks very much like an ACPI table from QEMU:\r
657 // - Length field consistent with both ACPI and containing blob size\r
658 // - checksum is correct\r
659 //\r
660 DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n",\r
661 (CONST CHAR8 *)&Header->Signature, Header->Length));\r
662 TableSize = Header->Length;\r
663\r
664 //\r
665 // Skip RSDT and XSDT because those are handled by\r
666 // EFI_ACPI_TABLE_PROTOCOL automatically.\r
667 if (Header->Signature ==\r
668 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE ||\r
669 Header->Signature ==\r
670 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {\r
671 return EFI_SUCCESS;\r
672 }\r
673 }\r
674 }\r
675\r
676 if (TableSize == 0) {\r
677 DEBUG ((EFI_D_VERBOSE, "not found; marking fw_cfg blob as opaque\n"));\r
678 Blob2->HostsOnlyTableData = FALSE;\r
679 return EFI_SUCCESS;\r
680 }\r
681\r
682 if (*NumInstalled == INSTALLED_TABLES_MAX) {\r
683 DEBUG ((EFI_D_ERROR, "%a: can't install more than %d tables\n",\r
684 __FUNCTION__, INSTALLED_TABLES_MAX));\r
685 return EFI_OUT_OF_RESOURCES;\r
686 }\r
687\r
688 Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol,\r
689 (VOID *)(UINTN)PointerValue, TableSize,\r
690 &InstalledKey[*NumInstalled]);\r
691 if (EFI_ERROR (Status)) {\r
692 DEBUG ((EFI_D_ERROR, "%a: InstallAcpiTable(): %r\n", __FUNCTION__,\r
693 Status));\r
694 return Status;\r
695 }\r
696 ++*NumInstalled;\r
697 return EFI_SUCCESS;\r
698}\r
699\r
700\r
701/**\r
702 Download, process, and install ACPI table data from the QEMU loader\r
703 interface.\r
704\r
705 @param[in] AcpiProtocol The ACPI table protocol used to install tables.\r
706\r
707 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU\r
708 loader command with unsupported parameters\r
709 has been found.\r
710\r
711 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg\r
712 files.\r
713\r
714 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than\r
715 INSTALLED_TABLES_MAX tables found.\r
716\r
717 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.\r
718\r
719 @return Status codes returned by\r
720 AcpiProtocol->InstallAcpiTable().\r
721\r
722**/\r
723EFI_STATUS\r
724EFIAPI\r
f186536b 725InstallQemuFwCfgTables (\r
14b0faad
JJ
726 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol\r
727 )\r
728{\r
729 EFI_STATUS Status;\r
730 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
731 UINTN FwCfgSize;\r
732 QEMU_LOADER_ENTRY *LoaderStart;\r
733 CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd;\r
9965cbd4 734 CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd;\r
8f35eb92
LE
735 ORIGINAL_ATTRIBUTES *OriginalPciAttributes;\r
736 UINTN OriginalPciAttributesCount;\r
df73df13 737 S3_CONTEXT *S3Context;\r
14b0faad
JJ
738 ORDERED_COLLECTION *Tracker;\r
739 UINTN *InstalledKey;\r
740 INT32 Installed;\r
741 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;\r
742\r
743 Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize);\r
744 if (EFI_ERROR (Status)) {\r
745 return Status;\r
746 }\r
747 if (FwCfgSize % sizeof *LoaderEntry != 0) {\r
748 DEBUG ((EFI_D_ERROR, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",\r
749 __FUNCTION__, (UINT64)FwCfgSize));\r
750 return EFI_PROTOCOL_ERROR;\r
751 }\r
752\r
753 LoaderStart = AllocatePool (FwCfgSize);\r
754 if (LoaderStart == NULL) {\r
755 return EFI_OUT_OF_RESOURCES;\r
756 }\r
8f35eb92 757 EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount);\r
14b0faad
JJ
758 QemuFwCfgSelectItem (FwCfgItem);\r
759 QemuFwCfgReadBytes (FwCfgSize, LoaderStart);\r
8f35eb92 760 RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount);\r
14b0faad
JJ
761 LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry;\r
762\r
df73df13
LE
763 S3Context = NULL;\r
764 if (QemuFwCfgS3Enabled ()) {\r
765 //\r
766 // Size the allocation pessimistically, assuming that all commands in the\r
767 // script are QEMU_LOADER_WRITE_POINTER commands.\r
768 //\r
769 Status = AllocateS3Context (&S3Context, LoaderEnd - LoaderStart);\r
770 if (EFI_ERROR (Status)) {\r
771 goto FreeLoader;\r
772 }\r
773 }\r
774\r
14b0faad
JJ
775 Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare);\r
776 if (Tracker == NULL) {\r
777 Status = EFI_OUT_OF_RESOURCES;\r
df73df13 778 goto FreeS3Context;\r
14b0faad
JJ
779 }\r
780\r
781 //\r
782 // first pass: process the commands\r
783 //\r
9965cbd4
LE
784 // "WritePointerSubsetEnd" points one past the last successful\r
785 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first\r
786 // pass, no such command has been encountered yet.\r
787 //\r
788 WritePointerSubsetEnd = LoaderStart;\r
14b0faad
JJ
789 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {\r
790 switch (LoaderEntry->Type) {\r
791 case QemuLoaderCmdAllocate:\r
792 Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker);\r
793 break;\r
794\r
795 case QemuLoaderCmdAddPointer:\r
796 Status = ProcessCmdAddPointer (&LoaderEntry->Command.AddPointer,\r
797 Tracker);\r
798 break;\r
799\r
800 case QemuLoaderCmdAddChecksum:\r
801 Status = ProcessCmdAddChecksum (&LoaderEntry->Command.AddChecksum,\r
802 Tracker);\r
803 break;\r
804\r
9965cbd4
LE
805 case QemuLoaderCmdWritePointer:\r
806 Status = ProcessCmdWritePointer (&LoaderEntry->Command.WritePointer,\r
df73df13 807 Tracker, S3Context);\r
9965cbd4
LE
808 if (!EFI_ERROR (Status)) {\r
809 WritePointerSubsetEnd = LoaderEntry + 1;\r
810 }\r
811 break;\r
812\r
14b0faad
JJ
813 default:\r
814 DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n",\r
815 __FUNCTION__, LoaderEntry->Type));\r
816 break;\r
817 }\r
818\r
819 if (EFI_ERROR (Status)) {\r
9965cbd4 820 goto RollbackWritePointersAndFreeTracker;\r
14b0faad
JJ
821 }\r
822 }\r
823\r
824 InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);\r
825 if (InstalledKey == NULL) {\r
826 Status = EFI_OUT_OF_RESOURCES;\r
9965cbd4 827 goto RollbackWritePointersAndFreeTracker;\r
14b0faad
JJ
828 }\r
829\r
830 //\r
831 // second pass: identify and install ACPI tables\r
832 //\r
833 Installed = 0;\r
834 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {\r
835 if (LoaderEntry->Type == QemuLoaderCmdAddPointer) {\r
836 Status = Process2ndPassCmdAddPointer (&LoaderEntry->Command.AddPointer,\r
837 Tracker, AcpiProtocol, InstalledKey, &Installed);\r
838 if (EFI_ERROR (Status)) {\r
df73df13 839 goto UninstallAcpiTables;\r
14b0faad
JJ
840 }\r
841 }\r
842 }\r
843\r
df73df13
LE
844 //\r
845 // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3\r
846 // Boot Script opcodes has to be the last operation in this function, because\r
847 // if it succeeds, it cannot be undone.\r
848 //\r
849 if (S3Context != NULL) {\r
850 Status = TransferS3ContextToBootScript (S3Context);\r
851 }\r
852\r
853UninstallAcpiTables:\r
14b0faad
JJ
854 if (EFI_ERROR (Status)) {\r
855 //\r
856 // roll back partial installation\r
857 //\r
858 while (Installed > 0) {\r
859 --Installed;\r
860 AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);\r
861 }\r
862 } else {\r
863 DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));\r
864 }\r
865\r
866 FreePool (InstalledKey);\r
867\r
9965cbd4
LE
868RollbackWritePointersAndFreeTracker:\r
869 //\r
870 // In case of failure, revoke any allocation addresses that were communicated\r
871 // to QEMU previously, before we release all the blobs.\r
872 //\r
873 if (EFI_ERROR (Status)) {\r
874 LoaderEntry = WritePointerSubsetEnd;\r
875 while (LoaderEntry > LoaderStart) {\r
876 --LoaderEntry;\r
877 if (LoaderEntry->Type == QemuLoaderCmdWritePointer) {\r
878 UndoCmdWritePointer (&LoaderEntry->Command.WritePointer);\r
879 }\r
880 }\r
881 }\r
882\r
14b0faad
JJ
883 //\r
884 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in\r
885 // place only if we're exiting with success and the blob hosts data that is\r
886 // not directly part of some ACPI table.\r
887 //\r
888 for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL;\r
889 TrackerEntry = TrackerEntry2) {\r
890 VOID *UserStruct;\r
891 BLOB *Blob;\r
892\r
893 TrackerEntry2 = OrderedCollectionNext (TrackerEntry);\r
894 OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct);\r
895 Blob = UserStruct;\r
896\r
897 if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) {\r
898 DEBUG ((EFI_D_VERBOSE, "%a: freeing \"%a\"\n", __FUNCTION__,\r
899 Blob->File));\r
900 gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size));\r
901 }\r
902 FreePool (Blob);\r
903 }\r
904 OrderedCollectionUninit (Tracker);\r
905\r
df73df13
LE
906FreeS3Context:\r
907 if (S3Context != NULL) {\r
908 ReleaseS3Context (S3Context);\r
909 }\r
910\r
14b0faad
JJ
911FreeLoader:\r
912 FreePool (LoaderStart);\r
913\r
914 return Status;\r
915}\r