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 SPDX-License-Identifier: BSD-2-Clause-Patent
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
22 #include "AcpiPlatform.h"
25 // The user structure for the ordered collection that will track the fw_cfg
26 // blobs under processing.
29 UINT8 File
[QEMU_LOADER_FNAME_SIZE
]; // NUL-terminated name of the fw_cfg
30 // blob. This is the ordering / search
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.
40 Compare a standalone key against a user structure containing an embedded key.
42 @param[in] StandaloneKey Pointer to the bare key.
44 @param[in] UserStruct Pointer to the user structure with the embedded
47 @retval <0 If StandaloneKey compares less than UserStruct's key.
49 @retval 0 If StandaloneKey compares equal to UserStruct's key.
51 @retval >0 If StandaloneKey compares greater than UserStruct's key.
57 IN CONST VOID
*StandaloneKey
,
58 IN CONST VOID
*UserStruct
64 return AsciiStrCmp (StandaloneKey
, (CONST CHAR8
*)Blob
->File
);
68 Comparator function for two user structures.
70 @param[in] UserStruct1 Pointer to the first user structure.
72 @param[in] UserStruct2 Pointer to the second user structure.
74 @retval <0 If UserStruct1 compares less than UserStruct2.
76 @retval 0 If UserStruct1 compares equal to UserStruct2.
78 @retval >0 If UserStruct1 compares greater than UserStruct2.
84 IN CONST VOID
*UserStruct1
,
85 IN CONST VOID
*UserStruct2
91 return BlobKeyCompare (Blob1
->File
, UserStruct2
);
95 Comparator function for two opaque pointers, ordering on (unsigned) pointer
97 Can be used as both Key and UserStruct comparator.
99 @param[in] Pointer1 First pointer.
101 @param[in] Pointer2 Second pointer.
103 @retval <0 If Pointer1 compares less than Pointer2.
105 @retval 0 If Pointer1 compares equal to Pointer2.
107 @retval >0 If Pointer1 compares greater than Pointer2.
113 IN CONST VOID
*Pointer1
,
114 IN CONST VOID
*Pointer2
117 if (Pointer1
== Pointer2
) {
121 if ((UINTN
)Pointer1
< (UINTN
)Pointer2
) {
129 Comparator function for two ASCII strings. Can be used as both Key and
130 UserStruct comparator.
132 This function exists solely so we can avoid casting &AsciiStrCmp to
133 ORDERED_COLLECTION_USER_COMPARE and ORDERED_COLLECTION_KEY_COMPARE.
135 @param[in] AsciiString1 Pointer to the first ASCII string.
137 @param[in] AsciiString2 Pointer to the second ASCII string.
139 @return The return value of AsciiStrCmp (AsciiString1, AsciiString2).
145 IN CONST VOID
*AsciiString1
,
146 IN CONST VOID
*AsciiString2
149 return AsciiStrCmp (AsciiString1
, AsciiString2
);
153 Release the ORDERED_COLLECTION structure populated by
154 CollectAllocationsRestrictedTo32Bit() (below).
156 This function may be called by CollectAllocationsRestrictedTo32Bit() itself,
159 @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure to
164 ReleaseAllocationsRestrictedTo32Bit (
165 IN ORDERED_COLLECTION
*AllocationsRestrictedTo32Bit
168 ORDERED_COLLECTION_ENTRY
*Entry
, *Entry2
;
170 for (Entry
= OrderedCollectionMin (AllocationsRestrictedTo32Bit
);
174 Entry2
= OrderedCollectionNext (Entry
);
175 OrderedCollectionDelete (AllocationsRestrictedTo32Bit
, Entry
, NULL
);
178 OrderedCollectionUninit (AllocationsRestrictedTo32Bit
);
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
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.
195 @param[in] LoaderStart Points to the first entry in the
196 linker/loader script.
198 @param[in] LoaderEnd Points one past the last entry in
199 the linker/loader script.
201 @retval EFI_SUCCESS AllocationsRestrictedTo32Bit has been
204 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
206 @retval EFI_PROTOCOL_ERROR Invalid linker/loader script contents.
210 CollectAllocationsRestrictedTo32Bit (
211 OUT ORDERED_COLLECTION
**AllocationsRestrictedTo32Bit
,
212 IN CONST QEMU_LOADER_ENTRY
*LoaderStart
,
213 IN CONST QEMU_LOADER_ENTRY
*LoaderEnd
216 ORDERED_COLLECTION
*Collection
;
217 CONST QEMU_LOADER_ENTRY
*LoaderEntry
;
220 Collection
= OrderedCollectionInit (AsciiStringCompare
, AsciiStringCompare
);
221 if (Collection
== NULL
) {
222 return EFI_OUT_OF_RESOURCES
;
225 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
226 CONST QEMU_LOADER_ADD_POINTER
*AddPointer
;
228 if (LoaderEntry
->Type
!= QemuLoaderCmdAddPointer
) {
232 AddPointer
= &LoaderEntry
->Command
.AddPointer
;
234 if (AddPointer
->PointerSize
>= 8) {
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
;
244 Status
= OrderedCollectionInsert (
247 (VOID
*)AddPointer
->PointeeFile
253 "%a: restricting blob \"%a\" from 64-bit allocation\n",
255 AddPointer
->PointeeFile
258 case EFI_ALREADY_STARTED
:
260 // The restriction has been recorded already.
263 case EFI_OUT_OF_RESOURCES
:
270 *AllocationsRestrictedTo32Bit
= Collection
;
274 ReleaseAllocationsRestrictedTo32Bit (Collection
);
279 Process a QEMU_LOADER_ALLOCATE command.
281 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to
284 @param[in,out] Tracker The ORDERED_COLLECTION tracking the
285 BLOB user structures created thus
288 @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION populated by
290 CollectAllocationsRestrictedTo32Bit,
291 naming the fw_cfg blobs that must
292 not be allocated from 64-bit address
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.
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.
306 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in
309 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.
311 @return Error codes from QemuFwCfgFindFile() and
312 gBS->AllocatePages().
318 IN CONST QEMU_LOADER_ALLOCATE
*Allocate
,
319 IN OUT ORDERED_COLLECTION
*Tracker
,
320 IN ORDERED_COLLECTION
*AllocationsRestrictedTo32Bit
323 FIRMWARE_CONFIG_ITEM FwCfgItem
;
327 EFI_PHYSICAL_ADDRESS Address
;
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
;
335 if (Allocate
->Alignment
> EFI_PAGE_SIZE
) {
338 "%a: unsupported alignment 0x%x\n",
342 return EFI_UNSUPPORTED
;
345 Status
= QemuFwCfgFindFile ((CHAR8
*)Allocate
->File
, &FwCfgItem
, &FwCfgSize
);
346 if (EFI_ERROR (Status
)) {
349 "%a: QemuFwCfgFindFile(\"%a\"): %r\n",
357 NumPages
= EFI_SIZE_TO_PAGES (FwCfgSize
);
358 Address
= MAX_UINT64
;
359 if (OrderedCollectionFind (
360 AllocationsRestrictedTo32Bit
,
364 Address
= MAX_UINT32
;
367 Status
= gBS
->AllocatePages (
373 if (EFI_ERROR (Status
)) {
377 Blob
= AllocatePool (sizeof *Blob
);
379 Status
= EFI_OUT_OF_RESOURCES
;
383 CopyMem (Blob
->File
, Allocate
->File
, QEMU_LOADER_FNAME_SIZE
);
384 Blob
->Size
= FwCfgSize
;
385 Blob
->Base
= (VOID
*)(UINTN
)Address
;
386 Blob
->HostsOnlyTableData
= TRUE
;
388 Status
= OrderedCollectionInsert (Tracker
, NULL
, Blob
);
389 if (Status
== RETURN_ALREADY_STARTED
) {
392 "%a: duplicated file \"%a\"\n",
396 Status
= EFI_PROTOCOL_ERROR
;
399 if (EFI_ERROR (Status
)) {
403 QemuFwCfgSelectItem (FwCfgItem
);
404 QemuFwCfgReadBytes (FwCfgSize
, Blob
->Base
);
405 ZeroMem (Blob
->Base
+ Blob
->Size
, EFI_PAGES_TO_SIZE (NumPages
) - Blob
->Size
);
409 "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "
416 (UINT64
)(UINTN
)Blob
->Base
424 gBS
->FreePages (Address
, NumPages
);
430 Process a QEMU_LOADER_ADD_POINTER command.
432 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
434 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
435 structures created thus far.
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.
444 @retval EFI_SUCCESS The pointer field inside the pointer blob has
450 ProcessCmdAddPointer (
451 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
452 IN CONST ORDERED_COLLECTION
*Tracker
455 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
460 if ((AddPointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') ||
461 (AddPointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0'))
463 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
464 return EFI_PROTOCOL_ERROR
;
467 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
468 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
469 if ((TrackerEntry
== NULL
) || (TrackerEntry2
== NULL
)) {
472 "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",
474 AddPointer
->PointerFile
,
475 AddPointer
->PointeeFile
477 return EFI_PROTOCOL_ERROR
;
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
))
489 "%a: invalid pointer location or size in \"%a\"\n",
491 AddPointer
->PointerFile
493 return EFI_PROTOCOL_ERROR
;
496 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
498 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
499 if (PointerValue
>= Blob2
->Size
) {
502 "%a: invalid pointer value in \"%a\"\n",
504 AddPointer
->PointerFile
506 return EFI_PROTOCOL_ERROR
;
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).
513 ASSERT ((UINTN
)Blob2
->Base
<= MAX_ADDRESS
- Blob2
->Size
);
515 PointerValue
+= (UINT64
)(UINTN
)Blob2
->Base
;
516 if ((AddPointer
->PointerSize
< 8) &&
517 (RShiftU64 (PointerValue
, AddPointer
->PointerSize
* 8) != 0))
521 "%a: relocated pointer value unrepresentable in "
524 AddPointer
->PointerFile
526 return EFI_PROTOCOL_ERROR
;
529 CopyMem (PointerField
, &PointerValue
, AddPointer
->PointerSize
);
533 "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
534 "PointerOffset=0x%x PointerSize=%d\n",
536 AddPointer
->PointerFile
,
537 AddPointer
->PointeeFile
,
538 AddPointer
->PointerOffset
,
539 AddPointer
->PointerSize
545 Process a QEMU_LOADER_ADD_CHECKSUM command.
547 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.
549 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
550 structures created thus far.
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.
557 @retval EFI_SUCCESS The requested range has been checksummed.
562 ProcessCmdAddChecksum (
563 IN CONST QEMU_LOADER_ADD_CHECKSUM
*AddChecksum
,
564 IN CONST ORDERED_COLLECTION
*Tracker
567 ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
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
;
575 TrackerEntry
= OrderedCollectionFind (Tracker
, AddChecksum
->File
);
576 if (TrackerEntry
== NULL
) {
579 "%a: invalid blob reference \"%a\"\n",
583 return EFI_PROTOCOL_ERROR
;
586 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
587 if ((Blob
->Size
<= AddChecksum
->ResultOffset
) ||
588 (Blob
->Size
< AddChecksum
->Length
) ||
589 (Blob
->Size
- AddChecksum
->Length
< AddChecksum
->Start
))
593 "%a: invalid checksum range in \"%a\"\n",
597 return EFI_PROTOCOL_ERROR
;
600 Blob
->Base
[AddChecksum
->ResultOffset
] = CalculateCheckSum8 (
601 Blob
->Base
+ AddChecksum
->Start
,
606 "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "
610 AddChecksum
->ResultOffset
,
618 Process a QEMU_LOADER_WRITE_POINTER command.
620 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.
622 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
623 structures created thus far.
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.
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.
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
643 @return Error codes propagated from
644 SaveCondensedWritePointerToS3Context(). The
645 pointer object inside the writeable fw_cfg file
646 has not been written.
650 ProcessCmdWritePointer (
651 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
,
652 IN CONST ORDERED_COLLECTION
*Tracker
,
653 IN OUT S3_CONTEXT
*S3Context OPTIONAL
656 RETURN_STATUS Status
;
657 FIRMWARE_CONFIG_ITEM PointerItem
;
658 UINTN PointerItemSize
;
659 ORDERED_COLLECTION_ENTRY
*PointeeEntry
;
663 if ((WritePointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') ||
664 (WritePointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0'))
666 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
667 return EFI_PROTOCOL_ERROR
;
670 Status
= QemuFwCfgFindFile (
671 (CONST CHAR8
*)WritePointer
->PointerFile
,
675 PointeeEntry
= OrderedCollectionFind (Tracker
, WritePointer
->PointeeFile
);
676 if (RETURN_ERROR (Status
) || (PointeeEntry
== NULL
)) {
679 "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
681 WritePointer
->PointerFile
,
682 WritePointer
->PointeeFile
684 return EFI_PROTOCOL_ERROR
;
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
))
695 "%a: invalid pointer location or size in \"%a\"\n",
697 WritePointer
->PointerFile
699 return EFI_PROTOCOL_ERROR
;
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
;
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).
713 ASSERT ((UINTN
)PointeeBlob
->Base
<= MAX_ADDRESS
- PointeeBlob
->Size
);
715 PointerValue
+= (UINT64
)(UINTN
)PointeeBlob
->Base
;
716 if ((WritePointer
->PointerSize
< 8) &&
717 (RShiftU64 (PointerValue
, WritePointer
->PointerSize
* 8) != 0))
721 "%a: pointer value unrepresentable in \"%a\"\n",
723 WritePointer
->PointerFile
725 return EFI_PROTOCOL_ERROR
;
729 // If S3 is enabled, we have to capture the below fw_cfg actions in condensed
730 // form, to be replayed during S3 resume.
732 if (S3Context
!= NULL
) {
733 EFI_STATUS SaveStatus
;
735 SaveStatus
= SaveCondensedWritePointerToS3Context (
738 WritePointer
->PointerSize
,
739 WritePointer
->PointerOffset
,
742 if (EFI_ERROR (SaveStatus
)) {
747 QemuFwCfgSelectItem (PointerItem
);
748 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
749 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
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.
756 PointeeBlob
->HostsOnlyTableData
= FALSE
;
760 "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
761 "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n",
763 WritePointer
->PointerFile
,
764 WritePointer
->PointeeFile
,
765 WritePointer
->PointerOffset
,
766 WritePointer
->PointeeOffset
,
767 WritePointer
->PointerSize
773 Undo a QEMU_LOADER_WRITE_POINTER command.
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().
780 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
784 UndoCmdWritePointer (
785 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
788 RETURN_STATUS Status
;
789 FIRMWARE_CONFIG_ITEM PointerItem
;
790 UINTN PointerItemSize
;
793 Status
= QemuFwCfgFindFile (
794 (CONST CHAR8
*)WritePointer
->PointerFile
,
798 ASSERT_RETURN_ERROR (Status
);
801 QemuFwCfgSelectItem (PointerItem
);
802 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
803 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
807 "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n",
809 WritePointer
->PointerFile
,
810 WritePointer
->PointerOffset
,
811 WritePointer
->PointerSize
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).
819 #define INSTALLED_TABLES_MAX 128
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.
825 This function assumes that the entire QEMU linker/loader command file has
826 been processed successfully in a prior first pass.
828 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
830 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
833 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
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.
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.
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
860 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
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.
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.
877 @return Error codes returned by
878 AcpiProtocol->InstallAcpiTable().
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
892 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
893 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry2
;
894 ORDERED_COLLECTION_ENTRY
*SeenPointerEntry
;
897 CONST UINT8
*PointerField
;
899 UINTN Blob2Remaining
;
901 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
902 CONST EFI_ACPI_DESCRIPTION_HEADER
*Header
;
905 if ((*NumInstalled
< 0) || (*NumInstalled
> INSTALLED_TABLES_MAX
)) {
906 return EFI_INVALID_PARAMETER
;
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
;
915 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
918 // We assert that PointerValue falls inside Blob2's contents. This is ensured
919 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().
921 Blob2Remaining
= (UINTN
)Blob2
->Base
;
922 ASSERT (PointerValue
>= Blob2Remaining
);
923 Blob2Remaining
+= Blob2
->Size
;
924 ASSERT (PointerValue
< Blob2Remaining
);
926 Status
= OrderedCollectionInsert (
928 &SeenPointerEntry
, // for reverting insertion in error case
929 (VOID
*)(UINTN
)PointerValue
931 if (EFI_ERROR (Status
)) {
932 if (Status
== RETURN_ALREADY_STARTED
) {
934 // Already seen this pointer, don't try to process it again.
938 "%a: PointerValue=0x%Lx already processed, skipping.\n",
942 Status
= EFI_SUCCESS
;
948 Blob2Remaining
-= (UINTN
)PointerValue
;
951 "%a: checking for ACPI header in \"%a\" at 0x%Lx "
952 "(remaining: 0x%Lx): ",
954 AddPointer
->PointeeFile
,
956 (UINT64
)Blob2Remaining
962 // To make our job simple, the FACS has a custom header. Sigh.
964 if (sizeof *Facs
<= Blob2Remaining
) {
965 Facs
= (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)PointerValue
;
967 if ((Facs
->Length
>= sizeof *Facs
) &&
968 (Facs
->Length
<= Blob2Remaining
) &&
970 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
))
974 "found \"%-4.4a\" size 0x%x\n",
975 (CONST CHAR8
*)&Facs
->Signature
,
978 TableSize
= Facs
->Length
;
983 // check for the uniform tables
985 if ((TableSize
== 0) && (sizeof *Header
<= Blob2Remaining
)) {
986 Header
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
)PointerValue
;
988 if ((Header
->Length
>= sizeof *Header
) &&
989 (Header
->Length
<= Blob2Remaining
) &&
990 (CalculateSum8 ((CONST UINT8
*)Header
, Header
->Length
) == 0))
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
999 "found \"%-4.4a\" size 0x%x\n",
1000 (CONST CHAR8
*)&Header
->Signature
,
1003 TableSize
= Header
->Length
;
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
))
1018 if (TableSize
== 0) {
1019 DEBUG ((DEBUG_VERBOSE
, "not found; marking fw_cfg blob as opaque\n"));
1020 Blob2
->HostsOnlyTableData
= FALSE
;
1024 if (*NumInstalled
== INSTALLED_TABLES_MAX
) {
1027 "%a: can't install more than %d tables\n",
1029 INSTALLED_TABLES_MAX
1031 Status
= EFI_OUT_OF_RESOURCES
;
1032 goto RollbackSeenPointer
;
1035 Status
= AcpiProtocol
->InstallAcpiTable (
1037 (VOID
*)(UINTN
)PointerValue
,
1039 &InstalledKey
[*NumInstalled
]
1041 if (EFI_ERROR (Status
)) {
1044 "%a: InstallAcpiTable(): %r\n",
1048 goto RollbackSeenPointer
;
1054 RollbackSeenPointer
:
1055 OrderedCollectionDelete (SeenPointers
, SeenPointerEntry
, NULL
);
1060 Download, process, and install ACPI table data from the QEMU loader
1063 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
1065 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU
1066 loader command with unsupported parameters
1069 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg
1072 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than
1073 INSTALLED_TABLES_MAX tables found.
1075 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.
1077 @return Status codes returned by
1078 AcpiProtocol->InstallAcpiTable().
1083 InstallQemuFwCfgTables (
1084 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
1088 FIRMWARE_CONFIG_ITEM FwCfgItem
;
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
;
1100 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
1101 ORDERED_COLLECTION
*SeenPointers
;
1102 ORDERED_COLLECTION_ENTRY
*SeenPointerEntry
, *SeenPointerEntry2
;
1104 Status
= QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem
, &FwCfgSize
);
1105 if (EFI_ERROR (Status
)) {
1109 if (FwCfgSize
% sizeof *LoaderEntry
!= 0) {
1112 "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
1116 return EFI_PROTOCOL_ERROR
;
1119 LoaderStart
= AllocatePool (FwCfgSize
);
1120 if (LoaderStart
== NULL
) {
1121 return EFI_OUT_OF_RESOURCES
;
1124 EnablePciDecoding (&OriginalPciAttributes
, &OriginalPciAttributesCount
);
1125 QemuFwCfgSelectItem (FwCfgItem
);
1126 QemuFwCfgReadBytes (FwCfgSize
, LoaderStart
);
1127 RestorePciDecoding (OriginalPciAttributes
, OriginalPciAttributesCount
);
1128 LoaderEnd
= LoaderStart
+ FwCfgSize
/ sizeof *LoaderEntry
;
1130 AllocationsRestrictedTo32Bit
= NULL
;
1131 Status
= CollectAllocationsRestrictedTo32Bit (
1132 &AllocationsRestrictedTo32Bit
,
1136 if (EFI_ERROR (Status
)) {
1141 if (QemuFwCfgS3Enabled ()) {
1143 // Size the allocation pessimistically, assuming that all commands in the
1144 // script are QEMU_LOADER_WRITE_POINTER commands.
1146 Status
= AllocateS3Context (&S3Context
, LoaderEnd
- LoaderStart
);
1147 if (EFI_ERROR (Status
)) {
1148 goto FreeAllocationsRestrictedTo32Bit
;
1152 Tracker
= OrderedCollectionInit (BlobCompare
, BlobKeyCompare
);
1153 if (Tracker
== NULL
) {
1154 Status
= EFI_OUT_OF_RESOURCES
;
1159 // first pass: process the commands
1161 // "WritePointerSubsetEnd" points one past the last successful
1162 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
1163 // pass, no such command has been encountered yet.
1165 WritePointerSubsetEnd
= LoaderStart
;
1166 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
1167 switch (LoaderEntry
->Type
) {
1168 case QemuLoaderCmdAllocate
:
1169 Status
= ProcessCmdAllocate (
1170 &LoaderEntry
->Command
.Allocate
,
1172 AllocationsRestrictedTo32Bit
1176 case QemuLoaderCmdAddPointer
:
1177 Status
= ProcessCmdAddPointer (
1178 &LoaderEntry
->Command
.AddPointer
,
1183 case QemuLoaderCmdAddChecksum
:
1184 Status
= ProcessCmdAddChecksum (
1185 &LoaderEntry
->Command
.AddChecksum
,
1190 case QemuLoaderCmdWritePointer
:
1191 Status
= ProcessCmdWritePointer (
1192 &LoaderEntry
->Command
.WritePointer
,
1196 if (!EFI_ERROR (Status
)) {
1197 WritePointerSubsetEnd
= LoaderEntry
+ 1;
1205 "%a: unknown loader command: 0x%x\n",
1212 if (EFI_ERROR (Status
)) {
1213 goto RollbackWritePointersAndFreeTracker
;
1217 InstalledKey
= AllocatePool (INSTALLED_TABLES_MAX
* sizeof *InstalledKey
);
1218 if (InstalledKey
== NULL
) {
1219 Status
= EFI_OUT_OF_RESOURCES
;
1220 goto RollbackWritePointersAndFreeTracker
;
1223 SeenPointers
= OrderedCollectionInit (PointerCompare
, PointerCompare
);
1224 if (SeenPointers
== NULL
) {
1225 Status
= EFI_OUT_OF_RESOURCES
;
1230 // second pass: identify and install ACPI tables
1233 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
1234 if (LoaderEntry
->Type
== QemuLoaderCmdAddPointer
) {
1235 Status
= Process2ndPassCmdAddPointer (
1236 &LoaderEntry
->Command
.AddPointer
,
1243 if (EFI_ERROR (Status
)) {
1244 goto UninstallAcpiTables
;
1250 // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3
1251 // Boot Script opcodes has to be the last operation in this function, because
1252 // if it succeeds, it cannot be undone.
1254 if (S3Context
!= NULL
) {
1255 Status
= TransferS3ContextToBootScript (S3Context
);
1256 if (EFI_ERROR (Status
)) {
1257 goto UninstallAcpiTables
;
1261 // Ownership of S3Context has been transferred.
1266 UninstallAcpiTables
:
1267 if (EFI_ERROR (Status
)) {
1269 // roll back partial installation
1271 while (Installed
> 0) {
1273 AcpiProtocol
->UninstallAcpiTable (AcpiProtocol
, InstalledKey
[Installed
]);
1276 DEBUG ((DEBUG_INFO
, "%a: installed %d tables\n", __FUNCTION__
, Installed
));
1279 for (SeenPointerEntry
= OrderedCollectionMin (SeenPointers
);
1280 SeenPointerEntry
!= NULL
;
1281 SeenPointerEntry
= SeenPointerEntry2
)
1283 SeenPointerEntry2
= OrderedCollectionNext (SeenPointerEntry
);
1284 OrderedCollectionDelete (SeenPointers
, SeenPointerEntry
, NULL
);
1287 OrderedCollectionUninit (SeenPointers
);
1290 FreePool (InstalledKey
);
1292 RollbackWritePointersAndFreeTracker
:
1294 // In case of failure, revoke any allocation addresses that were communicated
1295 // to QEMU previously, before we release all the blobs.
1297 if (EFI_ERROR (Status
)) {
1298 LoaderEntry
= WritePointerSubsetEnd
;
1299 while (LoaderEntry
> LoaderStart
) {
1301 if (LoaderEntry
->Type
== QemuLoaderCmdWritePointer
) {
1302 UndoCmdWritePointer (&LoaderEntry
->Command
.WritePointer
);
1308 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
1309 // place only if we're exiting with success and the blob hosts data that is
1310 // not directly part of some ACPI table.
1312 for (TrackerEntry
= OrderedCollectionMin (Tracker
); TrackerEntry
!= NULL
;
1313 TrackerEntry
= TrackerEntry2
)
1318 TrackerEntry2
= OrderedCollectionNext (TrackerEntry
);
1319 OrderedCollectionDelete (Tracker
, TrackerEntry
, &UserStruct
);
1322 if (EFI_ERROR (Status
) || Blob
->HostsOnlyTableData
) {
1325 "%a: freeing \"%a\"\n",
1329 gBS
->FreePages ((UINTN
)Blob
->Base
, EFI_SIZE_TO_PAGES (Blob
->Size
));
1335 OrderedCollectionUninit (Tracker
);
1338 if (S3Context
!= NULL
) {
1339 ReleaseS3Context (S3Context
);
1342 FreeAllocationsRestrictedTo32Bit
:
1343 ReleaseAllocationsRestrictedTo32Bit (AllocationsRestrictedTo32Bit
);
1346 FreePool (LoaderStart
);