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 This program and the accompanying materials are licensed and made available
8 under the terms and conditions of the BSD License which accompanies this
9 distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
13 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 #include "AcpiPlatform.h"
18 #include "QemuLoader.h"
19 #include <Library/BaseMemoryLib.h>
20 #include <Library/MemoryAllocationLib.h>
21 #include <Library/QemuFwCfgLib.h>
22 #include <Library/QemuFwCfgS3Lib.h>
23 #include <Library/DxeServicesTableLib.h>
24 #include <Library/PcdLib.h>
25 #include <Library/OrderedCollectionLib.h>
26 #include <IndustryStandard/Acpi.h>
30 // The user structure for the ordered collection that will track the fw_cfg
31 // blobs under processing.
34 UINT8 File
[QEMU_LOADER_FNAME_SIZE
]; // NUL-terminated name of the fw_cfg
35 // blob. This is the ordering / search
37 UINTN Size
; // The number of bytes in this blob.
38 UINT8
*Base
; // Pointer to the blob data.
39 BOOLEAN HostsOnlyTableData
; // TRUE iff the blob has been found to
40 // only contain data that is directly
41 // part of ACPI tables.
46 Compare a standalone key against a user structure containing an embedded key.
48 @param[in] StandaloneKey Pointer to the bare key.
50 @param[in] UserStruct Pointer to the user structure with the embedded
53 @retval <0 If StandaloneKey compares less than UserStruct's key.
55 @retval 0 If StandaloneKey compares equal to UserStruct's key.
57 @retval >0 If StandaloneKey compares greater than UserStruct's key.
63 IN CONST VOID
*StandaloneKey
,
64 IN CONST VOID
*UserStruct
70 return AsciiStrCmp (StandaloneKey
, (CONST CHAR8
*)Blob
->File
);
75 Comparator function for two user structures.
77 @param[in] UserStruct1 Pointer to the first user structure.
79 @param[in] UserStruct2 Pointer to the second user structure.
81 @retval <0 If UserStruct1 compares less than UserStruct2.
83 @retval 0 If UserStruct1 compares equal to UserStruct2.
85 @retval >0 If UserStruct1 compares greater than UserStruct2.
91 IN CONST VOID
*UserStruct1
,
92 IN CONST VOID
*UserStruct2
98 return BlobKeyCompare (Blob1
->File
, UserStruct2
);
103 Comparator function for two opaque pointers, ordering on (unsigned) pointer
105 Can be used as both Key and UserStruct comparator.
107 @param[in] Pointer1 First pointer.
109 @param[in] Pointer2 Second pointer.
111 @retval <0 If Pointer1 compares less than Pointer2.
113 @retval 0 If Pointer1 compares equal to Pointer2.
115 @retval >0 If Pointer1 compares greater than Pointer2.
121 IN CONST VOID
*Pointer1
,
122 IN CONST VOID
*Pointer2
125 if (Pointer1
== Pointer2
) {
128 if ((UINTN
)Pointer1
< (UINTN
)Pointer2
) {
136 Comparator function for two ASCII strings. Can be used as both Key and
137 UserStruct comparator.
139 This function exists solely so we can avoid casting &AsciiStrCmp to
140 ORDERED_COLLECTION_USER_COMPARE and ORDERED_COLLECTION_KEY_COMPARE.
142 @param[in] AsciiString1 Pointer to the first ASCII string.
144 @param[in] AsciiString2 Pointer to the second ASCII string.
146 @return The return value of AsciiStrCmp (AsciiString1, AsciiString2).
152 IN CONST VOID
*AsciiString1
,
153 IN CONST VOID
*AsciiString2
156 return AsciiStrCmp (AsciiString1
, AsciiString2
);
161 Release the ORDERED_COLLECTION structure populated by
162 CollectAllocationsRestrictedTo32Bit() (below).
164 This function may be called by CollectAllocationsRestrictedTo32Bit() itself,
167 @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure to
172 ReleaseAllocationsRestrictedTo32Bit (
173 IN ORDERED_COLLECTION
*AllocationsRestrictedTo32Bit
176 ORDERED_COLLECTION_ENTRY
*Entry
, *Entry2
;
178 for (Entry
= OrderedCollectionMin (AllocationsRestrictedTo32Bit
);
181 Entry2
= OrderedCollectionNext (Entry
);
182 OrderedCollectionDelete (AllocationsRestrictedTo32Bit
, Entry
, NULL
);
184 OrderedCollectionUninit (AllocationsRestrictedTo32Bit
);
189 Iterate over the linker/loader script, and collect the names of the fw_cfg
190 blobs that are referenced by QEMU_LOADER_ADD_POINTER.PointeeFile fields, such
191 that QEMU_LOADER_ADD_POINTER.PointerSize is less than 8. This means that the
192 pointee blob's address will have to be patched into a narrower-than-8 byte
193 pointer field, hence the pointee blob must not be allocated from 64-bit
196 @param[out] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure
197 linking (not copying / owning) such
198 QEMU_LOADER_ADD_POINTER.PointeeFile
199 fields that name the blobs
200 restricted from 64-bit allocation.
202 @param[in] LoaderStart Points to the first entry in the
203 linker/loader script.
205 @param[in] LoaderEnd Points one past the last entry in
206 the linker/loader script.
208 @retval EFI_SUCCESS AllocationsRestrictedTo32Bit has been
211 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
213 @retval EFI_PROTOCOL_ERROR Invalid linker/loader script contents.
217 CollectAllocationsRestrictedTo32Bit (
218 OUT ORDERED_COLLECTION
**AllocationsRestrictedTo32Bit
,
219 IN CONST QEMU_LOADER_ENTRY
*LoaderStart
,
220 IN CONST QEMU_LOADER_ENTRY
*LoaderEnd
223 ORDERED_COLLECTION
*Collection
;
224 CONST QEMU_LOADER_ENTRY
*LoaderEntry
;
227 Collection
= OrderedCollectionInit (AsciiStringCompare
, AsciiStringCompare
);
228 if (Collection
== NULL
) {
229 return EFI_OUT_OF_RESOURCES
;
232 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
233 CONST QEMU_LOADER_ADD_POINTER
*AddPointer
;
235 if (LoaderEntry
->Type
!= QemuLoaderCmdAddPointer
) {
238 AddPointer
= &LoaderEntry
->Command
.AddPointer
;
240 if (AddPointer
->PointerSize
>= 8) {
244 if (AddPointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
245 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
246 Status
= EFI_PROTOCOL_ERROR
;
250 Status
= OrderedCollectionInsert (
253 (VOID
*)AddPointer
->PointeeFile
259 "%a: restricting blob \"%a\" from 64-bit allocation\n",
261 AddPointer
->PointeeFile
264 case EFI_ALREADY_STARTED
:
266 // The restriction has been recorded already.
269 case EFI_OUT_OF_RESOURCES
:
276 *AllocationsRestrictedTo32Bit
= Collection
;
280 ReleaseAllocationsRestrictedTo32Bit (Collection
);
286 Process a QEMU_LOADER_ALLOCATE command.
288 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to
291 @param[in,out] Tracker The ORDERED_COLLECTION tracking the
292 BLOB user structures created thus
295 @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION populated by
297 CollectAllocationsRestrictedTo32Bit,
298 naming the fw_cfg blobs that must
299 not be allocated from 64-bit address
302 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been
303 allocated for the blob contents, and the
304 contents have been saved. A BLOB object (user
305 structure) has been allocated from pool memory,
306 referencing the blob contents. The BLOB user
307 structure has been linked into Tracker.
309 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
310 Allocate, or the Allocate command references a
311 file that is already known by Tracker.
313 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in
316 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.
318 @return Error codes from QemuFwCfgFindFile() and
319 gBS->AllocatePages().
325 IN CONST QEMU_LOADER_ALLOCATE
*Allocate
,
326 IN OUT ORDERED_COLLECTION
*Tracker
,
327 IN ORDERED_COLLECTION
*AllocationsRestrictedTo32Bit
330 FIRMWARE_CONFIG_ITEM FwCfgItem
;
334 EFI_PHYSICAL_ADDRESS Address
;
337 if (Allocate
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
338 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
339 return EFI_PROTOCOL_ERROR
;
342 if (Allocate
->Alignment
> EFI_PAGE_SIZE
) {
343 DEBUG ((EFI_D_ERROR
, "%a: unsupported alignment 0x%x\n", __FUNCTION__
,
344 Allocate
->Alignment
));
345 return EFI_UNSUPPORTED
;
348 Status
= QemuFwCfgFindFile ((CHAR8
*)Allocate
->File
, &FwCfgItem
, &FwCfgSize
);
349 if (EFI_ERROR (Status
)) {
350 DEBUG ((EFI_D_ERROR
, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__
,
351 Allocate
->File
, Status
));
355 NumPages
= EFI_SIZE_TO_PAGES (FwCfgSize
);
356 Address
= MAX_UINT64
;
357 if (OrderedCollectionFind (
358 AllocationsRestrictedTo32Bit
,
361 Address
= MAX_UINT32
;
363 Status
= gBS
->AllocatePages (AllocateMaxAddress
, EfiACPIMemoryNVS
, NumPages
,
365 if (EFI_ERROR (Status
)) {
369 Blob
= AllocatePool (sizeof *Blob
);
371 Status
= EFI_OUT_OF_RESOURCES
;
374 CopyMem (Blob
->File
, Allocate
->File
, QEMU_LOADER_FNAME_SIZE
);
375 Blob
->Size
= FwCfgSize
;
376 Blob
->Base
= (VOID
*)(UINTN
)Address
;
377 Blob
->HostsOnlyTableData
= TRUE
;
379 Status
= OrderedCollectionInsert (Tracker
, NULL
, Blob
);
380 if (Status
== RETURN_ALREADY_STARTED
) {
381 DEBUG ((EFI_D_ERROR
, "%a: duplicated file \"%a\"\n", __FUNCTION__
,
383 Status
= EFI_PROTOCOL_ERROR
;
385 if (EFI_ERROR (Status
)) {
389 QemuFwCfgSelectItem (FwCfgItem
);
390 QemuFwCfgReadBytes (FwCfgSize
, Blob
->Base
);
391 ZeroMem (Blob
->Base
+ Blob
->Size
, EFI_PAGES_TO_SIZE (NumPages
) - Blob
->Size
);
393 DEBUG ((EFI_D_VERBOSE
, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "
394 "Address=0x%Lx\n", __FUNCTION__
, Allocate
->File
, Allocate
->Alignment
,
395 Allocate
->Zone
, (UINT64
)Blob
->Size
, (UINT64
)(UINTN
)Blob
->Base
));
402 gBS
->FreePages (Address
, NumPages
);
409 Process a QEMU_LOADER_ADD_POINTER command.
411 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
413 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
414 structures created thus far.
416 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
417 AddPointer, or the AddPointer command references
418 a file unknown to Tracker, or the pointer to
419 relocate has invalid location, size, or value, or
420 the relocated pointer value is not representable
421 in the given pointer size.
423 @retval EFI_SUCCESS The pointer field inside the pointer blob has
429 ProcessCmdAddPointer (
430 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
431 IN CONST ORDERED_COLLECTION
*Tracker
434 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
439 if (AddPointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0' ||
440 AddPointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
441 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
442 return EFI_PROTOCOL_ERROR
;
445 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
446 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
447 if (TrackerEntry
== NULL
|| TrackerEntry2
== NULL
) {
448 DEBUG ((EFI_D_ERROR
, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",
449 __FUNCTION__
, AddPointer
->PointerFile
, AddPointer
->PointeeFile
));
450 return EFI_PROTOCOL_ERROR
;
453 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
454 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
455 if ((AddPointer
->PointerSize
!= 1 && AddPointer
->PointerSize
!= 2 &&
456 AddPointer
->PointerSize
!= 4 && AddPointer
->PointerSize
!= 8) ||
457 Blob
->Size
< AddPointer
->PointerSize
||
458 Blob
->Size
- AddPointer
->PointerSize
< AddPointer
->PointerOffset
) {
459 DEBUG ((EFI_D_ERROR
, "%a: invalid pointer location or size in \"%a\"\n",
460 __FUNCTION__
, AddPointer
->PointerFile
));
461 return EFI_PROTOCOL_ERROR
;
464 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
466 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
467 if (PointerValue
>= Blob2
->Size
) {
468 DEBUG ((EFI_D_ERROR
, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__
,
469 AddPointer
->PointerFile
));
470 return EFI_PROTOCOL_ERROR
;
474 // The memory allocation system ensures that the address of the byte past the
475 // last byte of any allocated object is expressible (no wraparound).
477 ASSERT ((UINTN
)Blob2
->Base
<= MAX_ADDRESS
- Blob2
->Size
);
479 PointerValue
+= (UINT64
)(UINTN
)Blob2
->Base
;
480 if (AddPointer
->PointerSize
< 8 &&
481 RShiftU64 (PointerValue
, AddPointer
->PointerSize
* 8) != 0) {
482 DEBUG ((EFI_D_ERROR
, "%a: relocated pointer value unrepresentable in "
483 "\"%a\"\n", __FUNCTION__
, AddPointer
->PointerFile
));
484 return EFI_PROTOCOL_ERROR
;
487 CopyMem (PointerField
, &PointerValue
, AddPointer
->PointerSize
);
489 DEBUG ((EFI_D_VERBOSE
, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
490 "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
491 AddPointer
->PointerFile
, AddPointer
->PointeeFile
,
492 AddPointer
->PointerOffset
, AddPointer
->PointerSize
));
498 Process a QEMU_LOADER_ADD_CHECKSUM command.
500 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.
502 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
503 structures created thus far.
505 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
506 AddChecksum, or the AddChecksum command
507 references a file unknown to Tracker, or the
508 range to checksum is invalid.
510 @retval EFI_SUCCESS The requested range has been checksummed.
515 ProcessCmdAddChecksum (
516 IN CONST QEMU_LOADER_ADD_CHECKSUM
*AddChecksum
,
517 IN CONST ORDERED_COLLECTION
*Tracker
520 ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
523 if (AddChecksum
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
524 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
525 return EFI_PROTOCOL_ERROR
;
528 TrackerEntry
= OrderedCollectionFind (Tracker
, AddChecksum
->File
);
529 if (TrackerEntry
== NULL
) {
530 DEBUG ((EFI_D_ERROR
, "%a: invalid blob reference \"%a\"\n", __FUNCTION__
,
532 return EFI_PROTOCOL_ERROR
;
535 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
536 if (Blob
->Size
<= AddChecksum
->ResultOffset
||
537 Blob
->Size
< AddChecksum
->Length
||
538 Blob
->Size
- AddChecksum
->Length
< AddChecksum
->Start
) {
539 DEBUG ((EFI_D_ERROR
, "%a: invalid checksum range in \"%a\"\n",
540 __FUNCTION__
, AddChecksum
->File
));
541 return EFI_PROTOCOL_ERROR
;
544 Blob
->Base
[AddChecksum
->ResultOffset
] = CalculateCheckSum8 (
545 Blob
->Base
+ AddChecksum
->Start
,
548 DEBUG ((EFI_D_VERBOSE
, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "
549 "Length=0x%x\n", __FUNCTION__
, AddChecksum
->File
,
550 AddChecksum
->ResultOffset
, AddChecksum
->Start
, AddChecksum
->Length
));
556 Process a QEMU_LOADER_WRITE_POINTER command.
558 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.
560 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
561 structures created thus far.
563 @param[in,out] S3Context The S3_CONTEXT object capturing the fw_cfg actions
564 of successfully processed QEMU_LOADER_WRITE_POINTER
565 commands, to be replayed at S3 resume. S3Context
566 may be NULL if S3 is disabled.
568 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
569 WritePointer. Or, the WritePointer command
570 references a file unknown to Tracker or the
571 fw_cfg directory. Or, the pointer object to
572 rewrite has invalid location, size, or initial
573 relative value. Or, the pointer value to store
574 does not fit in the given pointer size.
576 @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg
577 file has been written. If S3Context is not NULL,
578 then WritePointer has been condensed into
581 @return Error codes propagated from
582 SaveCondensedWritePointerToS3Context(). The
583 pointer object inside the writeable fw_cfg file
584 has not been written.
588 ProcessCmdWritePointer (
589 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
,
590 IN CONST ORDERED_COLLECTION
*Tracker
,
591 IN OUT S3_CONTEXT
*S3Context OPTIONAL
594 RETURN_STATUS Status
;
595 FIRMWARE_CONFIG_ITEM PointerItem
;
596 UINTN PointerItemSize
;
597 ORDERED_COLLECTION_ENTRY
*PointeeEntry
;
601 if (WritePointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0' ||
602 WritePointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
603 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
604 return EFI_PROTOCOL_ERROR
;
607 Status
= QemuFwCfgFindFile ((CONST CHAR8
*)WritePointer
->PointerFile
,
608 &PointerItem
, &PointerItemSize
);
609 PointeeEntry
= OrderedCollectionFind (Tracker
, WritePointer
->PointeeFile
);
610 if (RETURN_ERROR (Status
) || PointeeEntry
== NULL
) {
612 "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
613 __FUNCTION__
, WritePointer
->PointerFile
, WritePointer
->PointeeFile
));
614 return EFI_PROTOCOL_ERROR
;
617 if ((WritePointer
->PointerSize
!= 1 && WritePointer
->PointerSize
!= 2 &&
618 WritePointer
->PointerSize
!= 4 && WritePointer
->PointerSize
!= 8) ||
619 (PointerItemSize
< WritePointer
->PointerSize
) ||
620 (PointerItemSize
- WritePointer
->PointerSize
<
621 WritePointer
->PointerOffset
)) {
622 DEBUG ((DEBUG_ERROR
, "%a: invalid pointer location or size in \"%a\"\n",
623 __FUNCTION__
, WritePointer
->PointerFile
));
624 return EFI_PROTOCOL_ERROR
;
627 PointeeBlob
= OrderedCollectionUserStruct (PointeeEntry
);
628 PointerValue
= WritePointer
->PointeeOffset
;
629 if (PointerValue
>= PointeeBlob
->Size
) {
630 DEBUG ((DEBUG_ERROR
, "%a: invalid PointeeOffset\n", __FUNCTION__
));
631 return EFI_PROTOCOL_ERROR
;
635 // The memory allocation system ensures that the address of the byte past the
636 // last byte of any allocated object is expressible (no wraparound).
638 ASSERT ((UINTN
)PointeeBlob
->Base
<= MAX_ADDRESS
- PointeeBlob
->Size
);
640 PointerValue
+= (UINT64
)(UINTN
)PointeeBlob
->Base
;
641 if (WritePointer
->PointerSize
< 8 &&
642 RShiftU64 (PointerValue
, WritePointer
->PointerSize
* 8) != 0) {
643 DEBUG ((DEBUG_ERROR
, "%a: pointer value unrepresentable in \"%a\"\n",
644 __FUNCTION__
, WritePointer
->PointerFile
));
645 return EFI_PROTOCOL_ERROR
;
649 // If S3 is enabled, we have to capture the below fw_cfg actions in condensed
650 // form, to be replayed during S3 resume.
652 if (S3Context
!= NULL
) {
653 EFI_STATUS SaveStatus
;
655 SaveStatus
= SaveCondensedWritePointerToS3Context (
658 WritePointer
->PointerSize
,
659 WritePointer
->PointerOffset
,
662 if (EFI_ERROR (SaveStatus
)) {
667 QemuFwCfgSelectItem (PointerItem
);
668 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
669 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
672 // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob
673 // as unreleasable, for the case when the whole linker/loader script is
674 // handled successfully.
676 PointeeBlob
->HostsOnlyTableData
= FALSE
;
678 DEBUG ((DEBUG_VERBOSE
, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
679 "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
680 WritePointer
->PointerFile
, WritePointer
->PointeeFile
,
681 WritePointer
->PointerOffset
, WritePointer
->PointeeOffset
,
682 WritePointer
->PointerSize
));
688 Undo a QEMU_LOADER_WRITE_POINTER command.
690 This function revokes (zeroes out) a guest memory reference communicated to
691 QEMU earlier. The caller is responsible for invoking this function only on
692 such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed
693 by ProcessCmdWritePointer().
695 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
699 UndoCmdWritePointer (
700 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
703 RETURN_STATUS Status
;
704 FIRMWARE_CONFIG_ITEM PointerItem
;
705 UINTN PointerItemSize
;
708 Status
= QemuFwCfgFindFile ((CONST CHAR8
*)WritePointer
->PointerFile
,
709 &PointerItem
, &PointerItemSize
);
710 ASSERT_RETURN_ERROR (Status
);
713 QemuFwCfgSelectItem (PointerItem
);
714 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
715 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
717 DEBUG ((DEBUG_VERBOSE
,
718 "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
719 WritePointer
->PointerFile
, WritePointer
->PointerOffset
,
720 WritePointer
->PointerSize
));
725 // We'll be saving the keys of installed tables so that we can roll them back
726 // in case of failure. 128 tables should be enough for anyone (TM).
728 #define INSTALLED_TABLES_MAX 128
731 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte
732 array is an ACPI table, and if so, install it.
734 This function assumes that the entire QEMU linker/loader command file has
735 been processed successfully in a prior first pass.
737 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
739 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
742 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
744 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
745 elements, allocated by the caller. On output,
746 the function will have stored (appended) the
747 AcpiProtocol-internal key of the ACPI table that
748 the function has installed, if the AddPointer
749 command identified an ACPI table that is
750 different from RSDT and XSDT.
752 @param[in,out] NumInstalled On input, the number of entries already used in
753 InstalledKey; it must be in [0,
754 INSTALLED_TABLES_MAX] inclusive. On output, the
755 parameter is incremented if the AddPointer
756 command identified an ACPI table that is
757 different from RSDT and XSDT.
759 @param[in,out] SeenPointers The ORDERED_COLLECTION tracking the absolute
760 target addresses that have been pointed-to by
761 QEMU_LOADER_ADD_POINTER commands thus far. If a
762 target address is encountered for the first
763 time, and it identifies an ACPI table that is
764 different from RDST and XSDT, the table is
765 installed. If a target address is seen for the
766 second or later times, it is skipped without
769 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
772 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI
773 table different from RSDT and XSDT, but there
774 was no more room in InstalledKey.
776 @retval EFI_SUCCESS AddPointer has been processed. Either its
777 absolute target address has been encountered
778 before, or an ACPI table different from RSDT
779 and XSDT has been installed (reflected by
780 InstalledKey and NumInstalled), or RSDT or
781 XSDT has been identified but not installed, or
782 the fw_cfg blob pointed-into by AddPointer has
783 been marked as hosting something else than
784 just direct ACPI table contents.
786 @return Error codes returned by
787 AcpiProtocol->InstallAcpiTable().
792 Process2ndPassCmdAddPointer (
793 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
794 IN CONST ORDERED_COLLECTION
*Tracker
,
795 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
,
796 IN OUT UINTN InstalledKey
[INSTALLED_TABLES_MAX
],
797 IN OUT INT32
*NumInstalled
,
798 IN OUT ORDERED_COLLECTION
*SeenPointers
801 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
802 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry2
;
803 ORDERED_COLLECTION_ENTRY
*SeenPointerEntry
;
806 CONST UINT8
*PointerField
;
808 UINTN Blob2Remaining
;
810 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
811 CONST EFI_ACPI_DESCRIPTION_HEADER
*Header
;
814 if (*NumInstalled
< 0 || *NumInstalled
> INSTALLED_TABLES_MAX
) {
815 return EFI_INVALID_PARAMETER
;
818 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
819 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
820 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
821 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
822 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
824 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
827 // We assert that PointerValue falls inside Blob2's contents. This is ensured
828 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().
830 Blob2Remaining
= (UINTN
)Blob2
->Base
;
831 ASSERT(PointerValue
>= Blob2Remaining
);
832 Blob2Remaining
+= Blob2
->Size
;
833 ASSERT (PointerValue
< Blob2Remaining
);
835 Status
= OrderedCollectionInsert (
837 &SeenPointerEntry
, // for reverting insertion in error case
838 (VOID
*)(UINTN
)PointerValue
840 if (EFI_ERROR (Status
)) {
841 if (Status
== RETURN_ALREADY_STARTED
) {
843 // Already seen this pointer, don't try to process it again.
847 "%a: PointerValue=0x%Lx already processed, skipping.\n",
851 Status
= EFI_SUCCESS
;
856 Blob2Remaining
-= (UINTN
) PointerValue
;
857 DEBUG ((EFI_D_VERBOSE
, "%a: checking for ACPI header in \"%a\" at 0x%Lx "
858 "(remaining: 0x%Lx): ", __FUNCTION__
, AddPointer
->PointeeFile
,
859 PointerValue
, (UINT64
)Blob2Remaining
));
864 // To make our job simple, the FACS has a custom header. Sigh.
866 if (sizeof *Facs
<= Blob2Remaining
) {
867 Facs
= (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)PointerValue
;
869 if (Facs
->Length
>= sizeof *Facs
&&
870 Facs
->Length
<= Blob2Remaining
&&
872 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) {
873 DEBUG ((EFI_D_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
874 (CONST CHAR8
*)&Facs
->Signature
, Facs
->Length
));
875 TableSize
= Facs
->Length
;
880 // check for the uniform tables
882 if (TableSize
== 0 && sizeof *Header
<= Blob2Remaining
) {
883 Header
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
)PointerValue
;
885 if (Header
->Length
>= sizeof *Header
&&
886 Header
->Length
<= Blob2Remaining
&&
887 CalculateSum8 ((CONST UINT8
*)Header
, Header
->Length
) == 0) {
889 // This looks very much like an ACPI table from QEMU:
890 // - Length field consistent with both ACPI and containing blob size
891 // - checksum is correct
893 DEBUG ((EFI_D_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
894 (CONST CHAR8
*)&Header
->Signature
, Header
->Length
));
895 TableSize
= Header
->Length
;
898 // Skip RSDT and XSDT because those are handled by
899 // EFI_ACPI_TABLE_PROTOCOL automatically.
900 if (Header
->Signature
==
901 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
||
903 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
) {
909 if (TableSize
== 0) {
910 DEBUG ((EFI_D_VERBOSE
, "not found; marking fw_cfg blob as opaque\n"));
911 Blob2
->HostsOnlyTableData
= FALSE
;
915 if (*NumInstalled
== INSTALLED_TABLES_MAX
) {
916 DEBUG ((EFI_D_ERROR
, "%a: can't install more than %d tables\n",
917 __FUNCTION__
, INSTALLED_TABLES_MAX
));
918 Status
= EFI_OUT_OF_RESOURCES
;
919 goto RollbackSeenPointer
;
922 Status
= AcpiProtocol
->InstallAcpiTable (AcpiProtocol
,
923 (VOID
*)(UINTN
)PointerValue
, TableSize
,
924 &InstalledKey
[*NumInstalled
]);
925 if (EFI_ERROR (Status
)) {
926 DEBUG ((EFI_D_ERROR
, "%a: InstallAcpiTable(): %r\n", __FUNCTION__
,
928 goto RollbackSeenPointer
;
934 OrderedCollectionDelete (SeenPointers
, SeenPointerEntry
, NULL
);
940 Download, process, and install ACPI table data from the QEMU loader
943 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
945 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU
946 loader command with unsupported parameters
949 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg
952 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than
953 INSTALLED_TABLES_MAX tables found.
955 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.
957 @return Status codes returned by
958 AcpiProtocol->InstallAcpiTable().
963 InstallQemuFwCfgTables (
964 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
968 FIRMWARE_CONFIG_ITEM FwCfgItem
;
970 QEMU_LOADER_ENTRY
*LoaderStart
;
971 CONST QEMU_LOADER_ENTRY
*LoaderEntry
, *LoaderEnd
;
972 CONST QEMU_LOADER_ENTRY
*WritePointerSubsetEnd
;
973 ORIGINAL_ATTRIBUTES
*OriginalPciAttributes
;
974 UINTN OriginalPciAttributesCount
;
975 ORDERED_COLLECTION
*AllocationsRestrictedTo32Bit
;
976 S3_CONTEXT
*S3Context
;
977 ORDERED_COLLECTION
*Tracker
;
980 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
981 ORDERED_COLLECTION
*SeenPointers
;
982 ORDERED_COLLECTION_ENTRY
*SeenPointerEntry
, *SeenPointerEntry2
;
984 Status
= QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem
, &FwCfgSize
);
985 if (EFI_ERROR (Status
)) {
988 if (FwCfgSize
% sizeof *LoaderEntry
!= 0) {
989 DEBUG ((EFI_D_ERROR
, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
990 __FUNCTION__
, (UINT64
)FwCfgSize
));
991 return EFI_PROTOCOL_ERROR
;
994 LoaderStart
= AllocatePool (FwCfgSize
);
995 if (LoaderStart
== NULL
) {
996 return EFI_OUT_OF_RESOURCES
;
998 EnablePciDecoding (&OriginalPciAttributes
, &OriginalPciAttributesCount
);
999 QemuFwCfgSelectItem (FwCfgItem
);
1000 QemuFwCfgReadBytes (FwCfgSize
, LoaderStart
);
1001 RestorePciDecoding (OriginalPciAttributes
, OriginalPciAttributesCount
);
1002 LoaderEnd
= LoaderStart
+ FwCfgSize
/ sizeof *LoaderEntry
;
1004 AllocationsRestrictedTo32Bit
= NULL
;
1005 Status
= CollectAllocationsRestrictedTo32Bit (
1006 &AllocationsRestrictedTo32Bit
,
1010 if (EFI_ERROR (Status
)) {
1015 if (QemuFwCfgS3Enabled ()) {
1017 // Size the allocation pessimistically, assuming that all commands in the
1018 // script are QEMU_LOADER_WRITE_POINTER commands.
1020 Status
= AllocateS3Context (&S3Context
, LoaderEnd
- LoaderStart
);
1021 if (EFI_ERROR (Status
)) {
1022 goto FreeAllocationsRestrictedTo32Bit
;
1026 Tracker
= OrderedCollectionInit (BlobCompare
, BlobKeyCompare
);
1027 if (Tracker
== NULL
) {
1028 Status
= EFI_OUT_OF_RESOURCES
;
1033 // first pass: process the commands
1035 // "WritePointerSubsetEnd" points one past the last successful
1036 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
1037 // pass, no such command has been encountered yet.
1039 WritePointerSubsetEnd
= LoaderStart
;
1040 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
1041 switch (LoaderEntry
->Type
) {
1042 case QemuLoaderCmdAllocate
:
1043 Status
= ProcessCmdAllocate (
1044 &LoaderEntry
->Command
.Allocate
,
1046 AllocationsRestrictedTo32Bit
1050 case QemuLoaderCmdAddPointer
:
1051 Status
= ProcessCmdAddPointer (&LoaderEntry
->Command
.AddPointer
,
1055 case QemuLoaderCmdAddChecksum
:
1056 Status
= ProcessCmdAddChecksum (&LoaderEntry
->Command
.AddChecksum
,
1060 case QemuLoaderCmdWritePointer
:
1061 Status
= ProcessCmdWritePointer (&LoaderEntry
->Command
.WritePointer
,
1062 Tracker
, S3Context
);
1063 if (!EFI_ERROR (Status
)) {
1064 WritePointerSubsetEnd
= LoaderEntry
+ 1;
1069 DEBUG ((EFI_D_VERBOSE
, "%a: unknown loader command: 0x%x\n",
1070 __FUNCTION__
, LoaderEntry
->Type
));
1074 if (EFI_ERROR (Status
)) {
1075 goto RollbackWritePointersAndFreeTracker
;
1079 InstalledKey
= AllocatePool (INSTALLED_TABLES_MAX
* sizeof *InstalledKey
);
1080 if (InstalledKey
== NULL
) {
1081 Status
= EFI_OUT_OF_RESOURCES
;
1082 goto RollbackWritePointersAndFreeTracker
;
1085 SeenPointers
= OrderedCollectionInit (PointerCompare
, PointerCompare
);
1086 if (SeenPointers
== NULL
) {
1087 Status
= EFI_OUT_OF_RESOURCES
;
1092 // second pass: identify and install ACPI tables
1095 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
1096 if (LoaderEntry
->Type
== QemuLoaderCmdAddPointer
) {
1097 Status
= Process2ndPassCmdAddPointer (
1098 &LoaderEntry
->Command
.AddPointer
,
1105 if (EFI_ERROR (Status
)) {
1106 goto UninstallAcpiTables
;
1112 // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3
1113 // Boot Script opcodes has to be the last operation in this function, because
1114 // if it succeeds, it cannot be undone.
1116 if (S3Context
!= NULL
) {
1117 Status
= TransferS3ContextToBootScript (S3Context
);
1118 if (EFI_ERROR (Status
)) {
1119 goto UninstallAcpiTables
;
1122 // Ownership of S3Context has been transfered.
1127 UninstallAcpiTables
:
1128 if (EFI_ERROR (Status
)) {
1130 // roll back partial installation
1132 while (Installed
> 0) {
1134 AcpiProtocol
->UninstallAcpiTable (AcpiProtocol
, InstalledKey
[Installed
]);
1137 DEBUG ((EFI_D_INFO
, "%a: installed %d tables\n", __FUNCTION__
, Installed
));
1140 for (SeenPointerEntry
= OrderedCollectionMin (SeenPointers
);
1141 SeenPointerEntry
!= NULL
;
1142 SeenPointerEntry
= SeenPointerEntry2
) {
1143 SeenPointerEntry2
= OrderedCollectionNext (SeenPointerEntry
);
1144 OrderedCollectionDelete (SeenPointers
, SeenPointerEntry
, NULL
);
1146 OrderedCollectionUninit (SeenPointers
);
1149 FreePool (InstalledKey
);
1151 RollbackWritePointersAndFreeTracker
:
1153 // In case of failure, revoke any allocation addresses that were communicated
1154 // to QEMU previously, before we release all the blobs.
1156 if (EFI_ERROR (Status
)) {
1157 LoaderEntry
= WritePointerSubsetEnd
;
1158 while (LoaderEntry
> LoaderStart
) {
1160 if (LoaderEntry
->Type
== QemuLoaderCmdWritePointer
) {
1161 UndoCmdWritePointer (&LoaderEntry
->Command
.WritePointer
);
1167 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
1168 // place only if we're exiting with success and the blob hosts data that is
1169 // not directly part of some ACPI table.
1171 for (TrackerEntry
= OrderedCollectionMin (Tracker
); TrackerEntry
!= NULL
;
1172 TrackerEntry
= TrackerEntry2
) {
1176 TrackerEntry2
= OrderedCollectionNext (TrackerEntry
);
1177 OrderedCollectionDelete (Tracker
, TrackerEntry
, &UserStruct
);
1180 if (EFI_ERROR (Status
) || Blob
->HostsOnlyTableData
) {
1181 DEBUG ((EFI_D_VERBOSE
, "%a: freeing \"%a\"\n", __FUNCTION__
,
1183 gBS
->FreePages ((UINTN
)Blob
->Base
, EFI_SIZE_TO_PAGES (Blob
->Size
));
1187 OrderedCollectionUninit (Tracker
);
1190 if (S3Context
!= NULL
) {
1191 ReleaseS3Context (S3Context
);
1194 FreeAllocationsRestrictedTo32Bit
:
1195 ReleaseAllocationsRestrictedTo32Bit (AllocationsRestrictedTo32Bit
);
1198 FreePool (LoaderStart
);