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/DxeServicesTableLib.h>
23 #include <Library/PcdLib.h>
24 #include <Library/OrderedCollectionLib.h>
25 #include <IndustryStandard/Acpi.h>
29 // The user structure for the ordered collection that will track the fw_cfg
30 // blobs under processing.
33 UINT8 File
[QEMU_LOADER_FNAME_SIZE
]; // NUL-terminated name of the fw_cfg
34 // blob. This is the ordering / search
36 UINTN Size
; // The number of bytes in this blob.
37 UINT8
*Base
; // Pointer to the blob data.
38 BOOLEAN HostsOnlyTableData
; // TRUE iff the blob has been found to
39 // only contain data that is directly
40 // part of ACPI tables.
45 Compare a standalone key against a user structure containing an embedded key.
47 @param[in] StandaloneKey Pointer to the bare key.
49 @param[in] UserStruct Pointer to the user structure with the embedded
52 @retval <0 If StandaloneKey compares less than UserStruct's key.
54 @retval 0 If StandaloneKey compares equal to UserStruct's key.
56 @retval >0 If StandaloneKey compares greater than UserStruct's key.
62 IN CONST VOID
*StandaloneKey
,
63 IN CONST VOID
*UserStruct
69 return AsciiStrCmp (StandaloneKey
, (CONST CHAR8
*)Blob
->File
);
74 Comparator function for two user structures.
76 @param[in] UserStruct1 Pointer to the first user structure.
78 @param[in] UserStruct2 Pointer to the second user structure.
80 @retval <0 If UserStruct1 compares less than UserStruct2.
82 @retval 0 If UserStruct1 compares equal to UserStruct2.
84 @retval >0 If UserStruct1 compares greater than UserStruct2.
90 IN CONST VOID
*UserStruct1
,
91 IN CONST VOID
*UserStruct2
97 return BlobKeyCompare (Blob1
->File
, UserStruct2
);
102 Process a QEMU_LOADER_ALLOCATE command.
104 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to process.
106 @param[in,out] Tracker The ORDERED_COLLECTION tracking the BLOB user
107 structures created thus far.
109 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been
110 allocated for the blob contents, and the
111 contents have been saved. A BLOB object (user
112 structure) has been allocated from pool memory,
113 referencing the blob contents. The BLOB user
114 structure has been linked into Tracker.
116 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
117 Allocate, or the Allocate command references a
118 file that is already known by Tracker.
120 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in
123 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.
125 @return Error codes from QemuFwCfgFindFile() and
126 gBS->AllocatePages().
132 IN CONST QEMU_LOADER_ALLOCATE
*Allocate
,
133 IN OUT ORDERED_COLLECTION
*Tracker
136 FIRMWARE_CONFIG_ITEM FwCfgItem
;
140 EFI_PHYSICAL_ADDRESS Address
;
143 if (Allocate
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
144 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
145 return EFI_PROTOCOL_ERROR
;
148 if (Allocate
->Alignment
> EFI_PAGE_SIZE
) {
149 DEBUG ((EFI_D_ERROR
, "%a: unsupported alignment 0x%x\n", __FUNCTION__
,
150 Allocate
->Alignment
));
151 return EFI_UNSUPPORTED
;
154 Status
= QemuFwCfgFindFile ((CHAR8
*)Allocate
->File
, &FwCfgItem
, &FwCfgSize
);
155 if (EFI_ERROR (Status
)) {
156 DEBUG ((EFI_D_ERROR
, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__
,
157 Allocate
->File
, Status
));
161 NumPages
= EFI_SIZE_TO_PAGES (FwCfgSize
);
162 Address
= 0xFFFFFFFF;
163 Status
= gBS
->AllocatePages (AllocateMaxAddress
, EfiACPIMemoryNVS
, NumPages
,
165 if (EFI_ERROR (Status
)) {
169 Blob
= AllocatePool (sizeof *Blob
);
171 Status
= EFI_OUT_OF_RESOURCES
;
174 CopyMem (Blob
->File
, Allocate
->File
, QEMU_LOADER_FNAME_SIZE
);
175 Blob
->Size
= FwCfgSize
;
176 Blob
->Base
= (VOID
*)(UINTN
)Address
;
177 Blob
->HostsOnlyTableData
= TRUE
;
179 Status
= OrderedCollectionInsert (Tracker
, NULL
, Blob
);
180 if (Status
== RETURN_ALREADY_STARTED
) {
181 DEBUG ((EFI_D_ERROR
, "%a: duplicated file \"%a\"\n", __FUNCTION__
,
183 Status
= EFI_PROTOCOL_ERROR
;
185 if (EFI_ERROR (Status
)) {
189 QemuFwCfgSelectItem (FwCfgItem
);
190 QemuFwCfgReadBytes (FwCfgSize
, Blob
->Base
);
191 ZeroMem (Blob
->Base
+ Blob
->Size
, EFI_PAGES_TO_SIZE (NumPages
) - Blob
->Size
);
193 DEBUG ((EFI_D_VERBOSE
, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "
194 "Address=0x%Lx\n", __FUNCTION__
, Allocate
->File
, Allocate
->Alignment
,
195 Allocate
->Zone
, (UINT64
)Blob
->Size
, (UINT64
)(UINTN
)Blob
->Base
));
202 gBS
->FreePages (Address
, NumPages
);
209 Process a QEMU_LOADER_ADD_POINTER command.
211 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
213 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
214 structures created thus far.
216 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
217 AddPointer, or the AddPointer command references
218 a file unknown to Tracker, or the pointer to
219 relocate has invalid location, size, or value, or
220 the relocated pointer value is not representable
221 in the given pointer size.
223 @retval EFI_SUCCESS The pointer field inside the pointer blob has
229 ProcessCmdAddPointer (
230 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
231 IN CONST ORDERED_COLLECTION
*Tracker
234 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
239 if (AddPointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0' ||
240 AddPointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
241 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
242 return EFI_PROTOCOL_ERROR
;
245 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
246 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
247 if (TrackerEntry
== NULL
|| TrackerEntry2
== NULL
) {
248 DEBUG ((EFI_D_ERROR
, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",
249 __FUNCTION__
, AddPointer
->PointerFile
, AddPointer
->PointeeFile
));
250 return EFI_PROTOCOL_ERROR
;
253 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
254 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
255 if ((AddPointer
->PointerSize
!= 1 && AddPointer
->PointerSize
!= 2 &&
256 AddPointer
->PointerSize
!= 4 && AddPointer
->PointerSize
!= 8) ||
257 Blob
->Size
< AddPointer
->PointerSize
||
258 Blob
->Size
- AddPointer
->PointerSize
< AddPointer
->PointerOffset
) {
259 DEBUG ((EFI_D_ERROR
, "%a: invalid pointer location or size in \"%a\"\n",
260 __FUNCTION__
, AddPointer
->PointerFile
));
261 return EFI_PROTOCOL_ERROR
;
264 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
266 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
267 if (PointerValue
>= Blob2
->Size
) {
268 DEBUG ((EFI_D_ERROR
, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__
,
269 AddPointer
->PointerFile
));
270 return EFI_PROTOCOL_ERROR
;
274 // The memory allocation system ensures that the address of the byte past the
275 // last byte of any allocated object is expressible (no wraparound).
277 ASSERT ((UINTN
)Blob2
->Base
<= MAX_ADDRESS
- Blob2
->Size
);
279 PointerValue
+= (UINT64
)(UINTN
)Blob2
->Base
;
280 if (AddPointer
->PointerSize
< 8 &&
281 RShiftU64 (PointerValue
, AddPointer
->PointerSize
* 8) != 0) {
282 DEBUG ((EFI_D_ERROR
, "%a: relocated pointer value unrepresentable in "
283 "\"%a\"\n", __FUNCTION__
, AddPointer
->PointerFile
));
284 return EFI_PROTOCOL_ERROR
;
287 CopyMem (PointerField
, &PointerValue
, AddPointer
->PointerSize
);
289 DEBUG ((EFI_D_VERBOSE
, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
290 "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
291 AddPointer
->PointerFile
, AddPointer
->PointeeFile
,
292 AddPointer
->PointerOffset
, AddPointer
->PointerSize
));
298 Process a QEMU_LOADER_ADD_CHECKSUM command.
300 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.
302 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
303 structures created thus far.
305 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
306 AddChecksum, or the AddChecksum command
307 references a file unknown to Tracker, or the
308 range to checksum is invalid.
310 @retval EFI_SUCCESS The requested range has been checksummed.
315 ProcessCmdAddChecksum (
316 IN CONST QEMU_LOADER_ADD_CHECKSUM
*AddChecksum
,
317 IN CONST ORDERED_COLLECTION
*Tracker
320 ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
323 if (AddChecksum
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
324 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
325 return EFI_PROTOCOL_ERROR
;
328 TrackerEntry
= OrderedCollectionFind (Tracker
, AddChecksum
->File
);
329 if (TrackerEntry
== NULL
) {
330 DEBUG ((EFI_D_ERROR
, "%a: invalid blob reference \"%a\"\n", __FUNCTION__
,
332 return EFI_PROTOCOL_ERROR
;
335 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
336 if (Blob
->Size
<= AddChecksum
->ResultOffset
||
337 Blob
->Size
< AddChecksum
->Length
||
338 Blob
->Size
- AddChecksum
->Length
< AddChecksum
->Start
) {
339 DEBUG ((EFI_D_ERROR
, "%a: invalid checksum range in \"%a\"\n",
340 __FUNCTION__
, AddChecksum
->File
));
341 return EFI_PROTOCOL_ERROR
;
344 Blob
->Base
[AddChecksum
->ResultOffset
] = CalculateCheckSum8 (
345 Blob
->Base
+ AddChecksum
->Start
,
348 DEBUG ((EFI_D_VERBOSE
, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "
349 "Length=0x%x\n", __FUNCTION__
, AddChecksum
->File
,
350 AddChecksum
->ResultOffset
, AddChecksum
->Start
, AddChecksum
->Length
));
356 Process a QEMU_LOADER_WRITE_POINTER command.
358 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.
360 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
361 structures created thus far.
363 @param[in,out] S3Context The S3_CONTEXT object capturing the fw_cfg actions
364 of successfully processed QEMU_LOADER_WRITE_POINTER
365 commands, to be replayed at S3 resume. S3Context
366 may be NULL if S3 is disabled.
368 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
369 WritePointer. Or, the WritePointer command
370 references a file unknown to Tracker or the
371 fw_cfg directory. Or, the pointer object to
372 rewrite has invalid location, size, or initial
373 relative value. Or, the pointer value to store
374 does not fit in the given pointer size.
376 @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg
377 file has been written. If S3Context is not NULL,
378 then WritePointer has been condensed into
381 @return Error codes propagated from
382 SaveCondensedWritePointerToS3Context(). The
383 pointer object inside the writeable fw_cfg file
384 has not been written.
388 ProcessCmdWritePointer (
389 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
,
390 IN CONST ORDERED_COLLECTION
*Tracker
,
391 IN OUT S3_CONTEXT
*S3Context OPTIONAL
394 RETURN_STATUS Status
;
395 FIRMWARE_CONFIG_ITEM PointerItem
;
396 UINTN PointerItemSize
;
397 ORDERED_COLLECTION_ENTRY
*PointeeEntry
;
401 if (WritePointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0' ||
402 WritePointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
403 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
404 return EFI_PROTOCOL_ERROR
;
407 Status
= QemuFwCfgFindFile ((CONST CHAR8
*)WritePointer
->PointerFile
,
408 &PointerItem
, &PointerItemSize
);
409 PointeeEntry
= OrderedCollectionFind (Tracker
, WritePointer
->PointeeFile
);
410 if (RETURN_ERROR (Status
) || PointeeEntry
== NULL
) {
412 "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
413 __FUNCTION__
, WritePointer
->PointerFile
, WritePointer
->PointeeFile
));
414 return EFI_PROTOCOL_ERROR
;
417 if ((WritePointer
->PointerSize
!= 1 && WritePointer
->PointerSize
!= 2 &&
418 WritePointer
->PointerSize
!= 4 && WritePointer
->PointerSize
!= 8) ||
419 (PointerItemSize
< WritePointer
->PointerSize
) ||
420 (PointerItemSize
- WritePointer
->PointerSize
<
421 WritePointer
->PointerOffset
)) {
422 DEBUG ((DEBUG_ERROR
, "%a: invalid pointer location or size in \"%a\"\n",
423 __FUNCTION__
, WritePointer
->PointerFile
));
424 return EFI_PROTOCOL_ERROR
;
427 PointeeBlob
= OrderedCollectionUserStruct (PointeeEntry
);
428 PointerValue
= WritePointer
->PointeeOffset
;
429 if (PointerValue
>= PointeeBlob
->Size
) {
430 DEBUG ((DEBUG_ERROR
, "%a: invalid PointeeOffset\n", __FUNCTION__
));
431 return EFI_PROTOCOL_ERROR
;
435 // The memory allocation system ensures that the address of the byte past the
436 // last byte of any allocated object is expressible (no wraparound).
438 ASSERT ((UINTN
)PointeeBlob
->Base
<= MAX_ADDRESS
- PointeeBlob
->Size
);
440 PointerValue
+= (UINT64
)(UINTN
)PointeeBlob
->Base
;
441 if (WritePointer
->PointerSize
< 8 &&
442 RShiftU64 (PointerValue
, WritePointer
->PointerSize
* 8) != 0) {
443 DEBUG ((DEBUG_ERROR
, "%a: pointer value unrepresentable in \"%a\"\n",
444 __FUNCTION__
, WritePointer
->PointerFile
));
445 return EFI_PROTOCOL_ERROR
;
449 // If S3 is enabled, we have to capture the below fw_cfg actions in condensed
450 // form, to be replayed during S3 resume.
452 if (S3Context
!= NULL
) {
453 EFI_STATUS SaveStatus
;
455 SaveStatus
= SaveCondensedWritePointerToS3Context (
458 WritePointer
->PointerSize
,
459 WritePointer
->PointerOffset
,
462 if (EFI_ERROR (SaveStatus
)) {
467 QemuFwCfgSelectItem (PointerItem
);
468 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
469 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
472 // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob
473 // as unreleasable, for the case when the whole linker/loader script is
474 // handled successfully.
476 PointeeBlob
->HostsOnlyTableData
= FALSE
;
478 DEBUG ((DEBUG_VERBOSE
, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
479 "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
480 WritePointer
->PointerFile
, WritePointer
->PointeeFile
,
481 WritePointer
->PointerOffset
, WritePointer
->PointeeOffset
,
482 WritePointer
->PointerSize
));
488 Undo a QEMU_LOADER_WRITE_POINTER command.
490 This function revokes (zeroes out) a guest memory reference communicated to
491 QEMU earlier. The caller is responsible for invoking this function only on
492 such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed
493 by ProcessCmdWritePointer().
495 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
499 UndoCmdWritePointer (
500 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
503 RETURN_STATUS Status
;
504 FIRMWARE_CONFIG_ITEM PointerItem
;
505 UINTN PointerItemSize
;
508 Status
= QemuFwCfgFindFile ((CONST CHAR8
*)WritePointer
->PointerFile
,
509 &PointerItem
, &PointerItemSize
);
510 ASSERT_RETURN_ERROR (Status
);
513 QemuFwCfgSelectItem (PointerItem
);
514 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
515 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
517 DEBUG ((DEBUG_VERBOSE
,
518 "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
519 WritePointer
->PointerFile
, WritePointer
->PointerOffset
,
520 WritePointer
->PointerSize
));
525 // We'll be saving the keys of installed tables so that we can roll them back
526 // in case of failure. 128 tables should be enough for anyone (TM).
528 #define INSTALLED_TABLES_MAX 128
531 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte
532 array is an ACPI table, and if so, install it.
534 This function assumes that the entire QEMU linker/loader command file has
535 been processed successfully in a prior first pass.
537 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
539 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
542 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
544 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
545 elements, allocated by the caller. On output,
546 the function will have stored (appended) the
547 AcpiProtocol-internal key of the ACPI table that
548 the function has installed, if the AddPointer
549 command identified an ACPI table that is
550 different from RSDT and XSDT.
552 @param[in,out] NumInstalled On input, the number of entries already used in
553 InstalledKey; it must be in [0,
554 INSTALLED_TABLES_MAX] inclusive. On output, the
555 parameter is incremented if the AddPointer
556 command identified an ACPI table that is
557 different from RSDT and XSDT.
559 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
562 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI
563 table different from RSDT and XSDT, but there
564 was no more room in InstalledKey.
566 @retval EFI_SUCCESS AddPointer has been processed. Either an ACPI
567 table different from RSDT and XSDT has been
568 installed (reflected by InstalledKey and
569 NumInstalled), or RSDT or XSDT has been
570 identified but not installed, or the fw_cfg
571 blob pointed-into by AddPointer has been
572 marked as hosting something else than just
573 direct ACPI table contents.
575 @return Error codes returned by
576 AcpiProtocol->InstallAcpiTable().
581 Process2ndPassCmdAddPointer (
582 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
583 IN CONST ORDERED_COLLECTION
*Tracker
,
584 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
,
585 IN OUT UINTN InstalledKey
[INSTALLED_TABLES_MAX
],
586 IN OUT INT32
*NumInstalled
589 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
590 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry2
;
593 CONST UINT8
*PointerField
;
595 UINTN Blob2Remaining
;
597 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
598 CONST EFI_ACPI_DESCRIPTION_HEADER
*Header
;
601 if (*NumInstalled
< 0 || *NumInstalled
> INSTALLED_TABLES_MAX
) {
602 return EFI_INVALID_PARAMETER
;
605 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
606 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
607 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
608 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
609 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
611 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
614 // We assert that PointerValue falls inside Blob2's contents. This is ensured
615 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().
617 Blob2Remaining
= (UINTN
)Blob2
->Base
;
618 ASSERT(PointerValue
>= Blob2Remaining
);
619 Blob2Remaining
+= Blob2
->Size
;
620 ASSERT (PointerValue
< Blob2Remaining
);
622 Blob2Remaining
-= (UINTN
) PointerValue
;
623 DEBUG ((EFI_D_VERBOSE
, "%a: checking for ACPI header in \"%a\" at 0x%Lx "
624 "(remaining: 0x%Lx): ", __FUNCTION__
, AddPointer
->PointeeFile
,
625 PointerValue
, (UINT64
)Blob2Remaining
));
630 // To make our job simple, the FACS has a custom header. Sigh.
632 if (sizeof *Facs
<= Blob2Remaining
) {
633 Facs
= (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)PointerValue
;
635 if (Facs
->Length
>= sizeof *Facs
&&
636 Facs
->Length
<= Blob2Remaining
&&
638 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) {
639 DEBUG ((EFI_D_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
640 (CONST CHAR8
*)&Facs
->Signature
, Facs
->Length
));
641 TableSize
= Facs
->Length
;
646 // check for the uniform tables
648 if (TableSize
== 0 && sizeof *Header
<= Blob2Remaining
) {
649 Header
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
)PointerValue
;
651 if (Header
->Length
>= sizeof *Header
&&
652 Header
->Length
<= Blob2Remaining
&&
653 CalculateSum8 ((CONST UINT8
*)Header
, Header
->Length
) == 0) {
655 // This looks very much like an ACPI table from QEMU:
656 // - Length field consistent with both ACPI and containing blob size
657 // - checksum is correct
659 DEBUG ((EFI_D_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
660 (CONST CHAR8
*)&Header
->Signature
, Header
->Length
));
661 TableSize
= Header
->Length
;
664 // Skip RSDT and XSDT because those are handled by
665 // EFI_ACPI_TABLE_PROTOCOL automatically.
666 if (Header
->Signature
==
667 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
||
669 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
) {
675 if (TableSize
== 0) {
676 DEBUG ((EFI_D_VERBOSE
, "not found; marking fw_cfg blob as opaque\n"));
677 Blob2
->HostsOnlyTableData
= FALSE
;
681 if (*NumInstalled
== INSTALLED_TABLES_MAX
) {
682 DEBUG ((EFI_D_ERROR
, "%a: can't install more than %d tables\n",
683 __FUNCTION__
, INSTALLED_TABLES_MAX
));
684 return EFI_OUT_OF_RESOURCES
;
687 Status
= AcpiProtocol
->InstallAcpiTable (AcpiProtocol
,
688 (VOID
*)(UINTN
)PointerValue
, TableSize
,
689 &InstalledKey
[*NumInstalled
]);
690 if (EFI_ERROR (Status
)) {
691 DEBUG ((EFI_D_ERROR
, "%a: InstallAcpiTable(): %r\n", __FUNCTION__
,
701 Download, process, and install ACPI table data from the QEMU loader
704 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
706 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU
707 loader command with unsupported parameters
710 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg
713 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than
714 INSTALLED_TABLES_MAX tables found.
716 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.
718 @return Status codes returned by
719 AcpiProtocol->InstallAcpiTable().
724 InstallQemuFwCfgTables (
725 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
729 FIRMWARE_CONFIG_ITEM FwCfgItem
;
731 QEMU_LOADER_ENTRY
*LoaderStart
;
732 CONST QEMU_LOADER_ENTRY
*LoaderEntry
, *LoaderEnd
;
733 CONST QEMU_LOADER_ENTRY
*WritePointerSubsetEnd
;
734 ORIGINAL_ATTRIBUTES
*OriginalPciAttributes
;
735 UINTN OriginalPciAttributesCount
;
736 S3_CONTEXT
*S3Context
;
737 ORDERED_COLLECTION
*Tracker
;
740 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
742 Status
= QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem
, &FwCfgSize
);
743 if (EFI_ERROR (Status
)) {
746 if (FwCfgSize
% sizeof *LoaderEntry
!= 0) {
747 DEBUG ((EFI_D_ERROR
, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
748 __FUNCTION__
, (UINT64
)FwCfgSize
));
749 return EFI_PROTOCOL_ERROR
;
752 LoaderStart
= AllocatePool (FwCfgSize
);
753 if (LoaderStart
== NULL
) {
754 return EFI_OUT_OF_RESOURCES
;
756 EnablePciDecoding (&OriginalPciAttributes
, &OriginalPciAttributesCount
);
757 QemuFwCfgSelectItem (FwCfgItem
);
758 QemuFwCfgReadBytes (FwCfgSize
, LoaderStart
);
759 RestorePciDecoding (OriginalPciAttributes
, OriginalPciAttributesCount
);
760 LoaderEnd
= LoaderStart
+ FwCfgSize
/ sizeof *LoaderEntry
;
763 if (QemuFwCfgS3Enabled ()) {
765 // Size the allocation pessimistically, assuming that all commands in the
766 // script are QEMU_LOADER_WRITE_POINTER commands.
768 Status
= AllocateS3Context (&S3Context
, LoaderEnd
- LoaderStart
);
769 if (EFI_ERROR (Status
)) {
774 Tracker
= OrderedCollectionInit (BlobCompare
, BlobKeyCompare
);
775 if (Tracker
== NULL
) {
776 Status
= EFI_OUT_OF_RESOURCES
;
781 // first pass: process the commands
783 // "WritePointerSubsetEnd" points one past the last successful
784 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
785 // pass, no such command has been encountered yet.
787 WritePointerSubsetEnd
= LoaderStart
;
788 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
789 switch (LoaderEntry
->Type
) {
790 case QemuLoaderCmdAllocate
:
791 Status
= ProcessCmdAllocate (&LoaderEntry
->Command
.Allocate
, Tracker
);
794 case QemuLoaderCmdAddPointer
:
795 Status
= ProcessCmdAddPointer (&LoaderEntry
->Command
.AddPointer
,
799 case QemuLoaderCmdAddChecksum
:
800 Status
= ProcessCmdAddChecksum (&LoaderEntry
->Command
.AddChecksum
,
804 case QemuLoaderCmdWritePointer
:
805 Status
= ProcessCmdWritePointer (&LoaderEntry
->Command
.WritePointer
,
807 if (!EFI_ERROR (Status
)) {
808 WritePointerSubsetEnd
= LoaderEntry
+ 1;
813 DEBUG ((EFI_D_VERBOSE
, "%a: unknown loader command: 0x%x\n",
814 __FUNCTION__
, LoaderEntry
->Type
));
818 if (EFI_ERROR (Status
)) {
819 goto RollbackWritePointersAndFreeTracker
;
823 InstalledKey
= AllocatePool (INSTALLED_TABLES_MAX
* sizeof *InstalledKey
);
824 if (InstalledKey
== NULL
) {
825 Status
= EFI_OUT_OF_RESOURCES
;
826 goto RollbackWritePointersAndFreeTracker
;
830 // second pass: identify and install ACPI tables
833 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
834 if (LoaderEntry
->Type
== QemuLoaderCmdAddPointer
) {
835 Status
= Process2ndPassCmdAddPointer (&LoaderEntry
->Command
.AddPointer
,
836 Tracker
, AcpiProtocol
, InstalledKey
, &Installed
);
837 if (EFI_ERROR (Status
)) {
838 goto UninstallAcpiTables
;
844 // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3
845 // Boot Script opcodes has to be the last operation in this function, because
846 // if it succeeds, it cannot be undone.
848 if (S3Context
!= NULL
) {
849 Status
= TransferS3ContextToBootScript (S3Context
);
853 if (EFI_ERROR (Status
)) {
855 // roll back partial installation
857 while (Installed
> 0) {
859 AcpiProtocol
->UninstallAcpiTable (AcpiProtocol
, InstalledKey
[Installed
]);
862 DEBUG ((EFI_D_INFO
, "%a: installed %d tables\n", __FUNCTION__
, Installed
));
865 FreePool (InstalledKey
);
867 RollbackWritePointersAndFreeTracker
:
869 // In case of failure, revoke any allocation addresses that were communicated
870 // to QEMU previously, before we release all the blobs.
872 if (EFI_ERROR (Status
)) {
873 LoaderEntry
= WritePointerSubsetEnd
;
874 while (LoaderEntry
> LoaderStart
) {
876 if (LoaderEntry
->Type
== QemuLoaderCmdWritePointer
) {
877 UndoCmdWritePointer (&LoaderEntry
->Command
.WritePointer
);
883 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
884 // place only if we're exiting with success and the blob hosts data that is
885 // not directly part of some ACPI table.
887 for (TrackerEntry
= OrderedCollectionMin (Tracker
); TrackerEntry
!= NULL
;
888 TrackerEntry
= TrackerEntry2
) {
892 TrackerEntry2
= OrderedCollectionNext (TrackerEntry
);
893 OrderedCollectionDelete (Tracker
, TrackerEntry
, &UserStruct
);
896 if (EFI_ERROR (Status
) || Blob
->HostsOnlyTableData
) {
897 DEBUG ((EFI_D_VERBOSE
, "%a: freeing \"%a\"\n", __FUNCTION__
,
899 gBS
->FreePages ((UINTN
)Blob
->Base
, EFI_SIZE_TO_PAGES (Blob
->Size
));
903 OrderedCollectionUninit (Tracker
);
906 if (S3Context
!= NULL
) {
907 ReleaseS3Context (S3Context
);
911 FreePool (LoaderStart
);