Exemple #1
0
/* MADT */
static void
build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
{
    int madt_start = table_data->len;
    const MemMapEntry *memmap = guest_info->memmap;
    const int *irqmap = guest_info->irqmap;
    AcpiMultipleApicTable *madt;
    AcpiMadtGenericDistributor *gicd;
    AcpiMadtGenericMsiFrame *gic_msi;
    int i;

    madt = acpi_data_push(table_data, sizeof *madt);

    gicd = acpi_data_push(table_data, sizeof *gicd);
    gicd->type = ACPI_APIC_GENERIC_DISTRIBUTOR;
    gicd->length = sizeof(*gicd);
    gicd->base_address = memmap[VIRT_GIC_DIST].base;

    for (i = 0; i < guest_info->smp_cpus; i++) {
        AcpiMadtGenericInterrupt *gicc = acpi_data_push(table_data,
                                                     sizeof *gicc);
        ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i));

        gicc->type = ACPI_APIC_GENERIC_INTERRUPT;
        gicc->length = sizeof(*gicc);
        if (guest_info->gic_version == 2) {
            gicc->base_address = memmap[VIRT_GIC_CPU].base;
        }
        gicc->cpu_interface_number = i;
        gicc->arm_mpidr = armcpu->mp_affinity;
        gicc->uid = i;
        gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED);
    }

    if (guest_info->gic_version == 3) {
        AcpiMadtGenericRedistributor *gicr = acpi_data_push(table_data,
                                                         sizeof *gicr);

        gicr->type = ACPI_APIC_GENERIC_REDISTRIBUTOR;
        gicr->length = sizeof(*gicr);
        gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST].base);
        gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST].size);
    } else {
        gic_msi = acpi_data_push(table_data, sizeof *gic_msi);
        gic_msi->type = ACPI_APIC_GENERIC_MSI_FRAME;
        gic_msi->length = sizeof(*gic_msi);
        gic_msi->gic_msi_frame_id = 0;
        gic_msi->base_address = cpu_to_le64(memmap[VIRT_GIC_V2M].base);
        gic_msi->flags = cpu_to_le32(1);
        gic_msi->spi_count = cpu_to_le16(NUM_GICV2M_SPIS);
        gic_msi->spi_base = cpu_to_le16(irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE);
    }

    build_header(linker, table_data,
                 (void *)(table_data->data + madt_start), "APIC",
                 table_data->len - madt_start, 3, NULL, NULL);
}
/* FADT */
static void
build_fadt(GArray *table_data, GArray *linker, AcpiPmInfo *pm,
           unsigned facs, unsigned dsdt)
{
    AcpiFadtDescriptorRev1 *fadt = acpi_data_push(table_data, sizeof(*fadt));

    fadt->firmware_ctrl = cpu_to_le32(facs);
    /* FACS address to be filled by Guest linker */
    bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE,
                                   ACPI_BUILD_TABLE_FILE,
                                   table_data, &fadt->firmware_ctrl,
                                   sizeof fadt->firmware_ctrl);

    fadt->dsdt = cpu_to_le32(dsdt);
    /* DSDT address to be filled by Guest linker */
    bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE,
                                   ACPI_BUILD_TABLE_FILE,
                                   table_data, &fadt->dsdt,
                                   sizeof fadt->dsdt);

    fadt_setup(fadt, pm);

    build_header(linker, table_data,
                 (void *)fadt, ACPI_FACP_SIGNATURE, sizeof(*fadt), 1);
}
Exemple #3
0
/*
 * ACPI 6.0: 5.2.25.5 NVDIMM Control Region Structure.
 */
