2 OVMF ACPI support using QEMU's fw-cfg interface
4 Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>
5 Copyright (C) 2012-2014, Red Hat, Inc.
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 #include "AcpiPlatform.h"
18 #include "QemuLoader.h"
19 #include <Library/BaseMemoryLib.h>
20 #include <Library/MemoryAllocationLib.h>
21 #include <Library/QemuFwCfgLib.h>
22 #include <Library/DxeServicesTableLib.h>
23 #include <Library/PcdLib.h>
24 #include <Library/OrderedCollectionLib.h>
25 #include <IndustryStandard/Acpi.h>
29 // The user structure for the ordered collection that will track the fw_cfg
30 // blobs under processing.
33 UINT8 File
[QEMU_LOADER_FNAME_SIZE
]; // NUL-terminated name of the fw_cfg
34 // blob. This is the ordering / search
36 UINTN Size
; // The number of bytes in this blob.
37 UINT8
*Base
; // Pointer to the blob data.
38 BOOLEAN HostsOnlyTableData
; // TRUE iff the blob has been found to
39 // only contain data that is directly
40 // part of ACPI tables.
45 Compare a standalone key against a user structure containing an embedded key.
47 @param[in] StandaloneKey Pointer to the bare key.
49 @param[in] UserStruct Pointer to the user structure with the embedded
52 @retval <0 If StandaloneKey compares less than UserStruct's key.
54 @retval 0 If StandaloneKey compares equal to UserStruct's key.
56 @retval >0 If StandaloneKey compares greater than UserStruct's key.
62 IN CONST VOID
*StandaloneKey
,
63 IN CONST VOID
*UserStruct
69 return AsciiStrCmp (StandaloneKey
, (CONST CHAR8
*)Blob
->File
);
74 Comparator function for two user structures.
76 @param[in] UserStruct1 Pointer to the first user structure.
78 @param[in] UserStruct2 Pointer to the second user structure.
80 @retval <0 If UserStruct1 compares less than UserStruct2.
82 @retval 0 If UserStruct1 compares equal to UserStruct2.
84 @retval >0 If UserStruct1 compares greater than UserStruct2.
90 IN CONST VOID
*UserStruct1
,
91 IN CONST VOID
*UserStruct2
97 return BlobKeyCompare (Blob1
->File
, UserStruct2
);
102 Process a QEMU_LOADER_ALLOCATE command.
104 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to process.
106 @param[in,out] Tracker The ORDERED_COLLECTION tracking the BLOB user
107 structures created thus far.
109 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been
110 allocated for the blob contents, and the
111 contents have been saved. A BLOB object (user
112 structure) has been allocated from pool memory,
113 referencing the blob contents. The BLOB user
114 structure has been linked into Tracker.
116 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
117 Allocate, or the Allocate command references a
118 file that is already known by Tracker.
120 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in
123 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.
125 @return Error codes from QemuFwCfgFindFile() and
126 gBS->AllocatePages().
132 IN CONST QEMU_LOADER_ALLOCATE
*Allocate
,
133 IN OUT ORDERED_COLLECTION
*Tracker
136 FIRMWARE_CONFIG_ITEM FwCfgItem
;
140 EFI_PHYSICAL_ADDRESS Address
;
143 if (Allocate
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
144 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
145 return EFI_PROTOCOL_ERROR
;
148 if (Allocate
->Alignment
> EFI_PAGE_SIZE
) {
149 DEBUG ((EFI_D_ERROR
, "%a: unsupported alignment 0x%x\n", __FUNCTION__
,
150 Allocate
->Alignment
));
151 return EFI_UNSUPPORTED
;
154 Status
= QemuFwCfgFindFile ((CHAR8
*)Allocate
->File
, &FwCfgItem
, &FwCfgSize
);
155 if (EFI_ERROR (Status
)) {
156 DEBUG ((EFI_D_ERROR
, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__
,
157 Allocate
->File
, Status
));
161 NumPages
= EFI_SIZE_TO_PAGES (FwCfgSize
);
162 Address
= 0xFFFFFFFF;
163 Status
= gBS
->AllocatePages (AllocateMaxAddress
, EfiACPIMemoryNVS
, NumPages
,
165 if (EFI_ERROR (Status
)) {
169 Blob
= AllocatePool (sizeof *Blob
);
171 Status
= EFI_OUT_OF_RESOURCES
;
174 CopyMem (Blob
->File
, Allocate
->File
, QEMU_LOADER_FNAME_SIZE
);
175 Blob
->Size
= FwCfgSize
;
176 Blob
->Base
= (VOID
*)(UINTN
)Address
;
177 Blob
->HostsOnlyTableData
= TRUE
;
179 Status
= OrderedCollectionInsert (Tracker
, NULL
, Blob
);
180 if (Status
== RETURN_ALREADY_STARTED
) {
181 DEBUG ((EFI_D_ERROR
, "%a: duplicated file \"%a\"\n", __FUNCTION__
,
183 Status
= EFI_PROTOCOL_ERROR
;
185 if (EFI_ERROR (Status
)) {
189 QemuFwCfgSelectItem (FwCfgItem
);
190 QemuFwCfgReadBytes (FwCfgSize
, Blob
->Base
);
191 ZeroMem (Blob
->Base
+ Blob
->Size
, EFI_PAGES_TO_SIZE (NumPages
) - Blob
->Size
);
193 DEBUG ((EFI_D_VERBOSE
, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "
194 "Address=0x%Lx\n", __FUNCTION__
, Allocate
->File
, Allocate
->Alignment
,
195 Allocate
->Zone
, (UINT64
)Blob
->Size
, (UINT64
)(UINTN
)Blob
->Base
));
202 gBS
->FreePages (Address
, NumPages
);
209 Process a QEMU_LOADER_ADD_POINTER command.
211 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
213 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
214 structures created thus far.
216 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
217 AddPointer, or the AddPointer command references
218 a file unknown to Tracker, or the pointer to
219 relocate has invalid location, size, or value, or
220 the relocated pointer value is not representable
221 in the given pointer size.
223 @retval EFI_SUCCESS The pointer field inside the pointer blob has
229 ProcessCmdAddPointer (
230 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
231 IN CONST ORDERED_COLLECTION
*Tracker
234 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
239 if (AddPointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0' ||
240 AddPointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
241 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
242 return EFI_PROTOCOL_ERROR
;
245 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
246 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
247 if (TrackerEntry
== NULL
|| TrackerEntry2
== NULL
) {
248 DEBUG ((EFI_D_ERROR
, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",
249 __FUNCTION__
, AddPointer
->PointerFile
, AddPointer
->PointeeFile
));
250 return EFI_PROTOCOL_ERROR
;
253 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
254 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
255 if ((AddPointer
->PointerSize
!= 1 && AddPointer
->PointerSize
!= 2 &&
256 AddPointer
->PointerSize
!= 4 && AddPointer
->PointerSize
!= 8) ||
257 Blob
->Size
< AddPointer
->PointerSize
||
258 Blob
->Size
- AddPointer
->PointerSize
< AddPointer
->PointerOffset
) {
259 DEBUG ((EFI_D_ERROR
, "%a: invalid pointer location or size in \"%a\"\n",
260 __FUNCTION__
, AddPointer
->PointerFile
));
261 return EFI_PROTOCOL_ERROR
;
264 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
266 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
267 if (PointerValue
>= Blob2
->Size
) {
268 DEBUG ((EFI_D_ERROR
, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__
,
269 AddPointer
->PointerFile
));
270 return EFI_PROTOCOL_ERROR
;
274 // The memory allocation system ensures that the address of the byte past the
275 // last byte of any allocated object is expressible (no wraparound).
277 ASSERT ((UINTN
)Blob2
->Base
<= MAX_ADDRESS
- Blob2
->Size
);
279 PointerValue
+= (UINT64
)(UINTN
)Blob2
->Base
;
281 RShiftU64 (PointerValue
, AddPointer
->PointerSize
* 8 - 1), 1) != 0) {
282 DEBUG ((EFI_D_ERROR
, "%a: relocated pointer value unrepresentable in "
283 "\"%a\"\n", __FUNCTION__
, AddPointer
->PointerFile
));
284 return EFI_PROTOCOL_ERROR
;
287 CopyMem (PointerField
, &PointerValue
, AddPointer
->PointerSize
);
289 DEBUG ((EFI_D_VERBOSE
, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
290 "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
291 AddPointer
->PointerFile
, AddPointer
->PointeeFile
,
292 AddPointer
->PointerOffset
, AddPointer
->PointerSize
));
298 Process a QEMU_LOADER_ADD_CHECKSUM command.
300 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.
302 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
303 structures created thus far.
305 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
306 AddChecksum, or the AddChecksum command
307 references a file unknown to Tracker, or the
308 range to checksum is invalid.
310 @retval EFI_SUCCESS The requested range has been checksummed.
315 ProcessCmdAddChecksum (
316 IN CONST QEMU_LOADER_ADD_CHECKSUM
*AddChecksum
,
317 IN CONST ORDERED_COLLECTION
*Tracker
320 ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
323 if (AddChecksum
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
324 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
325 return EFI_PROTOCOL_ERROR
;
328 TrackerEntry
= OrderedCollectionFind (Tracker
, AddChecksum
->File
);
329 if (TrackerEntry
== NULL
) {
330 DEBUG ((EFI_D_ERROR
, "%a: invalid blob reference \"%a\"\n", __FUNCTION__
,
332 return EFI_PROTOCOL_ERROR
;
335 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
336 if (Blob
->Size
<= AddChecksum
->ResultOffset
||
337 Blob
->Size
< AddChecksum
->Length
||
338 Blob
->Size
- AddChecksum
->Length
< AddChecksum
->Start
) {
339 DEBUG ((EFI_D_ERROR
, "%a: invalid checksum range in \"%a\"\n",
340 __FUNCTION__
, AddChecksum
->File
));
341 return EFI_PROTOCOL_ERROR
;
344 Blob
->Base
[AddChecksum
->ResultOffset
] = CalculateCheckSum8 (
345 Blob
->Base
+ AddChecksum
->Start
,
348 DEBUG ((EFI_D_VERBOSE
, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "
349 "Length=0x%x\n", __FUNCTION__
, AddChecksum
->File
,
350 AddChecksum
->ResultOffset
, AddChecksum
->Start
, AddChecksum
->Length
));
356 // We'll be saving the keys of installed tables so that we can roll them back
357 // in case of failure. 128 tables should be enough for anyone (TM).
359 #define INSTALLED_TABLES_MAX 128
362 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte
363 array is an ACPI table, and if so, install it.
365 This function assumes that the entire QEMU linker/loader command file has
366 been processed successfuly in a prior first pass.
368 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
370 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
373 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
375 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
376 elements, allocated by the caller. On output,
377 the function will have stored (appended) the
378 AcpiProtocol-internal key of the ACPI table that
379 the function has installed, if the AddPointer
380 command identified an ACPI table that is
381 different from RSDT and XSDT.
383 @param[in,out] NumInstalled On input, the number of entries already used in
384 InstalledKey; it must be in [0,
385 INSTALLED_TABLES_MAX] inclusive. On output, the
386 parameter is incremented if the AddPointer
387 command identified an ACPI table that is
388 different from RSDT and XSDT.
390 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
393 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI
394 table different from RSDT and XSDT, but there
395 was no more room in InstalledKey.
397 @retval EFI_SUCCESS AddPointer has been processed. Either an ACPI
398 table different from RSDT and XSDT has been
399 installed (reflected by InstalledKey and
400 NumInstalled), or RSDT or XSDT has been
401 identified but not installed, or the fw_cfg
402 blob pointed-into by AddPointer has been
403 marked as hosting something else than just
404 direct ACPI table contents.
406 @return Error codes returned by
407 AcpiProtocol->InstallAcpiTable().
412 Process2ndPassCmdAddPointer (
413 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
414 IN CONST ORDERED_COLLECTION
*Tracker
,
415 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
,
416 IN OUT UINTN InstalledKey
[INSTALLED_TABLES_MAX
],
417 IN OUT INT32
*NumInstalled
420 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
421 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry2
;
424 CONST UINT8
*PointerField
;
426 UINTN Blob2Remaining
;
428 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
429 CONST EFI_ACPI_DESCRIPTION_HEADER
*Header
;
432 if (*NumInstalled
< 0 || *NumInstalled
> INSTALLED_TABLES_MAX
) {
433 return EFI_INVALID_PARAMETER
;
436 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
437 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
438 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
439 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
440 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
442 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
445 // We assert that PointerValue falls inside Blob2's contents. This is ensured
446 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().
448 Blob2Remaining
= (UINTN
)Blob2
->Base
;
449 ASSERT(PointerValue
>= Blob2Remaining
);
450 Blob2Remaining
+= Blob2
->Size
;
451 ASSERT (PointerValue
< Blob2Remaining
);
453 Blob2Remaining
-= (UINTN
) PointerValue
;
454 DEBUG ((EFI_D_VERBOSE
, "%a: checking for ACPI header in \"%a\" at 0x%Lx "
455 "(remaining: 0x%Lx): ", __FUNCTION__
, AddPointer
->PointeeFile
,
456 PointerValue
, (UINT64
)Blob2Remaining
));
461 // To make our job simple, the FACS has a custom header. Sigh.
463 if (sizeof *Facs
<= Blob2Remaining
) {
464 Facs
= (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)PointerValue
;
466 if (Facs
->Length
>= sizeof *Facs
&&
467 Facs
->Length
<= Blob2Remaining
&&
469 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) {
470 DEBUG ((EFI_D_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
471 (CONST CHAR8
*)&Facs
->Signature
, Facs
->Length
));
472 TableSize
= Facs
->Length
;
477 // check for the uniform tables
479 if (TableSize
== 0 && sizeof *Header
<= Blob2Remaining
) {
480 Header
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
)PointerValue
;
482 if (Header
->Length
>= sizeof *Header
&&
483 Header
->Length
<= Blob2Remaining
&&
484 CalculateSum8 ((CONST UINT8
*)Header
, Header
->Length
) == 0) {
486 // This looks very much like an ACPI table from QEMU:
487 // - Length field consistent with both ACPI and containing blob size
488 // - checksum is correct
490 DEBUG ((EFI_D_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
491 (CONST CHAR8
*)&Header
->Signature
, Header
->Length
));
492 TableSize
= Header
->Length
;
495 // Skip RSDT and XSDT because those are handled by
496 // EFI_ACPI_TABLE_PROTOCOL automatically.
497 if (Header
->Signature
==
498 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
||
500 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
) {
506 if (TableSize
== 0) {
507 DEBUG ((EFI_D_VERBOSE
, "not found; marking fw_cfg blob as opaque\n"));
508 Blob2
->HostsOnlyTableData
= FALSE
;
512 if (*NumInstalled
== INSTALLED_TABLES_MAX
) {
513 DEBUG ((EFI_D_ERROR
, "%a: can't install more than %d tables\n",
514 __FUNCTION__
, INSTALLED_TABLES_MAX
));
515 return EFI_OUT_OF_RESOURCES
;
518 Status
= AcpiProtocol
->InstallAcpiTable (AcpiProtocol
,
519 (VOID
*)(UINTN
)PointerValue
, TableSize
,
520 &InstalledKey
[*NumInstalled
]);
521 if (EFI_ERROR (Status
)) {
522 DEBUG ((EFI_D_ERROR
, "%a: InstallAcpiTable(): %r\n", __FUNCTION__
,
532 Download, process, and install ACPI table data from the QEMU loader
535 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
537 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU
538 loader command with unsupported parameters
541 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg
544 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than
545 INSTALLED_TABLES_MAX tables found.
547 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.
549 @return Status codes returned by
550 AcpiProtocol->InstallAcpiTable().
555 InstallQemuFwCfgTables (
556 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
560 FIRMWARE_CONFIG_ITEM FwCfgItem
;
562 QEMU_LOADER_ENTRY
*LoaderStart
;
563 CONST QEMU_LOADER_ENTRY
*LoaderEntry
, *LoaderEnd
;
564 ORIGINAL_ATTRIBUTES
*OriginalPciAttributes
;
565 UINTN OriginalPciAttributesCount
;
566 ORDERED_COLLECTION
*Tracker
;
569 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
571 Status
= QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem
, &FwCfgSize
);
572 if (EFI_ERROR (Status
)) {
575 if (FwCfgSize
% sizeof *LoaderEntry
!= 0) {
576 DEBUG ((EFI_D_ERROR
, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
577 __FUNCTION__
, (UINT64
)FwCfgSize
));
578 return EFI_PROTOCOL_ERROR
;
581 LoaderStart
= AllocatePool (FwCfgSize
);
582 if (LoaderStart
== NULL
) {
583 return EFI_OUT_OF_RESOURCES
;
585 EnablePciDecoding (&OriginalPciAttributes
, &OriginalPciAttributesCount
);
586 QemuFwCfgSelectItem (FwCfgItem
);
587 QemuFwCfgReadBytes (FwCfgSize
, LoaderStart
);
588 RestorePciDecoding (OriginalPciAttributes
, OriginalPciAttributesCount
);
589 LoaderEnd
= LoaderStart
+ FwCfgSize
/ sizeof *LoaderEntry
;
591 Tracker
= OrderedCollectionInit (BlobCompare
, BlobKeyCompare
);
592 if (Tracker
== NULL
) {
593 Status
= EFI_OUT_OF_RESOURCES
;
598 // first pass: process the commands
600 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
601 switch (LoaderEntry
->Type
) {
602 case QemuLoaderCmdAllocate
:
603 Status
= ProcessCmdAllocate (&LoaderEntry
->Command
.Allocate
, Tracker
);
606 case QemuLoaderCmdAddPointer
:
607 Status
= ProcessCmdAddPointer (&LoaderEntry
->Command
.AddPointer
,
611 case QemuLoaderCmdAddChecksum
:
612 Status
= ProcessCmdAddChecksum (&LoaderEntry
->Command
.AddChecksum
,
617 DEBUG ((EFI_D_VERBOSE
, "%a: unknown loader command: 0x%x\n",
618 __FUNCTION__
, LoaderEntry
->Type
));
622 if (EFI_ERROR (Status
)) {
627 InstalledKey
= AllocatePool (INSTALLED_TABLES_MAX
* sizeof *InstalledKey
);
628 if (InstalledKey
== NULL
) {
629 Status
= EFI_OUT_OF_RESOURCES
;
634 // second pass: identify and install ACPI tables
637 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
638 if (LoaderEntry
->Type
== QemuLoaderCmdAddPointer
) {
639 Status
= Process2ndPassCmdAddPointer (&LoaderEntry
->Command
.AddPointer
,
640 Tracker
, AcpiProtocol
, InstalledKey
, &Installed
);
641 if (EFI_ERROR (Status
)) {
647 if (EFI_ERROR (Status
)) {
649 // roll back partial installation
651 while (Installed
> 0) {
653 AcpiProtocol
->UninstallAcpiTable (AcpiProtocol
, InstalledKey
[Installed
]);
656 DEBUG ((EFI_D_INFO
, "%a: installed %d tables\n", __FUNCTION__
, Installed
));
659 FreePool (InstalledKey
);
663 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
664 // place only if we're exiting with success and the blob hosts data that is
665 // not directly part of some ACPI table.
667 for (TrackerEntry
= OrderedCollectionMin (Tracker
); TrackerEntry
!= NULL
;
668 TrackerEntry
= TrackerEntry2
) {
672 TrackerEntry2
= OrderedCollectionNext (TrackerEntry
);
673 OrderedCollectionDelete (Tracker
, TrackerEntry
, &UserStruct
);
676 if (EFI_ERROR (Status
) || Blob
->HostsOnlyTableData
) {
677 DEBUG ((EFI_D_VERBOSE
, "%a: freeing \"%a\"\n", __FUNCTION__
,
679 gBS
->FreePages ((UINTN
)Blob
->Base
, EFI_SIZE_TO_PAGES (Blob
->Size
));
683 OrderedCollectionUninit (Tracker
);
686 FreePool (LoaderStart
);