]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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
b26f0cf9 7 SPDX-License-Identifier: BSD-2-Clause-Patent\r
14b0faad
JJ
8\r
9**/\r
10\r
cc302b79
LE
11#include <IndustryStandard/Acpi.h> // EFI_ACPI_DESCRIPTION_HEADER\r
12#include <IndustryStandard/QemuLoader.h> // QEMU_LOADER_FNAME_SIZE\r
935343cf 13#include <IndustryStandard/UefiTcgPlatform.h>\r
cc302b79
LE
14#include <Library/BaseLib.h> // AsciiStrCmp()\r
15#include <Library/BaseMemoryLib.h> // CopyMem()\r
16#include <Library/DebugLib.h> // DEBUG()\r
17#include <Library/MemoryAllocationLib.h> // AllocatePool()\r
18#include <Library/OrderedCollectionLib.h> // OrderedCollectionMin()\r
19#include <Library/QemuFwCfgLib.h> // QemuFwCfgFindFile()\r
20#include <Library/QemuFwCfgS3Lib.h> // QemuFwCfgS3Enabled()\r
21#include <Library/UefiBootServicesTableLib.h> // gBS\r
935343cf 22#include <Library/TpmMeasurementLib.h>\r
14b0faad 23\r
180f1908 24#include "AcpiPlatform.h"\r
14b0faad
JJ
25\r
26//\r
27// The user structure for the ordered collection that will track the fw_cfg\r
28// blobs under processing.\r
29//\r
30typedef struct {\r
ac0a286f
MK
31 UINT8 File[QEMU_LOADER_FNAME_SIZE]; // NUL-terminated name of the fw_cfg\r
32 // blob. This is the ordering / search\r
33 // key.\r
34 UINTN Size; // The number of bytes in this blob.\r
35 UINT8 *Base; // Pointer to the blob data.\r
36 BOOLEAN HostsOnlyTableData; // TRUE iff the blob has been found to\r
37 // only contain data that is directly\r
38 // part of ACPI tables.\r
14b0faad
JJ
39} BLOB;\r
40\r
14b0faad
JJ
41/**\r
42 Compare a standalone key against a user structure containing an embedded key.\r
43\r
44 @param[in] StandaloneKey Pointer to the bare key.\r
45\r
46 @param[in] UserStruct Pointer to the user structure with the embedded\r
47 key.\r
48\r
49 @retval <0 If StandaloneKey compares less than UserStruct's key.\r
50\r
51 @retval 0 If StandaloneKey compares equal to UserStruct's key.\r
52\r
53 @retval >0 If StandaloneKey compares greater than UserStruct's key.\r
54**/\r
55STATIC\r
56INTN\r
57EFIAPI\r
58BlobKeyCompare (\r
ac0a286f
MK
59 IN CONST VOID *StandaloneKey,\r
60 IN CONST VOID *UserStruct\r
14b0faad
JJ
61 )\r
62{\r
ac0a286f 63 CONST BLOB *Blob;\r
14b0faad
JJ
64\r
65 Blob = UserStruct;\r
66 return AsciiStrCmp (StandaloneKey, (CONST CHAR8 *)Blob->File);\r
67}\r
68\r
14b0faad
JJ
69/**\r
70 Comparator function for two user structures.\r
71\r
72 @param[in] UserStruct1 Pointer to the first user structure.\r
73\r
74 @param[in] UserStruct2 Pointer to the second user structure.\r
75\r
76 @retval <0 If UserStruct1 compares less than UserStruct2.\r
77\r
78 @retval 0 If UserStruct1 compares equal to UserStruct2.\r
79\r
80 @retval >0 If UserStruct1 compares greater than UserStruct2.\r
81**/\r
82STATIC\r
83INTN\r
84EFIAPI\r
85BlobCompare (\r
ac0a286f
MK
86 IN CONST VOID *UserStruct1,\r
87 IN CONST VOID *UserStruct2\r
14b0faad
JJ
88 )\r
89{\r
ac0a286f 90 CONST BLOB *Blob1;\r
14b0faad
JJ
91\r
92 Blob1 = UserStruct1;\r
93 return BlobKeyCompare (Blob1->File, UserStruct2);\r
94}\r
95\r
072060a6
PDJ
96/**\r
97 Comparator function for two opaque pointers, ordering on (unsigned) pointer\r
98 value itself.\r
99 Can be used as both Key and UserStruct comparator.\r
100\r
101 @param[in] Pointer1 First pointer.\r
102\r
103 @param[in] Pointer2 Second pointer.\r
104\r
105 @retval <0 If Pointer1 compares less than Pointer2.\r
106\r
107 @retval 0 If Pointer1 compares equal to Pointer2.\r
108\r
109 @retval >0 If Pointer1 compares greater than Pointer2.\r
110**/\r
111STATIC\r
112INTN\r
113EFIAPI\r
114PointerCompare (\r
ac0a286f
MK
115 IN CONST VOID *Pointer1,\r
116 IN CONST VOID *Pointer2\r
072060a6
PDJ
117 )\r
118{\r
119 if (Pointer1 == Pointer2) {\r
120 return 0;\r
121 }\r
ac0a286f 122\r
072060a6
PDJ
123 if ((UINTN)Pointer1 < (UINTN)Pointer2) {\r
124 return -1;\r
125 }\r
ac0a286f 126\r
072060a6
PDJ
127 return 1;\r
128}\r
129\r
4275f385
LE
130/**\r
131 Comparator function for two ASCII strings. Can be used as both Key and\r
132 UserStruct comparator.\r
133\r
134 This function exists solely so we can avoid casting &AsciiStrCmp to\r
135 ORDERED_COLLECTION_USER_COMPARE and ORDERED_COLLECTION_KEY_COMPARE.\r
136\r
137 @param[in] AsciiString1 Pointer to the first ASCII string.\r
138\r
139 @param[in] AsciiString2 Pointer to the second ASCII string.\r
140\r
141 @return The return value of AsciiStrCmp (AsciiString1, AsciiString2).\r
142**/\r
143STATIC\r
144INTN\r
145EFIAPI\r
146AsciiStringCompare (\r
ac0a286f
MK
147 IN CONST VOID *AsciiString1,\r
148 IN CONST VOID *AsciiString2\r
4275f385
LE
149 )\r
150{\r
151 return AsciiStrCmp (AsciiString1, AsciiString2);\r
152}\r
153\r
4275f385
LE
154/**\r
155 Release the ORDERED_COLLECTION structure populated by\r
156 CollectAllocationsRestrictedTo32Bit() (below).\r
157\r
158 This function may be called by CollectAllocationsRestrictedTo32Bit() itself,\r
159 on the error path.\r
160\r
161 @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure to\r
162 release.\r
163**/\r
164STATIC\r
165VOID\r
166ReleaseAllocationsRestrictedTo32Bit (\r
ac0a286f
MK
167 IN ORDERED_COLLECTION *AllocationsRestrictedTo32Bit\r
168 )\r
4275f385 169{\r
ac0a286f 170 ORDERED_COLLECTION_ENTRY *Entry, *Entry2;\r
4275f385
LE
171\r
172 for (Entry = OrderedCollectionMin (AllocationsRestrictedTo32Bit);\r
173 Entry != NULL;\r
ac0a286f
MK
174 Entry = Entry2)\r
175 {\r
4275f385
LE
176 Entry2 = OrderedCollectionNext (Entry);\r
177 OrderedCollectionDelete (AllocationsRestrictedTo32Bit, Entry, NULL);\r
178 }\r
ac0a286f 179\r
4275f385
LE
180 OrderedCollectionUninit (AllocationsRestrictedTo32Bit);\r
181}\r
182\r
4275f385
LE
183/**\r
184 Iterate over the linker/loader script, and collect the names of the fw_cfg\r
185 blobs that are referenced by QEMU_LOADER_ADD_POINTER.PointeeFile fields, such\r
186 that QEMU_LOADER_ADD_POINTER.PointerSize is less than 8. This means that the\r
187 pointee blob's address will have to be patched into a narrower-than-8 byte\r
188 pointer field, hence the pointee blob must not be allocated from 64-bit\r
189 address space.\r
190\r
191 @param[out] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure\r
192 linking (not copying / owning) such\r
193 QEMU_LOADER_ADD_POINTER.PointeeFile\r
194 fields that name the blobs\r
195 restricted from 64-bit allocation.\r
196\r
197 @param[in] LoaderStart Points to the first entry in the\r
198 linker/loader script.\r
199\r
200 @param[in] LoaderEnd Points one past the last entry in\r
201 the linker/loader script.\r
202\r
203 @retval EFI_SUCCESS AllocationsRestrictedTo32Bit has been\r
204 populated.\r
205\r
206 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
207\r
208 @retval EFI_PROTOCOL_ERROR Invalid linker/loader script contents.\r
209**/\r
210STATIC\r
211EFI_STATUS\r
212CollectAllocationsRestrictedTo32Bit (\r
ac0a286f
MK
213 OUT ORDERED_COLLECTION **AllocationsRestrictedTo32Bit,\r
214 IN CONST QEMU_LOADER_ENTRY *LoaderStart,\r
215 IN CONST QEMU_LOADER_ENTRY *LoaderEnd\r
216 )\r
4275f385 217{\r
ac0a286f
MK
218 ORDERED_COLLECTION *Collection;\r
219 CONST QEMU_LOADER_ENTRY *LoaderEntry;\r
220 EFI_STATUS Status;\r
4275f385
LE
221\r
222 Collection = OrderedCollectionInit (AsciiStringCompare, AsciiStringCompare);\r
223 if (Collection == NULL) {\r
224 return EFI_OUT_OF_RESOURCES;\r
225 }\r
226\r
227 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {\r
ac0a286f 228 CONST QEMU_LOADER_ADD_POINTER *AddPointer;\r
4275f385
LE
229\r
230 if (LoaderEntry->Type != QemuLoaderCmdAddPointer) {\r
231 continue;\r
232 }\r
ac0a286f 233\r
4275f385
LE
234 AddPointer = &LoaderEntry->Command.AddPointer;\r
235\r
236 if (AddPointer->PointerSize >= 8) {\r
237 continue;\r
238 }\r
239\r
240 if (AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
241 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
242 Status = EFI_PROTOCOL_ERROR;\r
243 goto RollBack;\r
244 }\r
245\r
246 Status = OrderedCollectionInsert (\r
247 Collection,\r
248 NULL, // Entry\r
249 (VOID *)AddPointer->PointeeFile\r
250 );\r
251 switch (Status) {\r
ac0a286f
MK
252 case EFI_SUCCESS:\r
253 DEBUG ((\r
254 DEBUG_VERBOSE,\r
255 "%a: restricting blob \"%a\" from 64-bit allocation\n",\r
256 __FUNCTION__,\r
257 AddPointer->PointeeFile\r
258 ));\r
259 break;\r
260 case EFI_ALREADY_STARTED:\r
261 //\r
262 // The restriction has been recorded already.\r
263 //\r
264 break;\r
265 case EFI_OUT_OF_RESOURCES:\r
266 goto RollBack;\r
267 default:\r
268 ASSERT (FALSE);\r
4275f385
LE
269 }\r
270 }\r
271\r
272 *AllocationsRestrictedTo32Bit = Collection;\r
273 return EFI_SUCCESS;\r
274\r
275RollBack:\r
276 ReleaseAllocationsRestrictedTo32Bit (Collection);\r
277 return Status;\r
278}\r
279\r
14b0faad
JJ
280/**\r
281 Process a QEMU_LOADER_ALLOCATE command.\r
282\r
4275f385
LE
283 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to\r
284 process.\r
14b0faad 285\r
4275f385
LE
286 @param[in,out] Tracker The ORDERED_COLLECTION tracking the\r
287 BLOB user structures created thus\r
288 far.\r
289\r
290 @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION populated by\r
291 the function\r
292 CollectAllocationsRestrictedTo32Bit,\r
293 naming the fw_cfg blobs that must\r
294 not be allocated from 64-bit address\r
295 space.\r
14b0faad
JJ
296\r
297 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been\r
298 allocated for the blob contents, and the\r
299 contents have been saved. A BLOB object (user\r
300 structure) has been allocated from pool memory,\r
301 referencing the blob contents. The BLOB user\r
302 structure has been linked into Tracker.\r
303\r
304 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in\r
305 Allocate, or the Allocate command references a\r
306 file that is already known by Tracker.\r
307\r
308 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in\r
309 Allocate.\r
310\r
311 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.\r
312\r
313 @return Error codes from QemuFwCfgFindFile() and\r
314 gBS->AllocatePages().\r
315**/\r
316STATIC\r
317EFI_STATUS\r
318EFIAPI\r
319ProcessCmdAllocate (\r
ac0a286f
MK
320 IN CONST QEMU_LOADER_ALLOCATE *Allocate,\r
321 IN OUT ORDERED_COLLECTION *Tracker,\r
322 IN ORDERED_COLLECTION *AllocationsRestrictedTo32Bit\r
14b0faad
JJ
323 )\r
324{\r
ac0a286f
MK
325 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
326 UINTN FwCfgSize;\r
327 EFI_STATUS Status;\r
328 UINTN NumPages;\r
329 EFI_PHYSICAL_ADDRESS Address;\r
330 BLOB *Blob;\r
14b0faad
JJ
331\r
332 if (Allocate->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
70d5086c 333 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
14b0faad
JJ
334 return EFI_PROTOCOL_ERROR;\r
335 }\r
336\r
337 if (Allocate->Alignment > EFI_PAGE_SIZE) {\r
ac0a286f
MK
338 DEBUG ((\r
339 DEBUG_ERROR,\r
340 "%a: unsupported alignment 0x%x\n",\r
341 __FUNCTION__,\r
342 Allocate->Alignment\r
343 ));\r
14b0faad
JJ
344 return EFI_UNSUPPORTED;\r
345 }\r
346\r
347 Status = QemuFwCfgFindFile ((CHAR8 *)Allocate->File, &FwCfgItem, &FwCfgSize);\r
348 if (EFI_ERROR (Status)) {\r
ac0a286f
MK
349 DEBUG ((\r
350 DEBUG_ERROR,\r
351 "%a: QemuFwCfgFindFile(\"%a\"): %r\n",\r
352 __FUNCTION__,\r
353 Allocate->File,\r
354 Status\r
355 ));\r
14b0faad
JJ
356 return Status;\r
357 }\r
358\r
359 NumPages = EFI_SIZE_TO_PAGES (FwCfgSize);\r
ac0a286f 360 Address = MAX_UINT64;\r
4275f385
LE
361 if (OrderedCollectionFind (\r
362 AllocationsRestrictedTo32Bit,\r
363 Allocate->File\r
ac0a286f
MK
364 ) != NULL)\r
365 {\r
4275f385
LE
366 Address = MAX_UINT32;\r
367 }\r
ac0a286f
MK
368\r
369 Status = gBS->AllocatePages (\r
370 AllocateMaxAddress,\r
371 EfiACPIMemoryNVS,\r
372 NumPages,\r
373 &Address\r
374 );\r
14b0faad
JJ
375 if (EFI_ERROR (Status)) {\r
376 return Status;\r
377 }\r
378\r
379 Blob = AllocatePool (sizeof *Blob);\r
380 if (Blob == NULL) {\r
381 Status = EFI_OUT_OF_RESOURCES;\r
382 goto FreePages;\r
383 }\r
ac0a286f 384\r
14b0faad 385 CopyMem (Blob->File, Allocate->File, QEMU_LOADER_FNAME_SIZE);\r
ac0a286f
MK
386 Blob->Size = FwCfgSize;\r
387 Blob->Base = (VOID *)(UINTN)Address;\r
14b0faad
JJ
388 Blob->HostsOnlyTableData = TRUE;\r
389\r
390 Status = OrderedCollectionInsert (Tracker, NULL, Blob);\r
391 if (Status == RETURN_ALREADY_STARTED) {\r
ac0a286f
MK
392 DEBUG ((\r
393 DEBUG_ERROR,\r
394 "%a: duplicated file \"%a\"\n",\r
395 __FUNCTION__,\r
396 Allocate->File\r
397 ));\r
14b0faad
JJ
398 Status = EFI_PROTOCOL_ERROR;\r
399 }\r
ac0a286f 400\r
14b0faad
JJ
401 if (EFI_ERROR (Status)) {\r
402 goto FreeBlob;\r
403 }\r
404\r
405 QemuFwCfgSelectItem (FwCfgItem);\r
406 QemuFwCfgReadBytes (FwCfgSize, Blob->Base);\r
407 ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size);\r
408\r
ac0a286f
MK
409 DEBUG ((\r
410 DEBUG_VERBOSE,\r
411 "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "\r
412 "Address=0x%Lx\n",\r
413 __FUNCTION__,\r
414 Allocate->File,\r
415 Allocate->Alignment,\r
416 Allocate->Zone,\r
417 (UINT64)Blob->Size,\r
418 (UINT64)(UINTN)Blob->Base\r
419 ));\r
935343cf
MX
420\r
421 //\r
422 // Measure the data which is downloaded from QEMU.\r
423 // It has to be done before it is consumed. Because the data will\r
424 // be updated in the following operations.\r
425 //\r
426 TpmMeasureAndLogData (\r
427 1,\r
428 EV_PLATFORM_CONFIG_FLAGS,\r
429 EV_POSTCODE_INFO_ACPI_DATA,\r
430 ACPI_DATA_LEN,\r
431 (VOID *)(UINTN)Blob->Base,\r
432 Blob->Size\r
433 );\r
434\r
14b0faad
JJ
435 return EFI_SUCCESS;\r
436\r
437FreeBlob:\r
438 FreePool (Blob);\r
439\r
440FreePages:\r
441 gBS->FreePages (Address, NumPages);\r
442\r
443 return Status;\r
444}\r
445\r
14b0faad
JJ
446/**\r
447 Process a QEMU_LOADER_ADD_POINTER command.\r
448\r
449 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.\r
450\r
451 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
452 structures created thus far.\r
453\r
454 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in\r
455 AddPointer, or the AddPointer command references\r
456 a file unknown to Tracker, or the pointer to\r
457 relocate has invalid location, size, or value, or\r
458 the relocated pointer value is not representable\r
459 in the given pointer size.\r
460\r
461 @retval EFI_SUCCESS The pointer field inside the pointer blob has\r
462 been relocated.\r
463**/\r
464STATIC\r
465EFI_STATUS\r
466EFIAPI\r
467ProcessCmdAddPointer (\r
ac0a286f
MK
468 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,\r
469 IN CONST ORDERED_COLLECTION *Tracker\r
14b0faad
JJ
470 )\r
471{\r
ac0a286f
MK
472 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;\r
473 BLOB *Blob, *Blob2;\r
474 UINT8 *PointerField;\r
475 UINT64 PointerValue;\r
476\r
477 if ((AddPointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') ||\r
478 (AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0'))\r
479 {\r
70d5086c 480 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
14b0faad
JJ
481 return EFI_PROTOCOL_ERROR;\r
482 }\r
483\r
ac0a286f 484 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);\r
14b0faad 485 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);\r
ac0a286f
MK
486 if ((TrackerEntry == NULL) || (TrackerEntry2 == NULL)) {\r
487 DEBUG ((\r
488 DEBUG_ERROR,\r
489 "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",\r
490 __FUNCTION__,\r
491 AddPointer->PointerFile,\r
492 AddPointer->PointeeFile\r
493 ));\r
14b0faad
JJ
494 return EFI_PROTOCOL_ERROR;\r
495 }\r
496\r
ac0a286f 497 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
14b0faad 498 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);\r
ac0a286f
MK
499 if (((AddPointer->PointerSize != 1) && (AddPointer->PointerSize != 2) &&\r
500 (AddPointer->PointerSize != 4) && (AddPointer->PointerSize != 8)) ||\r
501 (Blob->Size < AddPointer->PointerSize) ||\r
502 (Blob->Size - AddPointer->PointerSize < AddPointer->PointerOffset))\r
503 {\r
504 DEBUG ((\r
505 DEBUG_ERROR,\r
506 "%a: invalid pointer location or size in \"%a\"\n",\r
507 __FUNCTION__,\r
508 AddPointer->PointerFile\r
509 ));\r
14b0faad
JJ
510 return EFI_PROTOCOL_ERROR;\r
511 }\r
512\r
513 PointerField = Blob->Base + AddPointer->PointerOffset;\r
514 PointerValue = 0;\r
515 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);\r
516 if (PointerValue >= Blob2->Size) {\r
ac0a286f
MK
517 DEBUG ((\r
518 DEBUG_ERROR,\r
519 "%a: invalid pointer value in \"%a\"\n",\r
520 __FUNCTION__,\r
521 AddPointer->PointerFile\r
522 ));\r
14b0faad
JJ
523 return EFI_PROTOCOL_ERROR;\r
524 }\r
525\r
526 //\r
527 // The memory allocation system ensures that the address of the byte past the\r
528 // last byte of any allocated object is expressible (no wraparound).\r
529 //\r
530 ASSERT ((UINTN)Blob2->Base <= MAX_ADDRESS - Blob2->Size);\r
531\r
532 PointerValue += (UINT64)(UINTN)Blob2->Base;\r
ac0a286f
MK
533 if ((AddPointer->PointerSize < 8) &&\r
534 (RShiftU64 (PointerValue, AddPointer->PointerSize * 8) != 0))\r
535 {\r
536 DEBUG ((\r
537 DEBUG_ERROR,\r
538 "%a: relocated pointer value unrepresentable in "\r
539 "\"%a\"\n",\r
540 __FUNCTION__,\r
541 AddPointer->PointerFile\r
542 ));\r
14b0faad
JJ
543 return EFI_PROTOCOL_ERROR;\r
544 }\r
545\r
546 CopyMem (PointerField, &PointerValue, AddPointer->PointerSize);\r
547\r
ac0a286f
MK
548 DEBUG ((\r
549 DEBUG_VERBOSE,\r
550 "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "\r
551 "PointerOffset=0x%x PointerSize=%d\n",\r
552 __FUNCTION__,\r
553 AddPointer->PointerFile,\r
554 AddPointer->PointeeFile,\r
555 AddPointer->PointerOffset,\r
556 AddPointer->PointerSize\r
557 ));\r
14b0faad
JJ
558 return EFI_SUCCESS;\r
559}\r
560\r
14b0faad
JJ
561/**\r
562 Process a QEMU_LOADER_ADD_CHECKSUM command.\r
563\r
564 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.\r
565\r
566 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
567 structures created thus far.\r
568\r
569 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in\r
570 AddChecksum, or the AddChecksum command\r
571 references a file unknown to Tracker, or the\r
572 range to checksum is invalid.\r
573\r
574 @retval EFI_SUCCESS The requested range has been checksummed.\r
575**/\r
576STATIC\r
577EFI_STATUS\r
578EFIAPI\r
579ProcessCmdAddChecksum (\r
ac0a286f
MK
580 IN CONST QEMU_LOADER_ADD_CHECKSUM *AddChecksum,\r
581 IN CONST ORDERED_COLLECTION *Tracker\r
14b0faad
JJ
582 )\r
583{\r
ac0a286f
MK
584 ORDERED_COLLECTION_ENTRY *TrackerEntry;\r
585 BLOB *Blob;\r
14b0faad
JJ
586\r
587 if (AddChecksum->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
70d5086c 588 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
14b0faad
JJ
589 return EFI_PROTOCOL_ERROR;\r
590 }\r
591\r
592 TrackerEntry = OrderedCollectionFind (Tracker, AddChecksum->File);\r
593 if (TrackerEntry == NULL) {\r
ac0a286f
MK
594 DEBUG ((\r
595 DEBUG_ERROR,\r
596 "%a: invalid blob reference \"%a\"\n",\r
597 __FUNCTION__,\r
598 AddChecksum->File\r
599 ));\r
14b0faad
JJ
600 return EFI_PROTOCOL_ERROR;\r
601 }\r
602\r
603 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
ac0a286f
MK
604 if ((Blob->Size <= AddChecksum->ResultOffset) ||\r
605 (Blob->Size < AddChecksum->Length) ||\r
606 (Blob->Size - AddChecksum->Length < AddChecksum->Start))\r
607 {\r
608 DEBUG ((\r
609 DEBUG_ERROR,\r
610 "%a: invalid checksum range in \"%a\"\n",\r
611 __FUNCTION__,\r
612 AddChecksum->File\r
613 ));\r
14b0faad
JJ
614 return EFI_PROTOCOL_ERROR;\r
615 }\r
616\r
617 Blob->Base[AddChecksum->ResultOffset] = CalculateCheckSum8 (\r
ac0a286f
MK
618 Blob->Base + AddChecksum->Start,\r
619 AddChecksum->Length\r
620 );\r
621 DEBUG ((\r
622 DEBUG_VERBOSE,\r
623 "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "\r
624 "Length=0x%x\n",\r
625 __FUNCTION__,\r
626 AddChecksum->File,\r
627 AddChecksum->ResultOffset,\r
628 AddChecksum->Start,\r
629 AddChecksum->Length\r
630 ));\r
14b0faad
JJ
631 return EFI_SUCCESS;\r
632}\r
633\r
9965cbd4
LE
634/**\r
635 Process a QEMU_LOADER_WRITE_POINTER command.\r
636\r
637 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.\r
638\r
639 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
640 structures created thus far.\r
641\r
df73df13
LE
642 @param[in,out] S3Context The S3_CONTEXT object capturing the fw_cfg actions\r
643 of successfully processed QEMU_LOADER_WRITE_POINTER\r
644 commands, to be replayed at S3 resume. S3Context\r
645 may be NULL if S3 is disabled.\r
646\r
9965cbd4
LE
647 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in\r
648 WritePointer. Or, the WritePointer command\r
649 references a file unknown to Tracker or the\r
650 fw_cfg directory. Or, the pointer object to\r
651 rewrite has invalid location, size, or initial\r
652 relative value. Or, the pointer value to store\r
653 does not fit in the given pointer size.\r
654\r
655 @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg\r
df73df13
LE
656 file has been written. If S3Context is not NULL,\r
657 then WritePointer has been condensed into\r
658 S3Context.\r
659\r
660 @return Error codes propagated from\r
661 SaveCondensedWritePointerToS3Context(). The\r
662 pointer object inside the writeable fw_cfg file\r
663 has not been written.\r
9965cbd4
LE
664**/\r
665STATIC\r
666EFI_STATUS\r
667ProcessCmdWritePointer (\r
ac0a286f
MK
668 IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer,\r
669 IN CONST ORDERED_COLLECTION *Tracker,\r
670 IN OUT S3_CONTEXT *S3Context OPTIONAL\r
9965cbd4
LE
671 )\r
672{\r
ac0a286f
MK
673 RETURN_STATUS Status;\r
674 FIRMWARE_CONFIG_ITEM PointerItem;\r
675 UINTN PointerItemSize;\r
676 ORDERED_COLLECTION_ENTRY *PointeeEntry;\r
677 BLOB *PointeeBlob;\r
678 UINT64 PointerValue;\r
679\r
680 if ((WritePointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') ||\r
681 (WritePointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0'))\r
682 {\r
9965cbd4
LE
683 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
684 return EFI_PROTOCOL_ERROR;\r
685 }\r
686\r
ac0a286f
MK
687 Status = QemuFwCfgFindFile (\r
688 (CONST CHAR8 *)WritePointer->PointerFile,\r
689 &PointerItem,\r
690 &PointerItemSize\r
691 );\r
9965cbd4 692 PointeeEntry = OrderedCollectionFind (Tracker, WritePointer->PointeeFile);\r
ac0a286f
MK
693 if (RETURN_ERROR (Status) || (PointeeEntry == NULL)) {\r
694 DEBUG ((\r
695 DEBUG_ERROR,\r
9965cbd4 696 "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",\r
ac0a286f
MK
697 __FUNCTION__,\r
698 WritePointer->PointerFile,\r
699 WritePointer->PointeeFile\r
700 ));\r
9965cbd4
LE
701 return EFI_PROTOCOL_ERROR;\r
702 }\r
703\r
ac0a286f
MK
704 if (((WritePointer->PointerSize != 1) && (WritePointer->PointerSize != 2) &&\r
705 (WritePointer->PointerSize != 4) && (WritePointer->PointerSize != 8)) ||\r
9965cbd4
LE
706 (PointerItemSize < WritePointer->PointerSize) ||\r
707 (PointerItemSize - WritePointer->PointerSize <\r
ac0a286f
MK
708 WritePointer->PointerOffset))\r
709 {\r
710 DEBUG ((\r
711 DEBUG_ERROR,\r
712 "%a: invalid pointer location or size in \"%a\"\n",\r
713 __FUNCTION__,\r
714 WritePointer->PointerFile\r
715 ));\r
9965cbd4
LE
716 return EFI_PROTOCOL_ERROR;\r
717 }\r
718\r
ac0a286f 719 PointeeBlob = OrderedCollectionUserStruct (PointeeEntry);\r
9965cbd4
LE
720 PointerValue = WritePointer->PointeeOffset;\r
721 if (PointerValue >= PointeeBlob->Size) {\r
722 DEBUG ((DEBUG_ERROR, "%a: invalid PointeeOffset\n", __FUNCTION__));\r
723 return EFI_PROTOCOL_ERROR;\r
724 }\r
725\r
726 //\r
727 // The memory allocation system ensures that the address of the byte past the\r
728 // last byte of any allocated object is expressible (no wraparound).\r
729 //\r
730 ASSERT ((UINTN)PointeeBlob->Base <= MAX_ADDRESS - PointeeBlob->Size);\r
731\r
732 PointerValue += (UINT64)(UINTN)PointeeBlob->Base;\r
ac0a286f
MK
733 if ((WritePointer->PointerSize < 8) &&\r
734 (RShiftU64 (PointerValue, WritePointer->PointerSize * 8) != 0))\r
735 {\r
736 DEBUG ((\r
737 DEBUG_ERROR,\r
738 "%a: pointer value unrepresentable in \"%a\"\n",\r
739 __FUNCTION__,\r
740 WritePointer->PointerFile\r
741 ));\r
9965cbd4
LE
742 return EFI_PROTOCOL_ERROR;\r
743 }\r
744\r
df73df13
LE
745 //\r
746 // If S3 is enabled, we have to capture the below fw_cfg actions in condensed\r
747 // form, to be replayed during S3 resume.\r
748 //\r
749 if (S3Context != NULL) {\r
ac0a286f 750 EFI_STATUS SaveStatus;\r
df73df13
LE
751\r
752 SaveStatus = SaveCondensedWritePointerToS3Context (\r
753 S3Context,\r
754 (UINT16)PointerItem,\r
755 WritePointer->PointerSize,\r
756 WritePointer->PointerOffset,\r
757 PointerValue\r
758 );\r
759 if (EFI_ERROR (SaveStatus)) {\r
760 return SaveStatus;\r
761 }\r
762 }\r
763\r
9965cbd4
LE
764 QemuFwCfgSelectItem (PointerItem);\r
765 QemuFwCfgSkipBytes (WritePointer->PointerOffset);\r
766 QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);\r
767\r
768 //\r
769 // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob\r
770 // as unreleasable, for the case when the whole linker/loader script is\r
771 // handled successfully.\r
772 //\r
773 PointeeBlob->HostsOnlyTableData = FALSE;\r
774\r
ac0a286f
MK
775 DEBUG ((\r
776 DEBUG_VERBOSE,\r
777 "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "\r
778 "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n",\r
779 __FUNCTION__,\r
780 WritePointer->PointerFile,\r
781 WritePointer->PointeeFile,\r
782 WritePointer->PointerOffset,\r
783 WritePointer->PointeeOffset,\r
784 WritePointer->PointerSize\r
785 ));\r
9965cbd4
LE
786 return EFI_SUCCESS;\r
787}\r
788\r
9965cbd4
LE
789/**\r
790 Undo a QEMU_LOADER_WRITE_POINTER command.\r
791\r
792 This function revokes (zeroes out) a guest memory reference communicated to\r
793 QEMU earlier. The caller is responsible for invoking this function only on\r
794 such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed\r
795 by ProcessCmdWritePointer().\r
796\r
797 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.\r
798**/\r
799STATIC\r
800VOID\r
801UndoCmdWritePointer (\r
ac0a286f 802 IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer\r
9965cbd4
LE
803 )\r
804{\r
ac0a286f
MK
805 RETURN_STATUS Status;\r
806 FIRMWARE_CONFIG_ITEM PointerItem;\r
807 UINTN PointerItemSize;\r
808 UINT64 PointerValue;\r
809\r
810 Status = QemuFwCfgFindFile (\r
811 (CONST CHAR8 *)WritePointer->PointerFile,\r
812 &PointerItem,\r
813 &PointerItemSize\r
814 );\r
9965cbd4
LE
815 ASSERT_RETURN_ERROR (Status);\r
816\r
817 PointerValue = 0;\r
818 QemuFwCfgSelectItem (PointerItem);\r
819 QemuFwCfgSkipBytes (WritePointer->PointerOffset);\r
820 QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);\r
821\r
ac0a286f
MK
822 DEBUG ((\r
823 DEBUG_VERBOSE,\r
824 "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n",\r
825 __FUNCTION__,\r
826 WritePointer->PointerFile,\r
827 WritePointer->PointerOffset,\r
828 WritePointer->PointerSize\r
829 ));\r
9965cbd4
LE
830}\r
831\r
14b0faad
JJ
832//\r
833// We'll be saving the keys of installed tables so that we can roll them back\r
834// in case of failure. 128 tables should be enough for anyone (TM).\r
835//\r
ac0a286f 836#define INSTALLED_TABLES_MAX 128\r
14b0faad
JJ
837\r
838/**\r
839 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte\r
840 array is an ACPI table, and if so, install it.\r
841\r
842 This function assumes that the entire QEMU linker/loader command file has\r
8c0b0b34 843 been processed successfully in a prior first pass.\r
14b0faad
JJ
844\r
845 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.\r
846\r
847 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
848 structures.\r
849\r
850 @param[in] AcpiProtocol The ACPI table protocol used to install tables.\r
851\r
852 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN\r
853 elements, allocated by the caller. On output,\r
854 the function will have stored (appended) the\r
855 AcpiProtocol-internal key of the ACPI table that\r
856 the function has installed, if the AddPointer\r
857 command identified an ACPI table that is\r
858 different from RSDT and XSDT.\r
859\r
860 @param[in,out] NumInstalled On input, the number of entries already used in\r
861 InstalledKey; it must be in [0,\r
862 INSTALLED_TABLES_MAX] inclusive. On output, the\r
863 parameter is incremented if the AddPointer\r
864 command identified an ACPI table that is\r
865 different from RSDT and XSDT.\r
866\r
072060a6
PDJ
867 @param[in,out] SeenPointers The ORDERED_COLLECTION tracking the absolute\r
868 target addresses that have been pointed-to by\r
869 QEMU_LOADER_ADD_POINTER commands thus far. If a\r
870 target address is encountered for the first\r
871 time, and it identifies an ACPI table that is\r
872 different from RDST and XSDT, the table is\r
873 installed. If a target address is seen for the\r
874 second or later times, it is skipped without\r
875 taking any action.\r
876\r
14b0faad
JJ
877 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on\r
878 input.\r
879\r
880 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI\r
881 table different from RSDT and XSDT, but there\r
882 was no more room in InstalledKey.\r
883\r
072060a6
PDJ
884 @retval EFI_SUCCESS AddPointer has been processed. Either its\r
885 absolute target address has been encountered\r
886 before, or an ACPI table different from RSDT\r
887 and XSDT has been installed (reflected by\r
888 InstalledKey and NumInstalled), or RSDT or\r
889 XSDT has been identified but not installed, or\r
890 the fw_cfg blob pointed-into by AddPointer has\r
891 been marked as hosting something else than\r
892 just direct ACPI table contents.\r
14b0faad
JJ
893\r
894 @return Error codes returned by\r
895 AcpiProtocol->InstallAcpiTable().\r
896**/\r
897STATIC\r
898EFI_STATUS\r
899EFIAPI\r
900Process2ndPassCmdAddPointer (\r
ac0a286f
MK
901 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,\r
902 IN CONST ORDERED_COLLECTION *Tracker,\r
903 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
904 IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX],\r
905 IN OUT INT32 *NumInstalled,\r
906 IN OUT ORDERED_COLLECTION *SeenPointers\r
14b0faad
JJ
907 )\r
908{\r
ac0a286f
MK
909 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry;\r
910 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry2;\r
911 ORDERED_COLLECTION_ENTRY *SeenPointerEntry;\r
912 CONST BLOB *Blob;\r
913 BLOB *Blob2;\r
914 CONST UINT8 *PointerField;\r
915 UINT64 PointerValue;\r
916 UINTN Blob2Remaining;\r
917 UINTN TableSize;\r
918 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;\r
919 CONST EFI_ACPI_DESCRIPTION_HEADER *Header;\r
920 EFI_STATUS Status;\r
921\r
922 if ((*NumInstalled < 0) || (*NumInstalled > INSTALLED_TABLES_MAX)) {\r
14b0faad
JJ
923 return EFI_INVALID_PARAMETER;\r
924 }\r
925\r
ac0a286f 926 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);\r
14b0faad 927 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);\r
ac0a286f
MK
928 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
929 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);\r
930 PointerField = Blob->Base + AddPointer->PointerOffset;\r
931 PointerValue = 0;\r
14b0faad
JJ
932 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);\r
933\r
934 //\r
935 // We assert that PointerValue falls inside Blob2's contents. This is ensured\r
936 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().\r
937 //\r
938 Blob2Remaining = (UINTN)Blob2->Base;\r
ac0a286f 939 ASSERT (PointerValue >= Blob2Remaining);\r
14b0faad
JJ
940 Blob2Remaining += Blob2->Size;\r
941 ASSERT (PointerValue < Blob2Remaining);\r
942\r
072060a6
PDJ
943 Status = OrderedCollectionInsert (\r
944 SeenPointers,\r
945 &SeenPointerEntry, // for reverting insertion in error case\r
946 (VOID *)(UINTN)PointerValue\r
947 );\r
948 if (EFI_ERROR (Status)) {\r
949 if (Status == RETURN_ALREADY_STARTED) {\r
950 //\r
951 // Already seen this pointer, don't try to process it again.\r
952 //\r
953 DEBUG ((\r
954 DEBUG_VERBOSE,\r
955 "%a: PointerValue=0x%Lx already processed, skipping.\n",\r
956 __FUNCTION__,\r
957 PointerValue\r
958 ));\r
959 Status = EFI_SUCCESS;\r
960 }\r
ac0a286f 961\r
072060a6
PDJ
962 return Status;\r
963 }\r
964\r
ac0a286f
MK
965 Blob2Remaining -= (UINTN)PointerValue;\r
966 DEBUG ((\r
967 DEBUG_VERBOSE,\r
968 "%a: checking for ACPI header in \"%a\" at 0x%Lx "\r
969 "(remaining: 0x%Lx): ",\r
970 __FUNCTION__,\r
971 AddPointer->PointeeFile,\r
972 PointerValue,\r
973 (UINT64)Blob2Remaining\r
974 ));\r
14b0faad
JJ
975\r
976 TableSize = 0;\r
977\r
978 //\r
979 // To make our job simple, the FACS has a custom header. Sigh.\r
980 //\r
981 if (sizeof *Facs <= Blob2Remaining) {\r
982 Facs = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)PointerValue;\r
983\r
ac0a286f
MK
984 if ((Facs->Length >= sizeof *Facs) &&\r
985 (Facs->Length <= Blob2Remaining) &&\r
986 (Facs->Signature ==\r
987 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE))\r
988 {\r
989 DEBUG ((\r
990 DEBUG_VERBOSE,\r
991 "found \"%-4.4a\" size 0x%x\n",\r
992 (CONST CHAR8 *)&Facs->Signature,\r
993 Facs->Length\r
994 ));\r
14b0faad
JJ
995 TableSize = Facs->Length;\r
996 }\r
997 }\r
998\r
999 //\r
1000 // check for the uniform tables\r
1001 //\r
ac0a286f 1002 if ((TableSize == 0) && (sizeof *Header <= Blob2Remaining)) {\r
14b0faad
JJ
1003 Header = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)PointerValue;\r
1004\r
ac0a286f
MK
1005 if ((Header->Length >= sizeof *Header) &&\r
1006 (Header->Length <= Blob2Remaining) &&\r
1007 (CalculateSum8 ((CONST UINT8 *)Header, Header->Length) == 0))\r
1008 {\r
14b0faad
JJ
1009 //\r
1010 // This looks very much like an ACPI table from QEMU:\r
1011 // - Length field consistent with both ACPI and containing blob size\r
1012 // - checksum is correct\r
1013 //\r
ac0a286f
MK
1014 DEBUG ((\r
1015 DEBUG_VERBOSE,\r
1016 "found \"%-4.4a\" size 0x%x\n",\r
1017 (CONST CHAR8 *)&Header->Signature,\r
1018 Header->Length\r
1019 ));\r
14b0faad
JJ
1020 TableSize = Header->Length;\r
1021\r
1022 //\r
1023 // Skip RSDT and XSDT because those are handled by\r
1024 // EFI_ACPI_TABLE_PROTOCOL automatically.\r
ac0a286f
MK
1025 if ((Header->Signature ==\r
1026 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) ||\r
1027 (Header->Signature ==\r
1028 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE))\r
1029 {\r
14b0faad
JJ
1030 return EFI_SUCCESS;\r
1031 }\r
1032 }\r
1033 }\r
1034\r
1035 if (TableSize == 0) {\r
70d5086c 1036 DEBUG ((DEBUG_VERBOSE, "not found; marking fw_cfg blob as opaque\n"));\r
14b0faad
JJ
1037 Blob2->HostsOnlyTableData = FALSE;\r
1038 return EFI_SUCCESS;\r
1039 }\r
1040\r
1041 if (*NumInstalled == INSTALLED_TABLES_MAX) {\r
ac0a286f
MK
1042 DEBUG ((\r
1043 DEBUG_ERROR,\r
1044 "%a: can't install more than %d tables\n",\r
1045 __FUNCTION__,\r
1046 INSTALLED_TABLES_MAX\r
1047 ));\r
072060a6
PDJ
1048 Status = EFI_OUT_OF_RESOURCES;\r
1049 goto RollbackSeenPointer;\r
14b0faad
JJ
1050 }\r
1051\r
ac0a286f
MK
1052 Status = AcpiProtocol->InstallAcpiTable (\r
1053 AcpiProtocol,\r
1054 (VOID *)(UINTN)PointerValue,\r
1055 TableSize,\r
1056 &InstalledKey[*NumInstalled]\r
1057 );\r
14b0faad 1058 if (EFI_ERROR (Status)) {\r
ac0a286f
MK
1059 DEBUG ((\r
1060 DEBUG_ERROR,\r
1061 "%a: InstallAcpiTable(): %r\n",\r
1062 __FUNCTION__,\r
1063 Status\r
1064 ));\r
072060a6 1065 goto RollbackSeenPointer;\r
14b0faad 1066 }\r
ac0a286f 1067\r
14b0faad
JJ
1068 ++*NumInstalled;\r
1069 return EFI_SUCCESS;\r
072060a6
PDJ
1070\r
1071RollbackSeenPointer:\r
1072 OrderedCollectionDelete (SeenPointers, SeenPointerEntry, NULL);\r
1073 return Status;\r
14b0faad
JJ
1074}\r
1075\r
14b0faad
JJ
1076/**\r
1077 Download, process, and install ACPI table data from the QEMU loader\r
1078 interface.\r
1079\r
1080 @param[in] AcpiProtocol The ACPI table protocol used to install tables.\r
1081\r
1082 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU\r
1083 loader command with unsupported parameters\r
1084 has been found.\r
1085\r
1086 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg\r
1087 files.\r
1088\r
1089 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than\r
1090 INSTALLED_TABLES_MAX tables found.\r
1091\r
1092 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.\r
1093\r
1094 @return Status codes returned by\r
1095 AcpiProtocol->InstallAcpiTable().\r
1096\r
1097**/\r
1098EFI_STATUS\r
1099EFIAPI\r
f186536b 1100InstallQemuFwCfgTables (\r
ac0a286f 1101 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol\r
14b0faad
JJ
1102 )\r
1103{\r
ac0a286f
MK
1104 EFI_STATUS Status;\r
1105 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
1106 UINTN FwCfgSize;\r
1107 QEMU_LOADER_ENTRY *LoaderStart;\r
1108 CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd;\r
1109 CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd;\r
1110 ORIGINAL_ATTRIBUTES *OriginalPciAttributes;\r
1111 UINTN OriginalPciAttributesCount;\r
1112 ORDERED_COLLECTION *AllocationsRestrictedTo32Bit;\r
1113 S3_CONTEXT *S3Context;\r
1114 ORDERED_COLLECTION *Tracker;\r
1115 UINTN *InstalledKey;\r
1116 INT32 Installed;\r
1117 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;\r
1118 ORDERED_COLLECTION *SeenPointers;\r
1119 ORDERED_COLLECTION_ENTRY *SeenPointerEntry, *SeenPointerEntry2;\r
165f1e49 1120 EFI_HANDLE QemuAcpiHandle;\r
14b0faad
JJ
1121\r
1122 Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize);\r
1123 if (EFI_ERROR (Status)) {\r
1124 return Status;\r
1125 }\r
ac0a286f 1126\r
14b0faad 1127 if (FwCfgSize % sizeof *LoaderEntry != 0) {\r
ac0a286f
MK
1128 DEBUG ((\r
1129 DEBUG_ERROR,\r
1130 "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",\r
1131 __FUNCTION__,\r
1132 (UINT64)FwCfgSize\r
1133 ));\r
14b0faad
JJ
1134 return EFI_PROTOCOL_ERROR;\r
1135 }\r
1136\r
1137 LoaderStart = AllocatePool (FwCfgSize);\r
1138 if (LoaderStart == NULL) {\r
1139 return EFI_OUT_OF_RESOURCES;\r
1140 }\r
ac0a286f 1141\r
8f35eb92 1142 EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount);\r
14b0faad
JJ
1143 QemuFwCfgSelectItem (FwCfgItem);\r
1144 QemuFwCfgReadBytes (FwCfgSize, LoaderStart);\r
8f35eb92 1145 RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount);\r
935343cf
MX
1146\r
1147 //\r
1148 // Measure the "etc/table-loader" which is downloaded from QEMU.\r
1149 // It has to be done before it is consumed. Because it would be\r
1150 // updated in the following operations.\r
1151 //\r
1152 TpmMeasureAndLogData (\r
1153 1,\r
1154 EV_PLATFORM_CONFIG_FLAGS,\r
1155 EV_POSTCODE_INFO_ACPI_DATA,\r
1156 ACPI_DATA_LEN,\r
1157 (VOID *)(UINTN)LoaderStart,\r
1158 FwCfgSize\r
1159 );\r
1160\r
14b0faad
JJ
1161 LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry;\r
1162\r
8f98c76f 1163 AllocationsRestrictedTo32Bit = NULL;\r
ac0a286f
MK
1164 Status = CollectAllocationsRestrictedTo32Bit (\r
1165 &AllocationsRestrictedTo32Bit,\r
1166 LoaderStart,\r
1167 LoaderEnd\r
1168 );\r
4275f385
LE
1169 if (EFI_ERROR (Status)) {\r
1170 goto FreeLoader;\r
1171 }\r
1172\r
df73df13
LE
1173 S3Context = NULL;\r
1174 if (QemuFwCfgS3Enabled ()) {\r
1175 //\r
1176 // Size the allocation pessimistically, assuming that all commands in the\r
1177 // script are QEMU_LOADER_WRITE_POINTER commands.\r
1178 //\r
1179 Status = AllocateS3Context (&S3Context, LoaderEnd - LoaderStart);\r
1180 if (EFI_ERROR (Status)) {\r
4275f385 1181 goto FreeAllocationsRestrictedTo32Bit;\r
df73df13
LE
1182 }\r
1183 }\r
1184\r
14b0faad
JJ
1185 Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare);\r
1186 if (Tracker == NULL) {\r
1187 Status = EFI_OUT_OF_RESOURCES;\r
df73df13 1188 goto FreeS3Context;\r
14b0faad
JJ
1189 }\r
1190\r
1191 //\r
1192 // first pass: process the commands\r
1193 //\r
9965cbd4
LE
1194 // "WritePointerSubsetEnd" points one past the last successful\r
1195 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first\r
1196 // pass, no such command has been encountered yet.\r
1197 //\r
1198 WritePointerSubsetEnd = LoaderStart;\r
14b0faad
JJ
1199 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {\r
1200 switch (LoaderEntry->Type) {\r
ac0a286f
MK
1201 case QemuLoaderCmdAllocate:\r
1202 Status = ProcessCmdAllocate (\r
1203 &LoaderEntry->Command.Allocate,\r
1204 Tracker,\r
1205 AllocationsRestrictedTo32Bit\r
1206 );\r
1207 break;\r
14b0faad 1208\r
ac0a286f
MK
1209 case QemuLoaderCmdAddPointer:\r
1210 Status = ProcessCmdAddPointer (\r
1211 &LoaderEntry->Command.AddPointer,\r
1212 Tracker\r
1213 );\r
1214 break;\r
14b0faad 1215\r
ac0a286f
MK
1216 case QemuLoaderCmdAddChecksum:\r
1217 Status = ProcessCmdAddChecksum (\r
1218 &LoaderEntry->Command.AddChecksum,\r
1219 Tracker\r
1220 );\r
1221 break;\r
14b0faad 1222\r
ac0a286f
MK
1223 case QemuLoaderCmdWritePointer:\r
1224 Status = ProcessCmdWritePointer (\r
1225 &LoaderEntry->Command.WritePointer,\r
1226 Tracker,\r
1227 S3Context\r
1228 );\r
9965cbd4
LE
1229 if (!EFI_ERROR (Status)) {\r
1230 WritePointerSubsetEnd = LoaderEntry + 1;\r
1231 }\r
ac0a286f 1232\r
9965cbd4
LE
1233 break;\r
1234\r
ac0a286f
MK
1235 default:\r
1236 DEBUG ((\r
1237 DEBUG_VERBOSE,\r
1238 "%a: unknown loader command: 0x%x\n",\r
1239 __FUNCTION__,\r
1240 LoaderEntry->Type\r
1241 ));\r
1242 break;\r
14b0faad
JJ
1243 }\r
1244\r
1245 if (EFI_ERROR (Status)) {\r
9965cbd4 1246 goto RollbackWritePointersAndFreeTracker;\r
14b0faad
JJ
1247 }\r
1248 }\r
1249\r
1250 InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);\r
1251 if (InstalledKey == NULL) {\r
1252 Status = EFI_OUT_OF_RESOURCES;\r
9965cbd4 1253 goto RollbackWritePointersAndFreeTracker;\r
14b0faad
JJ
1254 }\r
1255\r
072060a6
PDJ
1256 SeenPointers = OrderedCollectionInit (PointerCompare, PointerCompare);\r
1257 if (SeenPointers == NULL) {\r
1258 Status = EFI_OUT_OF_RESOURCES;\r
1259 goto FreeKeys;\r
1260 }\r
1261\r
14b0faad
JJ
1262 //\r
1263 // second pass: identify and install ACPI tables\r
1264 //\r
1265 Installed = 0;\r
1266 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {\r
1267 if (LoaderEntry->Type == QemuLoaderCmdAddPointer) {\r
072060a6
PDJ
1268 Status = Process2ndPassCmdAddPointer (\r
1269 &LoaderEntry->Command.AddPointer,\r
1270 Tracker,\r
1271 AcpiProtocol,\r
1272 InstalledKey,\r
1273 &Installed,\r
1274 SeenPointers\r
1275 );\r
14b0faad 1276 if (EFI_ERROR (Status)) {\r
df73df13 1277 goto UninstallAcpiTables;\r
14b0faad
JJ
1278 }\r
1279 }\r
1280 }\r
1281\r
66f18fde
MX
1282 //\r
1283 // Install a protocol to notify that the ACPI table provided by Qemu is\r
1284 // ready.\r
1285 //\r
1286 QemuAcpiHandle = NULL;\r
1287 Status = gBS->InstallProtocolInterface (\r
1288 &QemuAcpiHandle,\r
1289 &gQemuAcpiTableNotifyProtocolGuid,\r
1290 EFI_NATIVE_INTERFACE,\r
1291 NULL\r
1292 );\r
1293 if (EFI_ERROR (Status)) {\r
1294 goto UninstallAcpiTables;\r
1295 }\r
1296\r
df73df13
LE
1297 //\r
1298 // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3\r
1299 // Boot Script opcodes has to be the last operation in this function, because\r
1300 // if it succeeds, it cannot be undone.\r
1301 //\r
1302 if (S3Context != NULL) {\r
1303 Status = TransferS3ContextToBootScript (S3Context);\r
80576225 1304 if (EFI_ERROR (Status)) {\r
66f18fde 1305 goto UninstallQemuAcpiTableNotifyProtocol;\r
80576225 1306 }\r
ac0a286f 1307\r
80576225 1308 //\r
a2e75595 1309 // Ownership of S3Context has been transferred.\r
80576225
LE
1310 //\r
1311 S3Context = NULL;\r
df73df13
LE
1312 }\r
1313\r
2ef0ff39
MX
1314 DEBUG ((DEBUG_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));\r
1315\r
66f18fde
MX
1316UninstallQemuAcpiTableNotifyProtocol:\r
1317 if (EFI_ERROR (Status)) {\r
1318 gBS->UninstallProtocolInterface (\r
1319 QemuAcpiHandle,\r
1320 &gQemuAcpiTableNotifyProtocolGuid,\r
1321 NULL\r
1322 );\r
1323 }\r
1324\r
df73df13 1325UninstallAcpiTables:\r
14b0faad
JJ
1326 if (EFI_ERROR (Status)) {\r
1327 //\r
1328 // roll back partial installation\r
1329 //\r
1330 while (Installed > 0) {\r
1331 --Installed;\r
1332 AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);\r
1333 }\r
14b0faad
JJ
1334 }\r
1335\r
072060a6
PDJ
1336 for (SeenPointerEntry = OrderedCollectionMin (SeenPointers);\r
1337 SeenPointerEntry != NULL;\r
ac0a286f
MK
1338 SeenPointerEntry = SeenPointerEntry2)\r
1339 {\r
072060a6
PDJ
1340 SeenPointerEntry2 = OrderedCollectionNext (SeenPointerEntry);\r
1341 OrderedCollectionDelete (SeenPointers, SeenPointerEntry, NULL);\r
1342 }\r
ac0a286f 1343\r
072060a6
PDJ
1344 OrderedCollectionUninit (SeenPointers);\r
1345\r
1346FreeKeys:\r
14b0faad
JJ
1347 FreePool (InstalledKey);\r
1348\r
9965cbd4
LE
1349RollbackWritePointersAndFreeTracker:\r
1350 //\r
1351 // In case of failure, revoke any allocation addresses that were communicated\r
1352 // to QEMU previously, before we release all the blobs.\r
1353 //\r
1354 if (EFI_ERROR (Status)) {\r
1355 LoaderEntry = WritePointerSubsetEnd;\r
1356 while (LoaderEntry > LoaderStart) {\r
1357 --LoaderEntry;\r
1358 if (LoaderEntry->Type == QemuLoaderCmdWritePointer) {\r
1359 UndoCmdWritePointer (&LoaderEntry->Command.WritePointer);\r
1360 }\r
1361 }\r
1362 }\r
1363\r
14b0faad
JJ
1364 //\r
1365 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in\r
1366 // place only if we're exiting with success and the blob hosts data that is\r
1367 // not directly part of some ACPI table.\r
1368 //\r
1369 for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL;\r
ac0a286f
MK
1370 TrackerEntry = TrackerEntry2)\r
1371 {\r
1372 VOID *UserStruct;\r
1373 BLOB *Blob;\r
14b0faad
JJ
1374\r
1375 TrackerEntry2 = OrderedCollectionNext (TrackerEntry);\r
1376 OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct);\r
1377 Blob = UserStruct;\r
1378\r
1379 if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) {\r
ac0a286f
MK
1380 DEBUG ((\r
1381 DEBUG_VERBOSE,\r
1382 "%a: freeing \"%a\"\n",\r
1383 __FUNCTION__,\r
1384 Blob->File\r
1385 ));\r
14b0faad
JJ
1386 gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size));\r
1387 }\r
ac0a286f 1388\r
14b0faad
JJ
1389 FreePool (Blob);\r
1390 }\r
ac0a286f 1391\r
14b0faad
JJ
1392 OrderedCollectionUninit (Tracker);\r
1393\r
df73df13
LE
1394FreeS3Context:\r
1395 if (S3Context != NULL) {\r
1396 ReleaseS3Context (S3Context);\r
1397 }\r
1398\r
4275f385
LE
1399FreeAllocationsRestrictedTo32Bit:\r
1400 ReleaseAllocationsRestrictedTo32Bit (AllocationsRestrictedTo32Bit);\r
1401\r
14b0faad
JJ
1402FreeLoader:\r
1403 FreePool (LoaderStart);\r
1404\r
1405 return Status;\r
1406}\r