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