diff options
Diffstat (limited to 'hw')
36 files changed, 3183 insertions, 293 deletions
diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c index 9aa6725..5861a5b 100644 --- a/hw/9pfs/virtio-9p.c +++ b/hw/9pfs/virtio-9p.c @@ -299,9 +299,7 @@ static int v9fs_xattr_fid_clunk(V9fsPDU *pdu, V9fsFidState *fidp) free_out: v9fs_string_free(&fidp->fs.xattr.name); free_value: - if (fidp->fs.xattr.value) { - g_free(fidp->fs.xattr.value); - } + g_free(fidp->fs.xattr.value); return retval; } diff --git a/hw/Makefile.objs b/hw/Makefile.objs index d178b65..52a1464 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -29,6 +29,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += usb/ devices-dirs-$(CONFIG_VIRTIO) += virtio/ devices-dirs-$(CONFIG_SOFTMMU) += watchdog/ devices-dirs-$(CONFIG_SOFTMMU) += xen/ +devices-dirs-$(CONFIG_MEM_HOTPLUG) += mem/ devices-dirs-y += core/ common-obj-y += $(devices-dirs-y) obj-y += $(devices-dirs-y) diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs index 397d32b..acd2389 100644 --- a/hw/acpi/Makefile.objs +++ b/hw/acpi/Makefile.objs @@ -1 +1,3 @@ common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o pcihp.o cpu_hotplug.o +common-obj-$(CONFIG_ACPI) += memory_hotplug.o +common-obj-$(CONFIG_ACPI) += acpi_interface.o diff --git a/hw/acpi/acpi_interface.c b/hw/acpi/acpi_interface.c new file mode 100644 index 0000000..c181bb2 --- /dev/null +++ b/hw/acpi/acpi_interface.c @@ -0,0 +1,15 @@ +#include "hw/acpi/acpi_dev_interface.h" +#include "qemu/module.h" + +static void register_types(void) +{ + static const TypeInfo acpi_dev_if_info = { + .name = TYPE_ACPI_DEVICE_IF, + .parent = TYPE_INTERFACE, + .class_size = sizeof(AcpiDeviceIfClass), + }; + + type_register_static(&acpi_dev_if_info); +} + +type_init(register_types) diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 407ae89..e7d6c77 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -34,6 +34,7 @@ #include "exec/address-spaces.h" #include "hw/i386/ich9.h" +#include "hw/mem/pc-dimm.h" //#define DEBUG @@ -139,6 +140,23 @@ static int ich9_pm_post_load(void *opaque, int version_id) .offset = vmstate_offset_pointer(_state, _field, uint8_t), \ } +static bool vmstate_test_use_memhp(void *opaque) +{ + ICH9LPCPMRegs *s = opaque; + return s->acpi_memory_hotplug.is_enabled; +} + +static const VMStateDescription vmstate_memhp_state = { + .name = "ich9_pm/memhp", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, ICH9LPCPMRegs), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_ich9_pm = { .name = "ich9_pm", .version_id = 1, @@ -155,6 +173,13 @@ const VMStateDescription vmstate_ich9_pm = { VMSTATE_UINT32(smi_en, ICH9LPCPMRegs), VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &vmstate_memhp_state, + .needed = vmstate_test_use_memhp, + }, + VMSTATE_END_OF_LIST() } }; @@ -223,6 +248,11 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, &pm->gpe_cpu, ICH9_CPU_HOTPLUG_IO_BASE); pm->cpu_added_notifier.notify = ich9_cpu_added_req; qemu_register_cpu_added_notifier(&pm->cpu_added_notifier); + + if (pm->acpi_memory_hotplug.is_enabled) { + acpi_memory_hotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), + &pm->acpi_memory_hotplug); + } } static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v, @@ -235,9 +265,25 @@ static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v, visit_type_uint32(v, &value, name, errp); } +static bool ich9_pm_get_memory_hotplug_support(Object *obj, Error **errp) +{ + ICH9LPCState *s = ICH9_LPC_DEVICE(obj); + + return s->pm.acpi_memory_hotplug.is_enabled; +} + +static void ich9_pm_set_memory_hotplug_support(Object *obj, bool value, + Error **errp) +{ + ICH9LPCState *s = ICH9_LPC_DEVICE(obj); + + s->pm.acpi_memory_hotplug.is_enabled = value; +} + void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) { static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN; + pm->acpi_memory_hotplug.is_enabled = true; object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE, &pm->pm_io_base, errp); @@ -246,4 +292,27 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) NULL, NULL, pm, NULL); object_property_add_uint32_ptr(obj, ACPI_PM_PROP_GPE0_BLK_LEN, &gpe0_len, errp); + object_property_add_bool(obj, "memory-hotplug-support", + ich9_pm_get_memory_hotplug_support, + ich9_pm_set_memory_hotplug_support, + NULL); +} + +void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp) +{ + if (pm->acpi_memory_hotplug.is_enabled && + object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + acpi_memory_plug_cb(&pm->acpi_regs, pm->irq, &pm->acpi_memory_hotplug, + dev, errp); + } else { + error_setg(errp, "acpi: device plug request for not supported device" + " type: %s", object_get_typename(OBJECT(dev))); + } +} + +void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list) +{ + ICH9LPCState *s = ICH9_LPC_DEVICE(adev); + + acpi_memory_ospm_status(&s->pm.acpi_memory_hotplug, list); } diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c new file mode 100644 index 0000000..de4ddc2 --- /dev/null +++ b/hw/acpi/memory_hotplug.c @@ -0,0 +1,245 @@ +#include "hw/acpi/memory_hotplug.h" +#include "hw/acpi/pc-hotplug.h" +#include "hw/mem/pc-dimm.h" +#include "hw/boards.h" +#include "trace.h" +#include "qapi-visit.h" +#include "monitor/monitor.h" +#include "qapi/dealloc-visitor.h" +#include "qapi/qmp-output-visitor.h" + +static ACPIOSTInfo *acpi_memory_device_status(int slot, MemStatus *mdev) +{ + ACPIOSTInfo *info = g_new0(ACPIOSTInfo, 1); + + info->slot_type = ACPI_SLOT_TYPE_DIMM; + info->slot = g_strdup_printf("%d", slot); + info->source = mdev->ost_event; + info->status = mdev->ost_status; + if (mdev->dimm) { + DeviceState *dev = DEVICE(mdev->dimm); + if (dev->id) { + info->device = g_strdup(dev->id); + info->has_device = true; + } + } + return info; +} + +void acpi_memory_ospm_status(MemHotplugState *mem_st, ACPIOSTInfoList ***list) +{ + int i; + + for (i = 0; i < mem_st->dev_count; i++) { + ACPIOSTInfoList *elem = g_new0(ACPIOSTInfoList, 1); + elem->value = acpi_memory_device_status(i, &mem_st->devs[i]); + elem->next = NULL; + **list = elem; + *list = &elem->next; + } +} + +static void acpi_memory_ost_mon_event(const MemHotplugState *mem_st) +{ + Visitor *v; + QObject *out_info; + QapiDeallocVisitor *md; + QmpOutputVisitor *mo = qmp_output_visitor_new(); + MemStatus *mdev = &mem_st->devs[mem_st->selector]; + ACPIOSTInfo *info = acpi_memory_device_status(mem_st->selector, mdev); + + v = qmp_output_get_visitor(mo); + visit_type_ACPIOSTInfo(v, &info, "unused", NULL); + + out_info = qmp_output_get_qobject(mo); + monitor_protocol_event(QEVENT_ACPI_OST, out_info); + qobject_decref(out_info); + + qmp_output_visitor_cleanup(mo); + md = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(md); + visit_type_ACPIOSTInfo(v, &info, "unused", NULL); + qapi_dealloc_visitor_cleanup(md); +} + +static uint64_t acpi_memory_hotplug_read(void *opaque, hwaddr addr, + unsigned int size) +{ + uint32_t val = 0; + MemHotplugState *mem_st = opaque; + MemStatus *mdev; + Object *o; + + if (mem_st->selector >= mem_st->dev_count) { + trace_mhp_acpi_invalid_slot_selected(mem_st->selector); + return 0; + } + + mdev = &mem_st->devs[mem_st->selector]; + o = OBJECT(mdev->dimm); + switch (addr) { + case 0x0: /* Lo part of phys address where DIMM is mapped */ + val = o ? object_property_get_int(o, PC_DIMM_ADDR_PROP, NULL) : 0; + trace_mhp_acpi_read_addr_lo(mem_st->selector, val); + break; + case 0x4: /* Hi part of phys address where DIMM is mapped */ + val = o ? object_property_get_int(o, PC_DIMM_ADDR_PROP, NULL) >> 32 : 0; + trace_mhp_acpi_read_addr_hi(mem_st->selector, val); + break; + case 0x8: /* Lo part of DIMM size */ + val = o ? object_property_get_int(o, PC_DIMM_SIZE_PROP, NULL) : 0; + trace_mhp_acpi_read_size_lo(mem_st->selector, val); + break; + case 0xc: /* Hi part of DIMM size */ + val = o ? object_property_get_int(o, PC_DIMM_SIZE_PROP, NULL) >> 32 : 0; + trace_mhp_acpi_read_size_hi(mem_st->selector, val); + break; + case 0x10: /* node proximity for _PXM method */ + val = o ? object_property_get_int(o, PC_DIMM_NODE_PROP, NULL) : 0; + trace_mhp_acpi_read_pxm(mem_st->selector, val); + break; + case 0x14: /* pack and return is_* fields */ + val |= mdev->is_enabled ? 1 : 0; + val |= mdev->is_inserting ? 2 : 0; + trace_mhp_acpi_read_flags(mem_st->selector, val); + break; + default: + val = ~0; + break; + } + return val; +} + +static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + MemHotplugState *mem_st = opaque; + MemStatus *mdev; + + if (!mem_st->dev_count) { + return; + } + + if (addr) { + if (mem_st->selector >= mem_st->dev_count) { + trace_mhp_acpi_invalid_slot_selected(mem_st->selector); + return; + } + } + + switch (addr) { + case 0x0: /* DIMM slot selector */ + mem_st->selector = data; + trace_mhp_acpi_write_slot(mem_st->selector); + break; + case 0x4: /* _OST event */ + mdev = &mem_st->devs[mem_st->selector]; + if (data == 1) { + /* TODO: handle device insert OST event */ + } else if (data == 3) { + /* TODO: handle device remove OST event */ + } + mdev->ost_event = data; + trace_mhp_acpi_write_ost_ev(mem_st->selector, mdev->ost_event); + break; + case 0x8: /* _OST status */ + mdev = &mem_st->devs[mem_st->selector]; + mdev->ost_status = data; + trace_mhp_acpi_write_ost_status(mem_st->selector, mdev->ost_status); + /* TODO: implement memory removal on guest signal */ + acpi_memory_ost_mon_event(mem_st); + break; + case 0x14: + mdev = &mem_st->devs[mem_st->selector]; + if (data & 2) { /* clear insert event */ + mdev->is_inserting = false; + trace_mhp_acpi_clear_insert_evt(mem_st->selector); + } + break; + } + +} +static const MemoryRegionOps acpi_memory_hotplug_ops = { + .read = acpi_memory_hotplug_read, + .write = acpi_memory_hotplug_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner, + MemHotplugState *state) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + + state->dev_count = machine->ram_slots; + if (!state->dev_count) { + return; + } + + state->devs = g_malloc0(sizeof(*state->devs) * state->dev_count); + memory_region_init_io(&state->io, owner, &acpi_memory_hotplug_ops, state, + "apci-mem-hotplug", ACPI_MEMORY_HOTPLUG_IO_LEN); + memory_region_add_subregion(as, ACPI_MEMORY_HOTPLUG_BASE, &state->io); +} + +void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st, + DeviceState *dev, Error **errp) +{ + MemStatus *mdev; + Error *local_err = NULL; + int slot = object_property_get_int(OBJECT(dev), "slot", &local_err); + + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (slot >= mem_st->dev_count) { + char *dev_path = object_get_canonical_path(OBJECT(dev)); + error_setg(errp, "acpi_memory_plug_cb: " + "device [%s] returned invalid memory slot[%d]", + dev_path, slot); + g_free(dev_path); + return; + } + + mdev = &mem_st->devs[slot]; + mdev->dimm = dev; + mdev->is_enabled = true; + mdev->is_inserting = true; + + /* do ACPI magic */ + ar->gpe.sts[0] |= ACPI_MEMORY_HOTPLUG_STATUS; + acpi_update_sci(ar, irq); + return; +} + +static const VMStateDescription vmstate_memhp_sts = { + .name = "memory hotplug device state", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(is_enabled, MemStatus), + VMSTATE_BOOL(is_inserting, MemStatus), + VMSTATE_UINT32(ost_event, MemStatus), + VMSTATE_UINT32(ost_status, MemStatus), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_memory_hotplug = { + .name = "memory hotplug state", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(selector, MemHotplugState), + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(devs, MemHotplugState, dev_count, + vmstate_memhp_sts, MemStatus), + VMSTATE_END_OF_LIST() + } +}; diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 252bbf2..b72b34e 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -33,6 +33,9 @@ #include "hw/acpi/pcihp.h" #include "hw/acpi/cpu_hotplug.h" #include "hw/hotplug.h" +#include "hw/mem/pc-dimm.h" +#include "hw/acpi/memory_hotplug.h" +#include "hw/acpi/acpi_dev_interface.h" //#define DEBUG @@ -81,6 +84,8 @@ typedef struct PIIX4PMState { AcpiCpuHotplug gpe_cpu; Notifier cpu_added_notifier; + + MemHotplugState acpi_memory_hotplug; } PIIX4PMState; #define TYPE_PIIX4_PM "PIIX4_PM" @@ -244,6 +249,23 @@ static bool vmstate_test_no_use_acpi_pci_hotplug(void *opaque, int version_id) return !s->use_acpi_pci_hotplug; } +static bool vmstate_test_use_memhp(void *opaque) +{ + PIIX4PMState *s = opaque; + return s->acpi_memory_hotplug.is_enabled; +} + +static const VMStateDescription vmstate_memhp_state = { + .name = "piix4_pm/memhp", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, PIIX4PMState), + VMSTATE_END_OF_LIST() + } +}; + /* qemu-kvm 1.2 uses version 3 but advertised as 2 * To support incoming qemu-kvm 1.2 migration, change version_id * and minimum_version_id to 2 below (which breaks migration from @@ -275,6 +297,13 @@ static const VMStateDescription vmstate_acpi = { VMSTATE_PCI_HOTPLUG(acpi_pci_hotplug, PIIX4PMState, vmstate_test_use_acpi_pci_hotplug), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &vmstate_memhp_state, + .needed = vmstate_test_use_memhp, + }, + VMSTATE_END_OF_LIST() } }; @@ -308,19 +337,35 @@ static void piix4_pm_powerdown_req(Notifier *n, void *opaque) acpi_pm1_evt_power_down(&s->ar); } -static void piix4_pci_device_plug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void piix4_device_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { PIIX4PMState *s = PIIX4_PM(hotplug_dev); - acpi_pcihp_device_plug_cb(&s->ar, s->irq, &s->acpi_pci_hotplug, dev, errp); + + if (s->acpi_memory_hotplug.is_enabled && + object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + acpi_memory_plug_cb(&s->ar, s->irq, &s->acpi_memory_hotplug, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + acpi_pcihp_device_plug_cb(&s->ar, s->irq, &s->acpi_pci_hotplug, dev, + errp); + } else { + error_setg(errp, "acpi: device plug request for not supported device" + " type: %s", object_get_typename(OBJECT(dev))); + } } -static void piix4_pci_device_unplug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void piix4_device_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { PIIX4PMState *s = PIIX4_PM(hotplug_dev); - acpi_pcihp_device_unplug_cb(&s->ar, s->irq, &s->acpi_pci_hotplug, dev, - errp); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + acpi_pcihp_device_unplug_cb(&s->ar, s->irq, &s->acpi_pci_hotplug, dev, + errp); + } else { + error_setg(errp, "acpi: device unplug request for not supported device" + " type: %s", object_get_typename(OBJECT(dev))); + } } static void piix4_update_bus_hotplug(PCIBus *pci_bus, void *opaque) @@ -439,13 +484,17 @@ Object *piix4_pm_find(void) I2CBus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, qemu_irq sci_irq, qemu_irq smi_irq, - int kvm_enabled, FWCfgState *fw_cfg) + int kvm_enabled, FWCfgState *fw_cfg, + DeviceState **piix4_pm) { DeviceState *dev; PIIX4PMState *s; dev = DEVICE(pci_create(bus, devfn, TYPE_PIIX4_PM)); qdev_prop_set_uint32(dev, "smb_io_base", smb_io_base); + if (piix4_pm) { + *piix4_pm = dev; + } s = PIIX4_PM(dev); s->irq = sci_irq; @@ -518,6 +567,17 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, PIIX4_CPU_HOTPLUG_IO_BASE); s->cpu_added_notifier.notify = piix4_cpu_added_req; qemu_register_cpu_added_notifier(&s->cpu_added_notifier); + + if (s->acpi_memory_hotplug.is_enabled) { + acpi_memory_hotplug_init(parent, OBJECT(s), &s->acpi_memory_hotplug); + } +} + +static void piix4_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list) +{ + PIIX4PMState *s = PIIX4_PM(adev); + + acpi_memory_ospm_status(&s->acpi_memory_hotplug, list); } static Property piix4_pm_properties[] = { @@ -527,6 +587,8 @@ static Property piix4_pm_properties[] = { DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_VAL, PIIX4PMState, s4_val, 2), DEFINE_PROP_BOOL("acpi-pci-hotplug-with-bridge-support", PIIX4PMState, use_acpi_pci_hotplug, true), + DEFINE_PROP_BOOL("memory-hotplug-support", PIIX4PMState, + acpi_memory_hotplug.is_enabled, true), DEFINE_PROP_END_OF_LIST(), }; @@ -535,6 +597,7 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); + AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(klass); k->init = piix4_pm_initfn; k->config_write = pm_write_config; @@ -551,8 +614,9 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data) */ dc->cannot_instantiate_with_device_add_yet = true; dc->hotpluggable = false; - hc->plug = piix4_pci_device_plug_cb; - hc->unplug = piix4_pci_device_unplug_cb; + hc->plug = piix4_device_plug_cb; + hc->unplug = piix4_device_unplug_cb; + adevc->ospm_status = piix4_ospm_status; } static const TypeInfo piix4_pm_info = { @@ -562,6 +626,7 @@ static const TypeInfo piix4_pm_info = { .class_init = piix4_pm_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, + { TYPE_ACPI_DEVICE_IF }, { } } }; diff --git a/hw/core/qdev.c b/hw/core/qdev.c index e65a5aa..b9cd4fc 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -34,6 +34,7 @@ #include "qapi/qmp/qjson.h" #include "monitor/monitor.h" #include "hw/hotplug.h" +#include "hw/boards.h" int qdev_hotplug = 0; static bool qdev_hot_added = false; @@ -567,32 +568,35 @@ static void bus_set_realized(Object *obj, bool value, Error **errp) { BusState *bus = BUS(obj); BusClass *bc = BUS_GET_CLASS(bus); + BusChild *kid; Error *local_err = NULL; if (value && !bus->realized) { if (bc->realize) { bc->realize(bus, &local_err); + } + /* TODO: recursive realization */ + } else if (!value && bus->realized) { + QTAILQ_FOREACH(kid, &bus->children, sibling) { + DeviceState *dev = kid->child; + object_property_set_bool(OBJECT(dev), false, "realized", + &local_err); if (local_err != NULL) { - goto error; + break; } - } - } else if (!value && bus->realized) { - if (bc->unrealize) { + if (bc->unrealize && local_err == NULL) { bc->unrealize(bus, &local_err); - - if (local_err != NULL) { - goto error; - } } } - bus->realized = value; - return; + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } -error: - error_propagate(errp, local_err); + bus->realized = value; } void qbus_create_inplace(void *bus, size_t size, const char *typename, @@ -813,6 +817,18 @@ static void device_set_realized(Object *obj, bool value, Error **errp) local_err == NULL) { hotplug_handler_plug(dev->parent_bus->hotplug_handler, dev, &local_err); + } else if (local_err == NULL && + object_dynamic_cast(qdev_get_machine(), TYPE_MACHINE)) { + HotplugHandler *hotplug_ctrl; + MachineState *machine = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(machine); + + if (mc->get_hotplug_handler) { + hotplug_ctrl = mc->get_hotplug_handler(machine, dev); + if (hotplug_ctrl) { + hotplug_handler_plug(hotplug_ctrl, dev, &local_err); + } + } } if (qdev_get_vmsd(dev) && local_err == NULL) { @@ -865,6 +881,20 @@ static bool device_get_hotpluggable(Object *obj, Error **errp) dev->parent_bus->allow_hotplug); } +static bool device_get_hotplugged(Object *obj, Error **err) +{ + DeviceState *dev = DEVICE(obj); + + return dev->hotplugged; +} + +static void device_set_hotplugged(Object *obj, bool value, Error **err) +{ + DeviceState *dev = DEVICE(obj); + + dev->hotplugged = value; +} + static void device_initfn(Object *obj) { DeviceState *dev = DEVICE(obj); @@ -883,6 +913,9 @@ static void device_initfn(Object *obj) device_get_realized, device_set_realized, NULL); object_property_add_bool(obj, "hotpluggable", device_get_hotpluggable, NULL, NULL); + object_property_add_bool(obj, "hotplugged", + device_get_hotplugged, device_set_hotplugged, + &error_abort); class = object_get_class(OBJECT(dev)); do { diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index f66c349..48014ab 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -9,7 +9,8 @@ obj-y += acpi-build.o obj-y += bios-linker-loader.o hw/i386/acpi-build.o: hw/i386/acpi-build.c hw/i386/acpi-dsdt.hex \ hw/i386/ssdt-proc.hex hw/i386/ssdt-pcihp.hex hw/i386/ssdt-misc.hex \ - hw/i386/acpi-dsdt.hex hw/i386/q35-acpi-dsdt.hex + hw/i386/acpi-dsdt.hex hw/i386/q35-acpi-dsdt.hex \ + hw/i386/q35-acpi-dsdt.hex hw/i386/ssdt-mem.hex iasl-option=$(shell if test -z "`$(1) $(2) 2>&1 > /dev/null`" \ ; then echo "$(2)"; else echo "$(3)"; fi ;) @@ -17,7 +18,7 @@ iasl-option=$(shell if test -z "`$(1) $(2) 2>&1 > /dev/null`" \ ifdef IASL #IASL Present. Generate hex files from .dsl hw/i386/%.hex: $(SRC_PATH)/hw/i386/%.dsl $(SRC_PATH)/scripts/acpi_extract_preprocess.py $(SRC_PATH)/scripts/acpi_extract.py - $(call quiet-command, cpp -P $(QEMU_DGFLAGS) $(QEMU_INCLUDES) $< -o $*.dsl.i.orig, " CPP $(TARGET_DIR)$*.dsl.i.orig") + $(call quiet-command, $(CPP) -x c -P $(QEMU_DGFLAGS) $(QEMU_INCLUDES) $< -o $*.dsl.i.orig, " CPP $(TARGET_DIR)$*.dsl.i.orig") $(call quiet-command, $(PYTHON) $(SRC_PATH)/scripts/acpi_extract_preprocess.py $*.dsl.i.orig > $*.dsl.i, " ACPI_PREPROCESS $(TARGET_DIR)$*.dsl.i") $(call quiet-command, $(IASL) $(call iasl-option,$(IASL),-Pn,) -vs -l -tc -p $* $*.dsl.i $(if $(V), , > /dev/null) 2>&1 ," IASL $(TARGET_DIR)$*.dsl.i") $(call quiet-command, $(PYTHON) $(SRC_PATH)/scripts/acpi_extract.py $*.lst > $*.off, " ACPI_EXTRACT $(TARGET_DIR)$*.off") diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 1e0aa09..ebc5f03 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -37,6 +37,7 @@ #include "bios-linker-loader.h" #include "hw/loader.h" #include "hw/isa/isa.h" +#include "hw/acpi/memory_hotplug.h" /* Supported chipsets: */ #include "hw/acpi/piix4.h" @@ -667,6 +668,14 @@ static inline char acpi_get_hex(uint32_t val) #define ACPI_PCIQXL_SIZEOF (*ssdt_pciqxl_end - *ssdt_pciqxl_start) #define ACPI_PCIQXL_AML (ssdp_pcihp_aml + *ssdt_pciqxl_start) +#include "hw/i386/ssdt-mem.hex" + +/* 0x5B 0x82 DeviceOp PkgLength NameString DimmID */ +#define ACPI_MEM_OFFSET_HEX (*ssdt_mem_name - *ssdt_mem_start + 2) +#define ACPI_MEM_OFFSET_ID (*ssdt_mem_id - *ssdt_mem_start + 7) +#define ACPI_MEM_SIZEOF (*ssdt_mem_end - *ssdt_mem_start) +#define ACPI_MEM_AML (ssdm_mem_aml + *ssdt_mem_start) + #define ACPI_SSDT_SIGNATURE 0x54445353 /* SSDT */ #define ACPI_SSDT_HEADER_LENGTH 36 @@ -1003,6 +1012,8 @@ build_ssdt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu, AcpiPmInfo *pm, AcpiMiscInfo *misc, PcPciInfo *pci, PcGuestInfo *guest_info) { + MachineState *machine = MACHINE(qdev_get_machine()); + uint32_t nr_mem = machine->ram_slots; unsigned acpi_cpus = guest_info->apic_id_limit; int ssdt_start = table_data->len; uint8_t *ssdt_ptr; @@ -1031,6 +1042,9 @@ build_ssdt(GArray *table_data, GArray *linker, ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml), ssdt_isa_pest[0], 16, misc->pvpanic_port); + ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml), + ssdt_mctrl_nr_slots[0], 32, nr_mem); + { GArray *sb_scope = build_alloc_array(); uint8_t op = 0x10; /* ScopeOp */ @@ -1084,6 +1098,27 @@ build_ssdt(GArray *table_data, GArray *linker, build_free_array(package); } + if (nr_mem) { + assert(nr_mem <= ACPI_MAX_RAM_SLOTS); + /* build memory devices */ + for (i = 0; i < nr_mem; i++) { + char id[3]; + uint8_t *mem = acpi_data_push(sb_scope, ACPI_MEM_SIZEOF); + + snprintf(id, sizeof(id), "%02X", i); + memcpy(mem, ACPI_MEM_AML, ACPI_MEM_SIZEOF); + memcpy(mem + ACPI_MEM_OFFSET_HEX, id, 2); + memcpy(mem + ACPI_MEM_OFFSET_ID, id, 2); + } + + /* build Method(MEMORY_SLOT_NOTIFY_METHOD, 2) { + * If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ... + */ + build_append_notify_method(sb_scope, + stringify(MEMORY_SLOT_NOTIFY_METHOD), + "MP%0.02X", nr_mem); + } + { AcpiBuildPciBusHotplugState hotplug_state; Object *pci_host; @@ -1132,15 +1167,22 @@ build_hpet(GArray *table_data, GArray *linker) (void *)hpet, "HPET", sizeof(*hpet), 1); } +typedef enum { + MEM_AFFINITY_NOFLAGS = 0, + MEM_AFFINITY_ENABLED = (1 << 0), + MEM_AFFINITY_HOTPLUGGABLE = (1 << 1), + MEM_AFFINITY_NON_VOLATILE = (1 << 2), +} MemoryAffinityFlags; + static void -acpi_build_srat_memory(AcpiSratMemoryAffinity *numamem, - uint64_t base, uint64_t len, int node, int enabled) +acpi_build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base, + uint64_t len, int node, MemoryAffinityFlags flags) { numamem->type = ACPI_SRAT_MEMORY; numamem->length = sizeof(*numamem); memset(numamem->proximity, 0, 4); numamem->proximity[0] = node; - numamem->flags = cpu_to_le32(!!enabled); + numamem->flags = cpu_to_le32(flags); numamem->base_addr = cpu_to_le64(base); numamem->range_length = cpu_to_le64(len); } @@ -1157,6 +1199,10 @@ build_srat(GArray *table_data, GArray *linker, uint64_t curnode; int srat_start, numa_start, slots; uint64_t mem_len, mem_base, next_base; + PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); + ram_addr_t hotplugabble_address_space_size = + object_property_get_int(OBJECT(pcms), PC_MACHINE_MEMHP_REGION_SIZE, + NULL); srat_start = table_data->len; @@ -1188,7 +1234,7 @@ build_srat(GArray *table_data, GArray *linker, numa_start = table_data->len; numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, 0, 640*1024, 0, 1); + acpi_build_srat_memory(numamem, 0, 640*1024, 0, MEM_AFFINITY_ENABLED); next_base = 1024 * 1024; for (i = 1; i < guest_info->numa_nodes + 1; ++i) { mem_base = next_base; @@ -1204,19 +1250,34 @@ build_srat(GArray *table_data, GArray *linker, mem_len -= next_base - guest_info->ram_size_below_4g; if (mem_len > 0) { numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1); + acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1, + MEM_AFFINITY_ENABLED); } mem_base = 1ULL << 32; mem_len = next_base - guest_info->ram_size_below_4g; next_base += (1ULL << 32) - guest_info->ram_size_below_4g; } numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1, 1); + acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1, + MEM_AFFINITY_ENABLED); } slots = (table_data->len - numa_start) / sizeof *numamem; for (; slots < guest_info->numa_nodes + 2; slots++) { numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, 0, 0, 0, 0); + acpi_build_srat_memory(numamem, 0, 0, 0, MEM_AFFINITY_NOFLAGS); + } + + /* + * Entry is required for Windows to enable memory hotplug in OS. + * Memory devices may override proximity set by this entry, + * providing _PXM method if necessary. + */ + if (hotplugabble_address_space_size) { + numamem = acpi_data_push(table_data, sizeof *numamem); + acpi_build_srat_memory(numamem, pcms->hotplug_memory_base, + hotplugabble_address_space_size, 0, + MEM_AFFINITY_HOTPLUGGABLE | + MEM_AFFINITY_ENABLED); } build_header(linker, table_data, diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl index 0a1e252..3cc0ea0 100644 --- a/hw/i386/acpi-dsdt.dsl +++ b/hw/i386/acpi-dsdt.dsl @@ -306,7 +306,7 @@ DefinitionBlock ( } } -#include "hw/acpi/cpu_hotplug_defs.h" +#include "hw/acpi/pc-hotplug.h" #define CPU_STATUS_BASE PIIX4_CPU_HOTPLUG_IO_BASE #include "acpi-dsdt-cpu-hotplug.dsl" @@ -314,6 +314,7 @@ DefinitionBlock ( /**************************************************************** * General purpose events ****************************************************************/ + External(\_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_SCAN_METHOD, MethodObj) Scope(\_GPE) { Name(_HID, "ACPI0006") @@ -330,7 +331,9 @@ DefinitionBlock ( // CPU hotplug event \_SB.PRSC() } - Method(_L03) { + Method(_E03) { + // Memory hotplug event + \_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_SCAN_METHOD() } Method(_L04) { } diff --git a/hw/i386/acpi-dsdt.hex.generated b/hw/i386/acpi-dsdt.hex.generated index e61572a..ee490e8 100644 --- a/hw/i386/acpi-dsdt.hex.generated +++ b/hw/i386/acpi-dsdt.hex.generated @@ -3,12 +3,12 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x53, 0x44, 0x54, -0x80, +0x93, 0x11, 0x0, 0x0, 0x1, -0x60, +0xf5, 0x42, 0x58, 0x50, @@ -4285,8 +4285,8 @@ static unsigned char AcpiDsdtAmlCode[] = { 0xa, 0xb, 0x10, -0x42, -0xc, +0x45, +0xd, 0x5f, 0x47, 0x50, @@ -4389,12 +4389,31 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x53, 0x43, 0x14, -0x6, +0x19, 0x5f, -0x4c, +0x45, 0x30, 0x33, 0x0, +0x5c, +0x2f, +0x4, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x4d, +0x48, +0x50, +0x44, +0x4d, +0x53, +0x43, +0x4e, 0x14, 0x6, 0x5f, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 3e0ecf1..67eb450 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -58,6 +58,9 @@ #include "hw/boards.h" #include "hw/pci/pci_host.h" #include "acpi-build.h" +#include "hw/mem/pc-dimm.h" +#include "trace.h" +#include "qapi/visitor.h" /* debug PC/ISA interrupts */ //#define DEBUG_IRQ @@ -701,14 +704,14 @@ static FWCfgState *bochs_bios_init(void) unsigned int apic_id = x86_cpu_apic_id_from_index(i); assert(apic_id < apic_id_limit); for (j = 0; j < nb_numa_nodes; j++) { - if (test_bit(i, node_cpumask[j])) { + if (test_bit(i, numa_info[j].node_cpu)) { numa_fw_cfg[apic_id + 1] = cpu_to_le64(j); break; } } } for (i = 0; i < nb_numa_nodes; i++) { - numa_fw_cfg[apic_id_limit + 1 + i] = cpu_to_le64(node_mem[i]); + numa_fw_cfg[apic_id_limit + 1 + i] = cpu_to_le64(numa_info[i].node_mem); } fw_cfg_add_bytes(fw_cfg, FW_CFG_NUMA, numa_fw_cfg, (1 + apic_id_limit + nb_numa_nodes) * @@ -1119,8 +1122,12 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size, guest_info->apic_id_limit = pc_apic_id_limit(max_cpus); guest_info->apic_xrupt_override = kvm_allows_irq0_override(); guest_info->numa_nodes = nb_numa_nodes; - guest_info->node_mem = g_memdup(node_mem, guest_info->numa_nodes * + guest_info->node_mem = g_malloc0(guest_info->numa_nodes * sizeof *guest_info->node_mem); + for (i = 0; i < nb_numa_nodes; i++) { + guest_info->node_mem[i] = numa_info[i].node_mem; + } + guest_info->node_cpu = g_malloc0(guest_info->apic_id_limit * sizeof *guest_info->node_cpu); @@ -1128,7 +1135,7 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size, unsigned int apic_id = x86_cpu_apic_id_from_index(i); assert(apic_id < guest_info->apic_id_limit); for (j = 0; j < nb_numa_nodes; j++) { - if (test_bit(i, node_cpumask[j])) { + if (test_bit(i, numa_info[j].node_cpu)) { guest_info->node_cpu[apic_id] = j; break; } @@ -1183,10 +1190,8 @@ void pc_acpi_init(const char *default_dsdt) } } -FWCfgState *pc_memory_init(MemoryRegion *system_memory, - const char *kernel_filename, - const char *kernel_cmdline, - const char *initrd_filename, +FWCfgState *pc_memory_init(MachineState *machine, + MemoryRegion *system_memory, ram_addr_t below_4g_mem_size, ram_addr_t above_4g_mem_size, MemoryRegion *rom_memory, @@ -1197,17 +1202,19 @@ FWCfgState *pc_memory_init(MemoryRegion *system_memory, MemoryRegion *ram, *option_rom_mr; MemoryRegion *ram_below_4g, *ram_above_4g; FWCfgState *fw_cfg; + PCMachineState *pcms = PC_MACHINE(machine); - linux_boot = (kernel_filename != NULL); + assert(machine->ram_size == below_4g_mem_size + above_4g_mem_size); + + linux_boot = (machine->kernel_filename != NULL); /* Allocate RAM. We allocate it as a single memory region and use * aliases to address portions of it, mostly for backwards compatibility * with older qemus that used qemu_ram_alloc(). */ ram = g_malloc(sizeof(*ram)); - memory_region_init_ram(ram, NULL, "pc.ram", - below_4g_mem_size + above_4g_mem_size); - vmstate_register_ram_global(ram); + memory_region_allocate_system_memory(ram, NULL, "pc.ram", + machine->ram_size); *ram_memory = ram; ram_below_4g = g_malloc(sizeof(*ram_below_4g)); memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", ram, @@ -1223,6 +1230,43 @@ FWCfgState *pc_memory_init(MemoryRegion *system_memory, e820_add_entry(0x100000000ULL, above_4g_mem_size, E820_RAM); } + if (!guest_info->has_reserved_memory && + (machine->ram_slots || + (machine->maxram_size > machine->ram_size))) { + MachineClass *mc = MACHINE_GET_CLASS(machine); + + error_report("\"-memory 'slots|maxmem'\" is not supported by: %s", + mc->name); + exit(EXIT_FAILURE); + } + + /* initialize hotplug memory address space */ + if (guest_info->has_reserved_memory && + (machine->ram_size < machine->maxram_size)) { + ram_addr_t hotplug_mem_size = + machine->maxram_size - machine->ram_size; + + if (machine->ram_slots > ACPI_MAX_RAM_SLOTS) { + error_report("unsupported amount of memory slots: %"PRIu64, + machine->ram_slots); + exit(EXIT_FAILURE); + } + + pcms->hotplug_memory_base = + ROUND_UP(0x100000000ULL + above_4g_mem_size, 1ULL << 30); + + if ((pcms->hotplug_memory_base + hotplug_mem_size) < + hotplug_mem_size) { + error_report("unsupported amount of maximum memory: " RAM_ADDR_FMT, + machine->maxram_size); + exit(EXIT_FAILURE); + } + + memory_region_init(&pcms->hotplug_memory, OBJECT(pcms), + "hotplug-memory", hotplug_mem_size); + memory_region_add_subregion(system_memory, pcms->hotplug_memory_base, + &pcms->hotplug_memory); + } /* Initialize PC system firmware */ pc_system_firmware_init(rom_memory, guest_info->isapc_ram_fw); @@ -1238,8 +1282,15 @@ FWCfgState *pc_memory_init(MemoryRegion *system_memory, fw_cfg = bochs_bios_init(); rom_set_fw(fw_cfg); + if (guest_info->has_reserved_memory && pcms->hotplug_memory_base) { + uint64_t *val = g_malloc(sizeof(*val)); + *val = cpu_to_le64(ROUND_UP(pcms->hotplug_memory_base, 0x1ULL << 30)); + fw_cfg_add_file(fw_cfg, "etc/reserved-memory-end", val, sizeof(*val)); + } + if (linux_boot) { - load_linux(fw_cfg, kernel_filename, initrd_filename, kernel_cmdline, below_4g_mem_size); + load_linux(fw_cfg, machine->kernel_filename, machine->initrd_filename, + machine->kernel_cmdline, below_4g_mem_size); } for (i = 0; i < nb_option_roms; i++) { @@ -1455,3 +1506,178 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i); } } + +static void pc_generic_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + QEMUMachine *qm = data; + + mc->name = qm->name; + mc->alias = qm->alias; + mc->desc = qm->desc; + mc->init = qm->init; + mc->reset = qm->reset; + mc->hot_add_cpu = qm->hot_add_cpu; + mc->kvm_type = qm->kvm_type; + mc->block_default_type = qm->block_default_type; + mc->max_cpus = qm->max_cpus; + mc->no_serial = qm->no_serial; + mc->no_parallel = qm->no_parallel; + mc->use_virtcon = qm->use_virtcon; + mc->use_sclp = qm->use_sclp; + mc->no_floppy = qm->no_floppy; + mc->no_cdrom = qm->no_cdrom; + mc->no_sdcard = qm->no_sdcard; + mc->is_default = qm->is_default; + mc->default_machine_opts = qm->default_machine_opts; + mc->default_boot_order = qm->default_boot_order; + mc->compat_props = qm->compat_props; + mc->hw_version = qm->hw_version; +} + +void qemu_register_pc_machine(QEMUMachine *m) +{ + char *name = g_strconcat(m->name, TYPE_MACHINE_SUFFIX, NULL); + TypeInfo ti = { + .name = name, + .parent = TYPE_PC_MACHINE, + .class_init = pc_generic_machine_class_init, + .class_data = (void *)m, + }; + + type_register(&ti); + g_free(name); +} + +static void pc_dimm_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + int slot; + HotplugHandlerClass *hhc; + Error *local_err = NULL; + PCMachineState *pcms = PC_MACHINE(hotplug_dev); + MachineState *machine = MACHINE(hotplug_dev); + PCDIMMDevice *dimm = PC_DIMM(dev); + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); + MemoryRegion *mr = ddc->get_memory_region(dimm); + uint64_t addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, + &local_err); + if (local_err) { + goto out; + } + + addr = pc_dimm_get_free_addr(pcms->hotplug_memory_base, + memory_region_size(&pcms->hotplug_memory), + !addr ? NULL : &addr, + memory_region_size(mr), &local_err); + if (local_err) { + goto out; + } + + object_property_set_int(OBJECT(dev), addr, PC_DIMM_ADDR_PROP, &local_err); + if (local_err) { + goto out; + } + trace_mhp_pc_dimm_assigned_address(addr); + + slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, &local_err); + if (local_err) { + goto out; + } + + slot = pc_dimm_get_free_slot(slot == PC_DIMM_UNASSIGNED_SLOT ? NULL : &slot, + machine->ram_slots, &local_err); + if (local_err) { + goto out; + } + object_property_set_int(OBJECT(dev), slot, PC_DIMM_SLOT_PROP, &local_err); + if (local_err) { + goto out; + } + trace_mhp_pc_dimm_assigned_slot(slot); + + if (!pcms->acpi_dev) { + error_setg(&local_err, + "memory hotplug is not enabled: missing acpi device"); + goto out; + } + + memory_region_add_subregion(&pcms->hotplug_memory, + addr - pcms->hotplug_memory_base, mr); + vmstate_register_ram(mr, dev); + + hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); + hhc->plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); +out: + error_propagate(errp, local_err); +} + +static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + pc_dimm_plug(hotplug_dev, dev, errp); + } +} + +static HotplugHandler *pc_get_hotpug_handler(MachineState *machine, + DeviceState *dev) +{ + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(machine); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + return HOTPLUG_HANDLER(machine); + } + + return pcmc->get_hotplug_handler ? + pcmc->get_hotplug_handler(machine, dev) : NULL; +} + +static void +pc_machine_get_hotplug_memory_region_size(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + int64_t value = memory_region_size(&pcms->hotplug_memory); + + visit_type_int(v, &value, name, errp); +} + +static void pc_machine_initfn(Object *obj) +{ + object_property_add(obj, PC_MACHINE_MEMHP_REGION_SIZE, "int", + pc_machine_get_hotplug_memory_region_size, + NULL, NULL, NULL, NULL); +} + +static void pc_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + PCMachineClass *pcmc = PC_MACHINE_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); + + pcmc->get_hotplug_handler = mc->get_hotplug_handler; + mc->get_hotplug_handler = pc_get_hotpug_handler; + hc->plug = pc_machine_device_plug_cb; +} + +static const TypeInfo pc_machine_info = { + .name = TYPE_PC_MACHINE, + .parent = TYPE_MACHINE, + .abstract = true, + .instance_size = sizeof(PCMachineState), + .instance_init = pc_machine_initfn, + .class_size = sizeof(PCMachineClass), + .class_init = pc_machine_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, +}; + +static void pc_machine_register_types(void) +{ + type_register_static(&pc_machine_info); +} + +type_init(pc_machine_register_types) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index a48e263..3e7524b 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -67,12 +67,14 @@ static bool smbios_legacy_mode; * pages in the host. */ static bool gigabyte_align = true; +static bool has_reserved_memory = true; /* PC hardware initialisation */ static void pc_init1(MachineState *machine, int pci_enabled, int kvmclock_enabled) { + PCMachineState *pc_machine = PC_MACHINE(machine); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *system_io = get_system_io(); int i; @@ -143,6 +145,7 @@ static void pc_init1(MachineState *machine, guest_info->has_pci_info = has_pci_info; guest_info->isapc_ram_fw = !pci_enabled; + guest_info->has_reserved_memory = has_reserved_memory; if (smbios_defaults) { MachineClass *mc = MACHINE_GET_CLASS(machine); @@ -153,11 +156,9 @@ static void pc_init1(MachineState *machine, /* allocate ram and load rom/bios */ if (!xen_enabled()) { - fw_cfg = pc_memory_init(system_memory, - machine->kernel_filename, machine->kernel_cmdline, - machine->initrd_filename, - below_4g_mem_size, above_4g_mem_size, - rom_memory, &ram_memory, guest_info); + fw_cfg = pc_memory_init(machine, system_memory, + below_4g_mem_size, above_4g_mem_size, + rom_memory, &ram_memory, guest_info); } gsi_state = g_malloc0(sizeof(*gsi_state)); @@ -244,14 +245,23 @@ static void pc_init1(MachineState *machine, } if (pci_enabled && acpi_enabled) { + DeviceState *piix4_pm; I2CBus *smbus; smi_irq = qemu_allocate_irqs(pc_acpi_smi_interrupt, first_cpu, 1); /* TODO: Populate SPD eeprom data. */ smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, gsi[9], *smi_irq, - kvm_enabled(), fw_cfg); + kvm_enabled(), fw_cfg, &piix4_pm); smbus_eeprom_init(smbus, 8, NULL, 0); + + object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, + TYPE_HOTPLUG_HANDLER, + (Object **)&pc_machine->acpi_dev, + object_property_allow_set_link, + OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); + object_property_set_link(OBJECT(machine), OBJECT(piix4_pm), + PC_MACHINE_ACPI_DEVICE_PROP, &error_abort); } if (pci_enabled) { @@ -267,6 +277,7 @@ static void pc_init_pci(MachineState *machine) static void pc_compat_2_0(MachineState *machine) { smbios_legacy_mode = true; + has_reserved_memory = false; } static void pc_compat_1_7(MachineState *machine) @@ -843,25 +854,25 @@ static QEMUMachine xenfv_machine = { static void pc_machine_init(void) { - qemu_register_machine(&pc_i440fx_machine_v2_1); - qemu_register_machine(&pc_i440fx_machine_v2_0); - qemu_register_machine(&pc_i440fx_machine_v1_7); - qemu_register_machine(&pc_i440fx_machine_v1_6); - qemu_register_machine(&pc_i440fx_machine_v1_5); - qemu_register_machine(&pc_i440fx_machine_v1_4); - qemu_register_machine(&pc_machine_v1_3); - qemu_register_machine(&pc_machine_v1_2); - qemu_register_machine(&pc_machine_v1_1); - qemu_register_machine(&pc_machine_v1_0); - qemu_register_machine(&pc_machine_v0_15); - qemu_register_machine(&pc_machine_v0_14); - qemu_register_machine(&pc_machine_v0_13); - qemu_register_machine(&pc_machine_v0_12); - qemu_register_machine(&pc_machine_v0_11); - qemu_register_machine(&pc_machine_v0_10); - qemu_register_machine(&isapc_machine); + qemu_register_pc_machine(&pc_i440fx_machine_v2_1); + qemu_register_pc_machine(&pc_i440fx_machine_v2_0); + qemu_register_pc_machine(&pc_i440fx_machine_v1_7); + qemu_register_pc_machine(&pc_i440fx_machine_v1_6); + qemu_register_pc_machine(&pc_i440fx_machine_v1_5); + qemu_register_pc_machine(&pc_i440fx_machine_v1_4); + qemu_register_pc_machine(&pc_machine_v1_3); + qemu_register_pc_machine(&pc_machine_v1_2); + qemu_register_pc_machine(&pc_machine_v1_1); + qemu_register_pc_machine(&pc_machine_v1_0); + qemu_register_pc_machine(&pc_machine_v0_15); + qemu_register_pc_machine(&pc_machine_v0_14); + qemu_register_pc_machine(&pc_machine_v0_13); + qemu_register_pc_machine(&pc_machine_v0_12); + qemu_register_pc_machine(&pc_machine_v0_11); + qemu_register_pc_machine(&pc_machine_v0_10); + qemu_register_pc_machine(&isapc_machine); #ifdef CONFIG_XEN - qemu_register_machine(&xenfv_machine); + qemu_register_pc_machine(&xenfv_machine); #endif } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index b3c02c1..aa71332 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -57,10 +57,12 @@ static bool smbios_legacy_mode; * pages in the host. */ static bool gigabyte_align = true; +static bool has_reserved_memory = true; /* PC hardware initialisation */ static void pc_q35_init(MachineState *machine) { + PCMachineState *pc_machine = PC_MACHINE(machine); ram_addr_t below_4g_mem_size, above_4g_mem_size; Q35PCIHost *q35_host; PCIHostState *phb; @@ -130,6 +132,7 @@ static void pc_q35_init(MachineState *machine) guest_info->has_pci_info = has_pci_info; guest_info->isapc_ram_fw = false; guest_info->has_acpi_build = has_acpi_build; + guest_info->has_reserved_memory = has_reserved_memory; if (smbios_defaults) { MachineClass *mc = MACHINE_GET_CLASS(machine); @@ -140,9 +143,7 @@ static void pc_q35_init(MachineState *machine) /* allocate ram and load rom/bios */ if (!xen_enabled()) { - pc_memory_init(get_system_memory(), - machine->kernel_filename, machine->kernel_cmdline, - machine->initrd_filename, + pc_memory_init(machine, get_system_memory(), below_4g_mem_size, above_4g_mem_size, rom_memory, &ram_memory, guest_info); } @@ -176,6 +177,15 @@ static void pc_q35_init(MachineState *machine) lpc = pci_create_simple_multifunction(host_bus, PCI_DEVFN(ICH9_LPC_DEV, ICH9_LPC_FUNC), true, TYPE_ICH9_LPC_DEVICE); + + object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, + TYPE_HOTPLUG_HANDLER, + (Object **)&pc_machine->acpi_dev, + object_property_allow_set_link, + OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); + object_property_set_link(OBJECT(machine), OBJECT(lpc), + PC_MACHINE_ACPI_DEVICE_PROP, &error_abort); + ich9_lpc = ICH9_LPC_DEVICE(lpc); ich9_lpc->pic = gsi; ich9_lpc->ioapic = gsi_state->ioapic_irq; @@ -245,6 +255,7 @@ static void pc_q35_init(MachineState *machine) static void pc_compat_2_0(MachineState *machine) { smbios_legacy_mode = true; + has_reserved_memory = false; } static void pc_compat_1_7(MachineState *machine) @@ -384,12 +395,12 @@ static QEMUMachine pc_q35_machine_v1_4 = { static void pc_q35_machine_init(void) { - qemu_register_machine(&pc_q35_machine_v2_1); - qemu_register_machine(&pc_q35_machine_v2_0); - qemu_register_machine(&pc_q35_machine_v1_7); - qemu_register_machine(&pc_q35_machine_v1_6); - qemu_register_machine(&pc_q35_machine_v1_5); - qemu_register_machine(&pc_q35_machine_v1_4); + qemu_register_pc_machine(&pc_q35_machine_v2_1); + qemu_register_pc_machine(&pc_q35_machine_v2_0); + qemu_register_pc_machine(&pc_q35_machine_v1_7); + qemu_register_pc_machine(&pc_q35_machine_v1_6); + qemu_register_pc_machine(&pc_q35_machine_v1_5); + qemu_register_pc_machine(&pc_q35_machine_v1_4); } machine_init(pc_q35_machine_init); diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl index f4d2a2d..8c3eae7 100644 --- a/hw/i386/q35-acpi-dsdt.dsl +++ b/hw/i386/q35-acpi-dsdt.dsl @@ -402,7 +402,7 @@ DefinitionBlock ( define_gsi_link(GSIH, 0, 0x17) } -#include "hw/acpi/cpu_hotplug_defs.h" +#include "hw/acpi/pc-hotplug.h" #define CPU_STATUS_BASE ICH9_CPU_HOTPLUG_IO_BASE #include "acpi-dsdt-cpu-hotplug.dsl" @@ -410,6 +410,7 @@ DefinitionBlock ( /**************************************************************** * General purpose events ****************************************************************/ + External(\_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_SCAN_METHOD, MethodObj) Scope(\_GPE) { Name(_HID, "ACPI0006") @@ -422,7 +423,9 @@ DefinitionBlock ( // CPU hotplug event \_SB.PRSC() } - Method(_L03) { + Method(_E03) { + // Memory hotplug event + \_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_SCAN_METHOD() } Method(_L04) { } diff --git a/hw/i386/q35-acpi-dsdt.hex.generated b/hw/i386/q35-acpi-dsdt.hex.generated index 6b788c9..c9eb4ac 100644 --- a/hw/i386/q35-acpi-dsdt.hex.generated +++ b/hw/i386/q35-acpi-dsdt.hex.generated @@ -3,12 +3,12 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x53, 0x44, 0x54, -0xd2, +0xe5, 0x1c, 0x0, 0x0, 0x1, -0x13, +0xb7, 0x42, 0x58, 0x50, @@ -7234,8 +7234,8 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0xa, 0xb, 0x10, -0x4f, -0x8, +0x42, +0xa, 0x5f, 0x47, 0x50, @@ -7287,12 +7287,31 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x53, 0x43, 0x14, -0x6, +0x19, 0x5f, -0x4c, +0x45, 0x30, 0x33, 0x0, +0x5c, +0x2f, +0x4, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x4d, +0x48, +0x50, +0x44, +0x4d, +0x53, +0x43, +0x4e, 0x14, 0x6, 0x5f, diff --git a/hw/i386/ssdt-mem.dsl b/hw/i386/ssdt-mem.dsl new file mode 100644 index 0000000..8e17bd1 --- /dev/null +++ b/hw/i386/ssdt-mem.dsl @@ -0,0 +1,77 @@ +/* + * Memory hotplug ACPI DSDT static objects definitions + * + * Copyright ProfitBricks GmbH 2012 + * Copyright (C) 2013-2014 Red Hat Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/> + */ + +/* This file is the basis for the ssdt_mem[] variable in src/acpi.c. + * It defines the contents of the memory device object. At + * runtime, a dynamically generated SSDT will contain one copy of this + * AML snippet for every possible memory device in the system. The + * objects will be placed in the \_SB_ namespace. + * + * In addition to the aml code generated from this file, the + * src/acpi.c file creates a MTFY method with an entry for each memdevice: + * Method(MTFY, 2) { + * If (LEqual(Arg0, 0x00)) { Notify(MP00, Arg1) } + * If (LEqual(Arg0, 0x01)) { Notify(MP01, Arg1) } + * ... + * } + */ +#include "hw/acpi/pc-hotplug.h" + +ACPI_EXTRACT_ALL_CODE ssdm_mem_aml + +DefinitionBlock ("ssdt-mem.aml", "SSDT", 0x02, "BXPC", "CSSDT", 0x1) +{ + + External(\_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_CRS_METHOD, MethodObj) + External(\_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_STATUS_METHOD, MethodObj) + External(\_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_OST_METHOD, MethodObj) + External(\_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_PROXIMITY_METHOD, MethodObj) + + Scope(\_SB) { +/* v------------------ DO NOT EDIT ------------------v */ + ACPI_EXTRACT_DEVICE_START ssdt_mem_start + ACPI_EXTRACT_DEVICE_END ssdt_mem_end + ACPI_EXTRACT_DEVICE_STRING ssdt_mem_name + Device(MPAA) { + ACPI_EXTRACT_NAME_STRING ssdt_mem_id + Name(_UID, "0xAA") +/* ^------------------ DO NOT EDIT ------------------^ + * Don't change the above without also updating the C code. + */ + Name(_HID, EISAID("PNP0C80")) + + Method(_CRS, 0) { + Return(\_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_CRS_METHOD(_UID)) + } + + Method(_STA, 0) { + Return(\_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_STATUS_METHOD(_UID)) + } + + Method(_PXM, 0) { + Return(\_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_PROXIMITY_METHOD(_UID)) + } + + Method(_OST, 3) { + \_SB.PCI0.MEMORY_HOPTLUG_DEVICE.MEMORY_SLOT_OST_METHOD(_UID, Arg0, Arg1, Arg2) + } + } + } +} diff --git a/hw/i386/ssdt-mem.hex.generated b/hw/i386/ssdt-mem.hex.generated new file mode 100644 index 0000000..00bd34d --- /dev/null +++ b/hw/i386/ssdt-mem.hex.generated @@ -0,0 +1,213 @@ +static unsigned char ssdt_mem_id[] = { +0x35 +}; +static unsigned char ssdm_mem_aml[] = { +0x53, +0x53, +0x44, +0x54, +0xc7, +0x0, +0x0, +0x0, +0x2, +0x71, +0x42, +0x58, +0x50, +0x43, +0x0, +0x0, +0x43, +0x53, +0x53, +0x44, +0x54, +0x0, +0x0, +0x0, +0x1, +0x0, +0x0, +0x0, +0x49, +0x4e, +0x54, +0x4c, +0x15, +0x11, +0x13, +0x20, +0x10, +0x42, +0xa, +0x5c, +0x5f, +0x53, +0x42, +0x5f, +0x5b, +0x82, +0x49, +0x9, +0x4d, +0x50, +0x41, +0x41, +0x8, +0x5f, +0x55, +0x49, +0x44, +0xd, +0x30, +0x78, +0x41, +0x41, +0x0, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0x80, +0x14, +0x1e, +0x5f, +0x43, +0x52, +0x53, +0x0, +0xa4, +0x5c, +0x2f, +0x4, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x4d, +0x48, +0x50, +0x44, +0x4d, +0x43, +0x52, +0x53, +0x5f, +0x55, +0x49, +0x44, +0x14, +0x1e, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0x5c, +0x2f, +0x4, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x4d, +0x48, +0x50, +0x44, +0x4d, +0x52, +0x53, +0x54, +0x5f, +0x55, +0x49, +0x44, +0x14, +0x1e, +0x5f, +0x50, +0x58, +0x4d, +0x0, +0xa4, +0x5c, +0x2f, +0x4, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x4d, +0x48, +0x50, +0x44, +0x4d, +0x50, +0x58, +0x4d, +0x5f, +0x55, +0x49, +0x44, +0x14, +0x20, +0x5f, +0x4f, +0x53, +0x54, +0x3, +0x5c, +0x2f, +0x4, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x4d, +0x48, +0x50, +0x44, +0x4d, +0x4f, +0x53, +0x54, +0x5f, +0x55, +0x49, +0x44, +0x68, +0x69, +0x6a +}; +static unsigned char ssdt_mem_start[] = { +0x2c +}; +static unsigned char ssdt_mem_end[] = { +0xc7 +}; +static unsigned char ssdt_mem_name[] = { +0x30 +}; diff --git a/hw/i386/ssdt-misc.dsl b/hw/i386/ssdt-misc.dsl index a4484b8..d329b8b 100644 --- a/hw/i386/ssdt-misc.dsl +++ b/hw/i386/ssdt-misc.dsl @@ -12,6 +12,7 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see <http://www.gnu.org/licenses/>. */ +#include "hw/acpi/pc-hotplug.h" ACPI_EXTRACT_ALL_CODE ssdp_misc_aml @@ -116,4 +117,167 @@ DefinitionBlock ("ssdt-misc.aml", "SSDT", 0x01, "BXPC", "BXSSDTSUSP", 0x1) } } } + + External(MEMORY_SLOT_NOTIFY_METHOD, MethodObj) + Scope(\_SB.PCI0) { + Device(MEMORY_HOPTLUG_DEVICE) { + Name(_HID, "PNP0A06") + Name(_UID, "Memory hotplug resources") + + ACPI_EXTRACT_NAME_DWORD_CONST ssdt_mctrl_nr_slots + Name(MEMORY_SLOTS_NUMBER, 0x12345678) + + /* Memory hotplug IO registers */ + OperationRegion(MEMORY_HOTPLUG_IO_REGION, SystemIO, + ACPI_MEMORY_HOTPLUG_BASE, + ACPI_MEMORY_HOTPLUG_IO_LEN) + + Name(_CRS, ResourceTemplate() { + IO(Decode16, ACPI_MEMORY_HOTPLUG_BASE, ACPI_MEMORY_HOTPLUG_BASE, + 0, ACPI_MEMORY_HOTPLUG_IO_LEN, IO) + }) + + Method(_STA, 0) { + If (LEqual(MEMORY_SLOTS_NUMBER, Zero)) { + Return(0x0) + } + /* present, functioning, decoding, not shown in UI */ + Return(0xB) + } + + Field(MEMORY_HOTPLUG_IO_REGION, DWordAcc, NoLock, Preserve) { + MEMORY_SLOT_ADDR_LOW, 32, // read only + MEMORY_SLOT_ADDR_HIGH, 32, // read only + MEMORY_SLOT_SIZE_LOW, 32, // read only + MEMORY_SLOT_SIZE_HIGH, 32, // read only + MEMORY_SLOT_PROXIMITY, 32, // read only + } + Field(MEMORY_HOTPLUG_IO_REGION, ByteAcc, NoLock, Preserve) { + Offset(20), + MEMORY_SLOT_ENABLED, 1, // 1 if enabled, read only + MEMORY_SLOT_INSERT_EVENT, 1, // (read) 1 if has a insert event. (write) 1 to clear event + } + + Mutex (MEMORY_SLOT_LOCK, 0) + Field (MEMORY_HOTPLUG_IO_REGION, DWordAcc, NoLock, Preserve) { + MEMORY_SLOT_SLECTOR, 32, // DIMM selector, write only + MEMORY_SLOT_OST_EVENT, 32, // _OST event code, write only + MEMORY_SLOT_OST_STATUS, 32, // _OST status code, write only + } + + Method(MEMORY_SLOT_SCAN_METHOD, 0) { + If (LEqual(MEMORY_SLOTS_NUMBER, Zero)) { + Return(Zero) + } + + Store(Zero, Local0) // Mem devs iterrator + Acquire(MEMORY_SLOT_LOCK, 0xFFFF) + while (LLess(Local0, MEMORY_SLOTS_NUMBER)) { + Store(Local0, MEMORY_SLOT_SLECTOR) // select Local0 DIMM + If (LEqual(MEMORY_SLOT_INSERT_EVENT, One)) { // Memory device needs check + MEMORY_SLOT_NOTIFY_METHOD(Local0, 1) + Store(1, MEMORY_SLOT_INSERT_EVENT) + } + // TODO: handle memory eject request + Add(Local0, One, Local0) // goto next DIMM + } + Release(MEMORY_SLOT_LOCK) + Return(One) + } + + Method(MEMORY_SLOT_STATUS_METHOD, 1) { + Store(Zero, Local0) + + Acquire(MEMORY_SLOT_LOCK, 0xFFFF) + Store(ToInteger(Arg0), MEMORY_SLOT_SLECTOR) // select DIMM + + If (LEqual(MEMORY_SLOT_ENABLED, One)) { + Store(0xF, Local0) + } + + Release(MEMORY_SLOT_LOCK) + Return(Local0) + } + + Method(MEMORY_SLOT_CRS_METHOD, 1, Serialized) { + Acquire(MEMORY_SLOT_LOCK, 0xFFFF) + Store(ToInteger(Arg0), MEMORY_SLOT_SLECTOR) // select DIMM + + Name(MR64, ResourceTemplate() { + QWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x0000000000000000, // Address Space Granularity + 0x0000000000000000, // Address Range Minimum + 0xFFFFFFFFFFFFFFFE, // Address Range Maximum + 0x0000000000000000, // Address Translation Offset + 0xFFFFFFFFFFFFFFFF, // Address Length + ,, MW64, AddressRangeMemory, TypeStatic) + }) + + CreateDWordField(MR64, 14, MINL) + CreateDWordField(MR64, 18, MINH) + CreateDWordField(MR64, 38, LENL) + CreateDWordField(MR64, 42, LENH) + CreateDWordField(MR64, 22, MAXL) + CreateDWordField(MR64, 26, MAXH) + + Store(MEMORY_SLOT_ADDR_HIGH, MINH) + Store(MEMORY_SLOT_ADDR_LOW, MINL) + Store(MEMORY_SLOT_SIZE_HIGH, LENH) + Store(MEMORY_SLOT_SIZE_LOW, LENL) + + // 64-bit math: MAX = MIN + LEN - 1 + Add(MINL, LENL, MAXL) + Add(MINH, LENH, MAXH) + If (LLess(MAXL, MINL)) { + Add(MAXH, One, MAXH) + } + If (LLess(MAXL, One)) { + Subtract(MAXH, One, MAXH) + } + Subtract(MAXL, One, MAXL) + + If (LEqual(MAXH, Zero)){ + Name(MR32, ResourceTemplate() { + DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, // Address Space Granularity + 0x00000000, // Address Range Minimum + 0xFFFFFFFE, // Address Range Maximum + 0x00000000, // Address Translation Offset + 0xFFFFFFFF, // Address Length + ,, MW32, AddressRangeMemory, TypeStatic) + }) + CreateDWordField(MR32, MW32._MIN, MIN) + CreateDWordField(MR32, MW32._MAX, MAX) + CreateDWordField(MR32, MW32._LEN, LEN) + Store(MINL, MIN) + Store(MAXL, MAX) + Store(LENL, LEN) + + Release(MEMORY_SLOT_LOCK) + Return(MR32) + } + + Release(MEMORY_SLOT_LOCK) + Return(MR64) + } + + Method(MEMORY_SLOT_PROXIMITY_METHOD, 1) { + Acquire(MEMORY_SLOT_LOCK, 0xFFFF) + Store(ToInteger(Arg0), MEMORY_SLOT_SLECTOR) // select DIMM + Store(MEMORY_SLOT_PROXIMITY, Local0) + Release(MEMORY_SLOT_LOCK) + Return(Local0) + } + + Method(MEMORY_SLOT_OST_METHOD, 4) { + Acquire(MEMORY_SLOT_LOCK, 0xFFFF) + Store(ToInteger(Arg0), MEMORY_SLOT_SLECTOR) // select DIMM + Store(Arg1, MEMORY_SLOT_OST_EVENT) + Store(Arg2, MEMORY_SLOT_OST_STATUS) + Release(MEMORY_SLOT_LOCK) + } + } // Device() + } // Scope() } diff --git a/hw/i386/ssdt-misc.hex.generated b/hw/i386/ssdt-misc.hex.generated index 55e3bd2..ba4268a 100644 --- a/hw/i386/ssdt-misc.hex.generated +++ b/hw/i386/ssdt-misc.hex.generated @@ -4,6 +4,9 @@ static unsigned char acpi_pci64_length[] = { static unsigned char acpi_s4_pkg[] = { 0x8f }; +static unsigned short ssdt_mctrl_nr_slots[] = { +0x1aa +}; static unsigned char acpi_s3_name[] = { 0x7c }; @@ -18,12 +21,12 @@ static unsigned char ssdp_misc_aml[] = { 0x53, 0x44, 0x54, -0x62, -0x1, +0x7e, +0x4, 0x0, 0x0, 0x1, -0x76, +0x8b, 0x42, 0x58, 0x50, @@ -46,8 +49,8 @@ static unsigned char ssdp_misc_aml[] = { 0x4e, 0x54, 0x4c, -0x23, -0x8, +0x15, +0x11, 0x13, 0x20, 0x10, @@ -367,7 +370,803 @@ static unsigned char ssdp_misc_aml[] = { 0x49, 0x4f, 0x4d, -0x58 +0x58, +0x10, +0x4b, +0x31, +0x5c, +0x2e, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x5b, +0x82, +0x4d, +0x30, +0x4d, +0x48, +0x50, +0x44, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xd, +0x50, +0x4e, +0x50, +0x30, +0x41, +0x30, +0x36, +0x0, +0x8, +0x5f, +0x55, +0x49, +0x44, +0xd, +0x4d, +0x65, +0x6d, +0x6f, +0x72, +0x79, +0x20, +0x68, +0x6f, +0x74, +0x70, +0x6c, +0x75, +0x67, +0x20, +0x72, +0x65, +0x73, +0x6f, +0x75, +0x72, +0x63, +0x65, +0x73, +0x0, +0x8, +0x4d, +0x44, +0x4e, +0x52, +0xc, +0x78, +0x56, +0x34, +0x12, +0x5b, +0x80, +0x48, +0x50, +0x4d, +0x52, +0x1, +0xb, +0x0, +0xa, +0xa, +0x18, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0xd, +0xa, +0xa, +0x47, +0x1, +0x0, +0xa, +0x0, +0xa, +0x0, +0x18, +0x79, +0x0, +0x14, +0x13, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa0, +0x9, +0x93, +0x4d, +0x44, +0x4e, +0x52, +0x0, +0xa4, +0x0, +0xa4, +0xa, +0xb, +0x5b, +0x81, +0x1f, +0x48, +0x50, +0x4d, +0x52, +0x3, +0x4d, +0x52, +0x42, +0x4c, +0x20, +0x4d, +0x52, +0x42, +0x48, +0x20, +0x4d, +0x52, +0x4c, +0x4c, +0x20, +0x4d, +0x52, +0x4c, +0x48, +0x20, +0x4d, +0x50, +0x58, +0x5f, +0x20, +0x5b, +0x81, +0x13, +0x48, +0x50, +0x4d, +0x52, +0x1, +0x0, +0x40, +0xa, +0x4d, +0x45, +0x53, +0x5f, +0x1, +0x4d, +0x49, +0x4e, +0x53, +0x1, +0x5b, +0x1, +0x4d, +0x4c, +0x43, +0x4b, +0x0, +0x5b, +0x81, +0x15, +0x48, +0x50, +0x4d, +0x52, +0x3, +0x4d, +0x53, +0x45, +0x4c, +0x20, +0x4d, +0x4f, +0x45, +0x56, +0x20, +0x4d, +0x4f, +0x53, +0x43, +0x20, +0x14, +0x4a, +0x4, +0x4d, +0x53, +0x43, +0x4e, +0x0, +0xa0, +0x9, +0x93, +0x4d, +0x44, +0x4e, +0x52, +0x0, +0xa4, +0x0, +0x70, +0x0, +0x60, +0x5b, +0x23, +0x4d, +0x4c, +0x43, +0x4b, +0xff, +0xff, +0xa2, +0x25, +0x95, +0x60, +0x4d, +0x44, +0x4e, +0x52, +0x70, +0x60, +0x4d, +0x53, +0x45, +0x4c, +0xa0, +0x13, +0x93, +0x4d, +0x49, +0x4e, +0x53, +0x1, +0x4d, +0x54, +0x46, +0x59, +0x60, +0x1, +0x70, +0x1, +0x4d, +0x49, +0x4e, +0x53, +0x72, +0x60, +0x1, +0x60, +0x5b, +0x27, +0x4d, +0x4c, +0x43, +0x4b, +0xa4, +0x1, +0x14, +0x2d, +0x4d, +0x52, +0x53, +0x54, +0x1, +0x70, +0x0, +0x60, +0x5b, +0x23, +0x4d, +0x4c, +0x43, +0x4b, +0xff, +0xff, +0x70, +0x99, +0x68, +0x0, +0x4d, +0x53, +0x45, +0x4c, +0xa0, +0xb, +0x93, +0x4d, +0x45, +0x53, +0x5f, +0x1, +0x70, +0xa, +0xf, +0x60, +0x5b, +0x27, +0x4d, +0x4c, +0x43, +0x4b, +0xa4, +0x60, +0x14, +0x41, +0x18, +0x4d, +0x43, +0x52, +0x53, +0x9, +0x5b, +0x23, +0x4d, +0x4c, +0x43, +0x4b, +0xff, +0xff, +0x70, +0x99, +0x68, +0x0, +0x4d, +0x53, +0x45, +0x4c, +0x8, +0x4d, +0x52, +0x36, +0x34, +0x11, +0x33, +0xa, +0x30, +0x8a, +0x2b, +0x0, +0x0, +0xc, +0x3, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0xfe, +0xff, +0xff, +0xff, +0xff, +0xff, +0xff, +0xff, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0xff, +0xff, +0xff, +0xff, +0xff, +0xff, +0xff, +0xff, +0x79, +0x0, +0x8a, +0x4d, +0x52, +0x36, +0x34, +0xa, +0xe, +0x4d, +0x49, +0x4e, +0x4c, +0x8a, +0x4d, +0x52, +0x36, +0x34, +0xa, +0x12, +0x4d, +0x49, +0x4e, +0x48, +0x8a, +0x4d, +0x52, +0x36, +0x34, +0xa, +0x26, +0x4c, +0x45, +0x4e, +0x4c, +0x8a, +0x4d, +0x52, +0x36, +0x34, +0xa, +0x2a, +0x4c, +0x45, +0x4e, +0x48, +0x8a, +0x4d, +0x52, +0x36, +0x34, +0xa, +0x16, +0x4d, +0x41, +0x58, +0x4c, +0x8a, +0x4d, +0x52, +0x36, +0x34, +0xa, +0x1a, +0x4d, +0x41, +0x58, +0x48, +0x70, +0x4d, +0x52, +0x42, +0x48, +0x4d, +0x49, +0x4e, +0x48, +0x70, +0x4d, +0x52, +0x42, +0x4c, +0x4d, +0x49, +0x4e, +0x4c, +0x70, +0x4d, +0x52, +0x4c, +0x48, +0x4c, +0x45, +0x4e, +0x48, +0x70, +0x4d, +0x52, +0x4c, +0x4c, +0x4c, +0x45, +0x4e, +0x4c, +0x72, +0x4d, +0x49, +0x4e, +0x4c, +0x4c, +0x45, +0x4e, +0x4c, +0x4d, +0x41, +0x58, +0x4c, +0x72, +0x4d, +0x49, +0x4e, +0x48, +0x4c, +0x45, +0x4e, +0x48, +0x4d, +0x41, +0x58, +0x48, +0xa0, +0x14, +0x95, +0x4d, +0x41, +0x58, +0x4c, +0x4d, +0x49, +0x4e, +0x4c, +0x72, +0x4d, +0x41, +0x58, +0x48, +0x1, +0x4d, +0x41, +0x58, +0x48, +0xa0, +0x11, +0x95, +0x4d, +0x41, +0x58, +0x4c, +0x1, +0x74, +0x4d, +0x41, +0x58, +0x48, +0x1, +0x4d, +0x41, +0x58, +0x48, +0x74, +0x4d, +0x41, +0x58, +0x4c, +0x1, +0x4d, +0x41, +0x58, +0x4c, +0xa0, +0x44, +0x7, +0x93, +0x4d, +0x41, +0x58, +0x48, +0x0, +0x8, +0x4d, +0x52, +0x33, +0x32, +0x11, +0x1f, +0xa, +0x1c, +0x87, +0x17, +0x0, +0x0, +0xc, +0x3, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0xfe, +0xff, +0xff, +0xff, +0x0, +0x0, +0x0, +0x0, +0xff, +0xff, +0xff, +0xff, +0x79, +0x0, +0x8a, +0x4d, +0x52, +0x33, +0x32, +0xa, +0xa, +0x4d, +0x49, +0x4e, +0x5f, +0x8a, +0x4d, +0x52, +0x33, +0x32, +0xa, +0xe, +0x4d, +0x41, +0x58, +0x5f, +0x8a, +0x4d, +0x52, +0x33, +0x32, +0xa, +0x16, +0x4c, +0x45, +0x4e, +0x5f, +0x70, +0x4d, +0x49, +0x4e, +0x4c, +0x4d, +0x49, +0x4e, +0x5f, +0x70, +0x4d, +0x41, +0x58, +0x4c, +0x4d, +0x41, +0x58, +0x5f, +0x70, +0x4c, +0x45, +0x4e, +0x4c, +0x4c, +0x45, +0x4e, +0x5f, +0x5b, +0x27, +0x4d, +0x4c, +0x43, +0x4b, +0xa4, +0x4d, +0x52, +0x33, +0x32, +0x5b, +0x27, +0x4d, +0x4c, +0x43, +0x4b, +0xa4, +0x4d, +0x52, +0x36, +0x34, +0x14, +0x24, +0x4d, +0x50, +0x58, +0x4d, +0x1, +0x5b, +0x23, +0x4d, +0x4c, +0x43, +0x4b, +0xff, +0xff, +0x70, +0x99, +0x68, +0x0, +0x4d, +0x53, +0x45, +0x4c, +0x70, +0x4d, +0x50, +0x58, +0x5f, +0x60, +0x5b, +0x27, +0x4d, +0x4c, +0x43, +0x4b, +0xa4, +0x60, +0x14, +0x28, +0x4d, +0x4f, +0x53, +0x54, +0x4, +0x5b, +0x23, +0x4d, +0x4c, +0x43, +0x4b, +0xff, +0xff, +0x70, +0x99, +0x68, +0x0, +0x4d, +0x53, +0x45, +0x4c, +0x70, +0x69, +0x4d, +0x4f, +0x45, +0x56, +0x70, +0x6a, +0x4d, +0x4f, +0x53, +0x43, +0x5b, +0x27, +0x4d, +0x4c, +0x43, +0x4b }; static unsigned char ssdt_isa_pest[] = { 0xd0 diff --git a/hw/i386/ssdt-pcihp.hex.generated b/hw/i386/ssdt-pcihp.hex.generated index b599b46..72ffa84 100644 --- a/hw/i386/ssdt-pcihp.hex.generated +++ b/hw/i386/ssdt-pcihp.hex.generated @@ -32,7 +32,7 @@ static unsigned char ssdp_pcihp_aml[] = { 0x0, 0x0, 0x1, -0x6b, +0x70, 0x42, 0x58, 0x50, @@ -55,8 +55,8 @@ static unsigned char ssdp_pcihp_aml[] = { 0x4e, 0x54, 0x4c, -0x23, -0x8, +0x15, +0x11, 0x13, 0x20, 0x10, diff --git a/hw/i386/ssdt-proc.hex.generated b/hw/i386/ssdt-proc.hex.generated index 97e28d4..4df0734 100644 --- a/hw/i386/ssdt-proc.hex.generated +++ b/hw/i386/ssdt-proc.hex.generated @@ -11,7 +11,7 @@ static unsigned char ssdp_proc_aml[] = { 0x0, 0x0, 0x1, -0x78, +0x7d, 0x42, 0x58, 0x50, @@ -34,8 +34,8 @@ static unsigned char ssdp_proc_aml[] = { 0x4e, 0x54, 0x4c, -0x23, -0x8, +0x15, +0x11, 0x13, 0x20, 0x5b, diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 97f69d6..b846d81 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -563,7 +563,14 @@ static void ich9_lpc_add_properties(ICH9LPCState *lpc) ich9_pm_add_properties(OBJECT(lpc), &lpc->pm, NULL); } -static int ich9_lpc_initfn(PCIDevice *d) +static void ich9_lpc_initfn(Object *obj) +{ + ICH9LPCState *lpc = ICH9_LPC_DEVICE(obj); + + ich9_lpc_add_properties(lpc); +} + +static int ich9_lpc_init(PCIDevice *d) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(d); ISABus *isa_bus; @@ -589,10 +596,22 @@ static int ich9_lpc_initfn(PCIDevice *d) memory_region_add_subregion_overlap(pci_address_space_io(d), ICH9_RST_CNT_IOPORT, &lpc->rst_cnt_mem, 1); + return 0; +} - ich9_lpc_add_properties(lpc); +static void ich9_device_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); - return 0; + ich9_pm_device_plug_cb(&lpc->pm, dev, errp); +} + +static void ich9_device_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + error_setg(errp, "acpi: device unplug request for not supported device" + " type: %s", object_get_typename(OBJECT(dev))); } static bool ich9_rst_cnt_needed(void *opaque) @@ -638,10 +657,12 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); + AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(klass); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->reset = ich9_lpc_reset; - k->init = ich9_lpc_initfn; + k->init = ich9_lpc_init; dc->vmsd = &vmstate_ich9_lpc; k->config_write = ich9_lpc_config_write; dc->desc = "ICH9 LPC bridge"; @@ -654,13 +675,22 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data) * pc_q35_init() */ dc->cannot_instantiate_with_device_add_yet = true; + hc->plug = ich9_device_plug_cb; + hc->unplug = ich9_device_unplug_cb; + adevc->ospm_status = ich9_pm_ospm_status; } static const TypeInfo ich9_lpc_info = { .name = TYPE_ICH9_LPC_DEVICE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(struct ICH9LPCState), + .instance_init = ich9_lpc_initfn, .class_init = ich9_lpc_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { TYPE_ACPI_DEVICE_IF }, + { } + } }; static void ich9_lpc_register(void) diff --git a/hw/mem/Makefile.objs b/hw/mem/Makefile.objs new file mode 100644 index 0000000..b000fb4 --- /dev/null +++ b/hw/mem/Makefile.objs @@ -0,0 +1 @@ +common-obj-$(CONFIG_MEM_HOTPLUG) += pc-dimm.o diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c new file mode 100644 index 0000000..ad176b7 --- /dev/null +++ b/hw/mem/pc-dimm.c @@ -0,0 +1,281 @@ +/* + * Dimm device for Memory Hotplug + * + * Copyright ProfitBricks GmbH 2012 + * Copyright (C) 2014 Red Hat Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/> + */ + +#include "hw/mem/pc-dimm.h" +#include "qemu/config-file.h" +#include "qapi/visitor.h" +#include "qemu/range.h" + +int qmp_pc_dimm_device_list(Object *obj, void *opaque) +{ + MemoryDeviceInfoList ***prev = opaque; + + if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { + DeviceState *dev = DEVICE(obj); + + if (dev->realized) { + MemoryDeviceInfoList *elem = g_new0(MemoryDeviceInfoList, 1); + MemoryDeviceInfo *info = g_new0(MemoryDeviceInfo, 1); + PCDIMMDeviceInfo *di = g_new0(PCDIMMDeviceInfo, 1); + DeviceClass *dc = DEVICE_GET_CLASS(obj); + PCDIMMDevice *dimm = PC_DIMM(obj); + + if (dev->id) { + di->has_id = true; + di->id = g_strdup(dev->id); + } + di->hotplugged = dev->hotplugged; + di->hotpluggable = dc->hotpluggable; + di->addr = dimm->addr; + di->slot = dimm->slot; + di->node = dimm->node; + di->size = object_property_get_int(OBJECT(dimm), PC_DIMM_SIZE_PROP, + NULL); + di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem)); + + info->dimm = di; + elem->value = info; + elem->next = NULL; + **prev = elem; + *prev = &elem->next; + } + } + + object_child_foreach(obj, qmp_pc_dimm_device_list, opaque); + return 0; +} + +static int pc_dimm_slot2bitmap(Object *obj, void *opaque) +{ + unsigned long *bitmap = opaque; + + if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { + DeviceState *dev = DEVICE(obj); + if (dev->realized) { /* count only realized DIMMs */ + PCDIMMDevice *d = PC_DIMM(obj); + set_bit(d->slot, bitmap); + } + } + + object_child_foreach(obj, pc_dimm_slot2bitmap, opaque); + return 0; +} + +int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp) +{ + unsigned long *bitmap = bitmap_new(max_slots); + int slot = 0; + + object_child_foreach(qdev_get_machine(), pc_dimm_slot2bitmap, bitmap); + + /* check if requested slot is not occupied */ + if (hint) { + if (*hint >= max_slots) { + error_setg(errp, "invalid slot# %d, should be less than %d", + *hint, max_slots); + } else if (!test_bit(*hint, bitmap)) { + slot = *hint; + } else { + error_setg(errp, "slot %d is busy", *hint); + } + goto out; + } + + /* search for free slot */ + slot = find_first_zero_bit(bitmap, max_slots); + if (slot == max_slots) { + error_setg(errp, "no free slots available"); + } +out: + g_free(bitmap); + return slot; +} + +static gint pc_dimm_addr_sort(gconstpointer a, gconstpointer b) +{ + PCDIMMDevice *x = PC_DIMM(a); + PCDIMMDevice *y = PC_DIMM(b); + Int128 diff = int128_sub(int128_make64(x->addr), int128_make64(y->addr)); + + if (int128_lt(diff, int128_zero())) { + return -1; + } else if (int128_gt(diff, int128_zero())) { + return 1; + } + return 0; +} + +static int pc_dimm_built_list(Object *obj, void *opaque) +{ + GSList **list = opaque; + + if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { + DeviceState *dev = DEVICE(obj); + if (dev->realized) { /* only realized DIMMs matter */ + *list = g_slist_insert_sorted(*list, dev, pc_dimm_addr_sort); + } + } + + object_child_foreach(obj, pc_dimm_built_list, opaque); + return 0; +} + +uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, + uint64_t address_space_size, + uint64_t *hint, uint64_t size, + Error **errp) +{ + GSList *list = NULL, *item; + uint64_t new_addr, ret = 0; + uint64_t address_space_end = address_space_start + address_space_size; + + assert(address_space_end > address_space_size); + object_child_foreach(qdev_get_machine(), pc_dimm_built_list, &list); + + if (hint) { + new_addr = *hint; + } else { + new_addr = address_space_start; + } + + /* find address range that will fit new DIMM */ + for (item = list; item; item = g_slist_next(item)) { + PCDIMMDevice *dimm = item->data; + uint64_t dimm_size = object_property_get_int(OBJECT(dimm), + PC_DIMM_SIZE_PROP, + errp); + if (errp && *errp) { + goto out; + } + + if (ranges_overlap(dimm->addr, dimm_size, new_addr, size)) { + if (hint) { + DeviceState *d = DEVICE(dimm); + error_setg(errp, "address range conflicts with '%s'", d->id); + goto out; + } + new_addr = dimm->addr + dimm_size; + } + } + ret = new_addr; + + if (new_addr < address_space_start) { + error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 + "] at 0x%" PRIx64, new_addr, size, address_space_start); + } else if ((new_addr + size) > address_space_end) { + error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 + "] beyond 0x%" PRIx64, new_addr, size, address_space_end); + } + +out: + g_slist_free(list); + return ret; +} + +static Property pc_dimm_properties[] = { + DEFINE_PROP_UINT64(PC_DIMM_ADDR_PROP, PCDIMMDevice, addr, 0), + DEFINE_PROP_UINT32(PC_DIMM_NODE_PROP, PCDIMMDevice, node, 0), + DEFINE_PROP_INT32(PC_DIMM_SLOT_PROP, PCDIMMDevice, slot, + PC_DIMM_UNASSIGNED_SLOT), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pc_dimm_get_size(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + int64_t value; + MemoryRegion *mr; + PCDIMMDevice *dimm = PC_DIMM(obj); + + mr = host_memory_backend_get_memory(dimm->hostmem, errp); + value = memory_region_size(mr); + + visit_type_int(v, &value, name, errp); +} + +static void pc_dimm_check_memdev_is_busy(Object *obj, const char *name, + Object *val, Error **errp) +{ + MemoryRegion *mr; + + mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), errp); + if (memory_region_is_mapped(mr)) { + char *path = object_get_canonical_path_component(val); + error_setg(errp, "can't use already busy memdev: %s", path); + g_free(path); + } else { + qdev_prop_allow_set_link_before_realize(obj, name, val, errp); + } +} + +static void pc_dimm_init(Object *obj) +{ + PCDIMMDevice *dimm = PC_DIMM(obj); + + object_property_add(obj, PC_DIMM_SIZE_PROP, "int", pc_dimm_get_size, + NULL, NULL, NULL, &error_abort); + object_property_add_link(obj, PC_DIMM_MEMDEV_PROP, TYPE_MEMORY_BACKEND, + (Object **)&dimm->hostmem, + pc_dimm_check_memdev_is_busy, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); +} + +static void pc_dimm_realize(DeviceState *dev, Error **errp) +{ + PCDIMMDevice *dimm = PC_DIMM(dev); + + if (!dimm->hostmem) { + error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property is not set"); + return; + } +} + +static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm) +{ + return host_memory_backend_get_memory(dimm->hostmem, &error_abort); +} + +static void pc_dimm_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc); + + dc->realize = pc_dimm_realize; + dc->props = pc_dimm_properties; + + ddc->get_memory_region = pc_dimm_get_memory_region; +} + +static TypeInfo pc_dimm_info = { + .name = TYPE_PC_DIMM, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PCDIMMDevice), + .instance_init = pc_dimm_init, + .class_init = pc_dimm_class_init, + .class_size = sizeof(PCDIMMDeviceClass), +}; + +static void pc_dimm_register_types(void) +{ + type_register_static(&pc_dimm_info); +} + +type_init(pc_dimm_register_types) diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index f4a7d47..3c04342 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -1104,7 +1104,7 @@ void mips_malta_init(MachineState *machine) pci_piix4_ide_init(pci_bus, hd, piix4_devfn + 1); pci_create_simple(pci_bus, piix4_devfn + 2, "piix4-usb-uhci"); smbus = piix4_pm_init(pci_bus, piix4_devfn + 3, 0x1100, - isa_get_irq(NULL, 9), NULL, 0, NULL); + isa_get_irq(NULL, 9), NULL, 0, NULL, NULL); smbus_eeprom_init(smbus, 8, smbus_eeprom_buf, smbus_eeprom_size); g_free(smbus_eeprom_buf); pit = pit_init(isa_bus, 0x40, 0, NULL); diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index a1de2f4..7ac7c21 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -15,6 +15,7 @@ #include "net/net.h" #include "net/tap.h" +#include "net/vhost-user.h" #include "hw/virtio/virtio-net.h" #include "net/vhost_net.h" @@ -27,7 +28,6 @@ #include <sys/socket.h> #include <linux/kvm.h> #include <fcntl.h> -#include <sys/ioctl.h> #include <linux/virtio_ring.h> #include <netpacket/packet.h> #include <net/ethernet.h> @@ -46,39 +46,76 @@ struct vhost_net { NetClientState *nc; }; -unsigned vhost_net_get_features(struct vhost_net *net, unsigned features) +/* Features supported by host kernel. */ +static const int kernel_feature_bits[] = { + VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_RING_F_INDIRECT_DESC, + VIRTIO_RING_F_EVENT_IDX, + VIRTIO_NET_F_MRG_RXBUF, + VHOST_INVALID_FEATURE_BIT +}; + +/* Features supported by others. */ +const int user_feature_bits[] = { + VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_RING_F_INDIRECT_DESC, + VIRTIO_RING_F_EVENT_IDX, + + VIRTIO_F_ANY_LAYOUT, + VIRTIO_NET_F_CSUM, + VIRTIO_NET_F_GUEST_CSUM, + VIRTIO_NET_F_GSO, + VIRTIO_NET_F_GUEST_TSO4, + VIRTIO_NET_F_GUEST_TSO6, + VIRTIO_NET_F_GUEST_ECN, + VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_F_HOST_TSO4, + VIRTIO_NET_F_HOST_TSO6, + VIRTIO_NET_F_HOST_ECN, + VIRTIO_NET_F_HOST_UFO, + VIRTIO_NET_F_MRG_RXBUF, + VIRTIO_NET_F_STATUS, + VIRTIO_NET_F_CTRL_VQ, + VIRTIO_NET_F_CTRL_RX, + VIRTIO_NET_F_CTRL_VLAN, + VIRTIO_NET_F_CTRL_RX_EXTRA, + VIRTIO_NET_F_CTRL_MAC_ADDR, + VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, + + VIRTIO_NET_F_MQ, + + VHOST_INVALID_FEATURE_BIT +}; + +static const int *vhost_net_get_feature_bits(struct vhost_net *net) { - /* Clear features not supported by host kernel. */ - if (!(net->dev.features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY))) { - features &= ~(1 << VIRTIO_F_NOTIFY_ON_EMPTY); - } - if (!(net->dev.features & (1 << VIRTIO_RING_F_INDIRECT_DESC))) { - features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC); - } - if (!(net->dev.features & (1 << VIRTIO_RING_F_EVENT_IDX))) { - features &= ~(1 << VIRTIO_RING_F_EVENT_IDX); - } - if (!(net->dev.features & (1 << VIRTIO_NET_F_MRG_RXBUF))) { - features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF); + const int *feature_bits = 0; + + switch (net->nc->info->type) { + case NET_CLIENT_OPTIONS_KIND_TAP: + feature_bits = kernel_feature_bits; + break; + case NET_CLIENT_OPTIONS_KIND_VHOST_USER: + feature_bits = user_feature_bits; + break; + default: + error_report("Feature bits not defined for this type: %d", + net->nc->info->type); + break; } - return features; + + return feature_bits; +} + +unsigned vhost_net_get_features(struct vhost_net *net, unsigned features) +{ + return vhost_get_features(&net->dev, vhost_net_get_feature_bits(net), + features); } void vhost_net_ack_features(struct vhost_net *net, unsigned features) { - net->dev.acked_features = net->dev.backend_features; - if (features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) { - net->dev.acked_features |= (1 << VIRTIO_F_NOTIFY_ON_EMPTY); - } - if (features & (1 << VIRTIO_RING_F_INDIRECT_DESC)) { - net->dev.acked_features |= (1 << VIRTIO_RING_F_INDIRECT_DESC); - } - if (features & (1 << VIRTIO_RING_F_EVENT_IDX)) { - net->dev.acked_features |= (1 << VIRTIO_RING_F_EVENT_IDX); - } - if (features & (1 << VIRTIO_NET_F_MRG_RXBUF)) { - net->dev.acked_features |= (1 << VIRTIO_NET_F_MRG_RXBUF); - } + vhost_ack_features(&net->dev, vhost_net_get_feature_bits(net), features); } static int vhost_net_get_fd(NetClientState *backend) @@ -92,42 +129,52 @@ static int vhost_net_get_fd(NetClientState *backend) } } -struct vhost_net *vhost_net_init(NetClientState *backend, int devfd, - bool force) +struct vhost_net *vhost_net_init(VhostNetOptions *options) { int r; + bool backend_kernel = options->backend_type == VHOST_BACKEND_TYPE_KERNEL; struct vhost_net *net = g_malloc(sizeof *net); - if (!backend) { - fprintf(stderr, "vhost-net requires backend to be setup\n"); + + if (!options->net_backend) { + fprintf(stderr, "vhost-net requires net backend to be setup\n"); goto fail; } - r = vhost_net_get_fd(backend); - if (r < 0) { - goto fail; + + if (backend_kernel) { + r = vhost_net_get_fd(options->net_backend); + if (r < 0) { + goto fail; + } + net->dev.backend_features = qemu_has_vnet_hdr(options->net_backend) + ? 0 : (1 << VHOST_NET_F_VIRTIO_NET_HDR); + net->backend = r; + } else { + net->dev.backend_features = 0; + net->backend = -1; } - net->nc = backend; - net->dev.backend_features = qemu_has_vnet_hdr(backend) ? 0 : - (1 << VHOST_NET_F_VIRTIO_NET_HDR); - net->backend = r; + net->nc = options->net_backend; net->dev.nvqs = 2; net->dev.vqs = net->vqs; - r = vhost_dev_init(&net->dev, devfd, "/dev/vhost-net", force); + r = vhost_dev_init(&net->dev, options->opaque, + options->backend_type, options->force); if (r < 0) { goto fail; } - if (!qemu_has_vnet_hdr_len(backend, + if (!qemu_has_vnet_hdr_len(options->net_backend, sizeof(struct virtio_net_hdr_mrg_rxbuf))) { net->dev.features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF); } - if (~net->dev.features & net->dev.backend_features) { - fprintf(stderr, "vhost lacks feature mask %" PRIu64 " for backend\n", - (uint64_t)(~net->dev.features & net->dev.backend_features)); - vhost_dev_cleanup(&net->dev); - goto fail; + if (backend_kernel) { + if (~net->dev.features & net->dev.backend_features) { + fprintf(stderr, "vhost lacks feature mask %" PRIu64 + " for backend\n", + (uint64_t)(~net->dev.features & net->dev.backend_features)); + vhost_dev_cleanup(&net->dev); + goto fail; + } } - /* Set sane init value. Override when guest acks. */ vhost_net_ack_features(net, 0); return net; @@ -166,24 +213,37 @@ static int vhost_net_start_one(struct vhost_net *net, goto fail_start; } - net->nc->info->poll(net->nc, false); - qemu_set_fd_handler(net->backend, NULL, NULL, NULL); - file.fd = net->backend; - for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { - r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file); - if (r < 0) { - r = -errno; - goto fail; + if (net->nc->info->poll) { + net->nc->info->poll(net->nc, false); + } + + if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) { + qemu_set_fd_handler(net->backend, NULL, NULL, NULL); + file.fd = net->backend; + for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { + const VhostOps *vhost_ops = net->dev.vhost_ops; + r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND, + &file); + if (r < 0) { + r = -errno; + goto fail; + } } } return 0; fail: file.fd = -1; - while (file.index-- > 0) { - int r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file); - assert(r >= 0); + if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) { + while (file.index-- > 0) { + const VhostOps *vhost_ops = net->dev.vhost_ops; + int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND, + &file); + assert(r >= 0); + } + } + if (net->nc->info->poll) { + net->nc->info->poll(net->nc, true); } - net->nc->info->poll(net->nc, true); vhost_dev_stop(&net->dev, dev); fail_start: vhost_dev_disable_notifiers(&net->dev, dev); @@ -200,11 +260,17 @@ static void vhost_net_stop_one(struct vhost_net *net, return; } - for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { - int r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file); - assert(r >= 0); + if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) { + for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { + const VhostOps *vhost_ops = net->dev.vhost_ops; + int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND, + &file); + assert(r >= 0); + } + } + if (net->nc->info->poll) { + net->nc->info->poll(net->nc, true); } - net->nc->info->poll(net->nc, true); vhost_dev_stop(&net->dev, dev); vhost_dev_disable_notifiers(&net->dev, dev); } @@ -224,7 +290,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, } for (i = 0; i < total_queues; i++) { - r = vhost_net_start_one(tap_get_vhost_net(ncs[i].peer), dev, i * 2); + r = vhost_net_start_one(get_vhost_net(ncs[i].peer), dev, i * 2); if (r < 0) { goto err; @@ -241,7 +307,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, err: while (--i >= 0) { - vhost_net_stop_one(tap_get_vhost_net(ncs[i].peer), dev); + vhost_net_stop_one(get_vhost_net(ncs[i].peer), dev); } return r; } @@ -262,7 +328,7 @@ void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, assert(r >= 0); for (i = 0; i < total_queues; i++) { - vhost_net_stop_one(tap_get_vhost_net(ncs[i].peer), dev); + vhost_net_stop_one(get_vhost_net(ncs[i].peer), dev); } } @@ -282,9 +348,30 @@ void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, { vhost_virtqueue_mask(&net->dev, dev, idx, mask); } + +VHostNetState *get_vhost_net(NetClientState *nc) +{ + VHostNetState *vhost_net = 0; + + if (!nc) { + return 0; + } + + switch (nc->info->type) { + case NET_CLIENT_OPTIONS_KIND_TAP: + vhost_net = tap_get_vhost_net(nc); + break; + case NET_CLIENT_OPTIONS_KIND_VHOST_USER: + vhost_net = vhost_user_get_vhost_net(nc); + break; + default: + break; + } + + return vhost_net; +} #else -struct vhost_net *vhost_net_init(NetClientState *backend, int devfd, - bool force) +struct vhost_net *vhost_net_init(VhostNetOptions *options) { error_report("vhost-net support is not compiled in"); return NULL; @@ -328,4 +415,9 @@ void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, int idx, bool mask) { } + +VHostNetState *get_vhost_net(NetClientState *nc) +{ + return 0; +} #endif diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 940a7cf..d8588f3 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -99,20 +99,23 @@ static bool virtio_net_started(VirtIONet *n, uint8_t status) (n->status & VIRTIO_NET_S_LINK_UP) && vdev->vm_running; } +static void virtio_net_announce_timer(void *opaque) +{ + VirtIONet *n = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(n); + + n->announce_counter--; + n->status |= VIRTIO_NET_S_ANNOUNCE; + virtio_notify_config(vdev); +} + static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) { VirtIODevice *vdev = VIRTIO_DEVICE(n); NetClientState *nc = qemu_get_queue(n->nic); int queues = n->multiqueue ? n->max_queues : 1; - if (!nc->peer) { - return; - } - if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { - return; - } - - if (!tap_get_vhost_net(nc->peer)) { + if (!get_vhost_net(nc->peer)) { return; } @@ -122,7 +125,7 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) } if (!n->vhost_started) { int r; - if (!vhost_net_query(tap_get_vhost_net(nc->peer), vdev)) { + if (!vhost_net_query(get_vhost_net(nc->peer), vdev)) { return; } n->vhost_started = 1; @@ -322,6 +325,9 @@ static void virtio_net_reset(VirtIODevice *vdev) n->nobcast = 0; /* multiqueue is disabled by default */ n->curr_queues = 1; + timer_del(n->announce_timer); + n->announce_counter = 0; + n->status &= ~VIRTIO_NET_S_ANNOUNCE; /* Flush any MAC and VLAN filter table state */ n->mac_table.in_use = 0; @@ -452,13 +458,10 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) features &= ~(0x1 << VIRTIO_NET_F_HOST_UFO); } - if (!nc->peer || nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { + if (!get_vhost_net(nc->peer)) { return features; } - if (!tap_get_vhost_net(nc->peer)) { - return features; - } - return vhost_net_get_features(tap_get_vhost_net(nc->peer), features); + return vhost_net_get_features(get_vhost_net(nc->peer), features); } static uint32_t virtio_net_bad_features(VirtIODevice *vdev) @@ -522,13 +525,10 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) for (i = 0; i < n->max_queues; i++) { NetClientState *nc = qemu_get_subqueue(n->nic, i); - if (!nc->peer || nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { - continue; - } - if (!tap_get_vhost_net(nc->peer)) { + if (!get_vhost_net(nc->peer)) { continue; } - vhost_net_ack_features(tap_get_vhost_net(nc->peer), features); + vhost_net_ack_features(get_vhost_net(nc->peer), features); } if ((1 << VIRTIO_NET_F_CTRL_VLAN) & features) { @@ -731,6 +731,23 @@ static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd, return VIRTIO_NET_OK; } +static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd, + struct iovec *iov, unsigned int iov_cnt) +{ + if (cmd == VIRTIO_NET_CTRL_ANNOUNCE_ACK && + n->status & VIRTIO_NET_S_ANNOUNCE) { + n->status &= ~VIRTIO_NET_S_ANNOUNCE; + if (n->announce_counter) { + timer_mod(n->announce_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + self_announce_delay(n->announce_counter)); + } + return VIRTIO_NET_OK; + } else { + return VIRTIO_NET_ERR; + } +} + static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, struct iovec *iov, unsigned int iov_cnt) { @@ -794,6 +811,8 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt); } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) { status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt); + } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) { + status = virtio_net_handle_announce(n, ctrl.cmd, iov, iov_cnt); } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) { status = virtio_net_handle_mq(n, ctrl.cmd, iov, iov_cnt); } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) { @@ -1451,6 +1470,12 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) qemu_get_subqueue(n->nic, i)->link_down = link_down; } + if (vdev->guest_features & (0x1 << VIRTIO_NET_F_GUEST_ANNOUNCE) && + vdev->guest_features & (0x1 << VIRTIO_NET_F_CTRL_VQ)) { + n->announce_counter = SELF_ANNOUNCE_ROUNDS; + timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)); + } + return 0; } @@ -1476,7 +1501,7 @@ static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx) VirtIONet *n = VIRTIO_NET(vdev); NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx)); assert(n->vhost_started); - return vhost_net_virtqueue_pending(tap_get_vhost_net(nc->peer), idx); + return vhost_net_virtqueue_pending(get_vhost_net(nc->peer), idx); } static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, @@ -1485,7 +1510,7 @@ static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, VirtIONet *n = VIRTIO_NET(vdev); NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx)); assert(n->vhost_started); - vhost_net_virtqueue_mask(tap_get_vhost_net(nc->peer), + vhost_net_virtqueue_mask(get_vhost_net(nc->peer), vdev, idx, mask); } @@ -1509,18 +1534,9 @@ void virtio_net_set_netclient_name(VirtIONet *n, const char *name, */ assert(type != NULL); - if (n->netclient_name) { - g_free(n->netclient_name); - n->netclient_name = NULL; - } - if (n->netclient_type) { - g_free(n->netclient_type); - n->netclient_type = NULL; - } - - if (name != NULL) { - n->netclient_name = g_strdup(name); - } + g_free(n->netclient_name); + g_free(n->netclient_type); + n->netclient_name = g_strdup(name); n->netclient_type = g_strdup(type); } @@ -1562,6 +1578,8 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&n->nic_conf.macaddr); memcpy(&n->mac[0], &n->nic_conf.macaddr, sizeof(n->mac)); n->status = VIRTIO_NET_S_LINK_UP; + n->announce_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, + virtio_net_announce_timer, n); if (n->netclient_type) { /* @@ -1616,14 +1634,10 @@ static void virtio_net_device_unrealize(DeviceState *dev, Error **errp) unregister_savevm(dev, "virtio-net", n); - if (n->netclient_name) { - g_free(n->netclient_name); - n->netclient_name = NULL; - } - if (n->netclient_type) { - g_free(n->netclient_type); - n->netclient_type = NULL; - } + g_free(n->netclient_name); + n->netclient_name = NULL; + g_free(n->netclient_type); + n->netclient_type = NULL; g_free(n->mac_table.macs); g_free(n->vlans); @@ -1642,6 +1656,8 @@ static void virtio_net_device_unrealize(DeviceState *dev, Error **errp) } } + timer_del(n->announce_timer); + timer_free(n->announce_timer); g_free(n->vqs); qemu_del_nic(n->nic); virtio_cleanup(vdev); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index e06321c..82f183f 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -673,8 +673,8 @@ static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt) int i, off; /* memory node(s) */ - if (nb_numa_nodes > 1 && node_mem[0] < ram_size) { - node0_size = node_mem[0]; + if (nb_numa_nodes > 1 && numa_info[0].node_mem < ram_size) { + node0_size = numa_info[0].node_mem; } else { node0_size = ram_size; } @@ -712,7 +712,7 @@ static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt) if (mem_start >= ram_size) { node_size = 0; } else { - node_size = node_mem[i]; + node_size = numa_info[i].node_mem; if (node_size > ram_size - mem_start) { node_size = ram_size - mem_start; } @@ -857,7 +857,8 @@ static void spapr_reset_htab(sPAPREnvironment *spapr) /* Update the RMA size if necessary */ if (spapr->vrma_adjust) { - hwaddr node0_size = (nb_numa_nodes > 1) ? node_mem[0] : ram_size; + hwaddr node0_size = (nb_numa_nodes > 1) ? + numa_info[0].node_mem : ram_size; spapr->rma_size = kvmppc_rma_size(node0_size, spapr->htab_shift); } } @@ -1289,7 +1290,7 @@ static void ppc_spapr_init(MachineState *machine) MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); hwaddr rma_alloc_size; - hwaddr node0_size = (nb_numa_nodes > 1) ? node_mem[0] : ram_size; + hwaddr node0_size = (nb_numa_nodes > 1) ? numa_info[0].node_mem : ram_size; uint32_t initrd_base = 0; long kernel_size = 0, initrd_size = 0; long load_limit, rtas_limit, fw_size; diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index cc8df4e..ddfe76a 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -24,15 +24,25 @@ #include "hw/virtio/virtio-scsi.h" #include "hw/virtio/virtio-bus.h" +/* Features supported by host kernel. */ +static const int kernel_feature_bits[] = { + VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_RING_F_INDIRECT_DESC, + VIRTIO_RING_F_EVENT_IDX, + VIRTIO_SCSI_F_HOTPLUG, + VHOST_INVALID_FEATURE_BIT +}; + static int vhost_scsi_set_endpoint(VHostSCSI *s) { VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + const VhostOps *vhost_ops = s->dev.vhost_ops; struct vhost_scsi_target backend; int ret; memset(&backend, 0, sizeof(backend)); pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn); - ret = ioctl(s->dev.control, VHOST_SCSI_SET_ENDPOINT, &backend); + ret = vhost_ops->vhost_call(&s->dev, VHOST_SCSI_SET_ENDPOINT, &backend); if (ret < 0) { return -errno; } @@ -43,10 +53,11 @@ static void vhost_scsi_clear_endpoint(VHostSCSI *s) { VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); struct vhost_scsi_target backend; + const VhostOps *vhost_ops = s->dev.vhost_ops; memset(&backend, 0, sizeof(backend)); pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn); - ioctl(s->dev.control, VHOST_SCSI_CLEAR_ENDPOINT, &backend); + vhost_ops->vhost_call(&s->dev, VHOST_SCSI_CLEAR_ENDPOINT, &backend); } static int vhost_scsi_start(VHostSCSI *s) @@ -55,13 +66,15 @@ static int vhost_scsi_start(VHostSCSI *s) VirtIODevice *vdev = VIRTIO_DEVICE(s); BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + const VhostOps *vhost_ops = s->dev.vhost_ops; if (!k->set_guest_notifiers) { error_report("binding does not support guest notifiers"); return -ENOSYS; } - ret = ioctl(s->dev.control, VHOST_SCSI_GET_ABI_VERSION, &abi_version); + ret = vhost_ops->vhost_call(&s->dev, + VHOST_SCSI_GET_ABI_VERSION, &abi_version); if (ret < 0) { return -errno; } @@ -141,21 +154,7 @@ static uint32_t vhost_scsi_get_features(VirtIODevice *vdev, { VHostSCSI *s = VHOST_SCSI(vdev); - /* Clear features not supported by host kernel. */ - if (!(s->dev.features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY))) { - features &= ~(1 << VIRTIO_F_NOTIFY_ON_EMPTY); - } - if (!(s->dev.features & (1 << VIRTIO_RING_F_INDIRECT_DESC))) { - features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC); - } - if (!(s->dev.features & (1 << VIRTIO_RING_F_EVENT_IDX))) { - features &= ~(1 << VIRTIO_RING_F_EVENT_IDX); - } - if (!(s->dev.features & (1 << VIRTIO_SCSI_F_HOTPLUG))) { - features &= ~(1 << VIRTIO_SCSI_F_HOTPLUG); - } - - return features; + return vhost_get_features(&s->dev, kernel_feature_bits, features); } static void vhost_scsi_set_config(VirtIODevice *vdev, @@ -219,6 +218,13 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) error_setg(errp, "vhost-scsi: unable to parse vhostfd"); return; } + } else { + vhostfd = open("/dev/vhost-scsi", O_RDWR); + if (vhostfd < 0) { + error_setg(errp, "vhost-scsi: open vhost char device failed: %s", + strerror(errno)); + return; + } } virtio_scsi_common_realize(dev, &err, vhost_dummy_handle_output, @@ -233,7 +239,8 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) s->dev.vqs = g_new(struct vhost_virtqueue, s->dev.nvqs); s->dev.vq_index = 0; - ret = vhost_dev_init(&s->dev, vhostfd, "/dev/vhost-scsi", true); + ret = vhost_dev_init(&s->dev, (void *)(uintptr_t)vhostfd, + VHOST_BACKEND_TYPE_KERNEL, true); if (ret < 0) { error_setg(errp, "vhost-scsi: vhost initialization failed: %s", strerror(-ret)); diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs index 1ba53d9..ec9e855 100644 --- a/hw/virtio/Makefile.objs +++ b/hw/virtio/Makefile.objs @@ -5,4 +5,4 @@ common-obj-y += virtio-mmio.o common-obj-$(CONFIG_VIRTIO_BLK_DATA_PLANE) += dataplane/ obj-y += virtio.o virtio-balloon.o -obj-$(CONFIG_LINUX) += vhost.o +obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c new file mode 100644 index 0000000..35316c4 --- /dev/null +++ b/hw/virtio/vhost-backend.c @@ -0,0 +1,71 @@ +/* + * vhost-backend + * + * Copyright (c) 2013 Virtual Open Systems Sarl. + * + * 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 "hw/virtio/vhost.h" +#include "hw/virtio/vhost-backend.h" +#include "qemu/error-report.h" + +#include <sys/ioctl.h> + +extern const VhostOps user_ops; + +static int vhost_kernel_call(struct vhost_dev *dev, unsigned long int request, + void *arg) +{ + int fd = (uintptr_t) dev->opaque; + + assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_KERNEL); + + return ioctl(fd, request, arg); +} + +static int vhost_kernel_init(struct vhost_dev *dev, void *opaque) +{ + assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_KERNEL); + + dev->opaque = opaque; + + return 0; +} + +static int vhost_kernel_cleanup(struct vhost_dev *dev) +{ + int fd = (uintptr_t) dev->opaque; + + assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_KERNEL); + + return close(fd); +} + +static const VhostOps kernel_ops = { + .backend_type = VHOST_BACKEND_TYPE_KERNEL, + .vhost_call = vhost_kernel_call, + .vhost_backend_init = vhost_kernel_init, + .vhost_backend_cleanup = vhost_kernel_cleanup +}; + +int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type) +{ + int r = 0; + + switch (backend_type) { + case VHOST_BACKEND_TYPE_KERNEL: + dev->vhost_ops = &kernel_ops; + break; + case VHOST_BACKEND_TYPE_USER: + dev->vhost_ops = &user_ops; + break; + default: + error_report("Unknown vhost backend type\n"); + r = -1; + } + + return r; +} diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c new file mode 100644 index 0000000..0df6a93 --- /dev/null +++ b/hw/virtio/vhost-user.c @@ -0,0 +1,342 @@ +/* + * vhost-user + * + * Copyright (c) 2013 Virtual Open Systems Sarl. + * + * 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 "hw/virtio/vhost.h" +#include "hw/virtio/vhost-backend.h" +#include "sysemu/char.h" +#include "sysemu/kvm.h" +#include "qemu/error-report.h" +#include "qemu/sockets.h" + +#include <fcntl.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <linux/vhost.h> + +#define VHOST_MEMORY_MAX_NREGIONS 8 + +typedef enum VhostUserRequest { + VHOST_USER_NONE = 0, + VHOST_USER_GET_FEATURES = 1, + VHOST_USER_SET_FEATURES = 2, + VHOST_USER_SET_OWNER = 3, + VHOST_USER_RESET_OWNER = 4, + VHOST_USER_SET_MEM_TABLE = 5, + VHOST_USER_SET_LOG_BASE = 6, + VHOST_USER_SET_LOG_FD = 7, + VHOST_USER_SET_VRING_NUM = 8, + VHOST_USER_SET_VRING_ADDR = 9, + VHOST_USER_SET_VRING_BASE = 10, + VHOST_USER_GET_VRING_BASE = 11, + VHOST_USER_SET_VRING_KICK = 12, + VHOST_USER_SET_VRING_CALL = 13, + VHOST_USER_SET_VRING_ERR = 14, + VHOST_USER_MAX +} VhostUserRequest; + +typedef struct VhostUserMemoryRegion { + uint64_t guest_phys_addr; + uint64_t memory_size; + uint64_t userspace_addr; +} VhostUserMemoryRegion; + +typedef struct VhostUserMemory { + uint32_t nregions; + uint32_t padding; + VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS]; +} VhostUserMemory; + +typedef struct VhostUserMsg { + VhostUserRequest request; + +#define VHOST_USER_VERSION_MASK (0x3) +#define VHOST_USER_REPLY_MASK (0x1<<2) + uint32_t flags; + uint32_t size; /* the following payload size */ + union { +#define VHOST_USER_VRING_IDX_MASK (0xff) +#define VHOST_USER_VRING_NOFD_MASK (0x1<<8) + uint64_t u64; + struct vhost_vring_state state; + struct vhost_vring_addr addr; + VhostUserMemory memory; + }; +} QEMU_PACKED VhostUserMsg; + +static VhostUserMsg m __attribute__ ((unused)); +#define VHOST_USER_HDR_SIZE (sizeof(m.request) \ + + sizeof(m.flags) \ + + sizeof(m.size)) + +#define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE) + +/* The version of the protocol we support */ +#define VHOST_USER_VERSION (0x1) + +static bool ioeventfd_enabled(void) +{ + return kvm_enabled() && kvm_eventfds_enabled(); +} + +static unsigned long int ioctl_to_vhost_user_request[VHOST_USER_MAX] = { + -1, /* VHOST_USER_NONE */ + VHOST_GET_FEATURES, /* VHOST_USER_GET_FEATURES */ + VHOST_SET_FEATURES, /* VHOST_USER_SET_FEATURES */ + VHOST_SET_OWNER, /* VHOST_USER_SET_OWNER */ + VHOST_RESET_OWNER, /* VHOST_USER_RESET_OWNER */ + VHOST_SET_MEM_TABLE, /* VHOST_USER_SET_MEM_TABLE */ + VHOST_SET_LOG_BASE, /* VHOST_USER_SET_LOG_BASE */ + VHOST_SET_LOG_FD, /* VHOST_USER_SET_LOG_FD */ + VHOST_SET_VRING_NUM, /* VHOST_USER_SET_VRING_NUM */ + VHOST_SET_VRING_ADDR, /* VHOST_USER_SET_VRING_ADDR */ + VHOST_SET_VRING_BASE, /* VHOST_USER_SET_VRING_BASE */ + VHOST_GET_VRING_BASE, /* VHOST_USER_GET_VRING_BASE */ + VHOST_SET_VRING_KICK, /* VHOST_USER_SET_VRING_KICK */ + VHOST_SET_VRING_CALL, /* VHOST_USER_SET_VRING_CALL */ + VHOST_SET_VRING_ERR /* VHOST_USER_SET_VRING_ERR */ +}; + +static VhostUserRequest vhost_user_request_translate(unsigned long int request) +{ + VhostUserRequest idx; + + for (idx = 0; idx < VHOST_USER_MAX; idx++) { + if (ioctl_to_vhost_user_request[idx] == request) { + break; + } + } + + return (idx == VHOST_USER_MAX) ? VHOST_USER_NONE : idx; +} + +static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) +{ + CharDriverState *chr = dev->opaque; + uint8_t *p = (uint8_t *) msg; + int r, size = VHOST_USER_HDR_SIZE; + + r = qemu_chr_fe_read_all(chr, p, size); + if (r != size) { + error_report("Failed to read msg header. Read %d instead of %d.\n", r, + size); + goto fail; + } + + /* validate received flags */ + if (msg->flags != (VHOST_USER_REPLY_MASK | VHOST_USER_VERSION)) { + error_report("Failed to read msg header." + " Flags 0x%x instead of 0x%x.\n", msg->flags, + VHOST_USER_REPLY_MASK | VHOST_USER_VERSION); + goto fail; + } + + /* validate message size is sane */ + if (msg->size > VHOST_USER_PAYLOAD_SIZE) { + error_report("Failed to read msg header." + " Size %d exceeds the maximum %zu.\n", msg->size, + VHOST_USER_PAYLOAD_SIZE); + goto fail; + } + + if (msg->size) { + p += VHOST_USER_HDR_SIZE; + size = msg->size; + r = qemu_chr_fe_read_all(chr, p, size); + if (r != size) { + error_report("Failed to read msg payload." + " Read %d instead of %d.\n", r, msg->size); + goto fail; + } + } + + return 0; + +fail: + return -1; +} + +static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg, + int *fds, int fd_num) +{ + CharDriverState *chr = dev->opaque; + int size = VHOST_USER_HDR_SIZE + msg->size; + + if (fd_num) { + qemu_chr_fe_set_msgfds(chr, fds, fd_num); + } + + return qemu_chr_fe_write_all(chr, (const uint8_t *) msg, size) == size ? + 0 : -1; +} + +static int vhost_user_call(struct vhost_dev *dev, unsigned long int request, + void *arg) +{ + VhostUserMsg msg; + VhostUserRequest msg_request; + RAMBlock *block = 0; + struct vhost_vring_file *file = 0; + int need_reply = 0; + int fds[VHOST_MEMORY_MAX_NREGIONS]; + size_t fd_num = 0; + + assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); + + msg_request = vhost_user_request_translate(request); + msg.request = msg_request; + msg.flags = VHOST_USER_VERSION; + msg.size = 0; + + switch (request) { + case VHOST_GET_FEATURES: + need_reply = 1; + break; + + case VHOST_SET_FEATURES: + case VHOST_SET_LOG_BASE: + msg.u64 = *((__u64 *) arg); + msg.size = sizeof(m.u64); + break; + + case VHOST_SET_OWNER: + case VHOST_RESET_OWNER: + break; + + case VHOST_SET_MEM_TABLE: + QTAILQ_FOREACH(block, &ram_list.blocks, next) + { + if (block->fd > 0) { + msg.memory.regions[fd_num].userspace_addr = + (uintptr_t) block->host; + msg.memory.regions[fd_num].memory_size = block->length; + msg.memory.regions[fd_num].guest_phys_addr = block->offset; + fds[fd_num++] = block->fd; + } + } + + msg.memory.nregions = fd_num; + + if (!fd_num) { + error_report("Failed initializing vhost-user memory map\n" + "consider using -object memory-backend-file share=on\n"); + return -1; + } + + msg.size = sizeof(m.memory.nregions); + msg.size += sizeof(m.memory.padding); + msg.size += fd_num * sizeof(VhostUserMemoryRegion); + + break; + + case VHOST_SET_LOG_FD: + fds[fd_num++] = *((int *) arg); + break; + + case VHOST_SET_VRING_NUM: + case VHOST_SET_VRING_BASE: + memcpy(&msg.state, arg, sizeof(struct vhost_vring_state)); + msg.size = sizeof(m.state); + break; + + case VHOST_GET_VRING_BASE: + memcpy(&msg.state, arg, sizeof(struct vhost_vring_state)); + msg.size = sizeof(m.state); + need_reply = 1; + break; + + case VHOST_SET_VRING_ADDR: + memcpy(&msg.addr, arg, sizeof(struct vhost_vring_addr)); + msg.size = sizeof(m.addr); + break; + + case VHOST_SET_VRING_KICK: + case VHOST_SET_VRING_CALL: + case VHOST_SET_VRING_ERR: + file = arg; + msg.u64 = file->index & VHOST_USER_VRING_IDX_MASK; + msg.size = sizeof(m.u64); + if (ioeventfd_enabled() && file->fd > 0) { + fds[fd_num++] = file->fd; + } else { + msg.u64 |= VHOST_USER_VRING_NOFD_MASK; + } + break; + default: + error_report("vhost-user trying to send unhandled ioctl\n"); + return -1; + break; + } + + if (vhost_user_write(dev, &msg, fds, fd_num) < 0) { + return 0; + } + + if (need_reply) { + if (vhost_user_read(dev, &msg) < 0) { + return 0; + } + + if (msg_request != msg.request) { + error_report("Received unexpected msg type." + " Expected %d received %d\n", msg_request, msg.request); + return -1; + } + + switch (msg_request) { + case VHOST_USER_GET_FEATURES: + if (msg.size != sizeof(m.u64)) { + error_report("Received bad msg size.\n"); + return -1; + } + *((__u64 *) arg) = msg.u64; + break; + case VHOST_USER_GET_VRING_BASE: + if (msg.size != sizeof(m.state)) { + error_report("Received bad msg size.\n"); + return -1; + } + memcpy(arg, &msg.state, sizeof(struct vhost_vring_state)); + break; + default: + error_report("Received unexpected msg type.\n"); + return -1; + break; + } + } + + return 0; +} + +static int vhost_user_init(struct vhost_dev *dev, void *opaque) +{ + assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); + + dev->opaque = opaque; + + return 0; +} + +static int vhost_user_cleanup(struct vhost_dev *dev) +{ + assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); + + dev->opaque = 0; + + return 0; +} + +const VhostOps user_ops = { + .backend_type = VHOST_BACKEND_TYPE_USER, + .vhost_call = vhost_user_call, + .vhost_backend_init = vhost_user_init, + .vhost_backend_cleanup = vhost_user_cleanup + }; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index f62cfaf..c1b1aad 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -13,7 +13,6 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include <sys/ioctl.h> #include "hw/virtio/vhost.h" #include "hw/hw.h" #include "qemu/atomic.h" @@ -289,15 +288,13 @@ static inline void vhost_dev_log_resize(struct vhost_dev* dev, uint64_t size) log = g_malloc0(size * sizeof *log); log_base = (uint64_t)(unsigned long)log; - r = ioctl(dev->control, VHOST_SET_LOG_BASE, &log_base); + r = dev->vhost_ops->vhost_call(dev, VHOST_SET_LOG_BASE, &log_base); assert(r >= 0); /* Sync only the range covered by the old log */ if (dev->log_size) { vhost_log_sync_range(dev, 0, dev->log_size * VHOST_LOG_CHUNK - 1); } - if (dev->log) { - g_free(dev->log); - } + g_free(dev->log); dev->log = log; dev->log_size = size; } @@ -458,7 +455,7 @@ static void vhost_commit(MemoryListener *listener) } if (!dev->log_enabled) { - r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem); + r = dev->vhost_ops->vhost_call(dev, VHOST_SET_MEM_TABLE, dev->mem); assert(r >= 0); dev->memory_changed = false; return; @@ -471,7 +468,7 @@ static void vhost_commit(MemoryListener *listener) if (dev->log_size < log_size) { vhost_dev_log_resize(dev, log_size + VHOST_LOG_BUFFER); } - r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem); + r = dev->vhost_ops->vhost_call(dev, VHOST_SET_MEM_TABLE, dev->mem); assert(r >= 0); /* To log less, can only decrease log size after table update. */ if (dev->log_size > log_size + VHOST_LOG_BUFFER) { @@ -539,7 +536,7 @@ static int vhost_virtqueue_set_addr(struct vhost_dev *dev, .log_guest_addr = vq->used_phys, .flags = enable_log ? (1 << VHOST_VRING_F_LOG) : 0, }; - int r = ioctl(dev->control, VHOST_SET_VRING_ADDR, &addr); + int r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_ADDR, &addr); if (r < 0) { return -errno; } @@ -553,7 +550,7 @@ static int vhost_dev_set_features(struct vhost_dev *dev, bool enable_log) if (enable_log) { features |= 0x1 << VHOST_F_LOG_ALL; } - r = ioctl(dev->control, VHOST_SET_FEATURES, &features); + r = dev->vhost_ops->vhost_call(dev, VHOST_SET_FEATURES, &features); return r < 0 ? -errno : 0; } @@ -601,9 +598,7 @@ static int vhost_migration_log(MemoryListener *listener, int enable) if (r < 0) { return r; } - if (dev->log) { - g_free(dev->log); - } + g_free(dev->log); dev->log = NULL; dev->log_size = 0; } else { @@ -668,13 +663,13 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); vq->num = state.num = virtio_queue_get_num(vdev, idx); - r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state); + r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_NUM, &state); if (r) { return -errno; } state.num = virtio_queue_get_last_avail_idx(vdev, idx); - r = ioctl(dev->control, VHOST_SET_VRING_BASE, &state); + r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_BASE, &state); if (r) { return -errno; } @@ -716,7 +711,7 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, } file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq)); - r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file); + r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_KICK, &file); if (r) { r = -errno; goto fail_kick; @@ -754,7 +749,7 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev, }; int r; assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); - r = ioctl(dev->control, VHOST_GET_VRING_BASE, &state); + r = dev->vhost_ops->vhost_call(dev, VHOST_GET_VRING_BASE, &state); if (r < 0) { fprintf(stderr, "vhost VQ %d ring restore failed: %d\n", idx, r); fflush(stderr); @@ -796,7 +791,7 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, } file.fd = event_notifier_get_fd(&vq->masked_notifier); - r = ioctl(dev->control, VHOST_SET_VRING_CALL, &file); + r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_CALL, &file); if (r) { r = -errno; goto fail_call; @@ -812,25 +807,26 @@ static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq) event_notifier_cleanup(&vq->masked_notifier); } -int vhost_dev_init(struct vhost_dev *hdev, int devfd, const char *devpath, - bool force) +int vhost_dev_init(struct vhost_dev *hdev, void *opaque, + VhostBackendType backend_type, bool force) { uint64_t features; int i, r; - if (devfd >= 0) { - hdev->control = devfd; - } else { - hdev->control = open(devpath, O_RDWR); - if (hdev->control < 0) { - return -errno; - } + + if (vhost_set_backend_type(hdev, backend_type) < 0) { + return -1; } - r = ioctl(hdev->control, VHOST_SET_OWNER, NULL); + + if (hdev->vhost_ops->vhost_backend_init(hdev, opaque) < 0) { + return -errno; + } + + r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_OWNER, NULL); if (r < 0) { goto fail; } - r = ioctl(hdev->control, VHOST_GET_FEATURES, &features); + r = hdev->vhost_ops->vhost_call(hdev, VHOST_GET_FEATURES, &features); if (r < 0) { goto fail; } @@ -875,7 +871,7 @@ fail_vq: } fail: r = -errno; - close(hdev->control); + hdev->vhost_ops->vhost_backend_cleanup(hdev); return r; } @@ -888,7 +884,7 @@ void vhost_dev_cleanup(struct vhost_dev *hdev) memory_listener_unregister(&hdev->memory_listener); g_free(hdev->mem); g_free(hdev->mem_sections); - close(hdev->control); + hdev->vhost_ops->vhost_backend_cleanup(hdev); } bool vhost_dev_query(struct vhost_dev *hdev, VirtIODevice *vdev) @@ -990,10 +986,37 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, } else { file.fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vvq)); } - r = ioctl(hdev->control, VHOST_SET_VRING_CALL, &file); + r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_VRING_CALL, &file); assert(r >= 0); } +unsigned vhost_get_features(struct vhost_dev *hdev, const int *feature_bits, + unsigned features) +{ + const int *bit = feature_bits; + while (*bit != VHOST_INVALID_FEATURE_BIT) { + unsigned bit_mask = (1 << *bit); + if (!(hdev->features & bit_mask)) { + features &= ~bit_mask; + } + bit++; + } + return features; +} + +void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits, + unsigned features) +{ + const int *bit = feature_bits; + while (*bit != VHOST_INVALID_FEATURE_BIT) { + unsigned bit_mask = (1 << *bit); + if (features & bit_mask) { + hdev->acked_features |= bit_mask; + } + bit++; + } +} + /* Host notifiers must be enabled at this point. */ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) { @@ -1005,7 +1028,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) if (r < 0) { goto fail_features; } - r = ioctl(hdev->control, VHOST_SET_MEM_TABLE, hdev->mem); + r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_MEM_TABLE, hdev->mem); if (r < 0) { r = -errno; goto fail_mem; @@ -1024,8 +1047,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) hdev->log_size = vhost_get_log_size(hdev); hdev->log = hdev->log_size ? g_malloc0(hdev->log_size * sizeof *hdev->log) : NULL; - r = ioctl(hdev->control, VHOST_SET_LOG_BASE, - (uint64_t)(unsigned long)hdev->log); + r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_LOG_BASE, hdev->log); if (r < 0) { r = -errno; goto fail_log; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index a07ae8a..a3082d5 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -1164,14 +1164,8 @@ EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq) void virtio_device_set_child_bus_name(VirtIODevice *vdev, char *bus_name) { - if (vdev->bus_name) { - g_free(vdev->bus_name); - vdev->bus_name = NULL; - } - - if (bus_name) { - vdev->bus_name = g_strdup(bus_name); - } + g_free(vdev->bus_name); + vdev->bus_name = g_strdup(bus_name); } static void virtio_device_realize(DeviceState *dev, Error **errp) @@ -1206,10 +1200,8 @@ static void virtio_device_unrealize(DeviceState *dev, Error **errp) } } - if (vdev->bus_name) { - g_free(vdev->bus_name); - vdev->bus_name = NULL; - } + g_free(vdev->bus_name); + vdev->bus_name = NULL; } static void virtio_device_class_init(ObjectClass *klass, void *data) |