Exemplo n.º 1
0
/**
  Returns a boolean indicating if the firmware configuration interface
  is available or not.

  This function may change fw_cfg state.

  @retval    TRUE   The interface is available
  @retval    FALSE  The interface is not available

**/
BOOLEAN
EFIAPI
QemuFwCfgIsAvailable (
  VOID
  )
{
  UINT32 Signature;
  UINT32 Revision;

  QemuFwCfgSelectItem (QemuFwCfgItemSignature);
  Signature = QemuFwCfgRead32 ();
  DEBUG ((EFI_D_INFO, "FW CFG Signature: 0x%x\n", Signature));
  QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
  Revision = QemuFwCfgRead32 ();
  DEBUG ((EFI_D_INFO, "FW CFG Revision: 0x%x\n", Revision));
  if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) ||
      (Revision < 1)
     ) {
    DEBUG ((EFI_D_INFO, "QemuFwCfg interface not supported.\n"));
    return FALSE;
  }

  DEBUG ((EFI_D_INFO, "QemuFwCfg interface is supported.\n"));
  return TRUE;
}
Exemplo n.º 2
0
RETURN_STATUS
EFIAPI
QemuFwCfgInitialize (
  VOID
  )
{
  UINT32 Signature;
  UINT32 Revision;

  //
  // Enable the access routines while probing to see if it is supported.
  //
  mQemuFwCfgSupported = TRUE;

  QemuFwCfgSelectItem (QemuFwCfgItemSignature);
  Signature = QemuFwCfgRead32 ();
  DEBUG ((EFI_D_INFO, "FW CFG Signature: 0x%x\n", Signature));
  QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
  Revision = QemuFwCfgRead32 ();
  DEBUG ((EFI_D_INFO, "FW CFG Revision: 0x%x\n", Revision));
  if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) ||
      (Revision < 1)
     ) {
    DEBUG ((EFI_D_INFO, "QemuFwCfg interface not supported.\n"));
    mQemuFwCfgSupported = FALSE;
    return RETURN_SUCCESS;
  }

  DEBUG ((EFI_D_INFO, "QemuFwCfg interface is supported.\n"));
  return RETURN_SUCCESS;
}
Exemplo n.º 3
0
RETURN_STATUS
EFIAPI
QemuFwCfgInitialize (
  VOID
  )
{
  UINT32 Signature;
  UINT32 Revision;

  //
  // Enable the access routines while probing to see if it is supported.
  // For probing we always use the IO Port (IoReadFifo8()) access method.
  //
  mQemuFwCfgSupported = TRUE;
  mQemuFwCfgDmaSupported = FALSE;

  QemuFwCfgSelectItem (QemuFwCfgItemSignature);
  Signature = QemuFwCfgRead32 ();
  DEBUG ((EFI_D_INFO, "FW CFG Signature: 0x%x\n", Signature));
  QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
  Revision = QemuFwCfgRead32 ();
  DEBUG ((EFI_D_INFO, "FW CFG Revision: 0x%x\n", Revision));
  if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) ||
      (Revision < 1)
     ) {
    DEBUG ((EFI_D_INFO, "QemuFwCfg interface not supported.\n"));
    mQemuFwCfgSupported = FALSE;
    return RETURN_SUCCESS;
  }

  if ((Revision & FW_CFG_F_DMA) == 0) {
    DEBUG ((DEBUG_INFO, "QemuFwCfg interface (IO Port) is supported.\n"));
  } else {
    mQemuFwCfgDmaSupported = TRUE;
    DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n"));
  }

  if (mQemuFwCfgDmaSupported && MemEncryptSevIsEnabled ()) {
    EFI_STATUS   Status;

    //
    // IoMmuDxe driver must have installed the IOMMU protocol. If we are not
    // able to locate the protocol then something must have gone wrong.
    //
    Status = gBS->LocateProtocol (&gEdkiiIoMmuProtocolGuid, NULL,
                    (VOID **)&mIoMmuProtocol);
    if (EFI_ERROR (Status)) {
      DEBUG ((DEBUG_ERROR,
        "QemuFwCfgSevDma %a:%a Failed to locate IOMMU protocol.\n",
        gEfiCallerBaseName, __FUNCTION__));
      ASSERT (FALSE);
      CpuDeadLoop ();
    }
  }

  return RETURN_SUCCESS;
}
Exemplo n.º 4
0
RETURN_STATUS
EFIAPI
QemuFwCfgInitialize (
  VOID
  )
{
  UINT32 Signature;
  UINT32 Revision;

  //
  // Enable the access routines while probing to see if it is supported.
  // For probing we always use the IO Port (IoReadFifo8()) access method.
  //
  mQemuFwCfgSupported = TRUE;
  mQemuFwCfgDmaSupported = FALSE;

  QemuFwCfgSelectItem (QemuFwCfgItemSignature);
  Signature = QemuFwCfgRead32 ();
  DEBUG ((EFI_D_INFO, "FW CFG Signature: 0x%x\n", Signature));
  QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
  Revision = QemuFwCfgRead32 ();
  DEBUG ((EFI_D_INFO, "FW CFG Revision: 0x%x\n", Revision));
  if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) ||
      (Revision < 1)
     ) {
    DEBUG ((EFI_D_INFO, "QemuFwCfg interface not supported.\n"));
    mQemuFwCfgSupported = FALSE;
    return RETURN_SUCCESS;
  }

  if ((Revision & FW_CFG_F_DMA) == 0) {
    DEBUG ((DEBUG_INFO, "QemuFwCfg interface (IO Port) is supported.\n"));
  } else {
    //
    // If SEV is enabled then we do not support DMA operations in PEI phase.
    // This is mainly because DMA in SEV guest requires using bounce buffer
    // (which need to allocate dynamic memory and allocating a PAGE size'd
    // buffer can be challenge in PEI phase)
    //
    if (MemEncryptSevIsEnabled ()) {
      DEBUG ((DEBUG_INFO, "SEV: QemuFwCfg fallback to IO Port interface.\n"));
    } else {
      mQemuFwCfgDmaSupported = TRUE;
      DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n"));
    }
  }
  return RETURN_SUCCESS;
}
Exemplo n.º 5
0
/**
  Locates and extracts the QEMU SMBIOS data if present in fw_cfg

  @return                 Address of extracted QEMU SMBIOS data

**/
UINT8 *
GetQemuSmbiosTables (
  VOID
  )
{
  EFI_STATUS               Status;
  FIRMWARE_CONFIG_ITEM     Tables;
  UINTN                    TablesSize;
  UINT8                    *QemuTables;

  if (!PcdGetBool (PcdQemuSmbiosValidated)) {
    return NULL;
  }

  Status = QemuFwCfgFindFile ("etc/smbios/smbios-tables", &Tables,
             &TablesSize);
  ASSERT_EFI_ERROR (Status);
  ASSERT (TablesSize > 0);

  QemuTables = AllocatePool (TablesSize);
  if (QemuTables == NULL) {
    return NULL;
  }

  QemuFwCfgSelectItem (Tables);
  QemuFwCfgReadBytes (TablesSize, QemuTables);

  return QemuTables;
}
Exemplo n.º 6
0
/**
  Fetch the number of boot CPUs from QEMU and expose it to UefiCpuPkg modules.
  Set the mMaxCpuCount variable.
**/
VOID
MaxCpuCountInitialization (
  VOID
  )
{
  UINT16        ProcessorCount;
  RETURN_STATUS PcdStatus;

  QemuFwCfgSelectItem (QemuFwCfgItemSmpCpuCount);
  ProcessorCount = QemuFwCfgRead16 ();
  //
  // If the fw_cfg key or fw_cfg entirely is unavailable, load mMaxCpuCount
  // from the PCD default. No change to PCDs.
  //
  if (ProcessorCount == 0) {
    mMaxCpuCount = PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
    return;
  }
  //
  // Otherwise, set mMaxCpuCount to the value reported by QEMU.
  //
  mMaxCpuCount = ProcessorCount;
  //
  // Additionally, tell UefiCpuPkg modules (a) the exact number of VCPUs, (b)
  // to wait, in the initial AP bringup, exactly as long as it takes for all of
  // the APs to report in. For this, we set the longest representable timeout
  // (approx. 71 minutes).
  //
  PcdStatus = PcdSet32S (PcdCpuMaxLogicalProcessorNumber, ProcessorCount);
  ASSERT_RETURN_ERROR (PcdStatus);
  PcdStatus = PcdSet32S (PcdCpuApInitTimeOutInMicroSeconds, MAX_UINT32);
  ASSERT_RETURN_ERROR (PcdStatus);
  DEBUG ((DEBUG_INFO, "%a: QEMU reports %d processor(s)\n", __FUNCTION__,
    ProcessorCount));
}
Exemplo n.º 7
0
VOID
InstallFeatureControlCallback (
  VOID
  )
{
  EFI_STATUS           Status;
  FIRMWARE_CONFIG_ITEM FwCfgItem;
  UINTN                FwCfgSize;

  Status = QemuFwCfgFindFile ("etc/msr_feature_control", &FwCfgItem,
             &FwCfgSize);
  if (EFI_ERROR (Status) || FwCfgSize != sizeof mFeatureControlValue) {
    //
    // Nothing to do.
    //
    return;
  }
  QemuFwCfgSelectItem (FwCfgItem);
  QemuFwCfgReadBytes (sizeof mFeatureControlValue, &mFeatureControlValue);

  Status = PeiServicesNotifyPpi (&mMpServicesNotify);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "%a: failed to set up MP Services callback: %r\n",
      __FUNCTION__, Status));
  }
}
Exemplo n.º 8
0
/**
  Locates and extracts the QEMU SMBIOS data if present in fw_cfg

  @return                 Address of extracted QEMU SMBIOS data

**/
UINT8 *
GetQemuSmbiosTables (
  VOID
  )
{
  SMBIOS_TABLE_ENTRY_POINT QemuAnchor;
  FIRMWARE_CONFIG_ITEM     Anchor, Tables;
  UINTN                    AnchorSize, TablesSize;
  UINT8                    *QemuTables;

  if (EFI_ERROR (QemuFwCfgFindFile (
                   "etc/smbios/smbios-anchor", &Anchor, &AnchorSize)) ||
      EFI_ERROR (QemuFwCfgFindFile (
                   "etc/smbios/smbios-tables", &Tables, &TablesSize)) ||
      AnchorSize != sizeof (QemuAnchor) ||
      TablesSize == 0) {
    return NULL;
  }

  //
  // We copy the entry point structure to perform some additional checks,
  // but discard it upon return.
  //
  QemuFwCfgSelectItem (Anchor);
  QemuFwCfgReadBytes (AnchorSize, &QemuAnchor);

  if (AsciiStrnCmp ((CHAR8 *)QemuAnchor.AnchorString, "_SM_", 4) ||
      AsciiStrnCmp ((CHAR8 *)QemuAnchor.IntermediateAnchorString, "_DMI_", 5) ||
      TablesSize != QemuAnchor.TableLength) {
    return NULL;
  }

  QemuTables = AllocatePool (TablesSize);
  if (QemuTables == NULL) {
    return NULL;
  }

  QemuFwCfgSelectItem (Tables);
  QemuFwCfgReadBytes (TablesSize, QemuTables);

  return QemuTables;
}
Exemplo n.º 9
0
EFI_STATUS
GetNamedFwCfgBoolean (
  IN  CHAR8   *FwCfgFileName,
  OUT BOOLEAN *Setting
  )
{
  EFI_STATUS           Status;
  FIRMWARE_CONFIG_ITEM FwCfgItem;
  UINTN                FwCfgSize;
  UINT8                Value[3];

  Status = QemuFwCfgFindFile (FwCfgFileName, &FwCfgItem, &FwCfgSize);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  if (FwCfgSize > sizeof Value) {
    return EFI_BAD_BUFFER_SIZE;
  }
  QemuFwCfgSelectItem (FwCfgItem);
  QemuFwCfgReadBytes (FwCfgSize, Value);

  if ((FwCfgSize == 1) ||
      (FwCfgSize == 2 && Value[1] == '\n') ||
      (FwCfgSize == 3 && Value[1] == '\r' && Value[2] == '\n')) {
    switch (Value[0]) {
      case '0':
      case 'n':
      case 'N':
        *Setting = FALSE;
        return EFI_SUCCESS;

      case '1':
      case 'y':
      case 'Y':
        *Setting = TRUE;
        return EFI_SUCCESS;

      default:
        break;
    }
  }
  return EFI_PROTOCOL_ERROR;
}
Exemplo n.º 10
0
/**

  Set the boot order based on configuration retrieved from QEMU.

  Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the
  OpenFirmware device paths therein to UEFI device path fragments. Match the
  translated fragments against BootOptionList, and rewrite the BootOrder NvVar
  so that it corresponds to the order described in fw_cfg.

  @param[in] BootOptionList  A boot option list, created with
                             BdsLibEnumerateAllBootOption ().


  @retval RETURN_SUCCESS            BootOrder NvVar rewritten.

  @retval RETURN_UNSUPPORTED        QEMU's fw_cfg is not supported.

  @retval RETURN_NOT_FOUND          Empty or nonexistent "bootorder" fw_cfg
                                    file, or no match found between the
                                    "bootorder" fw_cfg file and BootOptionList.

  @retval RETURN_INVALID_PARAMETER  Parse error in the "bootorder" fw_cfg file.

  @retval RETURN_OUT_OF_RESOURCES   Memory allocation failed.

  @return                           Values returned by gBS->LocateProtocol ()
                                    or gRT->SetVariable ().

**/
RETURN_STATUS
SetBootOrderFromQemu (
  IN  CONST LIST_ENTRY *BootOptionList
  )
{
  RETURN_STATUS                    Status;
  FIRMWARE_CONFIG_ITEM             FwCfgItem;
  UINTN                            FwCfgSize;
  CHAR8                            *FwCfg;
  CONST CHAR8                      *FwCfgPtr;

  BOOT_ORDER                       BootOrder;
  ACTIVE_OPTION                    *ActiveOption;
  UINTN                            ActiveCount;

  UINTN                            TranslatedSize;
  CHAR16                           Translated[TRANSLATION_OUTPUT_SIZE];

  Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);
  if (Status != RETURN_SUCCESS) {
    return Status;
  }

  if (FwCfgSize == 0) {
    return RETURN_NOT_FOUND;
  }

  FwCfg = AllocatePool (FwCfgSize);
  if (FwCfg == NULL) {
    return RETURN_OUT_OF_RESOURCES;
  }

  QemuFwCfgSelectItem (FwCfgItem);
  QemuFwCfgReadBytes (FwCfgSize, FwCfg);
  if (FwCfg[FwCfgSize - 1] != '\0') {
    Status = RETURN_INVALID_PARAMETER;
    goto ErrorFreeFwCfg;
  }

  DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__));
  DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));
  DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__));
  FwCfgPtr = FwCfg;

  BootOrder.Produced  = 0;
  BootOrder.Allocated = 1;
  BootOrder.Data = AllocatePool (
                     BootOrder.Allocated * sizeof (*BootOrder.Data)
                     );
  if (BootOrder.Data == NULL) {
    Status = RETURN_OUT_OF_RESOURCES;
    goto ErrorFreeFwCfg;
  }

  Status = CollectActiveOptions (BootOptionList, &ActiveOption, &ActiveCount);
  if (RETURN_ERROR (Status)) {
    goto ErrorFreeBootOrder;
  }

  //
  // translate each OpenFirmware path
  //
  TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);
  Status = TranslateOfwPath (&FwCfgPtr, Translated, &TranslatedSize);
  while (Status == RETURN_SUCCESS ||
         Status == RETURN_UNSUPPORTED ||
         Status == RETURN_BUFFER_TOO_SMALL) {
    if (Status == RETURN_SUCCESS) {
      UINTN Idx;

      //
      // match translated OpenFirmware path against all active boot options
      //
      for (Idx = 0; Idx < ActiveCount; ++Idx) {
        if (Match (
              Translated,
              TranslatedSize, // contains length, not size, in CHAR16's here
              ActiveOption[Idx].BootOption->DevicePath
              )
            ) {
          //
          // match found, store ID and continue with next OpenFirmware path
          //
          Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);
          if (Status != RETURN_SUCCESS) {
            goto ErrorFreeActiveOption;
          }
          break;
        }
      } // scanned all active boot options
    }   // translation successful

    TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);
    Status = TranslateOfwPath (&FwCfgPtr, Translated, &TranslatedSize);
  } // scanning of OpenFirmware paths done

  if (Status == RETURN_NOT_FOUND && BootOrder.Produced > 0) {
    //
    // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.
    // Some of the active boot options that have not been selected over fw_cfg
    // should be preserved at the end of the boot order.
    //
    Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);
    if (RETURN_ERROR (Status)) {
      goto ErrorFreeActiveOption;
    }

    //
    // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required
    // attributes.
    //
    Status = gRT->SetVariable (
                    L"BootOrder",
                    &gEfiGlobalVariableGuid,
                    EFI_VARIABLE_NON_VOLATILE |
                      EFI_VARIABLE_BOOTSERVICE_ACCESS |
                      EFI_VARIABLE_RUNTIME_ACCESS,
                    BootOrder.Produced * sizeof (*BootOrder.Data),
                    BootOrder.Data
                    );
    if (EFI_ERROR (Status)) {
      DEBUG ((DEBUG_ERROR, "%a: setting BootOrder: %r\n", __FUNCTION__, Status));
      goto ErrorFreeActiveOption;
    }

    DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __FUNCTION__));
    PruneBootVariables (ActiveOption, ActiveCount);
  }

