/** 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; }
/** 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; }