diff options
Diffstat (limited to 'hw')
55 files changed, 1443 insertions, 987 deletions
diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs index 489e63bb75..834c63b980 100644 --- a/hw/acpi/Makefile.objs +++ b/hw/acpi/Makefile.objs @@ -1,7 +1,7 @@ common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o pcihp.o common-obj-$(CONFIG_ACPI_X86_ICH) += ich9.o tco.o common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o -common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o memory_hotplug_acpi_table.o +common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o common-obj-$(CONFIG_ACPI) += acpi_interface.o diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 830c475127..5c279bbaca 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -306,7 +306,8 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, if (pm->acpi_memory_hotplug.is_enabled) { acpi_memory_hotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), - &pm->acpi_memory_hotplug); + &pm->acpi_memory_hotplug, + ACPI_MEMORY_HOTPLUG_BASE); } } diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index ec4e64b361..210073d283 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c @@ -7,6 +7,34 @@ #include "trace.h" #include "qapi-event.h" +#define MEMORY_SLOTS_NUMBER "MDNR" +#define MEMORY_HOTPLUG_IO_REGION "HPMR" +#define MEMORY_SLOT_ADDR_LOW "MRBL" +#define MEMORY_SLOT_ADDR_HIGH "MRBH" +#define MEMORY_SLOT_SIZE_LOW "MRLL" +#define MEMORY_SLOT_SIZE_HIGH "MRLH" +#define MEMORY_SLOT_PROXIMITY "MPX" +#define MEMORY_SLOT_ENABLED "MES" +#define MEMORY_SLOT_INSERT_EVENT "MINS" +#define MEMORY_SLOT_REMOVE_EVENT "MRMV" +#define MEMORY_SLOT_EJECT "MEJ" +#define MEMORY_SLOT_SLECTOR "MSEL" +#define MEMORY_SLOT_OST_EVENT "MOEV" +#define MEMORY_SLOT_OST_STATUS "MOSC" +#define MEMORY_SLOT_LOCK "MLCK" +#define MEMORY_SLOT_STATUS_METHOD "MRST" +#define MEMORY_SLOT_CRS_METHOD "MCRS" +#define MEMORY_SLOT_OST_METHOD "MOST" +#define MEMORY_SLOT_PROXIMITY_METHOD "MPXM" +#define MEMORY_SLOT_EJECT_METHOD "MEJ0" +#define MEMORY_SLOT_NOTIFY_METHOD "MTFY" +#define MEMORY_SLOT_SCAN_METHOD "MSCN" +#define MEMORY_HOTPLUG_DEVICE "MHPD" +#define MEMORY_HOTPLUG_IO_LEN 24 +#define MEMORY_DEVICES_CONTAINER "\\_SB.MHPC" + +static uint16_t memhp_io_base; + static ACPIOSTInfo *acpi_memory_device_status(int slot, MemStatus *mdev) { ACPIOSTInfo *info = g_new0(ACPIOSTInfo, 1); @@ -178,7 +206,7 @@ static const MemoryRegionOps acpi_memory_hotplug_ops = { }; void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner, - MemHotplugState *state) + MemHotplugState *state, uint16_t io_base) { MachineState *machine = MACHINE(qdev_get_machine()); @@ -187,10 +215,12 @@ void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner, return; } + assert(!memhp_io_base); + memhp_io_base = io_base; state->devs = g_malloc0(sizeof(*state->devs) * state->dev_count); memory_region_init_io(&state->io, owner, &acpi_memory_hotplug_ops, state, - "acpi-mem-hotplug", ACPI_MEMORY_HOTPLUG_IO_LEN); - memory_region_add_subregion(as, ACPI_MEMORY_HOTPLUG_BASE, &state->io); + "acpi-mem-hotplug", MEMORY_HOTPLUG_IO_LEN); + memory_region_add_subregion(as, memhp_io_base, &state->io); } /** @@ -306,3 +336,387 @@ const VMStateDescription vmstate_memory_hotplug = { VMSTATE_END_OF_LIST() } }; + +void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem, + const char *res_root, + const char *event_handler_method) +{ + int i; + Aml *ifctx; + Aml *method; + Aml *dev_container; + Aml *mem_ctrl_dev; + char *mhp_res_path; + + if (!memhp_io_base) { + return; + } + + mhp_res_path = g_strdup_printf("%s." MEMORY_HOTPLUG_DEVICE, res_root); + mem_ctrl_dev = aml_device("%s", mhp_res_path); + { + Aml *crs; + + aml_append(mem_ctrl_dev, aml_name_decl("_HID", aml_string("PNP0A06"))); + aml_append(mem_ctrl_dev, + aml_name_decl("_UID", aml_string("Memory hotplug resources"))); + + crs = aml_resource_template(); + aml_append(crs, + aml_io(AML_DECODE16, memhp_io_base, memhp_io_base, 0, + MEMORY_HOTPLUG_IO_LEN) + ); + aml_append(mem_ctrl_dev, aml_name_decl("_CRS", crs)); + + aml_append(mem_ctrl_dev, aml_operation_region( + MEMORY_HOTPLUG_IO_REGION, AML_SYSTEM_IO, + aml_int(memhp_io_base), MEMORY_HOTPLUG_IO_LEN) + ); + + } + aml_append(table, mem_ctrl_dev); + + dev_container = aml_device(MEMORY_DEVICES_CONTAINER); + { + Aml *field; + Aml *one = aml_int(1); + Aml *zero = aml_int(0); + Aml *ret_val = aml_local(0); + Aml *slot_arg0 = aml_arg(0); + Aml *slots_nr = aml_name(MEMORY_SLOTS_NUMBER); + Aml *ctrl_lock = aml_name(MEMORY_SLOT_LOCK); + Aml *slot_selector = aml_name(MEMORY_SLOT_SLECTOR); + char *mmio_path = g_strdup_printf("%s." MEMORY_HOTPLUG_IO_REGION, + mhp_res_path); + + aml_append(dev_container, aml_name_decl("_HID", aml_string("PNP0A06"))); + aml_append(dev_container, + aml_name_decl("_UID", aml_string("DIMM devices"))); + + assert(nr_mem <= ACPI_MAX_RAM_SLOTS); + aml_append(dev_container, + aml_name_decl(MEMORY_SLOTS_NUMBER, aml_int(nr_mem)) + ); + + field = aml_field(mmio_path, AML_DWORD_ACC, + AML_NOLOCK, AML_PRESERVE); + aml_append(field, /* read only */ + aml_named_field(MEMORY_SLOT_ADDR_LOW, 32)); + aml_append(field, /* read only */ + aml_named_field(MEMORY_SLOT_ADDR_HIGH, 32)); + aml_append(field, /* read only */ + aml_named_field(MEMORY_SLOT_SIZE_LOW, 32)); + aml_append(field, /* read only */ + aml_named_field(MEMORY_SLOT_SIZE_HIGH, 32)); + aml_append(field, /* read only */ + aml_named_field(MEMORY_SLOT_PROXIMITY, 32)); + aml_append(dev_container, field); + + field = aml_field(mmio_path, AML_BYTE_ACC, + AML_NOLOCK, AML_WRITE_AS_ZEROS); + aml_append(field, aml_reserved_field(160 /* bits, Offset(20) */)); + aml_append(field, /* 1 if enabled, read only */ + aml_named_field(MEMORY_SLOT_ENABLED, 1)); + aml_append(field, + /*(read) 1 if has a insert event. (write) 1 to clear event */ + aml_named_field(MEMORY_SLOT_INSERT_EVENT, 1)); + aml_append(field, + /* (read) 1 if has a remove event. (write) 1 to clear event */ + aml_named_field(MEMORY_SLOT_REMOVE_EVENT, 1)); + aml_append(field, + /* initiates device eject, write only */ + aml_named_field(MEMORY_SLOT_EJECT, 1)); + aml_append(dev_container, field); + + field = aml_field(mmio_path, AML_DWORD_ACC, + AML_NOLOCK, AML_PRESERVE); + aml_append(field, /* DIMM selector, write only */ + aml_named_field(MEMORY_SLOT_SLECTOR, 32)); + aml_append(field, /* _OST event code, write only */ + aml_named_field(MEMORY_SLOT_OST_EVENT, 32)); + aml_append(field, /* _OST status code, write only */ + aml_named_field(MEMORY_SLOT_OST_STATUS, 32)); + aml_append(dev_container, field); + g_free(mmio_path); + + method = aml_method("_STA", 0, AML_NOTSERIALIZED); + ifctx = aml_if(aml_equal(slots_nr, zero)); + { + aml_append(ifctx, aml_return(zero)); + } + aml_append(method, ifctx); + /* present, functioning, decoding, not shown in UI */ + aml_append(method, aml_return(aml_int(0xB))); + aml_append(dev_container, method); + + aml_append(dev_container, aml_mutex(MEMORY_SLOT_LOCK, 0)); + + method = aml_method(MEMORY_SLOT_SCAN_METHOD, 0, AML_NOTSERIALIZED); + { + Aml *else_ctx; + Aml *while_ctx; + Aml *idx = aml_local(0); + Aml *eject_req = aml_int(3); + Aml *dev_chk = aml_int(1); + + ifctx = aml_if(aml_equal(slots_nr, zero)); + { + aml_append(ifctx, aml_return(zero)); + } + aml_append(method, ifctx); + + aml_append(method, aml_store(zero, idx)); + aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); + /* build AML that: + * loops over all slots and Notifies DIMMs with + * Device Check or Eject Request notifications if + * slot has corresponding status bit set and clears + * slot status. + */ + while_ctx = aml_while(aml_lless(idx, slots_nr)); + { + Aml *ins_evt = aml_name(MEMORY_SLOT_INSERT_EVENT); + Aml *rm_evt = aml_name(MEMORY_SLOT_REMOVE_EVENT); + + aml_append(while_ctx, aml_store(idx, slot_selector)); + ifctx = aml_if(aml_equal(ins_evt, one)); + { + aml_append(ifctx, + aml_call2(MEMORY_SLOT_NOTIFY_METHOD, + idx, dev_chk)); + aml_append(ifctx, aml_store(one, ins_evt)); + } + aml_append(while_ctx, ifctx); + + else_ctx = aml_else(); + ifctx = aml_if(aml_equal(rm_evt, one)); + { + aml_append(ifctx, + aml_call2(MEMORY_SLOT_NOTIFY_METHOD, + idx, eject_req)); + aml_append(ifctx, aml_store(one, rm_evt)); + } + aml_append(else_ctx, ifctx); + aml_append(while_ctx, else_ctx); + + aml_append(while_ctx, aml_add(idx, one, idx)); + } + aml_append(method, while_ctx); + aml_append(method, aml_release(ctrl_lock)); + aml_append(method, aml_return(one)); + } + aml_append(dev_container, method); + + method = aml_method(MEMORY_SLOT_STATUS_METHOD, 1, AML_NOTSERIALIZED); + { + Aml *slot_enabled = aml_name(MEMORY_SLOT_ENABLED); + + aml_append(method, aml_store(zero, ret_val)); + aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); + aml_append(method, + aml_store(aml_to_integer(slot_arg0), slot_selector)); + + ifctx = aml_if(aml_equal(slot_enabled, one)); + { + aml_append(ifctx, aml_store(aml_int(0xF), ret_val)); + } + aml_append(method, ifctx); + + aml_append(method, aml_release(ctrl_lock)); + aml_append(method, aml_return(ret_val)); + } + aml_append(dev_container, method); + + method = aml_method(MEMORY_SLOT_CRS_METHOD, 1, AML_SERIALIZED); + { + Aml *mr64 = aml_name("MR64"); + Aml *mr32 = aml_name("MR32"); + Aml *crs_tmpl = aml_resource_template(); + Aml *minl = aml_name("MINL"); + Aml *minh = aml_name("MINH"); + Aml *maxl = aml_name("MAXL"); + Aml *maxh = aml_name("MAXH"); + Aml *lenl = aml_name("LENL"); + Aml *lenh = aml_name("LENH"); + + aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); + aml_append(method, aml_store(aml_to_integer(slot_arg0), + slot_selector)); + + aml_append(crs_tmpl, + aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, + AML_CACHEABLE, AML_READ_WRITE, + 0, 0x0, 0xFFFFFFFFFFFFFFFEULL, 0, + 0xFFFFFFFFFFFFFFFFULL)); + aml_append(method, aml_name_decl("MR64", crs_tmpl)); + aml_append(method, + aml_create_dword_field(mr64, aml_int(14), "MINL")); + aml_append(method, + aml_create_dword_field(mr64, aml_int(18), "MINH")); + aml_append(method, + aml_create_dword_field(mr64, aml_int(38), "LENL")); + aml_append(method, + aml_create_dword_field(mr64, aml_int(42), "LENH")); + aml_append(method, + aml_create_dword_field(mr64, aml_int(22), "MAXL")); + aml_append(method, + aml_create_dword_field(mr64, aml_int(26), "MAXH")); + + aml_append(method, + aml_store(aml_name(MEMORY_SLOT_ADDR_HIGH), minh)); + aml_append(method, + aml_store(aml_name(MEMORY_SLOT_ADDR_LOW), minl)); + aml_append(method, + aml_store(aml_name(MEMORY_SLOT_SIZE_HIGH), lenh)); + aml_append(method, + aml_store(aml_name(MEMORY_SLOT_SIZE_LOW), lenl)); + + /* 64-bit math: MAX = MIN + LEN - 1 */ + aml_append(method, aml_add(minl, lenl, maxl)); + aml_append(method, aml_add(minh, lenh, maxh)); + ifctx = aml_if(aml_lless(maxl, minl)); + { + aml_append(ifctx, aml_add(maxh, one, maxh)); + } + aml_append(method, ifctx); + ifctx = aml_if(aml_lless(maxl, one)); + { + aml_append(ifctx, aml_subtract(maxh, one, maxh)); + } + aml_append(method, ifctx); + aml_append(method, aml_subtract(maxl, one, maxl)); + + /* return 32-bit _CRS if addr/size is in low mem */ + /* TODO: remove it since all hotplugged DIMMs are in high mem */ + ifctx = aml_if(aml_equal(maxh, zero)); + { + crs_tmpl = aml_resource_template(); + aml_append(crs_tmpl, + aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, + AML_MAX_FIXED, AML_CACHEABLE, + AML_READ_WRITE, + 0, 0x0, 0xFFFFFFFE, 0, + 0xFFFFFFFF)); + aml_append(ifctx, aml_name_decl("MR32", crs_tmpl)); + aml_append(ifctx, + aml_create_dword_field(mr32, aml_int(10), "MIN")); + aml_append(ifctx, + aml_create_dword_field(mr32, aml_int(14), "MAX")); + aml_append(ifctx, + aml_create_dword_field(mr32, aml_int(22), "LEN")); + aml_append(ifctx, aml_store(minl, aml_name("MIN"))); + aml_append(ifctx, aml_store(maxl, aml_name("MAX"))); + aml_append(ifctx, aml_store(lenl, aml_name("LEN"))); + + aml_append(ifctx, aml_release(ctrl_lock)); + aml_append(ifctx, aml_return(mr32)); + } + aml_append(method, ifctx); + + aml_append(method, aml_release(ctrl_lock)); + aml_append(method, aml_return(mr64)); + } + aml_append(dev_container, method); + + method = aml_method(MEMORY_SLOT_PROXIMITY_METHOD, 1, + AML_NOTSERIALIZED); + { + Aml *proximity = aml_name(MEMORY_SLOT_PROXIMITY); + + aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); + aml_append(method, aml_store(aml_to_integer(slot_arg0), + slot_selector)); + aml_append(method, aml_store(proximity, ret_val)); + aml_append(method, aml_release(ctrl_lock)); + aml_append(method, aml_return(ret_val)); + } + aml_append(dev_container, method); + + method = aml_method(MEMORY_SLOT_OST_METHOD, 4, AML_NOTSERIALIZED); + { + Aml *ost_evt = aml_name(MEMORY_SLOT_OST_EVENT); + Aml *ost_status = aml_name(MEMORY_SLOT_OST_STATUS); + + aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); + aml_append(method, aml_store(aml_to_integer(slot_arg0), + slot_selector)); + aml_append(method, aml_store(aml_arg(1), ost_evt)); + aml_append(method, aml_store(aml_arg(2), ost_status)); + aml_append(method, aml_release(ctrl_lock)); + } + aml_append(dev_container, method); + + method = aml_method(MEMORY_SLOT_EJECT_METHOD, 2, AML_NOTSERIALIZED); + { + Aml *eject = aml_name(MEMORY_SLOT_EJECT); + + aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); + aml_append(method, aml_store(aml_to_integer(slot_arg0), + slot_selector)); + aml_append(method, aml_store(one, eject)); + aml_append(method, aml_release(ctrl_lock)); + } + aml_append(dev_container, method); + + /* build memory devices */ + for (i = 0; i < nr_mem; i++) { + Aml *dev; + const char *s; + + dev = aml_device("MP%02X", i); + aml_append(dev, aml_name_decl("_UID", aml_string("0x%02X", i))); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C80"))); + + method = aml_method("_CRS", 0, AML_NOTSERIALIZED); + s = MEMORY_SLOT_CRS_METHOD; + aml_append(method, aml_return(aml_call1(s, aml_name("_UID")))); + aml_append(dev, method); + + method = aml_method("_STA", 0, AML_NOTSERIALIZED); + s = MEMORY_SLOT_STATUS_METHOD; + aml_append(method, aml_return(aml_call1(s, aml_name("_UID")))); + aml_append(dev, method); + + method = aml_method("_PXM", 0, AML_NOTSERIALIZED); + s = MEMORY_SLOT_PROXIMITY_METHOD; + aml_append(method, aml_return(aml_call1(s, aml_name("_UID")))); + aml_append(dev, method); + + method = aml_method("_OST", 3, AML_NOTSERIALIZED); + s = MEMORY_SLOT_OST_METHOD; + aml_append(method, aml_return(aml_call4( + s, aml_name("_UID"), aml_arg(0), aml_arg(1), aml_arg(2) + ))); + aml_append(dev, method); + + method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); + s = MEMORY_SLOT_EJECT_METHOD; + aml_append(method, aml_return(aml_call2( + s, aml_name("_UID"), aml_arg(0)))); + aml_append(dev, method); + + aml_append(dev_container, dev); + } + + /* build Method(MEMORY_SLOT_NOTIFY_METHOD, 2) { + * If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ... } + */ + method = aml_method(MEMORY_SLOT_NOTIFY_METHOD, 2, AML_NOTSERIALIZED); + for (i = 0; i < nr_mem; i++) { + ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i))); + aml_append(ifctx, + aml_notify(aml_name("MP%.02X", i), aml_arg(1)) + ); + aml_append(method, ifctx); + } + aml_append(dev_container, method); + } + aml_append(table, dev_container); + + method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED); + aml_append(method, + aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD)); + aml_append(table, method); + + g_free(mhp_res_path); +} diff --git a/hw/acpi/memory_hotplug_acpi_table.c b/hw/acpi/memory_hotplug_acpi_table.c deleted file mode 100644 index c75660215d..0000000000 --- a/hw/acpi/memory_hotplug_acpi_table.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Memory hotplug AML code of DSDT ACPI table - * - * Copyright (C) 2015 Red Hat Inc - * - * Author: Igor Mammedov <imammedo@redhat.com> - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "hw/acpi/memory_hotplug.h" -#include "include/hw/acpi/pc-hotplug.h" -#include "hw/boards.h" - -void build_memory_hotplug_aml(Aml *ctx, uint32_t nr_mem, - uint16_t io_base, uint16_t io_len) -{ - Aml *ifctx; - Aml *method; - Aml *pci_scope; - Aml *mem_ctrl_dev; - - /* scope for memory hotplug controller device node */ - pci_scope = aml_scope("_SB.PCI0"); - mem_ctrl_dev = aml_device(MEMORY_HOTPLUG_DEVICE); - { - Aml *one = aml_int(1); - Aml *zero = aml_int(0); - Aml *ret_val = aml_local(0); - Aml *slot_arg0 = aml_arg(0); - Aml *slots_nr = aml_name(MEMORY_SLOTS_NUMBER); - Aml *ctrl_lock = aml_name(MEMORY_SLOT_LOCK); - Aml *slot_selector = aml_name(MEMORY_SLOT_SLECTOR); - - aml_append(mem_ctrl_dev, aml_name_decl("_HID", aml_string("PNP0A06"))); - aml_append(mem_ctrl_dev, - aml_name_decl("_UID", aml_string("Memory hotplug resources"))); - - method = aml_method("_STA", 0, AML_NOTSERIALIZED); - ifctx = aml_if(aml_equal(slots_nr, zero)); - { - aml_append(ifctx, aml_return(zero)); - } - aml_append(method, ifctx); - /* present, functioning, decoding, not shown in UI */ - aml_append(method, aml_return(aml_int(0xB))); - aml_append(mem_ctrl_dev, method); - - aml_append(mem_ctrl_dev, aml_mutex(MEMORY_SLOT_LOCK, 0)); - - method = aml_method(MEMORY_SLOT_SCAN_METHOD, 0, AML_NOTSERIALIZED); - { - Aml *else_ctx; - Aml *while_ctx; - Aml *idx = aml_local(0); - Aml *eject_req = aml_int(3); - Aml *dev_chk = aml_int(1); - - ifctx = aml_if(aml_equal(slots_nr, zero)); - { - aml_append(ifctx, aml_return(zero)); - } - aml_append(method, ifctx); - - aml_append(method, aml_store(zero, idx)); - aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); - /* build AML that: - * loops over all slots and Notifies DIMMs with - * Device Check or Eject Request notifications if - * slot has corresponding status bit set and clears - * slot status. - */ - while_ctx = aml_while(aml_lless(idx, slots_nr)); - { - Aml *ins_evt = aml_name(MEMORY_SLOT_INSERT_EVENT); - Aml *rm_evt = aml_name(MEMORY_SLOT_REMOVE_EVENT); - - aml_append(while_ctx, aml_store(idx, slot_selector)); - ifctx = aml_if(aml_equal(ins_evt, one)); - { - aml_append(ifctx, - aml_call2(MEMORY_SLOT_NOTIFY_METHOD, - idx, dev_chk)); - aml_append(ifctx, aml_store(one, ins_evt)); - } - aml_append(while_ctx, ifctx); - - else_ctx = aml_else(); - ifctx = aml_if(aml_equal(rm_evt, one)); - { - aml_append(ifctx, - aml_call2(MEMORY_SLOT_NOTIFY_METHOD, - idx, eject_req)); - aml_append(ifctx, aml_store(one, rm_evt)); - } - aml_append(else_ctx, ifctx); - aml_append(while_ctx, else_ctx); - - aml_append(while_ctx, aml_add(idx, one, idx)); - } - aml_append(method, while_ctx); - aml_append(method, aml_release(ctrl_lock)); - aml_append(method, aml_return(one)); - } - aml_append(mem_ctrl_dev, method); - - method = aml_method(MEMORY_SLOT_STATUS_METHOD, 1, AML_NOTSERIALIZED); - { - Aml *slot_enabled = aml_name(MEMORY_SLOT_ENABLED); - - aml_append(method, aml_store(zero, ret_val)); - aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); - aml_append(method, - aml_store(aml_to_integer(slot_arg0), slot_selector)); - - ifctx = aml_if(aml_equal(slot_enabled, one)); - { - aml_append(ifctx, aml_store(aml_int(0xF), ret_val)); - } - aml_append(method, ifctx); - - aml_append(method, aml_release(ctrl_lock)); - aml_append(method, aml_return(ret_val)); - } - aml_append(mem_ctrl_dev, method); - - method = aml_method(MEMORY_SLOT_CRS_METHOD, 1, AML_SERIALIZED); - { - Aml *mr64 = aml_name("MR64"); - Aml *mr32 = aml_name("MR32"); - Aml *crs_tmpl = aml_resource_template(); - Aml *minl = aml_name("MINL"); - Aml *minh = aml_name("MINH"); - Aml *maxl = aml_name("MAXL"); - Aml *maxh = aml_name("MAXH"); - Aml *lenl = aml_name("LENL"); - Aml *lenh = aml_name("LENH"); - - aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); - aml_append(method, aml_store(aml_to_integer(slot_arg0), - slot_selector)); - - aml_append(crs_tmpl, - aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, - AML_CACHEABLE, AML_READ_WRITE, - 0, 0x0, 0xFFFFFFFFFFFFFFFEULL, 0, - 0xFFFFFFFFFFFFFFFFULL)); - aml_append(method, aml_name_decl("MR64", crs_tmpl)); - aml_append(method, - aml_create_dword_field(mr64, aml_int(14), "MINL")); - aml_append(method, - aml_create_dword_field(mr64, aml_int(18), "MINH")); - aml_append(method, - aml_create_dword_field(mr64, aml_int(38), "LENL")); - aml_append(method, - aml_create_dword_field(mr64, aml_int(42), "LENH")); - aml_append(method, - aml_create_dword_field(mr64, aml_int(22), "MAXL")); - aml_append(method, - aml_create_dword_field(mr64, aml_int(26), "MAXH")); - - aml_append(method, - aml_store(aml_name(MEMORY_SLOT_ADDR_HIGH), minh)); - aml_append(method, - aml_store(aml_name(MEMORY_SLOT_ADDR_LOW), minl)); - aml_append(method, - aml_store(aml_name(MEMORY_SLOT_SIZE_HIGH), lenh)); - aml_append(method, - aml_store(aml_name(MEMORY_SLOT_SIZE_LOW), lenl)); - - /* 64-bit math: MAX = MIN + LEN - 1 */ - aml_append(method, aml_add(minl, lenl, maxl)); - aml_append(method, aml_add(minh, lenh, maxh)); - ifctx = aml_if(aml_lless(maxl, minl)); - { - aml_append(ifctx, aml_add(maxh, one, maxh)); - } - aml_append(method, ifctx); - ifctx = aml_if(aml_lless(maxl, one)); - { - aml_append(ifctx, aml_subtract(maxh, one, maxh)); - } - aml_append(method, ifctx); - aml_append(method, aml_subtract(maxl, one, maxl)); - - /* return 32-bit _CRS if addr/size is in low mem */ - /* TODO: remove it since all hotplugged DIMMs are in high mem */ - ifctx = aml_if(aml_equal(maxh, zero)); - { - crs_tmpl = aml_resource_template(); - aml_append(crs_tmpl, - aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, - AML_MAX_FIXED, AML_CACHEABLE, - AML_READ_WRITE, - 0, 0x0, 0xFFFFFFFE, 0, - 0xFFFFFFFF)); - aml_append(ifctx, aml_name_decl("MR32", crs_tmpl)); - aml_append(ifctx, - aml_create_dword_field(mr32, aml_int(10), "MIN")); - aml_append(ifctx, - aml_create_dword_field(mr32, aml_int(14), "MAX")); - aml_append(ifctx, - aml_create_dword_field(mr32, aml_int(22), "LEN")); - aml_append(ifctx, aml_store(minl, aml_name("MIN"))); - aml_append(ifctx, aml_store(maxl, aml_name("MAX"))); - aml_append(ifctx, aml_store(lenl, aml_name("LEN"))); - - aml_append(ifctx, aml_release(ctrl_lock)); - aml_append(ifctx, aml_return(mr32)); - } - aml_append(method, ifctx); - - aml_append(method, aml_release(ctrl_lock)); - aml_append(method, aml_return(mr64)); - } - aml_append(mem_ctrl_dev, method); - - method = aml_method(MEMORY_SLOT_PROXIMITY_METHOD, 1, - AML_NOTSERIALIZED); - { - Aml *proximity = aml_name(MEMORY_SLOT_PROXIMITY); - - aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); - aml_append(method, aml_store(aml_to_integer(slot_arg0), - slot_selector)); - aml_append(method, aml_store(proximity, ret_val)); - aml_append(method, aml_release(ctrl_lock)); - aml_append(method, aml_return(ret_val)); - } - aml_append(mem_ctrl_dev, method); - - method = aml_method(MEMORY_SLOT_OST_METHOD, 4, AML_NOTSERIALIZED); - { - Aml *ost_evt = aml_name(MEMORY_SLOT_OST_EVENT); - Aml *ost_status = aml_name(MEMORY_SLOT_OST_STATUS); - - aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); - aml_append(method, aml_store(aml_to_integer(slot_arg0), - slot_selector)); - aml_append(method, aml_store(aml_arg(1), ost_evt)); - aml_append(method, aml_store(aml_arg(2), ost_status)); - aml_append(method, aml_release(ctrl_lock)); - } - aml_append(mem_ctrl_dev, method); - - method = aml_method(MEMORY_SLOT_EJECT_METHOD, 2, AML_NOTSERIALIZED); - { - Aml *eject = aml_name(MEMORY_SLOT_EJECT); - - aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); - aml_append(method, aml_store(aml_to_integer(slot_arg0), - slot_selector)); - aml_append(method, aml_store(one, eject)); - aml_append(method, aml_release(ctrl_lock)); - } - aml_append(mem_ctrl_dev, method); - } - aml_append(pci_scope, mem_ctrl_dev); - aml_append(ctx, pci_scope); -} diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 17d36bd595..6d99fe407c 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -644,7 +644,8 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, PIIX4_CPU_HOTPLUG_IO_BASE); if (s->acpi_memory_hotplug.is_enabled) { - acpi_memory_hotplug_init(parent, OBJECT(s), &s->acpi_memory_hotplug); + acpi_memory_hotplug_init(parent, OBJECT(s), &s->acpi_memory_hotplug, + ACPI_MEMORY_HOTPLUG_BASE); } } diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index bdcf6bcce7..d31b4577f0 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -1258,7 +1258,7 @@ static void pxa2xx_i2c_update(PXA2xxI2CState *s) } /* These are only stubs now. */ -static void pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event) +static int pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event) { PXA2xxI2CSlaveState *slave = PXA2XX_I2C_SLAVE(i2c); PXA2xxI2CState *s = slave->host; @@ -1280,6 +1280,8 @@ static void pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event) break; } pxa2xx_i2c_update(s); + + return 0; } static int pxa2xx_i2c_rx(I2CSlave *i2c) diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c index 39d9dbbae6..c3db996930 100644 --- a/hw/arm/tosa.c +++ b/hw/arm/tosa.c @@ -172,7 +172,7 @@ static int tosa_dac_send(I2CSlave *i2c, uint8_t data) return 0; } -static void tosa_dac_event(I2CSlave *i2c, enum i2c_event event) +static int tosa_dac_event(I2CSlave *i2c, enum i2c_event event) { TosaDACState *s = TOSA_DAC(i2c); @@ -194,6 +194,8 @@ static void tosa_dac_event(I2CSlave *i2c, enum i2c_event event) default: break; } + + return 0; } static int tosa_dac_recv(I2CSlave *s) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 7102686882..085a611173 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -29,7 +29,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu-common.h" -#include "hw/arm/virt-acpi-build.h" #include "qemu/bitmap.h" #include "trace.h" #include "qom/cpu.h" @@ -43,6 +42,7 @@ #include "hw/acpi/aml-build.h" #include "hw/pci/pcie_host.h" #include "hw/pci/pci.h" +#include "hw/arm/virt.h" #include "sysemu/numa.h" #include "kvm_arm.h" @@ -384,7 +384,7 @@ build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset) } static void -build_iort(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) +build_iort(GArray *table_data, BIOSLinker *linker) { int iort_start = table_data->len; AcpiIortIdMapping *idmap; @@ -439,11 +439,11 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) } static void -build_spcr(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) +build_spcr(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { AcpiSerialPortConsoleRedirection *spcr; - const MemMapEntry *uart_memmap = &guest_info->memmap[VIRT_UART]; - int irq = guest_info->irqmap[VIRT_UART] + ARM_SPI_BASE; + const MemMapEntry *uart_memmap = &vms->memmap[VIRT_UART]; + int irq = vms->irqmap[VIRT_UART] + ARM_SPI_BASE; spcr = acpi_data_push(table_data, sizeof(*spcr)); @@ -472,16 +472,16 @@ build_spcr(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) } static void -build_srat(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) +build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { 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)); + uint32_t *cpu_node = g_malloc0(vms->smp_cpus * sizeof(uint32_t)); - for (i = 0; i < guest_info->smp_cpus; i++) { + for (i = 0; i < vms->smp_cpus; i++) { j = numa_get_node_for_cpu(i); if (j < nb_numa_nodes) { cpu_node[i] = j; @@ -492,7 +492,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) srat = acpi_data_push(table_data, sizeof(*srat)); srat->reserved1 = cpu_to_le32(1); - for (i = 0; i < guest_info->smp_cpus; ++i) { + for (i = 0; i < vms->smp_cpus; ++i) { core = acpi_data_push(table_data, sizeof(*core)); core->type = ACPI_SRAT_PROCESSOR_GICC; core->length = sizeof(*core); @@ -502,7 +502,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) } g_free(cpu_node); - mem_base = guest_info->memmap[VIRT_MEM].base; + mem_base = vms->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, @@ -515,10 +515,10 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) } static void -build_mcfg(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) +build_mcfg(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { AcpiTableMcfg *mcfg; - const MemMapEntry *memmap = guest_info->memmap; + const MemMapEntry *memmap = vms->memmap; int len = sizeof(*mcfg) + sizeof(mcfg->allocation[0]); mcfg = acpi_data_push(table_data, len); @@ -535,24 +535,33 @@ build_mcfg(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) /* GTDT */ static void -build_gtdt(GArray *table_data, BIOSLinker *linker) +build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { + VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); int gtdt_start = table_data->len; AcpiGenericTimerTable *gtdt; + uint32_t irqflags; + + if (vmc->claim_edge_triggered_timers) { + irqflags = ACPI_GTDT_INTERRUPT_MODE_EDGE; + } else { + irqflags = ACPI_GTDT_INTERRUPT_MODE_LEVEL; + } 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->secure_el1_interrupt = cpu_to_le32(ARCH_TIMER_S_EL1_IRQ + 16); + gtdt->secure_el1_flags = cpu_to_le32(irqflags); - gtdt->non_secure_el1_interrupt = ARCH_TIMER_NS_EL1_IRQ + 16; - gtdt->non_secure_el1_flags = ACPI_EDGE_SENSITIVE | ACPI_GTDT_ALWAYS_ON; + gtdt->non_secure_el1_interrupt = cpu_to_le32(ARCH_TIMER_NS_EL1_IRQ + 16); + gtdt->non_secure_el1_flags = cpu_to_le32(irqflags | + ACPI_GTDT_CAP_ALWAYS_ON); - gtdt->virtual_timer_interrupt = ARCH_TIMER_VIRT_IRQ + 16; - gtdt->virtual_timer_flags = ACPI_EDGE_SENSITIVE; + gtdt->virtual_timer_interrupt = cpu_to_le32(ARCH_TIMER_VIRT_IRQ + 16); + gtdt->virtual_timer_flags = cpu_to_le32(irqflags); - gtdt->non_secure_el2_interrupt = ARCH_TIMER_NS_EL2_IRQ + 16; - gtdt->non_secure_el2_flags = ACPI_EDGE_SENSITIVE; + gtdt->non_secure_el2_interrupt = cpu_to_le32(ARCH_TIMER_NS_EL2_IRQ + 16); + gtdt->non_secure_el2_flags = cpu_to_le32(irqflags); build_header(linker, table_data, (void *)(table_data->data + gtdt_start), "GTDT", @@ -561,11 +570,12 @@ build_gtdt(GArray *table_data, BIOSLinker *linker) /* MADT */ static void -build_madt(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) +build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { + VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); int madt_start = table_data->len; - const MemMapEntry *memmap = guest_info->memmap; - const int *irqmap = guest_info->irqmap; + const MemMapEntry *memmap = vms->memmap; + const int *irqmap = vms->irqmap; AcpiMultipleApicTable *madt; AcpiMadtGenericDistributor *gicd; AcpiMadtGenericMsiFrame *gic_msi; @@ -576,30 +586,30 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) 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; - gicd->version = guest_info->gic_version; + gicd->base_address = cpu_to_le64(memmap[VIRT_GIC_DIST].base); + gicd->version = vms->gic_version; - for (i = 0; i < guest_info->smp_cpus; i++) { - AcpiMadtGenericInterrupt *gicc = acpi_data_push(table_data, - sizeof *gicc); + for (i = 0; i < vms->smp_cpus; i++) { + AcpiMadtGenericCpuInterface *gicc = acpi_data_push(table_data, + sizeof(*gicc)); ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i)); - gicc->type = ACPI_APIC_GENERIC_INTERRUPT; + gicc->type = ACPI_APIC_GENERIC_CPU_INTERFACE; gicc->length = sizeof(*gicc); - if (guest_info->gic_version == 2) { - gicc->base_address = memmap[VIRT_GIC_CPU].base; + if (vms->gic_version == 2) { + gicc->base_address = cpu_to_le64(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); + gicc->cpu_interface_number = cpu_to_le32(i); + gicc->arm_mpidr = cpu_to_le64(armcpu->mp_affinity); + gicc->uid = cpu_to_le32(i); + gicc->flags = cpu_to_le32(ACPI_MADT_GICC_ENABLED); if (arm_feature(&armcpu->env, ARM_FEATURE_PMU)) { gicc->performance_interrupt = cpu_to_le32(PPI(VIRTUAL_PMU_IRQ)); } } - if (guest_info->gic_version == 3) { + if (vms->gic_version == 3) { AcpiMadtGenericTranslator *gic_its; AcpiMadtGenericRedistributor *gicr = acpi_data_push(table_data, sizeof *gicr); @@ -609,7 +619,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST].base); gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST].size); - if (its_class_name() && !guest_info->no_its) { + if (its_class_name() && !vmc->no_its) { gic_its = acpi_data_push(table_data, sizeof *gic_its); gic_its->type = ACPI_APIC_GENERIC_TRANSLATOR; gic_its->length = sizeof(*gic_its); @@ -641,8 +651,8 @@ build_fadt(GArray *table_data, BIOSLinker *linker, unsigned dsdt_tbl_offset) /* 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)); + fadt->arm_boot_flags = cpu_to_le16(ACPI_FADT_ARM_PSCI_COMPLIANT | + ACPI_FADT_ARM_PSCI_USE_HVC); /* ACPI v5.1 (fadt->revision.fadt->minor_revision) */ fadt->minor_revision = 0x1; @@ -658,11 +668,11 @@ build_fadt(GArray *table_data, BIOSLinker *linker, unsigned dsdt_tbl_offset) /* DSDT */ static void -build_dsdt(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) +build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { Aml *scope, *dsdt; - const MemMapEntry *memmap = guest_info->memmap; - const int *irqmap = guest_info->irqmap; + const MemMapEntry *memmap = vms->memmap; + const int *irqmap = vms->irqmap; dsdt = init_aml_allocator(); /* Reserve space for header */ @@ -674,7 +684,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) * 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_cpus(scope, vms->smp_cpus); acpi_dsdt_add_uart(scope, &memmap[VIRT_UART], (irqmap[VIRT_UART] + ARM_SPI_BASE)); acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]); @@ -682,7 +692,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) 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); + vms->highmem); acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO], (irqmap[VIRT_GPIO] + ARM_SPI_BASE)); acpi_dsdt_add_power_button(scope); @@ -705,12 +715,12 @@ struct AcpiBuildState { MemoryRegion *linker_mr; /* Is table patched? */ bool patched; - VirtGuestInfo *guest_info; } AcpiBuildState; static -void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) +void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) { + VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); GArray *table_offsets; unsigned dsdt, rsdt; GArray *tables_blob = tables->table_data; @@ -724,32 +734,32 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) /* DSDT is pointed to by FADT */ dsdt = tables_blob->len; - build_dsdt(tables_blob, tables->linker, guest_info); + build_dsdt(tables_blob, tables->linker, vms); /* FADT MADT GTDT MCFG SPCR pointed to by RSDT */ acpi_add_table(table_offsets, tables_blob); build_fadt(tables_blob, tables->linker, dsdt); acpi_add_table(table_offsets, tables_blob); - build_madt(tables_blob, tables->linker, guest_info); + build_madt(tables_blob, tables->linker, vms); acpi_add_table(table_offsets, tables_blob); - build_gtdt(tables_blob, tables->linker); + build_gtdt(tables_blob, tables->linker, vms); acpi_add_table(table_offsets, tables_blob); - build_mcfg(tables_blob, tables->linker, guest_info); + build_mcfg(tables_blob, tables->linker, vms); acpi_add_table(table_offsets, tables_blob); - build_spcr(tables_blob, tables->linker, guest_info); + build_spcr(tables_blob, tables->linker, vms); if (nb_numa_nodes > 0) { acpi_add_table(table_offsets, tables_blob); - build_srat(tables_blob, tables->linker, guest_info); + build_srat(tables_blob, tables->linker, vms); } - if (its_class_name() && !guest_info->no_its) { + if (its_class_name() && !vmc->no_its) { acpi_add_table(table_offsets, tables_blob); - build_iort(tables_blob, tables->linker, guest_info); + build_iort(tables_blob, tables->linker); } /* RSDT is pointed to by RSDP */ @@ -788,13 +798,12 @@ static void virt_acpi_build_update(void *build_opaque) acpi_build_tables_init(&tables); - virt_acpi_build(build_state->guest_info, &tables); + virt_acpi_build(VIRT_MACHINE(qdev_get_machine()), &tables); acpi_ram_update(build_state->table_mr, tables.table_data); acpi_ram_update(build_state->rsdp_mr, tables.rsdp); acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob); - acpi_build_tables_cleanup(&tables, true); } @@ -822,12 +831,12 @@ static const VMStateDescription vmstate_virt_acpi_build = { }, }; -void virt_acpi_setup(VirtGuestInfo *guest_info) +void virt_acpi_setup(VirtMachineState *vms) { AcpiBuildTables tables; AcpiBuildState *build_state; - if (!guest_info->fw_cfg) { + if (!vms->fw_cfg) { trace_virt_acpi_setup(); return; } @@ -838,10 +847,9 @@ void virt_acpi_setup(VirtGuestInfo *guest_info) } build_state = g_malloc0(sizeof *build_state); - build_state->guest_info = guest_info; acpi_build_tables_init(&tables); - virt_acpi_build(build_state->guest_info, &tables); + virt_acpi_build(vms, &tables); /* Now expose it all to Guest */ build_state->table_mr = acpi_add_rom_blob(build_state, tables.table_data, @@ -853,8 +861,8 @@ void virt_acpi_setup(VirtGuestInfo *guest_info) acpi_add_rom_blob(build_state, tables.linker->cmd_blob, "etc/table-loader", 0); - fw_cfg_add_file(guest_info->fw_cfg, ACPI_BUILD_TPMLOG_FILE, - tables.tcpalog->data, acpi_data_len(tables.tcpalog)); + fw_cfg_add_file(vms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data, + acpi_data_len(tables.tcpalog)); build_state->rsdp_mr = acpi_add_rom_blob(build_state, tables.rsdp, ACPI_BUILD_RSDP_FILE, 0); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 11c53a56e0..7a03f84051 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -41,14 +41,12 @@ #include "sysemu/numa.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" -#include "hw/boards.h" #include "hw/compat.h" #include "hw/loader.h" #include "exec/address-spaces.h" #include "qemu/bitops.h" #include "qemu/error-report.h" #include "hw/pci-host/gpex.h" -#include "hw/arm/virt-acpi-build.h" #include "hw/arm/sysbus-fdt.h" #include "hw/platform-bus.h" #include "hw/arm/fdt.h" @@ -59,51 +57,6 @@ #include "qapi/visitor.h" #include "standard-headers/linux/input.h" -/* Number of external interrupt lines to configure the GIC with */ -#define NUM_IRQS 256 - -#define PLATFORM_BUS_NUM_IRQS 64 - -static ARMPlatformBusSystemParams platform_bus_params; - -typedef struct VirtBoardInfo { - struct arm_boot_info bootinfo; - const char *cpu_model; - const MemMapEntry *memmap; - const int *irqmap; - int smp_cpus; - void *fdt; - int fdt_size; - uint32_t clock_phandle; - uint32_t gic_phandle; - uint32_t msi_phandle; - bool using_psci; -} VirtBoardInfo; - -typedef struct { - MachineClass parent; - VirtBoardInfo *daughterboard; - bool disallow_affinity_adjustment; - bool no_its; - bool no_pmu; -} VirtMachineClass; - -typedef struct { - MachineState parent; - bool secure; - bool highmem; - int32_t gic_version; -} VirtMachineState; - -#define TYPE_VIRT_MACHINE MACHINE_TYPE_NAME("virt") -#define VIRT_MACHINE(obj) \ - OBJECT_CHECK(VirtMachineState, (obj), TYPE_VIRT_MACHINE) -#define VIRT_MACHINE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(VirtMachineClass, obj, TYPE_VIRT_MACHINE) -#define VIRT_MACHINE_CLASS(klass) \ - OBJECT_CLASS_CHECK(VirtMachineClass, klass, TYPE_VIRT_MACHINE) - - #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \ static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ void *data) \ @@ -133,6 +86,13 @@ typedef struct { DEFINE_VIRT_MACHINE_LATEST(major, minor, false) +/* Number of external interrupt lines to configure the GIC with */ +#define NUM_IRQS 256 + +#define PLATFORM_BUS_NUM_IRQS 64 + +static ARMPlatformBusSystemParams platform_bus_params; + /* RAM limit in GB. Since VIRT_MEM starts at the 1GB mark, this means * RAM can go up to the 256GB mark, leaving 256GB of the physical * address space unallocated and free for future use between 256G and 512G. @@ -202,51 +162,36 @@ static const int a15irqmap[] = { [VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */ }; -static VirtBoardInfo machines[] = { - { - .cpu_model = "cortex-a15", - .memmap = a15memmap, - .irqmap = a15irqmap, - }, - { - .cpu_model = "cortex-a53", - .memmap = a15memmap, - .irqmap = a15irqmap, - }, - { - .cpu_model = "cortex-a57", - .memmap = a15memmap, - .irqmap = a15irqmap, - }, - { - .cpu_model = "host", - .memmap = a15memmap, - .irqmap = a15irqmap, - }, +static const char *valid_cpus[] = { + "cortex-a15", + "cortex-a53", + "cortex-a57", + "host", + NULL }; -static VirtBoardInfo *find_machine_info(const char *cpu) +static bool cpuname_valid(const char *cpu) { int i; - for (i = 0; i < ARRAY_SIZE(machines); i++) { - if (strcmp(cpu, machines[i].cpu_model) == 0) { - return &machines[i]; + for (i = 0; i < ARRAY_SIZE(valid_cpus); i++) { + if (strcmp(cpu, valid_cpus[i]) == 0) { + return true; } } - return NULL; + return false; } -static void create_fdt(VirtBoardInfo *vbi) +static void create_fdt(VirtMachineState *vms) { - void *fdt = create_device_tree(&vbi->fdt_size); + void *fdt = create_device_tree(&vms->fdt_size); if (!fdt) { error_report("create_device_tree() failed"); exit(1); } - vbi->fdt = fdt; + vms->fdt = fdt; /* Header */ qemu_fdt_setprop_string(fdt, "/", "compatible", "linux,dummy-virt"); @@ -266,27 +211,27 @@ static void create_fdt(VirtBoardInfo *vbi) * optional but in practice if you omit them the kernel refuses to * probe for the device. */ - vbi->clock_phandle = qemu_fdt_alloc_phandle(fdt); + vms->clock_phandle = qemu_fdt_alloc_phandle(fdt); qemu_fdt_add_subnode(fdt, "/apb-pclk"); qemu_fdt_setprop_string(fdt, "/apb-pclk", "compatible", "fixed-clock"); qemu_fdt_setprop_cell(fdt, "/apb-pclk", "#clock-cells", 0x0); qemu_fdt_setprop_cell(fdt, "/apb-pclk", "clock-frequency", 24000000); qemu_fdt_setprop_string(fdt, "/apb-pclk", "clock-output-names", "clk24mhz"); - qemu_fdt_setprop_cell(fdt, "/apb-pclk", "phandle", vbi->clock_phandle); + qemu_fdt_setprop_cell(fdt, "/apb-pclk", "phandle", vms->clock_phandle); } -static void fdt_add_psci_node(const VirtBoardInfo *vbi) +static void fdt_add_psci_node(const VirtMachineState *vms) { uint32_t cpu_suspend_fn; uint32_t cpu_off_fn; uint32_t cpu_on_fn; uint32_t migrate_fn; - void *fdt = vbi->fdt; + void *fdt = vms->fdt; ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0)); - if (!vbi->using_psci) { + if (!vms->using_psci) { return; } @@ -327,41 +272,60 @@ static void fdt_add_psci_node(const VirtBoardInfo *vbi) qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn); } -static void fdt_add_timer_nodes(const VirtBoardInfo *vbi, int gictype) +static void fdt_add_timer_nodes(const VirtMachineState *vms) { - /* Note that on A15 h/w these interrupts are level-triggered, - * but for the GIC implementation provided by both QEMU and KVM - * they are edge-triggered. + /* On real hardware these interrupts are level-triggered. + * On KVM they were edge-triggered before host kernel version 4.4, + * and level-triggered afterwards. + * On emulated QEMU they are level-triggered. + * + * Getting the DTB info about them wrong is awkward for some + * guest kernels: + * pre-4.8 ignore the DT and leave the interrupt configured + * with whatever the GIC reset value (or the bootloader) left it at + * 4.8 before rc6 honour the incorrect data by programming it back + * into the GIC, causing problems + * 4.8rc6 and later ignore the DT and always write "level triggered" + * into the GIC + * + * For backwards-compatibility, virt-2.8 and earlier will continue + * to say these are edge-triggered, but later machines will report + * the correct information. */ ARMCPU *armcpu; - uint32_t irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI; + VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); + uint32_t irqflags = GIC_FDT_IRQ_FLAGS_LEVEL_HI; + + if (vmc->claim_edge_triggered_timers) { + irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI; + } - if (gictype == 2) { + if (vms->gic_version == 2) { irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START, GIC_FDT_IRQ_PPI_CPU_WIDTH, - (1 << vbi->smp_cpus) - 1); + (1 << vms->smp_cpus) - 1); } - qemu_fdt_add_subnode(vbi->fdt, "/timer"); + qemu_fdt_add_subnode(vms->fdt, "/timer"); armcpu = ARM_CPU(qemu_get_cpu(0)); if (arm_feature(&armcpu->env, ARM_FEATURE_V8)) { const char compat[] = "arm,armv8-timer\0arm,armv7-timer"; - qemu_fdt_setprop(vbi->fdt, "/timer", "compatible", + qemu_fdt_setprop(vms->fdt, "/timer", "compatible", compat, sizeof(compat)); } else { - qemu_fdt_setprop_string(vbi->fdt, "/timer", "compatible", + qemu_fdt_setprop_string(vms->fdt, "/timer", "compatible", "arm,armv7-timer"); } - qemu_fdt_setprop(vbi->fdt, "/timer", "always-on", NULL, 0); - qemu_fdt_setprop_cells(vbi->fdt, "/timer", "interrupts", + qemu_fdt_setprop(vms->fdt, "/timer", "always-on", NULL, 0); + qemu_fdt_setprop_cells(vms->fdt, "/timer", "interrupts", GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_S_EL1_IRQ, irqflags, GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_NS_EL1_IRQ, irqflags, GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_VIRT_IRQ, irqflags, GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_NS_EL2_IRQ, irqflags); } -static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) +static void fdt_add_cpu_nodes(const VirtMachineState *vms) { int cpu; int addr_cells = 1; @@ -380,7 +344,7 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) * The simplest way to go is to examine affinity IDs of all our CPUs. If * at least one of them has Aff3 populated, we set #address-cells to 2. */ - for (cpu = 0; cpu < vbi->smp_cpus; cpu++) { + for (cpu = 0; cpu < vms->smp_cpus; cpu++) { ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu)); if (armcpu->mp_affinity & ARM_AFF3_MASK) { @@ -389,101 +353,101 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) } } - qemu_fdt_add_subnode(vbi->fdt, "/cpus"); - qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#address-cells", addr_cells); - qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#size-cells", 0x0); + qemu_fdt_add_subnode(vms->fdt, "/cpus"); + qemu_fdt_setprop_cell(vms->fdt, "/cpus", "#address-cells", addr_cells); + qemu_fdt_setprop_cell(vms->fdt, "/cpus", "#size-cells", 0x0); - for (cpu = vbi->smp_cpus - 1; cpu >= 0; cpu--) { + for (cpu = vms->smp_cpus - 1; cpu >= 0; cpu--) { char *nodename = g_strdup_printf("/cpus/cpu@%d", cpu); ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu)); - qemu_fdt_add_subnode(vbi->fdt, nodename); - qemu_fdt_setprop_string(vbi->fdt, nodename, "device_type", "cpu"); - qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", + qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_setprop_string(vms->fdt, nodename, "device_type", "cpu"); + qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", armcpu->dtb_compatible); - if (vbi->using_psci && vbi->smp_cpus > 1) { - qemu_fdt_setprop_string(vbi->fdt, nodename, + if (vms->using_psci && vms->smp_cpus > 1) { + qemu_fdt_setprop_string(vms->fdt, nodename, "enable-method", "psci"); } if (addr_cells == 2) { - qemu_fdt_setprop_u64(vbi->fdt, nodename, "reg", + qemu_fdt_setprop_u64(vms->fdt, nodename, "reg", armcpu->mp_affinity); } else { - qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", + qemu_fdt_setprop_cell(vms->fdt, nodename, "reg", armcpu->mp_affinity); } i = numa_get_node_for_cpu(cpu); if (i < nb_numa_nodes) { - qemu_fdt_setprop_cell(vbi->fdt, nodename, "numa-node-id", i); + qemu_fdt_setprop_cell(vms->fdt, nodename, "numa-node-id", i); } g_free(nodename); } } -static void fdt_add_its_gic_node(VirtBoardInfo *vbi) +static void fdt_add_its_gic_node(VirtMachineState *vms) { - vbi->msi_phandle = qemu_fdt_alloc_phandle(vbi->fdt); - qemu_fdt_add_subnode(vbi->fdt, "/intc/its"); - qemu_fdt_setprop_string(vbi->fdt, "/intc/its", "compatible", + vms->msi_phandle = qemu_fdt_alloc_phandle(vms->fdt); + qemu_fdt_add_subnode(vms->fdt, "/intc/its"); + qemu_fdt_setprop_string(vms->fdt, "/intc/its", "compatible", "arm,gic-v3-its"); - qemu_fdt_setprop(vbi->fdt, "/intc/its", "msi-controller", NULL, 0); - qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc/its", "reg", - 2, vbi->memmap[VIRT_GIC_ITS].base, - 2, vbi->memmap[VIRT_GIC_ITS].size); - qemu_fdt_setprop_cell(vbi->fdt, "/intc/its", "phandle", vbi->msi_phandle); + qemu_fdt_setprop(vms->fdt, "/intc/its", "msi-controller", NULL, 0); + qemu_fdt_setprop_sized_cells(vms->fdt, "/intc/its", "reg", + 2, vms->memmap[VIRT_GIC_ITS].base, + 2, vms->memmap[VIRT_GIC_ITS].size); + qemu_fdt_setprop_cell(vms->fdt, "/intc/its", "phandle", vms->msi_phandle); } -static void fdt_add_v2m_gic_node(VirtBoardInfo *vbi) +static void fdt_add_v2m_gic_node(VirtMachineState *vms) { - vbi->msi_phandle = qemu_fdt_alloc_phandle(vbi->fdt); - qemu_fdt_add_subnode(vbi->fdt, "/intc/v2m"); - qemu_fdt_setprop_string(vbi->fdt, "/intc/v2m", "compatible", + vms->msi_phandle = qemu_fdt_alloc_phandle(vms->fdt); + qemu_fdt_add_subnode(vms->fdt, "/intc/v2m"); + qemu_fdt_setprop_string(vms->fdt, "/intc/v2m", "compatible", "arm,gic-v2m-frame"); - qemu_fdt_setprop(vbi->fdt, "/intc/v2m", "msi-controller", NULL, 0); - qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc/v2m", "reg", - 2, vbi->memmap[VIRT_GIC_V2M].base, - 2, vbi->memmap[VIRT_GIC_V2M].size); - qemu_fdt_setprop_cell(vbi->fdt, "/intc/v2m", "phandle", vbi->msi_phandle); + qemu_fdt_setprop(vms->fdt, "/intc/v2m", "msi-controller", NULL, 0); + qemu_fdt_setprop_sized_cells(vms->fdt, "/intc/v2m", "reg", + 2, vms->memmap[VIRT_GIC_V2M].base, + 2, vms->memmap[VIRT_GIC_V2M].size); + qemu_fdt_setprop_cell(vms->fdt, "/intc/v2m", "phandle", vms->msi_phandle); } -static void fdt_add_gic_node(VirtBoardInfo *vbi, int type) +static void fdt_add_gic_node(VirtMachineState *vms) { - vbi->gic_phandle = qemu_fdt_alloc_phandle(vbi->fdt); - qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", vbi->gic_phandle); - - qemu_fdt_add_subnode(vbi->fdt, "/intc"); - qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3); - qemu_fdt_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#address-cells", 0x2); - qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#size-cells", 0x2); - qemu_fdt_setprop(vbi->fdt, "/intc", "ranges", NULL, 0); - if (type == 3) { - qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible", + vms->gic_phandle = qemu_fdt_alloc_phandle(vms->fdt); + qemu_fdt_setprop_cell(vms->fdt, "/", "interrupt-parent", vms->gic_phandle); + + qemu_fdt_add_subnode(vms->fdt, "/intc"); + qemu_fdt_setprop_cell(vms->fdt, "/intc", "#interrupt-cells", 3); + qemu_fdt_setprop(vms->fdt, "/intc", "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(vms->fdt, "/intc", "#address-cells", 0x2); + qemu_fdt_setprop_cell(vms->fdt, "/intc", "#size-cells", 0x2); + qemu_fdt_setprop(vms->fdt, "/intc", "ranges", NULL, 0); + if (vms->gic_version == 3) { + qemu_fdt_setprop_string(vms->fdt, "/intc", "compatible", "arm,gic-v3"); - qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg", - 2, vbi->memmap[VIRT_GIC_DIST].base, - 2, vbi->memmap[VIRT_GIC_DIST].size, - 2, vbi->memmap[VIRT_GIC_REDIST].base, - 2, vbi->memmap[VIRT_GIC_REDIST].size); + qemu_fdt_setprop_sized_cells(vms->fdt, "/intc", "reg", + 2, vms->memmap[VIRT_GIC_DIST].base, + 2, vms->memmap[VIRT_GIC_DIST].size, + 2, vms->memmap[VIRT_GIC_REDIST].base, + 2, vms->memmap[VIRT_GIC_REDIST].size); } else { /* 'cortex-a15-gic' means 'GIC v2' */ - qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible", + qemu_fdt_setprop_string(vms->fdt, "/intc", "compatible", "arm,cortex-a15-gic"); - qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg", - 2, vbi->memmap[VIRT_GIC_DIST].base, - 2, vbi->memmap[VIRT_GIC_DIST].size, - 2, vbi->memmap[VIRT_GIC_CPU].base, - 2, vbi->memmap[VIRT_GIC_CPU].size); + qemu_fdt_setprop_sized_cells(vms->fdt, "/intc", "reg", + 2, vms->memmap[VIRT_GIC_DIST].base, + 2, vms->memmap[VIRT_GIC_DIST].size, + 2, vms->memmap[VIRT_GIC_CPU].base, + 2, vms->memmap[VIRT_GIC_CPU].size); } - qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", vbi->gic_phandle); + qemu_fdt_setprop_cell(vms->fdt, "/intc", "phandle", vms->gic_phandle); } -static void fdt_add_pmu_nodes(const VirtBoardInfo *vbi, int gictype) +static void fdt_add_pmu_nodes(const VirtMachineState *vms) { CPUState *cpu; ARMCPU *armcpu; @@ -497,24 +461,24 @@ static void fdt_add_pmu_nodes(const VirtBoardInfo *vbi, int gictype) } } - if (gictype == 2) { + if (vms->gic_version == 2) { irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START, GIC_FDT_IRQ_PPI_CPU_WIDTH, - (1 << vbi->smp_cpus) - 1); + (1 << vms->smp_cpus) - 1); } armcpu = ARM_CPU(qemu_get_cpu(0)); - qemu_fdt_add_subnode(vbi->fdt, "/pmu"); + qemu_fdt_add_subnode(vms->fdt, "/pmu"); if (arm_feature(&armcpu->env, ARM_FEATURE_V8)) { const char compat[] = "arm,armv8-pmuv3"; - qemu_fdt_setprop(vbi->fdt, "/pmu", "compatible", + qemu_fdt_setprop(vms->fdt, "/pmu", "compatible", compat, sizeof(compat)); - qemu_fdt_setprop_cells(vbi->fdt, "/pmu", "interrupts", + qemu_fdt_setprop_cells(vms->fdt, "/pmu", "interrupts", GIC_FDT_IRQ_TYPE_PPI, VIRTUAL_PMU_IRQ, irqflags); } } -static void create_its(VirtBoardInfo *vbi, DeviceState *gicdev) +static void create_its(VirtMachineState *vms, DeviceState *gicdev) { const char *itsclass = its_class_name(); DeviceState *dev; @@ -529,19 +493,19 @@ static void create_its(VirtBoardInfo *vbi, DeviceState *gicdev) object_property_set_link(OBJECT(dev), OBJECT(gicdev), "parent-gicv3", &error_abort); qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vbi->memmap[VIRT_GIC_ITS].base); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_GIC_ITS].base); - fdt_add_its_gic_node(vbi); + fdt_add_its_gic_node(vms); } -static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic) +static void create_v2m(VirtMachineState *vms, qemu_irq *pic) { int i; - int irq = vbi->irqmap[VIRT_GIC_V2M]; + int irq = vms->irqmap[VIRT_GIC_V2M]; DeviceState *dev; dev = qdev_create(NULL, "arm-gicv2m"); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vbi->memmap[VIRT_GIC_V2M].base); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_GIC_V2M].base); qdev_prop_set_uint32(dev, "base-spi", irq); qdev_prop_set_uint32(dev, "num-spi", NUM_GICV2M_SPIS); qdev_init_nofail(dev); @@ -550,17 +514,17 @@ static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic) sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]); } - fdt_add_v2m_gic_node(vbi); + fdt_add_v2m_gic_node(vms); } -static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, int type, - bool secure, bool no_its) +static void create_gic(VirtMachineState *vms, qemu_irq *pic) { /* We create a standalone GIC */ + VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); DeviceState *gicdev; SysBusDevice *gicbusdev; const char *gictype; - int i; + int type = vms->gic_version, i; gictype = (type == 3) ? gicv3_class_name() : gic_class_name(); @@ -572,15 +536,15 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, int type, */ qdev_prop_set_uint32(gicdev, "num-irq", NUM_IRQS + 32); if (!kvm_irqchip_in_kernel()) { - qdev_prop_set_bit(gicdev, "has-security-extensions", secure); + qdev_prop_set_bit(gicdev, "has-security-extensions", vms->secure); } qdev_init_nofail(gicdev); gicbusdev = SYS_BUS_DEVICE(gicdev); - sysbus_mmio_map(gicbusdev, 0, vbi->memmap[VIRT_GIC_DIST].base); + sysbus_mmio_map(gicbusdev, 0, vms->memmap[VIRT_GIC_DIST].base); if (type == 3) { - sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_REDIST].base); + sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_REDIST].base); } else { - sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_CPU].base); + sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_CPU].base); } /* Wire the outputs from each CPU's generic timer to the @@ -616,22 +580,22 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, int type, pic[i] = qdev_get_gpio_in(gicdev, i); } - fdt_add_gic_node(vbi, type); + fdt_add_gic_node(vms); - if (type == 3 && !no_its) { - create_its(vbi, gicdev); + if (type == 3 && !vmc->no_its) { + create_its(vms, gicdev); } else if (type == 2) { - create_v2m(vbi, pic); + create_v2m(vms, pic); } } -static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic, int uart, +static void create_uart(const VirtMachineState *vms, qemu_irq *pic, int uart, MemoryRegion *mem, CharDriverState *chr) { char *nodename; - hwaddr base = vbi->memmap[uart].base; - hwaddr size = vbi->memmap[uart].size; - int irq = vbi->irqmap[uart]; + hwaddr base = vms->memmap[uart].base; + hwaddr size = vms->memmap[uart].size; + int irq = vms->irqmap[uart]; const char compat[] = "arm,pl011\0arm,primecell"; const char clocknames[] = "uartclk\0apb_pclk"; DeviceState *dev = qdev_create(NULL, "pl011"); @@ -644,51 +608,51 @@ static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic, int uart, sysbus_connect_irq(s, 0, pic[irq]); nodename = g_strdup_printf("/pl011@%" PRIx64, base); - qemu_fdt_add_subnode(vbi->fdt, nodename); + qemu_fdt_add_subnode(vms->fdt, nodename); /* Note that we can't use setprop_string because of the embedded NUL */ - qemu_fdt_setprop(vbi->fdt, nodename, "compatible", + qemu_fdt_setprop(vms->fdt, nodename, "compatible", compat, sizeof(compat)); - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, base, 2, size); - qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts", + qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts", GIC_FDT_IRQ_TYPE_SPI, irq, GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop_cells(vbi->fdt, nodename, "clocks", - vbi->clock_phandle, vbi->clock_phandle); - qemu_fdt_setprop(vbi->fdt, nodename, "clock-names", + qemu_fdt_setprop_cells(vms->fdt, nodename, "clocks", + vms->clock_phandle, vms->clock_phandle); + qemu_fdt_setprop(vms->fdt, nodename, "clock-names", clocknames, sizeof(clocknames)); if (uart == VIRT_UART) { - qemu_fdt_setprop_string(vbi->fdt, "/chosen", "stdout-path", nodename); + qemu_fdt_setprop_string(vms->fdt, "/chosen", "stdout-path", nodename); } else { /* Mark as not usable by the normal world */ - qemu_fdt_setprop_string(vbi->fdt, nodename, "status", "disabled"); - qemu_fdt_setprop_string(vbi->fdt, nodename, "secure-status", "okay"); + qemu_fdt_setprop_string(vms->fdt, nodename, "status", "disabled"); + qemu_fdt_setprop_string(vms->fdt, nodename, "secure-status", "okay"); } g_free(nodename); } -static void create_rtc(const VirtBoardInfo *vbi, qemu_irq *pic) +static void create_rtc(const VirtMachineState *vms, qemu_irq *pic) { char *nodename; - hwaddr base = vbi->memmap[VIRT_RTC].base; - hwaddr size = vbi->memmap[VIRT_RTC].size; - int irq = vbi->irqmap[VIRT_RTC]; + hwaddr base = vms->memmap[VIRT_RTC].base; + hwaddr size = vms->memmap[VIRT_RTC].size; + int irq = vms->irqmap[VIRT_RTC]; const char compat[] = "arm,pl031\0arm,primecell"; sysbus_create_simple("pl031", base, pic[irq]); nodename = g_strdup_printf("/pl031@%" PRIx64, base); - qemu_fdt_add_subnode(vbi->fdt, nodename); - qemu_fdt_setprop(vbi->fdt, nodename, "compatible", compat, sizeof(compat)); - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_setprop(vms->fdt, nodename, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, base, 2, size); - qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts", + qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts", GIC_FDT_IRQ_TYPE_SPI, irq, GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop_cell(vbi->fdt, nodename, "clocks", vbi->clock_phandle); - qemu_fdt_setprop_string(vbi->fdt, nodename, "clock-names", "apb_pclk"); + qemu_fdt_setprop_cell(vms->fdt, nodename, "clocks", vms->clock_phandle); + qemu_fdt_setprop_string(vms->fdt, nodename, "clock-names", "apb_pclk"); g_free(nodename); } @@ -703,45 +667,45 @@ static Notifier virt_system_powerdown_notifier = { .notify = virt_powerdown_req }; -static void create_gpio(const VirtBoardInfo *vbi, qemu_irq *pic) +static void create_gpio(const VirtMachineState *vms, qemu_irq *pic) { char *nodename; DeviceState *pl061_dev; - hwaddr base = vbi->memmap[VIRT_GPIO].base; - hwaddr size = vbi->memmap[VIRT_GPIO].size; - int irq = vbi->irqmap[VIRT_GPIO]; + hwaddr base = vms->memmap[VIRT_GPIO].base; + hwaddr size = vms->memmap[VIRT_GPIO].size; + int irq = vms->irqmap[VIRT_GPIO]; const char compat[] = "arm,pl061\0arm,primecell"; pl061_dev = sysbus_create_simple("pl061", base, pic[irq]); - uint32_t phandle = qemu_fdt_alloc_phandle(vbi->fdt); + uint32_t phandle = qemu_fdt_alloc_phandle(vms->fdt); nodename = g_strdup_printf("/pl061@%" PRIx64, base); - qemu_fdt_add_subnode(vbi->fdt, nodename); - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, base, 2, size); - qemu_fdt_setprop(vbi->fdt, nodename, "compatible", compat, sizeof(compat)); - qemu_fdt_setprop_cell(vbi->fdt, nodename, "#gpio-cells", 2); - qemu_fdt_setprop(vbi->fdt, nodename, "gpio-controller", NULL, 0); - qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts", + qemu_fdt_setprop(vms->fdt, nodename, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_cell(vms->fdt, nodename, "#gpio-cells", 2); + qemu_fdt_setprop(vms->fdt, nodename, "gpio-controller", NULL, 0); + qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts", GIC_FDT_IRQ_TYPE_SPI, irq, GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop_cell(vbi->fdt, nodename, "clocks", vbi->clock_phandle); - qemu_fdt_setprop_string(vbi->fdt, nodename, "clock-names", "apb_pclk"); - qemu_fdt_setprop_cell(vbi->fdt, nodename, "phandle", phandle); + qemu_fdt_setprop_cell(vms->fdt, nodename, "clocks", vms->clock_phandle); + qemu_fdt_setprop_string(vms->fdt, nodename, "clock-names", "apb_pclk"); + qemu_fdt_setprop_cell(vms->fdt, nodename, "phandle", phandle); gpio_key_dev = sysbus_create_simple("gpio-key", -1, qdev_get_gpio_in(pl061_dev, 3)); - qemu_fdt_add_subnode(vbi->fdt, "/gpio-keys"); - qemu_fdt_setprop_string(vbi->fdt, "/gpio-keys", "compatible", "gpio-keys"); - qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys", "#size-cells", 0); - qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys", "#address-cells", 1); + qemu_fdt_add_subnode(vms->fdt, "/gpio-keys"); + qemu_fdt_setprop_string(vms->fdt, "/gpio-keys", "compatible", "gpio-keys"); + qemu_fdt_setprop_cell(vms->fdt, "/gpio-keys", "#size-cells", 0); + qemu_fdt_setprop_cell(vms->fdt, "/gpio-keys", "#address-cells", 1); - qemu_fdt_add_subnode(vbi->fdt, "/gpio-keys/poweroff"); - qemu_fdt_setprop_string(vbi->fdt, "/gpio-keys/poweroff", + qemu_fdt_add_subnode(vms->fdt, "/gpio-keys/poweroff"); + qemu_fdt_setprop_string(vms->fdt, "/gpio-keys/poweroff", "label", "GPIO Key Poweroff"); - qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys/poweroff", "linux,code", + qemu_fdt_setprop_cell(vms->fdt, "/gpio-keys/poweroff", "linux,code", KEY_POWER); - qemu_fdt_setprop_cells(vbi->fdt, "/gpio-keys/poweroff", + qemu_fdt_setprop_cells(vms->fdt, "/gpio-keys/poweroff", "gpios", phandle, 3, 0); /* connect powerdown request */ @@ -750,10 +714,10 @@ static void create_gpio(const VirtBoardInfo *vbi, qemu_irq *pic) g_free(nodename); } -static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic) +static void create_virtio_devices(const VirtMachineState *vms, qemu_irq *pic) { int i; - hwaddr size = vbi->memmap[VIRT_MMIO].size; + hwaddr size = vms->memmap[VIRT_MMIO].size; /* We create the transports in forwards order. Since qbus_realize() * prepends (not appends) new child buses, the incrementing loop below will @@ -783,8 +747,8 @@ static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic) * of disks users must use UUIDs or similar mechanisms. */ for (i = 0; i < NUM_VIRTIO_TRANSPORTS; i++) { - int irq = vbi->irqmap[VIRT_MMIO] + i; - hwaddr base = vbi->memmap[VIRT_MMIO].base + i * size; + int irq = vms->irqmap[VIRT_MMIO] + i; + hwaddr base = vms->memmap[VIRT_MMIO].base + i * size; sysbus_create_simple("virtio-mmio", base, pic[irq]); } @@ -798,16 +762,16 @@ static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic) */ for (i = NUM_VIRTIO_TRANSPORTS - 1; i >= 0; i--) { char *nodename; - int irq = vbi->irqmap[VIRT_MMIO] + i; - hwaddr base = vbi->memmap[VIRT_MMIO].base + i * size; + int irq = vms->irqmap[VIRT_MMIO] + i; + hwaddr base = vms->memmap[VIRT_MMIO].base + i * size; nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, base); - qemu_fdt_add_subnode(vbi->fdt, nodename); - qemu_fdt_setprop_string(vbi->fdt, nodename, + qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "virtio,mmio"); - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, base, 2, size); - qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts", + qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts", GIC_FDT_IRQ_TYPE_SPI, irq, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); g_free(nodename); @@ -870,7 +834,7 @@ static void create_one_flash(const char *name, hwaddr flashbase, } } -static void create_flash(const VirtBoardInfo *vbi, +static void create_flash(const VirtMachineState *vms, MemoryRegion *sysmem, MemoryRegion *secure_sysmem) { @@ -882,8 +846,8 @@ static void create_flash(const VirtBoardInfo *vbi, * If sysmem == secure_sysmem this means there is no separate Secure * address space and both flash devices are generally visible. */ - hwaddr flashsize = vbi->memmap[VIRT_FLASH].size / 2; - hwaddr flashbase = vbi->memmap[VIRT_FLASH].base; + hwaddr flashsize = vms->memmap[VIRT_FLASH].size / 2; + hwaddr flashbase = vms->memmap[VIRT_FLASH].base; char *nodename; create_one_flash("virt.flash0", flashbase, flashsize, @@ -894,41 +858,41 @@ static void create_flash(const VirtBoardInfo *vbi, if (sysmem == secure_sysmem) { /* Report both flash devices as a single node in the DT */ nodename = g_strdup_printf("/flash@%" PRIx64, flashbase); - qemu_fdt_add_subnode(vbi->fdt, nodename); - qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash"); - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "cfi-flash"); + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, flashbase, 2, flashsize, 2, flashbase + flashsize, 2, flashsize); - qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4); + qemu_fdt_setprop_cell(vms->fdt, nodename, "bank-width", 4); g_free(nodename); } else { /* Report the devices as separate nodes so we can mark one as * only visible to the secure world. */ nodename = g_strdup_printf("/secflash@%" PRIx64, flashbase); - qemu_fdt_add_subnode(vbi->fdt, nodename); - qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash"); - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "cfi-flash"); + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, flashbase, 2, flashsize); - qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4); - qemu_fdt_setprop_string(vbi->fdt, nodename, "status", "disabled"); - qemu_fdt_setprop_string(vbi->fdt, nodename, "secure-status", "okay"); + qemu_fdt_setprop_cell(vms->fdt, nodename, "bank-width", 4); + qemu_fdt_setprop_string(vms->fdt, nodename, "status", "disabled"); + qemu_fdt_setprop_string(vms->fdt, nodename, "secure-status", "okay"); g_free(nodename); nodename = g_strdup_printf("/flash@%" PRIx64, flashbase); - qemu_fdt_add_subnode(vbi->fdt, nodename); - qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash"); - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "cfi-flash"); + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, flashbase + flashsize, 2, flashsize); - qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4); + qemu_fdt_setprop_cell(vms->fdt, nodename, "bank-width", 4); g_free(nodename); } } -static void create_fw_cfg(const VirtBoardInfo *vbi, AddressSpace *as) +static FWCfgState *create_fw_cfg(const VirtMachineState *vms, AddressSpace *as) { - hwaddr base = vbi->memmap[VIRT_FW_CFG].base; - hwaddr size = vbi->memmap[VIRT_FW_CFG].size; + hwaddr base = vms->memmap[VIRT_FW_CFG].base; + hwaddr size = vms->memmap[VIRT_FW_CFG].size; FWCfgState *fw_cfg; char *nodename; @@ -936,15 +900,17 @@ static void create_fw_cfg(const VirtBoardInfo *vbi, AddressSpace *as) fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base); - qemu_fdt_add_subnode(vbi->fdt, nodename); - qemu_fdt_setprop_string(vbi->fdt, nodename, + qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "qemu,fw-cfg-mmio"); - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, base, 2, size); g_free(nodename); + return fw_cfg; } -static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle, +static void create_pcie_irq_map(const VirtMachineState *vms, + uint32_t gic_phandle, int first_irq, const char *nodename) { int devfn, pin; @@ -971,28 +937,27 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle, } } - qemu_fdt_setprop(vbi->fdt, nodename, "interrupt-map", + qemu_fdt_setprop(vms->fdt, nodename, "interrupt-map", full_irq_map, sizeof(full_irq_map)); - qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupt-map-mask", + qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupt-map-mask", 0x1800, 0, 0, /* devfn (PCI_SLOT(3)) */ 0x7 /* PCI irq */); } -static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic, - bool use_highmem) +static void create_pcie(const VirtMachineState *vms, qemu_irq *pic) { - hwaddr base_mmio = vbi->memmap[VIRT_PCIE_MMIO].base; - hwaddr size_mmio = vbi->memmap[VIRT_PCIE_MMIO].size; - hwaddr base_mmio_high = vbi->memmap[VIRT_PCIE_MMIO_HIGH].base; - hwaddr size_mmio_high = vbi->memmap[VIRT_PCIE_MMIO_HIGH].size; - hwaddr base_pio = vbi->memmap[VIRT_PCIE_PIO].base; - hwaddr size_pio = vbi->memmap[VIRT_PCIE_PIO].size; - hwaddr base_ecam = vbi->memmap[VIRT_PCIE_ECAM].base; - hwaddr size_ecam = vbi->memmap[VIRT_PCIE_ECAM].size; + hwaddr base_mmio = vms->memmap[VIRT_PCIE_MMIO].base; + hwaddr size_mmio = vms->memmap[VIRT_PCIE_MMIO].size; + hwaddr base_mmio_high = vms->memmap[VIRT_PCIE_MMIO_HIGH].base; + hwaddr size_mmio_high = vms->memmap[VIRT_PCIE_MMIO_HIGH].size; + hwaddr base_pio = vms->memmap[VIRT_PCIE_PIO].base; + hwaddr size_pio = vms->memmap[VIRT_PCIE_PIO].size; + hwaddr base_ecam = vms->memmap[VIRT_PCIE_ECAM].base; + hwaddr size_ecam = vms->memmap[VIRT_PCIE_ECAM].size; hwaddr base = base_mmio; int nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN; - int irq = vbi->irqmap[VIRT_PCIE]; + int irq = vms->irqmap[VIRT_PCIE]; MemoryRegion *mmio_alias; MemoryRegion *mmio_reg; MemoryRegion *ecam_alias; @@ -1023,7 +988,7 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic, mmio_reg, base_mmio, size_mmio); memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias); - if (use_highmem) { + if (vms->highmem) { /* Map high MMIO space */ MemoryRegion *high_mmio_alias = g_new0(MemoryRegion, 1); @@ -1054,26 +1019,26 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic, } nodename = g_strdup_printf("/pcie@%" PRIx64, base); - qemu_fdt_add_subnode(vbi->fdt, nodename); - qemu_fdt_setprop_string(vbi->fdt, nodename, + qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "pci-host-ecam-generic"); - qemu_fdt_setprop_string(vbi->fdt, nodename, "device_type", "pci"); - qemu_fdt_setprop_cell(vbi->fdt, nodename, "#address-cells", 3); - qemu_fdt_setprop_cell(vbi->fdt, nodename, "#size-cells", 2); - qemu_fdt_setprop_cells(vbi->fdt, nodename, "bus-range", 0, + qemu_fdt_setprop_string(vms->fdt, nodename, "device_type", "pci"); + qemu_fdt_setprop_cell(vms->fdt, nodename, "#address-cells", 3); + qemu_fdt_setprop_cell(vms->fdt, nodename, "#size-cells", 2); + qemu_fdt_setprop_cells(vms->fdt, nodename, "bus-range", 0, nr_pcie_buses - 1); - qemu_fdt_setprop(vbi->fdt, nodename, "dma-coherent", NULL, 0); + qemu_fdt_setprop(vms->fdt, nodename, "dma-coherent", NULL, 0); - if (vbi->msi_phandle) { - qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent", - vbi->msi_phandle); + if (vms->msi_phandle) { + qemu_fdt_setprop_cells(vms->fdt, nodename, "msi-parent", + vms->msi_phandle); } - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, base_ecam, 2, size_ecam); - if (use_highmem) { - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges", + if (vms->highmem) { + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "ranges", 1, FDT_PCI_RANGE_IOPORT, 2, 0, 2, base_pio, 2, size_pio, 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, @@ -1082,20 +1047,20 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic, 2, base_mmio_high, 2, base_mmio_high, 2, size_mmio_high); } else { - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges", + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "ranges", 1, FDT_PCI_RANGE_IOPORT, 2, 0, 2, base_pio, 2, size_pio, 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, 2, base_mmio, 2, size_mmio); } - qemu_fdt_setprop_cell(vbi->fdt, nodename, "#interrupt-cells", 1); - create_pcie_irq_map(vbi, vbi->gic_phandle, irq, nodename); + qemu_fdt_setprop_cell(vms->fdt, nodename, "#interrupt-cells", 1); + create_pcie_irq_map(vms, vms->gic_phandle, irq, nodename); g_free(nodename); } -static void create_platform_bus(VirtBoardInfo *vbi, qemu_irq *pic) +static void create_platform_bus(VirtMachineState *vms, qemu_irq *pic) { DeviceState *dev; SysBusDevice *s; @@ -1103,13 +1068,13 @@ static void create_platform_bus(VirtBoardInfo *vbi, qemu_irq *pic) ARMPlatformBusFDTParams *fdt_params = g_new(ARMPlatformBusFDTParams, 1); MemoryRegion *sysmem = get_system_memory(); - platform_bus_params.platform_bus_base = vbi->memmap[VIRT_PLATFORM_BUS].base; - platform_bus_params.platform_bus_size = vbi->memmap[VIRT_PLATFORM_BUS].size; - platform_bus_params.platform_bus_first_irq = vbi->irqmap[VIRT_PLATFORM_BUS]; + platform_bus_params.platform_bus_base = vms->memmap[VIRT_PLATFORM_BUS].base; + platform_bus_params.platform_bus_size = vms->memmap[VIRT_PLATFORM_BUS].size; + platform_bus_params.platform_bus_first_irq = vms->irqmap[VIRT_PLATFORM_BUS]; platform_bus_params.platform_bus_num_irqs = PLATFORM_BUS_NUM_IRQS; fdt_params->system_params = &platform_bus_params; - fdt_params->binfo = &vbi->bootinfo; + fdt_params->binfo = &vms->bootinfo; fdt_params->intc = "/intc"; /* * register a machine init done notifier that creates the device tree @@ -1136,43 +1101,44 @@ static void create_platform_bus(VirtBoardInfo *vbi, qemu_irq *pic) sysbus_mmio_get_region(s, 0)); } -static void create_secure_ram(VirtBoardInfo *vbi, MemoryRegion *secure_sysmem) +static void create_secure_ram(VirtMachineState *vms, + MemoryRegion *secure_sysmem) { MemoryRegion *secram = g_new(MemoryRegion, 1); char *nodename; - hwaddr base = vbi->memmap[VIRT_SECURE_MEM].base; - hwaddr size = vbi->memmap[VIRT_SECURE_MEM].size; + hwaddr base = vms->memmap[VIRT_SECURE_MEM].base; + hwaddr size = vms->memmap[VIRT_SECURE_MEM].size; memory_region_init_ram(secram, NULL, "virt.secure-ram", size, &error_fatal); vmstate_register_ram_global(secram); memory_region_add_subregion(secure_sysmem, base, secram); nodename = g_strdup_printf("/secram@%" PRIx64, base); - qemu_fdt_add_subnode(vbi->fdt, nodename); - qemu_fdt_setprop_string(vbi->fdt, nodename, "device_type", "memory"); - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", 2, base, 2, size); - qemu_fdt_setprop_string(vbi->fdt, nodename, "status", "disabled"); - qemu_fdt_setprop_string(vbi->fdt, nodename, "secure-status", "okay"); + qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_setprop_string(vms->fdt, nodename, "device_type", "memory"); + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, base, 2, size); + qemu_fdt_setprop_string(vms->fdt, nodename, "status", "disabled"); + qemu_fdt_setprop_string(vms->fdt, nodename, "secure-status", "okay"); g_free(nodename); } static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size) { - const VirtBoardInfo *board = (const VirtBoardInfo *)binfo; + const VirtMachineState *board = container_of(binfo, VirtMachineState, + bootinfo); *fdt_size = board->fdt_size; return board->fdt; } -static void virt_build_smbios(VirtGuestInfo *guest_info) +static void virt_build_smbios(VirtMachineState *vms) { - FWCfgState *fw_cfg = guest_info->fw_cfg; uint8_t *smbios_tables, *smbios_anchor; size_t smbios_tables_len, smbios_anchor_len; const char *product = "QEMU Virtual Machine"; - if (!fw_cfg) { + if (!vms->fw_cfg) { return; } @@ -1187,20 +1153,21 @@ static void virt_build_smbios(VirtGuestInfo *guest_info) &smbios_anchor, &smbios_anchor_len); if (smbios_anchor) { - fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables", + fw_cfg_add_file(vms->fw_cfg, "etc/smbios/smbios-tables", smbios_tables, smbios_tables_len); - fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor", + fw_cfg_add_file(vms->fw_cfg, "etc/smbios/smbios-anchor", smbios_anchor, smbios_anchor_len); } } static -void virt_guest_info_machine_done(Notifier *notifier, void *data) +void virt_machine_done(Notifier *notifier, void *data) { - VirtGuestInfoState *guest_info_state = container_of(notifier, - VirtGuestInfoState, machine_done); - virt_acpi_setup(&guest_info_state->info); - virt_build_smbios(&guest_info_state->info); + VirtMachineState *vms = container_of(notifier, VirtMachineState, + machine_done); + + virt_acpi_setup(vms); + virt_build_smbios(vms); } static void machvirt_init(MachineState *machine) @@ -1210,13 +1177,9 @@ static void machvirt_init(MachineState *machine) qemu_irq pic[NUM_IRQS]; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *secure_sysmem = NULL; - int gic_version = vms->gic_version; int n, virt_max_cpus; MemoryRegion *ram = g_new(MemoryRegion, 1); const char *cpu_model = machine->cpu_model; - VirtBoardInfo *vbi; - VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state); - VirtGuestInfo *guest_info = &guest_info_state->info; char **cpustr; ObjectClass *oc; const char *typename; @@ -1232,14 +1195,14 @@ static void machvirt_init(MachineState *machine) /* We can probe only here because during property set * KVM is not available yet */ - if (!gic_version) { + if (!vms->gic_version) { if (!kvm_enabled()) { error_report("gic-version=host requires KVM"); exit(1); } - gic_version = kvm_arm_vgic_probe(); - if (!gic_version) { + vms->gic_version = kvm_arm_vgic_probe(); + if (!vms->gic_version) { error_report("Unable to determine GIC version supported by host"); exit(1); } @@ -1248,9 +1211,7 @@ static void machvirt_init(MachineState *machine) /* Separate the actual CPU model name from any appended features */ cpustr = g_strsplit(cpu_model, ",", 2); - vbi = find_machine_info(cpustr[0]); - - if (!vbi) { + if (!cpuname_valid(cpustr[0])) { error_report("mach-virt: CPU %s not supported", cpustr[0]); exit(1); } @@ -1262,13 +1223,13 @@ static void machvirt_init(MachineState *machine) * let the boot ROM sort them out. * The usual case is that we do use QEMU's PSCI implementation. */ - vbi->using_psci = !(vms->secure && firmware_loaded); + vms->using_psci = !(vms->secure && firmware_loaded); /* The maximum number of CPUs depends on the GIC version, or on how * many redistributors we can fit into the memory map. */ - if (gic_version == 3) { - virt_max_cpus = vbi->memmap[VIRT_GIC_REDIST].size / 0x20000; + if (vms->gic_version == 3) { + virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / 0x20000; clustersz = GICV3_TARGETLIST_BITS; } else { virt_max_cpus = GIC_NCPU; @@ -1282,9 +1243,9 @@ static void machvirt_init(MachineState *machine) exit(1); } - vbi->smp_cpus = smp_cpus; + vms->smp_cpus = smp_cpus; - if (machine->ram_size > vbi->memmap[VIRT_MEM].size) { + if (machine->ram_size > vms->memmap[VIRT_MEM].size) { error_report("mach-virt: cannot model more than %dGB RAM", RAMLIMIT_GB); exit(1); } @@ -1306,7 +1267,7 @@ static void machvirt_init(MachineState *machine) memory_region_add_subregion_overlap(secure_sysmem, 0, sysmem, -1); } - create_fdt(vbi); + create_fdt(vms); oc = cpu_class_by_name(TYPE_ARM_CPU, cpustr[0]); if (!oc) { @@ -1345,7 +1306,7 @@ static void machvirt_init(MachineState *machine) object_property_set_bool(cpuobj, false, "has_el3", NULL); } - if (vbi->using_psci) { + if (vms->using_psci) { object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC, "psci-conduit", NULL); @@ -1361,7 +1322,7 @@ static void machvirt_init(MachineState *machine) } if (object_property_find(cpuobj, "reset-cbar", NULL)) { - object_property_set_int(cpuobj, vbi->memmap[VIRT_CPUPERIPHS].base, + object_property_set_int(cpuobj, vms->memmap[VIRT_CPUPERIPHS].base, "reset-cbar", &error_abort); } @@ -1374,62 +1335,55 @@ static void machvirt_init(MachineState *machine) object_property_set_bool(cpuobj, true, "realized", NULL); } - fdt_add_timer_nodes(vbi, gic_version); - fdt_add_cpu_nodes(vbi); - fdt_add_psci_node(vbi); + fdt_add_timer_nodes(vms); + fdt_add_cpu_nodes(vms); + fdt_add_psci_node(vms); memory_region_allocate_system_memory(ram, NULL, "mach-virt.ram", machine->ram_size); - memory_region_add_subregion(sysmem, vbi->memmap[VIRT_MEM].base, ram); + memory_region_add_subregion(sysmem, vms->memmap[VIRT_MEM].base, ram); - create_flash(vbi, sysmem, secure_sysmem ? secure_sysmem : sysmem); + create_flash(vms, sysmem, secure_sysmem ? secure_sysmem : sysmem); - create_gic(vbi, pic, gic_version, vms->secure, vmc->no_its); + create_gic(vms, pic); - fdt_add_pmu_nodes(vbi, gic_version); + fdt_add_pmu_nodes(vms); - create_uart(vbi, pic, VIRT_UART, sysmem, serial_hds[0]); + create_uart(vms, pic, VIRT_UART, sysmem, serial_hds[0]); if (vms->secure) { - create_secure_ram(vbi, secure_sysmem); - create_uart(vbi, pic, VIRT_SECURE_UART, secure_sysmem, serial_hds[1]); + create_secure_ram(vms, secure_sysmem); + create_uart(vms, pic, VIRT_SECURE_UART, secure_sysmem, serial_hds[1]); } - create_rtc(vbi, pic); + create_rtc(vms, pic); - create_pcie(vbi, pic, vms->highmem); + create_pcie(vms, pic); - create_gpio(vbi, pic); + create_gpio(vms, pic); /* Create mmio transports, so the user can create virtio backends * (which will be automatically plugged in to the transports). If * no backend is created the transport will just sit harmlessly idle. */ - create_virtio_devices(vbi, pic); - - create_fw_cfg(vbi, &address_space_memory); - rom_set_fw(fw_cfg_find()); - - guest_info->smp_cpus = smp_cpus; - guest_info->fw_cfg = fw_cfg_find(); - guest_info->memmap = vbi->memmap; - guest_info->irqmap = vbi->irqmap; - guest_info->use_highmem = vms->highmem; - guest_info->gic_version = gic_version; - guest_info->no_its = vmc->no_its; - guest_info_state->machine_done.notify = virt_guest_info_machine_done; - qemu_add_machine_init_done_notifier(&guest_info_state->machine_done); - - vbi->bootinfo.ram_size = machine->ram_size; - vbi->bootinfo.kernel_filename = machine->kernel_filename; - vbi->bootinfo.kernel_cmdline = machine->kernel_cmdline; - vbi->bootinfo.initrd_filename = machine->initrd_filename; - vbi->bootinfo.nb_cpus = smp_cpus; - vbi->bootinfo.board_id = -1; - vbi->bootinfo.loader_start = vbi->memmap[VIRT_MEM].base; - vbi->bootinfo.get_dtb = machvirt_dtb; - vbi->bootinfo.firmware_loaded = firmware_loaded; - arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo); + create_virtio_devices(vms, pic); + + vms->fw_cfg = create_fw_cfg(vms, &address_space_memory); + rom_set_fw(vms->fw_cfg); + + vms->machine_done.notify = virt_machine_done; + qemu_add_machine_init_done_notifier(&vms->machine_done); + + vms->bootinfo.ram_size = machine->ram_size; + vms->bootinfo.kernel_filename = machine->kernel_filename; + vms->bootinfo.kernel_cmdline = machine->kernel_cmdline; + vms->bootinfo.initrd_filename = machine->initrd_filename; + vms->bootinfo.nb_cpus = smp_cpus; + vms->bootinfo.board_id = -1; + vms->bootinfo.loader_start = vms->memmap[VIRT_MEM].base; + vms->bootinfo.get_dtb = machvirt_dtb; + vms->bootinfo.firmware_loaded = firmware_loaded; + arm_load_kernel(ARM_CPU(first_cpu), &vms->bootinfo); /* * arm_load_kernel machine init done notifier registration must @@ -1437,7 +1391,7 @@ static void machvirt_init(MachineState *machine) * another notifier is registered which adds platform bus nodes. * Notifiers are executed in registration reverse order. */ - create_platform_bus(vbi, pic); + create_platform_bus(vms, pic); } static bool virt_get_secure(Object *obj, Error **errp) @@ -1556,6 +1510,9 @@ static void virt_2_9_instance_init(Object *obj) object_property_set_description(obj, "gic-version", "Set GIC version. " "Valid values are 2, 3 and host", NULL); + + vms->memmap = a15memmap; + vms->irqmap = a15irqmap; } static void virt_machine_2_9_options(MachineClass *mc) @@ -1573,8 +1530,14 @@ static void virt_2_8_instance_init(Object *obj) static void virt_machine_2_8_options(MachineClass *mc) { + VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); + virt_machine_2_9_options(mc); SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_8); + /* For 2.8 and earlier we falsely claimed in the DT that + * our timers were edge-triggered, not level-triggered. + */ + vmc->claim_edge_triggered_timers = true; } DEFINE_VIRT_MACHINE(2, 8) diff --git a/hw/arm/z2.c b/hw/arm/z2.c index b3a6bbd210..1607cbdb03 100644 --- a/hw/arm/z2.c +++ b/hw/arm/z2.c @@ -220,7 +220,7 @@ static int aer915_send(I2CSlave *i2c, uint8_t data) return 0; } -static void aer915_event(I2CSlave *i2c, enum i2c_event event) +static int aer915_event(I2CSlave *i2c, enum i2c_event event) { AER915State *s = AER915(i2c); @@ -238,6 +238,8 @@ static void aer915_event(I2CSlave *i2c, enum i2c_event event) default: break; } + + return 0; } static int aer915_recv(I2CSlave *slave) diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c index 0c6500e96a..f8b5bebfc2 100644 --- a/hw/audio/wm8750.c +++ b/hw/audio/wm8750.c @@ -303,7 +303,7 @@ static void wm8750_reset(I2CSlave *i2c) s->i2c_len = 0; } -static void wm8750_event(I2CSlave *i2c, enum i2c_event event) +static int wm8750_event(I2CSlave *i2c, enum i2c_event event) { WM8750State *s = WM8750(i2c); @@ -321,6 +321,8 @@ static void wm8750_event(I2CSlave *i2c, enum i2c_event event) default: break; } + + return 0; } #define WM8750_LINVOL 0x00 diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index e3c1166ea6..4c5f8c3590 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -28,6 +28,7 @@ #include "hw/ssi/ssi.h" #include "qemu/bitops.h" #include "qemu/log.h" +#include "qemu/error-report.h" #include "qapi/error.h" #ifndef M25P80_ERR_DEBUG @@ -377,6 +378,8 @@ typedef enum { MAN_GENERIC, } Manufacturer; +#define M25P80_INTERNAL_DATA_BUFFER_SZ 16 + typedef struct Flash { SSISlave parent_obj; @@ -387,7 +390,7 @@ typedef struct Flash { int page_size; uint8_t state; - uint8_t data[16]; + uint8_t data[M25P80_INTERNAL_DATA_BUFFER_SZ]; uint32_t len; uint32_t pos; uint8_t needed_bytes; @@ -1115,6 +1118,17 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) case STATE_COLLECTING_DATA: case STATE_COLLECTING_VAR_LEN_DATA: + + if (s->len >= M25P80_INTERNAL_DATA_BUFFER_SZ) { + qemu_log_mask(LOG_GUEST_ERROR, + "M25P80: Write overrun internal data buffer. " + "SPI controller (QEMU emulator or guest driver) " + "is misbehaving\n"); + s->len = s->pos = 0; + s->state = STATE_IDLE; + break; + } + s->data[s->len] = (uint8_t)tx; s->len++; @@ -1124,6 +1138,17 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) break; case STATE_READING_DATA: + + if (s->pos >= M25P80_INTERNAL_DATA_BUFFER_SZ) { + qemu_log_mask(LOG_GUEST_ERROR, + "M25P80: Read overrun internal data buffer. " + "SPI controller (QEMU emulator or guest driver) " + "is misbehaving\n"); + s->len = s->pos = 0; + s->state = STATE_IDLE; + break; + } + r = s->data[s->pos]; s->pos++; if (s->pos == s->len) { @@ -1196,7 +1221,7 @@ static const VMStateDescription vmstate_m25p80 = { .pre_save = m25p80_pre_save, .fields = (VMStateField[]) { VMSTATE_UINT8(state, Flash), - VMSTATE_UINT8_ARRAY(data, Flash, 16), + VMSTATE_UINT8_ARRAY(data, Flash, M25P80_INTERNAL_DATA_BUFFER_SZ), VMSTATE_UINT32(len, Flash), VMSTATE_UINT32(pos, Flash), VMSTATE_UINT8(needed_bytes, Flash), diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 50bb0cbb93..702eda863e 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -863,7 +863,7 @@ static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f, } } - req = qemu_get_virtqueue_element(f, sizeof(VirtIOBlockReq)); + req = qemu_get_virtqueue_element(vdev, f, sizeof(VirtIOBlockReq)); virtio_blk_init_request(s, virtio_get_queue(vdev, vq_idx), req); req->next = s->rq; s->rq = req; diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index 571c324004..820d1abeb9 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -629,22 +629,26 @@ DeviceState *exynos4210_uart_create(hwaddr addr, return dev; } -static int exynos4210_uart_init(SysBusDevice *dev) +static void exynos4210_uart_init(Object *obj) { + SysBusDevice *dev = SYS_BUS_DEVICE(obj); Exynos4210UartState *s = EXYNOS4210_UART(dev); /* memory mapping */ - memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_uart_ops, s, + memory_region_init_io(&s->iomem, obj, &exynos4210_uart_ops, s, "exynos4210.uart", EXYNOS4210_UART_REGS_MEM_SIZE); sysbus_init_mmio(dev, &s->iomem); sysbus_init_irq(dev, &s->irq); +} + +static void exynos4210_uart_realize(DeviceState *dev, Error **errp) +{ + Exynos4210UartState *s = EXYNOS4210_UART(dev); qemu_chr_fe_set_handlers(&s->chr, exynos4210_uart_can_receive, exynos4210_uart_receive, exynos4210_uart_event, s, NULL, true); - - return 0; } static Property exynos4210_uart_properties[] = { @@ -658,9 +662,8 @@ static Property exynos4210_uart_properties[] = { static void exynos4210_uart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = exynos4210_uart_init; + dc->realize = exynos4210_uart_realize; dc->reset = exynos4210_uart_reset; dc->props = exynos4210_uart_properties; dc->vmsd = &vmstate_exynos4210_uart; @@ -670,6 +673,7 @@ static const TypeInfo exynos4210_uart_info = { .name = TYPE_EXYNOS4210_UART, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210UartState), + .instance_init = exynos4210_uart_init, .class_init = exynos4210_uart_class_init, }; diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index 7975c2cda1..d544cd91c0 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -732,6 +732,7 @@ static void virtio_serial_post_load_timer_cb(void *opaque) static int fetch_active_ports_list(QEMUFile *f, VirtIOSerial *s, uint32_t nr_active_ports) { + VirtIODevice *vdev = VIRTIO_DEVICE(s); uint32_t i; s->post_load = g_malloc0(sizeof(*s->post_load)); @@ -765,7 +766,7 @@ static int fetch_active_ports_list(QEMUFile *f, qemu_get_be64s(f, &port->iov_offset); port->elem = - qemu_get_virtqueue_element(f, sizeof(VirtQueueElement)); + qemu_get_virtqueue_element(vdev, f, sizeof(VirtQueueElement)); /* * Port was throttled on source machine. Let's diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c index d3017563f3..68a80b9d64 100644 --- a/hw/display/ssd0303.c +++ b/hw/display/ssd0303.c @@ -179,7 +179,7 @@ static int ssd0303_send(I2CSlave *i2c, uint8_t data) return 0; } -static void ssd0303_event(I2CSlave *i2c, enum i2c_event event) +static int ssd0303_event(I2CSlave *i2c, enum i2c_event event) { ssd0303_state *s = SSD0303(i2c); @@ -193,6 +193,8 @@ static void ssd0303_event(I2CSlave *i2c, enum i2c_event event) /* Nothing to do. */ break; } + + return 0; } static void ssd0303_update_display(void *opaque) diff --git a/hw/gpio/max7310.c b/hw/gpio/max7310.c index 1bd5eaf911..f82e3e6555 100644 --- a/hw/gpio/max7310.c +++ b/hw/gpio/max7310.c @@ -129,7 +129,7 @@ static int max7310_tx(I2CSlave *i2c, uint8_t data) return 0; } -static void max7310_event(I2CSlave *i2c, enum i2c_event event) +static int max7310_event(I2CSlave *i2c, enum i2c_event event) { MAX7310State *s = MAX7310(i2c); s->len = 0; @@ -147,6 +147,8 @@ static void max7310_event(I2CSlave *i2c, enum i2c_event event) default: break; } + + return 0; } static const VMStateDescription vmstate_max7310 = { diff --git a/hw/i2c/core.c b/hw/i2c/core.c index e40781ea3b..2c1234cdff 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -88,18 +88,26 @@ int i2c_bus_busy(I2CBus *bus) return !QLIST_EMPTY(&bus->current_devs); } +/* TODO: Make this handle multiple masters. */ /* - * Returns non-zero if the address is not valid. If this is called - * again without an intervening i2c_end_transfer(), like in the SMBus - * case where the operation is switched from write to read, this - * function will not rescan the bus and thus cannot fail. + * Start or continue an i2c transaction. When this is called for the + * first time or after an i2c_end_transfer(), if it returns an error + * the bus transaction is terminated (or really never started). If + * this is called after another i2c_start_transfer() without an + * intervening i2c_end_transfer(), and it returns an error, the + * transaction will not be terminated. The caller must do it. + * + * This corresponds with the way real hardware works. The SMBus + * protocol uses a start transfer to switch from write to read mode + * without releasing the bus. If that fails, the bus is still + * in a transaction. */ -/* TODO: Make this handle multiple masters. */ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) { BusChild *kid; I2CSlaveClass *sc; I2CNode *node; + bool bus_scanned = false; if (address == I2C_BROADCAST) { /* @@ -130,6 +138,7 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) } } } + bus_scanned = true; } if (QLIST_EMPTY(&bus->current_devs)) { @@ -137,11 +146,21 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) } QLIST_FOREACH(node, &bus->current_devs, next) { + int rv; + sc = I2C_SLAVE_GET_CLASS(node->elt); /* If the bus is already busy, assume this is a repeated start condition. */ + if (sc->event) { - sc->event(node->elt, recv ? I2C_START_RECV : I2C_START_SEND); + rv = sc->event(node->elt, recv ? I2C_START_RECV : I2C_START_SEND); + if (rv && !bus->broadcast) { + if (bus_scanned) { + /* First call, terminate the transfer. */ + i2c_end_transfer(bus); + } + return rv; + } } } return 0; diff --git a/hw/i2c/i2c-ddc.c b/hw/i2c/i2c-ddc.c index 1227212934..66899d7233 100644 --- a/hw/i2c/i2c-ddc.c +++ b/hw/i2c/i2c-ddc.c @@ -230,13 +230,15 @@ static void i2c_ddc_reset(DeviceState *ds) s->reg = 0; } -static void i2c_ddc_event(I2CSlave *i2c, enum i2c_event event) +static int i2c_ddc_event(I2CSlave *i2c, enum i2c_event event) { I2CDDCState *s = I2CDDC(i2c); if (event == I2C_START_SEND) { s->firstbyte = true; } + + return 0; } static int i2c_ddc_rx(I2CSlave *i2c) diff --git a/hw/i2c/smbus.c b/hw/i2c/smbus.c index 5b4dd3eba4..2d1b79a689 100644 --- a/hw/i2c/smbus.c +++ b/hw/i2c/smbus.c @@ -67,7 +67,7 @@ static void smbus_do_write(SMBusDevice *dev) } } -static void smbus_i2c_event(I2CSlave *s, enum i2c_event event) +static int smbus_i2c_event(I2CSlave *s, enum i2c_event event) { SMBusDevice *dev = SMBUS_DEVICE(s); @@ -148,6 +148,8 @@ static void smbus_i2c_event(I2CSlave *s, enum i2c_event event) break; } } + + return 0; } static int smbus_i2c_recv(I2CSlave *s) @@ -249,7 +251,8 @@ int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command) } i2c_send(bus, command); if (i2c_start_transfer(bus, addr, 1)) { - assert(0); + i2c_end_transfer(bus); + return -1; } data = i2c_recv(bus); i2c_nack(bus); @@ -276,7 +279,8 @@ int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command) } i2c_send(bus, command); if (i2c_start_transfer(bus, addr, 1)) { - assert(0); + i2c_end_transfer(bus); + return -1; } data = i2c_recv(bus); data |= i2c_recv(bus) << 8; @@ -307,7 +311,8 @@ int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data) } i2c_send(bus, command); if (i2c_start_transfer(bus, addr, 1)) { - assert(0); + i2c_end_transfer(bus); + return -1; } len = i2c_recv(bus); if (len > 32) { diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 42ecf619d5..0c8912fd86 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -101,8 +101,6 @@ typedef struct AcpiPmInfo { uint32_t gpe0_blk_len; uint32_t io_base; uint16_t cpu_hp_io_base; - uint16_t mem_hp_io_base; - uint16_t mem_hp_io_len; uint16_t pcihp_io_base; uint16_t pcihp_io_len; } AcpiPmInfo; @@ -148,9 +146,6 @@ static void acpi_get_pm_info(AcpiPmInfo *pm) } assert(obj); - pm->mem_hp_io_base = ACPI_MEMORY_HOTPLUG_BASE; - pm->mem_hp_io_len = ACPI_MEMORY_HOTPLUG_IO_LEN; - /* Fill in optional s3/s4 related properties */ o = object_property_get_qobject(obj, ACPI_PM_PROP_S3_DISABLED, NULL); if (o) { @@ -1038,130 +1033,6 @@ static Aml *build_crs(PCIHostState *host, CrsRangeSet *range_set) return crs; } -static void build_memory_devices(Aml *sb_scope, int nr_mem, - uint16_t io_base, uint16_t io_len) -{ - int i; - Aml *scope; - Aml *crs; - Aml *field; - Aml *dev; - Aml *method; - Aml *ifctx; - - /* build memory devices */ - assert(nr_mem <= ACPI_MAX_RAM_SLOTS); - scope = aml_scope("\\_SB.PCI0." MEMORY_HOTPLUG_DEVICE); - aml_append(scope, - aml_name_decl(MEMORY_SLOTS_NUMBER, aml_int(nr_mem)) - ); - - crs = aml_resource_template(); - aml_append(crs, - aml_io(AML_DECODE16, io_base, io_base, 0, io_len) - ); - aml_append(scope, aml_name_decl("_CRS", crs)); - - aml_append(scope, aml_operation_region( - MEMORY_HOTPLUG_IO_REGION, AML_SYSTEM_IO, - aml_int(io_base), io_len) - ); - - field = aml_field(MEMORY_HOTPLUG_IO_REGION, AML_DWORD_ACC, - AML_NOLOCK, AML_PRESERVE); - aml_append(field, /* read only */ - aml_named_field(MEMORY_SLOT_ADDR_LOW, 32)); - aml_append(field, /* read only */ - aml_named_field(MEMORY_SLOT_ADDR_HIGH, 32)); - aml_append(field, /* read only */ - aml_named_field(MEMORY_SLOT_SIZE_LOW, 32)); - aml_append(field, /* read only */ - aml_named_field(MEMORY_SLOT_SIZE_HIGH, 32)); - aml_append(field, /* read only */ - aml_named_field(MEMORY_SLOT_PROXIMITY, 32)); - aml_append(scope, field); - - field = aml_field(MEMORY_HOTPLUG_IO_REGION, AML_BYTE_ACC, - AML_NOLOCK, AML_WRITE_AS_ZEROS); - aml_append(field, aml_reserved_field(160 /* bits, Offset(20) */)); - aml_append(field, /* 1 if enabled, read only */ - aml_named_field(MEMORY_SLOT_ENABLED, 1)); - aml_append(field, - /*(read) 1 if has a insert event. (write) 1 to clear event */ - aml_named_field(MEMORY_SLOT_INSERT_EVENT, 1)); - aml_append(field, - /* (read) 1 if has a remove event. (write) 1 to clear event */ - aml_named_field(MEMORY_SLOT_REMOVE_EVENT, 1)); - aml_append(field, - /* initiates device eject, write only */ - aml_named_field(MEMORY_SLOT_EJECT, 1)); - aml_append(scope, field); - - field = aml_field(MEMORY_HOTPLUG_IO_REGION, AML_DWORD_ACC, - AML_NOLOCK, AML_PRESERVE); - aml_append(field, /* DIMM selector, write only */ - aml_named_field(MEMORY_SLOT_SLECTOR, 32)); - aml_append(field, /* _OST event code, write only */ - aml_named_field(MEMORY_SLOT_OST_EVENT, 32)); - aml_append(field, /* _OST status code, write only */ - aml_named_field(MEMORY_SLOT_OST_STATUS, 32)); - aml_append(scope, field); - aml_append(sb_scope, scope); - - for (i = 0; i < nr_mem; i++) { - #define BASEPATH "\\_SB.PCI0." MEMORY_HOTPLUG_DEVICE "." - const char *s; - - dev = aml_device("MP%02X", i); - aml_append(dev, aml_name_decl("_UID", aml_string("0x%02X", i))); - aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C80"))); - - method = aml_method("_CRS", 0, AML_NOTSERIALIZED); - s = BASEPATH MEMORY_SLOT_CRS_METHOD; - aml_append(method, aml_return(aml_call1(s, aml_name("_UID")))); - aml_append(dev, method); - - method = aml_method("_STA", 0, AML_NOTSERIALIZED); - s = BASEPATH MEMORY_SLOT_STATUS_METHOD; - aml_append(method, aml_return(aml_call1(s, aml_name("_UID")))); - aml_append(dev, method); - - method = aml_method("_PXM", 0, AML_NOTSERIALIZED); - s = BASEPATH MEMORY_SLOT_PROXIMITY_METHOD; - aml_append(method, aml_return(aml_call1(s, aml_name("_UID")))); - aml_append(dev, method); - - method = aml_method("_OST", 3, AML_NOTSERIALIZED); - s = BASEPATH MEMORY_SLOT_OST_METHOD; - - aml_append(method, aml_return(aml_call4( - s, aml_name("_UID"), aml_arg(0), aml_arg(1), aml_arg(2) - ))); - aml_append(dev, method); - - method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); - s = BASEPATH MEMORY_SLOT_EJECT_METHOD; - aml_append(method, aml_return(aml_call2( - s, aml_name("_UID"), aml_arg(0)))); - aml_append(dev, method); - - aml_append(sb_scope, dev); - } - - /* build Method(MEMORY_SLOT_NOTIFY_METHOD, 2) { - * If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ... } - */ - method = aml_method(MEMORY_SLOT_NOTIFY_METHOD, 2, AML_NOTSERIALIZED); - for (i = 0; i < nr_mem; i++) { - ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i))); - aml_append(ifctx, - aml_notify(aml_name("MP%.02X", i), aml_arg(1)) - ); - aml_append(method, ifctx); - } - aml_append(sb_scope, method); -} - static void build_hpet_aml(Aml *table) { Aml *crs; @@ -2049,8 +1920,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base, "\\_SB.PCI0", "\\_GPE._E02"); } - build_memory_hotplug_aml(dsdt, nr_mem, pm->mem_hp_io_base, - pm->mem_hp_io_len); + build_memory_hotplug_aml(dsdt, nr_mem, "\\_SB.PCI0", "\\_GPE._E03"); scope = aml_scope("_GPE"); { @@ -2065,10 +1935,6 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(scope, method); } - method = aml_method("_E03", 0, AML_NOTSERIALIZED); - aml_append(method, aml_call0(MEMORY_HOTPLUG_HANDLER_PATH)); - aml_append(scope, method); - if (pcms->acpi_nvdimm_state.is_enabled) { method = aml_method("_E04", 0, AML_NOTSERIALIZED); aml_append(method, aml_notify(aml_name("\\_SB.NVDR"), @@ -2321,45 +2187,40 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, sb_scope = aml_scope("\\_SB"); { - build_memory_devices(sb_scope, nr_mem, pm->mem_hp_io_base, - pm->mem_hp_io_len); + Object *pci_host; + PCIBus *bus = NULL; - { - Object *pci_host; - PCIBus *bus = NULL; + pci_host = acpi_get_i386_pci_host(); + if (pci_host) { + bus = PCI_HOST_BRIDGE(pci_host)->bus; + } - pci_host = acpi_get_i386_pci_host(); - if (pci_host) { - bus = PCI_HOST_BRIDGE(pci_host)->bus; + if (bus) { + 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)); + /* + FIXME: TPM_TIS_IRQ=5 conflicts with PNP0C0F irqs, + Rewrite to take IRQ from TPM device model and + fix default IRQ value there to use some unused IRQ + */ + /* aml_append(crs, aml_irq_no_flags(TPM_TIS_IRQ)); */ + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); } - if (bus) { - 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)); - /* - FIXME: TPM_TIS_IRQ=5 conflicts with PNP0C0F irqs, - Rewrite to take IRQ from TPM device model and - fix default IRQ value there to use some unused IRQ - */ - /* 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); - } + aml_append(sb_scope, scope); } - aml_append(dsdt, sb_scope); } + aml_append(dsdt, sb_scope); /* copy AML table into ACPI tables blob and patch header there */ g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); @@ -2575,6 +2436,7 @@ build_dmar_q35(GArray *table_data, BIOSLinker *linker) AcpiTableDmar *dmar; AcpiDmarHardwareUnit *drhd; + AcpiDmarRootPortATS *atsr; uint8_t dmar_flags = 0; X86IOMMUState *iommu = x86_iommu_get_default(); AcpiDmarDeviceScope *scope = NULL; @@ -2608,6 +2470,14 @@ build_dmar_q35(GArray *table_data, BIOSLinker *linker) scope->path[0].device = PCI_SLOT(Q35_PSEUDO_DEVFN_IOAPIC); scope->path[0].function = PCI_FUNC(Q35_PSEUDO_DEVFN_IOAPIC); + if (iommu->dt_supported) { + atsr = acpi_data_push(table_data, sizeof(*atsr)); + atsr->type = cpu_to_le16(ACPI_DMAR_TYPE_ATSR); + atsr->length = cpu_to_le16(sizeof(*atsr)); + atsr->flags = ACPI_DMAR_ATSR_ALL_PORTS; + atsr->pci_segment = cpu_to_le16(0); + } + build_header(linker, table_data, (void *)(table_data->data + dmar_start), "DMAR", table_data->len - dmar_start, 1, NULL, NULL); } diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 47b79d9112..e0732ccaf1 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -562,7 +562,7 @@ static void amdvi_mmio_trace(hwaddr addr, unsigned size) trace_amdvi_mmio_read(amdvi_mmio_high[index], addr, size, addr & ~0x07); } else { index = index >= AMDVI_MMIO_REGS_LOW ? AMDVI_MMIO_REGS_LOW : index; - trace_amdvi_mmio_read(amdvi_mmio_high[index], addr, size, addr & ~0x07); + trace_amdvi_mmio_read(amdvi_mmio_low[index], addr, size, addr & ~0x07); } } diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 884926e9e7..0d3dc6a9f2 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -49,8 +49,8 @@ #define AMDVI_CAPAB_INIT_TYPE (3 << 16) /* No. of used MMIO registers */ -#define AMDVI_MMIO_REGS_HIGH 8 -#define AMDVI_MMIO_REGS_LOW 7 +#define AMDVI_MMIO_REGS_HIGH 7 +#define AMDVI_MMIO_REGS_LOW 8 /* MMIO registers */ #define AMDVI_MMIO_DEVICE_TABLE 0x0000 diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 5f3e35123d..ec62239aba 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -738,11 +738,18 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, "context-entry hi 0x%"PRIx64 " lo 0x%"PRIx64, ce->hi, ce->lo); return -VTD_FR_CONTEXT_ENTRY_INV; - } else if (ce->lo & VTD_CONTEXT_ENTRY_TT) { - VTD_DPRINTF(GENERAL, "error: unsupported Translation Type in " - "context-entry hi 0x%"PRIx64 " lo 0x%"PRIx64, - ce->hi, ce->lo); - return -VTD_FR_CONTEXT_ENTRY_INV; + } else { + switch (ce->lo & VTD_CONTEXT_ENTRY_TT) { + case VTD_CONTEXT_TT_MULTI_LEVEL: + /* fall through */ + case VTD_CONTEXT_TT_DEV_IOTLB: + break; + default: + VTD_DPRINTF(GENERAL, "error: unsupported Translation Type in " + "context-entry hi 0x%"PRIx64 " lo 0x%"PRIx64, + ce->hi, ce->lo); + return -VTD_FR_CONTEXT_ENTRY_INV; + } } return 0; } @@ -1438,7 +1445,61 @@ static bool vtd_process_inv_iec_desc(IntelIOMMUState *s, vtd_iec_notify_all(s, !inv_desc->iec.granularity, inv_desc->iec.index, inv_desc->iec.index_mask); + return true; +} +static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, + VTDInvDesc *inv_desc) +{ + VTDAddressSpace *vtd_dev_as; + IOMMUTLBEntry entry; + struct VTDBus *vtd_bus; + hwaddr addr; + uint64_t sz; + uint16_t sid; + uint8_t devfn; + bool size; + uint8_t bus_num; + + addr = VTD_INV_DESC_DEVICE_IOTLB_ADDR(inv_desc->hi); + sid = VTD_INV_DESC_DEVICE_IOTLB_SID(inv_desc->lo); + devfn = sid & 0xff; + bus_num = sid >> 8; + size = VTD_INV_DESC_DEVICE_IOTLB_SIZE(inv_desc->hi); + + if ((inv_desc->lo & VTD_INV_DESC_DEVICE_IOTLB_RSVD_LO) || + (inv_desc->hi & VTD_INV_DESC_DEVICE_IOTLB_RSVD_HI)) { + VTD_DPRINTF(GENERAL, "error: non-zero reserved field in Device " + "IOTLB Invalidate Descriptor hi 0x%"PRIx64 " lo 0x%"PRIx64, + inv_desc->hi, inv_desc->lo); + return false; + } + + vtd_bus = vtd_find_as_from_bus_num(s, bus_num); + if (!vtd_bus) { + goto done; + } + + vtd_dev_as = vtd_bus->dev_as[devfn]; + if (!vtd_dev_as) { + goto done; + } + + if (size) { + sz = 1 << (ctz64(~(addr | (VTD_PAGE_MASK_4K - 1))) + 1); + addr &= ~(sz - 1); + } else { + sz = VTD_PAGE_SIZE; + } + + entry.target_as = &vtd_dev_as->as; + entry.addr_mask = sz - 1; + entry.iova = addr; + entry.perm = IOMMU_NONE; + entry.translated_addr = 0; + memory_region_notify_iommu(entry.target_as->root, entry); + +done: return true; } @@ -1490,6 +1551,14 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) } break; + case VTD_INV_DESC_DEVICE: + VTD_DPRINTF(INV, "Device IOTLB Invalidation Descriptor hi 0x%"PRIx64 + " lo 0x%"PRIx64, inv_desc.hi, inv_desc.lo); + if (!vtd_process_device_iotlb_desc(s, &inv_desc)) { + return false; + } + break; + default: VTD_DPRINTF(GENERAL, "error: unkonw Invalidation Descriptor type " "hi 0x%"PRIx64 " lo 0x%"PRIx64 " type %"PRIu8, @@ -1996,7 +2065,27 @@ static void vtd_iommu_notify_flag_changed(MemoryRegion *iommu, static const VMStateDescription vtd_vmstate = { .name = "iommu-intel", - .unmigratable = 1, + .version_id = 1, + .minimum_version_id = 1, + .priority = MIG_PRI_IOMMU, + .fields = (VMStateField[]) { + VMSTATE_UINT64(root, IntelIOMMUState), + VMSTATE_UINT64(intr_root, IntelIOMMUState), + VMSTATE_UINT64(iq, IntelIOMMUState), + VMSTATE_UINT32(intr_size, IntelIOMMUState), + VMSTATE_UINT16(iq_head, IntelIOMMUState), + VMSTATE_UINT16(iq_tail, IntelIOMMUState), + VMSTATE_UINT16(iq_size, IntelIOMMUState), + VMSTATE_UINT16(next_frcd_reg, IntelIOMMUState), + VMSTATE_UINT8_ARRAY(csr, IntelIOMMUState, DMAR_REG_SIZE), + VMSTATE_UINT8(iq_last_desc_type, IntelIOMMUState), + VMSTATE_BOOL(root_extended, IntelIOMMUState), + VMSTATE_BOOL(dmar_enabled, IntelIOMMUState), + VMSTATE_BOOL(qi_enabled, IntelIOMMUState), + VMSTATE_BOOL(intr_enabled, IntelIOMMUState), + VMSTATE_BOOL(intr_eime, IntelIOMMUState), + VMSTATE_END_OF_LIST() + } }; static const MemoryRegionOps vtd_mem_ops = { @@ -2324,19 +2413,22 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) uintptr_t key = (uintptr_t)bus; VTDBus *vtd_bus = g_hash_table_lookup(s->vtd_as_by_busptr, &key); VTDAddressSpace *vtd_dev_as; + char name[128]; if (!vtd_bus) { + uintptr_t *new_key = g_malloc(sizeof(*new_key)); + *new_key = (uintptr_t)bus; /* No corresponding free() */ vtd_bus = g_malloc0(sizeof(VTDBus) + sizeof(VTDAddressSpace *) * \ X86_IOMMU_PCI_DEVFN_MAX); vtd_bus->bus = bus; - key = (uintptr_t)bus; - g_hash_table_insert(s->vtd_as_by_busptr, &key, vtd_bus); + g_hash_table_insert(s->vtd_as_by_busptr, new_key, vtd_bus); } vtd_dev_as = vtd_bus->dev_as[devfn]; if (!vtd_dev_as) { + snprintf(name, sizeof(name), "intel_iommu_devfn_%d", devfn); vtd_bus->dev_as[devfn] = vtd_dev_as = g_malloc0(sizeof(VTDAddressSpace)); vtd_dev_as->bus = bus; @@ -2351,7 +2443,7 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) memory_region_add_subregion(&vtd_dev_as->iommu, VTD_INTERRUPT_ADDR_FIRST, &vtd_dev_as->iommu_ir); address_space_init(&vtd_dev_as->as, - &vtd_dev_as->iommu, "intel_iommu"); + &vtd_dev_as->iommu, name); } return vtd_dev_as; } @@ -2392,6 +2484,10 @@ static void vtd_init(IntelIOMMUState *s) assert(s->intr_eim != ON_OFF_AUTO_AUTO); } + if (x86_iommu->dt_supported) { + s->ecap |= VTD_ECAP_DT; + } + vtd_reset_context_cache(s); vtd_reset_iotlb(s); diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 11abfa2233..356f188b73 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -183,6 +183,7 @@ /* (offset >> 4) << 8 */ #define VTD_ECAP_IRO (DMAR_IOTLB_REG_OFFSET << 4) #define VTD_ECAP_QI (1ULL << 1) +#define VTD_ECAP_DT (1ULL << 2) /* Interrupt Remapping support */ #define VTD_ECAP_IR (1ULL << 3) #define VTD_ECAP_EIM (1ULL << 4) @@ -326,6 +327,7 @@ typedef union VTDInvDesc VTDInvDesc; #define VTD_INV_DESC_TYPE 0xf #define VTD_INV_DESC_CC 0x1 /* Context-cache Invalidate Desc */ #define VTD_INV_DESC_IOTLB 0x2 +#define VTD_INV_DESC_DEVICE 0x3 #define VTD_INV_DESC_IEC 0x4 /* Interrupt Entry Cache Invalidate Descriptor */ #define VTD_INV_DESC_WAIT 0x5 /* Invalidation Wait Descriptor */ @@ -361,6 +363,13 @@ typedef union VTDInvDesc VTDInvDesc; #define VTD_INV_DESC_IOTLB_RSVD_LO 0xffffffff0000ff00ULL #define VTD_INV_DESC_IOTLB_RSVD_HI 0xf80ULL +/* Mask for Device IOTLB Invalidate Descriptor */ +#define VTD_INV_DESC_DEVICE_IOTLB_ADDR(val) ((val) & 0xfffffffffffff000ULL) +#define VTD_INV_DESC_DEVICE_IOTLB_SIZE(val) ((val) & 0x1) +#define VTD_INV_DESC_DEVICE_IOTLB_SID(val) (((val) >> 32) & 0xFFFFULL) +#define VTD_INV_DESC_DEVICE_IOTLB_RSVD_HI 0xffeULL +#define VTD_INV_DESC_DEVICE_IOTLB_RSVD_LO 0xffff0000ffe0fff8 + /* Information about page-selective IOTLB invalidate */ struct VTDIOTLBPageInvInfo { uint16_t domain_id; @@ -399,8 +408,8 @@ typedef struct VTDRootEntry VTDRootEntry; #define VTD_CONTEXT_ENTRY_FPD (1ULL << 1) /* Fault Processing Disable */ #define VTD_CONTEXT_ENTRY_TT (3ULL << 2) /* Translation Type */ #define VTD_CONTEXT_TT_MULTI_LEVEL 0 -#define VTD_CONTEXT_TT_DEV_IOTLB 1 -#define VTD_CONTEXT_TT_PASS_THROUGH 2 +#define VTD_CONTEXT_TT_DEV_IOTLB (1ULL << 2) +#define VTD_CONTEXT_TT_PASS_THROUGH (2ULL << 2) /* Second Level Page Translation Pointer*/ #define VTD_CONTEXT_ENTRY_SLPTPTR (~0xfffULL) #define VTD_CONTEXT_ENTRY_RSVD_LO (0xff0ULL | ~VTD_HAW_MASK) diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c index 2278af7c32..23dcd3f039 100644 --- a/hw/i386/x86-iommu.c +++ b/hw/i386/x86-iommu.c @@ -106,6 +106,18 @@ static void x86_iommu_intremap_prop_set(Object *o, bool value, Error **errp) s->intr_supported = value; } +static bool x86_iommu_device_iotlb_prop_get(Object *o, Error **errp) +{ + X86IOMMUState *s = X86_IOMMU_DEVICE(o); + return s->dt_supported; +} + +static void x86_iommu_device_iotlb_prop_set(Object *o, bool value, Error **errp) +{ + X86IOMMUState *s = X86_IOMMU_DEVICE(o); + s->dt_supported = value; +} + static void x86_iommu_instance_init(Object *o) { X86IOMMUState *s = X86_IOMMU_DEVICE(o); @@ -114,6 +126,11 @@ static void x86_iommu_instance_init(Object *o) s->intr_supported = false; object_property_add_bool(o, "intremap", x86_iommu_intremap_prop_get, x86_iommu_intremap_prop_set, NULL); + s->dt_supported = false; + object_property_add_bool(o, "device-iotlb", + x86_iommu_device_iotlb_prop_get, + x86_iommu_device_iotlb_prop_set, + NULL); } static const TypeInfo x86_iommu_info = { diff --git a/hw/input/lm832x.c b/hw/input/lm832x.c index 539682cac8..2340523da0 100644 --- a/hw/input/lm832x.c +++ b/hw/input/lm832x.c @@ -383,7 +383,7 @@ static void lm_kbd_write(LM823KbdState *s, int reg, int byte, uint8_t value) } } -static void lm_i2c_event(I2CSlave *i2c, enum i2c_event event) +static int lm_i2c_event(I2CSlave *i2c, enum i2c_event event) { LM823KbdState *s = LM8323(i2c); @@ -397,6 +397,8 @@ static void lm_i2c_event(I2CSlave *i2c, enum i2c_event event) default: break; } + + return 0; } static int lm_i2c_rx(I2CSlave *i2c) diff --git a/hw/misc/tmp105.c b/hw/misc/tmp105.c index f5c2472b5b..04e83787d4 100644 --- a/hw/misc/tmp105.c +++ b/hw/misc/tmp105.c @@ -176,7 +176,7 @@ static int tmp105_tx(I2CSlave *i2c, uint8_t data) return 0; } -static void tmp105_event(I2CSlave *i2c, enum i2c_event event) +static int tmp105_event(I2CSlave *i2c, enum i2c_event event) { TMP105State *s = TMP105(i2c); @@ -185,6 +185,7 @@ static void tmp105_event(I2CSlave *i2c, enum i2c_event event) } s->len = 0; + return 0; } static int tmp105_post_load(void *opaque, int version_id) diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index 4994e1ca00..77a4b3e5bf 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -472,7 +472,8 @@ static void e1000e_pci_realize(PCIDevice *pci_dev, Error **errp) hw_error("Failed to initialize PM capability"); } - if (pcie_aer_init(pci_dev, e1000e_aer_offset, PCI_ERR_SIZEOF) < 0) { + if (pcie_aer_init(pci_dev, PCI_ERR_VER, e1000e_aer_offset, + PCI_ERR_SIZEOF, NULL) < 0) { hw_error("Failed to initialize AER capability"); } diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c index 54c01275d4..d0f93eebfc 100644 --- a/hw/net/fsl_etsec/rings.c +++ b/hw/net/fsl_etsec/rings.c @@ -358,25 +358,24 @@ void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr) /* Save flags before BD update */ bd_flags = bd.flags; - if (bd_flags & BD_TX_READY) { - process_tx_bd(etsec, &bd); - - /* Write back BD after update */ - write_buffer_descriptor(etsec, bd_addr, &bd); + if (!(bd_flags & BD_TX_READY)) { + break; } + process_tx_bd(etsec, &bd); + /* Write back BD after update */ + write_buffer_descriptor(etsec, bd_addr, &bd); + /* Wrap or next BD */ if (bd_flags & BD_WRAP) { bd_addr = ring_base; } else { bd_addr += sizeof(eTSEC_rxtx_bd); } + } while (TRUE); - } while (bd_addr != ring_base); - - bd_addr = ring_base; - - /* Save the Buffer Descriptor Pointers to current bd */ + /* Save the Buffer Descriptor Pointers to last bd that was not + * succesfully closed */ etsec->regs[TBPTR0 + ring_nbr].value = bd_addr; /* Set transmit halt THLTx */ diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index f05e59c85f..671c7e48c6 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -1205,6 +1205,20 @@ static void rtl8139_reset_rxring(RTL8139State *s, uint32_t bufferSize) s->RxBufAddr = 0; } +static void rtl8139_reset_phy(RTL8139State *s) +{ + s->BasicModeStatus = 0x7809; + s->BasicModeStatus |= 0x0020; /* autonegotiation completed */ + /* preserve link state */ + s->BasicModeStatus |= qemu_get_queue(s->nic)->link_down ? 0 : 0x04; + + s->NWayAdvert = 0x05e1; /* all modes, full duplex */ + s->NWayLPAR = 0x05e1; /* all modes, full duplex */ + s->NWayExpansion = 0x0001; /* autonegotiation supported */ + + s->CSCR = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD; +} + static void rtl8139_reset(DeviceState *d) { RTL8139State *s = RTL8139(d); @@ -1256,25 +1270,14 @@ static void rtl8139_reset(DeviceState *d) s->Config3 = 0x1; /* fast back-to-back compatible */ s->Config5 = 0x0; - s->CSCR = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD; - s->CpCmd = 0x0; /* reset C+ mode */ s->cplus_enabled = 0; - // s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation // s->BasicModeCtrl = 0x2100; // 100Mbps, full duplex s->BasicModeCtrl = 0x1000; // autonegotiation - s->BasicModeStatus = 0x7809; - //s->BasicModeStatus |= 0x0040; /* UTP medium */ - s->BasicModeStatus |= 0x0020; /* autonegotiation completed */ - /* preserve link state */ - s->BasicModeStatus |= qemu_get_queue(s->nic)->link_down ? 0 : 0x04; - - s->NWayAdvert = 0x05e1; /* all modes, full duplex */ - s->NWayLPAR = 0x05e1; /* all modes, full duplex */ - s->NWayExpansion = 0x0001; /* autonegotiation supported */ + rtl8139_reset_phy(s); /* also reset timer and disable timer interrupt */ s->TCTR = 0; @@ -1469,7 +1472,7 @@ static void rtl8139_BasicModeCtrl_write(RTL8139State *s, uint32_t val) DPRINTF("BasicModeCtrl register write(w) val=0x%04x\n", val); /* mask unwritable bits */ - uint32_t mask = 0x4cff; + uint32_t mask = 0xccff; if (1 || !rtl8139_config_writable(s)) { @@ -1479,6 +1482,11 @@ static void rtl8139_BasicModeCtrl_write(RTL8139State *s, uint32_t val) mask |= 0x0100; } + if (val & 0x8000) { + /* Reset PHY */ + rtl8139_reset_phy(s); + } + val = SET_MASKED(val, mask, s->BasicModeCtrl); s->BasicModeCtrl = val; diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index f2d49ad7e7..6280422d02 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -51,6 +51,7 @@ static const int kernel_feature_bits[] = { VIRTIO_RING_F_EVENT_IDX, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_F_VERSION_1, + VIRTIO_NET_F_MTU, VHOST_INVALID_FEATURE_BIT }; @@ -74,6 +75,7 @@ static const int user_feature_bits[] = { VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MRG_RXBUF, + VIRTIO_NET_F_MTU, /* This bit implies RARP isn't sent by QEMU out of band */ VIRTIO_NET_F_GUEST_ANNOUNCE, @@ -435,6 +437,17 @@ int vhost_set_vring_enable(NetClientState *nc, int enable) return 0; } +int vhost_net_set_mtu(struct vhost_net *net, uint16_t mtu) +{ + const VhostOps *vhost_ops = net->dev.vhost_ops; + + if (!vhost_ops->vhost_net_set_mtu) { + return 0; + } + + return vhost_ops->vhost_net_set_mtu(&net->dev, mtu); +} + #else uint64_t vhost_net_get_max_queues(VHostNetState *net) { @@ -501,4 +514,9 @@ int vhost_set_vring_enable(NetClientState *nc, int enable) { return 0; } + +int vhost_net_set_mtu(struct vhost_net *net, uint16_t mtu) +{ + return 0; +} #endif diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 5009533cfa..7b3ad4a9f0 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -55,6 +55,8 @@ static VirtIOFeature feature_sizes[] = { .end = endof(struct virtio_net_config, status)}, {.flags = 1 << VIRTIO_NET_F_MQ, .end = endof(struct virtio_net_config, max_virtqueue_pairs)}, + {.flags = 1 << VIRTIO_NET_F_MTU, + .end = endof(struct virtio_net_config, mtu)}, {} }; @@ -81,6 +83,7 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) virtio_stw_p(vdev, &netcfg.status, n->status); virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues); + virtio_stw_p(vdev, &netcfg.mtu, n->net_conf.mtu); memcpy(netcfg.mac, n->mac, ETH_ALEN); memcpy(config, &netcfg, n->config_size); } @@ -152,6 +155,16 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) qemu_net_queue_purge(qnc->incoming_queue, qnc->peer); } + if (virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_MTU)) { + r = vhost_net_set_mtu(get_vhost_net(nc->peer), n->net_conf.mtu); + if (r < 0) { + error_report("%uBytes MTU not supported by the backend", + n->net_conf.mtu); + + return; + } + } + n->vhost_started = 1; r = vhost_net_start(vdev, n->nic->ncs, queues); if (r < 0) { @@ -218,6 +231,14 @@ static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status) } } +static void virtio_net_drop_tx_queue_data(VirtIODevice *vdev, VirtQueue *vq) +{ + unsigned int dropped = virtqueue_drop_all(vq); + if (dropped) { + virtio_notify(vdev, vq); + } +} + static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) { VirtIONet *n = VIRTIO_NET(vdev); @@ -262,6 +283,14 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) } else { qemu_bh_cancel(q->tx_bh); } + if ((n->status & VIRTIO_NET_S_LINK_UP) == 0 && + (queue_status & VIRTIO_CONFIG_S_DRIVER_OK)) { + /* if tx is waiting we are likely have some packets in tx queue + * and disabled notification */ + q->tx_waiting = 0; + virtio_queue_set_notification(q->tx_vq, 1); + virtio_net_drop_tx_queue_data(vdev, q->tx_vq); + } } } } @@ -1323,6 +1352,11 @@ static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) VirtIONet *n = VIRTIO_NET(vdev); VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))]; + if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) { + virtio_net_drop_tx_queue_data(vdev, vq); + return; + } + /* This happens when device was stopped but VCPU wasn't. */ if (!vdev->vm_running) { q->tx_waiting = 1; @@ -1349,6 +1383,11 @@ static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) VirtIONet *n = VIRTIO_NET(vdev); VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))]; + if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) { + virtio_net_drop_tx_queue_data(vdev, vq); + return; + } + if (unlikely(q->tx_waiting)) { return; } @@ -1695,6 +1734,7 @@ static void virtio_net_set_config_size(VirtIONet *n, uint64_t host_features) { int i, config_size = 0; virtio_add_feature(&host_features, VIRTIO_NET_F_MAC); + for (i = 0; feature_sizes[i].flags != 0; i++) { if (host_features & feature_sizes[i].flags) { config_size = MAX(feature_sizes[i].end, config_size); @@ -1724,6 +1764,10 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) NetClientState *nc; int i; + if (n->net_conf.mtu) { + n->host_features |= (0x1 << VIRTIO_NET_F_MTU); + } + virtio_net_set_config_size(n, n->host_features); virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size); @@ -1922,6 +1966,7 @@ static Property virtio_net_properties[] = { DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx), DEFINE_PROP_UINT16("rx_queue_size", VirtIONet, net_conf.rx_queue_size, VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE), + DEFINE_PROP_UINT16("host_mtu", VirtIONet, net_conf.mtu, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c index c8b5ac4207..84b7946c31 100644 --- a/hw/pci-bridge/ioh3420.c +++ b/hw/pci-bridge/ioh3420.c @@ -135,8 +135,10 @@ static int ioh3420_initfn(PCIDevice *d) goto err_pcie_cap; } - rc = pcie_aer_init(d, IOH_EP_AER_OFFSET, PCI_ERR_SIZEOF); + rc = pcie_aer_init(d, PCI_ERR_VER, IOH_EP_AER_OFFSET, + PCI_ERR_SIZEOF, &err); if (rc < 0) { + error_report_err(err); goto err; } pcie_aer_root_init(d); diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c index cef6e1325e..04b8e5b847 100644 --- a/hw/pci-bridge/xio3130_downstream.c +++ b/hw/pci-bridge/xio3130_downstream.c @@ -97,8 +97,10 @@ static int xio3130_downstream_initfn(PCIDevice *d) goto err_pcie_cap; } - rc = pcie_aer_init(d, XIO3130_AER_OFFSET, PCI_ERR_SIZEOF); + rc = pcie_aer_init(d, PCI_ERR_VER, XIO3130_AER_OFFSET, + PCI_ERR_SIZEOF, &err); if (rc < 0) { + error_report_err(err); goto err; } diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c index 4ad0440aa1..d1f59c8834 100644 --- a/hw/pci-bridge/xio3130_upstream.c +++ b/hw/pci-bridge/xio3130_upstream.c @@ -85,8 +85,10 @@ static int xio3130_upstream_initfn(PCIDevice *d) pcie_cap_flr_init(d); pcie_cap_deverr_init(d); - rc = pcie_aer_init(d, XIO3130_AER_OFFSET, PCI_ERR_SIZEOF); + rc = pcie_aer_init(d, PCI_ERR_VER, XIO3130_AER_OFFSET, + PCI_ERR_SIZEOF, &err); if (rc < 0) { + error_report_err(err); goto err; } diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 24fae1689d..637d54549e 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -982,8 +982,8 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, pci_get_function_0(pci_dev)) { error_setg(errp, "PCI: slot %d function 0 already ocuppied by %s," " new func %s cannot be exposed to guest.", - PCI_SLOT(devfn), - bus->devices[PCI_DEVFN(PCI_SLOT(devfn), 0)]->name, + PCI_SLOT(pci_get_function_0(pci_dev)->devfn), + pci_get_function_0(pci_dev)->name, name); return NULL; diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 99cfb4561b..adeda04036 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -717,3 +717,18 @@ void pcie_dev_ser_num_init(PCIDevice *dev, uint16_t offset, uint64_t ser_num) PCI_EXT_CAP_DSN_SIZEOF); pci_set_quad(dev->config + offset + pci_dsn_cap, ser_num); } + +void pcie_ats_init(PCIDevice *dev, uint16_t offset) +{ + pcie_add_capability(dev, PCI_EXT_CAP_ID_ATS, 0x1, + offset, PCI_EXT_CAP_ATS_SIZEOF); + + dev->exp.ats_cap = offset; + + /* Invalidate Queue Depth 0, Page Aligned Request 0 */ + pci_set_word(dev->config + offset + PCI_ATS_CAP, 0); + /* STU 0, Disabled by default */ + pci_set_word(dev->config + offset + PCI_ATS_CTRL, 0); + + pci_set_word(dev->wmask + dev->exp.ats_cap + PCI_ATS_CTRL, 0x800f); +} diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c index 048ce6a424..daf1f65427 100644 --- a/hw/pci/pcie_aer.c +++ b/hw/pci/pcie_aer.c @@ -29,6 +29,7 @@ #include "hw/pci/msi.h" #include "hw/pci/pci_bus.h" #include "hw/pci/pcie_regs.h" +#include "qapi/error.h" //#define DEBUG_PCIE #ifdef DEBUG_PCIE @@ -96,21 +97,17 @@ static void aer_log_clear_all_err(PCIEAERLog *aer_log) aer_log->log_num = 0; } -int pcie_aer_init(PCIDevice *dev, uint16_t offset, uint16_t size) +int pcie_aer_init(PCIDevice *dev, uint8_t cap_ver, uint16_t offset, + uint16_t size, Error **errp) { - PCIExpressDevice *exp; - - pcie_add_capability(dev, PCI_EXT_CAP_ID_ERR, PCI_ERR_VER, + pcie_add_capability(dev, PCI_EXT_CAP_ID_ERR, cap_ver, offset, size); - exp = &dev->exp; - exp->aer_cap = offset; + dev->exp.aer_cap = offset; - /* log_max is property */ - if (dev->exp.aer_log.log_max == PCIE_AER_LOG_MAX_UNSET) { - dev->exp.aer_log.log_max = PCIE_AER_LOG_MAX_DEFAULT; - } - /* clip down the value to avoid unreasobale memory usage */ + /* clip down the value to avoid unreasonable memory usage */ if (dev->exp.aer_log.log_max > PCIE_AER_LOG_MAX_LIMIT) { + error_setg(errp, "Invalid aer_log_max %d. The max number of aer log " + "is %d", dev->exp.aer_log.log_max, PCIE_AER_LOG_MAX_LIMIT); return -EINVAL; } dev->exp.aer_log.log = g_malloc0(sizeof dev->exp.aer_log.log[0] * diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index f5c1d98192..07650683f7 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1098,7 +1098,7 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, * We do not support individual masking for channel devices, so we * need to manually trigger any guest masking callbacks here. */ - if (k->guest_notifier_mask) { + if (k->guest_notifier_mask && vdev->use_guest_notifier_mask) { k->guest_notifier_mask(vdev, n, false); } /* get lost events and re-inject */ @@ -1107,7 +1107,7 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, event_notifier_set(notifier); } } else { - if (k->guest_notifier_mask) { + if (k->guest_notifier_mask && vdev->use_guest_notifier_mask) { k->guest_notifier_mask(vdev, n, true); } if (with_irqfd) { diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 204e14f237..ce19efffc8 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -198,12 +198,14 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) SCSIBus *bus = sreq->bus; VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + VirtIODevice *vdev = VIRTIO_DEVICE(s); VirtIOSCSIReq *req; uint32_t n; qemu_get_be32s(f, &n); assert(n < vs->conf.num_queues); - req = qemu_get_virtqueue_element(f, sizeof(VirtIOSCSIReq) + vs->cdb_size); + req = qemu_get_virtqueue_element(vdev, f, + sizeof(VirtIOSCSIReq) + vs->cdb_size); virtio_scsi_init_req(s, vs->cmd_vqs[n], req); if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size, diff --git a/hw/ssi/imx_spi.c b/hw/ssi/imx_spi.c index e4e395fa67..b66505ca49 100644 --- a/hw/ssi/imx_spi.c +++ b/hw/ssi/imx_spi.c @@ -320,9 +320,6 @@ static void imx_spi_write(void *opaque, hwaddr offset, uint64_t value, TYPE_IMX_SPI, __func__); break; case ECSPI_TXDATA: - case ECSPI_MSGDATA: - /* Is there any difference between TXDATA and MSGDATA ? */ - /* I'll have to look in the linux driver */ if (!imx_spi_is_enabled(s)) { /* Ignore writes if device is disabled */ break; @@ -380,6 +377,14 @@ static void imx_spi_write(void *opaque, hwaddr offset, uint64_t value, } break; + case ECSPI_MSGDATA: + /* it is not clear from the spec what MSGDATA is for */ + /* Anyway it is not used by Linux driver */ + /* So for now we just ignore it */ + qemu_log_mask(LOG_UNIMP, + "[%s]%s: Trying to write to MSGDATA, ignoring\n", + TYPE_IMX_SPI, __func__); + break; default: s->regs[index] = value; diff --git a/hw/timer/ds1338.c b/hw/timer/ds1338.c index f5d04dd5d7..3849b74a68 100644 --- a/hw/timer/ds1338.c +++ b/hw/timer/ds1338.c @@ -94,7 +94,7 @@ static void inc_regptr(DS1338State *s) } } -static void ds1338_event(I2CSlave *i2c, enum i2c_event event) +static int ds1338_event(I2CSlave *i2c, enum i2c_event event) { DS1338State *s = DS1338(i2c); @@ -113,6 +113,8 @@ static void ds1338_event(I2CSlave *i2c, enum i2c_event event) default: break; } + + return 0; } static int ds1338_recv(I2CSlave *i2c) diff --git a/hw/timer/twl92230.c b/hw/timer/twl92230.c index 7ba4e9a7c9..b8d914e49b 100644 --- a/hw/timer/twl92230.c +++ b/hw/timer/twl92230.c @@ -713,12 +713,14 @@ static void menelaus_write(void *opaque, uint8_t addr, uint8_t value) } } -static void menelaus_event(I2CSlave *i2c, enum i2c_event event) +static int menelaus_event(I2CSlave *i2c, enum i2c_event event) { MenelausState *s = TWL92230(i2c); if (event == I2C_START_SEND) s->firstbyte = 1; + + return 0; } static int menelaus_tx(I2CSlave *i2c, uint8_t data) diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 7b6f55e70e..6926eedd3f 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -15,6 +15,8 @@ virtio_rng_pushed(void *rng, size_t len) "rng %p: %zd bytes pushed" virtio_rng_request(void *rng, size_t size, unsigned quota) "rng %p: %zd bytes requested, %u bytes quota left" # hw/virtio/virtio-balloon.c +# +virtio_balloon_bad_addr(uint64_t gpa) "%"PRIx64 virtio_balloon_handle_output(const char *name, uint64_t gpa) "section name: %s gpa: %"PRIx64 virtio_balloon_get_config(uint32_t num_pages, uint32_t actual) "num_pages: %d actual: %d" virtio_balloon_set_config(uint32_t actual, uint32_t oldactual) "actual: %d oldactual: %d" diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 7ee92b32c5..9334a8ae22 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -32,6 +32,7 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1, VHOST_USER_PROTOCOL_F_RARP = 2, VHOST_USER_PROTOCOL_F_REPLY_ACK = 3, + VHOST_USER_PROTOCOL_F_NET_MTU = 4, VHOST_USER_PROTOCOL_F_MAX }; @@ -59,6 +60,7 @@ typedef enum VhostUserRequest { VHOST_USER_GET_QUEUE_NUM = 17, VHOST_USER_SET_VRING_ENABLE = 18, VHOST_USER_SEND_RARP = 19, + VHOST_USER_NET_SET_MTU = 20, VHOST_USER_MAX } VhostUserRequest; @@ -186,6 +188,7 @@ static bool vhost_user_one_time_request(VhostUserRequest request) case VHOST_USER_RESET_OWNER: case VHOST_USER_SET_MEM_TABLE: case VHOST_USER_GET_QUEUE_NUM: + case VHOST_USER_NET_SET_MTU: return true; default: return false; @@ -685,6 +688,36 @@ static bool vhost_user_can_merge(struct vhost_dev *dev, return mfd == rfd; } +static int vhost_user_net_set_mtu(struct vhost_dev *dev, uint16_t mtu) +{ + VhostUserMsg msg; + bool reply_supported = virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_REPLY_ACK); + + if (!(dev->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_NET_MTU))) { + return 0; + } + + msg.request = VHOST_USER_NET_SET_MTU; + msg.payload.u64 = mtu; + msg.size = sizeof(msg.payload.u64); + msg.flags = VHOST_USER_VERSION; + if (reply_supported) { + msg.flags |= VHOST_USER_NEED_REPLY_MASK; + } + + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { + return -1; + } + + /* If reply_ack supported, slave has to ack specified MTU is valid */ + if (reply_supported) { + return process_message_reply(dev, msg.request); + } + + return 0; +} + const VhostOps user_ops = { .backend_type = VHOST_BACKEND_TYPE_USER, .vhost_backend_init = vhost_user_init, @@ -708,4 +741,5 @@ const VhostOps user_ops = { .vhost_requires_shm_log = vhost_user_requires_shm_log, .vhost_migration_done = vhost_user_migration_done, .vhost_backend_can_merge = vhost_user_can_merge, + .vhost_net_set_mtu = vhost_user_net_set_mtu, }; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index f7f70237db..d396b22531 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -993,6 +993,7 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev, virtio_queue_set_last_avail_idx(vdev, idx, state.num); } virtio_queue_invalidate_signalled_used(vdev, idx); + virtio_queue_update_used_idx(vdev, idx); /* In the cross-endian case, we need to reset the vring endianness to * native as legacy devices expect so by default. diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 884570a57d..a705e0ec55 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -228,8 +228,13 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) /* FIXME: remove get_system_memory(), but how? */ section = memory_region_find(get_system_memory(), pa, 1); - if (!int128_nz(section.size) || !memory_region_is_ram(section.mr)) + if (!int128_nz(section.size) || + !memory_region_is_ram(section.mr) || + memory_region_is_rom(section.mr) || + memory_region_is_romd(section.mr)) { + trace_virtio_balloon_bad_addr(pa); continue; + } trace_virtio_balloon_handle_output(memory_region_name(section.mr), pa); diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index d6c0c72bd2..d31cc00e83 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -28,6 +28,7 @@ #include "hw/qdev.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio.h" +#include "exec/address-spaces.h" /* #define DEBUG_VIRTIO_BUS */ @@ -61,6 +62,13 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) if (klass->device_plugged != NULL) { klass->device_plugged(qbus->parent, errp); } + + if (klass->get_dma_as != NULL && + virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) { + vdev->dma_as = klass->get_dma_as(qbus->parent); + } else { + vdev->dma_as = &address_space_memory; + } } /* Reset the virtio_bus */ diff --git a/hw/virtio/virtio-crypto-pci.c b/hw/virtio/virtio-crypto-pci.c index a1b09064c0..422aca3a98 100644 --- a/hw/virtio/virtio-crypto-pci.c +++ b/hw/virtio/virtio-crypto-pci.c @@ -31,6 +31,11 @@ static void virtio_crypto_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) VirtIOCryptoPCI *vcrypto = VIRTIO_CRYPTO_PCI(vpci_dev); DeviceState *vdev = DEVICE(&vcrypto->vdev); + if (vcrypto->vdev.conf.cryptodev == NULL) { + error_setg(errp, "'cryptodev' parameter expects a valid object"); + return; + } + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); virtio_pci_force_virtio_1(vpci_dev); object_property_set_bool(OBJECT(vdev), true, "realized", errp); @@ -48,7 +53,6 @@ static void virtio_crypto_pci_class_init(ObjectClass *klass, void *data) k->realize = virtio_crypto_pci_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->props = virtio_crypto_pci_properties; - dc->hotpluggable = false; pcidev_k->class_id = PCI_CLASS_OTHERS; } diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index 2f2467e859..296472fc6e 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -337,7 +337,18 @@ static void virtio_crypto_free_request(VirtIOCryptoReq *req) { if (req) { if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) { - g_free(req->u.sym_op_info); + size_t max_len; + CryptoDevBackendSymOpInfo *op_info = req->u.sym_op_info; + + max_len = op_info->iv_len + + op_info->aad_len + + op_info->src_len + + op_info->dst_len + + op_info->digest_result_len; + + /* Zeroize and free request data structure */ + memset(op_info, 0, sizeof(*op_info) + max_len); + g_free(op_info); } g_free(req); } @@ -355,7 +366,7 @@ virtio_crypto_sym_input_data_helper(VirtIODevice *vdev, return; } - len = sym_op_info->dst_len; + len = sym_op_info->src_len; /* Save the cipher result */ s = iov_from_buf(req->in_iov, req->in_num, 0, sym_op_info->dst, len); if (s != len) { @@ -416,7 +427,7 @@ virtio_crypto_sym_op_helper(VirtIODevice *vdev, uint32_t hash_start_src_offset = 0, len_to_hash = 0; uint32_t cipher_start_src_offset = 0, len_to_cipher = 0; - size_t max_len, curr_size = 0; + uint64_t max_len, curr_size = 0; size_t s; /* Plain cipher */ @@ -441,7 +452,7 @@ virtio_crypto_sym_op_helper(VirtIODevice *vdev, return NULL; } - max_len = iv_len + aad_len + src_len + dst_len + hash_result_len; + max_len = (uint64_t)iv_len + aad_len + src_len + dst_len + hash_result_len; if (unlikely(max_len > vcrypto->conf.max_size)) { virtio_error(vdev, "virtio-crypto too big length"); return NULL; @@ -732,7 +743,7 @@ static void virtio_crypto_reset(VirtIODevice *vdev) VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); /* multiqueue is disabled by default */ vcrypto->curr_queues = 1; - if (!vcrypto->cryptodev->ready) { + if (!cryptodev_backend_is_ready(vcrypto->cryptodev)) { vcrypto->status &= ~VIRTIO_CRYPTO_S_HW_READY; } else { vcrypto->status |= VIRTIO_CRYPTO_S_HW_READY; @@ -792,13 +803,14 @@ static void virtio_crypto_device_realize(DeviceState *dev, Error **errp) } vcrypto->ctrl_vq = virtio_add_queue(vdev, 64, virtio_crypto_handle_ctrl); - if (!vcrypto->cryptodev->ready) { + if (!cryptodev_backend_is_ready(vcrypto->cryptodev)) { vcrypto->status &= ~VIRTIO_CRYPTO_S_HW_READY; } else { vcrypto->status |= VIRTIO_CRYPTO_S_HW_READY; } virtio_crypto_init_config(vdev); + cryptodev_backend_set_used(vcrypto->cryptodev, true); } static void virtio_crypto_device_unrealize(DeviceState *dev, Error **errp) @@ -818,6 +830,7 @@ static void virtio_crypto_device_unrealize(DeviceState *dev, Error **errp) g_free(vcrypto->vqs); virtio_cleanup(vdev); + cryptodev_backend_set_used(vcrypto->cryptodev, false); } static const VMStateDescription vmstate_virtio_crypto = { @@ -875,6 +888,20 @@ static void virtio_crypto_class_init(ObjectClass *klass, void *data) vdc->reset = virtio_crypto_reset; } +static void +virtio_crypto_check_cryptodev_is_used(Object *obj, const char *name, + Object *val, Error **errp) +{ + if (cryptodev_backend_is_used(CRYPTODEV_BACKEND(val))) { + char *path = object_get_canonical_path_component(val); + error_setg(errp, + "can't use already used cryptodev backend: %s", path); + g_free(path); + } else { + qdev_prop_allow_set_link_before_realize(obj, name, val, errp); + } +} + static void virtio_crypto_instance_init(Object *obj) { VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(obj); @@ -888,7 +915,7 @@ static void virtio_crypto_instance_init(Object *obj) object_property_add_link(obj, "cryptodev", TYPE_CRYPTODEV_BACKEND, (Object **)&vcrypto->conf.cryptodev, - qdev_prop_allow_set_link_before_realize, + virtio_crypto_check_cryptodev_is_used, OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); } diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 17412cb7b5..60654dc19d 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -402,7 +402,7 @@ static int virtio_mmio_set_guest_notifier(DeviceState *d, int n, bool assign, event_notifier_cleanup(notifier); } - if (vdc->guest_notifier_mask) { + if (vdc->guest_notifier_mask && vdev->use_guest_notifier_mask) { vdc->guest_notifier_mask(vdev, n, !assign); } diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 21c2b9dbfc..854b8f22bf 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1144,6 +1144,14 @@ static int virtio_pci_query_nvectors(DeviceState *d) return proxy->nvectors; } +static AddressSpace *virtio_pci_get_dma_as(DeviceState *d) +{ + VirtIOPCIProxy *proxy = VIRTIO_PCI(d); + PCIDevice *dev = &proxy->pci_dev; + + return pci_get_address_space(dev); +} + static int virtio_pci_add_mem_cap(VirtIOPCIProxy *proxy, struct virtio_pci_cap *cap) { @@ -1601,6 +1609,11 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) } if (legacy) { + if (virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) { + error_setg(errp, "VIRTIO_F_IOMMU_PLATFORM was supported by" + "neither legacy nor transitional device."); + return ; + } /* legacy and transitional */ pci_set_word(config + PCI_SUBSYSTEM_VENDOR_ID, pci_get_word(config + PCI_VENDOR_ID)); @@ -1802,6 +1815,11 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) * PCI Power Management Interface Specification. */ pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3); + + if (proxy->flags & VIRTIO_PCI_FLAG_ATS) { + pcie_ats_init(pci_dev, 256); + } + } else { /* * make future invocations of pci_is_express() return false @@ -1855,6 +1873,8 @@ static Property virtio_pci_properties[] = { VIRTIO_PCI_FLAG_PAGE_PER_VQ_BIT, false), DEFINE_PROP_BOOL("x-ignore-backend-features", VirtIOPCIProxy, ignore_backend_features, false), + DEFINE_PROP_BIT("ats", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_ATS_BIT, false), DEFINE_PROP_END_OF_LIST(), }; @@ -2520,6 +2540,7 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) k->query_nvectors = virtio_pci_query_nvectors; k->ioeventfd_enabled = virtio_pci_ioeventfd_enabled; k->ioeventfd_assign = virtio_pci_ioeventfd_assign; + k->get_dma_as = virtio_pci_get_dma_as; } static const TypeInfo virtio_pci_bus_info = { diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h index 5e078866c4..d00064cc0c 100644 --- a/hw/virtio/virtio-pci.h +++ b/hw/virtio/virtio-pci.h @@ -72,6 +72,7 @@ enum { VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT, VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT, VIRTIO_PCI_FLAG_PAGE_PER_VQ_BIT, + VIRTIO_PCI_FLAG_ATS_BIT, }; /* Need to activate work-arounds for buggy guests at vmstate load. */ @@ -96,6 +97,9 @@ enum { #define VIRTIO_PCI_FLAG_PAGE_PER_VQ \ (1 << VIRTIO_PCI_FLAG_PAGE_PER_VQ_BIT) +/* address space translation service */ +#define VIRTIO_PCI_FLAG_ATS (1 << VIRTIO_PCI_FLAG_ATS_BIT) + typedef struct { MSIMessage msg; int virq; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index d40711a31d..aa4f38f50a 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -23,6 +23,7 @@ #include "hw/virtio/virtio-bus.h" #include "migration/migration.h" #include "hw/virtio/virtio-access.h" +#include "sysemu/dma.h" /* * The alignment to use between consumer and producer parts of vring. @@ -92,7 +93,7 @@ struct VirtQueue uint16_t queue_index; - int inuse; + unsigned int inuse; uint16_t vector; VirtIOHandleOutput handle_output; @@ -121,7 +122,7 @@ void virtio_queue_update_rings(VirtIODevice *vdev, int n) static void vring_desc_read(VirtIODevice *vdev, VRingDesc *desc, hwaddr desc_pa, int i) { - address_space_read(&address_space_memory, desc_pa + i * sizeof(VRingDesc), + address_space_read(vdev->dma_as, desc_pa + i * sizeof(VRingDesc), MEMTXATTRS_UNSPECIFIED, (void *)desc, sizeof(VRingDesc)); virtio_tswap64s(vdev, &desc->addr); virtio_tswap32s(vdev, &desc->len); @@ -163,7 +164,7 @@ static inline void vring_used_write(VirtQueue *vq, VRingUsedElem *uelem, virtio_tswap32s(vq->vdev, &uelem->id); virtio_tswap32s(vq->vdev, &uelem->len); pa = vq->vring.used + offsetof(VRingUsed, ring[i]); - address_space_write(&address_space_memory, pa, MEMTXATTRS_UNSPECIFIED, + address_space_write(vq->vdev->dma_as, pa, MEMTXATTRS_UNSPECIFIED, (void *)uelem, sizeof(VRingUsedElem)); } @@ -249,6 +250,7 @@ int virtio_queue_empty(VirtQueue *vq) static void virtqueue_unmap_sg(VirtQueue *vq, const VirtQueueElement *elem, unsigned int len) { + AddressSpace *dma_as = vq->vdev->dma_as; unsigned int offset; int i; @@ -256,17 +258,18 @@ static void virtqueue_unmap_sg(VirtQueue *vq, const VirtQueueElement *elem, for (i = 0; i < elem->in_num; i++) { size_t size = MIN(len - offset, elem->in_sg[i].iov_len); - cpu_physical_memory_unmap(elem->in_sg[i].iov_base, - elem->in_sg[i].iov_len, - 1, size); + dma_memory_unmap(dma_as, elem->in_sg[i].iov_base, + elem->in_sg[i].iov_len, + DMA_DIRECTION_FROM_DEVICE, size); offset += size; } for (i = 0; i < elem->out_num; i++) - cpu_physical_memory_unmap(elem->out_sg[i].iov_base, - elem->out_sg[i].iov_len, - 0, elem->out_sg[i].iov_len); + dma_memory_unmap(dma_as, elem->out_sg[i].iov_base, + elem->out_sg[i].iov_len, + DMA_DIRECTION_TO_DEVICE, + elem->out_sg[i].iov_len); } /* virtqueue_detach_element: @@ -560,7 +563,10 @@ static bool virtqueue_map_desc(VirtIODevice *vdev, unsigned int *p_num_sg, goto out; } - iov[num_sg].iov_base = cpu_physical_memory_map(pa, &len, is_write); + iov[num_sg].iov_base = dma_memory_map(vdev->dma_as, pa, &len, + is_write ? + DMA_DIRECTION_FROM_DEVICE : + DMA_DIRECTION_TO_DEVICE); if (!iov[num_sg].iov_base) { virtio_error(vdev, "virtio: bogus descriptor or out of resources"); goto out; @@ -597,9 +603,9 @@ static void virtqueue_undo_map_desc(unsigned int out_num, unsigned int in_num, } } -static void virtqueue_map_iovec(struct iovec *sg, hwaddr *addr, - unsigned int *num_sg, unsigned int max_size, - int is_write) +static void virtqueue_map_iovec(VirtIODevice *vdev, struct iovec *sg, + hwaddr *addr, unsigned int *num_sg, + unsigned int max_size, int is_write) { unsigned int i; hwaddr len; @@ -618,7 +624,10 @@ static void virtqueue_map_iovec(struct iovec *sg, hwaddr *addr, for (i = 0; i < *num_sg; i++) { len = sg[i].iov_len; - sg[i].iov_base = cpu_physical_memory_map(addr[i], &len, is_write); + sg[i].iov_base = dma_memory_map(vdev->dma_as, + addr[i], &len, is_write ? + DMA_DIRECTION_FROM_DEVICE : + DMA_DIRECTION_TO_DEVICE); if (!sg[i].iov_base) { error_report("virtio: error trying to map MMIO memory"); exit(1); @@ -630,12 +639,15 @@ static void virtqueue_map_iovec(struct iovec *sg, hwaddr *addr, } } -void virtqueue_map(VirtQueueElement *elem) +void virtqueue_map(VirtIODevice *vdev, VirtQueueElement *elem) { - virtqueue_map_iovec(elem->in_sg, elem->in_addr, &elem->in_num, - VIRTQUEUE_MAX_SIZE, 1); - virtqueue_map_iovec(elem->out_sg, elem->out_addr, &elem->out_num, - VIRTQUEUE_MAX_SIZE, 0); + virtqueue_map_iovec(vdev, elem->in_sg, elem->in_addr, &elem->in_num, + MIN(ARRAY_SIZE(elem->in_sg), ARRAY_SIZE(elem->in_addr)), + 1); + virtqueue_map_iovec(vdev, elem->out_sg, elem->out_addr, &elem->out_num, + MIN(ARRAY_SIZE(elem->out_sg), + ARRAY_SIZE(elem->out_addr)), + 0); } static void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_num) @@ -771,6 +783,44 @@ err_undo_map: return NULL; } +/* virtqueue_drop_all: + * @vq: The #VirtQueue + * Drops all queued buffers and indicates them to the guest + * as if they are done. Useful when buffers can not be + * processed but must be returned to the guest. + */ +unsigned int virtqueue_drop_all(VirtQueue *vq) +{ + unsigned int dropped = 0; + VirtQueueElement elem = {}; + VirtIODevice *vdev = vq->vdev; + bool fEventIdx = virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX); + + if (unlikely(vdev->broken)) { + return 0; + } + + while (!virtio_queue_empty(vq) && vq->inuse < vq->vring.num) { + /* works similar to virtqueue_pop but does not map buffers + * and does not allocate any memory */ + smp_rmb(); + if (!virtqueue_get_head(vq, vq->last_avail_idx, &elem.index)) { + break; + } + vq->inuse++; + vq->last_avail_idx++; + if (fEventIdx) { + vring_set_avail_event(vq, vq->last_avail_idx); + } + /* immediately push the element, nothing to unmap + * as both in_num and out_num are set to 0 */ + virtqueue_push(vq, &elem, 0); + dropped++; + } + + return dropped; +} + /* Reading and writing a structure directly to QEMUFile is *awful*, but * it is what QEMU has always done by mistake. We can change it sooner * or later by bumping the version number of the affected vm states. @@ -788,7 +838,7 @@ typedef struct VirtQueueElementOld { struct iovec out_sg[VIRTQUEUE_MAX_SIZE]; } VirtQueueElementOld; -void *qemu_get_virtqueue_element(QEMUFile *f, size_t sz) +void *qemu_get_virtqueue_element(VirtIODevice *vdev, QEMUFile *f, size_t sz) { VirtQueueElement *elem; VirtQueueElementOld data; @@ -819,7 +869,7 @@ void *qemu_get_virtqueue_element(QEMUFile *f, size_t sz) elem->out_sg[i].iov_len = data.out_sg[i].iov_len; } - virtqueue_map(elem); + virtqueue_map(vdev, elem); return elem; } @@ -878,6 +928,11 @@ static int virtio_validate_features(VirtIODevice *vdev) { VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + if (virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM) && + !virtio_vdev_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) { + return -EFAULT; + } + if (k->validate_features) { return k->validate_features(vdev); } else { @@ -1861,9 +1916,11 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) /* * Some devices migrate VirtQueueElements that have been popped * from the avail ring but not yet returned to the used ring. + * Since max ring size < UINT16_MAX it's safe to use modulo + * UINT16_MAX + 1 subtraction. */ - vdev->vq[i].inuse = vdev->vq[i].last_avail_idx - - vdev->vq[i].used_idx; + vdev->vq[i].inuse = (uint16_t)(vdev->vq[i].last_avail_idx - + vdev->vq[i].used_idx); if (vdev->vq[i].inuse > vdev->vq[i].vring.num) { error_report("VQ %d size 0x%x < last_avail_idx 0x%x - " "used_idx 0x%x", @@ -2001,6 +2058,11 @@ void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx) vdev->vq[n].shadow_avail_idx = idx; } +void virtio_queue_update_used_idx(VirtIODevice *vdev, int n) +{ + vdev->vq[n].used_idx = vring_used_idx(&vdev->vq[n]); +} + void virtio_queue_invalidate_signalled_used(VirtIODevice *vdev, int n) { vdev->vq[n].signalled_used_valid = false; |