#include "qapi/qmp/dispatch.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qerror.h"
+#include "qapi/qmp/qstring.h"
+#include "qapi/qobject-input-visitor.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
#include "qemu/help_option.h"
#include "qemu/qemu-print.h"
#include "qemu/option_int.h"
#include "sysemu/block-backend.h"
-#include "sysemu/sysemu.h"
#include "migration/misc.h"
#include "migration/migration.h"
#include "qemu/cutils.h"
#include "hw/qdev-properties.h"
#include "hw/clock.h"
+#include "hw/boards.h"
/*
* Aliases were a bad idea from the start. Let's keep them
uint32_t arch_mask;
} QDevAlias;
+/* default virtio transport per architecture */
+#define QEMU_ARCH_VIRTIO_PCI (QEMU_ARCH_ALPHA | QEMU_ARCH_ARM | \
+ QEMU_ARCH_HPPA | QEMU_ARCH_I386 | \
+ QEMU_ARCH_MIPS | QEMU_ARCH_PPC | \
+ QEMU_ARCH_RISCV | QEMU_ARCH_SH4 | \
+ QEMU_ARCH_SPARC | QEMU_ARCH_XTENSA)
+#define QEMU_ARCH_VIRTIO_CCW (QEMU_ARCH_S390X)
+#define QEMU_ARCH_VIRTIO_MMIO (QEMU_ARCH_M68K)
+
/* Please keep this table sorted by typename. */
static const QDevAlias qdev_alias_table[] = {
{ "AC97", "ac97" }, /* -soundhw name */
{ "ES1370", "es1370" }, /* -soundhw name */
{ "ich9-ahci", "ahci" },
{ "lsi53c895a", "lsi" },
- { "virtio-9p-ccw", "virtio-9p", QEMU_ARCH_S390X },
- { "virtio-9p-pci", "virtio-9p", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-balloon-ccw", "virtio-balloon", QEMU_ARCH_S390X },
- { "virtio-balloon-pci", "virtio-balloon",
- QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-blk-ccw", "virtio-blk", QEMU_ARCH_S390X },
- { "virtio-blk-pci", "virtio-blk", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-gpu-ccw", "virtio-gpu", QEMU_ARCH_S390X },
- { "virtio-gpu-pci", "virtio-gpu", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-input-host-ccw", "virtio-input-host", QEMU_ARCH_S390X },
- { "virtio-input-host-pci", "virtio-input-host",
- QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-iommu-pci", "virtio-iommu", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-keyboard-ccw", "virtio-keyboard", QEMU_ARCH_S390X },
- { "virtio-keyboard-pci", "virtio-keyboard",
- QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-mouse-ccw", "virtio-mouse", QEMU_ARCH_S390X },
- { "virtio-mouse-pci", "virtio-mouse", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-net-ccw", "virtio-net", QEMU_ARCH_S390X },
- { "virtio-net-pci", "virtio-net", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-rng-ccw", "virtio-rng", QEMU_ARCH_S390X },
- { "virtio-rng-pci", "virtio-rng", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-scsi-ccw", "virtio-scsi", QEMU_ARCH_S390X },
- { "virtio-scsi-pci", "virtio-scsi", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_S390X },
- { "virtio-serial-pci", "virtio-serial", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-tablet-ccw", "virtio-tablet", QEMU_ARCH_S390X },
- { "virtio-tablet-pci", "virtio-tablet", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
+ { "virtio-9p-device", "virtio-9p", QEMU_ARCH_VIRTIO_MMIO },
+ { "virtio-9p-ccw", "virtio-9p", QEMU_ARCH_VIRTIO_CCW },
+ { "virtio-9p-pci", "virtio-9p", QEMU_ARCH_VIRTIO_PCI },
+ { "virtio-balloon-device", "virtio-balloon", QEMU_ARCH_VIRTIO_MMIO },
+ { "virtio-balloon-ccw", "virtio-balloon", QEMU_ARCH_VIRTIO_CCW },
+ { "virtio-balloon-pci", "virtio-balloon", QEMU_ARCH_VIRTIO_PCI },
+ { "virtio-blk-device", "virtio-blk", QEMU_ARCH_VIRTIO_MMIO },
+ { "virtio-blk-ccw", "virtio-blk", QEMU_ARCH_VIRTIO_CCW },
+ { "virtio-blk-pci", "virtio-blk", QEMU_ARCH_VIRTIO_PCI },
+ { "virtio-gpu-device", "virtio-gpu", QEMU_ARCH_VIRTIO_MMIO },
+ { "virtio-gpu-ccw", "virtio-gpu", QEMU_ARCH_VIRTIO_CCW },
+ { "virtio-gpu-pci", "virtio-gpu", QEMU_ARCH_VIRTIO_PCI },
+ { "virtio-input-host-device", "virtio-input-host", QEMU_ARCH_VIRTIO_MMIO },
+ { "virtio-input-host-ccw", "virtio-input-host", QEMU_ARCH_VIRTIO_CCW },
+ { "virtio-input-host-pci", "virtio-input-host", QEMU_ARCH_VIRTIO_PCI },
+ { "virtio-iommu-pci", "virtio-iommu", QEMU_ARCH_VIRTIO_PCI },
+ { "virtio-keyboard-device", "virtio-keyboard", QEMU_ARCH_VIRTIO_MMIO },
+ { "virtio-keyboard-ccw", "virtio-keyboard", QEMU_ARCH_VIRTIO_CCW },
+ { "virtio-keyboard-pci", "virtio-keyboard", QEMU_ARCH_VIRTIO_PCI },
+ { "virtio-mouse-device", "virtio-mouse", QEMU_ARCH_VIRTIO_MMIO },
+ { "virtio-mouse-ccw", "virtio-mouse", QEMU_ARCH_VIRTIO_CCW },
+ { "virtio-mouse-pci", "virtio-mouse", QEMU_ARCH_VIRTIO_PCI },
+ { "virtio-net-device", "virtio-net", QEMU_ARCH_VIRTIO_MMIO },
+ { "virtio-net-ccw", "virtio-net", QEMU_ARCH_VIRTIO_CCW },
+ { "virtio-net-pci", "virtio-net", QEMU_ARCH_VIRTIO_PCI },
+ { "virtio-rng-device", "virtio-rng", QEMU_ARCH_VIRTIO_MMIO },
+ { "virtio-rng-ccw", "virtio-rng", QEMU_ARCH_VIRTIO_CCW },
+ { "virtio-rng-pci", "virtio-rng", QEMU_ARCH_VIRTIO_PCI },
+ { "virtio-scsi-device", "virtio-scsi", QEMU_ARCH_VIRTIO_MMIO },
+ { "virtio-scsi-ccw", "virtio-scsi", QEMU_ARCH_VIRTIO_CCW },
+ { "virtio-scsi-pci", "virtio-scsi", QEMU_ARCH_VIRTIO_PCI },
+ { "virtio-serial-device", "virtio-serial", QEMU_ARCH_VIRTIO_MMIO },
+ { "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_VIRTIO_CCW },
+ { "virtio-serial-pci", "virtio-serial", QEMU_ARCH_VIRTIO_PCI},
+ { "virtio-tablet-device", "virtio-tablet", QEMU_ARCH_VIRTIO_MMIO },
+ { "virtio-tablet-ccw", "virtio-tablet", QEMU_ARCH_VIRTIO_CCW },
+ { "virtio-tablet-pci", "virtio-tablet", QEMU_ARCH_VIRTIO_PCI },
{ }
};
[DEVICE_CATEGORY_SOUND] = "Sound",
[DEVICE_CATEGORY_MISC] = "Misc",
[DEVICE_CATEGORY_CPU] = "CPU",
+ [DEVICE_CATEGORY_WATCHDOG]= "Watchdog",
[DEVICE_CATEGORY_MAX] = "Uncategorized",
};
GSList *list, *elt;
g_slist_free(list);
}
-static int set_property(void *opaque, const char *name, const char *value,
- Error **errp)
-{
- Object *obj = opaque;
-
- if (strcmp(name, "driver") == 0)
- return 0;
- if (strcmp(name, "bus") == 0)
- return 0;
-
- if (!object_property_parse(obj, name, value, errp)) {
- return -1;
- }
- return 0;
-}
-
static const char *find_typename_by_alias(const char *alias)
{
int i;
return NULL;
}
+ if (object_class_dynamic_cast(oc, TYPE_SYS_BUS_DEVICE)) {
+ /* sysbus devices need to be allowed by the machine */
+ MachineClass *mc = MACHINE_CLASS(object_get_class(qdev_get_machine()));
+ if (!device_type_is_dynamic_sysbus(mc, *driver)) {
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
+ "a dynamic sysbus device type for the machine");
+ return NULL;
+ }
+ }
+
return dc;
}
static inline bool qbus_is_full(BusState *bus)
{
- BusClass *bus_class = BUS_GET_CLASS(bus);
+ BusClass *bus_class;
+
+ if (bus->full) {
+ return true;
+ }
+ bus_class = BUS_GET_CLASS(bus);
return bus_class->max_dev && bus->num_children >= bus_class->max_dev;
}
return bus;
}
-void qdev_set_id(DeviceState *dev, const char *id)
+/* Takes ownership of @id, will be freed when deleting the device */
+const char *qdev_set_id(DeviceState *dev, char *id, Error **errp)
{
- if (id) {
- dev->id = id;
- }
+ ObjectProperty *prop;
+
+ assert(!dev->id && !dev->realized);
- if (dev->id) {
- object_property_add_child(qdev_get_peripheral(), dev->id,
- OBJECT(dev));
+ /*
+ * object_property_[try_]add_child() below will assert the device
+ * has no parent
+ */
+ if (id) {
+ prop = object_property_try_add_child(qdev_get_peripheral(), id,
+ OBJECT(dev), NULL);
+ if (prop) {
+ dev->id = id;
+ } else {
+ g_free(id);
+ error_setg(errp, "Duplicate device ID '%s'", id);
+ return NULL;
+ }
} else {
static int anon_count;
gchar *name = g_strdup_printf("device[%d]", anon_count++);
- object_property_add_child(qdev_get_peripheral_anon(), name,
- OBJECT(dev));
+ prop = object_property_add_child(qdev_get_peripheral_anon(), name,
+ OBJECT(dev));
g_free(name);
}
+
+ return prop->name;
}
-DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
+DeviceState *qdev_device_add_from_qdict(const QDict *opts,
+ bool from_json, Error **errp)
{
+ ERRP_GUARD();
DeviceClass *dc;
const char *driver, *path;
+ char *id;
DeviceState *dev = NULL;
BusState *bus = NULL;
- driver = qemu_opt_get(opts, "driver");
+ driver = qdict_get_try_str(opts, "driver");
if (!driver) {
error_setg(errp, QERR_MISSING_PARAMETER, "driver");
return NULL;
}
/* find bus */
- path = qemu_opt_get(opts, "bus");
+ path = qdict_get_try_str(opts, "bus");
if (path != NULL) {
bus = qbus_find(path, errp);
if (!bus) {
}
}
- if (qemu_opt_get(opts, "failover_pair_id")) {
- if (!opts->id) {
- error_setg(errp, "Device with failover_pair_id don't have id");
- return NULL;
- }
- if (qdev_should_hide_device(opts)) {
- if (bus && !qbus_is_hotpluggable(bus)) {
- error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name);
- }
- return NULL;
+ if (qdev_should_hide_device(opts, from_json, errp)) {
+ if (bus && !qbus_is_hotpluggable(bus)) {
+ error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name);
}
+ return NULL;
+ } else if (*errp) {
+ return NULL;
}
if (phase_check(PHASE_MACHINE_READY) && bus && !qbus_is_hotpluggable(bus)) {
}
}
- qdev_set_id(dev, qemu_opts_id(opts));
+ /*
+ * set dev's parent and register its id.
+ * If it fails it means the id is already taken.
+ */
+ id = g_strdup(qdict_get_try_str(opts, "id"));
+ if (!qdev_set_id(dev, id, errp)) {
+ goto err_del_dev;
+ }
/* set properties */
- if (qemu_opt_foreach(opts, set_property, dev, errp)) {
+ dev->opts = qdict_clone_shallow(opts);
+ qdict_del(dev->opts, "driver");
+ qdict_del(dev->opts, "bus");
+ qdict_del(dev->opts, "id");
+
+ object_set_properties_from_keyval(&dev->parent_obj, dev->opts, from_json,
+ errp);
+ if (*errp) {
goto err_del_dev;
}
- dev->opts = opts;
if (!qdev_realize(DEVICE(dev), bus, errp)) {
- dev->opts = NULL;
goto err_del_dev;
}
return dev;
return NULL;
}
+/* Takes ownership of @opts on success */
+DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
+{
+ QDict *qdict = qemu_opts_to_qdict(opts, NULL);
+ DeviceState *ret;
+
+ ret = qdev_device_add_from_qdict(qdict, false, errp);
+ if (ret) {
+ qemu_opts_del(opts);
+ }
+ qobject_unref(qdict);
+ return ret;
+}
#define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__)
static void qbus_print(Monitor *mon, BusState *bus, int indent);
}
}
QLIST_FOREACH(ncl, &dev->clocks, node) {
- qdev_printf("clock-%s%s \"%s\" freq_hz=%e\n",
+ g_autofree char *freq_str = clock_display_freq(ncl->clock);
+ qdev_printf("clock-%s%s \"%s\" freq_hz=%s\n",
ncl->output ? "out" : "in",
ncl->alias ? " (alias)" : "",
- ncl->name,
- CLOCK_PERIOD_TO_HZ(1.0 * clock_get(ncl->clock)));
+ ncl->name, freq_str);
}
class = object_get_class(OBJECT(dev));
do {
static DeviceState *find_device_state(const char *id, Error **errp)
{
- Object *obj;
-
- if (id[0] == '/') {
- obj = object_resolve_path(id, NULL);
- } else {
- char *root_path = object_get_canonical_path(qdev_get_peripheral());
- char *path = g_strdup_printf("%s/%s", root_path, id);
-
- g_free(root_path);
- obj = object_resolve_path_type(path, TYPE_DEVICE, NULL);
- g_free(path);
- }
+ Object *obj = object_resolve_path_at(qdev_get_peripheral(), id);
+ DeviceState *dev;
if (!obj) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
return NULL;
}
- if (!object_dynamic_cast(obj, TYPE_DEVICE)) {
+ dev = (DeviceState *)object_dynamic_cast(obj, TYPE_DEVICE);
+ if (!dev) {
error_setg(errp, "%s is not a hotpluggable device", id);
return NULL;
}
- return DEVICE(obj);
+ return dev;
}
void qdev_unplug(DeviceState *dev, Error **errp)