]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
OvmfPkg/AcpiPlatformDxe: Measure ACPI table from QEMU in TDVF
[mirror_edk2.git] / OvmfPkg / AcpiPlatformDxe / QemuFwCfgAcpi.c
1 /** @file
2 OVMF ACPI support using QEMU's fw-cfg interface
3
4 Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>
5 Copyright (C) 2012-2014, Red Hat, Inc.
6
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8
9 **/
10
11 #include <IndustryStandard/Acpi.h> // EFI_ACPI_DESCRIPTION_HEADER
12 #include <IndustryStandard/QemuLoader.h> // QEMU_LOADER_FNAME_SIZE
13 #include <IndustryStandard/UefiTcgPlatform.h>
14 #include <Library/BaseLib.h> // AsciiStrCmp()
15 #include <Library/BaseMemoryLib.h> // CopyMem()
16 #include <Library/DebugLib.h> // DEBUG()
17 #include <Library/MemoryAllocationLib.h> // AllocatePool()
18 #include <Library/OrderedCollectionLib.h> // OrderedCollectionMin()
19 #include <Library/QemuFwCfgLib.h> // QemuFwCfgFindFile()
20 #include <Library/QemuFwCfgS3Lib.h> // QemuFwCfgS3Enabled()
21 #include <Library/UefiBootServicesTableLib.h> // gBS
22 #include <Library/TpmMeasurementLib.h>
23
24 #include "AcpiPlatform.h"
25
26 //
27 // The user structure for the ordered collection that will track the fw_cfg
28 // blobs under processing.
29 //
30 typedef struct {
31 UINT8 File[QEMU_LOADER_FNAME_SIZE]; // NUL-terminated name of the fw_cfg
32 // blob. This is the ordering / search
33 // key.
34 UINTN Size; // The number of bytes in this blob.
35 UINT8 *Base; // Pointer to the blob data.
36 BOOLEAN HostsOnlyTableData; // TRUE iff the blob has been found to
37 // only contain data that is directly
38 // part of ACPI tables.
39 } BLOB;
40
41 /**
42 Compare a standalone key against a user structure containing an embedded key.
43
44 @param[in] StandaloneKey Pointer to the bare key.
45
46 @param[in] UserStruct Pointer to the user structure with the embedded
47 key.
48
49 @retval <0 If StandaloneKey compares less than UserStruct's key.
50
51 @retval 0 If StandaloneKey compares equal to UserStruct's key.
52
53 @retval >0 If StandaloneKey compares greater than UserStruct's key.
54 **/
55 STATIC
56 INTN
57 EFIAPI
58 BlobKeyCompare (
59 IN CONST VOID *StandaloneKey,
60 IN CONST VOID *UserStruct
61 )
62 {
63 CONST BLOB *Blob;
64
65 Blob = UserStruct;
66 return AsciiStrCmp (StandaloneKey, (CONST CHAR8 *)Blob->File);
67 }
68
69 /**
70 Comparator function for two user structures.
71
72 @param[in] UserStruct1 Pointer to the first user structure.
73
74 @param[in] UserStruct2 Pointer to the second user structure.
75
76 @retval <0 If UserStruct1 compares less than UserStruct2.
77
78 @retval 0 If UserStruct1 compares equal to UserStruct2.
79
80 @retval >0 If UserStruct1 compares greater than UserStruct2.
81 **/
82 STATIC
83 INTN
84 EFIAPI
85 BlobCompare (
86 IN CONST VOID *UserStruct1,
87 IN CONST VOID *UserStruct2
88 )
89 {
90 CONST BLOB *Blob1;
91
92 Blob1 = UserStruct1;
93 return BlobKeyCompare (Blob1->File, UserStruct2);
94 }
95
96 /**
97 Comparator function for two opaque pointers, ordering on (unsigned) pointer
98 value itself.
99 Can be used as both Key and UserStruct comparator.
100
101 @param[in] Pointer1 First pointer.
102
103 @param[in] Pointer2 Second pointer.
104
105 @retval <0 If Pointer1 compares less than Pointer2.
106
107 @retval 0 If Pointer1 compares equal to Pointer2.
108
109 @retval >0 If Pointer1 compares greater than Pointer2.
110 **/
111 STATIC
112 INTN
113 EFIAPI
114 PointerCompare (
115 IN CONST VOID *Pointer1,
116 IN CONST VOID *Pointer2
117 )
118 {
119 if (Pointer1 == Pointer2) {
120 return 0;
121 }
122
123 if ((UINTN)Pointer1 < (UINTN)Pointer2) {
124 return -1;
125 }
126
127 return 1;
128 }
129
130 /**
131 Comparator function for two ASCII strings. Can be used as both Key and
132 UserStruct comparator.
133
134 This function exists solely so we can avoid casting &AsciiStrCmp to
135 ORDERED_COLLECTION_USER_COMPARE and ORDERED_COLLECTION_KEY_COMPARE.
136
137 @param[in] AsciiString1 Pointer to the first ASCII string.
138
139 @param[in] AsciiString2 Pointer to the second ASCII string.
140
141 @return The return value of AsciiStrCmp (AsciiString1, AsciiString2).
142 **/
143 STATIC
144 INTN
145 EFIAPI
146 AsciiStringCompare (
147 IN CONST VOID *AsciiString1,
148 IN CONST VOID *AsciiString2
149 )
150 {
151 return AsciiStrCmp (AsciiString1, AsciiString2);
152 }
153
154 /**
155 Release the ORDERED_COLLECTION structure populated by
156 CollectAllocationsRestrictedTo32Bit() (below).
157
158 This function may be called by CollectAllocationsRestrictedTo32Bit() itself,
159 on the error path.
160
161 @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure to
162 release.
163 **/
164 STATIC
165 VOID
166 ReleaseAllocationsRestrictedTo32Bit (
167 IN ORDERED_COLLECTION *AllocationsRestrictedTo32Bit
168 )
169 {
170 ORDERED_COLLECTION_ENTRY *Entry, *Entry2;
171
172 for (Entry = OrderedCollectionMin (AllocationsRestrictedTo32Bit);
173 Entry != NULL;
174 Entry = Entry2)
175 {
176 Entry2 = OrderedCollectionNext (Entry);
177 OrderedCollectionDelete (AllocationsRestrictedTo32Bit, Entry, NULL);
178 }
179
180 OrderedCollectionUninit (AllocationsRestrictedTo32Bit);
181 }
182
183 /**
184 Iterate over the linker/loader script, and collect the names of the fw_cfg
185 blobs that are referenced by QEMU_LOADER_ADD_POINTER.PointeeFile fields, such
186 that QEMU_LOADER_ADD_POINTER.PointerSize is less than 8. This means that the
187 pointee blob's address will have to be patched into a narrower-than-8 byte
188 pointer field, hence the pointee blob must not be allocated from 64-bit
189 address space.
190
191 @param[out] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure
192 linking (not copying / owning) such
193 QEMU_LOADER_ADD_POINTER.PointeeFile
194 fields that name the blobs
195 restricted from 64-bit allocation.
196
197 @param[in] LoaderStart Points to the first entry in the
198 linker/loader script.
199
200 @param[in] LoaderEnd Points one past the last entry in
201 the linker/loader script.
202
203 @retval EFI_SUCCESS AllocationsRestrictedTo32Bit has been
204 populated.
205
206 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
207
208 @retval EFI_PROTOCOL_ERROR Invalid linker/loader script contents.
209 **/
210 STATIC
211 EFI_STATUS
212 CollectAllocationsRestrictedTo32Bit (
213 OUT ORDERED_COLLECTION **AllocationsRestrictedTo32Bit,
214 IN CONST QEMU_LOADER_ENTRY *LoaderStart,
215 IN CONST QEMU_LOADER_ENTRY *LoaderEnd
216 )
217 {
218 ORDERED_COLLECTION *Collection;
219 CONST QEMU_LOADER_ENTRY *LoaderEntry;
220 EFI_STATUS Status;
221
222 Collection = OrderedCollectionInit (AsciiStringCompare, AsciiStringCompare);
223 if (Collection == NULL) {
224 return EFI_OUT_OF_RESOURCES;
225 }
226
227 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
228 CONST QEMU_LOADER_ADD_POINTER *AddPointer;
229
230 if (LoaderEntry->Type != QemuLoaderCmdAddPointer) {
231 continue;
232 }
233
234 AddPointer = &LoaderEntry->Command.AddPointer;
235
236 if (AddPointer->PointerSize >= 8) {
237 continue;
238 }
239
240 if (AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
241 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));
242 Status = EFI_PROTOCOL_ERROR;
243 goto RollBack;
244 }
245
246 Status = OrderedCollectionInsert (
247 Collection,
248 NULL, // Entry
249 (VOID *)AddPointer->PointeeFile
250 );
251 switch (Status) {
252 case EFI_SUCCESS:
253 DEBUG ((
254 DEBUG_VERBOSE,
255 "%a: restricting blob \"%a\" from 64-bit allocation\n",
256 __FUNCTION__,
257 AddPointer->PointeeFile
258 ));
259 break;
260 case EFI_ALREADY_STARTED:
261 //
262 // The restriction has been recorded already.
263 //
264 break;
265 case EFI_OUT_OF_RESOURCES:
266 goto RollBack;
267 default:
268 ASSERT (FALSE);
269 }
270 }
271
272 *AllocationsRestrictedTo32Bit = Collection;
273 return EFI_SUCCESS;
274
275 RollBack:
276 ReleaseAllocationsRestrictedTo32Bit (Collection);
277 return Status;
278 }
279
280 /**
281 Process a QEMU_LOADER_ALLOCATE command.
282
283 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to
284 process.
285
286 @param[in,out] Tracker The ORDERED_COLLECTION tracking the
287 BLOB user structures created thus
288 far.
289
290 @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION populated by
291 the function
292 CollectAllocationsRestrictedTo32Bit,
293 naming the fw_cfg blobs that must
294 not be allocated from 64-bit address
295 space.
296
297 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been
298 allocated for the blob contents, and the
299 contents have been saved. A BLOB object (user
300 structure) has been allocated from pool memory,
301 referencing the blob contents. The BLOB user
302 structure has been linked into Tracker.
303
304 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
305 Allocate, or the Allocate command references a
306 file that is already known by Tracker.
307
308 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in
309 Allocate.
310
311 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.
312
313 @return Error codes from QemuFwCfgFindFile() and
314 gBS->AllocatePages().
315 **/
316 STATIC
317 EFI_STATUS
318 EFIAPI
319 ProcessCmdAllocate (
320 IN CONST QEMU_LOADER_ALLOCATE *Allocate,
321 IN OUT ORDERED_COLLECTION *Tracker,
322 IN ORDERED_COLLECTION *AllocationsRestrictedTo32Bit
323 )
324 {
325 FIRMWARE_CONFIG_ITEM FwCfgItem;
326 UINTN FwCfgSize;
327 EFI_STATUS Status;
328 UINTN NumPages;
329 EFI_PHYSICAL_ADDRESS Address;
330 BLOB *Blob;
331
332 if (Allocate->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
333 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));
334 return EFI_PROTOCOL_ERROR;
335 }
336
337 if (Allocate->Alignment > EFI_PAGE_SIZE) {
338 DEBUG ((
339 DEBUG_ERROR,
340 "%a: unsupported alignment 0x%x\n",
341 __FUNCTION__,
342 Allocate->Alignment
343 ));
344 return EFI_UNSUPPORTED;
345 }
346
347 Status = QemuFwCfgFindFile ((CHAR8 *)Allocate->File, &FwCfgItem, &FwCfgSize);
348 if (EFI_ERROR (Status)) {
349 DEBUG ((
350 DEBUG_ERROR,
351 "%a: QemuFwCfgFindFile(\"%a\"): %r\n",
352 __FUNCTION__,
353 Allocate->File,
354 Status
355 ));
356 return Status;
357 }
358
359 NumPages = EFI_SIZE_TO_PAGES (FwCfgSize);
360 Address = MAX_UINT64;
361 if (OrderedCollectionFind (
362 AllocationsRestrictedTo32Bit,
363 Allocate->File
364 ) != NULL)
365 {
366 Address = MAX_UINT32;
367 }
368
369 Status = gBS->AllocatePages (
370 AllocateMaxAddress,
371 EfiACPIMemoryNVS,
372 NumPages,
373 &Address
374 );
375 if (EFI_ERROR (Status)) {
376 return Status;
377 }
378
379 Blob = AllocatePool (sizeof *Blob);
380 if (Blob == NULL) {
381 Status = EFI_OUT_OF_RESOURCES;
382 goto FreePages;
383 }
384
385 CopyMem (Blob->File, Allocate->File, QEMU_LOADER_FNAME_SIZE);
386 Blob->Size = FwCfgSize;
387 Blob->Base = (VOID *)(UINTN)Address;
388 Blob->HostsOnlyTableData = TRUE;
389
390 Status = OrderedCollectionInsert (Tracker, NULL, Blob);
391 if (Status == RETURN_ALREADY_STARTED) {
392 DEBUG ((
393 DEBUG_ERROR,
394 "%a: duplicated file \"%a\"\n",
395 __FUNCTION__,
396 Allocate->File
397 ));
398 Status = EFI_PROTOCOL_ERROR;
399 }
400
401 if (EFI_ERROR (Status)) {
402 goto FreeBlob;
403 }
404
405 QemuFwCfgSelectItem (FwCfgItem);
406 QemuFwCfgReadBytes (FwCfgSize, Blob->Base);
407 ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size);
408
409 DEBUG ((
410 DEBUG_VERBOSE,
411 "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "
412 "Address=0x%Lx\n",
413 __FUNCTION__,
414 Allocate->File,
415 Allocate->Alignment,
416 Allocate->Zone,
417 (UINT64)Blob->Size,
418 (UINT64)(UINTN)Blob->Base
419 ));
420
421 //
422 // Measure the data which is downloaded from QEMU.
423 // It has to be done before it is consumed. Because the data will
424 // be updated in the following operations.
425 //
426 TpmMeasureAndLogData (
427 1,
428 EV_PLATFORM_CONFIG_FLAGS,
429 EV_POSTCODE_INFO_ACPI_DATA,
430 ACPI_DATA_LEN,
431 (VOID *)(UINTN)Blob->Base,
432 Blob->Size
433 );
434
435 return EFI_SUCCESS;
436
437 FreeBlob:
438 FreePool (Blob);
439
440 FreePages:
441 gBS->FreePages (Address, NumPages);
442
443 return Status;
444 }
445
446 /**
447 Process a QEMU_LOADER_ADD_POINTER command.
448
449 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
450
451 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
452 structures created thus far.
453
454 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
455 AddPointer, or the AddPointer command references
456 a file unknown to Tracker, or the pointer to
457 relocate has invalid location, size, or value, or
458 the relocated pointer value is not representable
459 in the given pointer size.
460
461 @retval EFI_SUCCESS The pointer field inside the pointer blob has
462 been relocated.
463 **/
464 STATIC
465 EFI_STATUS
466 EFIAPI
467 ProcessCmdAddPointer (
468 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,
469 IN CONST ORDERED_COLLECTION *Tracker
470 )
471 {
472 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;
473 BLOB *Blob, *Blob2;
474 UINT8 *PointerField;
475 UINT64 PointerValue;
476
477 if ((AddPointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') ||
478 (AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0'))
479 {
480 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));
481 return EFI_PROTOCOL_ERROR;
482 }
483
484 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);
485 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);
486 if ((TrackerEntry == NULL) || (TrackerEntry2 == NULL)) {
487 DEBUG ((
488 DEBUG_ERROR,
489 "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",
490 __FUNCTION__,
491 AddPointer->PointerFile,
492 AddPointer->PointeeFile
493 ));
494 return EFI_PROTOCOL_ERROR;
495 }
496
497 Blob = OrderedCollectionUserStruct (TrackerEntry);
498 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);
499 if (((AddPointer->PointerSize != 1) && (AddPointer->PointerSize != 2) &&
500 (AddPointer->PointerSize != 4) && (AddPointer->PointerSize != 8)) ||
501 (Blob->Size < AddPointer->PointerSize) ||
502 (Blob->Size - AddPointer->PointerSize < AddPointer->PointerOffset))
503 {
504 DEBUG ((
505 DEBUG_ERROR,
506 "%a: invalid pointer location or size in \"%a\"\n",
507 __FUNCTION__,
508 AddPointer->PointerFile
509 ));
510 return EFI_PROTOCOL_ERROR;
511 }
512
513 PointerField = Blob->Base + AddPointer->PointerOffset;
514 PointerValue = 0;
515 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);
516 if (PointerValue >= Blob2->Size) {
517 DEBUG ((
518 DEBUG_ERROR,
519 "%a: invalid pointer value in \"%a\"\n",
520 __FUNCTION__,
521 AddPointer->PointerFile
522 ));
523 return EFI_PROTOCOL_ERROR;
524 }
525
526 //
527 // The memory allocation system ensures that the address of the byte past the
528 // last byte of any allocated object is expressible (no wraparound).
529 //
530 ASSERT ((UINTN)Blob2->Base <= MAX_ADDRESS - Blob2->Size);
531
532 PointerValue += (UINT64)(UINTN)Blob2->Base;
533 if ((AddPointer->PointerSize < 8) &&
534 (RShiftU64 (PointerValue, AddPointer->PointerSize * 8) != 0))
535 {
536 DEBUG ((
537 DEBUG_ERROR,
538 "%a: relocated pointer value unrepresentable in "
539 "\"%a\"\n",
540 __FUNCTION__,
541 AddPointer->PointerFile
542 ));
543 return EFI_PROTOCOL_ERROR;
544 }
545
546 CopyMem (PointerField, &PointerValue, AddPointer->PointerSize);
547
548 DEBUG ((
549 DEBUG_VERBOSE,
550 "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
551 "PointerOffset=0x%x PointerSize=%d\n",
552 __FUNCTION__,
553 AddPointer->PointerFile,
554 AddPointer->PointeeFile,
555 AddPointer->PointerOffset,
556 AddPointer->PointerSize
557 ));
558 return EFI_SUCCESS;
559 }
560
561 /**
562 Process a QEMU_LOADER_ADD_CHECKSUM command.
563
564 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.
565
566 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
567 structures created thus far.
568
569 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
570 AddChecksum, or the AddChecksum command
571 references a file unknown to Tracker, or the
572 range to checksum is invalid.
573
574 @retval EFI_SUCCESS The requested range has been checksummed.
575 **/
576 STATIC
577 EFI_STATUS
578 EFIAPI
579 ProcessCmdAddChecksum (
580 IN CONST QEMU_LOADER_ADD_CHECKSUM *AddChecksum,
581 IN CONST ORDERED_COLLECTION *Tracker
582 )
583 {
584 ORDERED_COLLECTION_ENTRY *TrackerEntry;
585 BLOB *Blob;
586
587 if (AddChecksum->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
588 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));
589 return EFI_PROTOCOL_ERROR;
590 }
591
592 TrackerEntry = OrderedCollectionFind (Tracker, AddChecksum->File);
593 if (TrackerEntry == NULL) {
594 DEBUG ((
595 DEBUG_ERROR,
596 "%a: invalid blob reference \"%a\"\n",
597 __FUNCTION__,
598 AddChecksum->File
599 ));
600 return EFI_PROTOCOL_ERROR;
601 }
602
603 Blob = OrderedCollectionUserStruct (TrackerEntry);
604 if ((Blob->Size <= AddChecksum->ResultOffset) ||
605 (Blob->Size < AddChecksum->Length) ||
606 (Blob->Size - AddChecksum->Length < AddChecksum->Start))
607 {
608 DEBUG ((
609 DEBUG_ERROR,
610 "%a: invalid checksum range in \"%a\"\n",
611 __FUNCTION__,
612 AddChecksum->File
613 ));
614 return EFI_PROTOCOL_ERROR;
615 }
616
617 Blob->Base[AddChecksum->ResultOffset] = CalculateCheckSum8 (
618 Blob->Base + AddChecksum->Start,
619 AddChecksum->Length
620 );
621 DEBUG ((
622 DEBUG_VERBOSE,
623 "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "
624 "Length=0x%x\n",
625 __FUNCTION__,
626 AddChecksum->File,
627 AddChecksum->ResultOffset,
628 AddChecksum->Start,
629 AddChecksum->Length
630 ));
631 return EFI_SUCCESS;
632 }
633
634 /**
635 Process a QEMU_LOADER_WRITE_POINTER command.
636
637 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.
638
639 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
640 structures created thus far.
641
642 @param[in,out] S3Context The S3_CONTEXT object capturing the fw_cfg actions
643 of successfully processed QEMU_LOADER_WRITE_POINTER
644 commands, to be replayed at S3 resume. S3Context
645 may be NULL if S3 is disabled.
646
647 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
648 WritePointer. Or, the WritePointer command
649 references a file unknown to Tracker or the
650 fw_cfg directory. Or, the pointer object to
651 rewrite has invalid location, size, or initial
652 relative value. Or, the pointer value to store
653 does not fit in the given pointer size.
654
655 @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg
656 file has been written. If S3Context is not NULL,
657 then WritePointer has been condensed into
658 S3Context.
659
660 @return Error codes propagated from
661 SaveCondensedWritePointerToS3Context(). The
662 pointer object inside the writeable fw_cfg file
663 has not been written.
664 **/
665 STATIC
666 EFI_STATUS
667 ProcessCmdWritePointer (
668 IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer,
669 IN CONST ORDERED_COLLECTION *Tracker,
670 IN OUT S3_CONTEXT *S3Context OPTIONAL
671 )
672 {
673 RETURN_STATUS Status;
674 FIRMWARE_CONFIG_ITEM PointerItem;
675 UINTN PointerItemSize;
676 ORDERED_COLLECTION_ENTRY *PointeeEntry;
677 BLOB *PointeeBlob;
678 UINT64 PointerValue;
679
680 if ((WritePointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') ||
681 (WritePointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0'))
682 {
683 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));
684 return EFI_PROTOCOL_ERROR;
685 }
686
687 Status = QemuFwCfgFindFile (
688 (CONST CHAR8 *)WritePointer->PointerFile,
689 &PointerItem,
690 &PointerItemSize
691 );
692 PointeeEntry = OrderedCollectionFind (Tracker, WritePointer->PointeeFile);
693 if (RETURN_ERROR (Status) || (PointeeEntry == NULL)) {
694 DEBUG ((
695 DEBUG_ERROR,
696 "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
697 __FUNCTION__,
698 WritePointer->PointerFile,
699 WritePointer->PointeeFile
700 ));
701 return EFI_PROTOCOL_ERROR;
702 }
703
704 if (((WritePointer->PointerSize != 1) && (WritePointer->PointerSize != 2) &&
705 (WritePointer->PointerSize != 4) && (WritePointer->PointerSize != 8)) ||
706 (PointerItemSize < WritePointer->PointerSize) ||
707 (PointerItemSize - WritePointer->PointerSize <
708 WritePointer->PointerOffset))
709 {
710 DEBUG ((
711 DEBUG_ERROR,
712 "%a: invalid pointer location or size in \"%a\"\n",
713 __FUNCTION__,
714 WritePointer->PointerFile
715 ));
716 return EFI_PROTOCOL_ERROR;
717 }
718
719 PointeeBlob = OrderedCollectionUserStruct (PointeeEntry);
720 PointerValue = WritePointer->PointeeOffset;
721 if (PointerValue >= PointeeBlob->Size) {
722 DEBUG ((DEBUG_ERROR, "%a: invalid PointeeOffset\n", __FUNCTION__));
723 return EFI_PROTOCOL_ERROR;
724 }
725
726 //
727 // The memory allocation system ensures that the address of the byte past the
728 // last byte of any allocated object is expressible (no wraparound).
729 //
730 ASSERT ((UINTN)PointeeBlob->Base <= MAX_ADDRESS - PointeeBlob->Size);
731
732 PointerValue += (UINT64)(UINTN)PointeeBlob->Base;
733 if ((WritePointer->PointerSize < 8) &&
734 (RShiftU64 (PointerValue, WritePointer->PointerSize * 8) != 0))
735 {
736 DEBUG ((
737 DEBUG_ERROR,
738 "%a: pointer value unrepresentable in \"%a\"\n",
739 __FUNCTION__,
740 WritePointer->PointerFile
741 ));
742 return EFI_PROTOCOL_ERROR;
743 }
744
745 //
746 // If S3 is enabled, we have to capture the below fw_cfg actions in condensed
747 // form, to be replayed during S3 resume.
748 //
749 if (S3Context != NULL) {
750 EFI_STATUS SaveStatus;
751
752 SaveStatus = SaveCondensedWritePointerToS3Context (
753 S3Context,
754 (UINT16)PointerItem,
755 WritePointer->PointerSize,
756 WritePointer->PointerOffset,
757 PointerValue
758 );
759 if (EFI_ERROR (SaveStatus)) {
760 return SaveStatus;
761 }
762 }
763
764 QemuFwCfgSelectItem (PointerItem);
765 QemuFwCfgSkipBytes (WritePointer->PointerOffset);
766 QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);
767
768 //
769 // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob
770 // as unreleasable, for the case when the whole linker/loader script is
771 // handled successfully.
772 //
773 PointeeBlob->HostsOnlyTableData = FALSE;
774
775 DEBUG ((
776 DEBUG_VERBOSE,
777 "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
778 "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n",
779 __FUNCTION__,
780 WritePointer->PointerFile,
781 WritePointer->PointeeFile,
782 WritePointer->PointerOffset,
783 WritePointer->PointeeOffset,
784 WritePointer->PointerSize
785 ));
786 return EFI_SUCCESS;
787 }
788
789 /**
790 Undo a QEMU_LOADER_WRITE_POINTER command.
791
792 This function revokes (zeroes out) a guest memory reference communicated to
793 QEMU earlier. The caller is responsible for invoking this function only on
794 such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed
795 by ProcessCmdWritePointer().
796
797 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
798 **/
799 STATIC
800 VOID
801 UndoCmdWritePointer (
802 IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer
803 )
804 {
805 RETURN_STATUS Status;
806 FIRMWARE_CONFIG_ITEM PointerItem;
807 UINTN PointerItemSize;
808 UINT64 PointerValue;
809
810 Status = QemuFwCfgFindFile (
811 (CONST CHAR8 *)WritePointer->PointerFile,
812 &PointerItem,
813 &PointerItemSize
814 );
815 ASSERT_RETURN_ERROR (Status);
816
817 PointerValue = 0;
818 QemuFwCfgSelectItem (PointerItem);
819 QemuFwCfgSkipBytes (WritePointer->PointerOffset);
820 QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);
821
822 DEBUG ((
823 DEBUG_VERBOSE,
824 "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n",
825 __FUNCTION__,
826 WritePointer->PointerFile,
827 WritePointer->PointerOffset,
828 WritePointer->PointerSize
829 ));
830 }
831
832 //
833 // We'll be saving the keys of installed tables so that we can roll them back
834 // in case of failure. 128 tables should be enough for anyone (TM).
835 //
836 #define INSTALLED_TABLES_MAX 128
837
838 /**
839 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte
840 array is an ACPI table, and if so, install it.
841
842 This function assumes that the entire QEMU linker/loader command file has
843 been processed successfully in a prior first pass.
844
845 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
846
847 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
848 structures.
849
850 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
851
852 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
853 elements, allocated by the caller. On output,
854 the function will have stored (appended) the
855 AcpiProtocol-internal key of the ACPI table that
856 the function has installed, if the AddPointer
857 command identified an ACPI table that is
858 different from RSDT and XSDT.
859
860 @param[in,out] NumInstalled On input, the number of entries already used in
861 InstalledKey; it must be in [0,
862 INSTALLED_TABLES_MAX] inclusive. On output, the
863 parameter is incremented if the AddPointer
864 command identified an ACPI table that is
865 different from RSDT and XSDT.
866
867 @param[in,out] SeenPointers The ORDERED_COLLECTION tracking the absolute
868 target addresses that have been pointed-to by
869 QEMU_LOADER_ADD_POINTER commands thus far. If a
870 target address is encountered for the first
871 time, and it identifies an ACPI table that is
872 different from RDST and XSDT, the table is
873 installed. If a target address is seen for the
874 second or later times, it is skipped without
875 taking any action.
876
877 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
878 input.
879
880 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI
881 table different from RSDT and XSDT, but there
882 was no more room in InstalledKey.
883
884 @retval EFI_SUCCESS AddPointer has been processed. Either its
885 absolute target address has been encountered
886 before, or an ACPI table different from RSDT
887 and XSDT has been installed (reflected by
888 InstalledKey and NumInstalled), or RSDT or
889 XSDT has been identified but not installed, or
890 the fw_cfg blob pointed-into by AddPointer has
891 been marked as hosting something else than
892 just direct ACPI table contents.
893
894 @return Error codes returned by
895 AcpiProtocol->InstallAcpiTable().
896 **/
897 STATIC
898 EFI_STATUS
899 EFIAPI
900 Process2ndPassCmdAddPointer (
901 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,
902 IN CONST ORDERED_COLLECTION *Tracker,
903 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
904 IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX],
905 IN OUT INT32 *NumInstalled,
906 IN OUT ORDERED_COLLECTION *SeenPointers
907 )
908 {
909 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry;
910 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry2;
911 ORDERED_COLLECTION_ENTRY *SeenPointerEntry;
912 CONST BLOB *Blob;
913 BLOB *Blob2;
914 CONST UINT8 *PointerField;
915 UINT64 PointerValue;
916 UINTN Blob2Remaining;
917 UINTN TableSize;
918 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
919 CONST EFI_ACPI_DESCRIPTION_HEADER *Header;
920 EFI_STATUS Status;
921
922 if ((*NumInstalled < 0) || (*NumInstalled > INSTALLED_TABLES_MAX)) {
923 return EFI_INVALID_PARAMETER;
924 }
925
926 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);
927 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);
928 Blob = OrderedCollectionUserStruct (TrackerEntry);
929 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);
930 PointerField = Blob->Base + AddPointer->PointerOffset;
931 PointerValue = 0;
932 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);
933
934 //
935 // We assert that PointerValue falls inside Blob2's contents. This is ensured
936 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().
937 //
938 Blob2Remaining = (UINTN)Blob2->Base;
939 ASSERT (PointerValue >= Blob2Remaining);
940 Blob2Remaining += Blob2->Size;
941 ASSERT (PointerValue < Blob2Remaining);
942
943 Status = OrderedCollectionInsert (
944 SeenPointers,
945 &SeenPointerEntry, // for reverting insertion in error case
946 (VOID *)(UINTN)PointerValue
947 );
948 if (EFI_ERROR (Status)) {
949 if (Status == RETURN_ALREADY_STARTED) {
950 //
951 // Already seen this pointer, don't try to process it again.
952 //
953 DEBUG ((
954 DEBUG_VERBOSE,
955 "%a: PointerValue=0x%Lx already processed, skipping.\n",
956 __FUNCTION__,
957 PointerValue
958 ));
959 Status = EFI_SUCCESS;
960 }
961
962 return Status;
963 }
964
965 Blob2Remaining -= (UINTN)PointerValue;
966 DEBUG ((
967 DEBUG_VERBOSE,
968 "%a: checking for ACPI header in \"%a\" at 0x%Lx "
969 "(remaining: 0x%Lx): ",
970 __FUNCTION__,
971 AddPointer->PointeeFile,
972 PointerValue,
973 (UINT64)Blob2Remaining
974 ));
975
976 TableSize = 0;
977
978 //
979 // To make our job simple, the FACS has a custom header. Sigh.
980 //
981 if (sizeof *Facs <= Blob2Remaining) {
982 Facs = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)PointerValue;
983
984 if ((Facs->Length >= sizeof *Facs) &&
985 (Facs->Length <= Blob2Remaining) &&
986 (Facs->Signature ==
987 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE))
988 {
989 DEBUG ((
990 DEBUG_VERBOSE,
991 "found \"%-4.4a\" size 0x%x\n",
992 (CONST CHAR8 *)&Facs->Signature,
993 Facs->Length
994 ));
995 TableSize = Facs->Length;
996 }
997 }
998
999 //
1000 // check for the uniform tables
1001 //
1002 if ((TableSize == 0) && (sizeof *Header <= Blob2Remaining)) {
1003 Header = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)PointerValue;
1004
1005 if ((Header->Length >= sizeof *Header) &&
1006 (Header->Length <= Blob2Remaining) &&
1007 (CalculateSum8 ((CONST UINT8 *)Header, Header->Length) == 0))
1008 {
1009 //
1010 // This looks very much like an ACPI table from QEMU:
1011 // - Length field consistent with both ACPI and containing blob size
1012 // - checksum is correct
1013 //
1014 DEBUG ((
1015 DEBUG_VERBOSE,
1016 "found \"%-4.4a\" size 0x%x\n",
1017 (CONST CHAR8 *)&Header->Signature,
1018 Header->Length
1019 ));
1020 TableSize = Header->Length;
1021
1022 //
1023 // Skip RSDT and XSDT because those are handled by
1024 // EFI_ACPI_TABLE_PROTOCOL automatically.
1025 if ((Header->Signature ==
1026 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) ||
1027 (Header->Signature ==
1028 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE))
1029 {
1030 return EFI_SUCCESS;
1031 }
1032 }
1033 }
1034
1035 if (TableSize == 0) {
1036 DEBUG ((DEBUG_VERBOSE, "not found; marking fw_cfg blob as opaque\n"));
1037 Blob2->HostsOnlyTableData = FALSE;
1038 return EFI_SUCCESS;
1039 }
1040
1041 if (*NumInstalled == INSTALLED_TABLES_MAX) {
1042 DEBUG ((
1043 DEBUG_ERROR,
1044 "%a: can't install more than %d tables\n",
1045 __FUNCTION__,
1046 INSTALLED_TABLES_MAX
1047 ));
1048 Status = EFI_OUT_OF_RESOURCES;
1049 goto RollbackSeenPointer;
1050 }
1051
1052 Status = AcpiProtocol->InstallAcpiTable (
1053 AcpiProtocol,
1054 (VOID *)(UINTN)PointerValue,
1055 TableSize,
1056 &InstalledKey[*NumInstalled]
1057 );
1058 if (EFI_ERROR (Status)) {
1059 DEBUG ((
1060 DEBUG_ERROR,
1061 "%a: InstallAcpiTable(): %r\n",
1062 __FUNCTION__,
1063 Status
1064 ));
1065 goto RollbackSeenPointer;
1066 }
1067
1068 ++*NumInstalled;
1069 return EFI_SUCCESS;
1070
1071 RollbackSeenPointer:
1072 OrderedCollectionDelete (SeenPointers, SeenPointerEntry, NULL);
1073 return Status;
1074 }
1075
1076 /**
1077 Download, process, and install ACPI table data from the QEMU loader
1078 interface.
1079
1080 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
1081
1082 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU
1083 loader command with unsupported parameters
1084 has been found.
1085
1086 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg
1087 files.
1088
1089 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than
1090 INSTALLED_TABLES_MAX tables found.
1091
1092 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.
1093
1094 @return Status codes returned by
1095 AcpiProtocol->InstallAcpiTable().
1096
1097 **/
1098 EFI_STATUS
1099 EFIAPI
1100 InstallQemuFwCfgTables (
1101 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol
1102 )
1103 {
1104 EFI_STATUS Status;
1105 FIRMWARE_CONFIG_ITEM FwCfgItem;
1106 UINTN FwCfgSize;
1107 QEMU_LOADER_ENTRY *LoaderStart;
1108 CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd;
1109 CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd;
1110 ORIGINAL_ATTRIBUTES *OriginalPciAttributes;
1111 UINTN OriginalPciAttributesCount;
1112 ORDERED_COLLECTION *AllocationsRestrictedTo32Bit;
1113 S3_CONTEXT *S3Context;
1114 ORDERED_COLLECTION *Tracker;
1115 UINTN *InstalledKey;
1116 INT32 Installed;
1117 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;
1118 ORDERED_COLLECTION *SeenPointers;
1119 ORDERED_COLLECTION_ENTRY *SeenPointerEntry, *SeenPointerEntry2;
1120 EFI_HANDLE QemuAcpiHandle;
1121
1122 Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize);
1123 if (EFI_ERROR (Status)) {
1124 return Status;
1125 }
1126
1127 if (FwCfgSize % sizeof *LoaderEntry != 0) {
1128 DEBUG ((
1129 DEBUG_ERROR,
1130 "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
1131 __FUNCTION__,
1132 (UINT64)FwCfgSize
1133 ));
1134 return EFI_PROTOCOL_ERROR;
1135 }
1136
1137 LoaderStart = AllocatePool (FwCfgSize);
1138 if (LoaderStart == NULL) {
1139 return EFI_OUT_OF_RESOURCES;
1140 }
1141
1142 EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount);
1143 QemuFwCfgSelectItem (FwCfgItem);
1144 QemuFwCfgReadBytes (FwCfgSize, LoaderStart);
1145 RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount);
1146
1147 //
1148 // Measure the "etc/table-loader" which is downloaded from QEMU.
1149 // It has to be done before it is consumed. Because it would be
1150 // updated in the following operations.
1151 //
1152 TpmMeasureAndLogData (
1153 1,
1154 EV_PLATFORM_CONFIG_FLAGS,
1155 EV_POSTCODE_INFO_ACPI_DATA,
1156 ACPI_DATA_LEN,
1157 (VOID *)(UINTN)LoaderStart,
1158 FwCfgSize
1159 );
1160
1161 LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry;
1162
1163 AllocationsRestrictedTo32Bit = NULL;
1164 Status = CollectAllocationsRestrictedTo32Bit (
1165 &AllocationsRestrictedTo32Bit,
1166 LoaderStart,
1167 LoaderEnd
1168 );
1169 if (EFI_ERROR (Status)) {
1170 goto FreeLoader;
1171 }
1172
1173 S3Context = NULL;
1174 if (QemuFwCfgS3Enabled ()) {
1175 //
1176 // Size the allocation pessimistically, assuming that all commands in the
1177 // script are QEMU_LOADER_WRITE_POINTER commands.
1178 //
1179 Status = AllocateS3Context (&S3Context, LoaderEnd - LoaderStart);
1180 if (EFI_ERROR (Status)) {
1181 goto FreeAllocationsRestrictedTo32Bit;
1182 }
1183 }
1184
1185 Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare);
1186 if (Tracker == NULL) {
1187 Status = EFI_OUT_OF_RESOURCES;
1188 goto FreeS3Context;
1189 }
1190
1191 //
1192 // first pass: process the commands
1193 //
1194 // "WritePointerSubsetEnd" points one past the last successful
1195 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
1196 // pass, no such command has been encountered yet.
1197 //
1198 WritePointerSubsetEnd = LoaderStart;
1199 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
1200 switch (LoaderEntry->Type) {
1201 case QemuLoaderCmdAllocate:
1202 Status = ProcessCmdAllocate (
1203 &LoaderEntry->Command.Allocate,
1204 Tracker,
1205 AllocationsRestrictedTo32Bit
1206 );
1207 break;
1208
1209 case QemuLoaderCmdAddPointer:
1210 Status = ProcessCmdAddPointer (
1211 &LoaderEntry->Command.AddPointer,
1212 Tracker
1213 );
1214 break;
1215
1216 case QemuLoaderCmdAddChecksum:
1217 Status = ProcessCmdAddChecksum (
1218 &LoaderEntry->Command.AddChecksum,
1219 Tracker
1220 );
1221 break;
1222
1223 case QemuLoaderCmdWritePointer:
1224 Status = ProcessCmdWritePointer (
1225 &LoaderEntry->Command.WritePointer,
1226 Tracker,
1227 S3Context
1228 );
1229 if (!EFI_ERROR (Status)) {
1230 WritePointerSubsetEnd = LoaderEntry + 1;
1231 }
1232
1233 break;
1234
1235 default:
1236 DEBUG ((
1237 DEBUG_VERBOSE,
1238 "%a: unknown loader command: 0x%x\n",
1239 __FUNCTION__,
1240 LoaderEntry->Type
1241 ));
1242 break;
1243 }
1244
1245 if (EFI_ERROR (Status)) {
1246 goto RollbackWritePointersAndFreeTracker;
1247 }
1248 }
1249
1250 InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);
1251 if (InstalledKey == NULL) {
1252 Status = EFI_OUT_OF_RESOURCES;
1253 goto RollbackWritePointersAndFreeTracker;
1254 }
1255
1256 SeenPointers = OrderedCollectionInit (PointerCompare, PointerCompare);
1257 if (SeenPointers == NULL) {
1258 Status = EFI_OUT_OF_RESOURCES;
1259 goto FreeKeys;
1260 }
1261
1262 //
1263 // second pass: identify and install ACPI tables
1264 //
1265 Installed = 0;
1266 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
1267 if (LoaderEntry->Type == QemuLoaderCmdAddPointer) {
1268 Status = Process2ndPassCmdAddPointer (
1269 &LoaderEntry->Command.AddPointer,
1270 Tracker,
1271 AcpiProtocol,
1272 InstalledKey,
1273 &Installed,
1274 SeenPointers
1275 );
1276 if (EFI_ERROR (Status)) {
1277 goto UninstallAcpiTables;
1278 }
1279 }
1280 }
1281
1282 //
1283 // Install a protocol to notify that the ACPI table provided by Qemu is
1284 // ready.
1285 //
1286 QemuAcpiHandle = NULL;
1287 Status = gBS->InstallProtocolInterface (
1288 &QemuAcpiHandle,
1289 &gQemuAcpiTableNotifyProtocolGuid,
1290 EFI_NATIVE_INTERFACE,
1291 NULL
1292 );
1293 if (EFI_ERROR (Status)) {
1294 goto UninstallAcpiTables;
1295 }
1296
1297 //
1298 // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3
1299 // Boot Script opcodes has to be the last operation in this function, because
1300 // if it succeeds, it cannot be undone.
1301 //
1302 if (S3Context != NULL) {
1303 Status = TransferS3ContextToBootScript (S3Context);
1304 if (EFI_ERROR (Status)) {
1305 goto UninstallQemuAcpiTableNotifyProtocol;
1306 }
1307
1308 //
1309 // Ownership of S3Context has been transferred.
1310 //
1311 S3Context = NULL;
1312 }
1313
1314 DEBUG ((DEBUG_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));
1315
1316 UninstallQemuAcpiTableNotifyProtocol:
1317 if (EFI_ERROR (Status)) {
1318 gBS->UninstallProtocolInterface (
1319 QemuAcpiHandle,
1320 &gQemuAcpiTableNotifyProtocolGuid,
1321 NULL
1322 );
1323 }
1324
1325 UninstallAcpiTables:
1326 if (EFI_ERROR (Status)) {
1327 //
1328 // roll back partial installation
1329 //
1330 while (Installed > 0) {
1331 --Installed;
1332 AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);
1333 }
1334 }
1335
1336 for (SeenPointerEntry = OrderedCollectionMin (SeenPointers);
1337 SeenPointerEntry != NULL;
1338 SeenPointerEntry = SeenPointerEntry2)
1339 {
1340 SeenPointerEntry2 = OrderedCollectionNext (SeenPointerEntry);
1341 OrderedCollectionDelete (SeenPointers, SeenPointerEntry, NULL);
1342 }
1343
1344 OrderedCollectionUninit (SeenPointers);
1345
1346 FreeKeys:
1347 FreePool (InstalledKey);
1348
1349 RollbackWritePointersAndFreeTracker:
1350 //
1351 // In case of failure, revoke any allocation addresses that were communicated
1352 // to QEMU previously, before we release all the blobs.
1353 //
1354 if (EFI_ERROR (Status)) {
1355 LoaderEntry = WritePointerSubsetEnd;
1356 while (LoaderEntry > LoaderStart) {
1357 --LoaderEntry;
1358 if (LoaderEntry->Type == QemuLoaderCmdWritePointer) {
1359 UndoCmdWritePointer (&LoaderEntry->Command.WritePointer);
1360 }
1361 }
1362 }
1363
1364 //
1365 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
1366 // place only if we're exiting with success and the blob hosts data that is
1367 // not directly part of some ACPI table.
1368 //
1369 for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL;
1370 TrackerEntry = TrackerEntry2)
1371 {
1372 VOID *UserStruct;
1373 BLOB *Blob;
1374
1375 TrackerEntry2 = OrderedCollectionNext (TrackerEntry);
1376 OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct);
1377 Blob = UserStruct;
1378
1379 if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) {
1380 DEBUG ((
1381 DEBUG_VERBOSE,
1382 "%a: freeing \"%a\"\n",
1383 __FUNCTION__,
1384 Blob->File
1385 ));
1386 gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size));
1387 }
1388
1389 FreePool (Blob);
1390 }
1391
1392 OrderedCollectionUninit (Tracker);
1393
1394 FreeS3Context:
1395 if (S3Context != NULL) {
1396 ReleaseS3Context (S3Context);
1397 }
1398
1399 FreeAllocationsRestrictedTo32Bit:
1400 ReleaseAllocationsRestrictedTo32Bit (AllocationsRestrictedTo32Bit);
1401
1402 FreeLoader:
1403 FreePool (LoaderStart);
1404
1405 return Status;
1406 }