TianoCore EDK2 master
Loading...
Searching...
No Matches
QemuFwCfgAcpi.c
Go to the documentation of this file.
1
11#include <IndustryStandard/Acpi.h> // EFI_ACPI_DESCRIPTION_HEADER
12#include <IndustryStandard/QemuLoader.h> // QEMU_LOADER_FNAME_SIZE
15#include <Library/BaseLib.h> // AsciiStrCmp()
16#include <Library/BaseMemoryLib.h> // CopyMem()
17#include <Library/DebugLib.h> // DEBUG()
18#include <Library/MemoryAllocationLib.h> // AllocatePool()
19#include <Library/OrderedCollectionLib.h> // OrderedCollectionMin()
20#include <Library/QemuFwCfgLib.h> // QemuFwCfgFindFile()
21#include <Library/QemuFwCfgS3Lib.h> // QemuFwCfgS3Enabled()
24
25//
26// The user structure for the ordered collection that will track the fw_cfg
27// blobs under processing.
28//
29typedef struct {
30 UINT8 File[QEMU_LOADER_FNAME_SIZE]; // NUL-terminated name of the fw_cfg
31 // blob. This is the ordering / search
32 // key.
33 UINTN Size; // The number of bytes in this blob.
34 UINT8 *Base; // Pointer to the blob data.
35 BOOLEAN HostsOnlyTableData; // TRUE iff the blob has been found to
36 // only contain data that is directly
37 // part of ACPI tables.
38} BLOB;
39
55INTN
56EFIAPI
58 IN CONST VOID *StandaloneKey,
59 IN CONST VOID *UserStruct
60 )
61{
62 CONST BLOB *Blob;
63
64 Blob = UserStruct;
65 return AsciiStrCmp (StandaloneKey, (CONST CHAR8 *)Blob->File);
66}
67
82INTN
83EFIAPI
85 IN CONST VOID *UserStruct1,
86 IN CONST VOID *UserStruct2
87 )
88{
89 CONST BLOB *Blob1;
90
91 Blob1 = UserStruct1;
92 return BlobKeyCompare (Blob1->File, UserStruct2);
93}
94
110STATIC
111INTN
112EFIAPI
114 IN CONST VOID *Pointer1,
115 IN CONST VOID *Pointer2
116 )
117{
118 if (Pointer1 == Pointer2) {
119 return 0;
120 }
121
122 if ((UINTN)Pointer1 < (UINTN)Pointer2) {
123 return -1;
124 }
125
126 return 1;
127}
128
142STATIC
143INTN
144EFIAPI
146 IN CONST VOID *AsciiString1,
147 IN CONST VOID *AsciiString2
148 )
149{
150 return AsciiStrCmp (AsciiString1, AsciiString2);
151}
152
163STATIC
164VOID
166 IN ORDERED_COLLECTION *AllocationsRestrictedTo32Bit
167 )
168{
169 ORDERED_COLLECTION_ENTRY *Entry, *Entry2;
170
171 for (Entry = OrderedCollectionMin (AllocationsRestrictedTo32Bit);
172 Entry != NULL;
173 Entry = Entry2)
174 {
175 Entry2 = OrderedCollectionNext (Entry);
176 OrderedCollectionDelete (AllocationsRestrictedTo32Bit, Entry, NULL);
177 }
178
179 OrderedCollectionUninit (AllocationsRestrictedTo32Bit);
180}
181
209STATIC
212 OUT ORDERED_COLLECTION **AllocationsRestrictedTo32Bit,
213 IN CONST QEMU_LOADER_ENTRY *LoaderStart,
214 IN CONST QEMU_LOADER_ENTRY *LoaderEnd
215 )
216{
217 ORDERED_COLLECTION *Collection;
218 CONST QEMU_LOADER_ENTRY *LoaderEntry;
219 EFI_STATUS Status;
220
222 if (Collection == NULL) {
223 return EFI_OUT_OF_RESOURCES;
224 }
225
226 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
227 CONST QEMU_LOADER_ADD_POINTER *AddPointer;
228
229 if (LoaderEntry->Type != QemuLoaderCmdAddPointer) {
230 continue;
231 }
232
233 AddPointer = &LoaderEntry->Command.AddPointer;
234
235 if (AddPointer->PointerSize >= 8) {
236 continue;
237 }
238
239 if (AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
240 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __func__));
241 Status = EFI_PROTOCOL_ERROR;
242 goto RollBack;
243 }
244
245 Status = OrderedCollectionInsert (
246 Collection,
247 NULL, // Entry
248 (VOID *)AddPointer->PointeeFile
249 );
250 switch (Status) {
251 case EFI_SUCCESS:
252 DEBUG ((
253 DEBUG_VERBOSE,
254 "%a: restricting blob \"%a\" from 64-bit allocation\n",
255 __func__,
256 AddPointer->PointeeFile
257 ));
258 break;
259 case EFI_ALREADY_STARTED:
260 //
261 // The restriction has been recorded already.
262 //
263 break;
264 case EFI_OUT_OF_RESOURCES:
265 goto RollBack;
266 default:
267 ASSERT (FALSE);
268 }
269 }
270
271 *AllocationsRestrictedTo32Bit = Collection;
272 return EFI_SUCCESS;
273
274RollBack:
276 return Status;
277}
278
315STATIC
317EFIAPI
319 IN CONST QEMU_LOADER_ALLOCATE *Allocate,
320 IN OUT ORDERED_COLLECTION *Tracker,
321 IN ORDERED_COLLECTION *AllocationsRestrictedTo32Bit
322 )
323{
324 FIRMWARE_CONFIG_ITEM FwCfgItem;
325 UINTN FwCfgSize;
326 EFI_STATUS Status;
327 UINTN NumPages;
328 EFI_PHYSICAL_ADDRESS Address;
329 BLOB *Blob;
330
331 if (Allocate->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
332 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __func__));
333 return EFI_PROTOCOL_ERROR;
334 }
335
336 if (Allocate->Alignment > EFI_PAGE_SIZE) {
337 DEBUG ((
338 DEBUG_ERROR,
339 "%a: unsupported alignment 0x%x\n",
340 __func__,
341 Allocate->Alignment
342 ));
343 return EFI_UNSUPPORTED;
344 }
345
346 Status = QemuFwCfgFindFile ((CHAR8 *)Allocate->File, &FwCfgItem, &FwCfgSize);
347 if (EFI_ERROR (Status)) {
348 DEBUG ((
349 DEBUG_ERROR,
350 "%a: QemuFwCfgFindFile(\"%a\"): %r\n",
351 __func__,
352 Allocate->File,
353 Status
354 ));
355 return Status;
356 }
357
358 NumPages = EFI_SIZE_TO_PAGES (FwCfgSize);
359 Address = MAX_UINT64;
361 AllocationsRestrictedTo32Bit,
362 Allocate->File
363 ) != NULL)
364 {
365 Address = MAX_UINT32;
366 }
367
368 Status = gBS->AllocatePages (
371 NumPages,
372 &Address
373 );
374 if (EFI_ERROR (Status)) {
375 return Status;
376 }
377
378 Blob = AllocatePool (sizeof *Blob);
379 if (Blob == NULL) {
380 Status = EFI_OUT_OF_RESOURCES;
381 goto FreePages;
382 }
383
384 CopyMem (Blob->File, Allocate->File, QEMU_LOADER_FNAME_SIZE);
385 Blob->Size = FwCfgSize;
386 Blob->Base = (VOID *)(UINTN)Address;
387 Blob->HostsOnlyTableData = TRUE;
388
389 Status = OrderedCollectionInsert (Tracker, NULL, Blob);
390 if (Status == RETURN_ALREADY_STARTED) {
391 DEBUG ((
392 DEBUG_ERROR,
393 "%a: duplicated file \"%a\"\n",
394 __func__,
395 Allocate->File
396 ));
397 Status = EFI_PROTOCOL_ERROR;
398 }
399
400 if (EFI_ERROR (Status)) {
401 goto FreeBlob;
402 }
403
404 QemuFwCfgSelectItem (FwCfgItem);
405 QemuFwCfgReadBytes (FwCfgSize, Blob->Base);
406 ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size);
407
408 DEBUG ((
409 DEBUG_VERBOSE,
410 "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "
411 "Address=0x%Lx\n",
412 __func__,
413 Allocate->File,
414 Allocate->Alignment,
415 Allocate->Zone,
416 (UINT64)Blob->Size,
417 (UINT64)(UINTN)Blob->Base
418 ));
419
420 //
421 // Measure the data which is downloaded from QEMU.
422 // It has to be done before it is consumed. Because the data will
423 // be updated in the following operations.
424 //
426 1,
427 EV_PLATFORM_CONFIG_FLAGS,
428 EV_POSTCODE_INFO_ACPI_DATA,
429 ACPI_DATA_LEN,
430 (VOID *)(UINTN)Blob->Base,
431 Blob->Size
432 );
433
434 return EFI_SUCCESS;
435
436FreeBlob:
437 FreePool (Blob);
438
440 gBS->FreePages (Address, NumPages);
441
442 return Status;
443}
444
463STATIC
465EFIAPI
467 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,
469 )
470{
471 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;
472 BLOB *Blob, *Blob2;
473 UINT8 *PointerField;
474 UINT64 PointerValue;
475
476 if ((AddPointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') ||
477 (AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0'))
478 {
479 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __func__));
480 return EFI_PROTOCOL_ERROR;
481 }
482
483 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);
484 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);
485 if ((TrackerEntry == NULL) || (TrackerEntry2 == NULL)) {
486 DEBUG ((
487 DEBUG_ERROR,
488 "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",
489 __func__,
490 AddPointer->PointerFile,
491 AddPointer->PointeeFile
492 ));
493 return EFI_PROTOCOL_ERROR;
494 }
495
496 Blob = OrderedCollectionUserStruct (TrackerEntry);
497 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);
498 if (((AddPointer->PointerSize != 1) && (AddPointer->PointerSize != 2) &&
499 (AddPointer->PointerSize != 4) && (AddPointer->PointerSize != 8)) ||
500 (Blob->Size < AddPointer->PointerSize) ||
501 (Blob->Size - AddPointer->PointerSize < AddPointer->PointerOffset))
502 {
503 DEBUG ((
504 DEBUG_ERROR,
505 "%a: invalid pointer location or size in \"%a\"\n",
506 __func__,
507 AddPointer->PointerFile
508 ));
509 return EFI_PROTOCOL_ERROR;
510 }
511
512 PointerField = Blob->Base + AddPointer->PointerOffset;
513 PointerValue = 0;
514 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);
515 if (PointerValue >= Blob2->Size) {
516 DEBUG ((
517 DEBUG_ERROR,
518 "%a: invalid pointer value in \"%a\"\n",
519 __func__,
520 AddPointer->PointerFile
521 ));
522 return EFI_PROTOCOL_ERROR;
523 }
524
525 //
526 // The memory allocation system ensures that the address of the byte past the
527 // last byte of any allocated object is expressible (no wraparound).
528 //
529 ASSERT ((UINTN)Blob2->Base <= MAX_ADDRESS - Blob2->Size);
530
531 PointerValue += (UINT64)(UINTN)Blob2->Base;
532 if ((AddPointer->PointerSize < 8) &&
533 (RShiftU64 (PointerValue, AddPointer->PointerSize * 8) != 0))
534 {
535 DEBUG ((
536 DEBUG_ERROR,
537 "%a: relocated pointer value unrepresentable in "
538 "\"%a\"\n",
539 __func__,
540 AddPointer->PointerFile
541 ));
542 return EFI_PROTOCOL_ERROR;
543 }
544
545 CopyMem (PointerField, &PointerValue, AddPointer->PointerSize);
546
547 DEBUG ((
548 DEBUG_VERBOSE,
549 "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
550 "PointerOffset=0x%x PointerSize=%d\n",
551 __func__,
552 AddPointer->PointerFile,
553 AddPointer->PointeeFile,
554 AddPointer->PointerOffset,
555 AddPointer->PointerSize
556 ));
557 return EFI_SUCCESS;
558}
559
575STATIC
577EFIAPI
579 IN CONST QEMU_LOADER_ADD_CHECKSUM *AddChecksum,
581 )
582{
583 ORDERED_COLLECTION_ENTRY *TrackerEntry;
584 BLOB *Blob;
585
586 if (AddChecksum->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
587 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __func__));
588 return EFI_PROTOCOL_ERROR;
589 }
590
591 TrackerEntry = OrderedCollectionFind (Tracker, AddChecksum->File);
592 if (TrackerEntry == NULL) {
593 DEBUG ((
594 DEBUG_ERROR,
595 "%a: invalid blob reference \"%a\"\n",
596 __func__,
597 AddChecksum->File
598 ));
599 return EFI_PROTOCOL_ERROR;
600 }
601
602 Blob = OrderedCollectionUserStruct (TrackerEntry);
603 if ((Blob->Size <= AddChecksum->ResultOffset) ||
604 (Blob->Size < AddChecksum->Length) ||
605 (Blob->Size - AddChecksum->Length < AddChecksum->Start))
606 {
607 DEBUG ((
608 DEBUG_ERROR,
609 "%a: invalid checksum range in \"%a\"\n",
610 __func__,
611 AddChecksum->File
612 ));
613 return EFI_PROTOCOL_ERROR;
614 }
615
616 Blob->Base[AddChecksum->ResultOffset] = CalculateCheckSum8 (
617 Blob->Base + AddChecksum->Start,
618 AddChecksum->Length
619 );
620 DEBUG ((
621 DEBUG_VERBOSE,
622 "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "
623 "Length=0x%x\n",
624 __func__,
625 AddChecksum->File,
626 AddChecksum->ResultOffset,
627 AddChecksum->Start,
628 AddChecksum->Length
629 ));
630 return EFI_SUCCESS;
631}
632
664STATIC
667 IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer,
668 IN CONST ORDERED_COLLECTION *Tracker,
669 IN OUT S3_CONTEXT *S3Context OPTIONAL
670 )
671{
672 RETURN_STATUS Status;
673 FIRMWARE_CONFIG_ITEM PointerItem;
674 UINTN PointerItemSize;
675 ORDERED_COLLECTION_ENTRY *PointeeEntry;
676 BLOB *PointeeBlob;
677 UINT64 PointerValue;
678
679 if ((WritePointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') ||
680 (WritePointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0'))
681 {
682 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __func__));
683 return EFI_PROTOCOL_ERROR;
684 }
685
686 Status = QemuFwCfgFindFile (
687 (CONST CHAR8 *)WritePointer->PointerFile,
688 &PointerItem,
689 &PointerItemSize
690 );
691 PointeeEntry = OrderedCollectionFind (Tracker, WritePointer->PointeeFile);
692 if (RETURN_ERROR (Status) || (PointeeEntry == NULL)) {
693 DEBUG ((
694 DEBUG_ERROR,
695 "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
696 __func__,
697 WritePointer->PointerFile,
698 WritePointer->PointeeFile
699 ));
700 return EFI_PROTOCOL_ERROR;
701 }
702
703 if (((WritePointer->PointerSize != 1) && (WritePointer->PointerSize != 2) &&
704 (WritePointer->PointerSize != 4) && (WritePointer->PointerSize != 8)) ||
705 (PointerItemSize < WritePointer->PointerSize) ||
706 (PointerItemSize - WritePointer->PointerSize <
707 WritePointer->PointerOffset))
708 {
709 DEBUG ((
710 DEBUG_ERROR,
711 "%a: invalid pointer location or size in \"%a\"\n",
712 __func__,
713 WritePointer->PointerFile
714 ));
715 return EFI_PROTOCOL_ERROR;
716 }
717
718 PointeeBlob = OrderedCollectionUserStruct (PointeeEntry);
719 PointerValue = WritePointer->PointeeOffset;
720 if (PointerValue >= PointeeBlob->Size) {
721 DEBUG ((DEBUG_ERROR, "%a: invalid PointeeOffset\n", __func__));
722 return EFI_PROTOCOL_ERROR;
723 }
724
725 //
726 // The memory allocation system ensures that the address of the byte past the
727 // last byte of any allocated object is expressible (no wraparound).
728 //
729 ASSERT ((UINTN)PointeeBlob->Base <= MAX_ADDRESS - PointeeBlob->Size);
730
731 PointerValue += (UINT64)(UINTN)PointeeBlob->Base;
732 if ((WritePointer->PointerSize < 8) &&
733 (RShiftU64 (PointerValue, WritePointer->PointerSize * 8) != 0))
734 {
735 DEBUG ((
736 DEBUG_ERROR,
737 "%a: pointer value unrepresentable in \"%a\"\n",
738 __func__,
739 WritePointer->PointerFile
740 ));
741 return EFI_PROTOCOL_ERROR;
742 }
743
744 //
745 // If S3 is enabled, we have to capture the below fw_cfg actions in condensed
746 // form, to be replayed during S3 resume.
747 //
748 if (S3Context != NULL) {
749 EFI_STATUS SaveStatus;
750
752 S3Context,
753 (UINT16)PointerItem,
754 WritePointer->PointerSize,
755 WritePointer->PointerOffset,
756 PointerValue
757 );
758 if (EFI_ERROR (SaveStatus)) {
759 return SaveStatus;
760 }
761 }
762
763 QemuFwCfgSelectItem (PointerItem);
764 QemuFwCfgSkipBytes (WritePointer->PointerOffset);
765 QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);
766
767 //
768 // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob
769 // as unreleasable, for the case when the whole linker/loader script is
770 // handled successfully.
771 //
772 PointeeBlob->HostsOnlyTableData = FALSE;
773
774 DEBUG ((
775 DEBUG_VERBOSE,
776 "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
777 "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n",
778 __func__,
779 WritePointer->PointerFile,
780 WritePointer->PointeeFile,
781 WritePointer->PointerOffset,
782 WritePointer->PointeeOffset,
783 WritePointer->PointerSize
784 ));
785 return EFI_SUCCESS;
786}
787
798STATIC
799VOID
802 )
803{
804 RETURN_STATUS Status;
805 FIRMWARE_CONFIG_ITEM PointerItem;
806 UINTN PointerItemSize;
807 UINT64 PointerValue;
808
809 Status = QemuFwCfgFindFile (
810 (CONST CHAR8 *)WritePointer->PointerFile,
811 &PointerItem,
812 &PointerItemSize
813 );
814 ASSERT_RETURN_ERROR (Status);
815
816 PointerValue = 0;
817 QemuFwCfgSelectItem (PointerItem);
818 QemuFwCfgSkipBytes (WritePointer->PointerOffset);
819 QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);
820
821 DEBUG ((
822 DEBUG_VERBOSE,
823 "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n",
824 __func__,
825 WritePointer->PointerFile,
826 WritePointer->PointerOffset,
827 WritePointer->PointerSize
828 ));
829}
830
831//
832// We'll be saving the keys of installed tables so that we can roll them back
833// in case of failure. 128 tables should be enough for anyone (TM).
834//
835#define INSTALLED_TABLES_MAX 128
836
896STATIC
898EFIAPI
900 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,
901 IN CONST ORDERED_COLLECTION *Tracker,
902 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
903 IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX],
904 IN OUT INT32 *NumInstalled,
905 IN OUT ORDERED_COLLECTION *SeenPointers
906 )
907{
908 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry;
909 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry2;
910 ORDERED_COLLECTION_ENTRY *SeenPointerEntry;
911 CONST BLOB *Blob;
912 BLOB *Blob2;
913 CONST UINT8 *PointerField;
914 UINT64 PointerValue;
915 UINTN Blob2Remaining;
916 UINTN TableSize;
919 EFI_STATUS Status;
920
921 if ((*NumInstalled < 0) || (*NumInstalled > INSTALLED_TABLES_MAX)) {
922 return EFI_INVALID_PARAMETER;
923 }
924
925 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);
926 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);
927 Blob = OrderedCollectionUserStruct (TrackerEntry);
928 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);
929 PointerField = Blob->Base + AddPointer->PointerOffset;
930 PointerValue = 0;
931 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);
932
933 //
934 // We assert that PointerValue falls inside Blob2's contents. This is ensured
935 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().
936 //
937 Blob2Remaining = (UINTN)Blob2->Base;
938 ASSERT (PointerValue >= Blob2Remaining);
939 Blob2Remaining += Blob2->Size;
940 ASSERT (PointerValue < Blob2Remaining);
941
942 Status = OrderedCollectionInsert (
943 SeenPointers,
944 &SeenPointerEntry, // for reverting insertion in error case
945 (VOID *)(UINTN)PointerValue
946 );
947 if (EFI_ERROR (Status)) {
948 if (Status == RETURN_ALREADY_STARTED) {
949 //
950 // Already seen this pointer, don't try to process it again.
951 //
952 DEBUG ((
953 DEBUG_VERBOSE,
954 "%a: PointerValue=0x%Lx already processed, skipping.\n",
955 __func__,
956 PointerValue
957 ));
958 Status = EFI_SUCCESS;
959 }
960
961 return Status;
962 }
963
964 Blob2Remaining -= (UINTN)PointerValue;
965 DEBUG ((
966 DEBUG_VERBOSE,
967 "%a: checking for ACPI header in \"%a\" at 0x%Lx "
968 "(remaining: 0x%Lx): ",
969 __func__,
970 AddPointer->PointeeFile,
971 PointerValue,
972 (UINT64)Blob2Remaining
973 ));
974
975 TableSize = 0;
976
977 //
978 // To make our job simple, the FACS has a custom header. Sigh.
979 //
980 if (sizeof *Facs <= Blob2Remaining) {
982
983 if ((Facs->Length >= sizeof *Facs) &&
984 (Facs->Length <= Blob2Remaining) &&
985 (Facs->Signature ==
987 {
988 DEBUG ((
989 DEBUG_VERBOSE,
990 "found \"%-4.4a\" size 0x%x\n",
991 (CONST CHAR8 *)&Facs->Signature,
992 Facs->Length
993 ));
994 TableSize = Facs->Length;
995 }
996 }
997
998 //
999 // check for the uniform tables
1000 //
1001 if ((TableSize == 0) && (sizeof *Header <= Blob2Remaining)) {
1002 Header = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)PointerValue;
1003
1004 if ((Header->Length >= sizeof *Header) &&
1005 (Header->Length <= Blob2Remaining) &&
1006 (CalculateSum8 ((CONST UINT8 *)Header, Header->Length) == 0))
1007 {
1008 //
1009 // This looks very much like an ACPI table from QEMU:
1010 // - Length field consistent with both ACPI and containing blob size
1011 // - checksum is correct
1012 //
1013 DEBUG ((
1014 DEBUG_VERBOSE,
1015 "found \"%-4.4a\" size 0x%x\n",
1016 (CONST CHAR8 *)&Header->Signature,
1017 Header->Length
1018 ));
1019 TableSize = Header->Length;
1020
1021 //
1022 // Skip RSDT and XSDT because those are handled by
1023 // EFI_ACPI_TABLE_PROTOCOL automatically.
1024 if ((Header->Signature ==
1026 (Header->Signature ==
1028 {
1029 return EFI_SUCCESS;
1030 }
1031 }
1032 }
1033
1034 if (TableSize == 0) {
1035 DEBUG ((DEBUG_VERBOSE, "not found; marking fw_cfg blob as opaque\n"));
1036 Blob2->HostsOnlyTableData = FALSE;
1037 return EFI_SUCCESS;
1038 }
1039
1040 if (*NumInstalled == INSTALLED_TABLES_MAX) {
1041 DEBUG ((
1042 DEBUG_ERROR,
1043 "%a: can't install more than %d tables\n",
1044 __func__,
1045 INSTALLED_TABLES_MAX
1046 ));
1047 Status = EFI_OUT_OF_RESOURCES;
1048 goto RollbackSeenPointer;
1049 }
1050
1051 Status = AcpiProtocol->InstallAcpiTable (
1052 AcpiProtocol,
1053 (VOID *)(UINTN)PointerValue,
1054 TableSize,
1055 &InstalledKey[*NumInstalled]
1056 );
1057 if (EFI_ERROR (Status)) {
1058 DEBUG ((
1059 DEBUG_ERROR,
1060 "%a: InstallAcpiTable(): %r\n",
1061 __func__,
1062 Status
1063 ));
1064 goto RollbackSeenPointer;
1065 }
1066
1067 ++*NumInstalled;
1068 return EFI_SUCCESS;
1069
1070RollbackSeenPointer:
1071 OrderedCollectionDelete (SeenPointers, SeenPointerEntry, NULL);
1072 return Status;
1073}
1074
1098EFIAPI
1100 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol
1101 )
1102{
1103 EFI_STATUS Status;
1104 FIRMWARE_CONFIG_ITEM FwCfgItem;
1105 UINTN FwCfgSize;
1106 QEMU_LOADER_ENTRY *LoaderStart;
1107 CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd;
1108 CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd;
1109 ORIGINAL_ATTRIBUTES *OriginalPciAttributes;
1110 UINTN OriginalPciAttributesCount;
1111 ORDERED_COLLECTION *AllocationsRestrictedTo32Bit;
1112 S3_CONTEXT *S3Context;
1113 ORDERED_COLLECTION *Tracker;
1114 UINTN *InstalledKey;
1115 INT32 Installed;
1116 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;
1117 ORDERED_COLLECTION *SeenPointers;
1118 ORDERED_COLLECTION_ENTRY *SeenPointerEntry, *SeenPointerEntry2;
1119 EFI_HANDLE QemuAcpiHandle;
1120
1121 Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize);
1122 if (EFI_ERROR (Status)) {
1123 return Status;
1124 }
1125
1126 if (FwCfgSize % sizeof *LoaderEntry != 0) {
1127 DEBUG ((
1128 DEBUG_ERROR,
1129 "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
1130 __func__,
1131 (UINT64)FwCfgSize
1132 ));
1133 return EFI_PROTOCOL_ERROR;
1134 }
1135
1136 LoaderStart = AllocatePool (FwCfgSize);
1137 if (LoaderStart == NULL) {
1138 return EFI_OUT_OF_RESOURCES;
1139 }
1140
1141 EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount);
1142 QemuFwCfgSelectItem (FwCfgItem);
1143 QemuFwCfgReadBytes (FwCfgSize, LoaderStart);
1144 RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount);
1145
1146 //
1147 // Measure the "etc/table-loader" which is downloaded from QEMU.
1148 // It has to be done before it is consumed. Because it would be
1149 // updated in the following operations.
1150 //
1152 1,
1153 EV_PLATFORM_CONFIG_FLAGS,
1154 EV_POSTCODE_INFO_ACPI_DATA,
1155 ACPI_DATA_LEN,
1156 (VOID *)(UINTN)LoaderStart,
1157 FwCfgSize
1158 );
1159
1160 LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry;
1161
1162 AllocationsRestrictedTo32Bit = NULL;
1164 &AllocationsRestrictedTo32Bit,
1165 LoaderStart,
1166 LoaderEnd
1167 );
1168 if (EFI_ERROR (Status)) {
1169 goto FreeLoader;
1170 }
1171
1172 S3Context = NULL;
1173 if (QemuFwCfgS3Enabled ()) {
1174 //
1175 // Size the allocation pessimistically, assuming that all commands in the
1176 // script are QEMU_LOADER_WRITE_POINTER commands.
1177 //
1178 Status = AllocateS3Context (&S3Context, LoaderEnd - LoaderStart);
1179 if (EFI_ERROR (Status)) {
1180 goto FreeAllocationsRestrictedTo32Bit;
1181 }
1182 }
1183
1185 if (Tracker == NULL) {
1186 Status = EFI_OUT_OF_RESOURCES;
1187 goto FreeS3Context;
1188 }
1189
1190 //
1191 // first pass: process the commands
1192 //
1193 // "WritePointerSubsetEnd" points one past the last successful
1194 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
1195 // pass, no such command has been encountered yet.
1196 //
1197 WritePointerSubsetEnd = LoaderStart;
1198 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
1199 switch (LoaderEntry->Type) {
1200 case QemuLoaderCmdAllocate:
1201 Status = ProcessCmdAllocate (
1202 &LoaderEntry->Command.Allocate,
1203 Tracker,
1204 AllocationsRestrictedTo32Bit
1205 );
1206 break;
1207
1208 case QemuLoaderCmdAddPointer:
1209 Status = ProcessCmdAddPointer (
1210 &LoaderEntry->Command.AddPointer,
1211 Tracker
1212 );
1213 break;
1214
1215 case QemuLoaderCmdAddChecksum:
1216 Status = ProcessCmdAddChecksum (
1217 &LoaderEntry->Command.AddChecksum,
1218 Tracker
1219 );
1220 break;
1221
1222 case QemuLoaderCmdWritePointer:
1223 Status = ProcessCmdWritePointer (
1224 &LoaderEntry->Command.WritePointer,
1225 Tracker,
1226 S3Context
1227 );
1228 if (!EFI_ERROR (Status)) {
1229 WritePointerSubsetEnd = LoaderEntry + 1;
1230 }
1231
1232 break;
1233
1234 default:
1235 DEBUG ((
1236 DEBUG_VERBOSE,
1237 "%a: unknown loader command: 0x%x\n",
1238 __func__,
1239 LoaderEntry->Type
1240 ));
1241 break;
1242 }
1243
1244 if (EFI_ERROR (Status)) {
1245 goto RollbackWritePointersAndFreeTracker;
1246 }
1247 }
1248
1249 InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);
1250 if (InstalledKey == NULL) {
1251 Status = EFI_OUT_OF_RESOURCES;
1252 goto RollbackWritePointersAndFreeTracker;
1253 }
1254
1256 if (SeenPointers == NULL) {
1257 Status = EFI_OUT_OF_RESOURCES;
1258 goto FreeKeys;
1259 }
1260
1261 //
1262 // second pass: identify and install ACPI tables
1263 //
1264 Installed = 0;
1265 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
1266 if (LoaderEntry->Type == QemuLoaderCmdAddPointer) {
1268 &LoaderEntry->Command.AddPointer,
1269 Tracker,
1270 AcpiProtocol,
1271 InstalledKey,
1272 &Installed,
1273 SeenPointers
1274 );
1275 if (EFI_ERROR (Status)) {
1276 goto UninstallAcpiTables;
1277 }
1278 }
1279 }
1280
1281 //
1282 // Install a protocol to notify that the ACPI table provided by Qemu is
1283 // ready.
1284 //
1285 QemuAcpiHandle = NULL;
1286 Status = gBS->InstallProtocolInterface (
1287 &QemuAcpiHandle,
1288 &gQemuAcpiTableNotifyProtocolGuid,
1290 NULL
1291 );
1292 if (EFI_ERROR (Status)) {
1293 goto UninstallAcpiTables;
1294 }
1295
1296 //
1297 // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3
1298 // Boot Script opcodes has to be the last operation in this function, because
1299 // if it succeeds, it cannot be undone.
1300 //
1301 if (S3Context != NULL) {
1302 Status = TransferS3ContextToBootScript (S3Context);
1303 if (EFI_ERROR (Status)) {
1304 goto UninstallQemuAcpiTableNotifyProtocol;
1305 }
1306
1307 //
1308 // Ownership of S3Context has been transferred.
1309 //
1310 S3Context = NULL;
1311 }
1312
1313 DEBUG ((DEBUG_INFO, "%a: installed %d tables\n", __func__, Installed));
1314
1315UninstallQemuAcpiTableNotifyProtocol:
1316 if (EFI_ERROR (Status)) {
1317 gBS->UninstallProtocolInterface (
1318 QemuAcpiHandle,
1319 &gQemuAcpiTableNotifyProtocolGuid,
1320 NULL
1321 );
1322 }
1323
1324UninstallAcpiTables:
1325 if (EFI_ERROR (Status)) {
1326 //
1327 // roll back partial installation
1328 //
1329 while (Installed > 0) {
1330 --Installed;
1331 AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);
1332 }
1333 }
1334
1335 for (SeenPointerEntry = OrderedCollectionMin (SeenPointers);
1336 SeenPointerEntry != NULL;
1337 SeenPointerEntry = SeenPointerEntry2)
1338 {
1339 SeenPointerEntry2 = OrderedCollectionNext (SeenPointerEntry);
1340 OrderedCollectionDelete (SeenPointers, SeenPointerEntry, NULL);
1341 }
1342
1343 OrderedCollectionUninit (SeenPointers);
1344
1345FreeKeys:
1346 FreePool (InstalledKey);
1347
1348RollbackWritePointersAndFreeTracker:
1349 //
1350 // In case of failure, revoke any allocation addresses that were communicated
1351 // to QEMU previously, before we release all the blobs.
1352 //
1353 if (EFI_ERROR (Status)) {
1354 LoaderEntry = WritePointerSubsetEnd;
1355 while (LoaderEntry > LoaderStart) {
1356 --LoaderEntry;
1357 if (LoaderEntry->Type == QemuLoaderCmdWritePointer) {
1358 UndoCmdWritePointer (&LoaderEntry->Command.WritePointer);
1359 }
1360 }
1361 }
1362
1363 //
1364 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
1365 // place only if we're exiting with success and the blob hosts data that is
1366 // not directly part of some ACPI table.
1367 //
1368 for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL;
1369 TrackerEntry = TrackerEntry2)
1370 {
1371 VOID *UserStruct;
1372 BLOB *Blob;
1373
1374 TrackerEntry2 = OrderedCollectionNext (TrackerEntry);
1375 OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct);
1376 Blob = UserStruct;
1377
1378 if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) {
1379 DEBUG ((
1380 DEBUG_VERBOSE,
1381 "%a: freeing \"%a\"\n",
1382 __func__,
1383 Blob->File
1384 ));
1385 gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size));
1386 }
1387
1388 FreePool (Blob);
1389 }
1390
1391 OrderedCollectionUninit (Tracker);
1392
1393FreeS3Context:
1394 if (S3Context != NULL) {
1395 ReleaseS3Context (S3Context);
1396 }
1397
1398FreeAllocationsRestrictedTo32Bit:
1399 ReleaseAllocationsRestrictedTo32Bit (AllocationsRestrictedTo32Bit);
1400
1401FreeLoader:
1402 FreePool (LoaderStart);
1403
1404 return Status;
1405}
UINT64 UINTN
INT64 INTN
#define MAX_ADDRESS
#define EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
Definition: Acpi10.h:637
#define EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
Definition: Acpi10.h:652
#define EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
Definition: Acpi20.h:530
VOID RestorePciDecoding(IN ORIGINAL_ATTRIBUTES *OriginalAttributes, IN UINTN Count)
Definition: PciDecoding.c:211
EFI_STATUS SaveCondensedWritePointerToS3Context(IN OUT S3_CONTEXT *S3Context, IN UINT16 PointerItem, IN UINT8 PointerSize, IN UINT32 PointerOffset, IN UINT64 PointerValue)
Definition: BootScript.c:155
EFI_STATUS AllocateS3Context(OUT S3_CONTEXT **S3Context, IN UINTN WritePointerCount)
Definition: BootScript.c:72
VOID EnablePciDecoding(OUT ORIGINAL_ATTRIBUTES **OriginalAttributes, OUT UINTN *Count)
Definition: PciDecoding.c:40
EFI_STATUS TransferS3ContextToBootScript(IN S3_CONTEXT *S3Context)
Definition: BootScript.c:258
VOID ReleaseS3Context(IN S3_CONTEXT *S3Context)
Definition: BootScript.c:114
INTN EFIAPI AsciiStrCmp(IN CONST CHAR8 *FirstString, IN CONST CHAR8 *SecondString)
Definition: String.c:716
UINT8 EFIAPI CalculateSum8(IN CONST UINT8 *Buffer, IN UINTN Length)
Definition: CheckSum.c:33
UINT8 EFIAPI CalculateCheckSum8(IN CONST UINT8 *Buffer, IN UINTN Length)
Definition: CheckSum.c:71
UINT64 EFIAPI RShiftU64(IN UINT64 Operand, IN UINTN Count)
Definition: RShiftU64.c:28
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
VOID EFIAPI FreePages(IN VOID *Buffer, IN UINTN Pages)
VOID EFIAPI FreePool(IN VOID *Buffer)
#define NULL
Definition: Base.h:319
#define CONST
Definition: Base.h:259
#define STATIC
Definition: Base.h:264
#define RETURN_ERROR(StatusCode)
Definition: Base.h:1061
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define RETURN_ALREADY_STARTED
Definition: Base.h:1172
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define ASSERT_RETURN_ERROR(StatusParameter)
Definition: DebugLib.h:493
#define DEBUG(Expression)
Definition: DebugLib.h:434
ORDERED_COLLECTION_ENTRY *EFIAPI OrderedCollectionFind(IN CONST ORDERED_COLLECTION *Collection, IN CONST VOID *StandaloneKey)
ORDERED_COLLECTION_ENTRY *EFIAPI OrderedCollectionMin(IN CONST ORDERED_COLLECTION *Collection)
VOID *EFIAPI OrderedCollectionUserStruct(IN CONST ORDERED_COLLECTION_ENTRY *Entry)
RETURN_STATUS EFIAPI OrderedCollectionInsert(IN OUT ORDERED_COLLECTION *Collection, OUT ORDERED_COLLECTION_ENTRY **Entry OPTIONAL, IN VOID *UserStruct)
VOID EFIAPI OrderedCollectionUninit(IN ORDERED_COLLECTION *Collection)
ORDERED_COLLECTION *EFIAPI OrderedCollectionInit(IN ORDERED_COLLECTION_USER_COMPARE UserStructCompare, IN ORDERED_COLLECTION_KEY_COMPARE KeyCompare)
ORDERED_COLLECTION_ENTRY *EFIAPI OrderedCollectionNext(IN CONST ORDERED_COLLECTION_ENTRY *Entry)
VOID EFIAPI OrderedCollectionDelete(IN OUT ORDERED_COLLECTION *Collection, IN ORDERED_COLLECTION_ENTRY *Entry, OUT VOID **UserStruct OPTIONAL)
VOID *EFIAPI AllocatePool(IN UINTN AllocationSize)
STATIC INTN EFIAPI BlobKeyCompare(IN CONST VOID *StandaloneKey, IN CONST VOID *UserStruct)
Definition: QemuFwCfgAcpi.c:57
STATIC EFI_STATUS EFIAPI ProcessCmdAddPointer(IN CONST QEMU_LOADER_ADD_POINTER *AddPointer, IN CONST ORDERED_COLLECTION *Tracker)
STATIC EFI_STATUS EFIAPI ProcessCmdAddChecksum(IN CONST QEMU_LOADER_ADD_CHECKSUM *AddChecksum, IN CONST ORDERED_COLLECTION *Tracker)
STATIC EFI_STATUS ProcessCmdWritePointer(IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer, IN CONST ORDERED_COLLECTION *Tracker, IN OUT S3_CONTEXT *S3Context OPTIONAL)
STATIC EFI_STATUS EFIAPI Process2ndPassCmdAddPointer(IN CONST QEMU_LOADER_ADD_POINTER *AddPointer, IN CONST ORDERED_COLLECTION *Tracker, IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol, IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX], IN OUT INT32 *NumInstalled, IN OUT ORDERED_COLLECTION *SeenPointers)
STATIC VOID UndoCmdWritePointer(IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer)
STATIC INTN EFIAPI BlobCompare(IN CONST VOID *UserStruct1, IN CONST VOID *UserStruct2)
Definition: QemuFwCfgAcpi.c:84
STATIC INTN EFIAPI PointerCompare(IN CONST VOID *Pointer1, IN CONST VOID *Pointer2)
STATIC EFI_STATUS CollectAllocationsRestrictedTo32Bit(OUT ORDERED_COLLECTION **AllocationsRestrictedTo32Bit, IN CONST QEMU_LOADER_ENTRY *LoaderStart, IN CONST QEMU_LOADER_ENTRY *LoaderEnd)
STATIC VOID ReleaseAllocationsRestrictedTo32Bit(IN ORDERED_COLLECTION *AllocationsRestrictedTo32Bit)
STATIC INTN EFIAPI AsciiStringCompare(IN CONST VOID *AsciiString1, IN CONST VOID *AsciiString2)
EFI_STATUS EFIAPI InstallQemuFwCfgTables(IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol)
STATIC EFI_STATUS EFIAPI ProcessCmdAllocate(IN CONST QEMU_LOADER_ALLOCATE *Allocate, IN OUT ORDERED_COLLECTION *Tracker, IN ORDERED_COLLECTION *AllocationsRestrictedTo32Bit)
VOID EFIAPI QemuFwCfgWriteBytes(IN UINTN Size, IN VOID *Buffer)
Definition: QemuFwCfgLib.c:101
RETURN_STATUS EFIAPI QemuFwCfgFindFile(IN CONST CHAR8 *Name, OUT FIRMWARE_CONFIG_ITEM *Item, OUT UINTN *Size)
Definition: QemuFwCfgLib.c:250
VOID EFIAPI QemuFwCfgReadBytes(IN UINTN Size, IN VOID *Buffer OPTIONAL)
Definition: QemuFwCfgNull.c:66
VOID EFIAPI QemuFwCfgSelectItem(IN FIRMWARE_CONFIG_ITEM QemuFwCfgItem)
Definition: QemuFwCfgLib.c:33
VOID EFIAPI QemuFwCfgSkipBytes(IN UINTN Size)
Definition: QemuFwCfgLib.c:127
BOOLEAN EFIAPI QemuFwCfgS3Enabled(VOID)
EFI_STATUS EFIAPI TpmMeasureAndLogData(IN UINT32 PcrIndex, IN UINT32 EventType, IN VOID *EventLog, IN UINT32 LogLen, IN VOID *HashData, IN UINT64 HashDataLen)
UINT64 EFI_PHYSICAL_ADDRESS
Definition: UefiBaseType.h:50
#define EFI_PAGES_TO_SIZE(Pages)
Definition: UefiBaseType.h:213
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
#define EFI_SIZE_TO_PAGES(Size)
Definition: UefiBaseType.h:200
VOID * EFI_HANDLE
Definition: UefiBaseType.h:33
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
EFI_BOOT_SERVICES * gBS
@ EfiACPIMemoryNVS
@ EFI_NATIVE_INTERFACE
Definition: UefiSpec.h:1193
@ AllocateMaxAddress
Definition: UefiSpec.h:38