Пример #1
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;
}
Пример #2
0
/**
  Create a structure that maps the relative positions of PCI root buses to bus
  numbers.

  In the "bootorder" fw_cfg file, QEMU refers to extra PCI root buses by their
  positions, in relative root bus number order, not by their actual PCI bus
  numbers. The ACPI HID device path nodes however that are associated with
  PciRootBridgeIo protocol instances in the system have their UID fields set to
  the bus numbers. Create a map that gives, for each extra PCI root bus's
  position (ie. "serial number") its actual PCI bus number.

  @param[out] ExtraRootBusMap  The data structure implementing the map.

  @retval EFI_SUCCESS           ExtraRootBusMap has been populated.

  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.

  @retval EFI_ALREADY_STARTED   A duplicate root bus number has been found in
                                the system. (This should never happen.)

  @return                       Error codes returned by
                                gBS->LocateHandleBuffer() and
                                gBS->HandleProtocol().

**/
EFI_STATUS
CreateExtraRootBusMap (
  OUT EXTRA_ROOT_BUS_MAP **ExtraRootBusMap
  )
{
  EFI_STATUS               Status;
  UINTN                    NumHandles;
  EFI_HANDLE               *Handles;
  ORDERED_COLLECTION       *Collection;
  EXTRA_ROOT_BUS_MAP       *Map;
  UINTN                    Idx;
  ORDERED_COLLECTION_ENTRY *Entry, *Entry2;

  //
  // Handles and Collection are temporary / helper variables, while in Map we
  // build the return value.
  //

  Status = gBS->LocateHandleBuffer (ByProtocol,
                  &gEfiPciRootBridgeIoProtocolGuid, NULL /* SearchKey */,
                  &NumHandles, &Handles);
  if (EFI_ERROR (Status))  {
    return Status;
  }

  Collection = OrderedCollectionInit (RootBridgePathCompare,
                 RootBridgePathKeyCompare);
  if (Collection == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto FreeHandles;
  }

  Map = AllocateZeroPool (sizeof *Map);
  if (Map == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto FreeCollection;
  }

  //
  // Collect the ACPI device path protocols of the root bridges.
  //
  for (Idx = 0; Idx < NumHandles; ++Idx) {
    EFI_DEVICE_PATH_PROTOCOL *DevicePath;

    Status = gBS->HandleProtocol (Handles[Idx], &gEfiDevicePathProtocolGuid,
                    (VOID**)&DevicePath);
    if (EFI_ERROR (Status)) {
      goto FreeMap;
    }

    //
    // Examine if the device path is an ACPI HID one, and if so, if UID is
    // nonzero (ie. the root bridge that the bus number belongs to is "extra",
    // not the main one). In that case, link the device path into Collection.
    //
    if (DevicePathType (DevicePath) == ACPI_DEVICE_PATH &&
        DevicePathSubType (DevicePath) == ACPI_DP &&
        ((ACPI_HID_DEVICE_PATH *)DevicePath)->HID == EISA_PNP_ID(0x0A03) &&
        ((ACPI_HID_DEVICE_PATH *)DevicePath)->UID > 0) {
      Status = OrderedCollectionInsert (Collection, NULL, DevicePath);
      if (EFI_ERROR (Status)) {
        goto FreeMap;
      }
      ++Map->Count;
    }
  }

  if (Map->Count > 0) {
    //
    // At least one extra PCI root bus exists.
    //
    Map->BusNumbers = AllocatePool (Map->Count * sizeof *Map->BusNumbers);
    if (Map->BusNumbers == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto FreeMap;
    }
  }

  //
  // Now collect the bus numbers of the extra PCI root buses into Map.
  //
  Idx = 0;
  Entry = OrderedCollectionMin (Collection);
  while (Idx < Map->Count) {
    ACPI_HID_DEVICE_PATH *Acpi;

    ASSERT (Entry != NULL);
    Acpi = OrderedCollectionUserStruct (Entry);
    Map->BusNumbers[Idx] = Acpi->UID;
    DEBUG ((EFI_D_VERBOSE,
      "%a: extra bus position 0x%Lx maps to bus number (UID) 0x%x\n",
      __FUNCTION__, (UINT64)(Idx + 1), Acpi->UID));
    ++Idx;
    Entry = OrderedCollectionNext (Entry);
  }
  ASSERT (Entry == NULL);

  *ExtraRootBusMap = Map;
  Status = EFI_SUCCESS;

  //
  // Fall through in order to release temporaries.
  //

FreeMap:
  if (EFI_ERROR (Status)) {
    if (Map->BusNumbers != NULL) {
      FreePool (Map->BusNumbers);
    }
    FreePool (Map);
  }

FreeCollection:
  for (Entry = OrderedCollectionMin (Collection); Entry != NULL;
       Entry = Entry2) {
    Entry2 = OrderedCollectionNext (Entry);
    OrderedCollectionDelete (Collection, Entry, NULL);
  }
  OrderedCollectionUninit (Collection);

FreeHandles:
  FreePool (Handles);

  return Status;
}