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 "AcpiPlatform.h"
12 #include "QemuLoader.h"
13 #include <Library/BaseMemoryLib.h>
14 #include <Library/MemoryAllocationLib.h>
15 #include <Library/QemuFwCfgLib.h>
16 #include <Library/QemuFwCfgS3Lib.h>
17 #include <Library/DxeServicesTableLib.h>
18 #include <Library/PcdLib.h>
19 #include <Library/OrderedCollectionLib.h>
20 #include <IndustryStandard/Acpi.h>
24 // The user structure for the ordered collection that will track the fw_cfg
25 // blobs under processing.
28 UINT8 File
[QEMU_LOADER_FNAME_SIZE
]; // NUL-terminated name of the fw_cfg
29 // blob. This is the ordering / search
31 UINTN Size
; // The number of bytes in this blob.
32 UINT8
*Base
; // Pointer to the blob data.
33 BOOLEAN HostsOnlyTableData
; // TRUE iff the blob has been found to
34 // only contain data that is directly
35 // 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
);
69 Comparator function for two user structures.
71 @param[in] UserStruct1 Pointer to the first user structure.
73 @param[in] UserStruct2 Pointer to the second user structure.
75 @retval <0 If UserStruct1 compares less than UserStruct2.
77 @retval 0 If UserStruct1 compares equal to UserStruct2.
79 @retval >0 If UserStruct1 compares greater than UserStruct2.
85 IN CONST VOID
*UserStruct1
,
86 IN CONST VOID
*UserStruct2
92 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
) {
122 if ((UINTN
)Pointer1
< (UINTN
)Pointer2
) {
130 Comparator function for two ASCII strings. Can be used as both Key and
131 UserStruct comparator.
133 This function exists solely so we can avoid casting &AsciiStrCmp to
134 ORDERED_COLLECTION_USER_COMPARE and ORDERED_COLLECTION_KEY_COMPARE.
136 @param[in] AsciiString1 Pointer to the first ASCII string.
138 @param[in] AsciiString2 Pointer to the second ASCII string.
140 @return The return value of AsciiStrCmp (AsciiString1, AsciiString2).
146 IN CONST VOID
*AsciiString1
,
147 IN CONST VOID
*AsciiString2
150 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
);
175 Entry2
= OrderedCollectionNext (Entry
);
176 OrderedCollectionDelete (AllocationsRestrictedTo32Bit
, Entry
, NULL
);
178 OrderedCollectionUninit (AllocationsRestrictedTo32Bit
);
183 Iterate over the linker/loader script, and collect the names of the fw_cfg
184 blobs that are referenced by QEMU_LOADER_ADD_POINTER.PointeeFile fields, such
185 that QEMU_LOADER_ADD_POINTER.PointerSize is less than 8. This means that the
186 pointee blob's address will have to be patched into a narrower-than-8 byte
187 pointer field, hence the pointee blob must not be allocated from 64-bit
190 @param[out] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure
191 linking (not copying / owning) such
192 QEMU_LOADER_ADD_POINTER.PointeeFile
193 fields that name the blobs
194 restricted from 64-bit allocation.
196 @param[in] LoaderStart Points to the first entry in the
197 linker/loader script.
199 @param[in] LoaderEnd Points one past the last entry in
200 the linker/loader script.
202 @retval EFI_SUCCESS AllocationsRestrictedTo32Bit has been
205 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
207 @retval EFI_PROTOCOL_ERROR Invalid linker/loader script contents.
211 CollectAllocationsRestrictedTo32Bit (
212 OUT ORDERED_COLLECTION
**AllocationsRestrictedTo32Bit
,
213 IN CONST QEMU_LOADER_ENTRY
*LoaderStart
,
214 IN CONST QEMU_LOADER_ENTRY
*LoaderEnd
217 ORDERED_COLLECTION
*Collection
;
218 CONST QEMU_LOADER_ENTRY
*LoaderEntry
;
221 Collection
= OrderedCollectionInit (AsciiStringCompare
, AsciiStringCompare
);
222 if (Collection
== NULL
) {
223 return EFI_OUT_OF_RESOURCES
;
226 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
227 CONST QEMU_LOADER_ADD_POINTER
*AddPointer
;
229 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
);
280 Process a QEMU_LOADER_ALLOCATE command.
282 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to
285 @param[in,out] Tracker The ORDERED_COLLECTION tracking the
286 BLOB user structures created thus
289 @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION populated by
291 CollectAllocationsRestrictedTo32Bit,
292 naming the fw_cfg blobs that must
293 not be allocated from 64-bit address
296 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been
297 allocated for the blob contents, and the
298 contents have been saved. A BLOB object (user
299 structure) has been allocated from pool memory,
300 referencing the blob contents. The BLOB user
301 structure has been linked into Tracker.
303 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
304 Allocate, or the Allocate command references a
305 file that is already known by Tracker.
307 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in
310 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.
312 @return Error codes from QemuFwCfgFindFile() and
313 gBS->AllocatePages().
319 IN CONST QEMU_LOADER_ALLOCATE
*Allocate
,
320 IN OUT ORDERED_COLLECTION
*Tracker
,
321 IN ORDERED_COLLECTION
*AllocationsRestrictedTo32Bit
324 FIRMWARE_CONFIG_ITEM FwCfgItem
;
328 EFI_PHYSICAL_ADDRESS Address
;
331 if (Allocate
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
332 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
333 return EFI_PROTOCOL_ERROR
;
336 if (Allocate
->Alignment
> EFI_PAGE_SIZE
) {
337 DEBUG ((EFI_D_ERROR
, "%a: unsupported alignment 0x%x\n", __FUNCTION__
,
338 Allocate
->Alignment
));
339 return EFI_UNSUPPORTED
;
342 Status
= QemuFwCfgFindFile ((CHAR8
*)Allocate
->File
, &FwCfgItem
, &FwCfgSize
);
343 if (EFI_ERROR (Status
)) {
344 DEBUG ((EFI_D_ERROR
, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__
,
345 Allocate
->File
, Status
));
349 NumPages
= EFI_SIZE_TO_PAGES (FwCfgSize
);
350 Address
= MAX_UINT64
;
351 if (OrderedCollectionFind (
352 AllocationsRestrictedTo32Bit
,
355 Address
= MAX_UINT32
;
357 Status
= gBS
->AllocatePages (AllocateMaxAddress
, EfiACPIMemoryNVS
, NumPages
,
359 if (EFI_ERROR (Status
)) {
363 Blob
= AllocatePool (sizeof *Blob
);
365 Status
= EFI_OUT_OF_RESOURCES
;
368 CopyMem (Blob
->File
, Allocate
->File
, QEMU_LOADER_FNAME_SIZE
);
369 Blob
->Size
= FwCfgSize
;
370 Blob
->Base
= (VOID
*)(UINTN
)Address
;
371 Blob
->HostsOnlyTableData
= TRUE
;
373 Status
= OrderedCollectionInsert (Tracker
, NULL
, Blob
);
374 if (Status
== RETURN_ALREADY_STARTED
) {
375 DEBUG ((EFI_D_ERROR
, "%a: duplicated file \"%a\"\n", __FUNCTION__
,
377 Status
= EFI_PROTOCOL_ERROR
;
379 if (EFI_ERROR (Status
)) {
383 QemuFwCfgSelectItem (FwCfgItem
);
384 QemuFwCfgReadBytes (FwCfgSize
, Blob
->Base
);
385 ZeroMem (Blob
->Base
+ Blob
->Size
, EFI_PAGES_TO_SIZE (NumPages
) - Blob
->Size
);
387 DEBUG ((EFI_D_VERBOSE
, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "
388 "Address=0x%Lx\n", __FUNCTION__
, Allocate
->File
, Allocate
->Alignment
,
389 Allocate
->Zone
, (UINT64
)Blob
->Size
, (UINT64
)(UINTN
)Blob
->Base
));
396 gBS
->FreePages (Address
, NumPages
);
403 Process a QEMU_LOADER_ADD_POINTER command.
405 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
407 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
408 structures created thus far.
410 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
411 AddPointer, or the AddPointer command references
412 a file unknown to Tracker, or the pointer to
413 relocate has invalid location, size, or value, or
414 the relocated pointer value is not representable
415 in the given pointer size.
417 @retval EFI_SUCCESS The pointer field inside the pointer blob has
423 ProcessCmdAddPointer (
424 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
425 IN CONST ORDERED_COLLECTION
*Tracker
428 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
433 if (AddPointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0' ||
434 AddPointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
435 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
436 return EFI_PROTOCOL_ERROR
;
439 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
440 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
441 if (TrackerEntry
== NULL
|| TrackerEntry2
== NULL
) {
442 DEBUG ((EFI_D_ERROR
, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",
443 __FUNCTION__
, AddPointer
->PointerFile
, AddPointer
->PointeeFile
));
444 return EFI_PROTOCOL_ERROR
;
447 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
448 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
449 if ((AddPointer
->PointerSize
!= 1 && AddPointer
->PointerSize
!= 2 &&
450 AddPointer
->PointerSize
!= 4 && AddPointer
->PointerSize
!= 8) ||
451 Blob
->Size
< AddPointer
->PointerSize
||
452 Blob
->Size
- AddPointer
->PointerSize
< AddPointer
->PointerOffset
) {
453 DEBUG ((EFI_D_ERROR
, "%a: invalid pointer location or size in \"%a\"\n",
454 __FUNCTION__
, AddPointer
->PointerFile
));
455 return EFI_PROTOCOL_ERROR
;
458 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
460 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
461 if (PointerValue
>= Blob2
->Size
) {
462 DEBUG ((EFI_D_ERROR
, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__
,
463 AddPointer
->PointerFile
));
464 return EFI_PROTOCOL_ERROR
;
468 // The memory allocation system ensures that the address of the byte past the
469 // last byte of any allocated object is expressible (no wraparound).
471 ASSERT ((UINTN
)Blob2
->Base
<= MAX_ADDRESS
- Blob2
->Size
);
473 PointerValue
+= (UINT64
)(UINTN
)Blob2
->Base
;
474 if (AddPointer
->PointerSize
< 8 &&
475 RShiftU64 (PointerValue
, AddPointer
->PointerSize
* 8) != 0) {
476 DEBUG ((EFI_D_ERROR
, "%a: relocated pointer value unrepresentable in "
477 "\"%a\"\n", __FUNCTION__
, AddPointer
->PointerFile
));
478 return EFI_PROTOCOL_ERROR
;
481 CopyMem (PointerField
, &PointerValue
, AddPointer
->PointerSize
);
483 DEBUG ((EFI_D_VERBOSE
, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
484 "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
485 AddPointer
->PointerFile
, AddPointer
->PointeeFile
,
486 AddPointer
->PointerOffset
, AddPointer
->PointerSize
));
492 Process a QEMU_LOADER_ADD_CHECKSUM command.
494 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.
496 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
497 structures created thus far.
499 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
500 AddChecksum, or the AddChecksum command
501 references a file unknown to Tracker, or the
502 range to checksum is invalid.
504 @retval EFI_SUCCESS The requested range has been checksummed.
509 ProcessCmdAddChecksum (
510 IN CONST QEMU_LOADER_ADD_CHECKSUM
*AddChecksum
,
511 IN CONST ORDERED_COLLECTION
*Tracker
514 ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
517 if (AddChecksum
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
518 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
519 return EFI_PROTOCOL_ERROR
;
522 TrackerEntry
= OrderedCollectionFind (Tracker
, AddChecksum
->File
);
523 if (TrackerEntry
== NULL
) {
524 DEBUG ((EFI_D_ERROR
, "%a: invalid blob reference \"%a\"\n", __FUNCTION__
,
526 return EFI_PROTOCOL_ERROR
;
529 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
530 if (Blob
->Size
<= AddChecksum
->ResultOffset
||
531 Blob
->Size
< AddChecksum
->Length
||
532 Blob
->Size
- AddChecksum
->Length
< AddChecksum
->Start
) {
533 DEBUG ((EFI_D_ERROR
, "%a: invalid checksum range in \"%a\"\n",
534 __FUNCTION__
, AddChecksum
->File
));
535 return EFI_PROTOCOL_ERROR
;
538 Blob
->Base
[AddChecksum
->ResultOffset
] = CalculateCheckSum8 (
539 Blob
->Base
+ AddChecksum
->Start
,
542 DEBUG ((EFI_D_VERBOSE
, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "
543 "Length=0x%x\n", __FUNCTION__
, AddChecksum
->File
,
544 AddChecksum
->ResultOffset
, AddChecksum
->Start
, AddChecksum
->Length
));
550 Process a QEMU_LOADER_WRITE_POINTER command.
552 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.
554 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
555 structures created thus far.
557 @param[in,out] S3Context The S3_CONTEXT object capturing the fw_cfg actions
558 of successfully processed QEMU_LOADER_WRITE_POINTER
559 commands, to be replayed at S3 resume. S3Context
560 may be NULL if S3 is disabled.
562 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
563 WritePointer. Or, the WritePointer command
564 references a file unknown to Tracker or the
565 fw_cfg directory. Or, the pointer object to
566 rewrite has invalid location, size, or initial
567 relative value. Or, the pointer value to store
568 does not fit in the given pointer size.
570 @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg
571 file has been written. If S3Context is not NULL,
572 then WritePointer has been condensed into
575 @return Error codes propagated from
576 SaveCondensedWritePointerToS3Context(). The
577 pointer object inside the writeable fw_cfg file
578 has not been written.
582 ProcessCmdWritePointer (
583 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
,
584 IN CONST ORDERED_COLLECTION
*Tracker
,
585 IN OUT S3_CONTEXT
*S3Context OPTIONAL
588 RETURN_STATUS Status
;
589 FIRMWARE_CONFIG_ITEM PointerItem
;
590 UINTN PointerItemSize
;
591 ORDERED_COLLECTION_ENTRY
*PointeeEntry
;
595 if (WritePointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0' ||
596 WritePointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
597 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
598 return EFI_PROTOCOL_ERROR
;
601 Status
= QemuFwCfgFindFile ((CONST CHAR8
*)WritePointer
->PointerFile
,
602 &PointerItem
, &PointerItemSize
);
603 PointeeEntry
= OrderedCollectionFind (Tracker
, WritePointer
->PointeeFile
);
604 if (RETURN_ERROR (Status
) || PointeeEntry
== NULL
) {
606 "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
607 __FUNCTION__
, WritePointer
->PointerFile
, WritePointer
->PointeeFile
));
608 return EFI_PROTOCOL_ERROR
;
611 if ((WritePointer
->PointerSize
!= 1 && WritePointer
->PointerSize
!= 2 &&
612 WritePointer
->PointerSize
!= 4 && WritePointer
->PointerSize
!= 8) ||
613 (PointerItemSize
< WritePointer
->PointerSize
) ||
614 (PointerItemSize
- WritePointer
->PointerSize
<
615 WritePointer
->PointerOffset
)) {
616 DEBUG ((DEBUG_ERROR
, "%a: invalid pointer location or size in \"%a\"\n",
617 __FUNCTION__
, WritePointer
->PointerFile
));
618 return EFI_PROTOCOL_ERROR
;
621 PointeeBlob
= OrderedCollectionUserStruct (PointeeEntry
);
622 PointerValue
= WritePointer
->PointeeOffset
;
623 if (PointerValue
>= PointeeBlob
->Size
) {
624 DEBUG ((DEBUG_ERROR
, "%a: invalid PointeeOffset\n", __FUNCTION__
));
625 return EFI_PROTOCOL_ERROR
;
629 // The memory allocation system ensures that the address of the byte past the
630 // last byte of any allocated object is expressible (no wraparound).
632 ASSERT ((UINTN
)PointeeBlob
->Base
<= MAX_ADDRESS
- PointeeBlob
->Size
);
634 PointerValue
+= (UINT64
)(UINTN
)PointeeBlob
->Base
;
635 if (WritePointer
->PointerSize
< 8 &&
636 RShiftU64 (PointerValue
, WritePointer
->PointerSize
* 8) != 0) {
637 DEBUG ((DEBUG_ERROR
, "%a: pointer value unrepresentable in \"%a\"\n",
638 __FUNCTION__
, WritePointer
->PointerFile
));
639 return EFI_PROTOCOL_ERROR
;
643 // If S3 is enabled, we have to capture the below fw_cfg actions in condensed
644 // form, to be replayed during S3 resume.
646 if (S3Context
!= NULL
) {
647 EFI_STATUS SaveStatus
;
649 SaveStatus
= SaveCondensedWritePointerToS3Context (
652 WritePointer
->PointerSize
,
653 WritePointer
->PointerOffset
,
656 if (EFI_ERROR (SaveStatus
)) {
661 QemuFwCfgSelectItem (PointerItem
);
662 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
663 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
666 // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob
667 // as unreleasable, for the case when the whole linker/loader script is
668 // handled successfully.
670 PointeeBlob
->HostsOnlyTableData
= FALSE
;
672 DEBUG ((DEBUG_VERBOSE
, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
673 "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
674 WritePointer
->PointerFile
, WritePointer
->PointeeFile
,
675 WritePointer
->PointerOffset
, WritePointer
->PointeeOffset
,
676 WritePointer
->PointerSize
));
682 Undo a QEMU_LOADER_WRITE_POINTER command.
684 This function revokes (zeroes out) a guest memory reference communicated to
685 QEMU earlier. The caller is responsible for invoking this function only on
686 such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed
687 by ProcessCmdWritePointer().
689 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
693 UndoCmdWritePointer (
694 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
697 RETURN_STATUS Status
;
698 FIRMWARE_CONFIG_ITEM PointerItem
;
699 UINTN PointerItemSize
;
702 Status
= QemuFwCfgFindFile ((CONST CHAR8
*)WritePointer
->PointerFile
,
703 &PointerItem
, &PointerItemSize
);
704 ASSERT_RETURN_ERROR (Status
);
707 QemuFwCfgSelectItem (PointerItem
);
708 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
709 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
711 DEBUG ((DEBUG_VERBOSE
,
712 "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
713 WritePointer
->PointerFile
, WritePointer
->PointerOffset
,
714 WritePointer
->PointerSize
));
719 // We'll be saving the keys of installed tables so that we can roll them back
720 // in case of failure. 128 tables should be enough for anyone (TM).
722 #define INSTALLED_TABLES_MAX 128
725 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte
726 array is an ACPI table, and if so, install it.
728 This function assumes that the entire QEMU linker/loader command file has
729 been processed successfully in a prior first pass.
731 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
733 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
736 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
738 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
739 elements, allocated by the caller. On output,
740 the function will have stored (appended) the
741 AcpiProtocol-internal key of the ACPI table that
742 the function has installed, if the AddPointer
743 command identified an ACPI table that is
744 different from RSDT and XSDT.
746 @param[in,out] NumInstalled On input, the number of entries already used in
747 InstalledKey; it must be in [0,
748 INSTALLED_TABLES_MAX] inclusive. On output, the
749 parameter is incremented if the AddPointer
750 command identified an ACPI table that is
751 different from RSDT and XSDT.
753 @param[in,out] SeenPointers The ORDERED_COLLECTION tracking the absolute
754 target addresses that have been pointed-to by
755 QEMU_LOADER_ADD_POINTER commands thus far. If a
756 target address is encountered for the first
757 time, and it identifies an ACPI table that is
758 different from RDST and XSDT, the table is
759 installed. If a target address is seen for the
760 second or later times, it is skipped without
763 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
766 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI
767 table different from RSDT and XSDT, but there
768 was no more room in InstalledKey.
770 @retval EFI_SUCCESS AddPointer has been processed. Either its
771 absolute target address has been encountered
772 before, or an ACPI table different from RSDT
773 and XSDT has been installed (reflected by
774 InstalledKey and NumInstalled), or RSDT or
775 XSDT has been identified but not installed, or
776 the fw_cfg blob pointed-into by AddPointer has
777 been marked as hosting something else than
778 just direct ACPI table contents.
780 @return Error codes returned by
781 AcpiProtocol->InstallAcpiTable().
786 Process2ndPassCmdAddPointer (
787 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
788 IN CONST ORDERED_COLLECTION
*Tracker
,
789 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
,
790 IN OUT UINTN InstalledKey
[INSTALLED_TABLES_MAX
],
791 IN OUT INT32
*NumInstalled
,
792 IN OUT ORDERED_COLLECTION
*SeenPointers
795 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
796 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry2
;
797 ORDERED_COLLECTION_ENTRY
*SeenPointerEntry
;
800 CONST UINT8
*PointerField
;
802 UINTN Blob2Remaining
;
804 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
805 CONST EFI_ACPI_DESCRIPTION_HEADER
*Header
;
808 if (*NumInstalled
< 0 || *NumInstalled
> INSTALLED_TABLES_MAX
) {
809 return EFI_INVALID_PARAMETER
;
812 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
813 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
814 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
815 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
816 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
818 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
821 // We assert that PointerValue falls inside Blob2's contents. This is ensured
822 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().
824 Blob2Remaining
= (UINTN
)Blob2
->Base
;
825 ASSERT(PointerValue
>= Blob2Remaining
);
826 Blob2Remaining
+= Blob2
->Size
;
827 ASSERT (PointerValue
< Blob2Remaining
);
829 Status
= OrderedCollectionInsert (
831 &SeenPointerEntry
, // for reverting insertion in error case
832 (VOID
*)(UINTN
)PointerValue
834 if (EFI_ERROR (Status
)) {
835 if (Status
== RETURN_ALREADY_STARTED
) {
837 // Already seen this pointer, don't try to process it again.
841 "%a: PointerValue=0x%Lx already processed, skipping.\n",
845 Status
= EFI_SUCCESS
;
850 Blob2Remaining
-= (UINTN
) PointerValue
;
851 DEBUG ((EFI_D_VERBOSE
, "%a: checking for ACPI header in \"%a\" at 0x%Lx "
852 "(remaining: 0x%Lx): ", __FUNCTION__
, AddPointer
->PointeeFile
,
853 PointerValue
, (UINT64
)Blob2Remaining
));
858 // To make our job simple, the FACS has a custom header. Sigh.
860 if (sizeof *Facs
<= Blob2Remaining
) {
861 Facs
= (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)PointerValue
;
863 if (Facs
->Length
>= sizeof *Facs
&&
864 Facs
->Length
<= Blob2Remaining
&&
866 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) {
867 DEBUG ((EFI_D_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
868 (CONST CHAR8
*)&Facs
->Signature
, Facs
->Length
));
869 TableSize
= Facs
->Length
;
874 // check for the uniform tables
876 if (TableSize
== 0 && sizeof *Header
<= Blob2Remaining
) {
877 Header
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
)PointerValue
;
879 if (Header
->Length
>= sizeof *Header
&&
880 Header
->Length
<= Blob2Remaining
&&
881 CalculateSum8 ((CONST UINT8
*)Header
, Header
->Length
) == 0) {
883 // This looks very much like an ACPI table from QEMU:
884 // - Length field consistent with both ACPI and containing blob size
885 // - checksum is correct
887 DEBUG ((EFI_D_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
888 (CONST CHAR8
*)&Header
->Signature
, Header
->Length
));
889 TableSize
= Header
->Length
;
892 // Skip RSDT and XSDT because those are handled by
893 // EFI_ACPI_TABLE_PROTOCOL automatically.
894 if (Header
->Signature
==
895 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
||
897 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
) {
903 if (TableSize
== 0) {
904 DEBUG ((EFI_D_VERBOSE
, "not found; marking fw_cfg blob as opaque\n"));
905 Blob2
->HostsOnlyTableData
= FALSE
;
909 if (*NumInstalled
== INSTALLED_TABLES_MAX
) {
910 DEBUG ((EFI_D_ERROR
, "%a: can't install more than %d tables\n",
911 __FUNCTION__
, INSTALLED_TABLES_MAX
));
912 Status
= EFI_OUT_OF_RESOURCES
;
913 goto RollbackSeenPointer
;
916 Status
= AcpiProtocol
->InstallAcpiTable (AcpiProtocol
,
917 (VOID
*)(UINTN
)PointerValue
, TableSize
,
918 &InstalledKey
[*NumInstalled
]);
919 if (EFI_ERROR (Status
)) {
920 DEBUG ((EFI_D_ERROR
, "%a: InstallAcpiTable(): %r\n", __FUNCTION__
,
922 goto RollbackSeenPointer
;
928 OrderedCollectionDelete (SeenPointers
, SeenPointerEntry
, NULL
);
934 Download, process, and install ACPI table data from the QEMU loader
937 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
939 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU
940 loader command with unsupported parameters
943 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg
946 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than
947 INSTALLED_TABLES_MAX tables found.
949 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.
951 @return Status codes returned by
952 AcpiProtocol->InstallAcpiTable().
957 InstallQemuFwCfgTables (
958 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
962 FIRMWARE_CONFIG_ITEM FwCfgItem
;
964 QEMU_LOADER_ENTRY
*LoaderStart
;
965 CONST QEMU_LOADER_ENTRY
*LoaderEntry
, *LoaderEnd
;
966 CONST QEMU_LOADER_ENTRY
*WritePointerSubsetEnd
;
967 ORIGINAL_ATTRIBUTES
*OriginalPciAttributes
;
968 UINTN OriginalPciAttributesCount
;
969 ORDERED_COLLECTION
*AllocationsRestrictedTo32Bit
;
970 S3_CONTEXT
*S3Context
;
971 ORDERED_COLLECTION
*Tracker
;
974 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
975 ORDERED_COLLECTION
*SeenPointers
;
976 ORDERED_COLLECTION_ENTRY
*SeenPointerEntry
, *SeenPointerEntry2
;
978 Status
= QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem
, &FwCfgSize
);
979 if (EFI_ERROR (Status
)) {
982 if (FwCfgSize
% sizeof *LoaderEntry
!= 0) {
983 DEBUG ((EFI_D_ERROR
, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
984 __FUNCTION__
, (UINT64
)FwCfgSize
));
985 return EFI_PROTOCOL_ERROR
;
988 LoaderStart
= AllocatePool (FwCfgSize
);
989 if (LoaderStart
== NULL
) {
990 return EFI_OUT_OF_RESOURCES
;
992 EnablePciDecoding (&OriginalPciAttributes
, &OriginalPciAttributesCount
);
993 QemuFwCfgSelectItem (FwCfgItem
);
994 QemuFwCfgReadBytes (FwCfgSize
, LoaderStart
);
995 RestorePciDecoding (OriginalPciAttributes
, OriginalPciAttributesCount
);
996 LoaderEnd
= LoaderStart
+ FwCfgSize
/ sizeof *LoaderEntry
;
998 AllocationsRestrictedTo32Bit
= NULL
;
999 Status
= CollectAllocationsRestrictedTo32Bit (
1000 &AllocationsRestrictedTo32Bit
,
1004 if (EFI_ERROR (Status
)) {
1009 if (QemuFwCfgS3Enabled ()) {
1011 // Size the allocation pessimistically, assuming that all commands in the
1012 // script are QEMU_LOADER_WRITE_POINTER commands.
1014 Status
= AllocateS3Context (&S3Context
, LoaderEnd
- LoaderStart
);
1015 if (EFI_ERROR (Status
)) {
1016 goto FreeAllocationsRestrictedTo32Bit
;
1020 Tracker
= OrderedCollectionInit (BlobCompare
, BlobKeyCompare
);
1021 if (Tracker
== NULL
) {
1022 Status
= EFI_OUT_OF_RESOURCES
;
1027 // first pass: process the commands
1029 // "WritePointerSubsetEnd" points one past the last successful
1030 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
1031 // pass, no such command has been encountered yet.
1033 WritePointerSubsetEnd
= LoaderStart
;
1034 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
1035 switch (LoaderEntry
->Type
) {
1036 case QemuLoaderCmdAllocate
:
1037 Status
= ProcessCmdAllocate (
1038 &LoaderEntry
->Command
.Allocate
,
1040 AllocationsRestrictedTo32Bit
1044 case QemuLoaderCmdAddPointer
:
1045 Status
= ProcessCmdAddPointer (&LoaderEntry
->Command
.AddPointer
,
1049 case QemuLoaderCmdAddChecksum
:
1050 Status
= ProcessCmdAddChecksum (&LoaderEntry
->Command
.AddChecksum
,
1054 case QemuLoaderCmdWritePointer
:
1055 Status
= ProcessCmdWritePointer (&LoaderEntry
->Command
.WritePointer
,
1056 Tracker
, S3Context
);
1057 if (!EFI_ERROR (Status
)) {
1058 WritePointerSubsetEnd
= LoaderEntry
+ 1;
1063 DEBUG ((EFI_D_VERBOSE
, "%a: unknown loader command: 0x%x\n",
1064 __FUNCTION__
, LoaderEntry
->Type
));
1068 if (EFI_ERROR (Status
)) {
1069 goto RollbackWritePointersAndFreeTracker
;
1073 InstalledKey
= AllocatePool (INSTALLED_TABLES_MAX
* sizeof *InstalledKey
);
1074 if (InstalledKey
== NULL
) {
1075 Status
= EFI_OUT_OF_RESOURCES
;
1076 goto RollbackWritePointersAndFreeTracker
;
1079 SeenPointers
= OrderedCollectionInit (PointerCompare
, PointerCompare
);
1080 if (SeenPointers
== NULL
) {
1081 Status
= EFI_OUT_OF_RESOURCES
;
1086 // second pass: identify and install ACPI tables
1089 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
1090 if (LoaderEntry
->Type
== QemuLoaderCmdAddPointer
) {
1091 Status
= Process2ndPassCmdAddPointer (
1092 &LoaderEntry
->Command
.AddPointer
,
1099 if (EFI_ERROR (Status
)) {
1100 goto UninstallAcpiTables
;
1106 // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3
1107 // Boot Script opcodes has to be the last operation in this function, because
1108 // if it succeeds, it cannot be undone.
1110 if (S3Context
!= NULL
) {
1111 Status
= TransferS3ContextToBootScript (S3Context
);
1112 if (EFI_ERROR (Status
)) {
1113 goto UninstallAcpiTables
;
1116 // Ownership of S3Context has been transfered.
1121 UninstallAcpiTables
:
1122 if (EFI_ERROR (Status
)) {
1124 // roll back partial installation
1126 while (Installed
> 0) {
1128 AcpiProtocol
->UninstallAcpiTable (AcpiProtocol
, InstalledKey
[Installed
]);
1131 DEBUG ((EFI_D_INFO
, "%a: installed %d tables\n", __FUNCTION__
, Installed
));
1134 for (SeenPointerEntry
= OrderedCollectionMin (SeenPointers
);
1135 SeenPointerEntry
!= NULL
;
1136 SeenPointerEntry
= SeenPointerEntry2
) {
1137 SeenPointerEntry2
= OrderedCollectionNext (SeenPointerEntry
);
1138 OrderedCollectionDelete (SeenPointers
, SeenPointerEntry
, NULL
);
1140 OrderedCollectionUninit (SeenPointers
);
1143 FreePool (InstalledKey
);
1145 RollbackWritePointersAndFreeTracker
:
1147 // In case of failure, revoke any allocation addresses that were communicated
1148 // to QEMU previously, before we release all the blobs.
1150 if (EFI_ERROR (Status
)) {
1151 LoaderEntry
= WritePointerSubsetEnd
;
1152 while (LoaderEntry
> LoaderStart
) {
1154 if (LoaderEntry
->Type
== QemuLoaderCmdWritePointer
) {
1155 UndoCmdWritePointer (&LoaderEntry
->Command
.WritePointer
);
1161 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
1162 // place only if we're exiting with success and the blob hosts data that is
1163 // not directly part of some ACPI table.
1165 for (TrackerEntry
= OrderedCollectionMin (Tracker
); TrackerEntry
!= NULL
;
1166 TrackerEntry
= TrackerEntry2
) {
1170 TrackerEntry2
= OrderedCollectionNext (TrackerEntry
);
1171 OrderedCollectionDelete (Tracker
, TrackerEntry
, &UserStruct
);
1174 if (EFI_ERROR (Status
) || Blob
->HostsOnlyTableData
) {
1175 DEBUG ((EFI_D_VERBOSE
, "%a: freeing \"%a\"\n", __FUNCTION__
,
1177 gBS
->FreePages ((UINTN
)Blob
->Base
, EFI_SIZE_TO_PAGES (Blob
->Size
));
1181 OrderedCollectionUninit (Tracker
);
1184 if (S3Context
!= NULL
) {
1185 ReleaseS3Context (S3Context
);
1188 FreeAllocationsRestrictedTo32Bit
:
1189 ReleaseAllocationsRestrictedTo32Bit (AllocationsRestrictedTo32Bit
);
1192 FreePool (LoaderStart
);