ErrorFreeActiveOption:
  FreePool (ActiveOption);

ErrorFreeBootOrder:
  FreePool (BootOrder.Data);

ErrorFreeFwCfg:
  FreePool (FwCfg);

  return Status;
}
Exemplo n.º 11
0
/**
  Process a QEMU_LOADER_ALLOCATE command.

  @param[in] Allocate     The QEMU_LOADER_ALLOCATE command to process.

  @param[in,out] Tracker  The ORDERED_COLLECTION tracking the BLOB user
                          structures created thus far.

  @retval EFI_SUCCESS           An area of whole AcpiNVS pages has been
                                allocated for the blob contents, and the
                                contents have been saved. A BLOB object (user
                                structure) has been allocated from pool memory,
                                referencing the blob contents. The BLOB user
                                structure has been linked into Tracker.

  @retval EFI_PROTOCOL_ERROR    Malformed fw_cfg file name has been found in
                                Allocate, or the Allocate command references a
                                file that is already known by Tracker.

  @retval EFI_UNSUPPORTED       Unsupported alignment request has been found in
                                Allocate.

  @retval EFI_OUT_OF_RESOURCES  Pool allocation failed.

  @return                       Error codes from QemuFwCfgFindFile() and
                                gBS->AllocatePages().
**/
STATIC
EFI_STATUS
EFIAPI
ProcessCmdAllocate (
  IN CONST QEMU_LOADER_ALLOCATE *Allocate,
  IN OUT ORDERED_COLLECTION     *Tracker
  )
{
  FIRMWARE_CONFIG_ITEM FwCfgItem;
  UINTN                FwCfgSize;
  EFI_STATUS           Status;
  UINTN                NumPages;
  EFI_PHYSICAL_ADDRESS Address;
  BLOB                 *Blob;

  if (Allocate->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
    DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__));
    return EFI_PROTOCOL_ERROR;
  }

  if (Allocate->Alignment > EFI_PAGE_SIZE) {
    DEBUG ((EFI_D_ERROR, "%a: unsupported alignment 0x%x\n", __FUNCTION__,
      Allocate->Alignment));
    return EFI_UNSUPPORTED;
  }

  Status = QemuFwCfgFindFile ((CHAR8 *)Allocate->File, &FwCfgItem, &FwCfgSize);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__,
      Allocate->File, Status));
    return Status;
  }

  NumPages = EFI_SIZE_TO_PAGES (FwCfgSize);
  Address = 0xFFFFFFFF;
  Status = gBS->AllocatePages (AllocateMaxAddress, EfiACPIMemoryNVS, NumPages,
                  &Address);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Blob = AllocatePool (sizeof *Blob);
  if (Blob == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto FreePages;
  }
  CopyMem (Blob->File, Allocate->File, QEMU_LOADER_FNAME_SIZE);
  Blob->Size = FwCfgSize;
  Blob->Base = (VOID *)(UINTN)Address;
  Blob->HostsOnlyTableData = TRUE;

  Status = OrderedCollectionInsert (Tracker, NULL, Blob);
  if (Status == RETURN_ALREADY_STARTED) {
    DEBUG ((EFI_D_ERROR, "%a: duplicated file \"%a\"\n", __FUNCTION__,
      Allocate->File));
    Status = EFI_PROTOCOL_ERROR;
  }
  if (EFI_ERROR (Status)) {
    goto FreeBlob;
  }

  QemuFwCfgSelectItem (FwCfgItem);
  QemuFwCfgReadBytes (FwCfgSize, Blob->Base);
  ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size);

  DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "
    "Address=0x%Lx\n", __FUNCTION__, Allocate->File, Allocate->Alignment,
    Allocate->Zone, (UINT64)Blob->Size, (UINT64)(UINTN)Blob->Base));
  return EFI_SUCCESS;

