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