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 Process a QEMU_LOADER_ALLOCATE command.
138 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to process.
140 @param[in,out] Tracker The ORDERED_COLLECTION tracking the BLOB user
141 structures created thus far.
143 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been
144 allocated for the blob contents, and the
145 contents have been saved. A BLOB object (user
146 structure) has been allocated from pool memory,
147 referencing the blob contents. The BLOB user
148 structure has been linked into Tracker.
150 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
151 Allocate, or the Allocate command references a
152 file that is already known by Tracker.
154 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in
157 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.
159 @return Error codes from QemuFwCfgFindFile() and
160 gBS->AllocatePages().
166 IN CONST QEMU_LOADER_ALLOCATE
*Allocate
,
167 IN OUT ORDERED_COLLECTION
*Tracker
170 FIRMWARE_CONFIG_ITEM FwCfgItem
;
174 EFI_PHYSICAL_ADDRESS Address
;
177 if (Allocate
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
178 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
179 return EFI_PROTOCOL_ERROR
;
182 if (Allocate
->Alignment
> EFI_PAGE_SIZE
) {
183 DEBUG ((EFI_D_ERROR
, "%a: unsupported alignment 0x%x\n", __FUNCTION__
,
184 Allocate
->Alignment
));
185 return EFI_UNSUPPORTED
;
188 Status
= QemuFwCfgFindFile ((CHAR8
*)Allocate
->File
, &FwCfgItem
, &FwCfgSize
);
189 if (EFI_ERROR (Status
)) {
190 DEBUG ((EFI_D_ERROR
, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__
,
191 Allocate
->File
, Status
));
195 NumPages
= EFI_SIZE_TO_PAGES (FwCfgSize
);
196 Address
= 0xFFFFFFFF;
197 Status
= gBS
->AllocatePages (AllocateMaxAddress
, EfiACPIMemoryNVS
, NumPages
,
199 if (EFI_ERROR (Status
)) {
203 Blob
= AllocatePool (sizeof *Blob
);
205 Status
= EFI_OUT_OF_RESOURCES
;
208 CopyMem (Blob
->File
, Allocate
->File
, QEMU_LOADER_FNAME_SIZE
);
209 Blob
->Size
= FwCfgSize
;
210 Blob
->Base
= (VOID
*)(UINTN
)Address
;
211 Blob
->HostsOnlyTableData
= TRUE
;
213 Status
= OrderedCollectionInsert (Tracker
, NULL
, Blob
);
214 if (Status
== RETURN_ALREADY_STARTED
) {
215 DEBUG ((EFI_D_ERROR
, "%a: duplicated file \"%a\"\n", __FUNCTION__
,
217 Status
= EFI_PROTOCOL_ERROR
;
219 if (EFI_ERROR (Status
)) {
223 QemuFwCfgSelectItem (FwCfgItem
);
224 QemuFwCfgReadBytes (FwCfgSize
, Blob
->Base
);
225 ZeroMem (Blob
->Base
+ Blob
->Size
, EFI_PAGES_TO_SIZE (NumPages
) - Blob
->Size
);
227 DEBUG ((EFI_D_VERBOSE
, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "
228 "Address=0x%Lx\n", __FUNCTION__
, Allocate
->File
, Allocate
->Alignment
,
229 Allocate
->Zone
, (UINT64
)Blob
->Size
, (UINT64
)(UINTN
)Blob
->Base
));
236 gBS
->FreePages (Address
, NumPages
);
243 Process a QEMU_LOADER_ADD_POINTER command.
245 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
247 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
248 structures created thus far.
250 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
251 AddPointer, or the AddPointer command references
252 a file unknown to Tracker, or the pointer to
253 relocate has invalid location, size, or value, or
254 the relocated pointer value is not representable
255 in the given pointer size.
257 @retval EFI_SUCCESS The pointer field inside the pointer blob has
263 ProcessCmdAddPointer (
264 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
265 IN CONST ORDERED_COLLECTION
*Tracker
268 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
273 if (AddPointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0' ||
274 AddPointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
275 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
276 return EFI_PROTOCOL_ERROR
;
279 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
280 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
281 if (TrackerEntry
== NULL
|| TrackerEntry2
== NULL
) {
282 DEBUG ((EFI_D_ERROR
, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",
283 __FUNCTION__
, AddPointer
->PointerFile
, AddPointer
->PointeeFile
));
284 return EFI_PROTOCOL_ERROR
;
287 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
288 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
289 if ((AddPointer
->PointerSize
!= 1 && AddPointer
->PointerSize
!= 2 &&
290 AddPointer
->PointerSize
!= 4 && AddPointer
->PointerSize
!= 8) ||
291 Blob
->Size
< AddPointer
->PointerSize
||
292 Blob
->Size
- AddPointer
->PointerSize
< AddPointer
->PointerOffset
) {
293 DEBUG ((EFI_D_ERROR
, "%a: invalid pointer location or size in \"%a\"\n",
294 __FUNCTION__
, AddPointer
->PointerFile
));
295 return EFI_PROTOCOL_ERROR
;
298 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
300 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
301 if (PointerValue
>= Blob2
->Size
) {
302 DEBUG ((EFI_D_ERROR
, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__
,
303 AddPointer
->PointerFile
));
304 return EFI_PROTOCOL_ERROR
;
308 // The memory allocation system ensures that the address of the byte past the
309 // last byte of any allocated object is expressible (no wraparound).
311 ASSERT ((UINTN
)Blob2
->Base
<= MAX_ADDRESS
- Blob2
->Size
);
313 PointerValue
+= (UINT64
)(UINTN
)Blob2
->Base
;
314 if (AddPointer
->PointerSize
< 8 &&
315 RShiftU64 (PointerValue
, AddPointer
->PointerSize
* 8) != 0) {
316 DEBUG ((EFI_D_ERROR
, "%a: relocated pointer value unrepresentable in "
317 "\"%a\"\n", __FUNCTION__
, AddPointer
->PointerFile
));
318 return EFI_PROTOCOL_ERROR
;
321 CopyMem (PointerField
, &PointerValue
, AddPointer
->PointerSize
);
323 DEBUG ((EFI_D_VERBOSE
, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
324 "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
325 AddPointer
->PointerFile
, AddPointer
->PointeeFile
,
326 AddPointer
->PointerOffset
, AddPointer
->PointerSize
));
332 Process a QEMU_LOADER_ADD_CHECKSUM command.
334 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.
336 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
337 structures created thus far.
339 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
340 AddChecksum, or the AddChecksum command
341 references a file unknown to Tracker, or the
342 range to checksum is invalid.
344 @retval EFI_SUCCESS The requested range has been checksummed.
349 ProcessCmdAddChecksum (
350 IN CONST QEMU_LOADER_ADD_CHECKSUM
*AddChecksum
,
351 IN CONST ORDERED_COLLECTION
*Tracker
354 ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
357 if (AddChecksum
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
358 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
359 return EFI_PROTOCOL_ERROR
;
362 TrackerEntry
= OrderedCollectionFind (Tracker
, AddChecksum
->File
);
363 if (TrackerEntry
== NULL
) {
364 DEBUG ((EFI_D_ERROR
, "%a: invalid blob reference \"%a\"\n", __FUNCTION__
,
366 return EFI_PROTOCOL_ERROR
;
369 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
370 if (Blob
->Size
<= AddChecksum
->ResultOffset
||
371 Blob
->Size
< AddChecksum
->Length
||
372 Blob
->Size
- AddChecksum
->Length
< AddChecksum
->Start
) {
373 DEBUG ((EFI_D_ERROR
, "%a: invalid checksum range in \"%a\"\n",
374 __FUNCTION__
, AddChecksum
->File
));
375 return EFI_PROTOCOL_ERROR
;
378 Blob
->Base
[AddChecksum
->ResultOffset
] = CalculateCheckSum8 (
379 Blob
->Base
+ AddChecksum
->Start
,
382 DEBUG ((EFI_D_VERBOSE
, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "
383 "Length=0x%x\n", __FUNCTION__
, AddChecksum
->File
,
384 AddChecksum
->ResultOffset
, AddChecksum
->Start
, AddChecksum
->Length
));
390 Process a QEMU_LOADER_WRITE_POINTER command.
392 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.
394 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
395 structures created thus far.
397 @param[in,out] S3Context The S3_CONTEXT object capturing the fw_cfg actions
398 of successfully processed QEMU_LOADER_WRITE_POINTER
399 commands, to be replayed at S3 resume. S3Context
400 may be NULL if S3 is disabled.
402 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
403 WritePointer. Or, the WritePointer command
404 references a file unknown to Tracker or the
405 fw_cfg directory. Or, the pointer object to
406 rewrite has invalid location, size, or initial
407 relative value. Or, the pointer value to store
408 does not fit in the given pointer size.
410 @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg
411 file has been written. If S3Context is not NULL,
412 then WritePointer has been condensed into
415 @return Error codes propagated from
416 SaveCondensedWritePointerToS3Context(). The
417 pointer object inside the writeable fw_cfg file
418 has not been written.
422 ProcessCmdWritePointer (
423 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
,
424 IN CONST ORDERED_COLLECTION
*Tracker
,
425 IN OUT S3_CONTEXT
*S3Context OPTIONAL
428 RETURN_STATUS Status
;
429 FIRMWARE_CONFIG_ITEM PointerItem
;
430 UINTN PointerItemSize
;
431 ORDERED_COLLECTION_ENTRY
*PointeeEntry
;
435 if (WritePointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0' ||
436 WritePointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
437 DEBUG ((DEBUG_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
438 return EFI_PROTOCOL_ERROR
;
441 Status
= QemuFwCfgFindFile ((CONST CHAR8
*)WritePointer
->PointerFile
,
442 &PointerItem
, &PointerItemSize
);
443 PointeeEntry
= OrderedCollectionFind (Tracker
, WritePointer
->PointeeFile
);
444 if (RETURN_ERROR (Status
) || PointeeEntry
== NULL
) {
446 "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
447 __FUNCTION__
, WritePointer
->PointerFile
, WritePointer
->PointeeFile
));
448 return EFI_PROTOCOL_ERROR
;
451 if ((WritePointer
->PointerSize
!= 1 && WritePointer
->PointerSize
!= 2 &&
452 WritePointer
->PointerSize
!= 4 && WritePointer
->PointerSize
!= 8) ||
453 (PointerItemSize
< WritePointer
->PointerSize
) ||
454 (PointerItemSize
- WritePointer
->PointerSize
<
455 WritePointer
->PointerOffset
)) {
456 DEBUG ((DEBUG_ERROR
, "%a: invalid pointer location or size in \"%a\"\n",
457 __FUNCTION__
, WritePointer
->PointerFile
));
458 return EFI_PROTOCOL_ERROR
;
461 PointeeBlob
= OrderedCollectionUserStruct (PointeeEntry
);
462 PointerValue
= WritePointer
->PointeeOffset
;
463 if (PointerValue
>= PointeeBlob
->Size
) {
464 DEBUG ((DEBUG_ERROR
, "%a: invalid PointeeOffset\n", __FUNCTION__
));
465 return EFI_PROTOCOL_ERROR
;
469 // The memory allocation system ensures that the address of the byte past the
470 // last byte of any allocated object is expressible (no wraparound).
472 ASSERT ((UINTN
)PointeeBlob
->Base
<= MAX_ADDRESS
- PointeeBlob
->Size
);
474 PointerValue
+= (UINT64
)(UINTN
)PointeeBlob
->Base
;
475 if (WritePointer
->PointerSize
< 8 &&
476 RShiftU64 (PointerValue
, WritePointer
->PointerSize
* 8) != 0) {
477 DEBUG ((DEBUG_ERROR
, "%a: pointer value unrepresentable in \"%a\"\n",
478 __FUNCTION__
, WritePointer
->PointerFile
));
479 return EFI_PROTOCOL_ERROR
;
483 // If S3 is enabled, we have to capture the below fw_cfg actions in condensed
484 // form, to be replayed during S3 resume.
486 if (S3Context
!= NULL
) {
487 EFI_STATUS SaveStatus
;
489 SaveStatus
= SaveCondensedWritePointerToS3Context (
492 WritePointer
->PointerSize
,
493 WritePointer
->PointerOffset
,
496 if (EFI_ERROR (SaveStatus
)) {
501 QemuFwCfgSelectItem (PointerItem
);
502 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
503 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
506 // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob
507 // as unreleasable, for the case when the whole linker/loader script is
508 // handled successfully.
510 PointeeBlob
->HostsOnlyTableData
= FALSE
;
512 DEBUG ((DEBUG_VERBOSE
, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
513 "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
514 WritePointer
->PointerFile
, WritePointer
->PointeeFile
,
515 WritePointer
->PointerOffset
, WritePointer
->PointeeOffset
,
516 WritePointer
->PointerSize
));
522 Undo a QEMU_LOADER_WRITE_POINTER command.
524 This function revokes (zeroes out) a guest memory reference communicated to
525 QEMU earlier. The caller is responsible for invoking this function only on
526 such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed
527 by ProcessCmdWritePointer().
529 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
533 UndoCmdWritePointer (
534 IN CONST QEMU_LOADER_WRITE_POINTER
*WritePointer
537 RETURN_STATUS Status
;
538 FIRMWARE_CONFIG_ITEM PointerItem
;
539 UINTN PointerItemSize
;
542 Status
= QemuFwCfgFindFile ((CONST CHAR8
*)WritePointer
->PointerFile
,
543 &PointerItem
, &PointerItemSize
);
544 ASSERT_RETURN_ERROR (Status
);
547 QemuFwCfgSelectItem (PointerItem
);
548 QemuFwCfgSkipBytes (WritePointer
->PointerOffset
);
549 QemuFwCfgWriteBytes (WritePointer
->PointerSize
, &PointerValue
);
551 DEBUG ((DEBUG_VERBOSE
,
552 "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
553 WritePointer
->PointerFile
, WritePointer
->PointerOffset
,
554 WritePointer
->PointerSize
));
559 // We'll be saving the keys of installed tables so that we can roll them back
560 // in case of failure. 128 tables should be enough for anyone (TM).
562 #define INSTALLED_TABLES_MAX 128
565 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte
566 array is an ACPI table, and if so, install it.
568 This function assumes that the entire QEMU linker/loader command file has
569 been processed successfully in a prior first pass.
571 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
573 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
576 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
578 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
579 elements, allocated by the caller. On output,
580 the function will have stored (appended) the
581 AcpiProtocol-internal key of the ACPI table that
582 the function has installed, if the AddPointer
583 command identified an ACPI table that is
584 different from RSDT and XSDT.
586 @param[in,out] NumInstalled On input, the number of entries already used in
587 InstalledKey; it must be in [0,
588 INSTALLED_TABLES_MAX] inclusive. On output, the
589 parameter is incremented if the AddPointer
590 command identified an ACPI table that is
591 different from RSDT and XSDT.
593 @param[in,out] SeenPointers The ORDERED_COLLECTION tracking the absolute
594 target addresses that have been pointed-to by
595 QEMU_LOADER_ADD_POINTER commands thus far. If a
596 target address is encountered for the first
597 time, and it identifies an ACPI table that is
598 different from RDST and XSDT, the table is
599 installed. If a target address is seen for the
600 second or later times, it is skipped without
603 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
606 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI
607 table different from RSDT and XSDT, but there
608 was no more room in InstalledKey.
610 @retval EFI_SUCCESS AddPointer has been processed. Either its
611 absolute target address has been encountered
612 before, or an ACPI table different from RSDT
613 and XSDT has been installed (reflected by
614 InstalledKey and NumInstalled), or RSDT or
615 XSDT has been identified but not installed, or
616 the fw_cfg blob pointed-into by AddPointer has
617 been marked as hosting something else than
618 just direct ACPI table contents.
620 @return Error codes returned by
621 AcpiProtocol->InstallAcpiTable().
626 Process2ndPassCmdAddPointer (
627 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
628 IN CONST ORDERED_COLLECTION
*Tracker
,
629 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
,
630 IN OUT UINTN InstalledKey
[INSTALLED_TABLES_MAX
],
631 IN OUT INT32
*NumInstalled
,
632 IN OUT ORDERED_COLLECTION
*SeenPointers
635 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
636 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry2
;
637 ORDERED_COLLECTION_ENTRY
*SeenPointerEntry
;
640 CONST UINT8
*PointerField
;
642 UINTN Blob2Remaining
;
644 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
645 CONST EFI_ACPI_DESCRIPTION_HEADER
*Header
;
648 if (*NumInstalled
< 0 || *NumInstalled
> INSTALLED_TABLES_MAX
) {
649 return EFI_INVALID_PARAMETER
;
652 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
653 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
654 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
655 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
656 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
658 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
661 // We assert that PointerValue falls inside Blob2's contents. This is ensured
662 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().
664 Blob2Remaining
= (UINTN
)Blob2
->Base
;
665 ASSERT(PointerValue
>= Blob2Remaining
);
666 Blob2Remaining
+= Blob2
->Size
;
667 ASSERT (PointerValue
< Blob2Remaining
);
669 Status
= OrderedCollectionInsert (
671 &SeenPointerEntry
, // for reverting insertion in error case
672 (VOID
*)(UINTN
)PointerValue
674 if (EFI_ERROR (Status
)) {
675 if (Status
== RETURN_ALREADY_STARTED
) {
677 // Already seen this pointer, don't try to process it again.
681 "%a: PointerValue=0x%Lx already processed, skipping.\n",
685 Status
= EFI_SUCCESS
;
690 Blob2Remaining
-= (UINTN
) PointerValue
;
691 DEBUG ((EFI_D_VERBOSE
, "%a: checking for ACPI header in \"%a\" at 0x%Lx "
692 "(remaining: 0x%Lx): ", __FUNCTION__
, AddPointer
->PointeeFile
,
693 PointerValue
, (UINT64
)Blob2Remaining
));
698 // To make our job simple, the FACS has a custom header. Sigh.
700 if (sizeof *Facs
<= Blob2Remaining
) {
701 Facs
= (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)PointerValue
;
703 if (Facs
->Length
>= sizeof *Facs
&&
704 Facs
->Length
<= Blob2Remaining
&&
706 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) {
707 DEBUG ((EFI_D_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
708 (CONST CHAR8
*)&Facs
->Signature
, Facs
->Length
));
709 TableSize
= Facs
->Length
;
714 // check for the uniform tables
716 if (TableSize
== 0 && sizeof *Header
<= Blob2Remaining
) {
717 Header
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
)PointerValue
;
719 if (Header
->Length
>= sizeof *Header
&&
720 Header
->Length
<= Blob2Remaining
&&
721 CalculateSum8 ((CONST UINT8
*)Header
, Header
->Length
) == 0) {
723 // This looks very much like an ACPI table from QEMU:
724 // - Length field consistent with both ACPI and containing blob size
725 // - checksum is correct
727 DEBUG ((EFI_D_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
728 (CONST CHAR8
*)&Header
->Signature
, Header
->Length
));
729 TableSize
= Header
->Length
;
732 // Skip RSDT and XSDT because those are handled by
733 // EFI_ACPI_TABLE_PROTOCOL automatically.
734 if (Header
->Signature
==
735 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
||
737 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
) {
743 if (TableSize
== 0) {
744 DEBUG ((EFI_D_VERBOSE
, "not found; marking fw_cfg blob as opaque\n"));
745 Blob2
->HostsOnlyTableData
= FALSE
;
749 if (*NumInstalled
== INSTALLED_TABLES_MAX
) {
750 DEBUG ((EFI_D_ERROR
, "%a: can't install more than %d tables\n",
751 __FUNCTION__
, INSTALLED_TABLES_MAX
));
752 Status
= EFI_OUT_OF_RESOURCES
;
753 goto RollbackSeenPointer
;
756 Status
= AcpiProtocol
->InstallAcpiTable (AcpiProtocol
,
757 (VOID
*)(UINTN
)PointerValue
, TableSize
,
758 &InstalledKey
[*NumInstalled
]);
759 if (EFI_ERROR (Status
)) {
760 DEBUG ((EFI_D_ERROR
, "%a: InstallAcpiTable(): %r\n", __FUNCTION__
,
762 goto RollbackSeenPointer
;
768 OrderedCollectionDelete (SeenPointers
, SeenPointerEntry
, NULL
);
774 Download, process, and install ACPI table data from the QEMU loader
777 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
779 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU
780 loader command with unsupported parameters
783 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg
786 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than
787 INSTALLED_TABLES_MAX tables found.
789 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.
791 @return Status codes returned by
792 AcpiProtocol->InstallAcpiTable().
797 InstallQemuFwCfgTables (
798 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
802 FIRMWARE_CONFIG_ITEM FwCfgItem
;
804 QEMU_LOADER_ENTRY
*LoaderStart
;
805 CONST QEMU_LOADER_ENTRY
*LoaderEntry
, *LoaderEnd
;
806 CONST QEMU_LOADER_ENTRY
*WritePointerSubsetEnd
;
807 ORIGINAL_ATTRIBUTES
*OriginalPciAttributes
;
808 UINTN OriginalPciAttributesCount
;
809 S3_CONTEXT
*S3Context
;
810 ORDERED_COLLECTION
*Tracker
;
813 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
814 ORDERED_COLLECTION
*SeenPointers
;
815 ORDERED_COLLECTION_ENTRY
*SeenPointerEntry
, *SeenPointerEntry2
;
817 Status
= QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem
, &FwCfgSize
);
818 if (EFI_ERROR (Status
)) {
821 if (FwCfgSize
% sizeof *LoaderEntry
!= 0) {
822 DEBUG ((EFI_D_ERROR
, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
823 __FUNCTION__
, (UINT64
)FwCfgSize
));
824 return EFI_PROTOCOL_ERROR
;
827 LoaderStart
= AllocatePool (FwCfgSize
);
828 if (LoaderStart
== NULL
) {
829 return EFI_OUT_OF_RESOURCES
;
831 EnablePciDecoding (&OriginalPciAttributes
, &OriginalPciAttributesCount
);
832 QemuFwCfgSelectItem (FwCfgItem
);
833 QemuFwCfgReadBytes (FwCfgSize
, LoaderStart
);
834 RestorePciDecoding (OriginalPciAttributes
, OriginalPciAttributesCount
);
835 LoaderEnd
= LoaderStart
+ FwCfgSize
/ sizeof *LoaderEntry
;
838 if (QemuFwCfgS3Enabled ()) {
840 // Size the allocation pessimistically, assuming that all commands in the
841 // script are QEMU_LOADER_WRITE_POINTER commands.
843 Status
= AllocateS3Context (&S3Context
, LoaderEnd
- LoaderStart
);
844 if (EFI_ERROR (Status
)) {
849 Tracker
= OrderedCollectionInit (BlobCompare
, BlobKeyCompare
);
850 if (Tracker
== NULL
) {
851 Status
= EFI_OUT_OF_RESOURCES
;
856 // first pass: process the commands
858 // "WritePointerSubsetEnd" points one past the last successful
859 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
860 // pass, no such command has been encountered yet.
862 WritePointerSubsetEnd
= LoaderStart
;
863 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
864 switch (LoaderEntry
->Type
) {
865 case QemuLoaderCmdAllocate
:
866 Status
= ProcessCmdAllocate (&LoaderEntry
->Command
.Allocate
, Tracker
);
869 case QemuLoaderCmdAddPointer
:
870 Status
= ProcessCmdAddPointer (&LoaderEntry
->Command
.AddPointer
,
874 case QemuLoaderCmdAddChecksum
:
875 Status
= ProcessCmdAddChecksum (&LoaderEntry
->Command
.AddChecksum
,
879 case QemuLoaderCmdWritePointer
:
880 Status
= ProcessCmdWritePointer (&LoaderEntry
->Command
.WritePointer
,
882 if (!EFI_ERROR (Status
)) {
883 WritePointerSubsetEnd
= LoaderEntry
+ 1;
888 DEBUG ((EFI_D_VERBOSE
, "%a: unknown loader command: 0x%x\n",
889 __FUNCTION__
, LoaderEntry
->Type
));
893 if (EFI_ERROR (Status
)) {
894 goto RollbackWritePointersAndFreeTracker
;
898 InstalledKey
= AllocatePool (INSTALLED_TABLES_MAX
* sizeof *InstalledKey
);
899 if (InstalledKey
== NULL
) {
900 Status
= EFI_OUT_OF_RESOURCES
;
901 goto RollbackWritePointersAndFreeTracker
;
904 SeenPointers
= OrderedCollectionInit (PointerCompare
, PointerCompare
);
905 if (SeenPointers
== NULL
) {
906 Status
= EFI_OUT_OF_RESOURCES
;
911 // second pass: identify and install ACPI tables
914 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
915 if (LoaderEntry
->Type
== QemuLoaderCmdAddPointer
) {
916 Status
= Process2ndPassCmdAddPointer (
917 &LoaderEntry
->Command
.AddPointer
,
924 if (EFI_ERROR (Status
)) {
925 goto UninstallAcpiTables
;
931 // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3
932 // Boot Script opcodes has to be the last operation in this function, because
933 // if it succeeds, it cannot be undone.
935 if (S3Context
!= NULL
) {
936 Status
= TransferS3ContextToBootScript (S3Context
);
937 if (EFI_ERROR (Status
)) {
938 goto UninstallAcpiTables
;
941 // Ownership of S3Context has been transfered.
947 if (EFI_ERROR (Status
)) {
949 // roll back partial installation
951 while (Installed
> 0) {
953 AcpiProtocol
->UninstallAcpiTable (AcpiProtocol
, InstalledKey
[Installed
]);
956 DEBUG ((EFI_D_INFO
, "%a: installed %d tables\n", __FUNCTION__
, Installed
));
959 for (SeenPointerEntry
= OrderedCollectionMin (SeenPointers
);
960 SeenPointerEntry
!= NULL
;
961 SeenPointerEntry
= SeenPointerEntry2
) {
962 SeenPointerEntry2
= OrderedCollectionNext (SeenPointerEntry
);
963 OrderedCollectionDelete (SeenPointers
, SeenPointerEntry
, NULL
);
965 OrderedCollectionUninit (SeenPointers
);
968 FreePool (InstalledKey
);
970 RollbackWritePointersAndFreeTracker
:
972 // In case of failure, revoke any allocation addresses that were communicated
973 // to QEMU previously, before we release all the blobs.
975 if (EFI_ERROR (Status
)) {
976 LoaderEntry
= WritePointerSubsetEnd
;
977 while (LoaderEntry
> LoaderStart
) {
979 if (LoaderEntry
->Type
== QemuLoaderCmdWritePointer
) {
980 UndoCmdWritePointer (&LoaderEntry
->Command
.WritePointer
);
986 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
987 // place only if we're exiting with success and the blob hosts data that is
988 // not directly part of some ACPI table.
990 for (TrackerEntry
= OrderedCollectionMin (Tracker
); TrackerEntry
!= NULL
;
991 TrackerEntry
= TrackerEntry2
) {
995 TrackerEntry2
= OrderedCollectionNext (TrackerEntry
);
996 OrderedCollectionDelete (Tracker
, TrackerEntry
, &UserStruct
);
999 if (EFI_ERROR (Status
) || Blob
->HostsOnlyTableData
) {
1000 DEBUG ((EFI_D_VERBOSE
, "%a: freeing \"%a\"\n", __FUNCTION__
,
1002 gBS
->FreePages ((UINTN
)Blob
->Base
, EFI_SIZE_TO_PAGES (Blob
->Size
));
1006 OrderedCollectionUninit (Tracker
);
1009 if (S3Context
!= NULL
) {
1010 ReleaseS3Context (S3Context
);
1014 FreePool (LoaderStart
);