FreeBlob:
  FreePool (Blob);

FreePages:
  gBS->FreePages (Address, NumPages);

  return Status;
}
Exemplo n.º 12
0
/**
  Download, process, and install ACPI table data from the QEMU loader
  interface.

  @param[in] AcpiProtocol  The ACPI table protocol used to install tables.

  @retval  EFI_UNSUPPORTED       Firmware configuration is unavailable, or QEMU
                                 loader command with unsupported parameters
                                 has been found.

  @retval  EFI_NOT_FOUND         The host doesn't export the required fw_cfg
                                 files.

  @retval  EFI_OUT_OF_RESOURCES  Memory allocation failed, or more than
                                 INSTALLED_TABLES_MAX tables found.

  @retval  EFI_PROTOCOL_ERROR    Found invalid fw_cfg contents.

  @return                        Status codes returned by
                                 AcpiProtocol->InstallAcpiTable().

**/
EFI_STATUS
EFIAPI
InstallQemuFwCfgTables (
  IN   EFI_ACPI_TABLE_PROTOCOL       *AcpiProtocol
  )
{
  EFI_STATUS               Status;
  FIRMWARE_CONFIG_ITEM     FwCfgItem;
  UINTN                    FwCfgSize;
  QEMU_LOADER_ENTRY        *LoaderStart;
  CONST QEMU_LOADER_ENTRY  *LoaderEntry, *LoaderEnd;
  ORIGINAL_ATTRIBUTES      *OriginalPciAttributes;
  UINTN                    OriginalPciAttributesCount;
  ORDERED_COLLECTION       *Tracker;
  UINTN                    *InstalledKey;
  INT32                    Installed;
  ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;

  Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  if (FwCfgSize % sizeof *LoaderEntry != 0) {
    DEBUG ((EFI_D_ERROR, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
      __FUNCTION__, (UINT64)FwCfgSize));
    return EFI_PROTOCOL_ERROR;
  }

  LoaderStart = AllocatePool (FwCfgSize);
  if (LoaderStart == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount);
  QemuFwCfgSelectItem (FwCfgItem);
  QemuFwCfgReadBytes (FwCfgSize, LoaderStart);
  RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount);
  LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry;

  Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare);
  if (Tracker == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto FreeLoader;
  }

  //
  // first pass: process the commands
  //
  for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
    switch (LoaderEntry->Type) {
    case QemuLoaderCmdAllocate:
      Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker);
      break;

    case QemuLoaderCmdAddPointer:
      Status = ProcessCmdAddPointer (&LoaderEntry->Command.AddPointer,
                 Tracker);
      break;

    case QemuLoaderCmdAddChecksum:
      Status = ProcessCmdAddChecksum (&LoaderEntry->Command.AddChecksum,
                 Tracker);
      break;

    default:
      DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n",
        __FUNCTION__, LoaderEntry->Type));
      break;
    }

    if (EFI_ERROR (Status)) {
      goto FreeTracker;
    }
  }

  InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);
  if (InstalledKey == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto FreeTracker;
  }

  //
  // second pass: identify and install ACPI tables
  //
  Installed = 0;
  for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
    if (LoaderEntry->Type == QemuLoaderCmdAddPointer) {
      Status = Process2ndPassCmdAddPointer (&LoaderEntry->Command.AddPointer,
                 Tracker, AcpiProtocol, InstalledKey, &Installed);
      if (EFI_ERROR (Status)) {
        break;
      }
    }
  }

  if (EFI_ERROR (Status)) {
    //
    // roll back partial installation
    //
    while (Installed > 0) {
      --Installed;
      AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);
    }
  } else {
    DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));
  }

  FreePool (InstalledKey);