static void nvdimm_build_structure_dcr(GArray *structures, DeviceState *dev)
{
    NvdimmNfitControlRegion *nfit_dcr;
    int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
                                       NULL);
    uint32_t sn = nvdimm_slot_to_sn(slot);

    nfit_dcr = acpi_data_push(structures, sizeof(*nfit_dcr));

    nfit_dcr->type = cpu_to_le16(4 /* NVDIMM Control Region Structure */);
    nfit_dcr->length = cpu_to_le16(sizeof(*nfit_dcr));
    nfit_dcr->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot));

    /* vendor: Intel. */
    nfit_dcr->vendor_id = cpu_to_le16(0x8086);
    nfit_dcr->device_id = cpu_to_le16(1);

    /* The _DSM method is following Intel's DSM specification. */
    nfit_dcr->revision_id = cpu_to_le16(1 /* Current Revision supported
                                             in ACPI 6.0 is 1. */);
    nfit_dcr->serial_number = cpu_to_le32(sn);
    nfit_dcr->fic = cpu_to_le16(0x301 /* Format Interface Code:
                                         Byte addressable, no energy backed.
                                         See ACPI 6.2, sect 5.2.25.6 and
                                         JEDEC Annex L Release 3. */);
}
/* FACS */
static void
build_facs(GArray *table_data, GArray *linker, PcGuestInfo *guest_info)
{
    AcpiFacsDescriptorRev1 *facs = acpi_data_push(table_data, sizeof *facs);
    facs->signature = cpu_to_le32(ACPI_FACS_SIGNATURE);
    facs->length = cpu_to_le32(sizeof(*facs));
}
Exemple #5
0
/* RSDP */
static GArray *
build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
{
    AcpiRsdpDescriptor *rsdp = acpi_data_push(rsdp_table, sizeof *rsdp);

    bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, 16,
                             true /* fseg memory */);

    memcpy(&rsdp->signature, "RSD PTR ", sizeof(rsdp->signature));
    memcpy(rsdp->oem_id, ACPI_BUILD_APPNAME6, sizeof(rsdp->oem_id));
    rsdp->length = cpu_to_le32(sizeof(*rsdp));
    rsdp->revision = 0x02;

    /* Point to RSDT */
    rsdp->rsdt_physical_address = cpu_to_le32(rsdt);
    /* Address to be filled by Guest linker */
    bios_linker_loader_add_pointer(linker, ACPI_BUILD_RSDP_FILE,
                                   ACPI_BUILD_TABLE_FILE,
                                   rsdp_table, &rsdp->rsdt_physical_address,
                                   sizeof rsdp->rsdt_physical_address);
    rsdp->checksum = 0;
    /* Checksum to be filled by Guest linker */
    bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE,
                                    rsdp_table, rsdp, sizeof *rsdp,
                                    &rsdp->checksum);

    return rsdp_table;
}
Exemple #6
0
/* Build rsdt table */
void
build_rsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets,
           const char *oem_id, const char *oem_table_id)
{
    int i;
    unsigned rsdt_entries_offset;
    AcpiRsdtDescriptorRev1 *rsdt;
    const unsigned table_data_len = (sizeof(uint32_t) * table_offsets->len);
    const unsigned rsdt_entry_size = sizeof(rsdt->table_offset_entry[0]);
    const size_t rsdt_len = sizeof(*rsdt) + table_data_len;

    rsdt = acpi_data_push(table_data, rsdt_len);
    rsdt_entries_offset = (char *)rsdt->table_offset_entry - table_data->data;
    for (i = 0; i < table_offsets->len; ++i) {
        uint32_t ref_tbl_offset = g_array_index(table_offsets, uint32_t, i);
        uint32_t rsdt_entry_offset = rsdt_entries_offset + rsdt_entry_size * i;

        /* rsdt->table_offset_entry to be filled by Guest linker */
        bios_linker_loader_add_pointer(linker,
            ACPI_BUILD_TABLE_FILE, rsdt_entry_offset, rsdt_entry_size,
            ACPI_BUILD_TABLE_FILE, ref_tbl_offset);
    }
    build_header(linker, table_data,
                 (void *)rsdt, "RSDT", rsdt_len, 1, oem_id, oem_table_id);
}
static void
build_mcfg_q35(GArray *table_data, GArray *linker, AcpiMcfgInfo *info)
{
    AcpiTableMcfg *mcfg;
    uint32_t sig;
    int len = sizeof(*mcfg) + 1 * sizeof(mcfg->allocation[0]);

    mcfg = acpi_data_push(table_data, len);
    mcfg->allocation[0].address = cpu_to_le64(info->mcfg_base);
    /* Only a single allocation so no need to play with segments */
    mcfg->allocation[0].pci_segment = cpu_to_le16(0);
    mcfg->allocation[0].start_bus_number = 0;
    mcfg->allocation[0].end_bus_number = PCIE_MMCFG_BUS(info->mcfg_size - 1);

    /* MCFG is used for ECAM which can be enabled or disabled by guest.
     * To avoid table size changes (which create migration issues),
     * always create the table even if there are no allocations,
     * but set the signature to a reserved value in this case.
     * ACPI spec requires OSPMs to ignore such tables.
     */
    if (info->mcfg_base == PCIE_BASE_ADDR_UNMAPPED) {
        sig = ACPI_RSRV_SIGNATURE;
    } else {
        sig = ACPI_MCFG_SIGNATURE;
    }
    build_header(linker, table_data, (void *)mcfg, sig, len, 1);
}
Exemple #8
0
/* RSDP */
static GArray *
build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset)
{
    AcpiRsdpDescriptor *rsdp = acpi_data_push(rsdp_table, sizeof *rsdp);
    unsigned rsdt_pa_size = sizeof(rsdp->rsdt_physical_address);
    unsigned rsdt_pa_offset =
        (char *)&rsdp->rsdt_physical_address - rsdp_table->data;

    bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, rsdp_table, 16,
                             true /* fseg memory */);

    memcpy(&rsdp->signature, "RSD PTR ", sizeof(rsdp->signature));
    memcpy(rsdp->oem_id, ACPI_BUILD_APPNAME6, sizeof(rsdp->oem_id));
    rsdp->length = cpu_to_le32(sizeof(*rsdp));
    rsdp->revision = 0x02;

    /* Address to be filled by Guest linker */
    bios_linker_loader_add_pointer(linker,
        ACPI_BUILD_RSDP_FILE, rsdt_pa_offset, rsdt_pa_size,
        ACPI_BUILD_TABLE_FILE, rsdt_tbl_offset);

    /* Checksum to be filled by Guest linker */
    bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE,
        (char *)rsdp - rsdp_table->data, sizeof *rsdp,
        (char *)&rsdp->checksum - rsdp_table->data);

    return rsdp_table;
}
Exemple #9
0
static void
build_spcr(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
{
    AcpiSerialPortConsoleRedirection *spcr;
    const MemMapEntry *uart_memmap = &guest_info->memmap[VIRT_UART];
    int irq = guest_info->irqmap[VIRT_UART] + ARM_SPI_BASE;

    spcr = acpi_data_push(table_data, sizeof(*spcr));

    spcr->interface_type = 0x3;    /* ARM PL011 UART */

    spcr->base_address.space_id = AML_SYSTEM_MEMORY;
    spcr->base_address.bit_width = 8;
    spcr->base_address.bit_offset = 0;
    spcr->base_address.access_width = 1;
    spcr->base_address.address = cpu_to_le64(uart_memmap->base);

    spcr->interrupt_types = (1 << 3); /* Bit[3] ARMH GIC interrupt */
    spcr->gsi = cpu_to_le32(irq);  /* Global System Interrupt */

    spcr->baud = 3;                /* Baud Rate: 3 = 9600 */
    spcr->parity = 0;              /* No Parity */
    spcr->stopbits = 1;            /* 1 Stop bit */
    spcr->flowctrl = (1 << 1);     /* Bit[1] = RTS/CTS hardware flow control */
    spcr->term_type = 0;           /* Terminal Type: 0 = VT100 */

    spcr->pci_device_id = 0xffff;  /* PCI Device ID: not a PCI device */
    spcr->pci_vendor_id = 0xffff;  /* PCI Vendor ID: not a PCI device */

    build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2,
                 NULL, NULL);
}
Exemple #10
0
/*
 * ACPI 6.0: 5.2.25.2 Memory Device to System Physical Address Range Mapping
 * Structure
 */
