These functions are used to attach devices that are passed in via the command line using the -device argument.
The reason for using three functions is to allow a multi-pass approach. This can then be extended to allow devices to connect to other devices that are being connected via the command line (for example DMA/ethernet AXI-Stream). Signed-off-by: Alistair Francis <alistair.fran...@xilinx.com> --- include/hw/boards.h | 2 + include/monitor/qdev.h | 3 + include/qemu/option_int.h | 1 + qdev-monitor.c | 237 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 241 insertions(+), 2 deletions(-) diff --git a/include/hw/boards.h b/include/hw/boards.h index dd2c70d..d06bf86 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -15,6 +15,8 @@ typedef struct QEMUMachineInitArgs { const char *kernel_cmdline; const char *initrd_filename; const char *cpu_model; + DeviceState *intc; + Object *cpu; } QEMUMachineInitArgs; typedef void QEMUMachineInitFunc(QEMUMachineInitArgs *args); diff --git a/include/monitor/qdev.h b/include/monitor/qdev.h index 8d16e11..18a5b3d 100644 --- a/include/monitor/qdev.h +++ b/include/monitor/qdev.h @@ -11,5 +11,8 @@ void do_info_qdm(Monitor *mon, const QDict *qdict); int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data); int qdev_device_help(QemuOpts *opts); DeviceState *qdev_device_add(QemuOpts *opts); +DeviceState *qdev_device_create(QemuOpts *opts); +DeviceState *qdev_device_init(QemuOpts *opts, DeviceState* intc); +DeviceState *qdev_device_connect(QemuOpts *opts, DeviceState* intc); #endif diff --git a/include/qemu/option_int.h b/include/qemu/option_int.h index 8212fa4..fdab055 100644 --- a/include/qemu/option_int.h +++ b/include/qemu/option_int.h @@ -47,6 +47,7 @@ struct QemuOpts { char *id; QemuOptsList *list; Location loc; + void *opaque; QTAILQ_HEAD(QemuOptHead, QemuOpt) head; QTAILQ_ENTRY(QemuOpts) next; }; diff --git a/qdev-monitor.c b/qdev-monitor.c index 9268c87..5920cb6 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -24,6 +24,8 @@ #include "qmp-commands.h" #include "sysemu/arch_init.h" #include "qemu/config-file.h" +#include "exec/address-spaces.h" +#include "qemu/option_int.h" /* * Aliases were a bad idea from the start. Let's keep them @@ -148,10 +150,27 @@ static int set_property(const char *name, const char *value, void *opaque) Object *obj = opaque; Error *err = NULL; - if (strcmp(name, "driver") == 0) + if (strcmp(name, "driver") == 0) { return 0; - if (strcmp(name, "bus") == 0) + } + if (strcmp(name, "bus") == 0) { + return 0; + } + if (strcmp(name, "addr") == 0) { + return 0; + } + if (strcmp(name, "irq") == 0) { + return 0; + } + if (strcmp(name, "model") == 0) { + return 0; + } + if (strcmp(name, "name") == 0) { return 0; + } + if (strcmp(name, "type") == 0) { + return 0; + } object_property_parse(obj, value, name, &err); if (err != NULL) { @@ -567,6 +586,220 @@ DeviceState *qdev_device_add(QemuOpts *opts) } +DeviceState *qdev_device_create(QemuOpts *opts) +{ + ObjectClass *oc; + DeviceClass *dc; + const char *driver, *path; + DeviceState *dev = NULL; + BusState *bus = NULL; + + hwaddr addr; + MemoryRegion *ram; + MemoryRegion *address_space_mem = get_system_memory();; + uint64_t ram_size; + + Object *cpu; + ObjectClass *cpu_oc; + Error *err = NULL; + + driver = qemu_opt_get(opts, "driver"); + if (!driver) { + qerror_report(QERR_MISSING_PARAMETER, "driver"); + exit(1); + } + + if (!strcmp(driver, "cpu")) { + /* The device being added is a cpu */ + if (qemu_opt_get(opts, "name")) { + cpu = object_new(qemu_opt_get(opts, "name")); + } else { + cpu_oc = cpu_class_by_name(qemu_opt_get(opts, "type"), + qemu_opt_get(opts, "model")); + cpu = object_new(object_class_get_name(cpu_oc)); + } + + /* Set Properties */ + qemu_opt_foreach(opts, set_property, cpu, 1); + + object_property_set_bool(cpu, true, "realized", &err); + if (err) { + error_report("%s", error_get_pretty(err)); + exit(1); + } + + return NULL; + } + + if (!strcmp(driver, "memory")) { + /* The device being added is a memory controller */ + ram = g_new(MemoryRegion, 1); + + sscanf(qemu_opt_get(opts, "size"), "%X", (uint *) &ram_size); + sscanf(qemu_opt_get(opts, "addr"), "%X", (uint *) &addr); + + memory_region_init_ram(ram, NULL, qemu_opt_get(opts, "name"), + (uint) ram_size); + vmstate_register_ram_global(ram); + memory_region_add_subregion(address_space_mem, (uint) addr, ram); + + return NULL; + } + + /* find the driver */ + oc = object_class_by_name(driver); + if (!oc) { + const char *typename = find_typename_by_alias(driver); + + if (typename) { + driver = typename; + oc = object_class_by_name(driver); + } + } + + if (!object_class_dynamic_cast(oc, TYPE_DEVICE)) { + qerror_report(ERROR_CLASS_GENERIC_ERROR, + "'%s' is not a valid device model name", driver); + exit(1); + } + + if (object_class_is_abstract(oc)) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "driver", + "non-abstract device type"); + exit(1); + } + + dc = DEVICE_CLASS(oc); + if (dc->cannot_instantiate_with_device_add_yet) { + /* Find the Bus */ + path = qemu_opt_get(opts, "bus"); + if (path != NULL) { + bus = qbus_find(path); + if (!bus) { + exit(1); + } + if (!object_dynamic_cast(OBJECT(bus), dc->bus_type)) { + qerror_report(QERR_BAD_BUS_FOR_DEVICE, + driver, object_get_typename(OBJECT(bus))); + exit(1); + } + } else if (dc->bus_type != NULL) { + bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type); + if (!bus) { + qerror_report(QERR_NO_BUS_FOR_DEVICE, + dc->bus_type, driver); + exit(1); + } + } + if (qdev_hotplug && bus && !bus->allow_hotplug) { + qerror_report(QERR_BUS_NO_HOTPLUG, bus->name); + exit(1); + } + + /* create driver */ + dev = DEVICE(object_new(driver)); + + if (bus) { + qdev_set_parent_bus(dev, bus); + } + } else { + /* Init the device later */ + dev = NULL; + } + + opts->opaque = (void *) dev; + return dev; +} + +DeviceState *qdev_device_init(QemuOpts *opts, DeviceState* intc) +{ + DeviceClass *dc; + const char *driver; + DeviceState *dev = NULL; + + driver = qemu_opt_get(opts, "driver"); + if (!driver) { + qerror_report(QERR_MISSING_PARAMETER, "driver"); + exit(1); + } + + dev = (DeviceState *) opts->opaque; + + if (!dev) { + return NULL; + } + + dc = DEVICE_CLASS(object_class_by_name(driver)); + if (!dc->cannot_instantiate_with_device_add_yet) { + return NULL; + } + + /* Set Properties */ + if (qemu_opt_foreach(opts, set_property, dev, 1) != 0) { + object_unparent(OBJECT(dev)); + object_unref(OBJECT(dev)); + exit(1); + } + + qdev_init_nofail(dev); + + return dev; +} + +DeviceState *qdev_device_connect(QemuOpts *opts, DeviceState* intc) +{ + DeviceClass *dc; + const char *driver; + DeviceState *dev = NULL; + int irq, i, n = 0; + hwaddr addr; + + driver = qemu_opt_get(opts, "driver"); + if (!driver) { + qerror_report(QERR_MISSING_PARAMETER, "driver"); + exit(1); + } + + dev = (DeviceState *) opts->opaque; + + if (!dev) { + return NULL; + } + + dc = DEVICE_CLASS(object_class_by_name(driver)); + if (!dc->cannot_instantiate_with_device_add_yet) { + return NULL; + } + + if (qemu_opt_get(opts, "addr")) { + sscanf(qemu_opt_get(opts, "addr"), "%X", (uint *) &addr); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, (uint) addr); + } + + if (qemu_opt_get(opts, "irq") && intc) { + for (i = 0; ; i++) { + if (!qemu_opt_name_step(opts, i)) { + break; + } + if (strcmp(qemu_opt_name_step(opts, i), "irq")) { + continue; + } + + sscanf(qemu_opt_step(opts, i), "%d", &irq); + + sysbus_connect_irq(SYS_BUS_DEVICE(dev), n, + qdev_get_gpio_in(intc, irq)); + + n++; + } + } else if (first_cpu && dev->num_gpio_in > 0) { + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, + qdev_get_gpio_in(DEVICE(first_cpu), 0)); + } + + return dev; +} + #define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__) static void qbus_print(Monitor *mon, BusState *bus, int indent); -- 1.7.1