FreeTracker:
  //
  // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
  // place only if we're exiting with success and the blob hosts data that is
  // not directly part of some ACPI table.
  //
  for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL;
       TrackerEntry = TrackerEntry2) {
    VOID *UserStruct;
    BLOB *Blob;

    TrackerEntry2 = OrderedCollectionNext (TrackerEntry);
    OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct);
    Blob = UserStruct;

    if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) {
      DEBUG ((EFI_D_VERBOSE, "%a: freeing \"%a\"\n", __FUNCTION__,
        Blob->File));
      gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size));
    }
    FreePool (Blob);
  }
  OrderedCollectionUninit (Tracker);

FreeLoader:
  FreePool (LoaderStart);

  return Status;
}
Exemplo n.º 13
0
/**
  Install default (fallback) table for SMBIOS Type 1.

  In case QEMU has provided no Type 1 SMBIOS table in whole, prepare one here,
  patch it with any referring saved patches, and install it.

  @param[in]     Smbios          The EFI_SMBIOS_PROTOCOL instance used for
                                 installing SMBIOS tables.
  @param[in]     ProducerHandle  Passed on to Smbios->Add(), ProducerHandle
                                 tracks the origin of installed SMBIOS tables.
  @param[in,out] Context         The BUILD_CONTEXT object tracking installed
                                 tables and saved patches.

  @retval EFI_SUCCESS  A Type 1 table has already been installed from the
                       SMBIOS firmware configuration blob.
  @retval EFI_SUCCESS  No Type 1 table was installed previously, and installing
                       the default here has succeeded.
  @return              Error codes from the PATCH_FORMATTED() and
                       PATCH_UNFORMATTED() macros, except EFI_NOT_FOUND, which
                       is only an informative result of theirs.
**/
EFI_STATUS
EFIAPI
InstallSmbiosType1 (
  IN     EFI_SMBIOS_PROTOCOL *Smbios,
  IN     EFI_HANDLE          ProducerHandle,
  IN OUT BUILD_CONTEXT       *Context
  )
{
  TABLE_CONTEXT     *Table;
  OVMF_TYPE1        OvmfType1;
  EFI_STATUS        Status;
  EFI_SMBIOS_HANDLE SmbiosHandle;

  Table = &Context->Table[1];
  if (Table->Installed) {
    return EFI_SUCCESS;
  }

  CopyMem (&OvmfType1, &mOvmfType1, sizeof OvmfType1);

  QemuFwCfgSelectItem (QemuFwCfgItemSystemUuid);
  OvmfType1.Base.Uuid.Data1 = SwapBytes32 (QemuFwCfgRead32 ());
  OvmfType1.Base.Uuid.Data2 = SwapBytes16 (QemuFwCfgRead16 ());
  OvmfType1.Base.Uuid.Data3 = SwapBytes16 (QemuFwCfgRead16 ());
  QemuFwCfgReadBytes (sizeof OvmfType1.Base.Uuid.Data4,
    &OvmfType1.Base.Uuid.Data4);

  //
  // Default contents ready. Formatted fields must be patched before installing
  // the table, while strings in the unformatted area will be patched
  // afterwards.
  //
  Status = PATCH_FORMATTED (Context, 1, &OvmfType1, Uuid);
  switch (Status) {
  case EFI_NOT_FOUND:
    break;
  case EFI_SUCCESS:
    OvmfType1.Base.Uuid.Data1 = SwapBytes32 (OvmfType1.Base.Uuid.Data1);
    OvmfType1.Base.Uuid.Data2 = SwapBytes16 (OvmfType1.Base.Uuid.Data2);
    OvmfType1.Base.Uuid.Data3 = SwapBytes16 (OvmfType1.Base.Uuid.Data3);
    break;
  default:
    return Status;
  }

  Status = PATCH_FORMATTED (Context, 1, &OvmfType1, WakeUpType);
  if (Status != EFI_NOT_FOUND && Status != EFI_SUCCESS) {
    return Status;
  }

  //
  // Install SMBIOS table with patched formatted area and default strings.
  //
  SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
  Status = Smbios->Add (Smbios, ProducerHandle, &SmbiosHandle,
                     (EFI_SMBIOS_TABLE_HEADER *) &OvmfType1);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "%a: Smbios->Add(): %r\n", __FUNCTION__, Status));
    return Status;
  }
  Table->Installed = TRUE;

  //
  // Patch strings in the unformatted area of the installed table.
  //
  Status = PATCH_UNFORMATTED (Smbios, SmbiosHandle, Context, 1, &OvmfType1,
             Manufacturer);
  if (Status != EFI_NOT_FOUND && Status != EFI_SUCCESS) {
    return Status;
  }
  Status = PATCH_UNFORMATTED (Smbios, SmbiosHandle, Context, 1, &OvmfType1,
             ProductName);
  if (Status != EFI_NOT_FOUND && Status != EFI_SUCCESS) {
    return Status;
  }
  Status = PATCH_UNFORMATTED (Smbios, SmbiosHandle, Context, 1, &OvmfType1,
             Version);
  if (Status != EFI_NOT_FOUND && Status != EFI_SUCCESS) {
    return Status;
  }
  Status = PATCH_UNFORMATTED (Smbios, SmbiosHandle, Context, 1, &OvmfType1,
             SerialNumber);
  if (Status != EFI_NOT_FOUND && Status != EFI_SUCCESS) {
    return Status;
  }
  Status = PATCH_UNFORMATTED (Smbios, SmbiosHandle, Context, 1, &OvmfType1,
             SKUNumber);
  if (Status != EFI_NOT_FOUND && Status != EFI_SUCCESS) {
    return Status;
  }
  Status = PATCH_UNFORMATTED (Smbios, SmbiosHandle, Context, 1, &OvmfType1,
             Family);
  if (Status != EFI_NOT_FOUND && Status != EFI_SUCCESS) {
    return Status;
  }
  return EFI_SUCCESS;
}
Exemplo n.º 14
0
RETURN_STATUS
EFIAPI
QemuFwCfgInitialize (
  VOID
  )
{
  EFI_STATUS                    Status;
  FDT_CLIENT_PROTOCOL           *FdtClient;
  CONST UINT64                  *Reg;
  UINT32                        RegSize;
  UINTN                         AddressCells, SizeCells;
  UINT64                        FwCfgSelectorAddress;
  UINT64                        FwCfgSelectorSize;
  UINT64                        FwCfgDataAddress;
  UINT64                        FwCfgDataSize;
  UINT64                        FwCfgDmaAddress;
  UINT64                        FwCfgDmaSize;

  Status = gBS->LocateProtocol (&gFdtClientProtocolGuid, NULL,
                  (VOID **)&FdtClient);
  ASSERT_EFI_ERROR (Status);

  Status = FdtClient->FindCompatibleNodeReg (FdtClient, "qemu,fw-cfg-mmio",
                         (CONST VOID **)&Reg, &AddressCells, &SizeCells,
                         &RegSize);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_WARN,
      "%a: No 'qemu,fw-cfg-mmio' compatible DT node found (Status == %r)\n",
      __FUNCTION__, Status));
    return EFI_SUCCESS;
  }

  ASSERT (AddressCells == 2);
  ASSERT (SizeCells == 2);
  ASSERT (RegSize == 2 * sizeof (UINT64));

  FwCfgDataAddress     = SwapBytes64 (Reg[0]);
  FwCfgDataSize        = 8;
  FwCfgSelectorAddress = FwCfgDataAddress + FwCfgDataSize;
  FwCfgSelectorSize    = 2;

  //
  // The following ASSERT()s express
  //
  //   Address + Size - 1 <= MAX_UINTN
  //
  // for both registers, that is, that the last byte in each MMIO range is
  // expressible as a MAX_UINTN. The form below is mathematically
  // equivalent, and it also prevents any unsigned overflow before the
  // comparison.
  //
  ASSERT (FwCfgSelectorAddress <= MAX_UINTN - FwCfgSelectorSize + 1);
  ASSERT (FwCfgDataAddress     <= MAX_UINTN - FwCfgDataSize     + 1);

  mFwCfgSelectorAddress = FwCfgSelectorAddress;
  mFwCfgDataAddress     = FwCfgDataAddress;

  DEBUG ((EFI_D_INFO, "Found FwCfg @ 0x%Lx/0x%Lx\n", FwCfgSelectorAddress,
    FwCfgDataAddress));

  if (SwapBytes64 (Reg[1]) >= 0x18) {
    FwCfgDmaAddress = FwCfgDataAddress + 0x10;
    FwCfgDmaSize    = 0x08;

    //
    // See explanation above.
    //
    ASSERT (FwCfgDmaAddress <= MAX_UINTN - FwCfgDmaSize + 1);

    DEBUG ((EFI_D_INFO, "Found FwCfg DMA @ 0x%Lx\n", FwCfgDmaAddress));
  } else {
    FwCfgDmaAddress = 0;
  }

  if (InternalQemuFwCfgIsAvailable ()) {
    UINT32 Signature;

    QemuFwCfgSelectItem (QemuFwCfgItemSignature);
    Signature = QemuFwCfgRead32 ();
    if (Signature == SIGNATURE_32 ('Q', 'E', 'M', 'U')) {
      //
      // For DMA support, we require the DTB to advertise the register, and the
      // feature bitmap (which we read without DMA) to confirm the feature.
      //
      if (FwCfgDmaAddress != 0) {
        UINT32 Features;

        QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
        Features = QemuFwCfgRead32 ();
        if ((Features & BIT1) != 0) {
          mFwCfgDmaAddress = FwCfgDmaAddress;
          InternalQemuFwCfgReadBytes = DmaReadBytes;
        }
      }
    } else {
      mFwCfgSelectorAddress = 0;
      mFwCfgDataAddress     = 0;
    }
  }
  return RETURN_SUCCESS;
}
Exemplo n.º 15
0
STATIC
EFI_STATUS
EFIAPI
QemuRamfbGraphicsOutputSetMode (
  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
  IN  UINT32                       ModeNumber
  )
{
  EFI_GRAPHICS_OUTPUT_MODE_INFORMATION  *ModeInfo;
  RAMFB_CONFIG                          Config;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         Black;
  RETURN_STATUS                         Status;

  if (ModeNumber >= mQemuRamfbMode.MaxMode) {
    return EFI_UNSUPPORTED;
  }
  ModeInfo = &mQemuRamfbModeInfo[ModeNumber];

  DEBUG ((DEBUG_INFO, "Ramfb: SetMode %u (%ux%u)\n", ModeNumber,
    ModeInfo->HorizontalResolution, ModeInfo->VerticalResolution));

  Config.Address = SwapBytes64 (mQemuRamfbMode.FrameBufferBase);
  Config.FourCC  = SwapBytes32 (RAMFB_FORMAT);
  Config.Flags   = SwapBytes32 (0);
  Config.Width   = SwapBytes32 (ModeInfo->HorizontalResolution);
  Config.Height  = SwapBytes32 (ModeInfo->VerticalResolution);
  Config.Stride  = SwapBytes32 (ModeInfo->HorizontalResolution * RAMFB_BPP);

  Status = FrameBufferBltConfigure (
             (VOID*)(UINTN)mQemuRamfbMode.FrameBufferBase,
             ModeInfo,
             mQemuRamfbFrameBufferBltConfigure,
             &mQemuRamfbFrameBufferBltConfigureSize
             );

  if (Status == RETURN_BUFFER_TOO_SMALL) {
    if (mQemuRamfbFrameBufferBltConfigure != NULL) {
      FreePool (mQemuRamfbFrameBufferBltConfigure);
    }
    mQemuRamfbFrameBufferBltConfigure =
      AllocatePool (mQemuRamfbFrameBufferBltConfigureSize);
    if (mQemuRamfbFrameBufferBltConfigure == NULL) {
      mQemuRamfbFrameBufferBltConfigureSize = 0;
      return EFI_OUT_OF_RESOURCES;
    }

    Status = FrameBufferBltConfigure (
               (VOID*)(UINTN)mQemuRamfbMode.FrameBufferBase,
               ModeInfo,
               mQemuRamfbFrameBufferBltConfigure,
               &mQemuRamfbFrameBufferBltConfigureSize
               );
  }
  if (RETURN_ERROR (Status)) {
    ASSERT (Status == RETURN_UNSUPPORTED);
    return Status;
  }

  mQemuRamfbMode.Mode = ModeNumber;
  mQemuRamfbMode.Info = ModeInfo;

  QemuFwCfgSelectItem (mRamfbFwCfgItem);
  QemuFwCfgWriteBytes (sizeof (Config), &Config);

  //
  // clear screen
  //
  ZeroMem (&Black, sizeof (Black));
  Status = FrameBufferBlt (
             mQemuRamfbFrameBufferBltConfigure,
             &Black,
             EfiBltVideoFill,
             0,                               // SourceX -- ignored
             0,                               // SourceY -- ignored
             0,                               // DestinationX
             0,                               // DestinationY
             ModeInfo->HorizontalResolution,  // Width
             ModeInfo->VerticalResolution,    // Height
             0                                // Delta -- ignored
             );
  if (RETURN_ERROR (Status)) {
    DEBUG ((DEBUG_WARN, "%a: clearing the screen failed: %r\n",
      __FUNCTION__, Status));
  }

  return EFI_SUCCESS;
}
Exemplo n.º 16
0
EFI_STATUS
TryRunningQemuKernel (
  VOID
  )
{
  EFI_STATUS                Status;
  UINTN                     KernelSize;
  UINTN                     KernelInitialSize;
  VOID                      *KernelBuf;
  UINTN                     SetupSize;
  VOID                      *SetupBuf;
  UINTN                     CommandLineSize;
  CHAR8                     *CommandLine;
  UINTN                     InitrdSize;
  VOID*                     InitrdData;

  SetupBuf = NULL;
  SetupSize = 0;
  KernelBuf = NULL;
  KernelInitialSize = 0;
  CommandLine = NULL;
  CommandLineSize = 0;
  InitrdData = NULL;
  InitrdSize = 0;

  if (!QemuFwCfgIsAvailable ()) {
    return EFI_NOT_FOUND;
  }

  QemuFwCfgSelectItem (QemuFwCfgItemKernelSize);
  KernelSize = (UINTN) QemuFwCfgRead64 ();

  QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize);
  SetupSize = (UINTN) QemuFwCfgRead64 ();

  if (KernelSize == 0 || SetupSize == 0) {
    DEBUG ((EFI_D_INFO, "qemu -kernel was not used.\n"));
    return EFI_NOT_FOUND;
  }

  SetupBuf = LoadLinuxAllocateKernelSetupPages (EFI_SIZE_TO_PAGES (SetupSize));
  if (SetupBuf == NULL) {
    DEBUG ((EFI_D_ERROR, "Unable to allocate memory for kernel setup!\n"));
    return EFI_OUT_OF_RESOURCES;
  }

  DEBUG ((EFI_D_INFO, "Setup size: 0x%x\n", (UINT32) SetupSize));
  DEBUG ((EFI_D_INFO, "Reading kernel setup image ..."));
  QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupData);
  QemuFwCfgReadBytes (SetupSize, SetupBuf);
  DEBUG ((EFI_D_INFO, " [done]\n"));

  Status = LoadLinuxCheckKernelSetup (SetupBuf, SetupSize);
  if (EFI_ERROR (Status)) {
    goto FreeAndReturn;
  }

  Status = LoadLinuxInitializeKernelSetup (SetupBuf);
  if (EFI_ERROR (Status)) {
    goto FreeAndReturn;
  }

  KernelInitialSize = LoadLinuxGetKernelSize (SetupBuf, KernelSize);
  if (KernelInitialSize == 0) {
    Status = EFI_UNSUPPORTED;
    goto FreeAndReturn;
  }

  KernelBuf = LoadLinuxAllocateKernelPages (
                SetupBuf,
                EFI_SIZE_TO_PAGES (KernelInitialSize));
  if (KernelBuf == NULL) {
    DEBUG ((EFI_D_ERROR, "Unable to allocate memory for kernel!\n"));
    Status = EFI_OUT_OF_RESOURCES;
    goto FreeAndReturn;
  }

  DEBUG ((EFI_D_INFO, "Kernel size: 0x%x\n", (UINT32) KernelSize));
  DEBUG ((EFI_D_INFO, "Reading kernel image ..."));
  QemuFwCfgSelectItem (QemuFwCfgItemKernelData);
  QemuFwCfgReadBytes (KernelSize, KernelBuf);
  DEBUG ((EFI_D_INFO, " [done]\n"));

  QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
  CommandLineSize = (UINTN) QemuFwCfgRead64 ();

  if (CommandLineSize > 0) {
    CommandLine = LoadLinuxAllocateCommandLinePages (
                    EFI_SIZE_TO_PAGES (CommandLineSize));
    QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
    QemuFwCfgReadBytes (CommandLineSize, CommandLine);
  } else {
    CommandLine = NULL;
  }

  Status = LoadLinuxSetCommandLine (SetupBuf, CommandLine);
  if (EFI_ERROR (Status)) {
    goto FreeAndReturn;
  }

  QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
  InitrdSize = (UINTN) QemuFwCfgRead64 ();

  if (InitrdSize > 0) {
    InitrdData = LoadLinuxAllocateInitrdPages (
                   SetupBuf,
                   EFI_SIZE_TO_PAGES (InitrdSize)
                   );
    DEBUG ((EFI_D_INFO, "Initrd size: 0x%x\n", (UINT32) InitrdSize));
    DEBUG ((EFI_D_INFO, "Reading initrd image ..."));
    QemuFwCfgSelectItem (QemuFwCfgItemInitrdData);
    QemuFwCfgReadBytes (InitrdSize, InitrdData);
    DEBUG ((EFI_D_INFO, " [done]\n"));
  } else {
    InitrdData = NULL;
  }

  Status = LoadLinuxSetInitrd (SetupBuf, InitrdData, InitrdSize);
  if (EFI_ERROR (Status)) {
    goto FreeAndReturn;
  }

  Status = LoadLinux (KernelBuf, SetupBuf);

