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