qemu-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-commits] [COMMIT aae9460] Basic qdev infrastructure.


From: Anthony Liguori
Subject: [Qemu-commits] [COMMIT aae9460] Basic qdev infrastructure.
Date: Thu, 14 May 2009 21:41:24 -0000

From: Paul Brook <address@hidden>

Signed-off-by: Paul Brook <address@hidden>

diff --git a/Makefile b/Makefile
index 22d2006..b25db42 100644
--- a/Makefile
+++ b/Makefile
@@ -101,6 +101,7 @@ OBJS+=bt-hci-csr.o
 OBJS+=buffered_file.o migration.o migration-tcp.o net.o qemu-sockets.o
 OBJS+=qemu-char.o aio.o net-checksum.o savevm.o cache-utils.o
 OBJS+=msmouse.o ps2.o
+OBJS+=qdev.o
 
 ifdef CONFIG_BRLAPI
 OBJS+= baum.o
diff --git a/Makefile.target b/Makefile.target
index 30b159d..6bfd5fb 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -486,7 +486,7 @@ endif #CONFIG_BSD_USER
 ifndef CONFIG_USER_ONLY
 
 OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o dma-helpers.o \
-     gdbstub.o gdbstub-xml.o
+     gdbstub.o gdbstub-xml.o sysbus.o
 # virtio has to be here due to weird dependency between PCI and virtio-net.
 # need to fix this properly
 OBJS+=virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o
diff --git a/hw/qdev.c b/hw/qdev.c
new file mode 100644
index 0000000..eaa30f4
--- /dev/null
+++ b/hw/qdev.c
@@ -0,0 +1,237 @@
+/*
+ *  Dynamic device configuration and creation.
+ *
+ *  Copyright (c) 2009 CodeSourcery
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ */
+
+/* The theory here is that it should be possible to create a machine without
+   knowledge of specific devices.  Historically board init routines have
+   passed a bunch of arguments to each device, requiring the board know
+   exactly which device it is dealing with.  This file provides an abstract
+   API for device configuration and initialization.  Devices will generally
+   inherit from a particular bus (e.g. PCI or I2C) rather than
+   this API directly.  */
+
+#include "qdev.h"
+#include "sysemu.h"
+
+struct DeviceProperty {
+    const char *name;
+    union {
+        int i;
+        void *ptr;
+    } value;
+    DeviceProperty *next;
+};
+
+struct DeviceType {
+    const char *name;
+    qdev_initfn init;
+    void *opaque;
+    int size;
+    DeviceType *next;
+};
+
+static DeviceType *device_type_list;
+
+/* Register a new device type.  */
+DeviceType *qdev_register(const char *name, int size, qdev_initfn init,
+                          void *opaque)
+{
+    DeviceType *t;
+
+    assert(size >= sizeof(DeviceState));
+
+    t = qemu_mallocz(sizeof(DeviceType));
+    t->next = device_type_list;
+    device_type_list = t;
+    t->name = qemu_strdup(name);
+    t->size = size;
+    t->init = init;
+    t->opaque = opaque;
+
+    return t;
+}
+
+/* Create a new device.  This only initializes the device state structure
+   and allows properties to be set.  qdev_init should be called to
+   initialize the actual device emulation.  */
+DeviceState *qdev_create(void *bus, const char *name)
+{
+    DeviceType *t;
+    DeviceState *dev;
+
+    for (t = device_type_list; t; t = t->next) {
+        if (strcmp(t->name, name) == 0) {
+            break;
+        }
+    }
+    if (!t) {
+        fprintf(stderr, "Unknown device '%s'\n", name);
+        exit(1);
+    }
+
+    dev = qemu_mallocz(t->size);
+    dev->name = name;
+    dev->type = t;
+    dev->bus = bus;
+    return dev;
+}
+
+/* Initialize a device.  Device properties should be set before calling
+   this function.  IRQs and MMIO regions should be connected/mapped after
+   calling this function.  */
+void qdev_init(DeviceState *dev)
+{
+    dev->type->init(dev, dev->type->opaque);
+}
+
+static DeviceProperty *create_prop(DeviceState *dev, const char *name)
+{
+    DeviceProperty *prop;
+
+    /* TODO: Check for duplicate properties.  */
+    prop = qemu_mallocz(sizeof(*prop));
+    prop->name = qemu_strdup(name);
+    prop->next = dev->props;
+    dev->props = prop;
+
+    return prop;
+}
+
+void qdev_set_prop_int(DeviceState *dev, const char *name, int value)
+{
+    DeviceProperty *prop;
+
+    prop = create_prop(dev, name);
+    prop->value.i = value;
+}
+
+void qdev_set_prop_ptr(DeviceState *dev, const char *name, void *value)
+{
+    DeviceProperty *prop;
+
+    prop = create_prop(dev, name);
+    prop->value.ptr = value;
+}
+
+
+qemu_irq qdev_get_irq_sink(DeviceState *dev, int n)
+{
+    assert(n >= 0 && n < dev->num_irq_sink);
+    return dev->irq_sink[n];
+}
+
+/* Register device IRQ sinks.  */
+void qdev_init_irq_sink(DeviceState *dev, qemu_irq_handler handler, int nirq)
+{
+    dev->num_irq_sink = nirq;
+    dev->irq_sink = qemu_allocate_irqs(handler, dev, nirq);
+}
+
+/* Get a character (serial) device interface.  */
+CharDriverState *qdev_init_chardev(DeviceState *dev)
+{
+    static int next_serial;
+    static int next_virtconsole;
+    /* FIXME: This is a nasty hack that needs to go away.  */
+    if (strncmp(dev->name, "virtio", 6) == 0) {
+        return virtcon_hds[next_virtconsole++];
+    } else {
+        return serial_hds[next_serial++];
+    }
+}
+
+void *qdev_get_bus(DeviceState *dev)
+{
+    return dev->bus;
+}
+
+static DeviceProperty *find_prop(DeviceState *dev, const char *name)
+{
+    DeviceProperty *prop;
+
+    for (prop = dev->props; prop; prop = prop->next) {
+        if (strcmp(prop->name, name) == 0) {
+            return prop;
+        }
+    }
+    return NULL;
+}
+
+uint64_t qdev_get_prop_int(DeviceState *dev, const char *name, uint64_t def)
+{
+    DeviceProperty *prop;
+
+    prop = find_prop(dev, name);
+    if (!prop)
+        return def;
+
+    return prop->value.i;
+}
+
+void *qdev_get_prop_ptr(DeviceState *dev, const char *name)
+{
+    DeviceProperty *prop;
+
+    prop = find_prop(dev, name);
+    assert(prop);
+    return prop->value.ptr;
+}
+
+void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
+{
+    assert(dev->num_gpio_in == 0);
+    dev->num_gpio_in = n;
+    dev->gpio_in = qemu_allocate_irqs(handler, dev, n);
+}
+
+void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
+{
+    assert(dev->num_gpio_out == 0);
+    dev->num_gpio_out = n;
+    dev->gpio_out = pins;
+}
+
+qemu_irq qdev_get_gpio_in(DeviceState *dev, int n)
+{
+    assert(n >= 0 && n < dev->num_gpio_in);
+    return dev->gpio_in[n];
+}
+
+void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin)
+{
+    assert(n >= 0 && n < dev->num_gpio_out);
+    dev->gpio_out[n] = pin;
+}
+
+static int next_block_unit[IF_COUNT];
+
+/* Get a block device.  This should only be used for single-drive devices
+   (e.g. SD/Floppy/MTD).  Multi-disk devices (scsi/ide) should use the
+   appropriate bus.  */
+BlockDriverState *qdev_init_bdrv(DeviceState *dev, BlockInterfaceType type)
+{
+    int unit = next_block_unit[type]++;
+    int index;
+
+    index = drive_get_index(type, 0, unit);
+    if (index == -1) {
+        return NULL;
+    }
+    return drives_table[index].bdrv;
+}
diff --git a/hw/qdev.h b/hw/qdev.h
new file mode 100644
index 0000000..6e3e5ec
--- /dev/null
+++ b/hw/qdev.h
@@ -0,0 +1,67 @@
+#ifndef QDEV_H
+#define QDEV_H
+
+#include "hw.h"
+
+typedef struct DeviceType DeviceType;
+
+typedef struct DeviceProperty DeviceProperty;
+
+/* This structure should not be accessed directly.  We declare it here
+   so that it can be embedded in individual device state structures.  */
+struct DeviceState
+{
+    const char *name;
+    DeviceType *type;
+    void *bus;
+    DeviceProperty *props;
+    int num_irq_sink;
+    qemu_irq *irq_sink;
+    int num_gpio_out;
+    qemu_irq *gpio_out;
+    int num_gpio_in;
+    qemu_irq *gpio_in;
+};
+
+/*** Board API.  This should go away once we have a machine config file.  ***/
+
+DeviceState *qdev_create(void *bus, const char *name);
+void qdev_init(DeviceState *dev);
+
+/* Set properties between creation and init.  */
+void qdev_set_prop_int(DeviceState *dev, const char *name, int value);
+void qdev_set_prop_ptr(DeviceState *dev, const char *name, void *value);
+
+qemu_irq qdev_get_irq_sink(DeviceState *dev, int n);
+qemu_irq qdev_get_gpio_in(DeviceState *dev, int n);
+void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin);
+
+/*** Device API.  ***/
+
+typedef void (*qdev_initfn)(DeviceState *dev, void *opaque);
+
+DeviceType *qdev_register(const char *name, int size, qdev_initfn init,
+                          void *opaque);
+
+/* Register device properties.  */
+void qdev_init_irq_sink(DeviceState *dev, qemu_irq_handler handler, int nirq);
+void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n);
+void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n);
+
+CharDriverState *qdev_init_chardev(DeviceState *dev);
+
+void *qdev_get_bus(DeviceState *dev);
+uint64_t qdev_get_prop_int(DeviceState *dev, const char *name, uint64_t def);
+void *qdev_get_prop_ptr(DeviceState *dev, const char *name);
+
+/* Convery from a base type to a parent type, with compile time checking.  */
+#ifdef __GNUC__
+#define DO_UPCAST(type, field, dev) ( __extension__ ( { \
+    char __attribute__((unused)) offset_must_be_zero[ \
+        -offsetof(type, field)]; \
+    container_of(dev, type, field);}))
+#else
+#define DO_UPCAST(type, field, dev) container_of(dev, type, field)
+#endif
+
+#endif
diff --git a/hw/sysbus.c b/hw/sysbus.c
new file mode 100644
index 0000000..e6cb7dd
--- /dev/null
+++ b/hw/sysbus.c
@@ -0,0 +1,139 @@
+/*
+ *  System (CPU) Bus device support code
+ *
+ *  Copyright (c) 2009 CodeSourcery
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+
+void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq)
+{
+    assert(n >= 0 && n < dev->num_irq);
+    dev->irqs[n] = 0;
+    if (dev->irqp[n]) {
+        *dev->irqp[n] = irq;
+    }
+}
+
+void sysbus_mmio_map(SysBusDevice *dev, int n, target_phys_addr_t addr)
+{
+    assert(n >= 0 && n < dev->num_mmio);
+
+    if (dev->mmio[n].addr == addr) {
+        /* ??? region already mapped here.  */
+        return;
+    }
+    if (dev->mmio[n].addr != (target_phys_addr_t)-1) {
+        /* Unregister previous mapping.  */
+        cpu_register_physical_memory(dev->mmio[n].addr, dev->mmio[n].size,
+                                     IO_MEM_UNASSIGNED);
+    }
+    dev->mmio[n].addr = addr;
+    if (dev->mmio[n].cb) {
+        dev->mmio[n].cb(dev, addr);
+    } else {
+        cpu_register_physical_memory(addr, dev->mmio[n].size,
+                                     dev->mmio[n].iofunc);
+    }
+}
+
+
+/* Request an IRQ source.  The actual IRQ object may be populated later.  */
+void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p)
+{
+    int n;
+
+    assert(dev->num_irq < QDEV_MAX_IRQ);
+    n = dev->num_irq++;
+    dev->irqp[n] = p;
+}
+
+/* Pass IRQs from a target device.  */
+void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target)
+{
+    int i;
+    assert(dev->num_irq == 0);
+    dev->num_irq = target->num_irq;
+    for (i = 0; i < dev->num_irq; i++) {
+        dev->irqp[i] = target->irqp[i];
+    }
+}
+
+void sysbus_init_mmio(SysBusDevice *dev, target_phys_addr_t size, int iofunc)
+{
+    int n;
+
+    assert(dev->num_mmio < QDEV_MAX_MMIO);
+    n = dev->num_mmio++;
+    dev->mmio[n].addr = -1;
+    dev->mmio[n].size = size;
+    dev->mmio[n].iofunc = iofunc;
+}
+
+void sysbus_init_mmio_cb(SysBusDevice *dev, target_phys_addr_t size,
+                         mmio_mapfunc cb)
+{
+    int n;
+
+    assert(dev->num_mmio < QDEV_MAX_MMIO);
+    n = dev->num_mmio++;
+    dev->mmio[n].addr = -1;
+    dev->mmio[n].size = size;
+    dev->mmio[n].cb = cb;
+}
+
+static void sysbus_device_init(DeviceState *dev, void *opaque)
+{
+    sysbus_initfn init = (sysbus_initfn)opaque;
+
+    init(sysbus_from_qdev(dev));
+}
+
+void sysbus_register_dev(const char *name, size_t size, sysbus_initfn init)
+{
+    assert(size >= sizeof(SysBusDevice));
+    qdev_register(name, size, sysbus_device_init, init);
+}
+
+DeviceState *sysbus_create_varargs(const char *name,
+                                   target_phys_addr_t addr, ...)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+    va_list va;
+    qemu_irq irq;
+    int n;
+
+    dev = qdev_create(NULL, name);
+    s = sysbus_from_qdev(dev);
+    qdev_init(dev);
+    if (addr != (target_phys_addr_t)-1) {
+        sysbus_mmio_map(s, 0, addr);
+    }
+    va_start(va, addr);
+    n = 0;
+    while (1) {
+        irq = va_arg(va, qemu_irq);
+        if (!irq) {
+            break;
+        }
+        sysbus_connect_irq(s, n, irq);
+        n++;
+    }
+    return dev;
+}
diff --git a/hw/sysbus.h b/hw/sysbus.h
new file mode 100644
index 0000000..44ed792
--- /dev/null
+++ b/hw/sysbus.h
@@ -0,0 +1,56 @@
+#ifndef HW_SYSBUS_H
+#define HW_SYSBUS_H 1
+
+/* Devices attached directly to the main system bus.  */
+
+#include "qdev.h"
+
+#define QDEV_MAX_MMIO 5
+#define QDEV_MAX_IRQ 32
+
+typedef struct SysBusDevice SysBusDevice;
+typedef void (*mmio_mapfunc)(SysBusDevice *dev, target_phys_addr_t addr);
+
+struct SysBusDevice {
+    DeviceState qdev;
+    int num_irq;
+    qemu_irq irqs[QDEV_MAX_IRQ];
+    qemu_irq *irqp[QDEV_MAX_IRQ];
+    int num_mmio;
+    struct {
+        target_phys_addr_t addr;
+        target_phys_addr_t size;
+        mmio_mapfunc cb;
+        int iofunc;
+    } mmio[QDEV_MAX_MMIO];
+};
+
+typedef void (*sysbus_initfn)(SysBusDevice *dev);
+
+/* Macros to compensate for lack of type inheritance in C.  */
+#define sysbus_from_qdev(dev) ((SysBusDevice *)(dev))
+#define FROM_SYSBUS(type, dev) DO_UPCAST(type, busdev, dev)
+
+void sysbus_register_dev(const char *name, size_t size, sysbus_initfn init);
+void *sysbus_new(void);
+void sysbus_init_mmio(SysBusDevice *dev, target_phys_addr_t size, int iofunc);
+void sysbus_init_mmio_cb(SysBusDevice *dev, target_phys_addr_t size,
+                            mmio_mapfunc cb);
+void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p);
+void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target);
+
+
+void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq);
+void sysbus_mmio_map(SysBusDevice *dev, int n, target_phys_addr_t addr);
+
+/* Legacy helper function for creating devices.  */
+DeviceState *sysbus_create_varargs(const char *name,
+                                 target_phys_addr_t addr, ...);
+static inline DeviceState *sysbus_create_simple(const char *name,
+                                              target_phys_addr_t addr,
+                                              qemu_irq irq)
+{
+    return sysbus_create_varargs(name, addr, irq, NULL);
+}
+
+#endif /* !HW_SYSBUS_H */
diff --git a/qemu-common.h b/qemu-common.h
index 5f7f275..9d644b6 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -182,6 +182,7 @@ typedef struct PCMCIACardState PCMCIACardState;
 typedef struct MouseTransformInfo MouseTransformInfo;
 typedef struct uWireSlave uWireSlave;
 typedef struct I2SCodec I2SCodec;