FreeAndReturn:
  if (SetupBuf != NULL) {
    FreePages (SetupBuf, EFI_SIZE_TO_PAGES (SetupSize));
  }
  if (KernelBuf != NULL) {
    FreePages (KernelBuf, EFI_SIZE_TO_PAGES (KernelInitialSize));
  }
  if (CommandLine != NULL) {
    FreePages (CommandLine, EFI_SIZE_TO_PAGES (CommandLineSize));
  }
  if (InitrdData != NULL) {
    FreePages (InitrdData, EFI_SIZE_TO_PAGES (InitrdSize));
  }

  return Status;
}
Exemplo n.º 17
0
EFIAPI
PciHostBridgeGetRootBridges (
  UINTN *Count
  )
{
  EFI_STATUS           Status;
  FIRMWARE_CONFIG_ITEM FwCfgItem;
  UINTN                FwCfgSize;
  UINT64               ExtraRootBridges;
  PCI_ROOT_BRIDGE      *Bridges;
  UINTN                Initialized;
  UINTN                LastRootBridgeNumber;
  UINTN                RootBridgeNumber;

  *Count = 0;

  //
  // QEMU provides the number of extra root buses, shortening the exhaustive
  // search below. If there is no hint, the feature is missing.
  //
  Status = QemuFwCfgFindFile ("etc/extra-pci-roots", &FwCfgItem, &FwCfgSize);
  if (EFI_ERROR (Status) || FwCfgSize != sizeof ExtraRootBridges) {
    ExtraRootBridges = 0;
  } else {
    QemuFwCfgSelectItem (FwCfgItem);
    QemuFwCfgReadBytes (FwCfgSize, &ExtraRootBridges);

    if (ExtraRootBridges > PCI_MAX_BUS) {
      DEBUG ((EFI_D_ERROR, "%a: invalid count of extra root buses (%Lu) "
        "reported by QEMU\n", __FUNCTION__, ExtraRootBridges));
      return NULL;
    }
    DEBUG ((EFI_D_INFO, "%a: %Lu extra root buses reported by QEMU\n",
      __FUNCTION__, ExtraRootBridges));
  }

  //
  // Allocate the "main" root bridge, and any extra root bridges.
  //
  Bridges = AllocatePool ((1 + (UINTN)ExtraRootBridges) * sizeof *Bridges);
  if (Bridges == NULL) {
    DEBUG ((EFI_D_ERROR, "%a: %r\n", __FUNCTION__, EFI_OUT_OF_RESOURCES));
    return NULL;
  }
  Initialized = 0;

  //
  // The "main" root bus is always there.
  //
  LastRootBridgeNumber = 0;

  //
  // Scan all other root buses. If function 0 of any device on a bus returns a
  // VendorId register value different from all-bits-one, then that bus is
  // alive.
  //
  for (RootBridgeNumber = 1;
       RootBridgeNumber <= PCI_MAX_BUS && Initialized < ExtraRootBridges;
       ++RootBridgeNumber) {
    UINTN Device;

    for (Device = 0; Device <= PCI_MAX_DEVICE; ++Device) {
      if (PciRead16 (PCI_LIB_ADDRESS (RootBridgeNumber, Device, 0,
                       PCI_VENDOR_ID_OFFSET)) != MAX_UINT16) {
        break;
      }
    }
    if (Device <= PCI_MAX_DEVICE) {
      //
      // Found the next root bus. We can now install the *previous* one,
      // because now we know how big a bus number range *that* one has, for any
      // subordinate buses that might exist behind PCI bridges hanging off it.
      //
      Status = InitRootBridge ((UINT8)LastRootBridgeNumber,
                 (UINT8)(RootBridgeNumber - 1), &Bridges[Initialized]);
      if (EFI_ERROR (Status)) {
        goto FreeBridges;
      }
      ++Initialized;
      LastRootBridgeNumber = RootBridgeNumber;
    }
  }

  //
  // Install the last root bus (which might be the only, ie. main, root bus, if
  // we've found no extra root buses).
  //
  Status = InitRootBridge ((UINT8)LastRootBridgeNumber, PCI_MAX_BUS,
             &Bridges[Initialized]);
  if (EFI_ERROR (Status)) {
    goto FreeBridges;
  }
  ++Initialized;

  *Count = Initialized;
  return Bridges;

FreeBridges:
  while (Initialized > 0) {
    --Initialized;
    UninitRootBridge (&Bridges[Initialized]);
  }

  FreePool (Bridges);
  return NULL;
}