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 successfuly 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   ORDERED_COLLECTION       *Tracker;
565   UINTN                    *InstalledKey;
566   INT32                    Installed;
567   ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;
568 
569   Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize);
570   if (EFI_ERROR (Status)) {
571     return Status;
572   }
573   if (FwCfgSize % sizeof *LoaderEntry != 0) {
574     DEBUG ((EFI_D_ERROR, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
575       __FUNCTION__, (UINT64)FwCfgSize));
576     return EFI_PROTOCOL_ERROR;
577   }
578 
579   LoaderStart = AllocatePool (FwCfgSize);
580   if (LoaderStart == NULL) {
581     return EFI_OUT_OF_RESOURCES;
582   }
583   QemuFwCfgSelectItem (FwCfgItem);
584   QemuFwCfgReadBytes (FwCfgSize, LoaderStart);
585   LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry;
586 
587   Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare);
588   if (Tracker == NULL) {
589     Status = EFI_OUT_OF_RESOURCES;
590     goto FreeLoader;
591   }
592 
593   //
594   // first pass: process the commands
595   //
596   for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
597     switch (LoaderEntry->Type) {
598     case QemuLoaderCmdAllocate:
599       Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker);
600       break;
601 
602     case QemuLoaderCmdAddPointer:
603       Status = ProcessCmdAddPointer (&LoaderEntry->Command.AddPointer,
604                  Tracker);
605       break;
606 
607     case QemuLoaderCmdAddChecksum:
608       Status = ProcessCmdAddChecksum (&LoaderEntry->Command.AddChecksum,
609                  Tracker);
610       break;
611 
612     default:
613       DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n",
614         __FUNCTION__, LoaderEntry->Type));
615       break;
616     }
617 
618     if (EFI_ERROR (Status)) {
619       goto FreeTracker;
620     }
621   }
622 
623   InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);
624   if (InstalledKey == NULL) {
625     Status = EFI_OUT_OF_RESOURCES;
626     goto FreeTracker;
627   }
628 
629   //
630   // second pass: identify and install ACPI tables
631   //
632   Installed = 0;
633   for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
634     if (LoaderEntry->Type == QemuLoaderCmdAddPointer) {
635       Status = Process2ndPassCmdAddPointer (&LoaderEntry->Command.AddPointer,
636                  Tracker, AcpiProtocol, InstalledKey, &Installed);
637       if (EFI_ERROR (Status)) {
638         break;
639       }
640     }
641   }
642 
643   if (EFI_ERROR (Status)) {
644     //
645     // roll back partial installation
646     //
647     while (Installed > 0) {
648       --Installed;
649       AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);
650     }
651   } else {
652     DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));
653   }
654 
655   FreePool (InstalledKey);
656 
657 FreeTracker:
658   //
659   // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
660   // place only if we're exiting with success and the blob hosts data that is
661   // not directly part of some ACPI table.
662   //
663   for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL;
664        TrackerEntry = TrackerEntry2) {
665     VOID *UserStruct;
666     BLOB *Blob;
667 
668     TrackerEntry2 = OrderedCollectionNext (TrackerEntry);
669     OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct);
670     Blob = UserStruct;
671 
672     if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) {
673       DEBUG ((EFI_D_VERBOSE, "%a: freeing \"%a\"\n", __FUNCTION__,
674         Blob->File));
675       gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size));
676     }
677     FreePool (Blob);
678   }
679   OrderedCollectionUninit (Tracker);
680 
681 FreeLoader:
682   FreePool (LoaderStart);
683 
684   return Status;
685 }
686