VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/OvmfPkg/Library/AcpiPlatformLib/QemuFwCfgAcpi.c

Last change on this file was 101291, checked in by vboxsync, 19 months ago

EFI/FirmwareNew: Make edk2-stable202308 build on all supported platforms (using gcc at least, msvc not tested yet), bugref:4643

  • Property svn:eol-style set to native
File size: 43.1 KB
Line 
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/AcpiPlatformLib.h>
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()
22#include <Library/UefiBootServicesTableLib.h> // gBS
23#include <Library/TpmMeasurementLib.h>
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
40/**
41 Compare a standalone key against a user structure containing an embedded key.
42
43 @param[in] StandaloneKey Pointer to the bare key.
44
45 @param[in] UserStruct Pointer to the user structure with the embedded
46 key.
47
48 @retval <0 If StandaloneKey compares less than UserStruct's key.
49
50 @retval 0 If StandaloneKey compares equal to UserStruct's key.
51
52 @retval >0 If StandaloneKey compares greater than UserStruct's key.
53**/
54STATIC
55INTN
56EFIAPI
57BlobKeyCompare (
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
68/**
69 Comparator function for two user structures.
70
71 @param[in] UserStruct1 Pointer to the first user structure.
72
73 @param[in] UserStruct2 Pointer to the second user structure.
74
75 @retval <0 If UserStruct1 compares less than UserStruct2.
76
77 @retval 0 If UserStruct1 compares equal to UserStruct2.
78
79 @retval >0 If UserStruct1 compares greater than UserStruct2.
80**/
81STATIC
82INTN
83EFIAPI
84BlobCompare (
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
95/**
96 Comparator function for two opaque pointers, ordering on (unsigned) pointer
97 value itself.
98 Can be used as both Key and UserStruct comparator.
99
100 @param[in] Pointer1 First pointer.
101
102 @param[in] Pointer2 Second pointer.
103
104 @retval <0 If Pointer1 compares less than Pointer2.
105
106 @retval 0 If Pointer1 compares equal to Pointer2.
107
108 @retval >0 If Pointer1 compares greater than Pointer2.
109**/
110STATIC
111INTN
112EFIAPI
113PointerCompare (
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
129/**
130 Comparator function for two ASCII strings. Can be used as both Key and
131 UserStruct comparator.
132
133 This function exists solely so we can avoid casting &AsciiStrCmp to
134 ORDERED_COLLECTION_USER_COMPARE and ORDERED_COLLECTION_KEY_COMPARE.
135
136 @param[in] AsciiString1 Pointer to the first ASCII string.
137
138 @param[in] AsciiString2 Pointer to the second ASCII string.
139
140 @return The return value of AsciiStrCmp (AsciiString1, AsciiString2).
141**/
142STATIC
143INTN
144EFIAPI
145AsciiStringCompare (
146 IN CONST VOID *AsciiString1,
147 IN CONST VOID *AsciiString2
148 )
149{
150 return AsciiStrCmp (AsciiString1, AsciiString2);
151}
152
153/**
154 Release the ORDERED_COLLECTION structure populated by
155 CollectAllocationsRestrictedTo32Bit() (below).
156
157 This function may be called by CollectAllocationsRestrictedTo32Bit() itself,
158 on the error path.
159
160 @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure to
161 release.
162**/
163STATIC
164VOID
165ReleaseAllocationsRestrictedTo32Bit (
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
182/**
183 Iterate over the linker/loader script, and collect the names of the fw_cfg
184 blobs that are referenced by QEMU_LOADER_ADD_POINTER.PointeeFile fields, such
185 that QEMU_LOADER_ADD_POINTER.PointerSize is less than 8. This means that the
186 pointee blob's address will have to be patched into a narrower-than-8 byte
187 pointer field, hence the pointee blob must not be allocated from 64-bit
188 address space.
189
190 @param[out] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure
191 linking (not copying / owning) such
192 QEMU_LOADER_ADD_POINTER.PointeeFile
193 fields that name the blobs
194 restricted from 64-bit allocation.
195
196 @param[in] LoaderStart Points to the first entry in the
197 linker/loader script.
198
199 @param[in] LoaderEnd Points one past the last entry in
200 the linker/loader script.
201
202 @retval EFI_SUCCESS AllocationsRestrictedTo32Bit has been
203 populated.
204
205 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
206
207 @retval EFI_PROTOCOL_ERROR Invalid linker/loader script contents.
208**/
209STATIC
210EFI_STATUS
211CollectAllocationsRestrictedTo32Bit (
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
221 Collection = OrderedCollectionInit (AsciiStringCompare, AsciiStringCompare);
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:
275 ReleaseAllocationsRestrictedTo32Bit (Collection);
276 return Status;
277}
278
279/**
280 Process a QEMU_LOADER_ALLOCATE command.
281
282 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to
283 process.
284
285 @param[in,out] Tracker The ORDERED_COLLECTION tracking the
286 BLOB user structures created thus
287 far.
288
289 @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION populated by
290 the function
291 CollectAllocationsRestrictedTo32Bit,
292 naming the fw_cfg blobs that must
293 not be allocated from 64-bit address
294 space.
295
296 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been
297 allocated for the blob contents, and the
298 contents have been saved. A BLOB object (user
299 structure) has been allocated from pool memory,
300 referencing the blob contents. The BLOB user
301 structure has been linked into Tracker.
302
303 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
304 Allocate, or the Allocate command references a
305 file that is already known by Tracker.
306
307 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in
308 Allocate.
309
310 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.
311
312 @return Error codes from QemuFwCfgFindFile() and
313 gBS->AllocatePages().
314**/
315STATIC
316EFI_STATUS
317EFIAPI
318ProcessCmdAllocate (
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;
360 if (OrderedCollectionFind (
361 AllocationsRestrictedTo32Bit,
362 Allocate->File
363 ) != NULL)
364 {
365 Address = MAX_UINT32;
366 }
367
368 Status = gBS->AllocatePages (
369 AllocateMaxAddress,
370 EfiACPIMemoryNVS,
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 //
425 TpmMeasureAndLogData (
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
439FreePages:
440 gBS->FreePages (Address, NumPages);
441
442 return Status;
443}
444
445/**
446 Process a QEMU_LOADER_ADD_POINTER command.
447
448 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
449
450 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
451 structures created thus far.
452
453 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
454 AddPointer, or the AddPointer command references
455 a file unknown to Tracker, or the pointer to
456 relocate has invalid location, size, or value, or
457 the relocated pointer value is not representable
458 in the given pointer size.
459
460 @retval EFI_SUCCESS The pointer field inside the pointer blob has
461 been relocated.
462**/
463STATIC
464EFI_STATUS
465EFIAPI
466ProcessCmdAddPointer (
467 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,
468 IN CONST ORDERED_COLLECTION *Tracker
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
560/**
561 Process a QEMU_LOADER_ADD_CHECKSUM command.
562
563 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.
564
565 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
566 structures created thus far.
567
568 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
569 AddChecksum, or the AddChecksum command
570 references a file unknown to Tracker, or the
571 range to checksum is invalid.
572
573 @retval EFI_SUCCESS The requested range has been checksummed.
574**/
575STATIC
576EFI_STATUS
577EFIAPI
578ProcessCmdAddChecksum (
579 IN CONST QEMU_LOADER_ADD_CHECKSUM *AddChecksum,
580 IN CONST ORDERED_COLLECTION *Tracker
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
633/**
634 Process a QEMU_LOADER_WRITE_POINTER command.
635
636 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.
637
638 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
639 structures created thus far.
640
641 @param[in,out] S3Context The S3_CONTEXT object capturing the fw_cfg actions
642 of successfully processed QEMU_LOADER_WRITE_POINTER
643 commands, to be replayed at S3 resume. S3Context
644 may be NULL if S3 is disabled.
645
646 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
647 WritePointer. Or, the WritePointer command
648 references a file unknown to Tracker or the
649 fw_cfg directory. Or, the pointer object to
650 rewrite has invalid location, size, or initial
651 relative value. Or, the pointer value to store
652 does not fit in the given pointer size.
653
654 @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg
655 file has been written. If S3Context is not NULL,
656 then WritePointer has been condensed into
657 S3Context.
658
659 @return Error codes propagated from
660 SaveCondensedWritePointerToS3Context(). The
661 pointer object inside the writeable fw_cfg file
662 has not been written.
663**/
664STATIC
665EFI_STATUS
666ProcessCmdWritePointer (
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
751 SaveStatus = SaveCondensedWritePointerToS3Context (
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
788/**
789 Undo a QEMU_LOADER_WRITE_POINTER command.
790
791 This function revokes (zeroes out) a guest memory reference communicated to
792 QEMU earlier. The caller is responsible for invoking this function only on
793 such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed
794 by ProcessCmdWritePointer().
795
796 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
797**/
798STATIC
799VOID
800UndoCmdWritePointer (
801 IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer
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
837/**
838 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte
839 array is an ACPI table, and if so, install it.
840
841 This function assumes that the entire QEMU linker/loader command file has
842 been processed successfully in a prior first pass.
843
844 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
845
846 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
847 structures.
848
849 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
850
851 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
852 elements, allocated by the caller. On output,
853 the function will have stored (appended) the
854 AcpiProtocol-internal key of the ACPI table that
855 the function has installed, if the AddPointer
856 command identified an ACPI table that is
857 different from RSDT and XSDT.
858
859 @param[in,out] NumInstalled On input, the number of entries already used in
860 InstalledKey; it must be in [0,
861 INSTALLED_TABLES_MAX] inclusive. On output, the
862 parameter is incremented if the AddPointer
863 command identified an ACPI table that is
864 different from RSDT and XSDT.
865
866 @param[in,out] SeenPointers The ORDERED_COLLECTION tracking the absolute
867 target addresses that have been pointed-to by
868 QEMU_LOADER_ADD_POINTER commands thus far. If a
869 target address is encountered for the first
870 time, and it identifies an ACPI table that is
871 different from RDST and XSDT, the table is
872 installed. If a target address is seen for the
873 second or later times, it is skipped without
874 taking any action.
875
876 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
877 input.
878
879 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI
880 table different from RSDT and XSDT, but there
881 was no more room in InstalledKey.
882
883 @retval EFI_SUCCESS AddPointer has been processed. Either its
884 absolute target address has been encountered
885 before, or an ACPI table different from RSDT
886 and XSDT has been installed (reflected by
887 InstalledKey and NumInstalled), or RSDT or
888 XSDT has been identified but not installed, or
889 the fw_cfg blob pointed-into by AddPointer has
890 been marked as hosting something else than
891 just direct ACPI table contents.
892
893 @return Error codes returned by
894 AcpiProtocol->InstallAcpiTable().
895**/
896STATIC
897EFI_STATUS
898EFIAPI
899Process2ndPassCmdAddPointer (
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;
917 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
918 CONST EFI_ACPI_DESCRIPTION_HEADER *Header;
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) {
981 Facs = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)PointerValue;
982
983 if ((Facs->Length >= sizeof *Facs) &&
984 (Facs->Length <= Blob2Remaining) &&
985 (Facs->Signature ==
986 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_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 ==
1025 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) ||
1026 (Header->Signature ==
1027 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_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
1075/**
1076 Download, process, and install ACPI table data from the QEMU loader
1077 interface.
1078
1079 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
1080
1081 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU
1082 loader command with unsupported parameters
1083 has been found.
1084
1085 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg
1086 files.
1087
1088 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than
1089 INSTALLED_TABLES_MAX tables found.
1090
1091 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.
1092
1093 @return Status codes returned by
1094 AcpiProtocol->InstallAcpiTable().
1095
1096**/
1097EFI_STATUS
1098EFIAPI
1099InstallQemuFwCfgTables (
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 //
1151 TpmMeasureAndLogData (
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;
1163 Status = CollectAllocationsRestrictedTo32Bit (
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
1184 Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare);
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
1255 SeenPointers = OrderedCollectionInit (PointerCompare, PointerCompare);
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) {
1267 Status = Process2ndPassCmdAddPointer (
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,
1289 EFI_NATIVE_INTERFACE,
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}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette