diff options
Diffstat (limited to 'hw')
40 files changed, 989 insertions, 718 deletions
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index d5a8b9c017..f365140319 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -387,7 +387,7 @@ build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) 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, 5); + build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1); } /* GTDT */ @@ -413,7 +413,7 @@ build_gtdt(GArray *table_data, GArray *linker) build_header(linker, table_data, (void *)(table_data->data + gtdt_start), "GTDT", - table_data->len - gtdt_start, 5); + table_data->len - gtdt_start, 2); } /* MADT */ @@ -423,8 +423,10 @@ 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); @@ -448,9 +450,18 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info, gicd->length = sizeof(*gicd); gicd->base_address = memmap[VIRT_GIC_DIST].base; + 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, 5); + table_data->len - madt_start, 3); } /* FADT */ @@ -507,7 +518,7 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) 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, 5); + "DSDT", dsdt->buf->len, 2); free_aml_allocator(); } @@ -545,6 +556,7 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) * FADT * GTDT * MADT + * MCFG * DSDT */ @@ -552,7 +564,7 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) dsdt = tables_blob->len; build_dsdt(tables_blob, tables->linker, guest_info); - /* FADT MADT GTDT SPCR pointed to by RSDT */ + /* FADT MADT GTDT MCFG SPCR pointed to by RSDT */ acpi_add_table(table_offsets, tables_blob); build_fadt(tables_blob, tables->linker, dsdt); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 4e78083a9d..484689264c 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -956,6 +956,8 @@ static void virt_class_init(ObjectClass *oc, void *data) mc->init = machvirt_init; mc->max_cpus = 8; mc->has_dynamic_sysbus = true; + mc->block_default_type = IF_VIRTIO; + mc->no_cdrom = 1; } static const TypeInfo machvirt_info = { diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index aa794ca445..921e799dbb 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -35,15 +35,15 @@ static void get_pointer(Object *obj, Visitor *v, Property *prop, } static void set_pointer(Object *obj, Visitor *v, Property *prop, - int (*parse)(DeviceState *dev, const char *str, - void **ptr), + void (*parse)(DeviceState *dev, const char *str, + void **ptr, const char *propname, + Error **errp), const char *name, Error **errp) { DeviceState *dev = DEVICE(obj); Error *local_err = NULL; void **ptr = qdev_get_prop_ptr(dev, prop); char *str; - int ret; if (dev->realized) { qdev_prop_set_after_realize(dev, name, errp); @@ -60,26 +60,38 @@ static void set_pointer(Object *obj, Visitor *v, Property *prop, *ptr = NULL; return; } - ret = parse(dev, str, ptr); - error_set_from_qdev_prop_error(errp, ret, dev, prop, str); + parse(dev, str, ptr, prop->name, errp); g_free(str); } /* --- drive --- */ -static int parse_drive(DeviceState *dev, const char *str, void **ptr) +static void parse_drive(DeviceState *dev, const char *str, void **ptr, + const char *propname, Error **errp) { BlockBackend *blk; blk = blk_by_name(str); if (!blk) { - return -ENOENT; + error_setg(errp, "Property '%s.%s' can't find value '%s'", + object_get_typename(OBJECT(dev)), propname, str); + return; } if (blk_attach_dev(blk, dev) < 0) { - return -EEXIST; + DriveInfo *dinfo = blk_legacy_dinfo(blk); + + if (dinfo->type != IF_NONE) { + error_setg(errp, "Drive '%s' is already in use because " + "it has been automatically connected to another " + "device (did you need 'if=none' in the drive options?)", + str); + } else { + error_setg(errp, "Drive '%s' is already in use by another device", + str); + } + return; } *ptr = blk; - return 0; } static void release_drive(Object *obj, const char *name, void *opaque) @@ -121,17 +133,21 @@ PropertyInfo qdev_prop_drive = { /* --- character device --- */ -static int parse_chr(DeviceState *dev, const char *str, void **ptr) +static void parse_chr(DeviceState *dev, const char *str, void **ptr, + const char *propname, Error **errp) { CharDriverState *chr = qemu_chr_find(str); if (chr == NULL) { - return -ENOENT; + error_setg(errp, "Property '%s.%s' can't find value '%s'", + object_get_typename(OBJECT(dev)), propname, str); + return; } if (qemu_chr_fe_claim(chr) != 0) { - return -EEXIST; + error_setg(errp, "Property '%s.%s' can't take value '%s', it's in use", + object_get_typename(OBJECT(dev)), propname, str); + return; } *ptr = chr; - return 0; } static void release_chr(Object *obj, const char *name, void *opaque) diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 47c1e8f3c5..e9e686f260 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -131,7 +131,7 @@ PropertyInfo qdev_prop_bit = { static uint64_t qdev_get_prop_mask64(Property *prop) { assert(prop->info == &qdev_prop_bit); - return 0x1 << prop->bitnr; + return 0x1ull << prop->bitnr; } static void bit64_prop_set(DeviceState *dev, Property *props, bool val) diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index 92eced9424..278a2d1bdd 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -281,6 +281,9 @@ static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent) static char *sysbus_get_fw_dev_path(DeviceState *dev) { SysBusDevice *s = SYS_BUS_DEVICE(dev); + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_GET_CLASS(s); + /* for the explicit unit address fallback case: */ + char *addr, *fw_dev_path; if (s->num_mmio) { return g_strdup_printf("%s@" TARGET_FMT_plx, qdev_fw_name(dev), @@ -289,6 +292,14 @@ static char *sysbus_get_fw_dev_path(DeviceState *dev) if (s->num_pio) { return g_strdup_printf("%s@i%04x", qdev_fw_name(dev), s->pio[0]); } + if (sbc->explicit_ofw_unit_address) { + addr = sbc->explicit_ofw_unit_address(s); + if (addr) { + fw_dev_path = g_strdup_printf("%s@%s", qdev_fw_name(dev), addr); + g_free(addr); + return fw_dev_path; + } + } return g_strdup(qdev_fw_name(dev)); } diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index 0be5d97c59..bd4f147f9d 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -8,8 +8,7 @@ obj-$(CONFIG_XEN) += ../xenpv/ xen/ obj-y += kvmvapic.o obj-y += acpi-build.o hw/i386/acpi-build.o: hw/i386/acpi-build.c \ - hw/i386/acpi-dsdt.hex hw/i386/q35-acpi-dsdt.hex \ - hw/i386/ssdt-tpm.hex hw/i386/ssdt-tpm2.hex + hw/i386/acpi-dsdt.hex hw/i386/q35-acpi-dsdt.hex iasl-option=$(shell if test -z "`$(1) $(2) 2>&1 > /dev/null`" \ ; then echo "$(2)"; else echo "$(3)"; fi ;) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index b71e942567..aed811a166 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -433,9 +433,6 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu, table_data->len - madt_start, 1); } -#include "hw/i386/ssdt-tpm.hex" -#include "hw/i386/ssdt-tpm2.hex" - /* Assign BSEL property to all buses. In the future, this can be changed * to only assign to buses that support hotplug. */ @@ -1328,6 +1325,19 @@ build_ssdt(GArray *table_data, GArray *linker, Aml *scope = aml_scope("PCI0"); /* Scan all PCI buses. Generate tables to support hotplug. */ build_append_pci_bus_devices(scope, bus, pm->pcihp_bridge_en); + + if (misc->tpm_version != TPM_VERSION_UNSPEC) { + dev = aml_device("ISA.TPM"); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C31"))); + aml_append(dev, aml_name_decl("_STA", aml_int(0xF))); + crs = aml_resource_template(); + aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE, + TPM_TIS_ADDR_SIZE, AML_READ_WRITE)); + aml_append(crs, aml_irq_no_flags(TPM_TIS_IRQ)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); + } + aml_append(sb_scope, scope); } } @@ -1383,22 +1393,9 @@ build_tpm_tcpa(GArray *table_data, GArray *linker, GArray *tcpalog) } static void -build_tpm_ssdt(GArray *table_data, GArray *linker) -{ - void *tpm_ptr; - - tpm_ptr = acpi_data_push(table_data, sizeof(ssdt_tpm_aml)); - memcpy(tpm_ptr, ssdt_tpm_aml, sizeof(ssdt_tpm_aml)); -} - -static void build_tpm2(GArray *table_data, GArray *linker) { Acpi20TPM2 *tpm2_ptr; - void *tpm_ptr; - - tpm_ptr = acpi_data_push(table_data, sizeof(ssdt_tpm2_aml)); - memcpy(tpm_ptr, ssdt_tpm2_aml, sizeof(ssdt_tpm2_aml)); tpm2_ptr = acpi_data_push(table_data, sizeof *tpm2_ptr); @@ -1512,7 +1509,7 @@ build_srat(GArray *table_data, GArray *linker, PcGuestInfo *guest_info) */ if (hotplugabble_address_space_size) { numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, pcms->hotplug_memory_base, + acpi_build_srat_memory(numamem, pcms->hotplug_memory.base, hotplugabble_address_space_size, 0, MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); @@ -1726,16 +1723,9 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) acpi_add_table(table_offsets, tables_blob); build_tpm_tcpa(tables_blob, tables->linker, tables->tcpalog); - acpi_add_table(table_offsets, tables_blob); - switch (misc.tpm_version) { - case TPM_VERSION_1_2: - build_tpm_ssdt(tables_blob, tables->linker); - break; - case TPM_VERSION_2_0: + if (misc.tpm_version == TPM_VERSION_2_0) { + acpi_add_table(table_offsets, tables_blob); build_tpm2(tables_blob, tables->linker); - break; - default: - assert(false); } } if (guest_info->numa_nodes) { diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 7072930cfc..a66416d188 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -64,7 +64,6 @@ #include "hw/pci/pci_host.h" #include "acpi-build.h" #include "hw/mem/pc-dimm.h" -#include "trace.h" #include "qapi/visitor.h" #include "qapi-visit.h" @@ -1297,7 +1296,7 @@ FWCfgState *pc_memory_init(MachineState *machine, exit(EXIT_FAILURE); } - pcms->hotplug_memory_base = + pcms->hotplug_memory.base = ROUND_UP(0x100000000ULL + above_4g_mem_size, 1ULL << 30); if (pcms->enforce_aligned_dimm) { @@ -1305,17 +1304,17 @@ FWCfgState *pc_memory_init(MachineState *machine, hotplug_mem_size += (1ULL << 30) * machine->ram_slots; } - if ((pcms->hotplug_memory_base + hotplug_mem_size) < + if ((pcms->hotplug_memory.base + hotplug_mem_size) < hotplug_mem_size) { error_report("unsupported amount of maximum memory: " RAM_ADDR_FMT, machine->maxram_size); exit(EXIT_FAILURE); } - memory_region_init(&pcms->hotplug_memory, OBJECT(pcms), + memory_region_init(&pcms->hotplug_memory.mr, OBJECT(pcms), "hotplug-memory", hotplug_mem_size); - memory_region_add_subregion(system_memory, pcms->hotplug_memory_base, - &pcms->hotplug_memory); + memory_region_add_subregion(system_memory, pcms->hotplug_memory.base, + &pcms->hotplug_memory.mr); } /* Initialize PC system firmware */ @@ -1333,9 +1332,9 @@ FWCfgState *pc_memory_init(MachineState *machine, fw_cfg = bochs_bios_init(); rom_set_fw(fw_cfg); - if (guest_info->has_reserved_memory && pcms->hotplug_memory_base) { + if (guest_info->has_reserved_memory && pcms->hotplug_memory.base) { uint64_t *val = g_malloc(sizeof(*val)); - *val = cpu_to_le64(ROUND_UP(pcms->hotplug_memory_base, 0x1ULL << 30)); + *val = cpu_to_le64(ROUND_UP(pcms->hotplug_memory.base, 0x1ULL << 30)); fw_cfg_add_file(fw_cfg, "etc/reserved-memory-end", val, sizeof(*val)); } @@ -1554,88 +1553,31 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) static void pc_dimm_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - int slot; HotplugHandlerClass *hhc; Error *local_err = NULL; PCMachineState *pcms = PC_MACHINE(hotplug_dev); - MachineState *machine = MACHINE(hotplug_dev); PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *mr = ddc->get_memory_region(dimm); - uint64_t existing_dimms_capacity = 0; uint64_t align = TARGET_PAGE_SIZE; - uint64_t addr; - - addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err); - if (local_err) { - goto out; - } if (memory_region_get_alignment(mr) && pcms->enforce_aligned_dimm) { align = memory_region_get_alignment(mr); } - addr = pc_dimm_get_free_addr(pcms->hotplug_memory_base, - memory_region_size(&pcms->hotplug_memory), - !addr ? NULL : &addr, align, - memory_region_size(mr), &local_err); - if (local_err) { - goto out; - } - - existing_dimms_capacity = pc_existing_dimms_capacity(&local_err); - if (local_err) { - goto out; - } - - if (existing_dimms_capacity + memory_region_size(mr) > - machine->maxram_size - machine->ram_size) { - error_setg(&local_err, "not enough space, currently 0x%" PRIx64 - " in use of total hot pluggable 0x" RAM_ADDR_FMT, - existing_dimms_capacity, - machine->maxram_size - machine->ram_size); - goto out; - } - - object_property_set_int(OBJECT(dev), addr, PC_DIMM_ADDR_PROP, &local_err); - if (local_err) { - goto out; - } - trace_mhp_pc_dimm_assigned_address(addr); - - slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, &local_err); - if (local_err) { - goto out; - } - - slot = pc_dimm_get_free_slot(slot == PC_DIMM_UNASSIGNED_SLOT ? NULL : &slot, - machine->ram_slots, &local_err); - if (local_err) { - goto out; - } - object_property_set_int(OBJECT(dev), slot, PC_DIMM_SLOT_PROP, &local_err); - if (local_err) { - goto out; - } - trace_mhp_pc_dimm_assigned_slot(slot); - if (!pcms->acpi_dev) { error_setg(&local_err, "memory hotplug is not enabled: missing acpi device"); goto out; } - if (kvm_enabled() && !kvm_has_free_slot(machine)) { - error_setg(&local_err, "hypervisor has no free memory slots left"); + pc_dimm_memory_plug(dev, &pcms->hotplug_memory, mr, align, &local_err); + if (local_err) { goto out; } - memory_region_add_subregion(&pcms->hotplug_memory, - addr - pcms->hotplug_memory_base, mr); - vmstate_register_ram(mr, dev); - hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); - hhc->plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); + hhc->plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &error_abort); out: error_propagate(errp, local_err); } @@ -1677,9 +1619,7 @@ static void pc_dimm_unplug(HotplugHandler *hotplug_dev, goto out; } - memory_region_del_subregion(&pcms->hotplug_memory, mr); - vmstate_unregister_ram(mr, dev); - + pc_dimm_memory_unplug(dev, &pcms->hotplug_memory, mr); object_unparent(OBJECT(dev)); out: @@ -1766,7 +1706,7 @@ pc_machine_get_hotplug_memory_region_size(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); - int64_t value = memory_region_size(&pcms->hotplug_memory); + int64_t value = memory_region_size(&pcms->hotplug_memory.mr); visit_type_int(v, &value, name, errp); } diff --git a/hw/i386/ssdt-tpm-common.dsl b/hw/i386/ssdt-tpm-common.dsl deleted file mode 100644 index 9da49700d1..0000000000 --- a/hw/i386/ssdt-tpm-common.dsl +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -/* - * Common parts for TPM 1.2 and TPM 2 (with slight differences for PPI) - * to be #included - */ - - - External(\_SB.PCI0.ISA, DeviceObj) - Scope(\_SB.PCI0.ISA) { - /* TPM with emulated TPM TIS interface */ - Device (TPM) { - Name (_HID, EisaID ("PNP0C31")) - Name (_CRS, ResourceTemplate () - { - Memory32Fixed (ReadWrite, TPM_TIS_ADDR_BASE, TPM_TIS_ADDR_SIZE) - IRQNoFlags () {TPM_TIS_IRQ} - }) - Method (_STA, 0, NotSerialized) { - Return (0x0F) - } - } - } diff --git a/hw/i386/ssdt-tpm.dsl b/hw/i386/ssdt-tpm.dsl deleted file mode 100644 index d81478c1b5..0000000000 --- a/hw/i386/ssdt-tpm.dsl +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ -#include "hw/acpi/tpm.h" - -ACPI_EXTRACT_ALL_CODE ssdt_tpm_aml - -DefinitionBlock ( - "ssdt-tpm.aml", // Output Filename - "SSDT", // Signature - 0x01, // SSDT Compliance Revision - "BXPC", // OEMID - "BXSSDT", // TABLE ID - 0x1 // OEM Revision - ) -{ -#include "ssdt-tpm-common.dsl" -} diff --git a/hw/i386/ssdt-tpm.hex.generated b/hw/i386/ssdt-tpm.hex.generated deleted file mode 100644 index 874418c946..0000000000 --- a/hw/i386/ssdt-tpm.hex.generated +++ /dev/null @@ -1,109 +0,0 @@ -static unsigned char ssdt_tpm_aml[] = { -0x53, -0x53, -0x44, -0x54, -0x6b, -0x0, -0x0, -0x0, -0x1, -0x37, -0x42, -0x58, -0x50, -0x43, -0x0, -0x0, -0x42, -0x58, -0x53, -0x53, -0x44, -0x54, -0x0, -0x0, -0x1, -0x0, -0x0, -0x0, -0x49, -0x4e, -0x54, -0x4c, -0x7, -0x11, -0x14, -0x20, -0x10, -0x46, -0x4, -0x5c, -0x2f, -0x3, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x49, -0x53, -0x41, -0x5f, -0x5b, -0x82, -0x33, -0x54, -0x50, -0x4d, -0x5f, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0x31, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x14, -0xa, -0x11, -0x86, -0x9, -0x0, -0x1, -0x0, -0x0, -0xd4, -0xfe, -0x0, -0x50, -0x0, -0x0, -0x22, -0x20, -0x0, -0x79, -0x0, -0x14, -0x9, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0xa, -0xf -}; diff --git a/hw/i386/ssdt-tpm2.dsl b/hw/i386/ssdt-tpm2.dsl deleted file mode 100644 index 58bbbf806d..0000000000 --- a/hw/i386/ssdt-tpm2.dsl +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ -#include "hw/acpi/tpm.h" - -ACPI_EXTRACT_ALL_CODE ssdt_tpm2_aml - -DefinitionBlock ( - "ssdt-tpm2.aml", // Output Filename - "SSDT", // Signature - 0x01, // SSDT Compliance Revision - "BXPC", // OEMID - "BXSSDT", // TABLE ID - 0x1 // OEM Revision - ) -{ -#include "ssdt-tpm-common.dsl" -} diff --git a/hw/i386/ssdt-tpm2.hex.generated b/hw/i386/ssdt-tpm2.hex.generated deleted file mode 100644 index 9ea827151a..0000000000 --- a/hw/i386/ssdt-tpm2.hex.generated +++ /dev/null @@ -1,109 +0,0 @@ -static unsigned char ssdt_tpm2_aml[] = { -0x53, -0x53, -0x44, -0x54, -0x6b, -0x0, -0x0, -0x0, -0x1, -0x37, -0x42, -0x58, -0x50, -0x43, -0x0, -0x0, -0x42, -0x58, -0x53, -0x53, -0x44, -0x54, -0x0, -0x0, -0x1, -0x0, -0x0, -0x0, -0x49, -0x4e, -0x54, -0x4c, -0x7, -0x11, -0x14, -0x20, -0x10, -0x46, -0x4, -0x5c, -0x2f, -0x3, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x49, -0x53, -0x41, -0x5f, -0x5b, -0x82, -0x33, -0x54, -0x50, -0x4d, -0x5f, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0x31, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x14, -0xa, -0x11, -0x86, -0x9, -0x0, -0x1, -0x0, -0x0, -0xd4, -0xfe, -0x0, -0x50, -0x0, -0x0, -0x22, -0x20, -0x0, -0x79, -0x0, -0x14, -0x9, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0xa, -0xf -}; diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index b4b65c100a..bb6a92f7f4 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -45,11 +45,11 @@ do { \ } while (0) static void check_cmd(AHCIState *s, int port); -static int handle_cmd(AHCIState *s,int port,int slot); +static int handle_cmd(AHCIState *s, int port, uint8_t slot); static void ahci_reset_port(AHCIState *s, int port); static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis); static void ahci_init_d2h(AHCIDevice *ad); -static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write); +static int ahci_dma_prepare_buf(IDEDMA *dma, int32_t limit); static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes); static bool ahci_map_clb_address(AHCIDevice *ad); static bool ahci_map_fis_address(AHCIDevice *ad); @@ -106,8 +106,6 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset) val = pr->scr_err; break; case PORT_SCR_ACT: - pr->scr_act &= ~s->dev[port].finished; - s->dev[port].finished = 0; val = pr->scr_act; break; case PORT_CMD_ISSUE: @@ -331,8 +329,7 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) } } -static uint64_t ahci_mem_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t ahci_mem_read_32(void *opaque, hwaddr addr) { AHCIState *s = opaque; uint32_t val = 0; @@ -368,6 +365,30 @@ static uint64_t ahci_mem_read(void *opaque, hwaddr addr, } +/** + * AHCI 1.3 section 3 ("HBA Memory Registers") + * Support unaligned 8/16/32 bit reads, and 64 bit aligned reads. + * Caller is responsible for masking unwanted higher order bytes. + */ +static uint64_t ahci_mem_read(void *opaque, hwaddr addr, unsigned size) +{ + hwaddr aligned = addr & ~0x3; + int ofst = addr - aligned; + uint64_t lo = ahci_mem_read_32(opaque, aligned); + uint64_t hi; + + /* if < 8 byte read does not cross 4 byte boundary */ + if (ofst + size <= 4) { + return lo >> (ofst * 8); + } + g_assert_cmpint(size, >, 1); + + /* If the 64bit read is unaligned, we will produce undefined + * results. AHCI does not support unaligned 64bit reads. */ + hi = ahci_mem_read_32(opaque, aligned + 4); + return (hi << 32 | lo) >> (ofst * 8); +} + static void ahci_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) @@ -483,7 +504,7 @@ static void ahci_reg_init(AHCIState *s) static void check_cmd(AHCIState *s, int port) { AHCIPortRegs *pr = &s->dev[port].port_regs; - int slot; + uint8_t slot; if ((pr->cmd & PORT_CMD_START) && pr->cmd_issue) { for (slot = 0; (slot < 32) && pr->cmd_issue; slot++) { @@ -558,6 +579,7 @@ static void ahci_reset_port(AHCIState *s, int port) /* reset ncq queue */ for (i = 0; i < AHCI_MAX_CMDS; i++) { NCQTransferState *ncq_tfs = &s->dev[port].ncq_tfs[i]; + ncq_tfs->halt = false; if (!ncq_tfs->used) { continue; } @@ -642,14 +664,14 @@ static void ahci_unmap_clb_address(AHCIDevice *ad) ad->lst = NULL; } -static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished) +static void ahci_write_fis_sdb(AHCIState *s, NCQTransferState *ncq_tfs) { - AHCIDevice *ad = &s->dev[port]; + AHCIDevice *ad = ncq_tfs->drive; AHCIPortRegs *pr = &ad->port_regs; IDEState *ide_state; SDBFIS *sdb_fis; - if (!s->dev[port].res_fis || + if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) { return; } @@ -659,53 +681,35 @@ static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished) sdb_fis->type = SATA_FIS_TYPE_SDB; /* Interrupt pending & Notification bit */ - sdb_fis->flags = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0); + sdb_fis->flags = 0x40; /* Interrupt bit, always 1 for NCQ */ sdb_fis->status = ide_state->status & 0x77; sdb_fis->error = ide_state->error; /* update SAct field in SDB_FIS */ - s->dev[port].finished |= finished; sdb_fis->payload = cpu_to_le32(ad->finished); /* Update shadow registers (except BSY 0x80 and DRQ 0x08) */ pr->tfdata = (ad->port.ifs[0].error << 8) | (ad->port.ifs[0].status & 0x77) | (pr->tfdata & 0x88); + pr->scr_act &= ~ad->finished; + ad->finished = 0; - ahci_trigger_irq(s, ad, PORT_IRQ_SDB_FIS); + /* Trigger IRQ if interrupt bit is set (which currently, it always is) */ + if (sdb_fis->flags & 0x40) { + ahci_trigger_irq(s, ad, PORT_IRQ_SDB_FIS); + } } static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) { AHCIPortRegs *pr = &ad->port_regs; - uint8_t *pio_fis, *cmd_fis; - uint64_t tbl_addr; - dma_addr_t cmd_len = 0x80; + uint8_t *pio_fis; IDEState *s = &ad->port.ifs[0]; if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) { return; } - /* map cmd_fis */ - tbl_addr = le64_to_cpu(ad->cur_cmd->tbl_addr); - cmd_fis = dma_memory_map(ad->hba->as, tbl_addr, &cmd_len, - DMA_DIRECTION_TO_DEVICE); - - if (cmd_fis == NULL) { - DPRINTF(ad->port_no, "dma_memory_map failed in ahci_write_fis_pio"); - ahci_trigger_irq(ad->hba, ad, PORT_IRQ_HBUS_ERR); - return; - } - - if (cmd_len != 0x80) { - DPRINTF(ad->port_no, - "dma_memory_map mapped too few bytes in ahci_write_fis_pio"); - dma_memory_unmap(ad->hba->as, cmd_fis, cmd_len, - DMA_DIRECTION_TO_DEVICE, cmd_len); - ahci_trigger_irq(ad->hba, ad, PORT_IRQ_HBUS_ERR); - return; - } - pio_fis = &ad->res_fis[RES_FIS_PSFIS]; pio_fis[0] = SATA_FIS_TYPE_PIO_SETUP; @@ -721,8 +725,8 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) pio_fis[9] = s->hob_lcyl; pio_fis[10] = s->hob_hcyl; pio_fis[11] = 0; - pio_fis[12] = cmd_fis[12]; - pio_fis[13] = cmd_fis[13]; + pio_fis[12] = s->nsector & 0xFF; + pio_fis[13] = (s->nsector >> 8) & 0xFF; pio_fis[14] = 0; pio_fis[15] = s->status; pio_fis[16] = len & 255; @@ -739,9 +743,6 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) } ahci_trigger_irq(ad->hba, ad, PORT_IRQ_PIOS_FIS); - - dma_memory_unmap(ad->hba->as, cmd_fis, cmd_len, - DMA_DIRECTION_TO_DEVICE, cmd_len); } static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) @@ -749,22 +750,12 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) AHCIPortRegs *pr = &ad->port_regs; uint8_t *d2h_fis; int i; - dma_addr_t cmd_len = 0x80; - int cmd_mapped = 0; IDEState *s = &ad->port.ifs[0]; if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) { return; } - if (!cmd_fis) { - /* map cmd_fis */ - uint64_t tbl_addr = le64_to_cpu(ad->cur_cmd->tbl_addr); - cmd_fis = dma_memory_map(ad->hba->as, tbl_addr, &cmd_len, - DMA_DIRECTION_TO_DEVICE); - cmd_mapped = 1; - } - d2h_fis = &ad->res_fis[RES_FIS_RFIS]; d2h_fis[0] = SATA_FIS_TYPE_REGISTER_D2H; @@ -780,8 +771,8 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) d2h_fis[9] = s->hob_lcyl; d2h_fis[10] = s->hob_hcyl; d2h_fis[11] = 0; - d2h_fis[12] = cmd_fis[12]; - d2h_fis[13] = cmd_fis[13]; + d2h_fis[12] = s->nsector & 0xFF; + d2h_fis[13] = (s->nsector >> 8) & 0xFF; for (i = 14; i < 20; i++) { d2h_fis[i] = 0; } @@ -795,26 +786,22 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) } ahci_trigger_irq(ad->hba, ad, PORT_IRQ_D2H_REG_FIS); - - if (cmd_mapped) { - dma_memory_unmap(ad->hba->as, cmd_fis, cmd_len, - DMA_DIRECTION_TO_DEVICE, cmd_len); - } } static int prdt_tbl_entry_size(const AHCI_SG *tbl) { + /* flags_size is zero-based */ return (le32_to_cpu(tbl->flags_size) & AHCI_PRDT_SIZE_MASK) + 1; } static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, - int32_t offset) + AHCICmdHdr *cmd, int64_t limit, int32_t offset) { - AHCICmdHdr *cmd = ad->cur_cmd; - uint32_t opts = le32_to_cpu(cmd->opts); - uint64_t prdt_addr = le64_to_cpu(cmd->tbl_addr) + 0x80; - int sglist_alloc_hint = opts >> AHCI_CMD_HDR_PRDT_LEN; - dma_addr_t prdt_len = (sglist_alloc_hint * sizeof(AHCI_SG)); + uint16_t opts = le16_to_cpu(cmd->opts); + uint16_t prdtl = le16_to_cpu(cmd->prdtl); + uint64_t cfis_addr = le64_to_cpu(cmd->tbl_addr); + uint64_t prdt_addr = cfis_addr + 0x80; + dma_addr_t prdt_len = (prdtl * sizeof(AHCI_SG)); dma_addr_t real_prdt_len = prdt_len; uint8_t *prdt; int i; @@ -834,7 +821,7 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, * request for sector sizes up to 32K. */ - if (!sglist_alloc_hint) { + if (!prdtl) { DPRINTF(ad->port_no, "no sg list given by guest: 0x%08x\n", opts); return -1; } @@ -853,13 +840,12 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, } /* Get entries in the PRDT, init a qemu sglist accordingly */ - if (sglist_alloc_hint > 0) { + if (prdtl > 0) { AHCI_SG *tbl = (AHCI_SG *)prdt; sum = 0; - for (i = 0; i < sglist_alloc_hint; i++) { - /* flags_size is zero-based */ + for (i = 0; i < prdtl; i++) { tbl_entry_size = prdt_tbl_entry_size(&tbl[i]); - if (offset <= (sum + tbl_entry_size)) { + if (offset < (sum + tbl_entry_size)) { off_idx = i; off_pos = offset - sum; break; @@ -874,15 +860,16 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, goto out; } - qemu_sglist_init(sglist, qbus->parent, (sglist_alloc_hint - off_idx), + qemu_sglist_init(sglist, qbus->parent, (prdtl - off_idx), ad->hba->as); qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr) + off_pos, - prdt_tbl_entry_size(&tbl[off_idx]) - off_pos); + MIN(prdt_tbl_entry_size(&tbl[off_idx]) - off_pos, + limit)); - for (i = off_idx + 1; i < sglist_alloc_hint; i++) { - /* flags_size is zero-based */ + for (i = off_idx + 1; i < prdtl && sglist->size < limit; i++) { qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr), - prdt_tbl_entry_size(&tbl[i])); + MIN(prdt_tbl_entry_size(&tbl[i]), + limit - sglist->size)); if (sglist->size > INT32_MAX) { error_report("AHCI Physical Region Descriptor Table describes " "more than 2 GiB.\n"); @@ -899,28 +886,25 @@ out: return r; } -static void ncq_cb(void *opaque, int ret) +static void ncq_err(NCQTransferState *ncq_tfs) { - NCQTransferState *ncq_tfs = (NCQTransferState *)opaque; IDEState *ide_state = &ncq_tfs->drive->port.ifs[0]; - if (ret == -ECANCELED) { - return; - } - /* Clear bit for this tag in SActive */ - ncq_tfs->drive->port_regs.scr_act &= ~(1 << ncq_tfs->tag); + ide_state->error = ABRT_ERR; + ide_state->status = READY_STAT | ERR_STAT; + ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag); +} - if (ret < 0) { - /* error */ - ide_state->error = ABRT_ERR; - ide_state->status = READY_STAT | ERR_STAT; - ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag); - } else { - ide_state->status = READY_STAT | SEEK_STAT; +static void ncq_finish(NCQTransferState *ncq_tfs) +{ + /* If we didn't error out, set our finished bit. Errored commands + * do not get a bit set for the SDB FIS ACT register, nor do they + * clear the outstanding bit in scr_act (PxSACT). */ + if (!(ncq_tfs->drive->port_regs.scr_err & (1 << ncq_tfs->tag))) { + ncq_tfs->drive->finished |= (1 << ncq_tfs->tag); } - ahci_write_fis_sdb(ncq_tfs->drive->hba, ncq_tfs->drive->port_no, - (1 << ncq_tfs->tag)); + ahci_write_fis_sdb(ncq_tfs->drive->hba, ncq_tfs); DPRINTF(ncq_tfs->drive->port_no, "NCQ transfer tag %d finished\n", ncq_tfs->tag); @@ -931,6 +915,35 @@ static void ncq_cb(void *opaque, int ret) ncq_tfs->used = 0; } +static void ncq_cb(void *opaque, int ret) +{ + NCQTransferState *ncq_tfs = (NCQTransferState *)opaque; + IDEState *ide_state = &ncq_tfs->drive->port.ifs[0]; + + if (ret == -ECANCELED) { + return; + } + + if (ret < 0) { + bool is_read = ncq_tfs->cmd == READ_FPDMA_QUEUED; + BlockErrorAction action = blk_get_error_action(ide_state->blk, + is_read, -ret); + if (action == BLOCK_ERROR_ACTION_STOP) { + ncq_tfs->halt = true; + ide_state->bus->error_status = IDE_RETRY_HBA; + } else if (action == BLOCK_ERROR_ACTION_REPORT) { + ncq_err(ncq_tfs); + } + blk_error_action(ide_state->blk, action, is_read, -ret); + } else { + ide_state->status = READY_STAT | SEEK_STAT; + } + + if (!ncq_tfs->halt) { + ncq_finish(ncq_tfs); + } +} + static int is_ncq(uint8_t ata_cmd) { /* Based on SATA 3.2 section 13.6.3.2 */ @@ -946,13 +959,60 @@ static int is_ncq(uint8_t ata_cmd) } } +static void execute_ncq_command(NCQTransferState *ncq_tfs) +{ + AHCIDevice *ad = ncq_tfs->drive; + IDEState *ide_state = &ad->port.ifs[0]; + int port = ad->port_no; + + g_assert(is_ncq(ncq_tfs->cmd)); + ncq_tfs->halt = false; + + switch (ncq_tfs->cmd) { + case READ_FPDMA_QUEUED: + DPRINTF(port, "NCQ reading %d sectors from LBA %"PRId64", tag %d\n", + ncq_tfs->sector_count, ncq_tfs->lba, ncq_tfs->tag); + + DPRINTF(port, "tag %d aio read %"PRId64"\n", + ncq_tfs->tag, ncq_tfs->lba); + + dma_acct_start(ide_state->blk, &ncq_tfs->acct, + &ncq_tfs->sglist, BLOCK_ACCT_READ); + ncq_tfs->aiocb = dma_blk_read(ide_state->blk, &ncq_tfs->sglist, + ncq_tfs->lba, ncq_cb, ncq_tfs); + break; + case WRITE_FPDMA_QUEUED: + DPRINTF(port, "NCQ writing %d sectors to LBA %"PRId64", tag %d\n", + ncq_tfs->sector_count, ncq_tfs->lba, ncq_tfs->tag); + + DPRINTF(port, "tag %d aio write %"PRId64"\n", + ncq_tfs->tag, ncq_tfs->lba); + + dma_acct_start(ide_state->blk, &ncq_tfs->acct, + &ncq_tfs->sglist, BLOCK_ACCT_WRITE); + ncq_tfs->aiocb = dma_blk_write(ide_state->blk, &ncq_tfs->sglist, + ncq_tfs->lba, ncq_cb, ncq_tfs); + break; + default: + DPRINTF(port, "error: unsupported NCQ command (0x%02x) received\n", + ncq_tfs->cmd); + qemu_sglist_destroy(&ncq_tfs->sglist); + ncq_err(ncq_tfs); + } +} + + static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, - int slot) + uint8_t slot) { + AHCIDevice *ad = &s->dev[port]; + IDEState *ide_state = &ad->port.ifs[0]; NCQFrame *ncq_fis = (NCQFrame*)cmd_fis; uint8_t tag = ncq_fis->tag >> 3; - NCQTransferState *ncq_tfs = &s->dev[port].ncq_tfs[tag]; + NCQTransferState *ncq_tfs = &ad->ncq_tfs[tag]; + size_t size; + g_assert(is_ncq(ncq_fis->command)); if (ncq_tfs->used) { /* error - already in use */ fprintf(stderr, "%s: tag %d already used\n", __FUNCTION__, tag); @@ -960,75 +1020,82 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, } ncq_tfs->used = 1; - ncq_tfs->drive = &s->dev[port]; + ncq_tfs->drive = ad; ncq_tfs->slot = slot; + ncq_tfs->cmdh = &((AHCICmdHdr *)ad->lst)[slot]; + ncq_tfs->cmd = ncq_fis->command; ncq_tfs->lba = ((uint64_t)ncq_fis->lba5 << 40) | ((uint64_t)ncq_fis->lba4 << 32) | ((uint64_t)ncq_fis->lba3 << 24) | ((uint64_t)ncq_fis->lba2 << 16) | ((uint64_t)ncq_fis->lba1 << 8) | (uint64_t)ncq_fis->lba0; + ncq_tfs->tag = tag; - /* Note: We calculate the sector count, but don't currently rely on it. - * The total size of the DMA buffer tells us the transfer size instead. */ - ncq_tfs->sector_count = ((uint16_t)ncq_fis->sector_count_high << 8) | - ncq_fis->sector_count_low; + /* Sanity-check the NCQ packet */ + if (tag != slot) { + DPRINTF(port, "Warn: NCQ slot (%d) did not match the given tag (%d)\n", + slot, tag); + } - DPRINTF(port, "NCQ transfer LBA from %"PRId64" to %"PRId64", " - "drive max %"PRId64"\n", - ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 2, - s->dev[port].port.ifs[0].nb_sectors - 1); + if (ncq_fis->aux0 || ncq_fis->aux1 || ncq_fis->aux2 || ncq_fis->aux3) { + DPRINTF(port, "Warn: Attempt to use NCQ auxiliary fields.\n"); + } + if (ncq_fis->prio || ncq_fis->icc) { + DPRINTF(port, "Warn: Unsupported attempt to use PRIO/ICC fields\n"); + } + if (ncq_fis->fua & NCQ_FIS_FUA_MASK) { + DPRINTF(port, "Warn: Unsupported attempt to use Force Unit Access\n"); + } + if (ncq_fis->tag & NCQ_FIS_RARC_MASK) { + DPRINTF(port, "Warn: Unsupported attempt to use Rebuild Assist\n"); + } - ahci_populate_sglist(&s->dev[port], &ncq_tfs->sglist, 0); - ncq_tfs->tag = tag; + ncq_tfs->sector_count = ((ncq_fis->sector_count_high << 8) | + ncq_fis->sector_count_low); + if (!ncq_tfs->sector_count) { + ncq_tfs->sector_count = 0x10000; + } + size = ncq_tfs->sector_count * 512; + ahci_populate_sglist(ad, &ncq_tfs->sglist, ncq_tfs->cmdh, size, 0); - switch(ncq_fis->command) { - case READ_FPDMA_QUEUED: - DPRINTF(port, "NCQ reading %d sectors from LBA %"PRId64", " - "tag %d\n", - ncq_tfs->sector_count-1, ncq_tfs->lba, ncq_tfs->tag); + if (ncq_tfs->sglist.size < size) { + error_report("ahci: PRDT length for NCQ command (0x%zx) " + "is smaller than the requested size (0x%zx)", + ncq_tfs->sglist.size, size); + qemu_sglist_destroy(&ncq_tfs->sglist); + ncq_err(ncq_tfs); + ahci_trigger_irq(ad->hba, ad, PORT_IRQ_OVERFLOW); + return; + } else if (ncq_tfs->sglist.size != size) { + DPRINTF(port, "Warn: PRDTL (0x%zx)" + " does not match requested size (0x%zx)", + ncq_tfs->sglist.size, size); + } - DPRINTF(port, "tag %d aio read %"PRId64"\n", - ncq_tfs->tag, ncq_tfs->lba); + DPRINTF(port, "NCQ transfer LBA from %"PRId64" to %"PRId64", " + "drive max %"PRId64"\n", + ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 1, + ide_state->nb_sectors - 1); - dma_acct_start(ncq_tfs->drive->port.ifs[0].blk, &ncq_tfs->acct, - &ncq_tfs->sglist, BLOCK_ACCT_READ); - ncq_tfs->aiocb = dma_blk_read(ncq_tfs->drive->port.ifs[0].blk, - &ncq_tfs->sglist, ncq_tfs->lba, - ncq_cb, ncq_tfs); - break; - case WRITE_FPDMA_QUEUED: - DPRINTF(port, "NCQ writing %d sectors to LBA %"PRId64", tag %d\n", - ncq_tfs->sector_count-1, ncq_tfs->lba, ncq_tfs->tag); - - DPRINTF(port, "tag %d aio write %"PRId64"\n", - ncq_tfs->tag, ncq_tfs->lba); - - dma_acct_start(ncq_tfs->drive->port.ifs[0].blk, &ncq_tfs->acct, - &ncq_tfs->sglist, BLOCK_ACCT_WRITE); - ncq_tfs->aiocb = dma_blk_write(ncq_tfs->drive->port.ifs[0].blk, - &ncq_tfs->sglist, ncq_tfs->lba, - ncq_cb, ncq_tfs); - break; - default: - if (is_ncq(cmd_fis[2])) { - DPRINTF(port, - "error: unsupported NCQ command (0x%02x) received\n", - cmd_fis[2]); - } else { - DPRINTF(port, - "error: tried to process non-NCQ command as NCQ\n"); - } - qemu_sglist_destroy(&ncq_tfs->sglist); + execute_ncq_command(ncq_tfs); +} + +static AHCICmdHdr *get_cmd_header(AHCIState *s, uint8_t port, uint8_t slot) +{ + if (port >= s->ports || slot >= AHCI_MAX_CMDS) { + return NULL; } + + return s->dev[port].lst ? &((AHCICmdHdr *)s->dev[port].lst)[slot] : NULL; } static void handle_reg_h2d_fis(AHCIState *s, int port, - int slot, uint8_t *cmd_fis) + uint8_t slot, uint8_t *cmd_fis) { IDEState *ide_state = &s->dev[port].port.ifs[0]; - AHCICmdHdr *cmd = s->dev[port].cur_cmd; - uint32_t opts = le32_to_cpu(cmd->opts); + AHCICmdHdr *cmd = get_cmd_header(s, port, slot); + uint16_t opts = le16_to_cpu(cmd->opts); if (cmd_fis[1] & 0x0F) { DPRINTF(port, "Port Multiplier not supported." @@ -1108,7 +1175,7 @@ static void handle_reg_h2d_fis(AHCIState *s, int port, ide_exec_cmd(&s->dev[port].port, cmd_fis[2]); } -static int handle_cmd(AHCIState *s, int port, int slot) +static int handle_cmd(AHCIState *s, int port, uint8_t slot) { IDEState *ide_state; uint64_t tbl_addr; @@ -1126,7 +1193,7 @@ static int handle_cmd(AHCIState *s, int port, int slot) DPRINTF(port, "error: lst not given but cmd handled"); return -1; } - cmd = &((AHCICmdHdr *)s->dev[port].lst)[slot]; + cmd = get_cmd_header(s, port, slot); /* remember current slot handle for later */ s->dev[port].cur_cmd = cmd; @@ -1185,7 +1252,7 @@ static void ahci_start_transfer(IDEDMA *dma) IDEState *s = &ad->port.ifs[0]; uint32_t size = (uint32_t)(s->data_end - s->data_ptr); /* write == ram -> device */ - uint32_t opts = le32_to_cpu(ad->cur_cmd->opts); + uint16_t opts = le16_to_cpu(ad->cur_cmd->opts); int is_write = opts & AHCI_CMD_WRITE; int is_atapi = opts & AHCI_CMD_ATAPI; int has_sglist = 0; @@ -1197,7 +1264,7 @@ static void ahci_start_transfer(IDEDMA *dma) goto out; } - if (ahci_dma_prepare_buf(dma, is_write)) { + if (ahci_dma_prepare_buf(dma, size)) { has_sglist = 1; } @@ -1243,16 +1310,34 @@ static void ahci_restart_dma(IDEDMA *dma) } /** + * IDE/PIO restarts are handled by the core layer, but NCQ commands + * need an extra kick from the AHCI HBA. + */ +static void ahci_restart(IDEDMA *dma) +{ + AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); + int i; + + for (i = 0; i < AHCI_MAX_CMDS; i++) { + NCQTransferState *ncq_tfs = &ad->ncq_tfs[i]; + if (ncq_tfs->halt) { + execute_ncq_command(ncq_tfs); + } + } +} + +/** * Called in DMA R/W chains to read the PRDT, utilizing ahci_populate_sglist. * Not currently invoked by PIO R/W chains, * which invoke ahci_populate_sglist via ahci_start_transfer. */ -static int32_t ahci_dma_prepare_buf(IDEDMA *dma, int is_write) +static int32_t ahci_dma_prepare_buf(IDEDMA *dma, int32_t limit) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); IDEState *s = &ad->port.ifs[0]; - if (ahci_populate_sglist(ad, &s->sg, s->io_buffer_offset) == -1) { + if (ahci_populate_sglist(ad, &s->sg, ad->cur_cmd, + limit, s->io_buffer_offset) == -1) { DPRINTF(ad->port_no, "ahci_dma_prepare_buf failed.\n"); return -1; } @@ -1287,7 +1372,7 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) uint8_t *p = s->io_buffer + s->io_buffer_index; int l = s->io_buffer_size - s->io_buffer_index; - if (ahci_populate_sglist(ad, &s->sg, s->io_buffer_offset)) { + if (ahci_populate_sglist(ad, &s->sg, ad->cur_cmd, l, s->io_buffer_offset)) { return 0; } @@ -1330,6 +1415,7 @@ static void ahci_irq_set(void *opaque, int n, int level) static const IDEDMAOps ahci_dma_ops = { .start_dma = ahci_start_dma, + .restart = ahci_restart, .restart_dma = ahci_restart_dma, .start_transfer = ahci_start_transfer, .prepare_buf = ahci_dma_prepare_buf, @@ -1400,6 +1486,21 @@ void ahci_reset(AHCIState *s) } } +static const VMStateDescription vmstate_ncq_tfs = { + .name = "ncq state", + .version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(sector_count, NCQTransferState), + VMSTATE_UINT64(lba, NCQTransferState), + VMSTATE_UINT8(tag, NCQTransferState), + VMSTATE_UINT8(cmd, NCQTransferState), + VMSTATE_UINT8(slot, NCQTransferState), + VMSTATE_BOOL(used, NCQTransferState), + VMSTATE_BOOL(halt, NCQTransferState), + VMSTATE_END_OF_LIST() + }, +}; + static const VMStateDescription vmstate_ahci_device = { .name = "ahci port", .version_id = 1, @@ -1425,14 +1526,17 @@ static const VMStateDescription vmstate_ahci_device = { VMSTATE_BOOL(done_atapi_packet, AHCIDevice), VMSTATE_INT32(busy_slot, AHCIDevice), VMSTATE_BOOL(init_d2h_sent, AHCIDevice), + VMSTATE_STRUCT_ARRAY(ncq_tfs, AHCIDevice, AHCI_MAX_CMDS, + 1, vmstate_ncq_tfs, NCQTransferState), VMSTATE_END_OF_LIST() }, }; static int ahci_state_post_load(void *opaque, int version_id) { - int i; + int i, j; struct AHCIDevice *ad; + NCQTransferState *ncq_tfs; AHCIState *s = opaque; for (i = 0; i < s->ports; i++) { @@ -1444,6 +1548,37 @@ static int ahci_state_post_load(void *opaque, int version_id) return -1; } + for (j = 0; j < AHCI_MAX_CMDS; j++) { + ncq_tfs = &ad->ncq_tfs[j]; + ncq_tfs->drive = ad; + + if (ncq_tfs->used != ncq_tfs->halt) { + return -1; + } + if (!ncq_tfs->halt) { + continue; + } + if (!is_ncq(ncq_tfs->cmd)) { + return -1; + } + if (ncq_tfs->slot != ncq_tfs->tag) { + return -1; + } + /* If ncq_tfs->halt is justly set, the engine should be engaged, + * and the command list buffer should be mapped. */ + ncq_tfs->cmdh = get_cmd_header(s, i, ncq_tfs->slot); + if (!ncq_tfs->cmdh) { + return -1; + } + ahci_populate_sglist(ncq_tfs->drive, &ncq_tfs->sglist, + ncq_tfs->cmdh, ncq_tfs->sector_count * 512, + 0); + if (ncq_tfs->sector_count != ncq_tfs->sglist.size >> 9) { + return -1; + } + } + + /* * If an error is present, ad->busy_slot will be valid and not -1. * In this case, an operation is waiting to resume and will re-check @@ -1460,7 +1595,7 @@ static int ahci_state_post_load(void *opaque, int version_id) if (ad->busy_slot < 0 || ad->busy_slot >= AHCI_MAX_CMDS) { return -1; } - ad->cur_cmd = &((AHCICmdHdr *)ad->lst)[ad->busy_slot]; + ad->cur_cmd = get_cmd_header(s, i, ad->busy_slot); } } diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h index 501c002c31..9f5b4d20b5 100644 --- a/hw/ide/ahci.h +++ b/hw/ide/ahci.h @@ -195,6 +195,9 @@ #define RECEIVE_FPDMA_QUEUED 0x65 #define SEND_FPDMA_QUEUED 0x64 +#define NCQ_FIS_FUA_MASK 0x80 +#define NCQ_FIS_RARC_MASK 0x01 + #define RES_FIS_DSFIS 0x00 #define RES_FIS_PSFIS 0x20 #define RES_FIS_RFIS 0x40 @@ -233,7 +236,8 @@ typedef struct AHCIPortRegs { } AHCIPortRegs; typedef struct AHCICmdHdr { - uint32_t opts; + uint16_t opts; + uint16_t prdtl; uint32_t status; uint64_t tbl_addr; uint32_t reserved[4]; @@ -250,13 +254,16 @@ typedef struct AHCIDevice AHCIDevice; typedef struct NCQTransferState { AHCIDevice *drive; BlockAIOCB *aiocb; + AHCICmdHdr *cmdh; QEMUSGList sglist; BlockAcctCookie acct; - uint16_t sector_count; + uint32_t sector_count; uint64_t lba; uint8_t tag; - int slot; - int used; + uint8_t cmd; + uint8_t slot; + bool used; + bool halt; } NCQTransferState; struct AHCIDevice { @@ -312,27 +319,39 @@ extern const VMStateDescription vmstate_ahci; .offset = vmstate_offset_value(_state, _field, AHCIState), \ } +/** + * NCQFrame is the same as a Register H2D FIS (described in SATA 3.2), + * but some fields have been re-mapped and re-purposed, as seen in + * SATA 3.2 section 13.6.4.1 ("READ FPDMA QUEUED") + * + * cmd_fis[3], feature 7:0, becomes sector count 7:0. + * cmd_fis[7], device 7:0, uses bit 7 as the Force Unit Access bit. + * cmd_fis[11], feature 15:8, becomes sector count 15:8. + * cmd_fis[12], count 7:0, becomes the NCQ TAG (7:3) and RARC bit (0) + * cmd_fis[13], count 15:8, becomes the priority value (7:6) + * bytes 16-19 become an le32 "auxiliary" field. + */ typedef struct NCQFrame { uint8_t fis_type; uint8_t c; uint8_t command; - uint8_t sector_count_low; + uint8_t sector_count_low; /* (feature 7:0) */ uint8_t lba0; uint8_t lba1; uint8_t lba2; - uint8_t fua; + uint8_t fua; /* (device 7:0) */ uint8_t lba3; uint8_t lba4; uint8_t lba5; - uint8_t sector_count_high; - uint8_t tag; - uint8_t reserved5; - uint8_t reserved6; + uint8_t sector_count_high; /* (feature 15:8) */ + uint8_t tag; /* (count 0:7) */ + uint8_t prio; /* (count 15:8) */ + uint8_t icc; uint8_t control; - uint8_t reserved7; - uint8_t reserved8; - uint8_t reserved9; - uint8_t reserved10; + uint8_t aux0; + uint8_t aux1; + uint8_t aux2; + uint8_t aux3; } QEMU_PACKED NCQFrame; typedef struct SDBFIS { diff --git a/hw/ide/core.c b/hw/ide/core.c index 1efd98af63..122e955084 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -716,8 +716,8 @@ static void ide_dma_cb(void *opaque, int ret) sector_num = ide_get_sector(s); if (n > 0) { - assert(s->io_buffer_size == s->sg.size); - dma_buf_commit(s, s->io_buffer_size); + assert(n * 512 == s->sg.size); + dma_buf_commit(s, s->sg.size); sector_num += n; ide_set_sector(s, sector_num); s->nsector -= n; @@ -734,7 +734,7 @@ static void ide_dma_cb(void *opaque, int ret) n = s->nsector; s->io_buffer_index = 0; s->io_buffer_size = n * 512; - if (s->bus->dma->ops->prepare_buf(s->bus->dma, ide_cmd_is_read(s)) < 512) { + if (s->bus->dma->ops->prepare_buf(s->bus->dma, s->io_buffer_size) < 512) { /* The PRDs were too short. Reset the Active bit, but don't raise an * interrupt. */ s->status = READY_STAT | SEEK_STAT; @@ -2326,7 +2326,7 @@ static void ide_nop(IDEDMA *dma) { } -static int32_t ide_nop_int32(IDEDMA *dma, int x) +static int32_t ide_nop_int32(IDEDMA *dma, int32_t l) { return 0; } @@ -2371,6 +2371,13 @@ static void ide_restart_bh(void *opaque) * called function can set a new error status. */ bus->error_status = 0; + /* The HBA has generically asked to be kicked on retry */ + if (error_status & IDE_RETRY_HBA) { + if (s->bus->dma->ops->restart) { + s->bus->dma->ops->restart(s->bus->dma); + } + } + if (error_status & IDE_RETRY_DMA) { if (error_status & IDE_RETRY_TRIM) { ide_restart_dma(s, IDE_DMA_TRIM); diff --git a/hw/ide/internal.h b/hw/ide/internal.h index 965cc55cb8..30fdcbc5fa 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -324,7 +324,7 @@ typedef void EndTransferFunc(IDEState *); typedef void DMAStartFunc(IDEDMA *, IDEState *, BlockCompletionFunc *); typedef void DMAVoidFunc(IDEDMA *); typedef int DMAIntFunc(IDEDMA *, int); -typedef int32_t DMAInt32Func(IDEDMA *, int); +typedef int32_t DMAInt32Func(IDEDMA *, int32_t len); typedef void DMAu32Func(IDEDMA *, uint32_t); typedef void DMAStopFunc(IDEDMA *, bool); typedef void DMARestartFunc(void *, int, RunState); @@ -436,6 +436,7 @@ struct IDEDMAOps { DMAInt32Func *prepare_buf; DMAu32Func *commit_buf; DMAIntFunc *rw_buf; + DMAVoidFunc *restart; DMAVoidFunc *restart_dma; DMAStopFunc *set_inactive; DMAVoidFunc *cmd_done; @@ -499,6 +500,7 @@ struct IDEDevice { #define IDE_RETRY_READ 0x20 #define IDE_RETRY_FLUSH 0x40 #define IDE_RETRY_TRIM 0x80 +#define IDE_RETRY_HBA 0x100 static inline IDEState *idebus_active_if(IDEBus *bus) { diff --git a/hw/ide/macio.c b/hw/ide/macio.c index dd52d50732..a55a479da6 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -499,7 +499,7 @@ static int ide_nop_int(IDEDMA *dma, int x) return 0; } -static int32_t ide_nop_int32(IDEDMA *dma, int x) +static int32_t ide_nop_int32(IDEDMA *dma, int32_t l) { return 0; } diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 4afd0cfe8c..d31ff885b7 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -53,10 +53,14 @@ static void bmdma_start_dma(IDEDMA *dma, IDEState *s, } /** - * Return the number of bytes successfully prepared. - * -1 on error. + * Prepare an sglist based on available PRDs. + * @limit: How many bytes to prepare total. + * + * Returns the number of bytes prepared, -1 on error. + * IDEState.io_buffer_size will contain the number of bytes described + * by the PRDs, whether or not we added them to the sglist. */ -static int32_t bmdma_prepare_buf(IDEDMA *dma, int is_write) +static int32_t bmdma_prepare_buf(IDEDMA *dma, int32_t limit) { BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); IDEState *s = bmdma_active_if(bm); @@ -75,7 +79,7 @@ static int32_t bmdma_prepare_buf(IDEDMA *dma, int is_write) /* end of table (with a fail safe of one page) */ if (bm->cur_prd_last || (bm->cur_addr - bm->addr) >= BMDMA_PAGE_SIZE) { - return s->io_buffer_size; + return s->sg.size; } pci_dma_read(pci_dev, bm->cur_addr, &prd, 8); bm->cur_addr += 8; @@ -90,7 +94,14 @@ static int32_t bmdma_prepare_buf(IDEDMA *dma, int is_write) } l = bm->cur_prd_len; if (l > 0) { - qemu_sglist_add(&s->sg, bm->cur_prd_addr, l); + uint64_t sg_len; + + /* Don't add extra bytes to the SGList; consume any remaining + * PRDs from the guest, but ignore them. */ + sg_len = MIN(limit - s->sg.size, bm->cur_prd_len); + if (sg_len) { + qemu_sglist_add(&s->sg, bm->cur_prd_addr, sg_len); + } /* Note: We limit the max transfer to be 2GiB. * This should accommodate the largest ATA transaction diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c index 32cc94af0b..616a815ed4 100644 --- a/hw/input/virtio-input-hid.c +++ b/hw/input/virtio-input-hid.c @@ -252,7 +252,11 @@ static void virtio_input_handle_sync(DeviceState *dev) static void virtio_input_hid_realize(DeviceState *dev, Error **errp) { VirtIOInputHID *vhid = VIRTIO_INPUT_HID(dev); + vhid->hs = qemu_input_handler_register(dev, vhid->handler); + if (vhid->display && vhid->hs) { + qemu_input_handler_bind(vhid->hs, vhid->display, vhid->head, NULL); + } } static void virtio_input_hid_unrealize(DeviceState *dev, Error **errp) @@ -301,10 +305,17 @@ static void virtio_input_hid_handle_status(VirtIOInput *vinput, } } +static Property virtio_input_hid_properties[] = { + DEFINE_PROP_STRING("display", VirtIOInputHID, display), + DEFINE_PROP_UINT32("head", VirtIOInputHID, head, 0), +}; + static void virtio_input_hid_class_init(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass); + dc->props = virtio_input_hid_properties; vic->realize = virtio_input_hid_realize; vic->unrealize = virtio_input_hid_unrealize; vic->change_active = virtio_input_hid_change_active; diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 044ad66730..a64d0714ea 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -123,7 +123,7 @@ static void arm_gic_common_realize(DeviceState *dev, Error **errp) static void arm_gic_common_reset(DeviceState *dev) { GICState *s = ARM_GIC_COMMON(dev); - int i; + int i, j; memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state)); for (i = 0 ; i < s->num_cpu; i++) { if (s->revision == REV_11MPCORE) { @@ -135,15 +135,30 @@ static void arm_gic_common_reset(DeviceState *dev) s->running_irq[i] = 1023; s->running_priority[i] = 0x100; s->cpu_ctlr[i] = 0; + s->bpr[i] = GIC_MIN_BPR; + s->abpr[i] = GIC_MIN_ABPR; + for (j = 0; j < GIC_INTERNAL; j++) { + s->priority1[j][i] = 0; + } + for (j = 0; j < GIC_NR_SGIS; j++) { + s->sgi_pending[j][i] = 0; + } } for (i = 0; i < GIC_NR_SGIS; i++) { GIC_SET_ENABLED(i, ALL_CPU_MASK); GIC_SET_EDGE_TRIGGER(i); } - if (s->num_cpu == 1) { + + for (i = 0; i < ARRAY_SIZE(s->priority2); i++) { + s->priority2[i] = 0; + } + + for (i = 0; i < GIC_MAXIRQ; i++) { /* For uniprocessor GICs all interrupts always target the sole CPU */ - for (i = 0; i < GIC_MAXIRQ; i++) { + if (s->num_cpu == 1) { s->irq_target[i] = 1; + } else { + s->irq_target[i] = 0; } } s->ctlr = 0; diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index e70633d29f..bb04862de8 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -23,12 +23,96 @@ #include "qapi/visitor.h" #include "qemu/range.h" #include "sysemu/numa.h" +#include "sysemu/kvm.h" +#include "trace.h" typedef struct pc_dimms_capacity { uint64_t size; Error **errp; } pc_dimms_capacity; +void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, + MemoryRegion *mr, uint64_t align, Error **errp) +{ + int slot; + MachineState *machine = MACHINE(qdev_get_machine()); + PCDIMMDevice *dimm = PC_DIMM(dev); + Error *local_err = NULL; + uint64_t existing_dimms_capacity = 0; + uint64_t addr; + + addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err); + if (local_err) { + goto out; + } + + addr = pc_dimm_get_free_addr(hpms->base, + memory_region_size(&hpms->mr), + !addr ? NULL : &addr, align, + memory_region_size(mr), &local_err); + if (local_err) { + goto out; + } + + existing_dimms_capacity = pc_existing_dimms_capacity(&local_err); + if (local_err) { + goto out; + } + + if (existing_dimms_capacity + memory_region_size(mr) > + machine->maxram_size - machine->ram_size) { + error_setg(&local_err, "not enough space, currently 0x%" PRIx64 + " in use of total hot pluggable 0x" RAM_ADDR_FMT, + existing_dimms_capacity, + machine->maxram_size - machine->ram_size); + goto out; + } + + object_property_set_int(OBJECT(dev), addr, PC_DIMM_ADDR_PROP, &local_err); + if (local_err) { + goto out; + } + trace_mhp_pc_dimm_assigned_address(addr); + + slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, &local_err); + if (local_err) { + goto out; + } + + slot = pc_dimm_get_free_slot(slot == PC_DIMM_UNASSIGNED_SLOT ? NULL : &slot, + machine->ram_slots, &local_err); + if (local_err) { + goto out; + } + object_property_set_int(OBJECT(dev), slot, PC_DIMM_SLOT_PROP, &local_err); + if (local_err) { + goto out; + } + trace_mhp_pc_dimm_assigned_slot(slot); + + if (kvm_enabled() && !kvm_has_free_slot(machine)) { + error_setg(&local_err, "hypervisor has no free memory slots left"); + goto out; + } + + memory_region_add_subregion(&hpms->mr, addr - hpms->base, mr); + vmstate_register_ram(mr, dev); + numa_set_mem_node_id(addr, memory_region_size(mr), dimm->node); + +out: + error_propagate(errp, local_err); +} + +void pc_dimm_memory_unplug(DeviceState *dev, MemoryHotplugState *hpms, + MemoryRegion *mr) +{ + PCDIMMDevice *dimm = PC_DIMM(dev); + + numa_unset_mem_node_id(dimm->addr, memory_region_size(mr), dimm->node); + memory_region_del_subregion(&hpms->mr, mr); + vmstate_unregister_ram(mr, dev); +} + static int pc_existing_dimms_capacity_internal(Object *obj, void *opaque) { pc_dimms_capacity *cap = opaque; diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 786a8f0638..3082e75340 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -53,6 +53,7 @@ #include "qemu/error-report.h" #include "hw/empty_slot.h" #include "sysemu/kvm.h" +#include "exec/semihost.h" //#define DEBUG_BOARD_INIT @@ -634,7 +635,13 @@ static void write_bootloader (CPUMIPSState *env, uint8_t *base, /* Second part of the bootloader */ p = (uint32_t *) (base + 0x580); - stl_p(p++, 0x24040002); /* addiu a0, zero, 2 */ + + if (semihosting_get_argc()) { + /* Preserve a0 content as arguments have been passed */ + stl_p(p++, 0x00000000); /* nop */ + } else { + stl_p(p++, 0x24040002); /* addiu a0, zero, 2 */ + } stl_p(p++, 0x3c1d0000 | (((ENVP_ADDR - 64) >> 16) & 0xffff)); /* lui sp, high(ENVP_ADDR) */ stl_p(p++, 0x37bd0000 | ((ENVP_ADDR - 64) & 0xffff)); /* ori sp, sp, low(ENVP_ADDR) */ stl_p(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff)); /* lui a1, high(ENVP_ADDR) */ @@ -887,7 +894,7 @@ static void main_cpu_reset(void *opaque) read only location. The kernel location and the arguments table location does not change. */ if (loaderparams.kernel_filename) { - env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL)); + env->CP0_Status &= ~(1 << CP0St_ERL); } malta_mips_config(cpu); diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 36f73e1f8b..26aded9f00 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -28,7 +28,8 @@ #include "hw/pci/pci_bus.h" #include "hw/hotplug.h" -#define TYPE_PCI_BRIDGE_DEV "pci-bridge" +#define TYPE_PCI_BRIDGE_DEV "pci-bridge" +#define TYPE_PCI_BRIDGE_SEAT_DEV "pci-bridge-seat" #define PCI_BRIDGE_DEV(obj) \ OBJECT_CHECK(PCIBridgeDev, (obj), TYPE_PCI_BRIDGE_DEV) @@ -40,6 +41,7 @@ struct PCIBridgeDev { MemoryRegion bar; uint8_t chassis_nr; #define PCI_BRIDGE_DEV_F_MSI_REQ 0 +#define PCI_BRIDGE_DEV_F_SHPC_REQ 1 uint32_t flags; }; typedef struct PCIBridgeDev PCIBridgeDev; @@ -54,11 +56,17 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) if (err) { goto bridge_error; } - dev->config[PCI_INTERRUPT_PIN] = 0x1; - memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar", shpc_bar_size(dev)); - err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0); - if (err) { - goto shpc_error; + if (bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_SHPC_REQ)) { + dev->config[PCI_INTERRUPT_PIN] = 0x1; + memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar", + shpc_bar_size(dev)); + err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0); + if (err) { + goto shpc_error; + } + } else { + /* MSI is not applicable without SHPC */ + bridge_dev->flags &= ~(1 << PCI_BRIDGE_DEV_F_MSI_REQ); } err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0); if (err) { @@ -71,15 +79,19 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) goto msi_error; } } - /* TODO: spec recommends using 64 bit prefetcheable BAR. - * Check whether that works well. */ - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar); + if (shpc_present(dev)) { + /* TODO: spec recommends using 64 bit prefetcheable BAR. + * Check whether that works well. */ + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar); + } return 0; msi_error: slotid_cap_cleanup(dev); slotid_error: - shpc_cleanup(dev, &bridge_dev->bar); + if (shpc_present(dev)) { + shpc_cleanup(dev, &bridge_dev->bar); + } shpc_error: pci_bridge_exitfn(dev); bridge_error: @@ -93,12 +105,15 @@ static void pci_bridge_dev_exitfn(PCIDevice *dev) msi_uninit(dev); } slotid_cap_cleanup(dev); - shpc_cleanup(dev, &bridge_dev->bar); + if (shpc_present(dev)) { + shpc_cleanup(dev, &bridge_dev->bar); + } pci_bridge_exitfn(dev); } static void pci_bridge_dev_instance_finalize(Object *obj) { + /* this function is idempotent and handles (PCIDevice.shpc == NULL) */ shpc_free(PCI_DEVICE(obj)); } @@ -109,7 +124,9 @@ static void pci_bridge_dev_write_config(PCIDevice *d, if (msi_present(d)) { msi_write_config(d, address, val, len); } - shpc_cap_write_config(d, address, val, len); + if (shpc_present(d)) { + shpc_cap_write_config(d, address, val, len); + } } static void qdev_pci_bridge_dev_reset(DeviceState *qdev) @@ -117,25 +134,65 @@ static void qdev_pci_bridge_dev_reset(DeviceState *qdev) PCIDevice *dev = PCI_DEVICE(qdev); pci_bridge_reset(qdev); - shpc_reset(dev); + if (shpc_present(dev)) { + shpc_reset(dev); + } } static Property pci_bridge_dev_properties[] = { /* Note: 0 is not a legal chassis number. */ - DEFINE_PROP_UINT8("chassis_nr", PCIBridgeDev, chassis_nr, 0), - DEFINE_PROP_BIT("msi", PCIBridgeDev, flags, PCI_BRIDGE_DEV_F_MSI_REQ, true), + DEFINE_PROP_UINT8(PCI_BRIDGE_DEV_PROP_CHASSIS_NR, PCIBridgeDev, chassis_nr, + 0), + DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_MSI, PCIBridgeDev, flags, + PCI_BRIDGE_DEV_F_MSI_REQ, true), + DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_SHPC, PCIBridgeDev, flags, + PCI_BRIDGE_DEV_F_SHPC_REQ, true), DEFINE_PROP_END_OF_LIST(), }; +static bool pci_device_shpc_present(void *opaque, int version_id) +{ + PCIDevice *dev = opaque; + + return shpc_present(dev); +} + static const VMStateDescription pci_bridge_dev_vmstate = { .name = "pci_bridge", .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, PCIBridge), - SHPC_VMSTATE(shpc, PCIDevice), + SHPC_VMSTATE(shpc, PCIDevice, pci_device_shpc_present), VMSTATE_END_OF_LIST() } }; +static void pci_bridge_dev_hotplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); + + if (!shpc_present(pci_hotplug_dev)) { + error_setg(errp, "standard hotplug controller has been disabled for " + "this %s", TYPE_PCI_BRIDGE_DEV); + return; + } + shpc_device_hotplug_cb(hotplug_dev, dev, errp); +} + +static void pci_bridge_dev_hot_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, + Error **errp) +{ + PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); + + if (!shpc_present(pci_hotplug_dev)) { + error_setg(errp, "standard hotplug controller has been disabled for " + "this %s", TYPE_PCI_BRIDGE_DEV); + return; + } + shpc_device_hot_unplug_request_cb(hotplug_dev, dev, errp); +} + static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -154,8 +211,8 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) dc->props = pci_bridge_dev_properties; dc->vmsd = &pci_bridge_dev_vmstate; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - hc->plug = shpc_device_hotplug_cb; - hc->unplug_request = shpc_device_hot_unplug_request_cb; + hc->plug = pci_bridge_dev_hotplug_cb; + hc->unplug_request = pci_bridge_dev_hot_unplug_request_cb; } static const TypeInfo pci_bridge_dev_info = { @@ -170,9 +227,31 @@ static const TypeInfo pci_bridge_dev_info = { } }; +/* + * Multiseat bridge. Same as the standard pci bridge, only with a + * different pci id, so we can match it easily in the guest for + * automagic multiseat configuration. See docs/multiseat.txt for more. + */ +static void pci_bridge_dev_seat_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE_SEAT; + dc->desc = "Standard PCI Bridge (multiseat)"; +} + +static const TypeInfo pci_bridge_dev_seat_info = { + .name = TYPE_PCI_BRIDGE_SEAT_DEV, + .parent = TYPE_PCI_BRIDGE_DEV, + .instance_size = sizeof(PCIBridgeDev), + .class_init = pci_bridge_dev_seat_class_init, +}; + static void pci_bridge_dev_register(void) { type_register_static(&pci_bridge_dev_info); + type_register_static(&pci_bridge_dev_seat_info); } type_init(pci_bridge_dev_register); diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index ec2bb458f7..57f8a3762b 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -14,6 +14,7 @@ #include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" #include "hw/pci/pci_bus.h" +#include "hw/pci/pci_bridge.h" #include "hw/i386/pc.h" #include "qemu/range.h" #include "qemu/error-report.h" @@ -42,6 +43,8 @@ typedef struct PXBDev { uint16_t numa_node; } PXBDev; +static GList *pxb_dev_list; + #define TYPE_PXB_HOST "pxb-host" static int pxb_bus_num(PCIBus *bus) @@ -88,12 +91,45 @@ static const char *pxb_host_root_bus_path(PCIHostState *host_bridge, return bus->bus_path; } +static char *pxb_host_ofw_unit_address(const SysBusDevice *dev) +{ + const PCIHostState *pxb_host; + const PCIBus *pxb_bus; + const PXBDev *pxb_dev; + int position; + const DeviceState *pxb_dev_base; + const PCIHostState *main_host; + const SysBusDevice *main_host_sbd; + + pxb_host = PCI_HOST_BRIDGE(dev); + pxb_bus = pxb_host->bus; + pxb_dev = PXB_DEV(pxb_bus->parent_dev); + position = g_list_index(pxb_dev_list, pxb_dev); + assert(position >= 0); + + pxb_dev_base = DEVICE(pxb_dev); + main_host = PCI_HOST_BRIDGE(pxb_dev_base->parent_bus->parent); + main_host_sbd = SYS_BUS_DEVICE(main_host); + + if (main_host_sbd->num_mmio > 0) { + return g_strdup_printf(TARGET_FMT_plx ",%x", + main_host_sbd->mmio[0].addr, position + 1); + } + if (main_host_sbd->num_pio > 0) { + return g_strdup_printf("i%04x,%x", + main_host_sbd->pio[0], position + 1); + } + return NULL; +} + static void pxb_host_class_init(ObjectClass *class, void *data) { DeviceClass *dc = DEVICE_CLASS(class); + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(class); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class); dc->fw_name = "pci"; + sbc->explicit_ofw_unit_address = pxb_host_ofw_unit_address; hc->root_bus_path = pxb_host_root_bus_path; } @@ -148,6 +184,15 @@ static int pxb_map_irq_fn(PCIDevice *pci_dev, int pin) return pin - PCI_SLOT(pxb->devfn); } +static gint pxb_compare(gconstpointer a, gconstpointer b) +{ + const PXBDev *pxb_a = a, *pxb_b = b; + + return pxb_a->bus_nr < pxb_b->bus_nr ? -1 : + pxb_a->bus_nr > pxb_b->bus_nr ? 1 : + 0; +} + static int pxb_dev_initfn(PCIDevice *dev) { PXBDev *pxb = PXB_DEV(dev); @@ -175,7 +220,8 @@ static int pxb_dev_initfn(PCIDevice *dev) bds = qdev_create(BUS(bus), "pci-bridge"); bds->id = dev_name; - qdev_prop_set_uint8(bds, "chassis_nr", pxb->bus_nr); + qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr); + qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false); PCI_HOST_BRIDGE(ds)->bus = bus; @@ -190,9 +236,17 @@ static int pxb_dev_initfn(PCIDevice *dev) PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_HOST); + pxb_dev_list = g_list_insert_sorted(pxb_dev_list, pxb, pxb_compare); return 0; } +static void pxb_dev_exitfn(PCIDevice *pci_dev) +{ + PXBDev *pxb = PXB_DEV(pci_dev); + + pxb_dev_list = g_list_remove(pxb_dev_list, pxb); +} + static Property pxb_dev_properties[] = { /* Note: 0 is not a legal a PXB bus number. */ DEFINE_PROP_UINT8("bus_nr", PXBDev, bus_nr, 0), @@ -206,6 +260,7 @@ static void pxb_dev_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->init = pxb_dev_initfn; + k->exit = pxb_dev_exitfn; k->vendor_id = PCI_VENDOR_ID_REDHAT; k->device_id = PCI_DEVICE_ID_REDHAT_PXB; k->class_id = PCI_CLASS_BRIDGE_HOST; diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 99db56c8d0..2a4b8e18b3 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -51,8 +51,6 @@ # define LOG_TB(...) do { } while (0) #endif -#define NSEC_PER_SEC 1000000000LL - static void cpu_ppc_tb_stop (CPUPPCState *env); static void cpu_ppc_tb_start (CPUPPCState *env); diff --git a/hw/ppc/spapr_rtc.c b/hw/ppc/spapr_rtc.c index 83eb7c186f..9da3746e7c 100644 --- a/hw/ppc/spapr_rtc.c +++ b/hw/ppc/spapr_rtc.c @@ -26,6 +26,7 @@ * */ #include "cpu.h" +#include "qemu/timer.h" #include "sysemu/sysemu.h" #include "hw/ppc/spapr.h" #include "qapi-event.h" @@ -40,8 +41,6 @@ struct sPAPRRTCState { int64_t ns_offset; }; -#define NSEC_PER_SEC 1000000000LL - void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns) { sPAPRRTCState *rtc = SPAPR_RTC(dev); diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 5561d807dc..5df450e00b 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -588,6 +588,7 @@ int css_do_msch(SubchDev *sch, const SCHIB *orig_schib) { SCSW *s = &sch->curr_status.scsw; PMCW *p = &sch->curr_status.pmcw; + uint16_t oldflags; int ret; SCHIB schib; @@ -610,6 +611,7 @@ int css_do_msch(SubchDev *sch, const SCHIB *orig_schib) copy_schib_from_guest(&schib, orig_schib); /* Only update the program-modifiable fields. */ p->intparm = schib.pmcw.intparm; + oldflags = p->flags; p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | PMCW_FLAGS_MASK_MP); @@ -625,6 +627,12 @@ int css_do_msch(SubchDev *sch, const SCHIB *orig_schib) (PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_CSENSE); sch->curr_status.mba = schib.mba; + /* Has the channel been disabled? */ + if (sch->disable_cb && (oldflags & PMCW_FLAGS_MASK_ENA) != 0 + && (p->flags & PMCW_FLAGS_MASK_ENA) == 0) { + sch->disable_cb(sch); + } + ret = 0; out: @@ -1464,6 +1472,21 @@ int subch_device_load(SubchDev *s, QEMUFile *f) } s->ccw_fmt_1 = qemu_get_byte(f); s->ccw_no_data_cnt = qemu_get_byte(f); + /* + * Hack alert. We don't migrate the channel subsystem status (no + * device!), but we need to find out if the guest enabled mss/mcss-e. + * If the subchannel is enabled, it certainly was able to access it, + * so adjust the max_ssid/max_cssid values for relevant ssid/cssid + * values. This is not watertight, but better than nothing. + */ + if (s->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA) { + if (s->ssid) { + channel_subsys->max_ssid = MAX_SSID; + } + if (s->cssid != channel_subsys->default_cssid) { + channel_subsys->max_cssid = MAX_CSSID; + } + } return 0; } @@ -1483,6 +1506,10 @@ void css_reset_sch(SubchDev *sch) { PMCW *p = &sch->curr_status.pmcw; + if ((p->flags & PMCW_FLAGS_MASK_ENA) != 0 && sch->disable_cb) { + sch->disable_cb(sch); + } + p->intparm = 0; p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | diff --git a/hw/s390x/css.h b/hw/s390x/css.h index 7e53148700..a09bb1f87c 100644 --- a/hw/s390x/css.h +++ b/hw/s390x/css.h @@ -81,6 +81,7 @@ struct SubchDev { uint8_t ccw_no_data_cnt; /* transport-provided data: */ int (*ccw_cb) (SubchDev *, CCW1); + void (*disable_cb)(SubchDev *); SenseId id; void *driver_data; }; diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 132004ae4f..2e0a8b6e0c 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -218,7 +218,7 @@ static Property s390_ipl_properties[] = { * - -1 if no valid boot device was found * - ccw id of the boot device otherwise */ -static uint32_t s390_update_iplstate(CPUS390XState *env, S390IPLState *ipl) +static uint64_t s390_update_iplstate(CPUS390XState *env, S390IPLState *ipl) { DeviceState *dev_st; @@ -248,7 +248,7 @@ static uint32_t s390_update_iplstate(CPUS390XState *env, S390IPLState *ipl) return -1; out: - return ipl->cssid << 24 | ipl->ssid << 16 | ipl->devno; + return (uint32_t) (ipl->cssid << 24 | ipl->ssid << 16 | ipl->devno); } int s390_ipl_update_diag308(IplParameterBlock *iplb) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index c574988c36..3d20d6a0f7 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -204,9 +204,6 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); - mc->name = "s390-ccw-virtio"; - mc->alias = "s390-ccw"; - mc->desc = "VirtIO-ccw based S390 machine"; mc->init = ccw_init; mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; @@ -216,7 +213,6 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) mc->no_sdcard = 1; mc->use_sclp = 1; mc->max_cpus = 255; - mc->is_default = 1; nc->nmi_monitor_handler = s390_nmi; } @@ -272,6 +268,7 @@ static inline void s390_machine_initfn(Object *obj) static const TypeInfo ccw_machine_info = { .name = TYPE_S390_CCW_MACHINE, .parent = TYPE_MACHINE, + .abstract = true, .instance_size = sizeof(S390CcwMachineState), .instance_init = s390_machine_initfn, .class_init = ccw_machine_class_init, @@ -281,9 +278,26 @@ static const TypeInfo ccw_machine_info = { }, }; +static void ccw_machine_2_4_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->name = "s390-ccw-virtio-2.4"; + mc->alias = "s390-ccw-virtio"; + mc->desc = "VirtIO-ccw based S390 machine v2.4"; + mc->is_default = 1; +} + +static const TypeInfo ccw_machine_2_4_info = { + .name = TYPE_S390_CCW_MACHINE "2.4", + .parent = TYPE_S390_CCW_MACHINE, + .class_init = ccw_machine_2_4_class_init, +}; + static void ccw_machine_register_types(void) { type_register_static(&ccw_machine_info); + type_register_static(&ccw_machine_2_4_info); } type_init(ccw_machine_register_types) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 9605bf40b9..d631337e11 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -21,6 +21,7 @@ #include "hw/sysbus.h" #include "qemu/bitops.h" #include "qemu/error-report.h" +#include "hw/virtio/virtio-access.h" #include "hw/virtio/virtio-bus.h" #include "hw/s390x/adapter.h" #include "hw/s390x/s390_flic.h" @@ -237,11 +238,20 @@ VirtualCssBus *virtual_css_bus_init(void) } /* Communication blocks used by several channel commands. */ -typedef struct VqInfoBlock { +typedef struct VqInfoBlockLegacy { uint64_t queue; uint32_t align; uint16_t index; uint16_t num; +} QEMU_PACKED VqInfoBlockLegacy; + +typedef struct VqInfoBlock { + uint64_t desc; + uint32_t res0; + uint16_t index; + uint16_t num; + uint64_t avail; + uint64_t used; } QEMU_PACKED VqInfoBlock; typedef struct VqConfigBlock { @@ -261,18 +271,27 @@ typedef struct VirtioThinintInfo { uint8_t isc; } QEMU_PACKED VirtioThinintInfo; +typedef struct VirtioRevInfo { + uint16_t revision; + uint16_t length; + uint8_t data[0]; +} QEMU_PACKED VirtioRevInfo; + /* Specify where the virtqueues for the subchannel are in guest memory. */ -static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, - uint16_t index, uint16_t num) +static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info, + VqInfoBlockLegacy *linfo) { VirtIODevice *vdev = virtio_ccw_get_vdev(sch); + uint16_t index = info ? info->index : linfo->index; + uint16_t num = info ? info->num : linfo->num; + uint64_t desc = info ? info->desc : linfo->queue; if (index >= VIRTIO_CCW_QUEUE_MAX) { return -EINVAL; } /* Current code in virtio.c relies on 4K alignment. */ - if (addr && (align != 4096)) { + if (linfo && desc && (linfo->align != 4096)) { return -EINVAL; } @@ -280,8 +299,12 @@ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, return -EINVAL; } - virtio_queue_set_addr(vdev, index, addr); - if (!addr) { + if (info) { + virtio_queue_set_rings(vdev, index, desc, info->avail, info->used); + } else { + virtio_queue_set_addr(vdev, index, desc); + } + if (!desc) { virtio_queue_set_vector(vdev, index, VIRTIO_NO_VECTOR); } else { /* Fail if we don't have a big enough queue. */ @@ -296,10 +319,98 @@ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, return 0; } -static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) +static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev) +{ + virtio_ccw_stop_ioeventfd(dev); + virtio_reset(vdev); + if (dev->indicators) { + release_indicator(&dev->routes.adapter, dev->indicators); + dev->indicators = NULL; + } + if (dev->indicators2) { + release_indicator(&dev->routes.adapter, dev->indicators2); + dev->indicators2 = NULL; + } + if (dev->summary_indicator) { + release_indicator(&dev->routes.adapter, dev->summary_indicator); + dev->summary_indicator = NULL; + } + dev->sch->thinint_active = false; +} + +static int virtio_ccw_handle_set_vq(SubchDev *sch, CCW1 ccw, bool check_len, + bool is_legacy) { int ret; VqInfoBlock info; + VqInfoBlockLegacy linfo; + size_t info_len = is_legacy ? sizeof(linfo) : sizeof(info); + + if (check_len) { + if (ccw.count != info_len) { + return -EINVAL; + } + } else if (ccw.count < info_len) { + /* Can't execute command. */ + return -EINVAL; + } + if (!ccw.cda) { + return -EFAULT; + } + if (is_legacy) { + linfo.queue = address_space_ldq_be(&address_space_memory, ccw.cda, + MEMTXATTRS_UNSPECIFIED, NULL); + linfo.align = address_space_ldl_be(&address_space_memory, + ccw.cda + sizeof(linfo.queue), + MEMTXATTRS_UNSPECIFIED, + NULL); + linfo.index = address_space_lduw_be(&address_space_memory, + ccw.cda + sizeof(linfo.queue) + + sizeof(linfo.align), + MEMTXATTRS_UNSPECIFIED, + NULL); + linfo.num = address_space_lduw_be(&address_space_memory, + ccw.cda + sizeof(linfo.queue) + + sizeof(linfo.align) + + sizeof(linfo.index), + MEMTXATTRS_UNSPECIFIED, + NULL); + ret = virtio_ccw_set_vqs(sch, NULL, &linfo); + } else { + info.desc = address_space_ldq_be(&address_space_memory, ccw.cda, + MEMTXATTRS_UNSPECIFIED, NULL); + info.index = address_space_lduw_be(&address_space_memory, + ccw.cda + sizeof(info.desc) + + sizeof(info.res0), + MEMTXATTRS_UNSPECIFIED, NULL); + info.num = address_space_lduw_be(&address_space_memory, + ccw.cda + sizeof(info.desc) + + sizeof(info.res0) + + sizeof(info.index), + MEMTXATTRS_UNSPECIFIED, NULL); + info.avail = address_space_ldq_be(&address_space_memory, + ccw.cda + sizeof(info.desc) + + sizeof(info.res0) + + sizeof(info.index) + + sizeof(info.num), + MEMTXATTRS_UNSPECIFIED, NULL); + info.used = address_space_ldq_be(&address_space_memory, + ccw.cda + sizeof(info.desc) + + sizeof(info.res0) + + sizeof(info.index) + + sizeof(info.num) + + sizeof(info.avail), + MEMTXATTRS_UNSPECIFIED, NULL); + ret = virtio_ccw_set_vqs(sch, &info, NULL); + } + sch->curr_status.scsw.count = 0; + return ret; +} + +static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) +{ + int ret; + VirtioRevInfo revinfo; uint8_t status; VirtioFeatDesc features; void *config; @@ -323,44 +434,10 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) /* Look at the command. */ switch (ccw.cmd_code) { case CCW_CMD_SET_VQ: - if (check_len) { - if (ccw.count != sizeof(info)) { - ret = -EINVAL; - break; - } - } else if (ccw.count < sizeof(info)) { - /* Can't execute command. */ - ret = -EINVAL; - break; - } - if (!ccw.cda) { - ret = -EFAULT; - } else { - info.queue = address_space_ldq(&address_space_memory, ccw.cda, - MEMTXATTRS_UNSPECIFIED, NULL); - info.align = address_space_ldl(&address_space_memory, - ccw.cda + sizeof(info.queue), - MEMTXATTRS_UNSPECIFIED, - NULL); - info.index = address_space_lduw(&address_space_memory, - ccw.cda + sizeof(info.queue) - + sizeof(info.align), - MEMTXATTRS_UNSPECIFIED, - NULL); - info.num = address_space_lduw(&address_space_memory, - ccw.cda + sizeof(info.queue) - + sizeof(info.align) - + sizeof(info.index), - MEMTXATTRS_UNSPECIFIED, - NULL); - ret = virtio_ccw_set_vqs(sch, info.queue, info.align, info.index, - info.num); - sch->curr_status.scsw.count = 0; - } + ret = virtio_ccw_handle_set_vq(sch, ccw, check_len, dev->revision < 1); break; case CCW_CMD_VDEV_RESET: - virtio_ccw_stop_ioeventfd(dev); - virtio_reset(vdev); + virtio_ccw_reset_virtio(dev, vdev); ret = 0; break; case CCW_CMD_READ_FEAT: @@ -383,7 +460,16 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) MEMTXATTRS_UNSPECIFIED, NULL); if (features.index == 0) { - features.features = vdev->host_features; + features.features = (uint32_t)vdev->host_features; + } else if (features.index == 1) { + features.features = (uint32_t)(vdev->host_features >> 32); + /* + * Don't offer version 1 to the guest if it did not + * negotiate at least revision 1. + */ + if (dev->revision <= 0) { + features.features &= ~(1 << (VIRTIO_F_VERSION_1 - 32)); + } } else { /* Return zeroes if the guest supports more feature bits. */ features.features = 0; @@ -419,7 +505,20 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) MEMTXATTRS_UNSPECIFIED, NULL); if (features.index == 0) { - virtio_set_features(vdev, features.features); + virtio_set_features(vdev, + (vdev->guest_features & 0xffffffff00000000ULL) | + features.features); + } else if (features.index == 1) { + /* + * The guest should not set version 1 if it didn't + * negotiate a revision >= 1. + */ + if (dev->revision <= 0) { + features.features &= ~(1 << (VIRTIO_F_VERSION_1 - 32)); + } + virtio_set_features(vdev, + (vdev->guest_features & 0x00000000ffffffffULL) | + ((uint64_t)features.features << 32)); } else { /* * If the guest supports more feature bits, assert that it @@ -500,7 +599,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) } if (virtio_set_status(vdev, status) == 0) { if (vdev->status == 0) { - virtio_reset(vdev); + virtio_ccw_reset_virtio(dev, vdev); } if (status & VIRTIO_CONFIG_S_DRIVER_OK) { virtio_ccw_start_ioeventfd(dev); @@ -640,6 +739,40 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) } } break; + case CCW_CMD_SET_VIRTIO_REV: + len = sizeof(revinfo); + if (ccw.count < len) { + ret = -EINVAL; + break; + } + if (!ccw.cda) { + ret = -EFAULT; + break; + } + revinfo.revision = + address_space_lduw_be(&address_space_memory, ccw.cda, + MEMTXATTRS_UNSPECIFIED, NULL); + revinfo.length = + address_space_lduw_be(&address_space_memory, + ccw.cda + sizeof(revinfo.revision), + MEMTXATTRS_UNSPECIFIED, NULL); + if (ccw.count < len + revinfo.length || + (check_len && ccw.count > len + revinfo.length)) { + ret = -EINVAL; + break; + } + /* + * Once we start to support revisions with additional data, we'll + * need to fetch it here. Nothing to do for now, though. + */ + if (dev->revision >= 0 || + revinfo.revision > virtio_ccw_rev_max(vdev)) { + ret = -ENOSYS; + break; + } + ret = 0; + dev->revision = revinfo.revision; + break; default: ret = -ENOSYS; break; @@ -647,6 +780,13 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) return ret; } +static void virtio_sch_disable_cb(SubchDev *sch) +{ + VirtioCcwDevice *dev = sch->driver_data; + + dev->revision = -1; +} + static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) { unsigned int cssid = 0; @@ -766,12 +906,15 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); sch->ccw_cb = virtio_ccw_cb; + sch->disable_cb = virtio_sch_disable_cb; /* Build senseid data. */ memset(&sch->id, 0, sizeof(SenseId)); sch->id.reserved = 0xff; sch->id.cu_type = VIRTIO_CCW_CU_TYPE; + dev->revision = -1; + if (k->realize) { k->realize(dev, &err); } @@ -1081,21 +1224,8 @@ static void virtio_ccw_reset(DeviceState *d) VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - virtio_ccw_stop_ioeventfd(dev); - virtio_reset(vdev); + virtio_ccw_reset_virtio(dev, vdev); css_reset_sch(dev->sch); - if (dev->indicators) { - release_indicator(&dev->routes.adapter, dev->indicators); - dev->indicators = NULL; - } - if (dev->indicators2) { - release_indicator(&dev->routes.adapter, dev->indicators2); - dev->indicators2 = NULL; - } - if (dev->summary_indicator) { - release_indicator(&dev->routes.adapter, dev->summary_indicator); - dev->summary_indicator = NULL; - } } static void virtio_ccw_vmstate_change(DeviceState *d, bool running) @@ -1342,6 +1472,7 @@ static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f) qemu_put_be16(f, vdev->config_vector); qemu_put_be64(f, dev->routes.adapter.ind_offset); qemu_put_byte(f, dev->thinint_isc); + qemu_put_be32(f, dev->revision); } static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) @@ -1382,6 +1513,7 @@ static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) dev->thinint_isc, true, false, &dev->routes.adapter.adapter_id); } + dev->revision = qemu_get_be32(f); return 0; } diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index d729263960..692ddd7318 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -41,6 +41,7 @@ #define CCW_CMD_SET_CONF_IND 0x53 #define CCW_CMD_READ_VQ_CONF 0x32 #define CCW_CMD_SET_IND_ADAPTER 0x73 +#define CCW_CMD_SET_VIRTIO_REV 0x83 #define TYPE_VIRTIO_CCW_DEVICE "virtio-ccw-device" #define VIRTIO_CCW_DEVICE(obj) \ @@ -86,6 +87,7 @@ struct VirtioCcwDevice { DeviceState parent_obj; SubchDev *sch; char *bus_id; + int revision; VirtioBusState bus; bool ioeventfd_started; bool ioeventfd_disabled; @@ -99,6 +101,12 @@ struct VirtioCcwDevice { uint64_t ind_bit; }; +/* The maximum virtio revision we support. */ +static inline int virtio_ccw_rev_max(VirtIODevice *vdev) +{ + return 0; +} + /* virtual css bus type */ typedef struct VirtualCssBus { BusState parent_obj; diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c index 8b93b3c1ae..3e59c2a288 100644 --- a/hw/timer/arm_mptimer.c +++ b/hw/timer/arm_mptimer.c @@ -38,7 +38,7 @@ static inline int get_current_cpu(ARMMPTimerState *s) static inline void timerblock_update_irq(TimerBlock *tb) { - qemu_set_irq(tb->irq, tb->status); + qemu_set_irq(tb->irq, tb->status && (tb->control & 4)); } /* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ @@ -122,11 +122,18 @@ static void timerblock_write(void *opaque, hwaddr addr, case 8: /* Control. */ old = tb->control; tb->control = value; - if (((old & 1) == 0) && (value & 1)) { - if (tb->count == 0 && (tb->control & 2)) { + if (value & 1) { + if ((old & 1) && (tb->count != 0)) { + /* Do nothing if timer is ticking right now. */ + break; + } + if (tb->control & 2) { tb->count = tb->load; } timerblock_reload(tb, 1); + } else if (old & 1) { + /* Shutdown the timer. */ + timer_del(tb->timer); } break; case 12: /* Interrupt status. */ diff --git a/hw/timer/cadence_ttc.c b/hw/timer/cadence_ttc.c index d46db3c0e2..35bc88033e 100644 --- a/hw/timer/cadence_ttc.c +++ b/hw/timer/cadence_ttc.c @@ -208,15 +208,14 @@ static void cadence_timer_sync(CadenceTimerState *s) s->reg_intr |= (2 << i); } } + if ((x < 0) || (x >= interval)) { + s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ? + COUNTER_INTR_IV : COUNTER_INTR_OV; + } while (x < 0) { x += interval; } s->reg_value = (uint32_t)(x % interval); - - if (s->reg_value != x) { - s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ? - COUNTER_INTR_IV : COUNTER_INTR_OV; - } cadence_timer_update(s); } diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 2e3ffc8c07..954c34d6cc 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -48,7 +48,6 @@ # define DPRINTF_C(format, ...) do { } while (0) #endif -#define NSEC_PER_SEC 1000000000LL #define SEC_PER_MIN 60 #define MIN_PER_HOUR 60 #define SEC_PER_HOUR 3600 diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index d4d754765b..d7cd40ba12 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -32,7 +32,7 @@ #include "trace.h" #define FRAME_TIMER_FREQ 1000 -#define FRAME_TIMER_NS (1000000000 / FRAME_TIMER_FREQ) +#define FRAME_TIMER_NS (NSEC_PER_SEC / FRAME_TIMER_FREQ) #define UFRAME_TIMER_NS (FRAME_TIMER_NS / 8) #define NB_MAXINTRATE 8 // Max rate at which controller issues ints diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index a6dcc79399..2712c6fc0a 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -999,7 +999,7 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); VirtioBusState *vbus = VIRTIO_BUS(qbus); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); - int i, r; + int i, r, e; if (!k->set_host_notifier) { fprintf(stderr, "binding does not support host notifiers\n"); r = -ENOSYS; @@ -1017,12 +1017,12 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) return 0; fail_vq: while (--i >= 0) { - r = k->set_host_notifier(qbus->parent, hdev->vq_index + i, false); - if (r < 0) { + e = k->set_host_notifier(qbus->parent, hdev->vq_index + i, false); + if (e < 0) { fprintf(stderr, "vhost VQ %d notifier cleanup error: %d\n", i, -r); fflush(stderr); } - assert (r >= 0); + assert (e >= 0); } fail: return r; diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 78bc14fc85..2990f8de5d 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -312,6 +312,8 @@ static void virtio_balloon_set_config(VirtIODevice *vdev, static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f) { + VirtIOBalloon *dev = VIRTIO_BALLOON(vdev); + f |= dev->host_features; virtio_add_feature(&f, VIRTIO_BALLOON_F_STATS_VQ); return f; } @@ -423,6 +425,8 @@ static void virtio_balloon_instance_init(Object *obj) } static Property virtio_balloon_properties[] = { + DEFINE_PROP_BIT("deflate-on-oom", VirtIOBalloon, host_features, + VIRTIO_BALLOON_F_DEFLATE_ON_OOM, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 70bc6d801e..6a0174e9cc 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -977,7 +977,7 @@ static uint64_t virtio_pci_common_read(void *opaque, hwaddr addr, val = proxy->gfselect; break; case VIRTIO_PCI_COMMON_GF: - if (proxy->gfselect <= ARRAY_SIZE(proxy->guest_features)) { + if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features)) { val = proxy->guest_features[proxy->gfselect]; } break; @@ -1052,7 +1052,7 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr, proxy->gfselect = val; break; case VIRTIO_PCI_COMMON_GF: - if (proxy->gfselect <= ARRAY_SIZE(proxy->guest_features)) { + if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features)) { proxy->guest_features[proxy->gfselect] = val; virtio_set_features(vdev, (((uint64_t)proxy->guest_features[1]) << 32) | |