static void
nvdimm_build_structure_memdev(GArray *structures, DeviceState *dev)
{
    NvdimmNfitMemDev *nfit_memdev;
    uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP,
                                            NULL);
    uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP,
                                            NULL);
    int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
                                       NULL);
    uint32_t handle = nvdimm_slot_to_handle(slot);

    nfit_memdev = acpi_data_push(structures, sizeof(*nfit_memdev));

    nfit_memdev->type = cpu_to_le16(1 /* Memory Device to System Address
                                         Range Map Structure*/);
    nfit_memdev->length = cpu_to_le16(sizeof(*nfit_memdev));
    nfit_memdev->nfit_handle = cpu_to_le32(handle);

    /*
     * associate memory device with System Physical Address Range
     * Structure.
     */
    nfit_memdev->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot));
    /* associate memory device with Control Region Structure. */
    nfit_memdev->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot));

    /* The memory region on the device. */
    nfit_memdev->region_len = cpu_to_le64(size);
    nfit_memdev->region_dpa = cpu_to_le64(addr);

    /* Only one interleave for PMEM. */
    nfit_memdev->interleave_ways = cpu_to_le16(1);
}
Exemple #11
0
static void
build_srat(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info)
{
    AcpiSystemResourceAffinityTable *srat;
    AcpiSratProcessorGiccAffinity *core;
    AcpiSratMemoryAffinity *numamem;
    int i, j, srat_start;
    uint64_t mem_base;
    uint32_t *cpu_node = g_malloc0(guest_info->smp_cpus * sizeof(uint32_t));

    for (i = 0; i < guest_info->smp_cpus; i++) {
        j = numa_get_node_for_cpu(i);
        if (j < nb_numa_nodes) {
                cpu_node[i] = j;
        }
    }

    srat_start = table_data->len;
    srat = acpi_data_push(table_data, sizeof(*srat));
    srat->reserved1 = cpu_to_le32(1);

    for (i = 0; i < guest_info->smp_cpus; ++i) {
        core = acpi_data_push(table_data, sizeof(*core));
        core->type = ACPI_SRAT_PROCESSOR_GICC;
        core->length = sizeof(*core);
        core->proximity = cpu_to_le32(cpu_node[i]);
        core->acpi_processor_uid = cpu_to_le32(i);
        core->flags = cpu_to_le32(1);
    }
    g_free(cpu_node);

    mem_base = guest_info->memmap[VIRT_MEM].base;
    for (i = 0; i < nb_numa_nodes; ++i) {
        numamem = acpi_data_push(table_data, sizeof(*numamem));
        build_srat_memory(numamem, mem_base, numa_info[i].node_mem, i,
                          MEM_AFFINITY_ENABLED);
        mem_base += numa_info[i].node_mem;
    }

    build_header(linker, table_data, (void *)srat, "SRAT",
                 table_data->len - srat_start, 3, NULL, NULL);
}
Exemple #12
0
/*
 * ACPI 6.2 Errata A: 5.2.25.9 NVDIMM Platform Capabilities Structure
 */
