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 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15 **/
16
17 #include "AcpiPlatform.h"
18 #include "QemuLoader.h"
19 #include <Library/BaseMemoryLib.h>
20 #include <Library/MemoryAllocationLib.h>
21 #include <Library/QemuFwCfgLib.h>
22 #include <Library/DxeServicesTableLib.h>
23 #include <Library/PcdLib.h>
24 #include <Library/OrderedCollectionLib.h>
25 #include <IndustryStandard/Acpi.h>
26
27
28 //
29 // The user structure for the ordered collection that will track the fw_cfg
30 // blobs under processing.
31 //
32 typedef struct {
33 UINT8 File[QEMU_LOADER_FNAME_SIZE]; // NUL-terminated name of the fw_cfg
34 // blob. This is the ordering / search
35 // key.
36 UINTN Size; // The number of bytes in this blob.
37 UINT8 *Base; // Pointer to the blob data.
38 BOOLEAN HostsOnlyTableData; // TRUE iff the blob has been found to
39 // only contain data that is directly
40 // part of ACPI tables.
41 } BLOB;
42
43
44 /**
45 Compare a standalone key against a user structure containing an embedded key.
46
47 @param[in] StandaloneKey Pointer to the bare key.
48
49 @param[in] UserStruct Pointer to the user structure with the embedded
50 key.
51
52 @retval <0 If StandaloneKey compares less than UserStruct's key.
53
54 @retval 0 If StandaloneKey compares equal to UserStruct's key.
55
56 @retval >0 If StandaloneKey compares greater than UserStruct's key.
57 **/
58 STATIC
59 INTN
60 EFIAPI
BlobKeyCompare(IN CONST VOID * StandaloneKey,IN CONST VOID * UserStruct)61 BlobKeyCompare (
62 IN CONST VOID *StandaloneKey,
63 IN CONST VOID *UserStruct
64 )
65 {
66 CONST BLOB *Blob;
67
68 Blob = UserStruct;
69 return AsciiStrCmp (StandaloneKey, (CONST CHAR8 *)Blob->File);
70 }
71
72
73 /**
74 Comparator function for two user structures.
75
76 @param[in] UserStruct1 Pointer to the first user structure.
77
78 @param[in] UserStruct2 Pointer to the second user structure.
79
80 @retval <0 If UserStruct1 compares less than UserStruct2.
81
82 @retval 0 If UserStruct1 compares equal to UserStruct2.
83
84 @retval >0 If UserStruct1 compares greater than UserStruct2.
85 **/
86 STATIC
87 INTN
88 EFIAPI
BlobCompare(IN CONST VOID * UserStruct1,IN CONST VOID * UserStruct2)89 BlobCompare (
90 IN CONST VOID *UserStruct1,
91 IN CONST VOID *UserStruct2
92 )
93 {
94 CONST BLOB *Blob1;
95
96 Blob1 = UserStruct1;
97 return BlobKeyCompare (Blob1->File, UserStruct2);
98 }
99
100
101 /**
102 Process a QEMU_LOADER_ALLOCATE command.
103
104 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to process.
105
106 @param[in,out] Tracker The ORDERED_COLLECTION tracking the BLOB user
107 structures created thus far.
108
109 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been
110 allocated for the blob contents, and the
111 contents have been saved. A BLOB object (user
112 structure) has been allocated from pool memory,
113 referencing the blob contents. The BLOB user
114 structure has been linked into Tracker.
115
116 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
117 Allocate, or the Allocate command references a
118 file that is already known by Tracker.
119
120 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in
121 Allocate.
122
123 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.
124
125 @return Error codes from QemuFwCfgFindFile() and
126 gBS->AllocatePages().
127 **/
128 STATIC
129 EFI_STATUS
130 EFIAPI
ProcessCmdAllocate(IN CONST QEMU_LOADER_ALLOCATE * Allocate,IN OUT ORDERED_COLLECTION * Tracker)131 ProcessCmdAllocate (
132 IN CONST QEMU_LOADER_ALLOCATE *Allocate,
133 IN OUT ORDERED_COLLECTION *Tracker
134 )
135 {
136 FIRMWARE_CONFIG_ITEM FwCfgItem;
137 UINTN FwCfgSize;
138 EFI_STATUS Status;
139 UINTN NumPages;
140 EFI_PHYSICAL_ADDRESS Address;
141 BLOB *Blob;
142
143 if (Allocate->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
144 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__));
145 return EFI_PROTOCOL_ERROR;
146 }
147
148 if (Allocate->Alignment > EFI_PAGE_SIZE) {
149 DEBUG ((EFI_D_ERROR, "%a: unsupported alignment 0x%x\n", __FUNCTION__,
150 Allocate->Alignment));
151 return EFI_UNSUPPORTED;
152 }
153
154 Status = QemuFwCfgFindFile ((CHAR8 *)Allocate->File, &FwCfgItem, &FwCfgSize);
155 if (EFI_ERROR (Status)) {
156 DEBUG ((EFI_D_ERROR, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__,
157 Allocate->File, Status));
158 return Status;
159 }
160
161 NumPages = EFI_SIZE_TO_PAGES (FwCfgSize);
162 Address = 0xFFFFFFFF;
163 Status = gBS->AllocatePages (AllocateMaxAddress, EfiACPIMemoryNVS, NumPages,
164 &Address);
165 if (EFI_ERROR (Status)) {
166 return Status;
167 }
168
169 Blob = AllocatePool (sizeof *Blob);
170 if (Blob == NULL) {
171 Status = EFI_OUT_OF_RESOURCES;
172 goto FreePages;
173 }
174 CopyMem (Blob->File, Allocate->File, QEMU_LOADER_FNAME_SIZE);
175 Blob->Size = FwCfgSize;
176 Blob->Base = (VOID *)(UINTN)Address;
177 Blob->HostsOnlyTableData = TRUE;
178
179 Status = OrderedCollectionInsert (Tracker, NULL, Blob);
180 if (Status == RETURN_ALREADY_STARTED) {
181 DEBUG ((EFI_D_ERROR, "%a: duplicated file \"%a\"\n", __FUNCTION__,
182 Allocate->File));
183 Status = EFI_PROTOCOL_ERROR;
184 }
185 if (EFI_ERROR (Status)) {
186 goto FreeBlob;
187 }
188
189 QemuFwCfgSelectItem (FwCfgItem);
190 QemuFwCfgReadBytes (FwCfgSize, Blob->Base);
191 ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size);
192
193 DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "
194 "Address=0x%Lx\n", __FUNCTION__, Allocate->File, Allocate->Alignment,
195 Allocate->Zone, (UINT64)Blob->Size, (UINT64)(UINTN)Blob->Base));
196 return EFI_SUCCESS;
197
198 FreeBlob:
199 FreePool (Blob);
200
201 FreePages:
202 gBS->FreePages (Address, NumPages);
203
204 return Status;
205 }
206
207
208 /**
209 Process a QEMU_LOADER_ADD_POINTER command.
210
211 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
212
213 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
214 structures created thus far.
215
216 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
217 AddPointer, or the AddPointer command references
218 a file unknown to Tracker, or the pointer to
219 relocate has invalid location, size, or value, or
220 the relocated pointer value is not representable
221 in the given pointer size.
222
223 @retval EFI_SUCCESS The pointer field inside the pointer blob has
224 been relocated.
225 **/
226 STATIC
227 EFI_STATUS
228 EFIAPI
ProcessCmdAddPointer(IN CONST QEMU_LOADER_ADD_POINTER * AddPointer,IN CONST ORDERED_COLLECTION * Tracker)229 ProcessCmdAddPointer (
230 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,
231 IN CONST ORDERED_COLLECTION *Tracker
232 )
233 {
234 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;
235 BLOB *Blob, *Blob2;
236 UINT8 *PointerField;
237 UINT64 PointerValue;
238
239 if (AddPointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' ||
240 AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
241 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__));
242 return EFI_PROTOCOL_ERROR;
243 }
244
245 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);
246 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);
247 if (TrackerEntry == NULL || TrackerEntry2 == NULL) {
248 DEBUG ((EFI_D_ERROR, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",
249 __FUNCTION__, AddPointer->PointerFile, AddPointer->PointeeFile));
250 return EFI_PROTOCOL_ERROR;
251 }
252
253 Blob = OrderedCollectionUserStruct (TrackerEntry);
254 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);
255 if ((AddPointer->PointerSize != 1 && AddPointer->PointerSize != 2 &&
256 AddPointer->PointerSize != 4 && AddPointer->PointerSize != 8) ||
257 Blob->Size < AddPointer->PointerSize ||
258 Blob->Size - AddPointer->PointerSize < AddPointer->PointerOffset) {
259 DEBUG ((EFI_D_ERROR, "%a: invalid pointer location or size in \"%a\"\n",
260 __FUNCTION__, AddPointer->PointerFile));
261 return EFI_PROTOCOL_ERROR;
262 }
263
264 PointerField = Blob->Base + AddPointer->PointerOffset;
265 PointerValue = 0;
266 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);
267 if (PointerValue >= Blob2->Size) {
268 DEBUG ((EFI_D_ERROR, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__,
269 AddPointer->PointerFile));
270 return EFI_PROTOCOL_ERROR;
271 }
272
273 //
274 // The memory allocation system ensures that the address of the byte past the
275 // last byte of any allocated object is expressible (no wraparound).
276 //
277 ASSERT ((UINTN)Blob2->Base <= MAX_ADDRESS - Blob2->Size);
278
279 PointerValue += (UINT64)(UINTN)Blob2->Base;
280 if (RShiftU64 (
281 RShiftU64 (PointerValue, AddPointer->PointerSize * 8 - 1), 1) != 0) {
282 DEBUG ((EFI_D_ERROR, "%a: relocated pointer value unrepresentable in "
283 "\"%a\"\n", __FUNCTION__, AddPointer->PointerFile));
284 return EFI_PROTOCOL_ERROR;
285 }
286
287 CopyMem (PointerField, &PointerValue, AddPointer->PointerSize);
288
289 DEBUG ((EFI_D_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
290 "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__,
291 AddPointer->PointerFile, AddPointer->PointeeFile,
292 AddPointer->PointerOffset, AddPointer->PointerSize));
293 return EFI_SUCCESS;
294 }
295
296
297 /**
298 Process a QEMU_LOADER_ADD_CHECKSUM command.
299
300 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.
301
302 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
303 structures created thus far.
304
305 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
306 AddChecksum, or the AddChecksum command
307 references a file unknown to Tracker, or the
308 range to checksum is invalid.
309
310 @retval EFI_SUCCESS The requested range has been checksummed.
311 **/
312 STATIC
313 EFI_STATUS
314 EFIAPI
ProcessCmdAddChecksum(IN CONST QEMU_LOADER_ADD_CHECKSUM * AddChecksum,IN CONST ORDERED_COLLECTION * Tracker)315 ProcessCmdAddChecksum (
316 IN CONST QEMU_LOADER_ADD_CHECKSUM *AddChecksum,
317 IN CONST ORDERED_COLLECTION *Tracker
318 )
319 {
320 ORDERED_COLLECTION_ENTRY *TrackerEntry;
321 BLOB *Blob;
322
323 if (AddChecksum->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
324 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__));
325 return EFI_PROTOCOL_ERROR;
326 }
327
328 TrackerEntry = OrderedCollectionFind (Tracker, AddChecksum->File);
329 if (TrackerEntry == NULL) {
330 DEBUG ((EFI_D_ERROR, "%a: invalid blob reference \"%a\"\n", __FUNCTION__,
331 AddChecksum->File));
332 return EFI_PROTOCOL_ERROR;
333 }
334
335 Blob = OrderedCollectionUserStruct (TrackerEntry);
336 if (Blob->Size <= AddChecksum->ResultOffset ||
337 Blob->Size < AddChecksum->Length ||
338 Blob->Size - AddChecksum->Length < AddChecksum->Start) {
339 DEBUG ((EFI_D_ERROR, "%a: invalid checksum range in \"%a\"\n",
340 __FUNCTION__, AddChecksum->File));
341 return EFI_PROTOCOL_ERROR;
342 }
343
344 Blob->Base[AddChecksum->ResultOffset] = CalculateCheckSum8 (
345 Blob->Base + AddChecksum->Start,
346 AddChecksum->Length
347 );
348 DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "
349 "Length=0x%x\n", __FUNCTION__, AddChecksum->File,
350 AddChecksum->ResultOffset, AddChecksum->Start, AddChecksum->Length));
351 return EFI_SUCCESS;
352 }
353
354
355 //
356 // We'll be saving the keys of installed tables so that we can roll them back
357 // in case of failure. 128 tables should be enough for anyone (TM).
358 //
359 #define INSTALLED_TABLES_MAX 128
360
361 /**
362 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte
363 array is an ACPI table, and if so, install it.
364
365 This function assumes that the entire QEMU linker/loader command file has
366 been processed successfully in a prior first pass.
367
368 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
369
370 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
371 structures.
372
373 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
374
375 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
376 elements, allocated by the caller. On output,
377 the function will have stored (appended) the
378 AcpiProtocol-internal key of the ACPI table that
379 the function has installed, if the AddPointer
380 command identified an ACPI table that is
381 different from RSDT and XSDT.
382
383 @param[in,out] NumInstalled On input, the number of entries already used in
384 InstalledKey; it must be in [0,
385 INSTALLED_TABLES_MAX] inclusive. On output, the
386 parameter is incremented if the AddPointer
387 command identified an ACPI table that is
388 different from RSDT and XSDT.
389
390 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
391 input.
392
393 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI
394 table different from RSDT and XSDT, but there
395 was no more room in InstalledKey.
396
397 @retval EFI_SUCCESS AddPointer has been processed. Either an ACPI
398 table different from RSDT and XSDT has been
399 installed (reflected by InstalledKey and
400 NumInstalled), or RSDT or XSDT has been
401 identified but not installed, or the fw_cfg
402 blob pointed-into by AddPointer has been
403 marked as hosting something else than just
404 direct ACPI table contents.
405
406 @return Error codes returned by
407 AcpiProtocol->InstallAcpiTable().
408 **/
409 STATIC
410 EFI_STATUS
411 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)412 Process2ndPassCmdAddPointer (
413 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,
414 IN CONST ORDERED_COLLECTION *Tracker,
415 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
416 IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX],
417 IN OUT INT32 *NumInstalled
418 )
419 {
420 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry;
421 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry2;
422 CONST BLOB *Blob;
423 BLOB *Blob2;
424 CONST UINT8 *PointerField;
425 UINT64 PointerValue;
426 UINTN Blob2Remaining;
427 UINTN TableSize;
428 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
429 CONST EFI_ACPI_DESCRIPTION_HEADER *Header;
430 EFI_STATUS Status;
431
432 if (*NumInstalled < 0 || *NumInstalled > INSTALLED_TABLES_MAX) {
433 return EFI_INVALID_PARAMETER;
434 }
435
436 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);
437 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);
438 Blob = OrderedCollectionUserStruct (TrackerEntry);
439 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);
440 PointerField = Blob->Base + AddPointer->PointerOffset;
441 PointerValue = 0;
442 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);
443
444 //
445 // We assert that PointerValue falls inside Blob2's contents. This is ensured
446 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().
447 //
448 Blob2Remaining = (UINTN)Blob2->Base;
449 ASSERT(PointerValue >= Blob2Remaining);
450 Blob2Remaining += Blob2->Size;
451 ASSERT (PointerValue < Blob2Remaining);
452
453 Blob2Remaining -= (UINTN) PointerValue;
454 DEBUG ((EFI_D_VERBOSE, "%a: checking for ACPI header in \"%a\" at 0x%Lx "
455 "(remaining: 0x%Lx): ", __FUNCTION__, AddPointer->PointeeFile,
456 PointerValue, (UINT64)Blob2Remaining));
457
458 TableSize = 0;
459
460 //
461 // To make our job simple, the FACS has a custom header. Sigh.
462 //
463 if (sizeof *Facs <= Blob2Remaining) {
464 Facs = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)PointerValue;
465
466 if (Facs->Length >= sizeof *Facs &&
467 Facs->Length <= Blob2Remaining &&
468 Facs->Signature ==
469 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) {
470 DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n",
471 (CONST CHAR8 *)&Facs->Signature, Facs->Length));
472 TableSize = Facs->Length;
473 }
474 }
475
476 //
477 // check for the uniform tables
478 //
479 if (TableSize == 0 && sizeof *Header <= Blob2Remaining) {
480 Header = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)PointerValue;
481
482 if (Header->Length >= sizeof *Header &&
483 Header->Length <= Blob2Remaining &&
484 CalculateSum8 ((CONST UINT8 *)Header, Header->Length) == 0) {
485 //
486 // This looks very much like an ACPI table from QEMU:
487 // - Length field consistent with both ACPI and containing blob size
488 // - checksum is correct
489 //
490 DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n",
491 (CONST CHAR8 *)&Header->Signature, Header->Length));
492 TableSize = Header->Length;
493
494 //
495 // Skip RSDT and XSDT because those are handled by
496 // EFI_ACPI_TABLE_PROTOCOL automatically.
497 if (Header->Signature ==
498 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE ||
499 Header->Signature ==
500 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {
501 return EFI_SUCCESS;
502 }
503 }
504 }
505
506 if (TableSize == 0) {
507 DEBUG ((EFI_D_VERBOSE, "not found; marking fw_cfg blob as opaque\n"));
508 Blob2->HostsOnlyTableData = FALSE;
509 return EFI_SUCCESS;
510 }
511
512 if (*NumInstalled == INSTALLED_TABLES_MAX) {
513 DEBUG ((EFI_D_ERROR, "%a: can't install more than %d tables\n",
514 __FUNCTION__, INSTALLED_TABLES_MAX));
515 return EFI_OUT_OF_RESOURCES;
516 }
517
518 Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol,
519 (VOID *)(UINTN)PointerValue, TableSize,
520 &InstalledKey[*NumInstalled]);
521 if (EFI_ERROR (Status)) {
522 DEBUG ((EFI_D_ERROR, "%a: InstallAcpiTable(): %r\n", __FUNCTION__,
523 Status));
524 return Status;
525 }
526 ++*NumInstalled;
527 return EFI_SUCCESS;
528 }
529
530
531 /**
532 Download, process, and install ACPI table data from the QEMU loader
533 interface.
534
535 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
536
537 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU
538 loader command with unsupported parameters
539 has been found.
540
541 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg
542 files.
543
544 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than
545 INSTALLED_TABLES_MAX tables found.
546
547 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.
548
549 @return Status codes returned by
550 AcpiProtocol->InstallAcpiTable().
551
552 **/
553 EFI_STATUS
554 EFIAPI
InstallQemuFwCfgTables(IN EFI_ACPI_TABLE_PROTOCOL * AcpiProtocol)555 InstallQemuFwCfgTables (
556 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol
557 )
558 {
559 EFI_STATUS Status;
560 FIRMWARE_CONFIG_ITEM FwCfgItem;
561 UINTN FwCfgSize;
562 QEMU_LOADER_ENTRY *LoaderStart;
563 CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd;
564 ORIGINAL_ATTRIBUTES *OriginalPciAttributes;
565 UINTN OriginalPciAttributesCount;
566 ORDERED_COLLECTION *Tracker;
567 UINTN *InstalledKey;
568 INT32 Installed;
569 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;
570
571 Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize);
572 if (EFI_ERROR (Status)) {
573 return Status;
574 }
575 if (FwCfgSize % sizeof *LoaderEntry != 0) {
576 DEBUG ((EFI_D_ERROR, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
577 __FUNCTION__, (UINT64)FwCfgSize));
578 return EFI_PROTOCOL_ERROR;
579 }
580
581 LoaderStart = AllocatePool (FwCfgSize);
582 if (LoaderStart == NULL) {
583 return EFI_OUT_OF_RESOURCES;
584 }
585 EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount);
586 QemuFwCfgSelectItem (FwCfgItem);
587 QemuFwCfgReadBytes (FwCfgSize, LoaderStart);
588 RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount);
589 LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry;
590
591 Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare);
592 if (Tracker == NULL) {
593 Status = EFI_OUT_OF_RESOURCES;
594 goto FreeLoader;
595 }
596
597 //
598 // first pass: process the commands
599 //
600 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
601 switch (LoaderEntry->Type) {
602 case QemuLoaderCmdAllocate:
603 Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker);
604 break;
605
606 case QemuLoaderCmdAddPointer:
607 Status = ProcessCmdAddPointer (&LoaderEntry->Command.AddPointer,
608 Tracker);
609 break;
610
611 case QemuLoaderCmdAddChecksum:
612 Status = ProcessCmdAddChecksum (&LoaderEntry->Command.AddChecksum,
613 Tracker);
614 break;
615
616 default:
617 DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n",
618 __FUNCTION__, LoaderEntry->Type));
619 break;
620 }
621
622 if (EFI_ERROR (Status)) {
623 goto FreeTracker;
624 }
625 }
626
627 InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);
628 if (InstalledKey == NULL) {
629 Status = EFI_OUT_OF_RESOURCES;
630 goto FreeTracker;
631 }
632
633 //
634 // second pass: identify and install ACPI tables
635 //
636 Installed = 0;
637 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
638 if (LoaderEntry->Type == QemuLoaderCmdAddPointer) {
639 Status = Process2ndPassCmdAddPointer (&LoaderEntry->Command.AddPointer,
640 Tracker, AcpiProtocol, InstalledKey, &Installed);
641 if (EFI_ERROR (Status)) {
642 break;
643 }
644 }
645 }
646
647 if (EFI_ERROR (Status)) {
648 //
649 // roll back partial installation
650 //
651 while (Installed > 0) {
652 --Installed;
653 AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);
654 }
655 } else {
656 DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));
657 }
658
659 FreePool (InstalledKey);
660
661 FreeTracker:
662 //
663 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
664 // place only if we're exiting with success and the blob hosts data that is
665 // not directly part of some ACPI table.
666 //
667 for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL;
668 TrackerEntry = TrackerEntry2) {
669 VOID *UserStruct;
670 BLOB *Blob;
671
672 TrackerEntry2 = OrderedCollectionNext (TrackerEntry);
673 OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct);
674 Blob = UserStruct;
675
676 if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) {
677 DEBUG ((EFI_D_VERBOSE, "%a: freeing \"%a\"\n", __FUNCTION__,
678 Blob->File));
679 gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size));
680 }
681 FreePool (Blob);
682 }
683 OrderedCollectionUninit (Tracker);
684
685 FreeLoader:
686 FreePool (LoaderStart);
687
688 return Status;
689 }
690