2 OVMF ACPI support using QEMU's fw-cfg interface
4 Copyright (C) 2012-2021, Red Hat, Inc.
5 Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>
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.
41 Compare a standalone key against a user structure containing an embedded key.
43 @param[in] StandaloneKey Pointer to the bare key.
45 @param[in] UserStruct Pointer to the user structure with the embedded
48 @retval <0 If StandaloneKey compares less than UserStruct's key.
50 @retval 0 If StandaloneKey compares equal to UserStruct's key.
52 @retval >0 If StandaloneKey compares greater than UserStruct's key.
58 IN CONST VOID
*StandaloneKey
,
59 IN CONST VOID
*UserStruct
65 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
);
98 Comparator function for two opaque pointers, ordering on (unsigned) pointer
100 Can be used as both Key and UserStruct comparator.
102 @param[in] Pointer1 First pointer.
104 @param[in] Pointer2 Second pointer.
106 @retval <0 If Pointer1 compares less than Pointer2.
108 @retval 0 If Pointer1 compares equal to Pointer2.
110 @retval >0 If Pointer1 compares greater than Pointer2.
116 IN CONST VOID
*Pointer1
,
117 IN CONST VOID
*Pointer2
120 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
);
156 Release the ORDERED_COLLECTION structure populated by
157 CollectAllocationsRestrictedTo32Bit() (below).
159 This function may be called by CollectAllocationsRestrictedTo32Bit() itself,
162 @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure to
167 ReleaseAllocationsRestrictedTo32Bit (
168 IN ORDERED_COLLECTION
*AllocationsRestrictedTo32Bit
171 ORDERED_COLLECTION_ENTRY
*Entry
, *Entry2
;
173 for (Entry
= OrderedCollectionMin (AllocationsRestrictedTo32Bit
);
176 Entry2
= OrderedCollectionNext (Entry
);
177 OrderedCollectionDelete (AllocationsRestrictedTo32Bit
, Entry
, NULL
);
179 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
) {
233 AddPointer
= &LoaderEntry
->Command
.AddPointer
;
235 if (AddPointer
->PointerSize
>= 8) {
239 if (AddPointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
240 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
241 Status
= EFI_PROTOCOL_ERROR
;
245 Status
= OrderedCollectionInsert (
248 (VOID
*)AddPointer
->PointeeFile
254 "%a: restricting blob \"%a\" from 64-bit allocation\n",
256 AddPointer
->PointeeFile
259 case EFI_ALREADY_STARTED
:
261 // The restriction has been recorded already.
264 case EFI_OUT_OF_RESOURCES
:
271 *AllocationsRestrictedTo32Bit
= Collection
;
275 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
) {
338 DEBUG ((DEBUG_ERROR
, "%a: unsupported alignment 0x%x\n", __FUNCTION__
,
339 Allocate
->Alignment
));
340 return EFI_UNSUPPORTED
;
343 Status
= QemuFwCfgFindFile ((CHAR8
*)Allocate
->File
, &FwCfgItem
, &FwCfgSize
);
344 if (EFI_ERROR (Status
)) {
345 DEBUG ((DEBUG_ERROR
, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__
,
346 Allocate
->File
, Status
));
350 NumPages
= EFI_SIZE_TO_PAGES (FwCfgSize
);
351 Address
= MAX_UINT64
;
352 if (OrderedCollectionFind (
353 AllocationsRestrictedTo32Bit
,
356 Address
= MAX_UINT32
;
358 Status
= gBS
->AllocatePages (AllocateMaxAddress
, EfiACPIMemoryNVS
, NumPages
,
360 if (EFI_ERROR (Status
)) {
364 Blob
= AllocatePool (sizeof *Blob
);
366 Status
= EFI_OUT_OF_RESOURCES
;
369 CopyMem (Blob
->File
, Allocate
->File
, QEMU_LOADER_FNAME_SIZE
);
370 Blob
->Size
= FwCfgSize
;
371 Blob
->Base
= (VOID
*)(UINTN
)Address
;
372 Blob
->HostsOnlyTableData
= TRUE
;
374 Status
= OrderedCollectionInsert (Tracker
, NULL
, Blob
);
375 if (Status
== RETURN_ALREADY_STARTED
) {
376 DEBUG ((DEBUG_ERROR
, "%a: duplicated file \"%a\"\n", __FUNCTION__
,
378 Status
= EFI_PROTOCOL_ERROR
;
380 if (EFI_ERROR (Status
)) {
384 QemuFwCfgSelectItem (FwCfgItem
);
385 QemuFwCfgReadBytes (FwCfgSize
, Blob
->Base
);
386 ZeroMem (Blob
->Base
+ Blob
->Size
, EFI_PAGES_TO_SIZE (NumPages
) - Blob
->Size
);
388 DEBUG ((DEBUG_VERBOSE
, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "
389 "Address=0x%Lx\n", __FUNCTION__
, Allocate
->File
, Allocate
->Alignment
,
390 Allocate
->Zone
, (UINT64
)Blob
->Size
, (UINT64
)(UINTN
)Blob
->Base
));
397 gBS
->FreePages (Address
, NumPages
);
404 Process a QEMU_LOADER_ADD_POINTER command.
406 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
408 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
409 structures created thus far.
411 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
412 AddPointer, or the AddPointer command references
413 a file unknown to Tracker, or the pointer to
414 relocate has invalid location, size, or value, or
415 the relocated pointer value is not representable
416 in the given pointer size.
418 @retval EFI_SUCCESS The pointer field inside the pointer blob has
424 ProcessCmdAddPointer (
425 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
426 IN CONST ORDERED_COLLECTION
*Tracker
429 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
434 if (AddPointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0' ||
435 AddPointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
436 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
437 return EFI_PROTOCOL_ERROR
;
440 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
441 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
442 if (TrackerEntry
== NULL
|| TrackerEntry2
== NULL
) {
443 DEBUG ((DEBUG_ERROR
, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",
444 __FUNCTION__
, AddPointer
->PointerFile
, AddPointer
->PointeeFile
));
445 return EFI_PROTOCOL_ERROR
;
448 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
449 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
450 if ((AddPointer
->PointerSize
!= 1 && AddPointer
->PointerSize
!= 2 &&
451 AddPointer
->PointerSize
!= 4 && AddPointer
->PointerSize
!= 8) ||
452 Blob
->Size
< AddPointer
->PointerSize
||
453 Blob
->Size
- AddPointer
->PointerSize
< AddPointer
->PointerOffset
) {
454 DEBUG ((DEBUG_ERROR
, "%a: invalid pointer location or size in \"%a\"\n",
455 __FUNCTION__
, AddPointer
->PointerFile
));
456 return EFI_PROTOCOL_ERROR
;
459 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
461 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
462 if (PointerValue
>= Blob2
->Size
) {
463 DEBUG ((DEBUG_ERROR
, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__
,
464 AddPointer
->PointerFile
));
465 return EFI_PROTOCOL_ERROR
;
469 // The memory allocation system ensures that the address of the byte past the
470 // last byte of any allocated object is expressible (no wraparound).
472 ASSERT ((UINTN
)Blob2
->Base
<= MAX_ADDRESS
- Blob2
->Size
);
474 PointerValue
+= (UINT64
)(UINTN
)Blob2
->Base
;
475 if (AddPointer
->PointerSize
< 8 &&
476 RShiftU64 (PointerValue
, AddPointer
->PointerSize
* 8) != 0) {
477 DEBUG ((DEBUG_ERROR
, "%a: relocated pointer value unrepresentable in "
478 "\"%a\"\n", __FUNCTION__
, AddPointer
->PointerFile
));
479 return EFI_PROTOCOL_ERROR
;
482 CopyMem (PointerField
, &PointerValue
, AddPointer
->PointerSize
);
484 DEBUG ((DEBUG_VERBOSE
, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
485 "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
486 AddPointer
->PointerFile
, AddPointer
->PointeeFile
,
487 AddPointer
->PointerOffset
, AddPointer
->PointerSize
));
493 Process a QEMU_LOADER_ADD_CHECKSUM command.
495 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.
497 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
498 structures created thus far.
500 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
501 AddChecksum, or the AddChecksum command
502 references a file unknown to Tracker, or the
503 range to checksum is invalid.
505 @retval EFI_SUCCESS The requested range has been checksummed.
510 ProcessCmdAddChecksum (
511 IN CONST QEMU_LOADER_ADD_CHECKSUM
*AddChecksum
,
512 IN CONST ORDERED_COLLECTION
*Tracker
515 ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
518 if (AddChecksum
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
519 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
520 return EFI_PROTOCOL_ERROR
;
523 TrackerEntry
= OrderedCollectionFind (Tracker
, AddChecksum
->File
);
524 if (TrackerEntry
== NULL
) {
525 DEBUG ((DEBUG_ERROR
, "%a: invalid blob reference \"%a\"\n", __FUNCTION__
,
527 return EFI_PROTOCOL_ERROR
;
530 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
531 if (Blob
->Size
<= AddChecksum
->ResultOffset
||
532 Blob
->Size
< AddChecksum
->Length
||
533 Blob
->Size
- AddChecksum
->Length
< AddChecksum
->Start
) {
534 DEBUG ((DEBUG_ERROR
, "%a: invalid checksum range in \"%a\"\n",
535 __FUNCTION__
, AddChecksum
->File
));
536 return EFI_PROTOCOL_ERROR
;
539 Blob
->Base
[AddChecksum
->ResultOffset
] = CalculateCheckSum8 (
540 Blob
->Base
+ AddChecksum
->Start
,
543 DEBUG ((DEBUG_VERBOSE
, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "
544 "Length=0x%x\n", __FUNCTION__
, AddChecksum
->File
,
545 AddChecksum
->ResultOffset
, AddChecksum
->Start
, AddChecksum
->Length
));
551 Process a QEMU_LOADER_WRITE_POINTER command.
553 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.
555 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
556 structures created thus far.
558 @param[in,out] S3Context The S3_CONTEXT object capturing the fw_cfg actions
559 of successfully processed QEMU_LOADER_WRITE_POINTER
560 commands, to be replayed at S3 resume. S3Context
561 may be NULL if S3 is disabled.
563 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
564 WritePointer. Or, the WritePointer command
565 references a file unknown to Tracker or the
566 fw_cfg directory. Or, the pointer object to
567 rewrite has invalid location, size, or initial
568 relative value. Or, the pointer value to store
569 does not fit in the given pointer size.
571 @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg
572 file has been written. If S3Context is not NULL,
573 then WritePointer has been condensed into
576 @return Error codes propagated from
577 SaveCondensedWritePointerToS3Context(). The
578 pointer object inside the writeable fw_cfg file
579 has not been written.
583 ProcessCmdWritePointer (
584 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
,
585 IN CONST ORDERED_COLLECTION
*Tracker
,
586 IN OUT S3_CONTEXT
*S3Context OPTIONAL
589 RETURN_STATUS Status
;
590 FIRMWARE_CONFIG_ITEM PointerItem
;
591 UINTN PointerItemSize
;
592 ORDERED_COLLECTION_ENTRY
*PointeeEntry
;
596 if (WritePointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0' ||
597 WritePointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
598 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
599 return EFI_PROTOCOL_ERROR
;
602 Status
= QemuFwCfgFindFile ((CONST CHAR8
*)WritePointer
->PointerFile
,
603 &PointerItem
, &PointerItemSize
);
604 PointeeEntry
= OrderedCollectionFind (Tracker
, WritePointer
->PointeeFile
);
605 if (RETURN_ERROR (Status
) || PointeeEntry
== NULL
) {
607 "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
608 __FUNCTION__
, WritePointer
->PointerFile
, WritePointer
->PointeeFile
));
609 return EFI_PROTOCOL_ERROR
;
612 if ((WritePointer
->PointerSize
!= 1 && WritePointer
->PointerSize
!= 2 &&
613 WritePointer
->PointerSize
!= 4 && WritePointer
->PointerSize
!= 8) ||
614 (PointerItemSize
< WritePointer
->PointerSize
) ||
615 (PointerItemSize
- WritePointer
->PointerSize
<
616 WritePointer
->PointerOffset
)) {
617 DEBUG ((DEBUG_ERROR
, "%a: invalid pointer location or size in \"%a\"\n",
618 __FUNCTION__
, WritePointer
->PointerFile
));
619 return EFI_PROTOCOL_ERROR
;
622 PointeeBlob
= OrderedCollectionUserStruct (PointeeEntry
);
623 PointerValue
= WritePointer
->PointeeOffset
;
624 if (PointerValue
>= PointeeBlob
->Size
) {
625 DEBUG ((DEBUG_ERROR
, "%a: invalid PointeeOffset\n", __FUNCTION__
));
626 return EFI_PROTOCOL_ERROR
;
630 // The memory allocation system ensures that the address of the byte past the
631 // last byte of any allocated object is expressible (no wraparound).
633 ASSERT ((UINTN
)PointeeBlob
->Base
<= MAX_ADDRESS
- PointeeBlob
->Size
);
635 PointerValue
+= (UINT64
)(UINTN
)PointeeBlob
->Base
;
636 if (WritePointer
->PointerSize
< 8 &&
637 RShiftU64 (PointerValue
, WritePointer
->PointerSize
* 8) != 0) {
638 DEBUG ((DEBUG_ERROR
, "%a: pointer value unrepresentable in \"%a\"\n",
639 __FUNCTION__
, WritePointer
->PointerFile
));
640 return EFI_PROTOCOL_ERROR
;
644 // If S3 is enabled, we have to capture the below fw_cfg actions in condensed
645 // form, to be replayed during S3 resume.
647 if (S3Context
!= NULL
) {
648 EFI_STATUS SaveStatus
;
650 SaveStatus
= SaveCondensedWritePointerToS3Context (
653 WritePointer
->PointerSize
,
654 WritePointer
->PointerOffset
,
657 if (EFI_ERROR (SaveStatus
)) {
662 QemuFwCfgSelectItem (PointerItem
);
663 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
664 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
667 // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob
668 // as unreleasable, for the case when the whole linker/loader script is
669 // handled successfully.
671 PointeeBlob
->HostsOnlyTableData
= FALSE
;
673 DEBUG ((DEBUG_VERBOSE
, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
674 "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
675 WritePointer
->PointerFile
, WritePointer
->PointeeFile
,
676 WritePointer
->PointerOffset
, WritePointer
->PointeeOffset
,
677 WritePointer
->PointerSize
));
683 Undo a QEMU_LOADER_WRITE_POINTER command.
685 This function revokes (zeroes out) a guest memory reference communicated to
686 QEMU earlier. The caller is responsible for invoking this function only on
687 such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed
688 by ProcessCmdWritePointer().
690 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
694 UndoCmdWritePointer (
695 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
698 RETURN_STATUS Status
;
699 FIRMWARE_CONFIG_ITEM PointerItem
;
700 UINTN PointerItemSize
;
703 Status
= QemuFwCfgFindFile ((CONST CHAR8
*)WritePointer
->PointerFile
,
704 &PointerItem
, &PointerItemSize
);
705 ASSERT_RETURN_ERROR (Status
);
708 QemuFwCfgSelectItem (PointerItem
);
709 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
710 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
712 DEBUG ((DEBUG_VERBOSE
,
713 "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
714 WritePointer
->PointerFile
, WritePointer
->PointerOffset
,
715 WritePointer
->PointerSize
));
720 // We'll be saving the keys of installed tables so that we can roll them back
721 // in case of failure. 128 tables should be enough for anyone (TM).
723 #define INSTALLED_TABLES_MAX 128
726 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte
727 array is an ACPI table, and if so, install it.
729 This function assumes that the entire QEMU linker/loader command file has
730 been processed successfully in a prior first pass.
732 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
734 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
737 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
739 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
740 elements, allocated by the caller. On output,
741 the function will have stored (appended) the
742 AcpiProtocol-internal key of the ACPI table that
743 the function has installed, if the AddPointer
744 command identified an ACPI table that is
745 different from RSDT and XSDT.
747 @param[in,out] NumInstalled On input, the number of entries already used in
748 InstalledKey; it must be in [0,
749 INSTALLED_TABLES_MAX] inclusive. On output, the
750 parameter is incremented if the AddPointer
751 command identified an ACPI table that is
752 different from RSDT and XSDT.
754 @param[in,out] SeenPointers The ORDERED_COLLECTION tracking the absolute
755 target addresses that have been pointed-to by
756 QEMU_LOADER_ADD_POINTER commands thus far. If a
757 target address is encountered for the first
758 time, and it identifies an ACPI table that is
759 different from RDST and XSDT, the table is
760 installed. If a target address is seen for the
761 second or later times, it is skipped without
764 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
767 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI
768 table different from RSDT and XSDT, but there
769 was no more room in InstalledKey.
771 @retval EFI_SUCCESS AddPointer has been processed. Either its
772 absolute target address has been encountered
773 before, or an ACPI table different from RSDT
774 and XSDT has been installed (reflected by
775 InstalledKey and NumInstalled), or RSDT or
776 XSDT has been identified but not installed, or
777 the fw_cfg blob pointed-into by AddPointer has
778 been marked as hosting something else than
779 just direct ACPI table contents.
781 @return Error codes returned by
782 AcpiProtocol->InstallAcpiTable().
787 Process2ndPassCmdAddPointer (
788 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
789 IN CONST ORDERED_COLLECTION
*Tracker
,
790 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
,
791 IN OUT UINTN InstalledKey
[INSTALLED_TABLES_MAX
],
792 IN OUT INT32
*NumInstalled
,
793 IN OUT ORDERED_COLLECTION
*SeenPointers
796 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
797 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry2
;
798 ORDERED_COLLECTION_ENTRY
*SeenPointerEntry
;
801 CONST UINT8
*PointerField
;
803 UINTN Blob2Remaining
;
805 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
806 CONST EFI_ACPI_DESCRIPTION_HEADER
*Header
;
809 if (*NumInstalled
< 0 || *NumInstalled
> INSTALLED_TABLES_MAX
) {
810 return EFI_INVALID_PARAMETER
;
813 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
814 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
815 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
816 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
817 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
819 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
822 // We assert that PointerValue falls inside Blob2's contents. This is ensured
823 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().
825 Blob2Remaining
= (UINTN
)Blob2
->Base
;
826 ASSERT(PointerValue
>= Blob2Remaining
);
827 Blob2Remaining
+= Blob2
->Size
;
828 ASSERT (PointerValue
< Blob2Remaining
);
830 Status
= OrderedCollectionInsert (
832 &SeenPointerEntry
, // for reverting insertion in error case
833 (VOID
*)(UINTN
)PointerValue
835 if (EFI_ERROR (Status
)) {
836 if (Status
== RETURN_ALREADY_STARTED
) {
838 // Already seen this pointer, don't try to process it again.
842 "%a: PointerValue=0x%Lx already processed, skipping.\n",
846 Status
= EFI_SUCCESS
;
851 Blob2Remaining
-= (UINTN
) PointerValue
;
852 DEBUG ((DEBUG_VERBOSE
, "%a: checking for ACPI header in \"%a\" at 0x%Lx "
853 "(remaining: 0x%Lx): ", __FUNCTION__
, AddPointer
->PointeeFile
,
854 PointerValue
, (UINT64
)Blob2Remaining
));
859 // To make our job simple, the FACS has a custom header. Sigh.
861 if (sizeof *Facs
<= Blob2Remaining
) {
862 Facs
= (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)PointerValue
;
864 if (Facs
->Length
>= sizeof *Facs
&&
865 Facs
->Length
<= Blob2Remaining
&&
867 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) {
868 DEBUG ((DEBUG_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
869 (CONST CHAR8
*)&Facs
->Signature
, Facs
->Length
));
870 TableSize
= Facs
->Length
;
875 // check for the uniform tables
877 if (TableSize
== 0 && sizeof *Header
<= Blob2Remaining
) {
878 Header
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
)PointerValue
;
880 if (Header
->Length
>= sizeof *Header
&&
881 Header
->Length
<= Blob2Remaining
&&
882 CalculateSum8 ((CONST UINT8
*)Header
, Header
->Length
) == 0) {
884 // This looks very much like an ACPI table from QEMU:
885 // - Length field consistent with both ACPI and containing blob size
886 // - checksum is correct
888 DEBUG ((DEBUG_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
889 (CONST CHAR8
*)&Header
->Signature
, Header
->Length
));
890 TableSize
= Header
->Length
;
893 // Skip RSDT and XSDT because those are handled by
894 // EFI_ACPI_TABLE_PROTOCOL automatically.
895 if (Header
->Signature
==
896 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
||
898 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
) {
904 if (TableSize
== 0) {
905 DEBUG ((DEBUG_VERBOSE
, "not found; marking fw_cfg blob as opaque\n"));
906 Blob2
->HostsOnlyTableData
= FALSE
;
910 if (*NumInstalled
== INSTALLED_TABLES_MAX
) {
911 DEBUG ((DEBUG_ERROR
, "%a: can't install more than %d tables\n",
912 __FUNCTION__
, INSTALLED_TABLES_MAX
));
913 Status
= EFI_OUT_OF_RESOURCES
;
914 goto RollbackSeenPointer
;
917 Status
= AcpiProtocol
->InstallAcpiTable (AcpiProtocol
,
918 (VOID
*)(UINTN
)PointerValue
, TableSize
,
919 &InstalledKey
[*NumInstalled
]);
920 if (EFI_ERROR (Status
)) {
921 DEBUG ((DEBUG_ERROR
, "%a: InstallAcpiTable(): %r\n", __FUNCTION__
,
923 goto RollbackSeenPointer
;
929 OrderedCollectionDelete (SeenPointers
, SeenPointerEntry
, NULL
);
935 Download, process, and install ACPI table data from the QEMU loader
938 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
940 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU
941 loader command with unsupported parameters
944 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg
947 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than
948 INSTALLED_TABLES_MAX tables found.
950 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.
952 @return Status codes returned by
953 AcpiProtocol->InstallAcpiTable().
958 InstallQemuFwCfgTables (
959 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
963 FIRMWARE_CONFIG_ITEM FwCfgItem
;
965 QEMU_LOADER_ENTRY
*LoaderStart
;
966 CONST QEMU_LOADER_ENTRY
*LoaderEntry
, *LoaderEnd
;
967 CONST QEMU_LOADER_ENTRY
*WritePointerSubsetEnd
;
968 ORIGINAL_ATTRIBUTES
*OriginalPciAttributes
;
969 UINTN OriginalPciAttributesCount
;
970 ORDERED_COLLECTION
*AllocationsRestrictedTo32Bit
;
971 S3_CONTEXT
*S3Context
;
972 ORDERED_COLLECTION
*Tracker
;
975 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
976 ORDERED_COLLECTION
*SeenPointers
;
977 ORDERED_COLLECTION_ENTRY
*SeenPointerEntry
, *SeenPointerEntry2
;
979 Status
= QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem
, &FwCfgSize
);
980 if (EFI_ERROR (Status
)) {
983 if (FwCfgSize
% sizeof *LoaderEntry
!= 0) {
984 DEBUG ((DEBUG_ERROR
, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
985 __FUNCTION__
, (UINT64
)FwCfgSize
));
986 return EFI_PROTOCOL_ERROR
;
989 LoaderStart
= AllocatePool (FwCfgSize
);
990 if (LoaderStart
== NULL
) {
991 return EFI_OUT_OF_RESOURCES
;
993 EnablePciDecoding (&OriginalPciAttributes
, &OriginalPciAttributesCount
);
994 QemuFwCfgSelectItem (FwCfgItem
);
995 QemuFwCfgReadBytes (FwCfgSize
, LoaderStart
);
996 RestorePciDecoding (OriginalPciAttributes
, OriginalPciAttributesCount
);
997 LoaderEnd
= LoaderStart
+ FwCfgSize
/ sizeof *LoaderEntry
;
999 AllocationsRestrictedTo32Bit
= NULL
;
1000 Status
= CollectAllocationsRestrictedTo32Bit (
1001 &AllocationsRestrictedTo32Bit
,
1005 if (EFI_ERROR (Status
)) {
1010 if (QemuFwCfgS3Enabled ()) {
1012 // Size the allocation pessimistically, assuming that all commands in the
1013 // script are QEMU_LOADER_WRITE_POINTER commands.
1015 Status
= AllocateS3Context (&S3Context
, LoaderEnd
- LoaderStart
);
1016 if (EFI_ERROR (Status
)) {
1017 goto FreeAllocationsRestrictedTo32Bit
;
1021 Tracker
= OrderedCollectionInit (BlobCompare
, BlobKeyCompare
);
1022 if (Tracker
== NULL
) {
1023 Status
= EFI_OUT_OF_RESOURCES
;
1028 // first pass: process the commands
1030 // "WritePointerSubsetEnd" points one past the last successful
1031 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
1032 // pass, no such command has been encountered yet.
1034 WritePointerSubsetEnd
= LoaderStart
;
1035 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
1036 switch (LoaderEntry
->Type
) {
1037 case QemuLoaderCmdAllocate
:
1038 Status
= ProcessCmdAllocate (
1039 &LoaderEntry
->Command
.Allocate
,
1041 AllocationsRestrictedTo32Bit
1045 case QemuLoaderCmdAddPointer
:
1046 Status
= ProcessCmdAddPointer (&LoaderEntry
->Command
.AddPointer
,
1050 case QemuLoaderCmdAddChecksum
:
1051 Status
= ProcessCmdAddChecksum (&LoaderEntry
->Command
.AddChecksum
,
1055 case QemuLoaderCmdWritePointer
:
1056 Status
= ProcessCmdWritePointer (&LoaderEntry
->Command
.WritePointer
,
1057 Tracker
, S3Context
);
1058 if (!EFI_ERROR (Status
)) {
1059 WritePointerSubsetEnd
= LoaderEntry
+ 1;
1064 DEBUG ((DEBUG_VERBOSE
, "%a: unknown loader command: 0x%x\n",
1065 __FUNCTION__
, LoaderEntry
->Type
));
1069 if (EFI_ERROR (Status
)) {
1070 goto RollbackWritePointersAndFreeTracker
;
1074 InstalledKey
= AllocatePool (INSTALLED_TABLES_MAX
* sizeof *InstalledKey
);
1075 if (InstalledKey
== NULL
) {
1076 Status
= EFI_OUT_OF_RESOURCES
;
1077 goto RollbackWritePointersAndFreeTracker
;
1080 SeenPointers
= OrderedCollectionInit (PointerCompare
, PointerCompare
);
1081 if (SeenPointers
== NULL
) {
1082 Status
= EFI_OUT_OF_RESOURCES
;
1087 // second pass: identify and install ACPI tables
1090 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
1091 if (LoaderEntry
->Type
== QemuLoaderCmdAddPointer
) {
1092 Status
= Process2ndPassCmdAddPointer (
1093 &LoaderEntry
->Command
.AddPointer
,
1100 if (EFI_ERROR (Status
)) {
1101 goto UninstallAcpiTables
;
1107 // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3
1108 // Boot Script opcodes has to be the last operation in this function, because
1109 // if it succeeds, it cannot be undone.
1111 if (S3Context
!= NULL
) {
1112 Status
= TransferS3ContextToBootScript (S3Context
);
1113 if (EFI_ERROR (Status
)) {
1114 goto UninstallAcpiTables
;
1117 // Ownership of S3Context has been transferred.
1122 UninstallAcpiTables
:
1123 if (EFI_ERROR (Status
)) {
1125 // roll back partial installation
1127 while (Installed
> 0) {
1129 AcpiProtocol
->UninstallAcpiTable (AcpiProtocol
, InstalledKey
[Installed
]);
1132 DEBUG ((DEBUG_INFO
, "%a: installed %d tables\n", __FUNCTION__
, Installed
));
1135 for (SeenPointerEntry
= OrderedCollectionMin (SeenPointers
);
1136 SeenPointerEntry
!= NULL
;
1137 SeenPointerEntry
= SeenPointerEntry2
) {
1138 SeenPointerEntry2
= OrderedCollectionNext (SeenPointerEntry
);
1139 OrderedCollectionDelete (SeenPointers
, SeenPointerEntry
, NULL
);
1141 OrderedCollectionUninit (SeenPointers
);
1144 FreePool (InstalledKey
);
1146 RollbackWritePointersAndFreeTracker
:
1148 // In case of failure, revoke any allocation addresses that were communicated
1149 // to QEMU previously, before we release all the blobs.
1151 if (EFI_ERROR (Status
)) {
1152 LoaderEntry
= WritePointerSubsetEnd
;
1153 while (LoaderEntry
> LoaderStart
) {
1155 if (LoaderEntry
->Type
== QemuLoaderCmdWritePointer
) {
1156 UndoCmdWritePointer (&LoaderEntry
->Command
.WritePointer
);
1162 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
1163 // place only if we're exiting with success and the blob hosts data that is
1164 // not directly part of some ACPI table.
1166 for (TrackerEntry
= OrderedCollectionMin (Tracker
); TrackerEntry
!= NULL
;
1167 TrackerEntry
= TrackerEntry2
) {
1171 TrackerEntry2
= OrderedCollectionNext (TrackerEntry
);
1172 OrderedCollectionDelete (Tracker
, TrackerEntry
, &UserStruct
);
1175 if (EFI_ERROR (Status
) || Blob
->HostsOnlyTableData
) {
1176 DEBUG ((DEBUG_VERBOSE
, "%a: freeing \"%a\"\n", __FUNCTION__
,
1178 gBS
->FreePages ((UINTN
)Blob
->Base
, EFI_SIZE_TO_PAGES (Blob
->Size
));
1182 OrderedCollectionUninit (Tracker
);
1185 if (S3Context
!= NULL
) {
1186 ReleaseS3Context (S3Context
);
1189 FreeAllocationsRestrictedTo32Bit
:
1190 ReleaseAllocationsRestrictedTo32Bit (AllocationsRestrictedTo32Bit
);
1193 FreePool (LoaderStart
);