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
;
281 RShiftU64 (PointerValue
, AddPointer
->PointerSize
* 8 - 1), 1) != 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 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
364 WritePointer. Or, the WritePointer command
365 references a file unknown to Tracker or the
366 fw_cfg directory. Or, the pointer object to
367 rewrite has invalid location, size, or initial
368 relative value. Or, the pointer value to store
369 does not fit in the given pointer size.
371 @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg
372 file has been written.
376 ProcessCmdWritePointer (
377 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
,
378 IN CONST ORDERED_COLLECTION
*Tracker
381 RETURN_STATUS Status
;
382 FIRMWARE_CONFIG_ITEM PointerItem
;
383 UINTN PointerItemSize
;
384 ORDERED_COLLECTION_ENTRY
*PointeeEntry
;
388 if (WritePointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0' ||
389 WritePointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
390 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
391 return EFI_PROTOCOL_ERROR
;
394 Status
= QemuFwCfgFindFile ((CONST CHAR8
*)WritePointer
->PointerFile
,
395 &PointerItem
, &PointerItemSize
);
396 PointeeEntry
= OrderedCollectionFind (Tracker
, WritePointer
->PointeeFile
);
397 if (RETURN_ERROR (Status
) || PointeeEntry
== NULL
) {
399 "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
400 __FUNCTION__
, WritePointer
->PointerFile
, WritePointer
->PointeeFile
));
401 return EFI_PROTOCOL_ERROR
;
404 if ((WritePointer
->PointerSize
!= 1 && WritePointer
->PointerSize
!= 2 &&
405 WritePointer
->PointerSize
!= 4 && WritePointer
->PointerSize
!= 8) ||
406 (PointerItemSize
< WritePointer
->PointerSize
) ||
407 (PointerItemSize
- WritePointer
->PointerSize
<
408 WritePointer
->PointerOffset
)) {
409 DEBUG ((DEBUG_ERROR
, "%a: invalid pointer location or size in \"%a\"\n",
410 __FUNCTION__
, WritePointer
->PointerFile
));
411 return EFI_PROTOCOL_ERROR
;
414 PointeeBlob
= OrderedCollectionUserStruct (PointeeEntry
);
415 PointerValue
= WritePointer
->PointeeOffset
;
416 if (PointerValue
>= PointeeBlob
->Size
) {
417 DEBUG ((DEBUG_ERROR
, "%a: invalid PointeeOffset\n", __FUNCTION__
));
418 return EFI_PROTOCOL_ERROR
;
422 // The memory allocation system ensures that the address of the byte past the
423 // last byte of any allocated object is expressible (no wraparound).
425 ASSERT ((UINTN
)PointeeBlob
->Base
<= MAX_ADDRESS
- PointeeBlob
->Size
);
427 PointerValue
+= (UINT64
)(UINTN
)PointeeBlob
->Base
;
429 RShiftU64 (PointerValue
, WritePointer
->PointerSize
* 8 - 1), 1) != 0) {
430 DEBUG ((DEBUG_ERROR
, "%a: pointer value unrepresentable in \"%a\"\n",
431 __FUNCTION__
, WritePointer
->PointerFile
));
432 return EFI_PROTOCOL_ERROR
;
435 QemuFwCfgSelectItem (PointerItem
);
436 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
437 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
440 // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob
441 // as unreleasable, for the case when the whole linker/loader script is
442 // handled successfully.
444 PointeeBlob
->HostsOnlyTableData
= FALSE
;
446 DEBUG ((DEBUG_VERBOSE
, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
447 "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
448 WritePointer
->PointerFile
, WritePointer
->PointeeFile
,
449 WritePointer
->PointerOffset
, WritePointer
->PointeeOffset
,
450 WritePointer
->PointerSize
));
456 Undo a QEMU_LOADER_WRITE_POINTER command.
458 This function revokes (zeroes out) a guest memory reference communicated to
459 QEMU earlier. The caller is responsible for invoking this function only on
460 such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed
461 by ProcessCmdWritePointer().
463 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
467 UndoCmdWritePointer (
468 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
471 RETURN_STATUS Status
;
472 FIRMWARE_CONFIG_ITEM PointerItem
;
473 UINTN PointerItemSize
;
476 Status
= QemuFwCfgFindFile ((CONST CHAR8
*)WritePointer
->PointerFile
,
477 &PointerItem
, &PointerItemSize
);
478 ASSERT_RETURN_ERROR (Status
);
481 QemuFwCfgSelectItem (PointerItem
);
482 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
483 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
485 DEBUG ((DEBUG_VERBOSE
,
486 "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
487 WritePointer
->PointerFile
, WritePointer
->PointerOffset
,
488 WritePointer
->PointerSize
));
493 // We'll be saving the keys of installed tables so that we can roll them back
494 // in case of failure. 128 tables should be enough for anyone (TM).
496 #define INSTALLED_TABLES_MAX 128
499 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte
500 array is an ACPI table, and if so, install it.
502 This function assumes that the entire QEMU linker/loader command file has
503 been processed successfully in a prior first pass.
505 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
507 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
510 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
512 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
513 elements, allocated by the caller. On output,
514 the function will have stored (appended) the
515 AcpiProtocol-internal key of the ACPI table that
516 the function has installed, if the AddPointer
517 command identified an ACPI table that is
518 different from RSDT and XSDT.
520 @param[in,out] NumInstalled On input, the number of entries already used in
521 InstalledKey; it must be in [0,
522 INSTALLED_TABLES_MAX] inclusive. On output, the
523 parameter is incremented if the AddPointer
524 command identified an ACPI table that is
525 different from RSDT and XSDT.
527 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
530 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI
531 table different from RSDT and XSDT, but there
532 was no more room in InstalledKey.
534 @retval EFI_SUCCESS AddPointer has been processed. Either an ACPI
535 table different from RSDT and XSDT has been
536 installed (reflected by InstalledKey and
537 NumInstalled), or RSDT or XSDT has been
538 identified but not installed, or the fw_cfg
539 blob pointed-into by AddPointer has been
540 marked as hosting something else than just
541 direct ACPI table contents.
543 @return Error codes returned by
544 AcpiProtocol->InstallAcpiTable().
549 Process2ndPassCmdAddPointer (
550 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
551 IN CONST ORDERED_COLLECTION
*Tracker
,
552 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
,
553 IN OUT UINTN InstalledKey
[INSTALLED_TABLES_MAX
],
554 IN OUT INT32
*NumInstalled
557 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
558 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry2
;
561 CONST UINT8
*PointerField
;
563 UINTN Blob2Remaining
;
565 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
566 CONST EFI_ACPI_DESCRIPTION_HEADER
*Header
;
569 if (*NumInstalled
< 0 || *NumInstalled
> INSTALLED_TABLES_MAX
) {
570 return EFI_INVALID_PARAMETER
;
573 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
574 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
575 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
576 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
577 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
579 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
582 // We assert that PointerValue falls inside Blob2's contents. This is ensured
583 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().
585 Blob2Remaining
= (UINTN
)Blob2
->Base
;
586 ASSERT(PointerValue
>= Blob2Remaining
);
587 Blob2Remaining
+= Blob2
->Size
;
588 ASSERT (PointerValue
< Blob2Remaining
);
590 Blob2Remaining
-= (UINTN
) PointerValue
;
591 DEBUG ((EFI_D_VERBOSE
, "%a: checking for ACPI header in \"%a\" at 0x%Lx "
592 "(remaining: 0x%Lx): ", __FUNCTION__
, AddPointer
->PointeeFile
,
593 PointerValue
, (UINT64
)Blob2Remaining
));
598 // To make our job simple, the FACS has a custom header. Sigh.
600 if (sizeof *Facs
<= Blob2Remaining
) {
601 Facs
= (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)PointerValue
;
603 if (Facs
->Length
>= sizeof *Facs
&&
604 Facs
->Length
<= Blob2Remaining
&&
606 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) {
607 DEBUG ((EFI_D_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
608 (CONST CHAR8
*)&Facs
->Signature
, Facs
->Length
));
609 TableSize
= Facs
->Length
;
614 // check for the uniform tables
616 if (TableSize
== 0 && sizeof *Header
<= Blob2Remaining
) {
617 Header
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
)PointerValue
;
619 if (Header
->Length
>= sizeof *Header
&&
620 Header
->Length
<= Blob2Remaining
&&
621 CalculateSum8 ((CONST UINT8
*)Header
, Header
->Length
) == 0) {
623 // This looks very much like an ACPI table from QEMU:
624 // - Length field consistent with both ACPI and containing blob size
625 // - checksum is correct
627 DEBUG ((EFI_D_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
628 (CONST CHAR8
*)&Header
->Signature
, Header
->Length
));
629 TableSize
= Header
->Length
;
632 // Skip RSDT and XSDT because those are handled by
633 // EFI_ACPI_TABLE_PROTOCOL automatically.
634 if (Header
->Signature
==
635 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
||
637 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
) {
643 if (TableSize
== 0) {
644 DEBUG ((EFI_D_VERBOSE
, "not found; marking fw_cfg blob as opaque\n"));
645 Blob2
->HostsOnlyTableData
= FALSE
;
649 if (*NumInstalled
== INSTALLED_TABLES_MAX
) {
650 DEBUG ((EFI_D_ERROR
, "%a: can't install more than %d tables\n",
651 __FUNCTION__
, INSTALLED_TABLES_MAX
));
652 return EFI_OUT_OF_RESOURCES
;
655 Status
= AcpiProtocol
->InstallAcpiTable (AcpiProtocol
,
656 (VOID
*)(UINTN
)PointerValue
, TableSize
,
657 &InstalledKey
[*NumInstalled
]);
658 if (EFI_ERROR (Status
)) {
659 DEBUG ((EFI_D_ERROR
, "%a: InstallAcpiTable(): %r\n", __FUNCTION__
,
669 Download, process, and install ACPI table data from the QEMU loader
672 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
674 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU
675 loader command with unsupported parameters
678 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg
681 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than
682 INSTALLED_TABLES_MAX tables found.
684 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.
686 @return Status codes returned by
687 AcpiProtocol->InstallAcpiTable().
692 InstallQemuFwCfgTables (
693 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
697 FIRMWARE_CONFIG_ITEM FwCfgItem
;
699 QEMU_LOADER_ENTRY
*LoaderStart
;
700 CONST QEMU_LOADER_ENTRY
*LoaderEntry
, *LoaderEnd
;
701 CONST QEMU_LOADER_ENTRY
*WritePointerSubsetEnd
;
702 ORIGINAL_ATTRIBUTES
*OriginalPciAttributes
;
703 UINTN OriginalPciAttributesCount
;
704 ORDERED_COLLECTION
*Tracker
;
707 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
709 Status
= QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem
, &FwCfgSize
);
710 if (EFI_ERROR (Status
)) {
713 if (FwCfgSize
% sizeof *LoaderEntry
!= 0) {
714 DEBUG ((EFI_D_ERROR
, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
715 __FUNCTION__
, (UINT64
)FwCfgSize
));
716 return EFI_PROTOCOL_ERROR
;
719 LoaderStart
= AllocatePool (FwCfgSize
);
720 if (LoaderStart
== NULL
) {
721 return EFI_OUT_OF_RESOURCES
;
723 EnablePciDecoding (&OriginalPciAttributes
, &OriginalPciAttributesCount
);
724 QemuFwCfgSelectItem (FwCfgItem
);
725 QemuFwCfgReadBytes (FwCfgSize
, LoaderStart
);
726 RestorePciDecoding (OriginalPciAttributes
, OriginalPciAttributesCount
);
727 LoaderEnd
= LoaderStart
+ FwCfgSize
/ sizeof *LoaderEntry
;
729 Tracker
= OrderedCollectionInit (BlobCompare
, BlobKeyCompare
);
730 if (Tracker
== NULL
) {
731 Status
= EFI_OUT_OF_RESOURCES
;
736 // first pass: process the commands
738 // "WritePointerSubsetEnd" points one past the last successful
739 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
740 // pass, no such command has been encountered yet.
742 WritePointerSubsetEnd
= LoaderStart
;
743 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
744 switch (LoaderEntry
->Type
) {
745 case QemuLoaderCmdAllocate
:
746 Status
= ProcessCmdAllocate (&LoaderEntry
->Command
.Allocate
, Tracker
);
749 case QemuLoaderCmdAddPointer
:
750 Status
= ProcessCmdAddPointer (&LoaderEntry
->Command
.AddPointer
,
754 case QemuLoaderCmdAddChecksum
:
755 Status
= ProcessCmdAddChecksum (&LoaderEntry
->Command
.AddChecksum
,
759 case QemuLoaderCmdWritePointer
:
760 Status
= ProcessCmdWritePointer (&LoaderEntry
->Command
.WritePointer
,
762 if (!EFI_ERROR (Status
)) {
763 WritePointerSubsetEnd
= LoaderEntry
+ 1;
768 DEBUG ((EFI_D_VERBOSE
, "%a: unknown loader command: 0x%x\n",
769 __FUNCTION__
, LoaderEntry
->Type
));
773 if (EFI_ERROR (Status
)) {
774 goto RollbackWritePointersAndFreeTracker
;
778 InstalledKey
= AllocatePool (INSTALLED_TABLES_MAX
* sizeof *InstalledKey
);
779 if (InstalledKey
== NULL
) {
780 Status
= EFI_OUT_OF_RESOURCES
;
781 goto RollbackWritePointersAndFreeTracker
;
785 // second pass: identify and install ACPI tables
788 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
789 if (LoaderEntry
->Type
== QemuLoaderCmdAddPointer
) {
790 Status
= Process2ndPassCmdAddPointer (&LoaderEntry
->Command
.AddPointer
,
791 Tracker
, AcpiProtocol
, InstalledKey
, &Installed
);
792 if (EFI_ERROR (Status
)) {
798 if (EFI_ERROR (Status
)) {
800 // roll back partial installation
802 while (Installed
> 0) {
804 AcpiProtocol
->UninstallAcpiTable (AcpiProtocol
, InstalledKey
[Installed
]);
807 DEBUG ((EFI_D_INFO
, "%a: installed %d tables\n", __FUNCTION__
, Installed
));
810 FreePool (InstalledKey
);
812 RollbackWritePointersAndFreeTracker
:
814 // In case of failure, revoke any allocation addresses that were communicated
815 // to QEMU previously, before we release all the blobs.
817 if (EFI_ERROR (Status
)) {
818 LoaderEntry
= WritePointerSubsetEnd
;
819 while (LoaderEntry
> LoaderStart
) {
821 if (LoaderEntry
->Type
== QemuLoaderCmdWritePointer
) {
822 UndoCmdWritePointer (&LoaderEntry
->Command
.WritePointer
);
828 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
829 // place only if we're exiting with success and the blob hosts data that is
830 // not directly part of some ACPI table.
832 for (TrackerEntry
= OrderedCollectionMin (Tracker
); TrackerEntry
!= NULL
;
833 TrackerEntry
= TrackerEntry2
) {
837 TrackerEntry2
= OrderedCollectionNext (TrackerEntry
);
838 OrderedCollectionDelete (Tracker
, TrackerEntry
, &UserStruct
);
841 if (EFI_ERROR (Status
) || Blob
->HostsOnlyTableData
) {
842 DEBUG ((EFI_D_VERBOSE
, "%a: freeing \"%a\"\n", __FUNCTION__
,
844 gBS
->FreePages ((UINTN
)Blob
->Base
, EFI_SIZE_TO_PAGES (Blob
->Size
));
848 OrderedCollectionUninit (Tracker
);
851 FreePool (LoaderStart
);