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
;
1103 EFI_HANDLE QemuAcpiHandle
;
1105 Status
= QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem
, &FwCfgSize
);
1106 if (EFI_ERROR (Status
)) {
1110 if (FwCfgSize
% sizeof *LoaderEntry
!= 0) {
1113 "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
1117 return EFI_PROTOCOL_ERROR
;
1120 LoaderStart
= AllocatePool (FwCfgSize
);
1121 if (LoaderStart
== NULL
) {
1122 return EFI_OUT_OF_RESOURCES
;
1125 EnablePciDecoding (&OriginalPciAttributes
, &OriginalPciAttributesCount
);
1126 QemuFwCfgSelectItem (FwCfgItem
);
1127 QemuFwCfgReadBytes (FwCfgSize
, LoaderStart
);
1128 RestorePciDecoding (OriginalPciAttributes
, OriginalPciAttributesCount
);
1129 LoaderEnd
= LoaderStart
+ FwCfgSize
/ sizeof *LoaderEntry
;
1131 AllocationsRestrictedTo32Bit
= NULL
;
1132 Status
= CollectAllocationsRestrictedTo32Bit (
1133 &AllocationsRestrictedTo32Bit
,
1137 if (EFI_ERROR (Status
)) {
1142 if (QemuFwCfgS3Enabled ()) {
1144 // Size the allocation pessimistically, assuming that all commands in the
1145 // script are QEMU_LOADER_WRITE_POINTER commands.
1147 Status
= AllocateS3Context (&S3Context
, LoaderEnd
- LoaderStart
);
1148 if (EFI_ERROR (Status
)) {
1149 goto FreeAllocationsRestrictedTo32Bit
;
1153 Tracker
= OrderedCollectionInit (BlobCompare
, BlobKeyCompare
);
1154 if (Tracker
== NULL
) {
1155 Status
= EFI_OUT_OF_RESOURCES
;
1160 // first pass: process the commands
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.
1166 WritePointerSubsetEnd
= LoaderStart
;
1167 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
1168 switch (LoaderEntry
->Type
) {
1169 case QemuLoaderCmdAllocate
:
1170 Status
= ProcessCmdAllocate (
1171 &LoaderEntry
->Command
.Allocate
,
1173 AllocationsRestrictedTo32Bit
1177 case QemuLoaderCmdAddPointer
:
1178 Status
= ProcessCmdAddPointer (
1179 &LoaderEntry
->Command
.AddPointer
,
1184 case QemuLoaderCmdAddChecksum
:
1185 Status
= ProcessCmdAddChecksum (
1186 &LoaderEntry
->Command
.AddChecksum
,
1191 case QemuLoaderCmdWritePointer
:
1192 Status
= ProcessCmdWritePointer (
1193 &LoaderEntry
->Command
.WritePointer
,
1197 if (!EFI_ERROR (Status
)) {
1198 WritePointerSubsetEnd
= LoaderEntry
+ 1;
1206 "%a: unknown loader command: 0x%x\n",
1213 if (EFI_ERROR (Status
)) {
1214 goto RollbackWritePointersAndFreeTracker
;
1218 InstalledKey
= AllocatePool (INSTALLED_TABLES_MAX
* sizeof *InstalledKey
);
1219 if (InstalledKey
== NULL
) {
1220 Status
= EFI_OUT_OF_RESOURCES
;
1221 goto RollbackWritePointersAndFreeTracker
;
1224 SeenPointers
= OrderedCollectionInit (PointerCompare
, PointerCompare
);
1225 if (SeenPointers
== NULL
) {
1226 Status
= EFI_OUT_OF_RESOURCES
;
1231 // second pass: identify and install ACPI tables
1234 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
1235 if (LoaderEntry
->Type
== QemuLoaderCmdAddPointer
) {
1236 Status
= Process2ndPassCmdAddPointer (
1237 &LoaderEntry
->Command
.AddPointer
,
1244 if (EFI_ERROR (Status
)) {
1245 goto UninstallAcpiTables
;
1251 // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3
1252 // Boot Script opcodes has to be the last operation in this function, because
1253 // if it succeeds, it cannot be undone.
1255 if (S3Context
!= NULL
) {
1256 Status
= TransferS3ContextToBootScript (S3Context
);
1257 if (EFI_ERROR (Status
)) {
1258 goto UninstallAcpiTables
;
1262 // Ownership of S3Context has been transferred.
1267 DEBUG ((DEBUG_INFO
, "%a: installed %d tables\n", __FUNCTION__
, Installed
));
1269 UninstallAcpiTables
:
1270 if (EFI_ERROR (Status
)) {
1272 // roll back partial installation
1274 while (Installed
> 0) {
1276 AcpiProtocol
->UninstallAcpiTable (AcpiProtocol
, InstalledKey
[Installed
]);
1280 // Install a protocol to notify that the ACPI table provided by Qemu is
1283 QemuAcpiHandle
= NULL
;
1284 gBS
->InstallProtocolInterface (
1286 &gQemuAcpiTableNotifyProtocolGuid
,
1287 EFI_NATIVE_INTERFACE
,
1292 for (SeenPointerEntry
= OrderedCollectionMin (SeenPointers
);
1293 SeenPointerEntry
!= NULL
;
1294 SeenPointerEntry
= SeenPointerEntry2
)
1296 SeenPointerEntry2
= OrderedCollectionNext (SeenPointerEntry
);
1297 OrderedCollectionDelete (SeenPointers
, SeenPointerEntry
, NULL
);
1300 OrderedCollectionUninit (SeenPointers
);
1303 FreePool (InstalledKey
);
1305 RollbackWritePointersAndFreeTracker
:
1307 // In case of failure, revoke any allocation addresses that were communicated
1308 // to QEMU previously, before we release all the blobs.
1310 if (EFI_ERROR (Status
)) {
1311 LoaderEntry
= WritePointerSubsetEnd
;
1312 while (LoaderEntry
> LoaderStart
) {
1314 if (LoaderEntry
->Type
== QemuLoaderCmdWritePointer
) {
1315 UndoCmdWritePointer (&LoaderEntry
->Command
.WritePointer
);
1321 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
1322 // place only if we're exiting with success and the blob hosts data that is
1323 // not directly part of some ACPI table.
1325 for (TrackerEntry
= OrderedCollectionMin (Tracker
); TrackerEntry
!= NULL
;
1326 TrackerEntry
= TrackerEntry2
)
1331 TrackerEntry2
= OrderedCollectionNext (TrackerEntry
);
1332 OrderedCollectionDelete (Tracker
, TrackerEntry
, &UserStruct
);
1335 if (EFI_ERROR (Status
) || Blob
->HostsOnlyTableData
) {
1338 "%a: freeing \"%a\"\n",
1342 gBS
->FreePages ((UINTN
)Blob
->Base
, EFI_SIZE_TO_PAGES (Blob
->Size
));
1348 OrderedCollectionUninit (Tracker
);
1351 if (S3Context
!= NULL
) {
1352 ReleaseS3Context (S3Context
);
1355 FreeAllocationsRestrictedTo32Bit
:
1356 ReleaseAllocationsRestrictedTo32Bit (AllocationsRestrictedTo32Bit
);
1359 FreePool (LoaderStart
);