]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
f0d81d6fd73da8b27576433909c57a0616698d34
[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 EFI_HANDLE QemuAcpiHandle;
1104
1105 Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize);
1106 if (EFI_ERROR (Status)) {
1107 return Status;
1108 }
1109
1110 if (FwCfgSize % sizeof *LoaderEntry != 0) {
1111 DEBUG ((
1112 DEBUG_ERROR,
1113 "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
1114 __FUNCTION__,
1115 (UINT64)FwCfgSize
1116 ));
1117 return EFI_PROTOCOL_ERROR;
1118 }
1119
1120 LoaderStart = AllocatePool (FwCfgSize);
1121 if (LoaderStart == NULL) {
1122 return EFI_OUT_OF_RESOURCES;
1123 }
1124
1125 EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount);
1126 QemuFwCfgSelectItem (FwCfgItem);
1127 QemuFwCfgReadBytes (FwCfgSize, LoaderStart);
1128 RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount);
1129 LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry;
1130
1131 AllocationsRestrictedTo32Bit = NULL;
1132 Status = CollectAllocationsRestrictedTo32Bit (
1133 &AllocationsRestrictedTo32Bit,
1134 LoaderStart,
1135 LoaderEnd
1136 );
1137 if (EFI_ERROR (Status)) {
1138 goto FreeLoader;
1139 }
1140
1141 S3Context = NULL;
1142 if (QemuFwCfgS3Enabled ()) {
1143 //
1144 // Size the allocation pessimistically, assuming that all commands in the
1145 // script are QEMU_LOADER_WRITE_POINTER commands.
1146 //
1147 Status = AllocateS3Context (&S3Context, LoaderEnd - LoaderStart);
1148 if (EFI_ERROR (Status)) {
1149 goto FreeAllocationsRestrictedTo32Bit;
1150 }
1151 }
1152
1153 Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare);
1154 if (Tracker == NULL) {
1155 Status = EFI_OUT_OF_RESOURCES;
1156 goto FreeS3Context;
1157 }
1158
1159 //
1160 // first pass: process the commands
1161 //
1162 // "WritePointerSubsetEnd" points one past the last successful
1163 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
1164 // pass, no such command has been encountered yet.
1165 //
1166 WritePointerSubsetEnd = LoaderStart;
1167 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
1168 switch (LoaderEntry->Type) {
1169 case QemuLoaderCmdAllocate:
1170 Status = ProcessCmdAllocate (
1171 &LoaderEntry->Command.Allocate,
1172 Tracker,
1173 AllocationsRestrictedTo32Bit
1174 );
1175 break;
1176
1177 case QemuLoaderCmdAddPointer:
1178 Status = ProcessCmdAddPointer (
1179 &LoaderEntry->Command.AddPointer,
1180 Tracker
1181 );
1182 break;
1183
1184 case QemuLoaderCmdAddChecksum:
1185 Status = ProcessCmdAddChecksum (
1186 &LoaderEntry->Command.AddChecksum,
1187 Tracker
1188 );
1189 break;
1190
1191 case QemuLoaderCmdWritePointer:
1192 Status = ProcessCmdWritePointer (
1193 &LoaderEntry->Command.WritePointer,
1194 Tracker,
1195 S3Context
1196 );
1197 if (!EFI_ERROR (Status)) {
1198 WritePointerSubsetEnd = LoaderEntry + 1;
1199 }
1200
1201 break;
1202
1203 default:
1204 DEBUG ((
1205 DEBUG_VERBOSE,
1206 "%a: unknown loader command: 0x%x\n",
1207 __FUNCTION__,
1208 LoaderEntry->Type
1209 ));
1210 break;
1211 }
1212
1213 if (EFI_ERROR (Status)) {
1214 goto RollbackWritePointersAndFreeTracker;
1215 }
1216 }
1217
1218 InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);
1219 if (InstalledKey == NULL) {
1220 Status = EFI_OUT_OF_RESOURCES;
1221 goto RollbackWritePointersAndFreeTracker;
1222 }
1223
1224 SeenPointers = OrderedCollectionInit (PointerCompare, PointerCompare);
1225 if (SeenPointers == NULL) {
1226 Status = EFI_OUT_OF_RESOURCES;
1227 goto FreeKeys;
1228 }
1229
1230 //
1231 // second pass: identify and install ACPI tables
1232 //
1233 Installed = 0;
1234 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
1235 if (LoaderEntry->Type == QemuLoaderCmdAddPointer) {
1236 Status = Process2ndPassCmdAddPointer (
1237 &LoaderEntry->Command.AddPointer,
1238 Tracker,
1239 AcpiProtocol,
1240 InstalledKey,
1241 &Installed,
1242 SeenPointers
1243 );
1244 if (EFI_ERROR (Status)) {
1245 goto UninstallAcpiTables;
1246 }
1247 }
1248 }
1249
1250 //
1251 // Install a protocol to notify that the ACPI table provided by Qemu is
1252 // ready.
1253 //
1254 QemuAcpiHandle = NULL;
1255 Status = gBS->InstallProtocolInterface (
1256 &QemuAcpiHandle,
1257 &gQemuAcpiTableNotifyProtocolGuid,
1258 EFI_NATIVE_INTERFACE,
1259 NULL
1260 );
1261 if (EFI_ERROR (Status)) {
1262 goto UninstallAcpiTables;
1263 }
1264
1265 //
1266 // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3
1267 // Boot Script opcodes has to be the last operation in this function, because
1268 // if it succeeds, it cannot be undone.
1269 //
1270 if (S3Context != NULL) {
1271 Status = TransferS3ContextToBootScript (S3Context);
1272 if (EFI_ERROR (Status)) {
1273 goto UninstallQemuAcpiTableNotifyProtocol;
1274 }
1275
1276 //
1277 // Ownership of S3Context has been transferred.
1278 //
1279 S3Context = NULL;
1280 }
1281
1282 DEBUG ((DEBUG_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));
1283
1284 UninstallQemuAcpiTableNotifyProtocol:
1285 if (EFI_ERROR (Status)) {
1286 gBS->UninstallProtocolInterface (
1287 QemuAcpiHandle,
1288 &gQemuAcpiTableNotifyProtocolGuid,
1289 NULL
1290 );
1291 }
1292
1293 UninstallAcpiTables:
1294 if (EFI_ERROR (Status)) {
1295 //
1296 // roll back partial installation
1297 //
1298 while (Installed > 0) {
1299 --Installed;
1300 AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);
1301 }
1302 }
1303
1304 for (SeenPointerEntry = OrderedCollectionMin (SeenPointers);
1305 SeenPointerEntry != NULL;
1306 SeenPointerEntry = SeenPointerEntry2)
1307 {
1308 SeenPointerEntry2 = OrderedCollectionNext (SeenPointerEntry);
1309 OrderedCollectionDelete (SeenPointers, SeenPointerEntry, NULL);
1310 }
1311
1312 OrderedCollectionUninit (SeenPointers);
1313
1314 FreeKeys:
1315 FreePool (InstalledKey);
1316
1317 RollbackWritePointersAndFreeTracker:
1318 //
1319 // In case of failure, revoke any allocation addresses that were communicated
1320 // to QEMU previously, before we release all the blobs.
1321 //
1322 if (EFI_ERROR (Status)) {
1323 LoaderEntry = WritePointerSubsetEnd;
1324 while (LoaderEntry > LoaderStart) {
1325 --LoaderEntry;
1326 if (LoaderEntry->Type == QemuLoaderCmdWritePointer) {
1327 UndoCmdWritePointer (&LoaderEntry->Command.WritePointer);
1328 }
1329 }
1330 }
1331
1332 //
1333 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
1334 // place only if we're exiting with success and the blob hosts data that is
1335 // not directly part of some ACPI table.
1336 //
1337 for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL;
1338 TrackerEntry = TrackerEntry2)
1339 {
1340 VOID *UserStruct;
1341 BLOB *Blob;
1342
1343 TrackerEntry2 = OrderedCollectionNext (TrackerEntry);
1344 OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct);
1345 Blob = UserStruct;
1346
1347 if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) {
1348 DEBUG ((
1349 DEBUG_VERBOSE,
1350 "%a: freeing \"%a\"\n",
1351 __FUNCTION__,
1352 Blob->File
1353 ));
1354 gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size));
1355 }
1356
1357 FreePool (Blob);
1358 }
1359
1360 OrderedCollectionUninit (Tracker);
1361
1362 FreeS3Context:
1363 if (S3Context != NULL) {
1364 ReleaseS3Context (S3Context);
1365 }
1366
1367 FreeAllocationsRestrictedTo32Bit:
1368 ReleaseAllocationsRestrictedTo32Bit (AllocationsRestrictedTo32Bit);
1369
1370 FreeLoader:
1371 FreePool (LoaderStart);
1372
1373 return Status;
1374 }