static void
nvdimm_build_structure_caps(GArray *structures, uint32_t capabilities)
{
    NvdimmNfitPlatformCaps *nfit_caps;

    nfit_caps = acpi_data_push(structures, sizeof(*nfit_caps));

    nfit_caps->type = cpu_to_le16(7 /* NVDIMM Platform Capabilities */);
    nfit_caps->length = cpu_to_le16(sizeof(*nfit_caps));
    nfit_caps->highest_cap = 31 - clz32(capabilities);
    nfit_caps->capabilities = cpu_to_le32(capabilities);
}
static void
build_dsdt(GArray *table_data, GArray *linker, AcpiMiscInfo *misc)
{
    AcpiTableHeader *dsdt;

    assert(misc->dsdt_code && misc->dsdt_size);

    dsdt = acpi_data_push(table_data, misc->dsdt_size);
    memcpy(dsdt, misc->dsdt_code, misc->dsdt_size);

    memset(dsdt, 0, sizeof *dsdt);
    build_header(linker, table_data, dsdt, ACPI_DSDT_SIGNATURE,
                 misc->dsdt_size, 1);
}
static void
build_hpet(GArray *table_data, GArray *linker)
{
    Acpi20Hpet *hpet;

    hpet = acpi_data_push(table_data, sizeof(*hpet));
    /* Note timer_block_id value must be kept in sync with value advertised by
     * emulated hpet
     */
    hpet->timer_block_id = cpu_to_le32(0x8086a201);
    hpet->addr.address = cpu_to_le64(HPET_BASE);
    build_header(linker, table_data,
                 (void *)hpet, ACPI_HPET_SIGNATURE, sizeof(*hpet), 1);
}
Exemple #15
0
static void nvdimm_build_nfit(GSList *device_list, GArray *table_offsets,
                              GArray *table_data, GArray *linker)
{
    GArray *structures = nvdimm_build_device_structure(device_list);
    void *header;

    acpi_add_table(table_offsets, table_data);

    /* NFIT header. */
    header = acpi_data_push(table_data, sizeof(NvdimmNfitHeader));
    /* NVDIMM device structures. */
    g_array_append_vals(table_data, structures->data, structures->len);

    build_header(linker, table_data, header, "NFIT",
                 sizeof(NvdimmNfitHeader) + structures->len, 1, NULL);
    g_array_free(structures, true);
}
Exemple #16
0
static void nvdimm_build_nfit(AcpiNVDIMMState *state, GArray *table_offsets,
                              GArray *table_data, BIOSLinker *linker)
{
    NvdimmFitBuffer *fit_buf = &state->fit_buf;
    unsigned int header;

    acpi_add_table(table_offsets, table_data);

    /* NFIT header. */
    header = table_data->len;
    acpi_data_push(table_data, sizeof(NvdimmNfitHeader));
    /* NVDIMM device structures. */
    g_array_append_vals(table_data, fit_buf->fit->data, fit_buf->fit->len);

    build_header(linker, table_data,
                 (void *)(table_data->data + header), "NFIT",
                 sizeof(NvdimmNfitHeader) + fit_buf->fit->len, 1, NULL, NULL);
}
Exemple #17
0
static void
build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
{
    AcpiTableMcfg *mcfg;
    const MemMapEntry *memmap = guest_info->memmap;
    int len = sizeof(*mcfg) + sizeof(mcfg->allocation[0]);

    mcfg = acpi_data_push(table_data, len);
    mcfg->allocation[0].address = cpu_to_le64(memmap[VIRT_PCIE_ECAM].base);

    /* Only a single allocation so no need to play with segments */
    mcfg->allocation[0].pci_segment = cpu_to_le16(0);
    mcfg->allocation[0].start_bus_number = 0;
    mcfg->allocation[0].end_bus_number = (memmap[VIRT_PCIE_ECAM].size
                                          / PCIE_MMCFG_SIZE_MIN) - 1;

    build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1, NULL, NULL);
}
Exemple #18
0
/* ACPI 6.0: 5.2.25.1 System Physical Address Range Structure */
static void
nvdimm_build_structure_spa(GArray *structures, DeviceState *dev)
{
    NvdimmNfitSpa *nfit_spa;
    uint64_t addr = object_property_get_uint(OBJECT(dev), PC_DIMM_ADDR_PROP,
                                             NULL);
    uint64_t size = object_property_get_uint(OBJECT(dev), PC_DIMM_SIZE_PROP,
                                             NULL);
    uint32_t node = object_property_get_uint(OBJECT(dev), PC_DIMM_NODE_PROP,
                                             NULL);
    int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
                                       NULL);

    nfit_spa = acpi_data_push(structures, sizeof(*nfit_spa));

    nfit_spa->type = cpu_to_le16(0 /* System Physical Address Range
                                      Structure */);
    nfit_spa->length = cpu_to_le16(sizeof(*nfit_spa));
    nfit_spa->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot));

    /*
     * Control region is strict as all the device info, such as SN, index,
     * is associated with slot id.
     */
    nfit_spa->flags = cpu_to_le16(1 /* Control region is strictly for
                                       management during hot add/online
                                       operation */ |
                                  2 /* Data in Proximity Domain field is
                                       valid*/);

    /* NUMA node. */
    nfit_spa->proximity_domain = cpu_to_le32(node);
    /* the region reported as PMEM. */
    memcpy(nfit_spa->type_guid, nvdimm_nfit_spa_uuid,
           sizeof(nvdimm_nfit_spa_uuid));

    nfit_spa->spa_base = cpu_to_le64(addr);
    nfit_spa->spa_length = cpu_to_le64(size);

    /* It is the PMEM and can be cached as writeback. */
    nfit_spa->mem_attr = cpu_to_le64(0x8ULL /* EFI_MEMORY_WB */ |
                                     0x8000ULL /* EFI_MEMORY_NV */);
}
Exemple #19
0
static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets,
                              GArray *table_data, GArray *linker)
{
    Aml *ssdt, *sb_scope, *dev;

    acpi_add_table(table_offsets, table_data);

    ssdt = init_aml_allocator();
    acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader));

    sb_scope = aml_scope("\\_SB");

    dev = aml_device("NVDR");

    /*
     * ACPI 6.0: 9.20 NVDIMM Devices:
     *
     * The ACPI Name Space device uses _HID of ACPI0012 to identify the root
     * NVDIMM interface device. Platform firmware is required to contain one
     * such device in _SB scope if NVDIMMs support is exposed by platform to
     * OSPM.
     * For each NVDIMM present or intended to be supported by platform,
     * platform firmware also exposes an ACPI Namespace Device under the
     * root device.
     */
    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0012")));

    nvdimm_build_common_dsm(dev);
    nvdimm_build_device_dsm(dev);

    nvdimm_build_nvdimm_devices(device_list, dev);

    aml_append(sb_scope, dev);

    aml_append(ssdt, sb_scope);
    /* copy AML table into ACPI tables blob and patch header there */
    g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
    build_header(linker, table_data,
                 (void *)(table_data->data + table_data->len - ssdt->buf->len),
                 "SSDT", ssdt->buf->len, 1, "NVDIMM");
    free_aml_allocator();
}
Exemple #20
0
/* FADT */
static void
build_fadt(GArray *table_data, BIOSLinker *linker, unsigned dsdt_tbl_offset)
{
    AcpiFadtDescriptorRev5_1 *fadt = acpi_data_push(table_data, sizeof(*fadt));
    unsigned dsdt_entry_offset = (char *)&fadt->dsdt - table_data->data;

    /* Hardware Reduced = 1 and use PSCI 0.2+ and with HVC */
    fadt->flags = cpu_to_le32(1 << ACPI_FADT_F_HW_REDUCED_ACPI);
    fadt->arm_boot_flags = cpu_to_le16((1 << ACPI_FADT_ARM_USE_PSCI_G_0_2) |
                                       (1 << ACPI_FADT_ARM_PSCI_USE_HVC));

    /* ACPI v5.1 (fadt->revision.fadt->minor_revision) */
    fadt->minor_revision = 0x1;

    /* DSDT address to be filled by Guest linker */
    bios_linker_loader_add_pointer(linker,
        ACPI_BUILD_TABLE_FILE, dsdt_entry_offset, sizeof(fadt->dsdt),
        ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);

    build_header(linker, table_data,
                 (void *)fadt, "FACP", sizeof(*fadt), 5, NULL, NULL);
}
/* Build final rsdt table */
static void
build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets)
{
    AcpiRsdtDescriptorRev1 *rsdt;
    size_t rsdt_len;
    int i;

    rsdt_len = sizeof(*rsdt) + sizeof(uint32_t) * table_offsets->len;
    rsdt = acpi_data_push(table_data, rsdt_len);
    memcpy(rsdt->table_offset_entry, table_offsets->data,
           sizeof(uint32_t) * table_offsets->len);
    for (i = 0; i < table_offsets->len; ++i) {
        /* rsdt->table_offset_entry to be filled by Guest linker */
        bios_linker_loader_add_pointer(linker,
                                       ACPI_BUILD_TABLE_FILE,
                                       ACPI_BUILD_TABLE_FILE,
                                       table_data, &rsdt->table_offset_entry[i],
                                       sizeof(uint32_t));
    }
    build_header(linker, table_data,
                 (void *)rsdt, ACPI_RSDT_SIGNATURE, rsdt_len, 1);
}
Exemple #22
0
/* DSDT */
static void
build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
{
    Aml *scope, *dsdt;
    const MemMapEntry *memmap = guest_info->memmap;
    const int *irqmap = guest_info->irqmap;

    dsdt = init_aml_allocator();
    /* Reserve space for header */
    acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader));

    /* When booting the VM with UEFI, UEFI takes ownership of the RTC hardware.
     * While UEFI can use libfdt to disable the RTC device node in the DTB that
     * it passes to the OS, it cannot modify AML. Therefore, we won't generate
     * the RTC ACPI device at all when using UEFI.
     */
    scope = aml_scope("\\_SB");
    acpi_dsdt_add_cpus(scope, guest_info->smp_cpus);
    acpi_dsdt_add_uart(scope, &memmap[VIRT_UART],
                       (irqmap[VIRT_UART] + ARM_SPI_BASE));
    acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]);
    acpi_dsdt_add_fw_cfg(scope, &memmap[VIRT_FW_CFG]);
    acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO],
                    (irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
    acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
                      guest_info->use_highmem);
    acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO],
                       (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
    acpi_dsdt_add_power_button(scope);

    aml_append(dsdt, scope);

    /* copy AML table into ACPI tables blob and patch header there */
    g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
    build_header(linker, table_data,
        (void *)(table_data->data + table_data->len - dsdt->buf->len),
        "DSDT", dsdt->buf->len, 2, NULL, NULL);
    free_aml_allocator();
}
static GArray *
build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
{
    AcpiRsdpDescriptor *rsdp = acpi_data_push(rsdp_table, sizeof *rsdp);

    bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, 1,
                             true /* fseg memory */);

    rsdp->signature = cpu_to_le64(ACPI_RSDP_SIGNATURE);
    memcpy(rsdp->oem_id, ACPI_BUILD_APPNAME6, 6);
    rsdp->rsdt_physical_address = cpu_to_le32(rsdt);
    /* Address to be filled by Guest linker */
    bios_linker_loader_add_pointer(linker, ACPI_BUILD_RSDP_FILE,
                                   ACPI_BUILD_TABLE_FILE,
                                   rsdp_table, &rsdp->rsdt_physical_address,
                                   sizeof rsdp->rsdt_physical_address);
    rsdp->checksum = 0;
    /* Checksum to be filled by Guest linker */
    bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE,
                                    rsdp, rsdp, sizeof *rsdp, &rsdp->checksum);

    return rsdp_table;
}
Exemple #24
0
/* Build rsdt table */
void
build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets,
           const char *oem_id, const char *oem_table_id)
{
    AcpiRsdtDescriptorRev1 *rsdt;
    size_t rsdt_len;
    int i;
    const int table_data_len = (sizeof(uint32_t) * table_offsets->len);

    rsdt_len = sizeof(*rsdt) + table_data_len;
    rsdt = acpi_data_push(table_data, rsdt_len);
    memcpy(rsdt->table_offset_entry, table_offsets->data, table_data_len);
    for (i = 0; i < table_offsets->len; ++i) {
        /* rsdt->table_offset_entry to be filled by Guest linker */
        bios_linker_loader_add_pointer(linker,
                                       ACPI_BUILD_TABLE_FILE,
                                       ACPI_BUILD_TABLE_FILE,
                                       table_data, &rsdt->table_offset_entry[i],
                                       sizeof(uint32_t));
    }
    build_header(linker, table_data,
                 (void *)rsdt, "RSDT", rsdt_len, 1, oem_id, oem_table_id);
}
Exemple #25
0
/* GTDT */
static void
build_gtdt(GArray *table_data, GArray *linker)
{
    int gtdt_start = table_data->len;
    AcpiGenericTimerTable *gtdt;

    gtdt = acpi_data_push(table_data, sizeof *gtdt);
    /* The interrupt values are the same with the device tree when adding 16 */
    gtdt->secure_el1_interrupt = ARCH_TIMER_S_EL1_IRQ + 16;
    gtdt->secure_el1_flags = ACPI_EDGE_SENSITIVE;

    gtdt->non_secure_el1_interrupt = ARCH_TIMER_NS_EL1_IRQ + 16;
    gtdt->non_secure_el1_flags = ACPI_EDGE_SENSITIVE | ACPI_GTDT_ALWAYS_ON;

    gtdt->virtual_timer_interrupt = ARCH_TIMER_VIRT_IRQ + 16;
    gtdt->virtual_timer_flags = ACPI_EDGE_SENSITIVE;

    gtdt->non_secure_el2_interrupt = ARCH_TIMER_NS_EL2_IRQ + 16;
    gtdt->non_secure_el2_flags = ACPI_EDGE_SENSITIVE;

    build_header(linker, table_data,
                 (void *)(table_data->data + gtdt_start), "GTDT",
                 table_data->len - gtdt_start, 2, NULL, NULL);
}
static void
build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu,
           PcGuestInfo *guest_info)
{
    int madt_start = table_data->len;

    AcpiMultipleApicTable *madt;
    AcpiMadtIoApic *io_apic;
    AcpiMadtIntsrcovr *intsrcovr;
    AcpiMadtLocalNmi *local_nmi;
    int i;

    madt = acpi_data_push(table_data, sizeof *madt);
    madt->local_apic_address = cpu_to_le32(APIC_DEFAULT_ADDRESS);
    madt->flags = cpu_to_le32(1);

    for (i = 0; i < guest_info->apic_id_limit; i++) {
        AcpiMadtProcessorApic *apic = acpi_data_push(table_data, sizeof *apic);
        apic->type = ACPI_APIC_PROCESSOR;
        apic->length = sizeof(*apic);
        apic->processor_id = i;
        apic->local_apic_id = i;
        if (test_bit(i, cpu->found_cpus)) {
            apic->flags = cpu_to_le32(1);
        } else {
            apic->flags = cpu_to_le32(0);
        }
    }
    io_apic = acpi_data_push(table_data, sizeof *io_apic);
    io_apic->type = ACPI_APIC_IO;
    io_apic->length = sizeof(*io_apic);
#define ACPI_BUILD_IOAPIC_ID 0x0
    io_apic->io_apic_id = ACPI_BUILD_IOAPIC_ID;
    io_apic->address = cpu_to_le32(IO_APIC_DEFAULT_ADDRESS);
    io_apic->interrupt = cpu_to_le32(0);

    if (guest_info->apic_xrupt_override) {
        intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr);
        intsrcovr->type   = ACPI_APIC_XRUPT_OVERRIDE;
        intsrcovr->length = sizeof(*intsrcovr);
        intsrcovr->source = 0;
        intsrcovr->gsi    = cpu_to_le32(2);
        intsrcovr->flags  = cpu_to_le16(0); /* conforms to bus specifications */
    }
    for (i = 1; i < 16; i++) {
#define ACPI_BUILD_PCI_IRQS ((1<<5) | (1<<9) | (1<<10) | (1<<11))
        if (!(ACPI_BUILD_PCI_IRQS & (1 << i))) {
            /* No need for a INT source override structure. */
            continue;
        }
        intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr);
        intsrcovr->type   = ACPI_APIC_XRUPT_OVERRIDE;
        intsrcovr->length = sizeof(*intsrcovr);
        intsrcovr->source = i;
        intsrcovr->gsi    = cpu_to_le32(i);
        intsrcovr->flags  = cpu_to_le16(0xd); /* active high, level triggered */
    }

    local_nmi = acpi_data_push(table_data, sizeof *local_nmi);
    local_nmi->type         = ACPI_APIC_LOCAL_NMI;
    local_nmi->length       = sizeof(*local_nmi);
    local_nmi->processor_id = 0xff; /* all processors */
    local_nmi->flags        = cpu_to_le16(0);
    local_nmi->lint         = 1; /* ACPI_LINT1 */

    build_header(linker, table_data,
                 (void *)(table_data->data + madt_start), ACPI_APIC_SIGNATURE,
                 table_data->len - madt_start, 1);
}
static void
build_srat(GArray *table_data, GArray *linker,
           AcpiCpuInfo *cpu, PcGuestInfo *guest_info)
{
    AcpiSystemResourceAffinityTable *srat;
    AcpiSratProcessorAffinity *core;
    AcpiSratMemoryAffinity *numamem;

    int i;
    uint64_t curnode;
    int srat_start, numa_start, slots;
    uint64_t mem_len, mem_base, next_base;

    srat_start = table_data->len;

    srat = acpi_data_push(table_data, sizeof *srat);
    srat->reserved1 = cpu_to_le32(1);
    core = (void *)(srat + 1);

    for (i = 0; i < guest_info->apic_id_limit; ++i) {
        core = acpi_data_push(table_data, sizeof *core);
        core->type = ACPI_SRAT_PROCESSOR;
        core->length = sizeof(*core);
        core->local_apic_id = i;
        curnode = guest_info->node_cpu[i];
        core->proximity_lo = curnode;
        memset(core->proximity_hi, 0, 3);
        core->local_sapic_eid = 0;
        if (test_bit(i, cpu->found_cpus)) {
            core->flags = cpu_to_le32(1);
        } else {
            core->flags = cpu_to_le32(0);
        }
    }


    /* the memory map is a bit tricky, it contains at least one hole
     * from 640k-1M and possibly another one from 3.5G-4G.
     */
    next_base = 0;
    numa_start = table_data->len;

    numamem = acpi_data_push(table_data, sizeof *numamem);
    acpi_build_srat_memory(numamem, 0, 640*1024, 0, 1);
    next_base = 1024 * 1024;
    for (i = 1; i < guest_info->numa_nodes + 1; ++i) {
        mem_base = next_base;
        mem_len = guest_info->node_mem[i - 1];
        if (i == 1) {
            mem_len -= 1024 * 1024;
        }
        next_base = mem_base + mem_len;

        /* Cut out the ACPI_PCI hole */
        if (mem_base <= guest_info->ram_size_below_4g &&
            next_base > guest_info->ram_size_below_4g) {
            mem_len -= next_base - guest_info->ram_size_below_4g;
            if (mem_len > 0) {
                numamem = acpi_data_push(table_data, sizeof *numamem);
                acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1);
            }
            mem_base = 1ULL << 32;
            mem_len = next_base - guest_info->ram_size_below_4g;
            next_base += (1ULL << 32) - guest_info->ram_size_below_4g;
        }
        numamem = acpi_data_push(table_data, sizeof *numamem);
        acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1, 1);
    }
    slots = (table_data->len - numa_start) / sizeof *numamem;
    for (; slots < guest_info->numa_nodes + 2; slots++) {
        numamem = acpi_data_push(table_data, sizeof *numamem);
        acpi_build_srat_memory(numamem, 0, 0, 0, 0);
    }

    build_header(linker, table_data,
                 (void *)(table_data->data + srat_start),
                 ACPI_SRAT_SIGNATURE,
                 table_data->len - srat_start, 1);
}
static void build_pci_bus_end(PCIBus *bus, void *bus_state)
{
    AcpiBuildPciBusHotplugState *child = bus_state;
    AcpiBuildPciBusHotplugState *parent = child->parent;
    GArray *bus_table = build_alloc_array();
    DECLARE_BITMAP(slot_hotplug_enable, PCI_SLOT_MAX);
    DECLARE_BITMAP(slot_device_present, PCI_SLOT_MAX);
    DECLARE_BITMAP(slot_device_system, PCI_SLOT_MAX);
    DECLARE_BITMAP(slot_device_vga, PCI_SLOT_MAX);
    DECLARE_BITMAP(slot_device_qxl, PCI_SLOT_MAX);
    uint8_t op;
    int i;
    QObject *bsel;
    GArray *method;
    bool bus_hotplug_support = false;

    if (bus->parent_dev) {
        op = 0x82; /* DeviceOp */
        build_append_nameseg(bus_table, "S%.02X_",
                             bus->parent_dev->devfn);
        build_append_byte(bus_table, 0x08); /* NameOp */
        build_append_nameseg(bus_table, "_SUN");
        build_append_value(bus_table, PCI_SLOT(bus->parent_dev->devfn), 1);
        build_append_byte(bus_table, 0x08); /* NameOp */
        build_append_nameseg(bus_table, "_ADR");
        build_append_value(bus_table, (PCI_SLOT(bus->parent_dev->devfn) << 16) |
                           PCI_FUNC(bus->parent_dev->devfn), 4);
    } else {
        op = 0x10; /* ScopeOp */;
        build_append_nameseg(bus_table, "PCI0");
    }

    bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL);
    if (bsel) {
        build_append_byte(bus_table, 0x08); /* NameOp */
        build_append_nameseg(bus_table, "BSEL");
        build_append_int(bus_table, qint_get_int(qobject_to_qint(bsel)));
        memset(slot_hotplug_enable, 0xff, sizeof slot_hotplug_enable);
    } else {
        /* No bsel - no slots are hot-pluggable */
        memset(slot_hotplug_enable, 0x00, sizeof slot_hotplug_enable);
    }

    memset(slot_device_present, 0x00, sizeof slot_device_present);
    memset(slot_device_system, 0x00, sizeof slot_device_present);
    memset(slot_device_vga, 0x00, sizeof slot_device_vga);
    memset(slot_device_qxl, 0x00, sizeof slot_device_qxl);

    for (i = 0; i < ARRAY_SIZE(bus->devices); i += PCI_FUNC_MAX) {
        DeviceClass *dc;
        PCIDeviceClass *pc;
        PCIDevice *pdev = bus->devices[i];
        int slot = PCI_SLOT(i);

        if (!pdev) {
            continue;
        }

        set_bit(slot, slot_device_present);
        pc = PCI_DEVICE_GET_CLASS(pdev);
        dc = DEVICE_GET_CLASS(pdev);

        if (pc->class_id == PCI_CLASS_BRIDGE_ISA) {
            set_bit(slot, slot_device_system);
        }

        if (pc->class_id == PCI_CLASS_DISPLAY_VGA) {
            set_bit(slot, slot_device_vga);

            if (object_dynamic_cast(OBJECT(pdev), "qxl-vga")) {
                set_bit(slot, slot_device_qxl);
            }
        }

        if (!dc->hotpluggable || pc->is_bridge) {
            clear_bit(slot, slot_hotplug_enable);
        }
    }

    /* Append Device object for each slot */
    for (i = 0; i < PCI_SLOT_MAX; i++) {
        bool can_eject = test_bit(i, slot_hotplug_enable);
        bool present = test_bit(i, slot_device_present);
        bool vga = test_bit(i, slot_device_vga);
        bool qxl = test_bit(i, slot_device_qxl);
        bool system = test_bit(i, slot_device_system);
        if (can_eject) {
            void *pcihp = acpi_data_push(bus_table,
                                         ACPI_PCIHP_SIZEOF);
            memcpy(pcihp, ACPI_PCIHP_AML, ACPI_PCIHP_SIZEOF);
            patch_pcihp(i, pcihp);
            bus_hotplug_support = true;
        } else if (qxl) {
            void *pcihp = acpi_data_push(bus_table,
                                         ACPI_PCIQXL_SIZEOF);
            memcpy(pcihp, ACPI_PCIQXL_AML, ACPI_PCIQXL_SIZEOF);
            patch_pciqxl(i, pcihp);
        } else if (vga) {
            void *pcihp = acpi_data_push(bus_table,
                                         ACPI_PCIVGA_SIZEOF);
            memcpy(pcihp, ACPI_PCIVGA_AML, ACPI_PCIVGA_SIZEOF);
            patch_pcivga(i, pcihp);
        } else if (system) {
            /* Nothing to do: system devices are in DSDT. */
        } else if (present) {
            void *pcihp = acpi_data_push(bus_table,
                                         ACPI_PCINOHP_SIZEOF);
            memcpy(pcihp, ACPI_PCINOHP_AML, ACPI_PCINOHP_SIZEOF);
            patch_pcinohp(i, pcihp);
        }
    }

    if (bsel) {
        method = build_alloc_method("DVNT", 2);

        for (i = 0; i < PCI_SLOT_MAX; i++) {
            GArray *notify;
            uint8_t op;

            if (!test_bit(i, slot_hotplug_enable)) {
                continue;
            }

            notify = build_alloc_array();
            op = 0xA0; /* IfOp */

            build_append_byte(notify, 0x7B); /* AndOp */
            build_append_byte(notify, 0x68); /* Arg0Op */
            build_append_int(notify, 0x1 << i);
            build_append_byte(notify, 0x00); /* NullName */
            build_append_byte(notify, 0x86); /* NotifyOp */
            build_append_nameseg(notify, "S%.02X_", PCI_DEVFN(i, 0));
            build_append_byte(notify, 0x69); /* Arg1Op */

            /* Pack it up */
            build_package(notify, op, 0);

            build_append_array(method, notify);

            build_free_array(notify);
        }

        build_append_and_cleanup_method(bus_table, method);
    }

    /* Append PCNT method to notify about events on local and child buses.
     * Add unconditionally for root since DSDT expects it.
     */
    if (bus_hotplug_support || child->notify_table->len || !bus->parent_dev) {
        method = build_alloc_method("PCNT", 0);

        /* If bus supports hotplug select it and notify about local events */
        if (bsel) {
            build_append_byte(method, 0x70); /* StoreOp */
            build_append_int(method, qint_get_int(qobject_to_qint(bsel)));
            build_append_nameseg(method, "BNUM");
            build_append_nameseg(method, "DVNT");
            build_append_nameseg(method, "PCIU");
            build_append_int(method, 1); /* Device Check */
            build_append_nameseg(method, "DVNT");
            build_append_nameseg(method, "PCID");
            build_append_int(method, 3); /* Eject Request */
        }

        /* Notify about child bus events in any case */
        build_append_array(method, child->notify_table);

        build_append_and_cleanup_method(bus_table, method);

        /* Append description of child buses */
        build_append_array(bus_table, child->device_table);

        /* Pack it up */
        if (bus->parent_dev) {
            build_extop_package(bus_table, op);
        } else {
            build_package(bus_table, op, 0);
        }

        /* Append our bus description to parent table */
        build_append_array(parent->device_table, bus_table);

        /* Also tell parent how to notify us, invoking PCNT method.
         * At the moment this is not needed for root as we have a single root.
         */
        if (bus->parent_dev) {
            build_append_byte(parent->notify_table, '^'); /* ParentPrefixChar */
            build_append_byte(parent->notify_table, 0x2E); /* DualNamePrefix */
            build_append_nameseg(parent->notify_table, "S%.02X_",
                                 bus->parent_dev->devfn);
            build_append_nameseg(parent->notify_table, "PCNT");
        }
    }

    build_free_array(bus_table);
    build_pci_bus_state_cleanup(child);
    g_free(child);
}
static void
build_ssdt(GArray *table_data, GArray *linker,
           AcpiCpuInfo *cpu, AcpiPmInfo *pm, AcpiMiscInfo *misc,
           PcPciInfo *pci, PcGuestInfo *guest_info)
{
    int acpi_cpus = MIN(0xff, guest_info->apic_id_limit);
    int ssdt_start = table_data->len;
    uint8_t *ssdt_ptr;
    int i;

    /* Copy header and patch values in the S3_ / S4_ / S5_ packages */
    ssdt_ptr = acpi_data_push(table_data, sizeof(ssdp_misc_aml));
    memcpy(ssdt_ptr, ssdp_misc_aml, sizeof(ssdp_misc_aml));
    if (pm->s3_disabled) {
        ssdt_ptr[acpi_s3_name[0]] = 'X';
    }
    if (pm->s4_disabled) {
        ssdt_ptr[acpi_s4_name[0]] = 'X';
    } else {
        ssdt_ptr[acpi_s4_pkg[0] + 1] = ssdt_ptr[acpi_s4_pkg[0] + 3] =
            pm->s4_val;
    }

    patch_pci_windows(pci, ssdt_ptr, sizeof(ssdp_misc_aml));

    *(uint16_t *)(ssdt_ptr + *ssdt_isa_pest) =
        cpu_to_le16(misc->pvpanic_port);

    {
        GArray *sb_scope = build_alloc_array();
        uint8_t op = 0x10; /* ScopeOp */

        build_append_nameseg(sb_scope, "_SB_");

        /* build Processor object for each processor */
        for (i = 0; i < acpi_cpus; i++) {
            uint8_t *proc = acpi_data_push(sb_scope, ACPI_PROC_SIZEOF);
            memcpy(proc, ACPI_PROC_AML, ACPI_PROC_SIZEOF);
            proc[ACPI_PROC_OFFSET_CPUHEX] = acpi_get_hex(i >> 4);
            proc[ACPI_PROC_OFFSET_CPUHEX+1] = acpi_get_hex(i);
            proc[ACPI_PROC_OFFSET_CPUID1] = i;
            proc[ACPI_PROC_OFFSET_CPUID2] = i;
        }

        /* build this code:
         *   Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}
         */
        /* Arg0 = Processor ID = APIC ID */
        build_append_notify_method(sb_scope, "NTFY", "CP%0.02X", acpi_cpus);

        /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" */
        build_append_byte(sb_scope, 0x08); /* NameOp */
        build_append_nameseg(sb_scope, "CPON");

        {
            GArray *package = build_alloc_array();
            uint8_t op = 0x12; /* PackageOp */

            build_append_byte(package, acpi_cpus); /* NumElements */
            for (i = 0; i < acpi_cpus; i++) {
                uint8_t b = test_bit(i, cpu->found_cpus) ? 0x01 : 0x00;
                build_append_byte(package, b);
            }

            build_package(package, op, 2);
            build_append_array(sb_scope, package);
            build_free_array(package);
        }

        {
            AcpiBuildPciBusHotplugState hotplug_state;
            Object *pci_host;
            PCIBus *bus = NULL;
            bool ambiguous;

            pci_host = object_resolve_path_type("", TYPE_PCI_HOST_BRIDGE, &ambiguous);
            if (!ambiguous && pci_host) {
                bus = PCI_HOST_BRIDGE(pci_host)->bus;
            }

            build_pci_bus_state_init(&hotplug_state, NULL);

            if (bus) {
                /* Scan all PCI buses. Generate tables to support hotplug. */
                pci_for_each_bus_depth_first(bus, build_pci_bus_begin,
                                             build_pci_bus_end, &hotplug_state);
            }

            build_append_array(sb_scope, hotplug_state.device_table);
            build_pci_bus_state_cleanup(&hotplug_state);
        }

        build_package(sb_scope, op, 3);
        build_append_array(table_data, sb_scope);
        build_free_array(sb_scope);
    }

    build_header(linker, table_data,
                 (void *)(table_data->data + ssdt_start),
                 ACPI_SSDT_SIGNATURE, table_data->len - ssdt_start, 1);
}