Home | History | Annotate | Download | only in AcpiPlatformDxe
      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
     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
     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
    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
    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
    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
    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
    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