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 <IndustryStandard/UefiTcgPlatform.h>
14 #include <Library/BaseLib.h> // AsciiStrCmp()
15 #include <Library/BaseMemoryLib.h> // CopyMem()
16 #include <Library/DebugLib.h> // DEBUG()
17 #include <Library/MemoryAllocationLib.h> // AllocatePool()
18 #include <Library/OrderedCollectionLib.h> // OrderedCollectionMin()
19 #include <Library/QemuFwCfgLib.h> // QemuFwCfgFindFile()
20 #include <Library/QemuFwCfgS3Lib.h> // QemuFwCfgS3Enabled()
21 #include <Library/UefiBootServicesTableLib.h> // gBS
22 #include <Library/TpmMeasurementLib.h>
24 #include "AcpiPlatform.h"
27 // The user structure for the ordered collection that will track the fw_cfg
28 // blobs under processing.
31 UINT8 File
[QEMU_LOADER_FNAME_SIZE
]; // NUL-terminated name of the fw_cfg
32 // blob. This is the ordering / search
34 UINTN Size
; // The number of bytes in this blob.
35 UINT8
*Base
; // Pointer to the blob data.
36 BOOLEAN HostsOnlyTableData
; // TRUE iff the blob has been found to
37 // only contain data that is directly
38 // part of ACPI tables.
42 Compare a standalone key against a user structure containing an embedded key.
44 @param[in] StandaloneKey Pointer to the bare key.
46 @param[in] UserStruct Pointer to the user structure with the embedded
49 @retval <0 If StandaloneKey compares less than UserStruct's key.
51 @retval 0 If StandaloneKey compares equal to UserStruct's key.
53 @retval >0 If StandaloneKey compares greater than UserStruct's key.
59 IN CONST VOID
*StandaloneKey
,
60 IN CONST VOID
*UserStruct
66 return AsciiStrCmp (StandaloneKey
, (CONST CHAR8
*)Blob
->File
);
70 Comparator function for two user structures.
72 @param[in] UserStruct1 Pointer to the first user structure.
74 @param[in] UserStruct2 Pointer to the second user structure.
76 @retval <0 If UserStruct1 compares less than UserStruct2.
78 @retval 0 If UserStruct1 compares equal to UserStruct2.
80 @retval >0 If UserStruct1 compares greater than UserStruct2.
86 IN CONST VOID
*UserStruct1
,
87 IN CONST VOID
*UserStruct2
93 return BlobKeyCompare (Blob1
->File
, UserStruct2
);
97 Comparator function for two opaque pointers, ordering on (unsigned) pointer
99 Can be used as both Key and UserStruct comparator.
101 @param[in] Pointer1 First pointer.
103 @param[in] Pointer2 Second pointer.
105 @retval <0 If Pointer1 compares less than Pointer2.
107 @retval 0 If Pointer1 compares equal to Pointer2.
109 @retval >0 If Pointer1 compares greater than Pointer2.
115 IN CONST VOID
*Pointer1
,
116 IN CONST VOID
*Pointer2
119 if (Pointer1
== Pointer2
) {
123 if ((UINTN
)Pointer1
< (UINTN
)Pointer2
) {
131 Comparator function for two ASCII strings. Can be used as both Key and
132 UserStruct comparator.
134 This function exists solely so we can avoid casting &AsciiStrCmp to
135 ORDERED_COLLECTION_USER_COMPARE and ORDERED_COLLECTION_KEY_COMPARE.
137 @param[in] AsciiString1 Pointer to the first ASCII string.
139 @param[in] AsciiString2 Pointer to the second ASCII string.
141 @return The return value of AsciiStrCmp (AsciiString1, AsciiString2).
147 IN CONST VOID
*AsciiString1
,
148 IN CONST VOID
*AsciiString2
151 return AsciiStrCmp (AsciiString1
, AsciiString2
);
155 Release the ORDERED_COLLECTION structure populated by
156 CollectAllocationsRestrictedTo32Bit() (below).
158 This function may be called by CollectAllocationsRestrictedTo32Bit() itself,
161 @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure to
166 ReleaseAllocationsRestrictedTo32Bit (
167 IN ORDERED_COLLECTION
*AllocationsRestrictedTo32Bit
170 ORDERED_COLLECTION_ENTRY
*Entry
, *Entry2
;
172 for (Entry
= OrderedCollectionMin (AllocationsRestrictedTo32Bit
);
176 Entry2
= OrderedCollectionNext (Entry
);
177 OrderedCollectionDelete (AllocationsRestrictedTo32Bit
, Entry
, NULL
);
180 OrderedCollectionUninit (AllocationsRestrictedTo32Bit
);
184 Iterate over the linker/loader script, and collect the names of the fw_cfg
185 blobs that are referenced by QEMU_LOADER_ADD_POINTER.PointeeFile fields, such
186 that QEMU_LOADER_ADD_POINTER.PointerSize is less than 8. This means that the
187 pointee blob's address will have to be patched into a narrower-than-8 byte
188 pointer field, hence the pointee blob must not be allocated from 64-bit
191 @param[out] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure
192 linking (not copying / owning) such
193 QEMU_LOADER_ADD_POINTER.PointeeFile
194 fields that name the blobs
195 restricted from 64-bit allocation.
197 @param[in] LoaderStart Points to the first entry in the
198 linker/loader script.
200 @param[in] LoaderEnd Points one past the last entry in
201 the linker/loader script.
203 @retval EFI_SUCCESS AllocationsRestrictedTo32Bit has been
206 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
208 @retval EFI_PROTOCOL_ERROR Invalid linker/loader script contents.
212 CollectAllocationsRestrictedTo32Bit (
213 OUT ORDERED_COLLECTION
**AllocationsRestrictedTo32Bit
,
214 IN CONST QEMU_LOADER_ENTRY
*LoaderStart
,
215 IN CONST QEMU_LOADER_ENTRY
*LoaderEnd
218 ORDERED_COLLECTION
*Collection
;
219 CONST QEMU_LOADER_ENTRY
*LoaderEntry
;
222 Collection
= OrderedCollectionInit (AsciiStringCompare
, AsciiStringCompare
);
223 if (Collection
== NULL
) {
224 return EFI_OUT_OF_RESOURCES
;
227 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
228 CONST QEMU_LOADER_ADD_POINTER
*AddPointer
;
230 if (LoaderEntry
->Type
!= QemuLoaderCmdAddPointer
) {
234 AddPointer
= &LoaderEntry
->Command
.AddPointer
;
236 if (AddPointer
->PointerSize
>= 8) {
240 if (AddPointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
241 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
242 Status
= EFI_PROTOCOL_ERROR
;
246 Status
= OrderedCollectionInsert (
249 (VOID
*)AddPointer
->PointeeFile
255 "%a: restricting blob \"%a\" from 64-bit allocation\n",
257 AddPointer
->PointeeFile
260 case EFI_ALREADY_STARTED
:
262 // The restriction has been recorded already.
265 case EFI_OUT_OF_RESOURCES
:
272 *AllocationsRestrictedTo32Bit
= Collection
;
276 ReleaseAllocationsRestrictedTo32Bit (Collection
);
281 Process a QEMU_LOADER_ALLOCATE command.
283 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to
286 @param[in,out] Tracker The ORDERED_COLLECTION tracking the
287 BLOB user structures created thus
290 @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION populated by
292 CollectAllocationsRestrictedTo32Bit,
293 naming the fw_cfg blobs that must
294 not be allocated from 64-bit address
297 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been
298 allocated for the blob contents, and the
299 contents have been saved. A BLOB object (user
300 structure) has been allocated from pool memory,
301 referencing the blob contents. The BLOB user
302 structure has been linked into Tracker.
304 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
305 Allocate, or the Allocate command references a
306 file that is already known by Tracker.
308 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in
311 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.
313 @return Error codes from QemuFwCfgFindFile() and
314 gBS->AllocatePages().
320 IN CONST QEMU_LOADER_ALLOCATE
*Allocate
,
321 IN OUT ORDERED_COLLECTION
*Tracker
,
322 IN ORDERED_COLLECTION
*AllocationsRestrictedTo32Bit
325 FIRMWARE_CONFIG_ITEM FwCfgItem
;
329 EFI_PHYSICAL_ADDRESS Address
;
332 if (Allocate
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
333 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
334 return EFI_PROTOCOL_ERROR
;
337 if (Allocate
->Alignment
> EFI_PAGE_SIZE
) {
340 "%a: unsupported alignment 0x%x\n",
344 return EFI_UNSUPPORTED
;
347 Status
= QemuFwCfgFindFile ((CHAR8
*)Allocate
->File
, &FwCfgItem
, &FwCfgSize
);
348 if (EFI_ERROR (Status
)) {
351 "%a: QemuFwCfgFindFile(\"%a\"): %r\n",
359 NumPages
= EFI_SIZE_TO_PAGES (FwCfgSize
);
360 Address
= MAX_UINT64
;
361 if (OrderedCollectionFind (
362 AllocationsRestrictedTo32Bit
,
366 Address
= MAX_UINT32
;
369 Status
= gBS
->AllocatePages (
375 if (EFI_ERROR (Status
)) {
379 Blob
= AllocatePool (sizeof *Blob
);
381 Status
= EFI_OUT_OF_RESOURCES
;
385 CopyMem (Blob
->File
, Allocate
->File
, QEMU_LOADER_FNAME_SIZE
);
386 Blob
->Size
= FwCfgSize
;
387 Blob
->Base
= (VOID
*)(UINTN
)Address
;
388 Blob
->HostsOnlyTableData
= TRUE
;
390 Status
= OrderedCollectionInsert (Tracker
, NULL
, Blob
);
391 if (Status
== RETURN_ALREADY_STARTED
) {
394 "%a: duplicated file \"%a\"\n",
398 Status
= EFI_PROTOCOL_ERROR
;
401 if (EFI_ERROR (Status
)) {
405 QemuFwCfgSelectItem (FwCfgItem
);
406 QemuFwCfgReadBytes (FwCfgSize
, Blob
->Base
);
407 ZeroMem (Blob
->Base
+ Blob
->Size
, EFI_PAGES_TO_SIZE (NumPages
) - Blob
->Size
);
411 "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "
418 (UINT64
)(UINTN
)Blob
->Base
422 // Measure the data which is downloaded from QEMU.
423 // It has to be done before it is consumed. Because the data will
424 // be updated in the following operations.
426 TpmMeasureAndLogData (
428 EV_PLATFORM_CONFIG_FLAGS
,
429 EV_POSTCODE_INFO_ACPI_DATA
,
431 (VOID
*)(UINTN
)Blob
->Base
,
441 gBS
->FreePages (Address
, NumPages
);
447 Process a QEMU_LOADER_ADD_POINTER command.
449 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
451 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
452 structures created thus far.
454 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
455 AddPointer, or the AddPointer command references
456 a file unknown to Tracker, or the pointer to
457 relocate has invalid location, size, or value, or
458 the relocated pointer value is not representable
459 in the given pointer size.
461 @retval EFI_SUCCESS The pointer field inside the pointer blob has
467 ProcessCmdAddPointer (
468 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
469 IN CONST ORDERED_COLLECTION
*Tracker
472 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
477 if ((AddPointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') ||
478 (AddPointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0'))
480 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
481 return EFI_PROTOCOL_ERROR
;
484 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
485 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
486 if ((TrackerEntry
== NULL
) || (TrackerEntry2
== NULL
)) {
489 "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",
491 AddPointer
->PointerFile
,
492 AddPointer
->PointeeFile
494 return EFI_PROTOCOL_ERROR
;
497 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
498 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
499 if (((AddPointer
->PointerSize
!= 1) && (AddPointer
->PointerSize
!= 2) &&
500 (AddPointer
->PointerSize
!= 4) && (AddPointer
->PointerSize
!= 8)) ||
501 (Blob
->Size
< AddPointer
->PointerSize
) ||
502 (Blob
->Size
- AddPointer
->PointerSize
< AddPointer
->PointerOffset
))
506 "%a: invalid pointer location or size in \"%a\"\n",
508 AddPointer
->PointerFile
510 return EFI_PROTOCOL_ERROR
;
513 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
515 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
516 if (PointerValue
>= Blob2
->Size
) {
519 "%a: invalid pointer value in \"%a\"\n",
521 AddPointer
->PointerFile
523 return EFI_PROTOCOL_ERROR
;
527 // The memory allocation system ensures that the address of the byte past the
528 // last byte of any allocated object is expressible (no wraparound).
530 ASSERT ((UINTN
)Blob2
->Base
<= MAX_ADDRESS
- Blob2
->Size
);
532 PointerValue
+= (UINT64
)(UINTN
)Blob2
->Base
;
533 if ((AddPointer
->PointerSize
< 8) &&
534 (RShiftU64 (PointerValue
, AddPointer
->PointerSize
* 8) != 0))
538 "%a: relocated pointer value unrepresentable in "
541 AddPointer
->PointerFile
543 return EFI_PROTOCOL_ERROR
;
546 CopyMem (PointerField
, &PointerValue
, AddPointer
->PointerSize
);
550 "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
551 "PointerOffset=0x%x PointerSize=%d\n",
553 AddPointer
->PointerFile
,
554 AddPointer
->PointeeFile
,
555 AddPointer
->PointerOffset
,
556 AddPointer
->PointerSize
562 Process a QEMU_LOADER_ADD_CHECKSUM command.
564 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.
566 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
567 structures created thus far.
569 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
570 AddChecksum, or the AddChecksum command
571 references a file unknown to Tracker, or the
572 range to checksum is invalid.
574 @retval EFI_SUCCESS The requested range has been checksummed.
579 ProcessCmdAddChecksum (
580 IN CONST QEMU_LOADER_ADD_CHECKSUM
*AddChecksum
,
581 IN CONST ORDERED_COLLECTION
*Tracker
584 ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
587 if (AddChecksum
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
588 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
589 return EFI_PROTOCOL_ERROR
;
592 TrackerEntry
= OrderedCollectionFind (Tracker
, AddChecksum
->File
);
593 if (TrackerEntry
== NULL
) {
596 "%a: invalid blob reference \"%a\"\n",
600 return EFI_PROTOCOL_ERROR
;
603 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
604 if ((Blob
->Size
<= AddChecksum
->ResultOffset
) ||
605 (Blob
->Size
< AddChecksum
->Length
) ||
606 (Blob
->Size
- AddChecksum
->Length
< AddChecksum
->Start
))
610 "%a: invalid checksum range in \"%a\"\n",
614 return EFI_PROTOCOL_ERROR
;
617 Blob
->Base
[AddChecksum
->ResultOffset
] = CalculateCheckSum8 (
618 Blob
->Base
+ AddChecksum
->Start
,
623 "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "
627 AddChecksum
->ResultOffset
,
635 Process a QEMU_LOADER_WRITE_POINTER command.
637 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.
639 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
640 structures created thus far.
642 @param[in,out] S3Context The S3_CONTEXT object capturing the fw_cfg actions
643 of successfully processed QEMU_LOADER_WRITE_POINTER
644 commands, to be replayed at S3 resume. S3Context
645 may be NULL if S3 is disabled.
647 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
648 WritePointer. Or, the WritePointer command
649 references a file unknown to Tracker or the
650 fw_cfg directory. Or, the pointer object to
651 rewrite has invalid location, size, or initial
652 relative value. Or, the pointer value to store
653 does not fit in the given pointer size.
655 @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg
656 file has been written. If S3Context is not NULL,
657 then WritePointer has been condensed into
660 @return Error codes propagated from
661 SaveCondensedWritePointerToS3Context(). The
662 pointer object inside the writeable fw_cfg file
663 has not been written.
667 ProcessCmdWritePointer (
668 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
,
669 IN CONST ORDERED_COLLECTION
*Tracker
,
670 IN OUT S3_CONTEXT
*S3Context OPTIONAL
673 RETURN_STATUS Status
;
674 FIRMWARE_CONFIG_ITEM PointerItem
;
675 UINTN PointerItemSize
;
676 ORDERED_COLLECTION_ENTRY
*PointeeEntry
;
680 if ((WritePointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') ||
681 (WritePointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0'))
683 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
684 return EFI_PROTOCOL_ERROR
;
687 Status
= QemuFwCfgFindFile (
688 (CONST CHAR8
*)WritePointer
->PointerFile
,
692 PointeeEntry
= OrderedCollectionFind (Tracker
, WritePointer
->PointeeFile
);
693 if (RETURN_ERROR (Status
) || (PointeeEntry
== NULL
)) {
696 "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
698 WritePointer
->PointerFile
,
699 WritePointer
->PointeeFile
701 return EFI_PROTOCOL_ERROR
;
704 if (((WritePointer
->PointerSize
!= 1) && (WritePointer
->PointerSize
!= 2) &&
705 (WritePointer
->PointerSize
!= 4) && (WritePointer
->PointerSize
!= 8)) ||
706 (PointerItemSize
< WritePointer
->PointerSize
) ||
707 (PointerItemSize
- WritePointer
->PointerSize
<
708 WritePointer
->PointerOffset
))
712 "%a: invalid pointer location or size in \"%a\"\n",
714 WritePointer
->PointerFile
716 return EFI_PROTOCOL_ERROR
;
719 PointeeBlob
= OrderedCollectionUserStruct (PointeeEntry
);
720 PointerValue
= WritePointer
->PointeeOffset
;
721 if (PointerValue
>= PointeeBlob
->Size
) {
722 DEBUG ((DEBUG_ERROR
, "%a: invalid PointeeOffset\n", __FUNCTION__
));
723 return EFI_PROTOCOL_ERROR
;
727 // The memory allocation system ensures that the address of the byte past the
728 // last byte of any allocated object is expressible (no wraparound).
730 ASSERT ((UINTN
)PointeeBlob
->Base
<= MAX_ADDRESS
- PointeeBlob
->Size
);
732 PointerValue
+= (UINT64
)(UINTN
)PointeeBlob
->Base
;
733 if ((WritePointer
->PointerSize
< 8) &&
734 (RShiftU64 (PointerValue
, WritePointer
->PointerSize
* 8) != 0))
738 "%a: pointer value unrepresentable in \"%a\"\n",
740 WritePointer
->PointerFile
742 return EFI_PROTOCOL_ERROR
;
746 // If S3 is enabled, we have to capture the below fw_cfg actions in condensed
747 // form, to be replayed during S3 resume.
749 if (S3Context
!= NULL
) {
750 EFI_STATUS SaveStatus
;
752 SaveStatus
= SaveCondensedWritePointerToS3Context (
755 WritePointer
->PointerSize
,
756 WritePointer
->PointerOffset
,
759 if (EFI_ERROR (SaveStatus
)) {
764 QemuFwCfgSelectItem (PointerItem
);
765 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
766 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
769 // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob
770 // as unreleasable, for the case when the whole linker/loader script is
771 // handled successfully.
773 PointeeBlob
->HostsOnlyTableData
= FALSE
;
777 "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
778 "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n",
780 WritePointer
->PointerFile
,
781 WritePointer
->PointeeFile
,
782 WritePointer
->PointerOffset
,
783 WritePointer
->PointeeOffset
,
784 WritePointer
->PointerSize
790 Undo a QEMU_LOADER_WRITE_POINTER command.
792 This function revokes (zeroes out) a guest memory reference communicated to
793 QEMU earlier. The caller is responsible for invoking this function only on
794 such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed
795 by ProcessCmdWritePointer().
797 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
801 UndoCmdWritePointer (
802 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
805 RETURN_STATUS Status
;
806 FIRMWARE_CONFIG_ITEM PointerItem
;
807 UINTN PointerItemSize
;
810 Status
= QemuFwCfgFindFile (
811 (CONST CHAR8
*)WritePointer
->PointerFile
,
815 ASSERT_RETURN_ERROR (Status
);
818 QemuFwCfgSelectItem (PointerItem
);
819 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
820 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
824 "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n",
826 WritePointer
->PointerFile
,
827 WritePointer
->PointerOffset
,
828 WritePointer
->PointerSize
833 // We'll be saving the keys of installed tables so that we can roll them back
834 // in case of failure. 128 tables should be enough for anyone (TM).
836 #define INSTALLED_TABLES_MAX 128
839 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte
840 array is an ACPI table, and if so, install it.
842 This function assumes that the entire QEMU linker/loader command file has
843 been processed successfully in a prior first pass.
845 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
847 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
850 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
852 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
853 elements, allocated by the caller. On output,
854 the function will have stored (appended) the
855 AcpiProtocol-internal key of the ACPI table that
856 the function has installed, if the AddPointer
857 command identified an ACPI table that is
858 different from RSDT and XSDT.
860 @param[in,out] NumInstalled On input, the number of entries already used in
861 InstalledKey; it must be in [0,
862 INSTALLED_TABLES_MAX] inclusive. On output, the
863 parameter is incremented if the AddPointer
864 command identified an ACPI table that is
865 different from RSDT and XSDT.
867 @param[in,out] SeenPointers The ORDERED_COLLECTION tracking the absolute
868 target addresses that have been pointed-to by
869 QEMU_LOADER_ADD_POINTER commands thus far. If a
870 target address is encountered for the first
871 time, and it identifies an ACPI table that is
872 different from RDST and XSDT, the table is
873 installed. If a target address is seen for the
874 second or later times, it is skipped without
877 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
880 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI
881 table different from RSDT and XSDT, but there
882 was no more room in InstalledKey.
884 @retval EFI_SUCCESS AddPointer has been processed. Either its
885 absolute target address has been encountered
886 before, or an ACPI table different from RSDT
887 and XSDT has been installed (reflected by
888 InstalledKey and NumInstalled), or RSDT or
889 XSDT has been identified but not installed, or
890 the fw_cfg blob pointed-into by AddPointer has
891 been marked as hosting something else than
892 just direct ACPI table contents.
894 @return Error codes returned by
895 AcpiProtocol->InstallAcpiTable().
900 Process2ndPassCmdAddPointer (
901 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
902 IN CONST ORDERED_COLLECTION
*Tracker
,
903 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
,
904 IN OUT UINTN InstalledKey
[INSTALLED_TABLES_MAX
],
905 IN OUT INT32
*NumInstalled
,
906 IN OUT ORDERED_COLLECTION
*SeenPointers
909 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
910 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry2
;
911 ORDERED_COLLECTION_ENTRY
*SeenPointerEntry
;
914 CONST UINT8
*PointerField
;
916 UINTN Blob2Remaining
;
918 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
919 CONST EFI_ACPI_DESCRIPTION_HEADER
*Header
;
922 if ((*NumInstalled
< 0) || (*NumInstalled
> INSTALLED_TABLES_MAX
)) {
923 return EFI_INVALID_PARAMETER
;
926 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
927 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
928 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
929 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
930 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
932 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
935 // We assert that PointerValue falls inside Blob2's contents. This is ensured
936 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().
938 Blob2Remaining
= (UINTN
)Blob2
->Base
;
939 ASSERT (PointerValue
>= Blob2Remaining
);
940 Blob2Remaining
+= Blob2
->Size
;
941 ASSERT (PointerValue
< Blob2Remaining
);
943 Status
= OrderedCollectionInsert (
945 &SeenPointerEntry
, // for reverting insertion in error case
946 (VOID
*)(UINTN
)PointerValue
948 if (EFI_ERROR (Status
)) {
949 if (Status
== RETURN_ALREADY_STARTED
) {
951 // Already seen this pointer, don't try to process it again.
955 "%a: PointerValue=0x%Lx already processed, skipping.\n",
959 Status
= EFI_SUCCESS
;
965 Blob2Remaining
-= (UINTN
)PointerValue
;
968 "%a: checking for ACPI header in \"%a\" at 0x%Lx "
969 "(remaining: 0x%Lx): ",
971 AddPointer
->PointeeFile
,
973 (UINT64
)Blob2Remaining
979 // To make our job simple, the FACS has a custom header. Sigh.
981 if (sizeof *Facs
<= Blob2Remaining
) {
982 Facs
= (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)PointerValue
;
984 if ((Facs
->Length
>= sizeof *Facs
) &&
985 (Facs
->Length
<= Blob2Remaining
) &&
987 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
))
991 "found \"%-4.4a\" size 0x%x\n",
992 (CONST CHAR8
*)&Facs
->Signature
,
995 TableSize
= Facs
->Length
;
1000 // check for the uniform tables
1002 if ((TableSize
== 0) && (sizeof *Header
<= Blob2Remaining
)) {
1003 Header
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
)PointerValue
;
1005 if ((Header
->Length
>= sizeof *Header
) &&
1006 (Header
->Length
<= Blob2Remaining
) &&
1007 (CalculateSum8 ((CONST UINT8
*)Header
, Header
->Length
) == 0))
1010 // This looks very much like an ACPI table from QEMU:
1011 // - Length field consistent with both ACPI and containing blob size
1012 // - checksum is correct
1016 "found \"%-4.4a\" size 0x%x\n",
1017 (CONST CHAR8
*)&Header
->Signature
,
1020 TableSize
= Header
->Length
;
1023 // Skip RSDT and XSDT because those are handled by
1024 // EFI_ACPI_TABLE_PROTOCOL automatically.
1025 if ((Header
->Signature
==
1026 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
) ||
1027 (Header
->Signature
==
1028 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
))
1035 if (TableSize
== 0) {
1036 DEBUG ((DEBUG_VERBOSE
, "not found; marking fw_cfg blob as opaque\n"));
1037 Blob2
->HostsOnlyTableData
= FALSE
;
1041 if (*NumInstalled
== INSTALLED_TABLES_MAX
) {
1044 "%a: can't install more than %d tables\n",
1046 INSTALLED_TABLES_MAX
1048 Status
= EFI_OUT_OF_RESOURCES
;
1049 goto RollbackSeenPointer
;
1052 Status
= AcpiProtocol
->InstallAcpiTable (
1054 (VOID
*)(UINTN
)PointerValue
,
1056 &InstalledKey
[*NumInstalled
]
1058 if (EFI_ERROR (Status
)) {
1061 "%a: InstallAcpiTable(): %r\n",
1065 goto RollbackSeenPointer
;
1071 RollbackSeenPointer
:
1072 OrderedCollectionDelete (SeenPointers
, SeenPointerEntry
, NULL
);
1077 Download, process, and install ACPI table data from the QEMU loader
1080 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
1082 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU
1083 loader command with unsupported parameters
1086 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg
1089 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than
1090 INSTALLED_TABLES_MAX tables found.
1092 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.
1094 @return Status codes returned by
1095 AcpiProtocol->InstallAcpiTable().
1100 InstallQemuFwCfgTables (
1101 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
1105 FIRMWARE_CONFIG_ITEM FwCfgItem
;
1107 QEMU_LOADER_ENTRY
*LoaderStart
;
1108 CONST QEMU_LOADER_ENTRY
*LoaderEntry
, *LoaderEnd
;
1109 CONST QEMU_LOADER_ENTRY
*WritePointerSubsetEnd
;
1110 ORIGINAL_ATTRIBUTES
*OriginalPciAttributes
;
1111 UINTN OriginalPciAttributesCount
;
1112 ORDERED_COLLECTION
*AllocationsRestrictedTo32Bit
;
1113 S3_CONTEXT
*S3Context
;
1114 ORDERED_COLLECTION
*Tracker
;
1115 UINTN
*InstalledKey
;
1117 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
1118 ORDERED_COLLECTION
*SeenPointers
;
1119 ORDERED_COLLECTION_ENTRY
*SeenPointerEntry
, *SeenPointerEntry2
;
1120 EFI_HANDLE QemuAcpiHandle
;
1122 Status
= QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem
, &FwCfgSize
);
1123 if (EFI_ERROR (Status
)) {
1127 if (FwCfgSize
% sizeof *LoaderEntry
!= 0) {
1130 "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
1134 return EFI_PROTOCOL_ERROR
;
1137 LoaderStart
= AllocatePool (FwCfgSize
);
1138 if (LoaderStart
== NULL
) {
1139 return EFI_OUT_OF_RESOURCES
;
1142 EnablePciDecoding (&OriginalPciAttributes
, &OriginalPciAttributesCount
);
1143 QemuFwCfgSelectItem (FwCfgItem
);
1144 QemuFwCfgReadBytes (FwCfgSize
, LoaderStart
);
1145 RestorePciDecoding (OriginalPciAttributes
, OriginalPciAttributesCount
);
1148 // Measure the "etc/table-loader" which is downloaded from QEMU.
1149 // It has to be done before it is consumed. Because it would be
1150 // updated in the following operations.
1152 TpmMeasureAndLogData (
1154 EV_PLATFORM_CONFIG_FLAGS
,
1155 EV_POSTCODE_INFO_ACPI_DATA
,
1157 (VOID
*)(UINTN
)LoaderStart
,
1161 LoaderEnd
= LoaderStart
+ FwCfgSize
/ sizeof *LoaderEntry
;
1163 AllocationsRestrictedTo32Bit
= NULL
;
1164 Status
= CollectAllocationsRestrictedTo32Bit (
1165 &AllocationsRestrictedTo32Bit
,
1169 if (EFI_ERROR (Status
)) {
1174 if (QemuFwCfgS3Enabled ()) {
1176 // Size the allocation pessimistically, assuming that all commands in the
1177 // script are QEMU_LOADER_WRITE_POINTER commands.
1179 Status
= AllocateS3Context (&S3Context
, LoaderEnd
- LoaderStart
);
1180 if (EFI_ERROR (Status
)) {
1181 goto FreeAllocationsRestrictedTo32Bit
;
1185 Tracker
= OrderedCollectionInit (BlobCompare
, BlobKeyCompare
);
1186 if (Tracker
== NULL
) {
1187 Status
= EFI_OUT_OF_RESOURCES
;
1192 // first pass: process the commands
1194 // "WritePointerSubsetEnd" points one past the last successful
1195 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
1196 // pass, no such command has been encountered yet.
1198 WritePointerSubsetEnd
= LoaderStart
;
1199 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
1200 switch (LoaderEntry
->Type
) {
1201 case QemuLoaderCmdAllocate
:
1202 Status
= ProcessCmdAllocate (
1203 &LoaderEntry
->Command
.Allocate
,
1205 AllocationsRestrictedTo32Bit
1209 case QemuLoaderCmdAddPointer
:
1210 Status
= ProcessCmdAddPointer (
1211 &LoaderEntry
->Command
.AddPointer
,
1216 case QemuLoaderCmdAddChecksum
:
1217 Status
= ProcessCmdAddChecksum (
1218 &LoaderEntry
->Command
.AddChecksum
,
1223 case QemuLoaderCmdWritePointer
:
1224 Status
= ProcessCmdWritePointer (
1225 &LoaderEntry
->Command
.WritePointer
,
1229 if (!EFI_ERROR (Status
)) {
1230 WritePointerSubsetEnd
= LoaderEntry
+ 1;
1238 "%a: unknown loader command: 0x%x\n",
1245 if (EFI_ERROR (Status
)) {
1246 goto RollbackWritePointersAndFreeTracker
;
1250 InstalledKey
= AllocatePool (INSTALLED_TABLES_MAX
* sizeof *InstalledKey
);
1251 if (InstalledKey
== NULL
) {
1252 Status
= EFI_OUT_OF_RESOURCES
;
1253 goto RollbackWritePointersAndFreeTracker
;
1256 SeenPointers
= OrderedCollectionInit (PointerCompare
, PointerCompare
);
1257 if (SeenPointers
== NULL
) {
1258 Status
= EFI_OUT_OF_RESOURCES
;
1263 // second pass: identify and install ACPI tables
1266 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
1267 if (LoaderEntry
->Type
== QemuLoaderCmdAddPointer
) {
1268 Status
= Process2ndPassCmdAddPointer (
1269 &LoaderEntry
->Command
.AddPointer
,
1276 if (EFI_ERROR (Status
)) {
1277 goto UninstallAcpiTables
;
1283 // Install a protocol to notify that the ACPI table provided by Qemu is
1286 QemuAcpiHandle
= NULL
;
1287 Status
= gBS
->InstallProtocolInterface (
1289 &gQemuAcpiTableNotifyProtocolGuid
,
1290 EFI_NATIVE_INTERFACE
,
1293 if (EFI_ERROR (Status
)) {
1294 goto UninstallAcpiTables
;
1298 // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3
1299 // Boot Script opcodes has to be the last operation in this function, because
1300 // if it succeeds, it cannot be undone.
1302 if (S3Context
!= NULL
) {
1303 Status
= TransferS3ContextToBootScript (S3Context
);
1304 if (EFI_ERROR (Status
)) {
1305 goto UninstallQemuAcpiTableNotifyProtocol
;
1309 // Ownership of S3Context has been transferred.
1314 DEBUG ((DEBUG_INFO
, "%a: installed %d tables\n", __FUNCTION__
, Installed
));
1316 UninstallQemuAcpiTableNotifyProtocol
:
1317 if (EFI_ERROR (Status
)) {
1318 gBS
->UninstallProtocolInterface (
1320 &gQemuAcpiTableNotifyProtocolGuid
,
1325 UninstallAcpiTables
:
1326 if (EFI_ERROR (Status
)) {
1328 // roll back partial installation
1330 while (Installed
> 0) {
1332 AcpiProtocol
->UninstallAcpiTable (AcpiProtocol
, InstalledKey
[Installed
]);
1336 for (SeenPointerEntry
= OrderedCollectionMin (SeenPointers
);
1337 SeenPointerEntry
!= NULL
;
1338 SeenPointerEntry
= SeenPointerEntry2
)
1340 SeenPointerEntry2
= OrderedCollectionNext (SeenPointerEntry
);
1341 OrderedCollectionDelete (SeenPointers
, SeenPointerEntry
, NULL
);
1344 OrderedCollectionUninit (SeenPointers
);
1347 FreePool (InstalledKey
);
1349 RollbackWritePointersAndFreeTracker
:
1351 // In case of failure, revoke any allocation addresses that were communicated
1352 // to QEMU previously, before we release all the blobs.
1354 if (EFI_ERROR (Status
)) {
1355 LoaderEntry
= WritePointerSubsetEnd
;
1356 while (LoaderEntry
> LoaderStart
) {
1358 if (LoaderEntry
->Type
== QemuLoaderCmdWritePointer
) {
1359 UndoCmdWritePointer (&LoaderEntry
->Command
.WritePointer
);
1365 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
1366 // place only if we're exiting with success and the blob hosts data that is
1367 // not directly part of some ACPI table.
1369 for (TrackerEntry
= OrderedCollectionMin (Tracker
); TrackerEntry
!= NULL
;
1370 TrackerEntry
= TrackerEntry2
)
1375 TrackerEntry2
= OrderedCollectionNext (TrackerEntry
);
1376 OrderedCollectionDelete (Tracker
, TrackerEntry
, &UserStruct
);
1379 if (EFI_ERROR (Status
) || Blob
->HostsOnlyTableData
) {
1382 "%a: freeing \"%a\"\n",
1386 gBS
->FreePages ((UINTN
)Blob
->Base
, EFI_SIZE_TO_PAGES (Blob
->Size
));
1392 OrderedCollectionUninit (Tracker
);
1395 if (S3Context
!= NULL
) {
1396 ReleaseS3Context (S3Context
);
1399 FreeAllocationsRestrictedTo32Bit
:
1400 ReleaseAllocationsRestrictedTo32Bit (AllocationsRestrictedTo32Bit
);
1403 FreePool (LoaderStart
);