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 Process a QEMU_LOADER_ALLOCATE command.
105 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to process.
107 @param[in,out] Tracker The ORDERED_COLLECTION tracking the BLOB user
108 structures created thus far.
110 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been
111 allocated for the blob contents, and the
112 contents have been saved. A BLOB object (user
113 structure) has been allocated from pool memory,
114 referencing the blob contents. The BLOB user
115 structure has been linked into Tracker.
117 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
118 Allocate, or the Allocate command references a
119 file that is already known by Tracker.
121 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in
124 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.
126 @return Error codes from QemuFwCfgFindFile() and
127 gBS->AllocatePages().
133 IN CONST QEMU_LOADER_ALLOCATE
*Allocate
,
134 IN OUT ORDERED_COLLECTION
*Tracker
137 FIRMWARE_CONFIG_ITEM FwCfgItem
;
141 EFI_PHYSICAL_ADDRESS Address
;
144 if (Allocate
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
145 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
146 return EFI_PROTOCOL_ERROR
;
149 if (Allocate
->Alignment
> EFI_PAGE_SIZE
) {
150 DEBUG ((EFI_D_ERROR
, "%a: unsupported alignment 0x%x\n", __FUNCTION__
,
151 Allocate
->Alignment
));
152 return EFI_UNSUPPORTED
;
155 Status
= QemuFwCfgFindFile ((CHAR8
*)Allocate
->File
, &FwCfgItem
, &FwCfgSize
);
156 if (EFI_ERROR (Status
)) {
157 DEBUG ((EFI_D_ERROR
, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__
,
158 Allocate
->File
, Status
));
162 NumPages
= EFI_SIZE_TO_PAGES (FwCfgSize
);
163 Address
= 0xFFFFFFFF;
164 Status
= gBS
->AllocatePages (AllocateMaxAddress
, EfiACPIMemoryNVS
, NumPages
,
166 if (EFI_ERROR (Status
)) {
170 Blob
= AllocatePool (sizeof *Blob
);
172 Status
= EFI_OUT_OF_RESOURCES
;
175 CopyMem (Blob
->File
, Allocate
->File
, QEMU_LOADER_FNAME_SIZE
);
176 Blob
->Size
= FwCfgSize
;
177 Blob
->Base
= (VOID
*)(UINTN
)Address
;
178 Blob
->HostsOnlyTableData
= TRUE
;
180 Status
= OrderedCollectionInsert (Tracker
, NULL
, Blob
);
181 if (Status
== RETURN_ALREADY_STARTED
) {
182 DEBUG ((EFI_D_ERROR
, "%a: duplicated file \"%a\"\n", __FUNCTION__
,
184 Status
= EFI_PROTOCOL_ERROR
;
186 if (EFI_ERROR (Status
)) {
190 QemuFwCfgSelectItem (FwCfgItem
);
191 QemuFwCfgReadBytes (FwCfgSize
, Blob
->Base
);
192 ZeroMem (Blob
->Base
+ Blob
->Size
, EFI_PAGES_TO_SIZE (NumPages
) - Blob
->Size
);
194 DEBUG ((EFI_D_VERBOSE
, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "
195 "Address=0x%Lx\n", __FUNCTION__
, Allocate
->File
, Allocate
->Alignment
,
196 Allocate
->Zone
, (UINT64
)Blob
->Size
, (UINT64
)(UINTN
)Blob
->Base
));
203 gBS
->FreePages (Address
, NumPages
);
210 Process a QEMU_LOADER_ADD_POINTER command.
212 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
214 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
215 structures created thus far.
217 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
218 AddPointer, or the AddPointer command references
219 a file unknown to Tracker, or the pointer to
220 relocate has invalid location, size, or value, or
221 the relocated pointer value is not representable
222 in the given pointer size.
224 @retval EFI_SUCCESS The pointer field inside the pointer blob has
230 ProcessCmdAddPointer (
231 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
232 IN CONST ORDERED_COLLECTION
*Tracker
235 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
240 if (AddPointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0' ||
241 AddPointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
242 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
243 return EFI_PROTOCOL_ERROR
;
246 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
247 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
248 if (TrackerEntry
== NULL
|| TrackerEntry2
== NULL
) {
249 DEBUG ((EFI_D_ERROR
, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",
250 __FUNCTION__
, AddPointer
->PointerFile
, AddPointer
->PointeeFile
));
251 return EFI_PROTOCOL_ERROR
;
254 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
255 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
256 if ((AddPointer
->PointerSize
!= 1 && AddPointer
->PointerSize
!= 2 &&
257 AddPointer
->PointerSize
!= 4 && AddPointer
->PointerSize
!= 8) ||
258 Blob
->Size
< AddPointer
->PointerSize
||
259 Blob
->Size
- AddPointer
->PointerSize
< AddPointer
->PointerOffset
) {
260 DEBUG ((EFI_D_ERROR
, "%a: invalid pointer location or size in \"%a\"\n",
261 __FUNCTION__
, AddPointer
->PointerFile
));
262 return EFI_PROTOCOL_ERROR
;
265 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
267 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
268 if (PointerValue
>= Blob2
->Size
) {
269 DEBUG ((EFI_D_ERROR
, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__
,
270 AddPointer
->PointerFile
));
271 return EFI_PROTOCOL_ERROR
;
275 // The memory allocation system ensures that the address of the byte past the
276 // last byte of any allocated object is expressible (no wraparound).
278 ASSERT ((UINTN
)Blob2
->Base
<= MAX_ADDRESS
- Blob2
->Size
);
280 PointerValue
+= (UINT64
)(UINTN
)Blob2
->Base
;
281 if (AddPointer
->PointerSize
< 8 &&
282 RShiftU64 (PointerValue
, AddPointer
->PointerSize
* 8) != 0) {
283 DEBUG ((EFI_D_ERROR
, "%a: relocated pointer value unrepresentable in "
284 "\"%a\"\n", __FUNCTION__
, AddPointer
->PointerFile
));
285 return EFI_PROTOCOL_ERROR
;
288 CopyMem (PointerField
, &PointerValue
, AddPointer
->PointerSize
);
290 DEBUG ((EFI_D_VERBOSE
, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
291 "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
292 AddPointer
->PointerFile
, AddPointer
->PointeeFile
,
293 AddPointer
->PointerOffset
, AddPointer
->PointerSize
));
299 Process a QEMU_LOADER_ADD_CHECKSUM command.
301 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.
303 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
304 structures created thus far.
306 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
307 AddChecksum, or the AddChecksum command
308 references a file unknown to Tracker, or the
309 range to checksum is invalid.
311 @retval EFI_SUCCESS The requested range has been checksummed.
316 ProcessCmdAddChecksum (
317 IN CONST QEMU_LOADER_ADD_CHECKSUM
*AddChecksum
,
318 IN CONST ORDERED_COLLECTION
*Tracker
321 ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
324 if (AddChecksum
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
325 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
326 return EFI_PROTOCOL_ERROR
;
329 TrackerEntry
= OrderedCollectionFind (Tracker
, AddChecksum
->File
);
330 if (TrackerEntry
== NULL
) {
331 DEBUG ((EFI_D_ERROR
, "%a: invalid blob reference \"%a\"\n", __FUNCTION__
,
333 return EFI_PROTOCOL_ERROR
;
336 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
337 if (Blob
->Size
<= AddChecksum
->ResultOffset
||
338 Blob
->Size
< AddChecksum
->Length
||
339 Blob
->Size
- AddChecksum
->Length
< AddChecksum
->Start
) {
340 DEBUG ((EFI_D_ERROR
, "%a: invalid checksum range in \"%a\"\n",
341 __FUNCTION__
, AddChecksum
->File
));
342 return EFI_PROTOCOL_ERROR
;
345 Blob
->Base
[AddChecksum
->ResultOffset
] = CalculateCheckSum8 (
346 Blob
->Base
+ AddChecksum
->Start
,
349 DEBUG ((EFI_D_VERBOSE
, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "
350 "Length=0x%x\n", __FUNCTION__
, AddChecksum
->File
,
351 AddChecksum
->ResultOffset
, AddChecksum
->Start
, AddChecksum
->Length
));
357 Process a QEMU_LOADER_WRITE_POINTER command.
359 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.
361 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
362 structures created thus far.
364 @param[in,out] S3Context The S3_CONTEXT object capturing the fw_cfg actions
365 of successfully processed QEMU_LOADER_WRITE_POINTER
366 commands, to be replayed at S3 resume. S3Context
367 may be NULL if S3 is disabled.
369 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
370 WritePointer. Or, the WritePointer command
371 references a file unknown to Tracker or the
372 fw_cfg directory. Or, the pointer object to
373 rewrite has invalid location, size, or initial
374 relative value. Or, the pointer value to store
375 does not fit in the given pointer size.
377 @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg
378 file has been written. If S3Context is not NULL,
379 then WritePointer has been condensed into
382 @return Error codes propagated from
383 SaveCondensedWritePointerToS3Context(). The
384 pointer object inside the writeable fw_cfg file
385 has not been written.
389 ProcessCmdWritePointer (
390 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
,
391 IN CONST ORDERED_COLLECTION
*Tracker
,
392 IN OUT S3_CONTEXT
*S3Context OPTIONAL
395 RETURN_STATUS Status
;
396 FIRMWARE_CONFIG_ITEM PointerItem
;
397 UINTN PointerItemSize
;
398 ORDERED_COLLECTION_ENTRY
*PointeeEntry
;
402 if (WritePointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0' ||
403 WritePointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
404 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
405 return EFI_PROTOCOL_ERROR
;
408 Status
= QemuFwCfgFindFile ((CONST CHAR8
*)WritePointer
->PointerFile
,
409 &PointerItem
, &PointerItemSize
);
410 PointeeEntry
= OrderedCollectionFind (Tracker
, WritePointer
->PointeeFile
);
411 if (RETURN_ERROR (Status
) || PointeeEntry
== NULL
) {
413 "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
414 __FUNCTION__
, WritePointer
->PointerFile
, WritePointer
->PointeeFile
));
415 return EFI_PROTOCOL_ERROR
;
418 if ((WritePointer
->PointerSize
!= 1 && WritePointer
->PointerSize
!= 2 &&
419 WritePointer
->PointerSize
!= 4 && WritePointer
->PointerSize
!= 8) ||
420 (PointerItemSize
< WritePointer
->PointerSize
) ||
421 (PointerItemSize
- WritePointer
->PointerSize
<
422 WritePointer
->PointerOffset
)) {
423 DEBUG ((DEBUG_ERROR
, "%a: invalid pointer location or size in \"%a\"\n",
424 __FUNCTION__
, WritePointer
->PointerFile
));
425 return EFI_PROTOCOL_ERROR
;
428 PointeeBlob
= OrderedCollectionUserStruct (PointeeEntry
);
429 PointerValue
= WritePointer
->PointeeOffset
;
430 if (PointerValue
>= PointeeBlob
->Size
) {
431 DEBUG ((DEBUG_ERROR
, "%a: invalid PointeeOffset\n", __FUNCTION__
));
432 return EFI_PROTOCOL_ERROR
;
436 // The memory allocation system ensures that the address of the byte past the
437 // last byte of any allocated object is expressible (no wraparound).
439 ASSERT ((UINTN
)PointeeBlob
->Base
<= MAX_ADDRESS
- PointeeBlob
->Size
);
441 PointerValue
+= (UINT64
)(UINTN
)PointeeBlob
->Base
;
442 if (WritePointer
->PointerSize
< 8 &&
443 RShiftU64 (PointerValue
, WritePointer
->PointerSize
* 8) != 0) {
444 DEBUG ((DEBUG_ERROR
, "%a: pointer value unrepresentable in \"%a\"\n",
445 __FUNCTION__
, WritePointer
->PointerFile
));
446 return EFI_PROTOCOL_ERROR
;
450 // If S3 is enabled, we have to capture the below fw_cfg actions in condensed
451 // form, to be replayed during S3 resume.
453 if (S3Context
!= NULL
) {
454 EFI_STATUS SaveStatus
;
456 SaveStatus
= SaveCondensedWritePointerToS3Context (
459 WritePointer
->PointerSize
,
460 WritePointer
->PointerOffset
,
463 if (EFI_ERROR (SaveStatus
)) {
468 QemuFwCfgSelectItem (PointerItem
);
469 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
470 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
473 // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob
474 // as unreleasable, for the case when the whole linker/loader script is
475 // handled successfully.
477 PointeeBlob
->HostsOnlyTableData
= FALSE
;
479 DEBUG ((DEBUG_VERBOSE
, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
480 "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
481 WritePointer
->PointerFile
, WritePointer
->PointeeFile
,
482 WritePointer
->PointerOffset
, WritePointer
->PointeeOffset
,
483 WritePointer
->PointerSize
));
489 Undo a QEMU_LOADER_WRITE_POINTER command.
491 This function revokes (zeroes out) a guest memory reference communicated to
492 QEMU earlier. The caller is responsible for invoking this function only on
493 such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed
494 by ProcessCmdWritePointer().
496 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
500 UndoCmdWritePointer (
501 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
504 RETURN_STATUS Status
;
505 FIRMWARE_CONFIG_ITEM PointerItem
;
506 UINTN PointerItemSize
;
509 Status
= QemuFwCfgFindFile ((CONST CHAR8
*)WritePointer
->PointerFile
,
510 &PointerItem
, &PointerItemSize
);
511 ASSERT_RETURN_ERROR (Status
);
514 QemuFwCfgSelectItem (PointerItem
);
515 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
516 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
518 DEBUG ((DEBUG_VERBOSE
,
519 "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
520 WritePointer
->PointerFile
, WritePointer
->PointerOffset
,
521 WritePointer
->PointerSize
));
526 // We'll be saving the keys of installed tables so that we can roll them back
527 // in case of failure. 128 tables should be enough for anyone (TM).
529 #define INSTALLED_TABLES_MAX 128
532 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte
533 array is an ACPI table, and if so, install it.
535 This function assumes that the entire QEMU linker/loader command file has
536 been processed successfully in a prior first pass.
538 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
540 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
543 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
545 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
546 elements, allocated by the caller. On output,
547 the function will have stored (appended) the
548 AcpiProtocol-internal key of the ACPI table that
549 the function has installed, if the AddPointer
550 command identified an ACPI table that is
551 different from RSDT and XSDT.
553 @param[in,out] NumInstalled On input, the number of entries already used in
554 InstalledKey; it must be in [0,
555 INSTALLED_TABLES_MAX] inclusive. On output, the
556 parameter is incremented if the AddPointer
557 command identified an ACPI table that is
558 different from RSDT and XSDT.
560 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
563 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI
564 table different from RSDT and XSDT, but there
565 was no more room in InstalledKey.
567 @retval EFI_SUCCESS AddPointer has been processed. Either an ACPI
568 table different from RSDT and XSDT has been
569 installed (reflected by InstalledKey and
570 NumInstalled), or RSDT or XSDT has been
571 identified but not installed, or the fw_cfg
572 blob pointed-into by AddPointer has been
573 marked as hosting something else than just
574 direct ACPI table contents.
576 @return Error codes returned by
577 AcpiProtocol->InstallAcpiTable().
582 Process2ndPassCmdAddPointer (
583 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
584 IN CONST ORDERED_COLLECTION
*Tracker
,
585 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
,
586 IN OUT UINTN InstalledKey
[INSTALLED_TABLES_MAX
],
587 IN OUT INT32
*NumInstalled
590 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
591 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry2
;
594 CONST UINT8
*PointerField
;
596 UINTN Blob2Remaining
;
598 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
599 CONST EFI_ACPI_DESCRIPTION_HEADER
*Header
;
602 if (*NumInstalled
< 0 || *NumInstalled
> INSTALLED_TABLES_MAX
) {
603 return EFI_INVALID_PARAMETER
;
606 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
607 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
608 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
609 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
610 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
612 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
615 // We assert that PointerValue falls inside Blob2's contents. This is ensured
616 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().
618 Blob2Remaining
= (UINTN
)Blob2
->Base
;
619 ASSERT(PointerValue
>= Blob2Remaining
);
620 Blob2Remaining
+= Blob2
->Size
;
621 ASSERT (PointerValue
< Blob2Remaining
);
623 Blob2Remaining
-= (UINTN
) PointerValue
;
624 DEBUG ((EFI_D_VERBOSE
, "%a: checking for ACPI header in \"%a\" at 0x%Lx "
625 "(remaining: 0x%Lx): ", __FUNCTION__
, AddPointer
->PointeeFile
,
626 PointerValue
, (UINT64
)Blob2Remaining
));
631 // To make our job simple, the FACS has a custom header. Sigh.
633 if (sizeof *Facs
<= Blob2Remaining
) {
634 Facs
= (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)PointerValue
;
636 if (Facs
->Length
>= sizeof *Facs
&&
637 Facs
->Length
<= Blob2Remaining
&&
639 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) {
640 DEBUG ((EFI_D_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
641 (CONST CHAR8
*)&Facs
->Signature
, Facs
->Length
));
642 TableSize
= Facs
->Length
;
647 // check for the uniform tables
649 if (TableSize
== 0 && sizeof *Header
<= Blob2Remaining
) {
650 Header
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
)PointerValue
;
652 if (Header
->Length
>= sizeof *Header
&&
653 Header
->Length
<= Blob2Remaining
&&
654 CalculateSum8 ((CONST UINT8
*)Header
, Header
->Length
) == 0) {
656 // This looks very much like an ACPI table from QEMU:
657 // - Length field consistent with both ACPI and containing blob size
658 // - checksum is correct
660 DEBUG ((EFI_D_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
661 (CONST CHAR8
*)&Header
->Signature
, Header
->Length
));
662 TableSize
= Header
->Length
;
665 // Skip RSDT and XSDT because those are handled by
666 // EFI_ACPI_TABLE_PROTOCOL automatically.
667 if (Header
->Signature
==
668 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
||
670 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
) {
676 if (TableSize
== 0) {
677 DEBUG ((EFI_D_VERBOSE
, "not found; marking fw_cfg blob as opaque\n"));
678 Blob2
->HostsOnlyTableData
= FALSE
;
682 if (*NumInstalled
== INSTALLED_TABLES_MAX
) {
683 DEBUG ((EFI_D_ERROR
, "%a: can't install more than %d tables\n",
684 __FUNCTION__
, INSTALLED_TABLES_MAX
));
685 return EFI_OUT_OF_RESOURCES
;
688 Status
= AcpiProtocol
->InstallAcpiTable (AcpiProtocol
,
689 (VOID
*)(UINTN
)PointerValue
, TableSize
,
690 &InstalledKey
[*NumInstalled
]);
691 if (EFI_ERROR (Status
)) {
692 DEBUG ((EFI_D_ERROR
, "%a: InstallAcpiTable(): %r\n", __FUNCTION__
,
702 Download, process, and install ACPI table data from the QEMU loader
705 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
707 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU
708 loader command with unsupported parameters
711 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg
714 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than
715 INSTALLED_TABLES_MAX tables found.
717 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.
719 @return Status codes returned by
720 AcpiProtocol->InstallAcpiTable().
725 InstallQemuFwCfgTables (
726 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
730 FIRMWARE_CONFIG_ITEM FwCfgItem
;
732 QEMU_LOADER_ENTRY
*LoaderStart
;
733 CONST QEMU_LOADER_ENTRY
*LoaderEntry
, *LoaderEnd
;
734 CONST QEMU_LOADER_ENTRY
*WritePointerSubsetEnd
;
735 ORIGINAL_ATTRIBUTES
*OriginalPciAttributes
;
736 UINTN OriginalPciAttributesCount
;
737 S3_CONTEXT
*S3Context
;
738 ORDERED_COLLECTION
*Tracker
;
741 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
743 Status
= QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem
, &FwCfgSize
);
744 if (EFI_ERROR (Status
)) {
747 if (FwCfgSize
% sizeof *LoaderEntry
!= 0) {
748 DEBUG ((EFI_D_ERROR
, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
749 __FUNCTION__
, (UINT64
)FwCfgSize
));
750 return EFI_PROTOCOL_ERROR
;
753 LoaderStart
= AllocatePool (FwCfgSize
);
754 if (LoaderStart
== NULL
) {
755 return EFI_OUT_OF_RESOURCES
;
757 EnablePciDecoding (&OriginalPciAttributes
, &OriginalPciAttributesCount
);
758 QemuFwCfgSelectItem (FwCfgItem
);
759 QemuFwCfgReadBytes (FwCfgSize
, LoaderStart
);
760 RestorePciDecoding (OriginalPciAttributes
, OriginalPciAttributesCount
);
761 LoaderEnd
= LoaderStart
+ FwCfgSize
/ sizeof *LoaderEntry
;
764 if (QemuFwCfgS3Enabled ()) {
766 // Size the allocation pessimistically, assuming that all commands in the
767 // script are QEMU_LOADER_WRITE_POINTER commands.
769 Status
= AllocateS3Context (&S3Context
, LoaderEnd
- LoaderStart
);
770 if (EFI_ERROR (Status
)) {
775 Tracker
= OrderedCollectionInit (BlobCompare
, BlobKeyCompare
);
776 if (Tracker
== NULL
) {
777 Status
= EFI_OUT_OF_RESOURCES
;
782 // first pass: process the commands
784 // "WritePointerSubsetEnd" points one past the last successful
785 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
786 // pass, no such command has been encountered yet.
788 WritePointerSubsetEnd
= LoaderStart
;
789 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
790 switch (LoaderEntry
->Type
) {
791 case QemuLoaderCmdAllocate
:
792 Status
= ProcessCmdAllocate (&LoaderEntry
->Command
.Allocate
, Tracker
);
795 case QemuLoaderCmdAddPointer
:
796 Status
= ProcessCmdAddPointer (&LoaderEntry
->Command
.AddPointer
,
800 case QemuLoaderCmdAddChecksum
:
801 Status
= ProcessCmdAddChecksum (&LoaderEntry
->Command
.AddChecksum
,
805 case QemuLoaderCmdWritePointer
:
806 Status
= ProcessCmdWritePointer (&LoaderEntry
->Command
.WritePointer
,
808 if (!EFI_ERROR (Status
)) {
809 WritePointerSubsetEnd
= LoaderEntry
+ 1;
814 DEBUG ((EFI_D_VERBOSE
, "%a: unknown loader command: 0x%x\n",
815 __FUNCTION__
, LoaderEntry
->Type
));
819 if (EFI_ERROR (Status
)) {
820 goto RollbackWritePointersAndFreeTracker
;
824 InstalledKey
= AllocatePool (INSTALLED_TABLES_MAX
* sizeof *InstalledKey
);
825 if (InstalledKey
== NULL
) {
826 Status
= EFI_OUT_OF_RESOURCES
;
827 goto RollbackWritePointersAndFreeTracker
;
831 // second pass: identify and install ACPI tables
834 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
835 if (LoaderEntry
->Type
== QemuLoaderCmdAddPointer
) {
836 Status
= Process2ndPassCmdAddPointer (&LoaderEntry
->Command
.AddPointer
,
837 Tracker
, AcpiProtocol
, InstalledKey
, &Installed
);
838 if (EFI_ERROR (Status
)) {
839 goto UninstallAcpiTables
;
845 // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3
846 // Boot Script opcodes has to be the last operation in this function, because
847 // if it succeeds, it cannot be undone.
849 if (S3Context
!= NULL
) {
850 Status
= TransferS3ContextToBootScript (S3Context
);
851 if (EFI_ERROR (Status
)) {
852 goto UninstallAcpiTables
;
855 // Ownership of S3Context has been transfered.
861 if (EFI_ERROR (Status
)) {
863 // roll back partial installation
865 while (Installed
> 0) {
867 AcpiProtocol
->UninstallAcpiTable (AcpiProtocol
, InstalledKey
[Installed
]);
870 DEBUG ((EFI_D_INFO
, "%a: installed %d tables\n", __FUNCTION__
, Installed
));
873 FreePool (InstalledKey
);
875 RollbackWritePointersAndFreeTracker
:
877 // In case of failure, revoke any allocation addresses that were communicated
878 // to QEMU previously, before we release all the blobs.
880 if (EFI_ERROR (Status
)) {
881 LoaderEntry
= WritePointerSubsetEnd
;
882 while (LoaderEntry
> LoaderStart
) {
884 if (LoaderEntry
->Type
== QemuLoaderCmdWritePointer
) {
885 UndoCmdWritePointer (&LoaderEntry
->Command
.WritePointer
);
891 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
892 // place only if we're exiting with success and the blob hosts data that is
893 // not directly part of some ACPI table.
895 for (TrackerEntry
= OrderedCollectionMin (Tracker
); TrackerEntry
!= NULL
;
896 TrackerEntry
= TrackerEntry2
) {
900 TrackerEntry2
= OrderedCollectionNext (TrackerEntry
);
901 OrderedCollectionDelete (Tracker
, TrackerEntry
, &UserStruct
);
904 if (EFI_ERROR (Status
) || Blob
->HostsOnlyTableData
) {
905 DEBUG ((EFI_D_VERBOSE
, "%a: freeing \"%a\"\n", __FUNCTION__
,
907 gBS
->FreePages ((UINTN
)Blob
->Base
, EFI_SIZE_TO_PAGES (Blob
->Size
));
911 OrderedCollectionUninit (Tracker
);
914 if (S3Context
!= NULL
) {
915 ReleaseS3Context (S3Context
);
919 FreePool (LoaderStart
);