+typedef struct DeviceState DeviceState;
 
 /* CPU save/load.  */
 void cpu_save(QEMUFile *f, void *opaque);
diff --git a/sysemu.h b/sysemu.h
index 25a7c32..2ff6194 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -132,7 +132,8 @@ extern unsigned int nb_prom_envs;
 #endif
 
 typedef enum {
-    IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN
+    IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN,
+    IF_COUNT
 } BlockInterfaceType;
 
 typedef enum {
@@ -165,6 +166,8 @@ extern void drive_remove(int index);
 extern const char *drive_get_serial(BlockDriverState *bdrv);
 extern BlockInterfaceErrorAction drive_get_onerror(BlockDriverState *bdrv);
 
+BlockDriverState *qdev_init_bdrv(DeviceState *dev, BlockInterfaceType type);
+
 struct drive_opt {
     const char *file;
     char opt[1024];
@@ -259,4 +262,6 @@ int get_param_value(char *buf, int buf_size,
                     const char *tag, const char *str);
 int check_params(const char * const *params, const char *str);
 
+void register_devices(void);
+
 #endif
diff --git a/vl.c b/vl.c
index b43cdd1..40b1d8b 100644
--- a/vl.c
+++ b/vl.c
@@ -2565,6 +2565,8 @@ int drive_init(struct drive_opt *arg, int snapshot, void 
*opaque)
     case IF_MTD:
     case IF_VIRTIO:
         break;
+    case IF_COUNT:
+        abort();
     }
     if (!file[0])
         return -2;
@@ -5896,6 +5898,8 @@ int main(int argc, char **argv, char **envp)
         }
     }
 
+    module_call_init(MODULE_INIT_DEVICE);
+
     machine->init(ram_size, boot_devices,
                   kernel_filename, kernel_cmdline, initrd_filename, cpu_model);
 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]