commit 11c765848af6cb345a81f2722f141b305538182d Author: Paul Brook Date: Mon May 4 17:13:08 2009 +0100 Basic qdev infrastructure. Signed-off-by: Paul Brook diff --git a/Makefile.target b/Makefile.target index f735105..a35e724 100644 --- a/Makefile.target +++ b/Makefile.target @@ -498,6 +498,7 @@ endif #CONFIG_BSD_USER # System emulator target ifndef CONFIG_USER_ONLY +DEVICES= OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o dma-helpers.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly @@ -686,6 +687,13 @@ ifeq ($(TARGET_BASE_ARCH), m68k) OBJS+= an5206.o mcf5206.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o OBJS+= m68k-semi.o dummy_m68k.o endif + +DEVICE_OBJS=$(addsuffix .o,$(DEVICES)) +$(DEVICE_OBJS): CPPFLAGS+=-DDEVICE_NAME=$(subst -,_,$(@:.o=)) + +OBJS+=$(DEVICE_OBJS) +OBJS+=devices.o qdev.o + ifdef CONFIG_GDBSTUB OBJS+=gdbstub.o gdbstub-xml.o endif @@ -752,6 +760,9 @@ endif qemu-options.h: $(SRC_PATH)/qemu-options.hx $(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@") +devices.c: gen_devices.sh Makefile.target config.mak + $(call quiet-command,$(SHELL) $(SRC_PATH)/gen_devices.sh $@ $(subst -,_,$(DEVICES))," GEN $(TARGET_DIR)$@") + clean: rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o qemu-options.h gdbstub-xml.c rm -f *.d */*.d tcg/*.o diff --git a/gen_devices.sh b/gen_devices.sh new file mode 100644 index 0000000..1d6ec12 --- /dev/null +++ b/gen_devices.sh @@ -0,0 +1,17 @@ +#! /bin/sh +# Call device init functions. + +file="$1" +shift +devices="$@" +echo '/* Generated by gen_devices.sh */' > $file +echo '#include "sysemu.h"' >> $file +for x in $devices ; do + echo "void ${x}_register(void);" >> $file +done +echo "void register_devices(void)" >> $file +echo "{" >> $file +for x in $devices ; do + echo " ${x}_register();" >> $file +done +echo "}" >> $file diff --git a/hw/qdev.c b/hw/qdev.c new file mode 100644 index 0000000..eb8c172 --- /dev/null +++ b/hw/qdev.c @@ -0,0 +1,294 @@ +/* + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "hw.h" +#include "qdev.h" +#include "sysemu.h" + +#include + +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(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; + 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); +} + +void qdev_connect_irq(DeviceState *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 qdev_mmio_map(DeviceState *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); + } +} + +void qdev_set_bus(DeviceState *dev, void *bus) +{ + assert(!dev->bus); + dev->bus = bus; +} + +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]; +} + +void qdev_init_mmio(DeviceState *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 qdev_init_mmio_cb(DeviceState *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; +} + +/* Request an IRQ source. The actual IRQ object may be populated later. */ +void qdev_init_irq(DeviceState *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 qdev_pass_irq(DeviceState *dev, DeviceState *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]; + } +} + +/* 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; + 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; + } + } + /* TODO: When this comes from a config file we will need to handle + missing properties gracefully. */ + abort(); +} + +int qdev_get_prop_int(DeviceState *dev, const char *name) +{ + DeviceProperty *prop; + + prop = find_prop(dev, name); + return prop->value.i; +} + +void *qdev_get_prop_ptr(DeviceState *dev, const char *name) +{ + DeviceProperty *prop; + + prop = find_prop(dev, name); + return prop->value.ptr; +} + +DeviceState *qdev_create_varargs(const char *name, + target_phys_addr_t addr, ...) +{ + DeviceState *dev; + va_list va; + qemu_irq irq; + int n; + + dev = qdev_create(name); + qdev_init(dev); + if (addr != (target_phys_addr_t)-1) { + qdev_mmio_map(dev, 0, addr); + } + va_start(va, addr); + n = 0; + while (1) { + irq = va_arg(va, qemu_irq); + if (!irq) { + break; + } + qdev_connect_irq(dev, n, irq); + n++; + } + return dev; +} diff --git a/hw/qdev.h b/hw/qdev.h new file mode 100644 index 0000000..08b2713 --- /dev/null +++ b/hw/qdev.h @@ -0,0 +1,89 @@ +#ifndef QDEV_H +#define QDEV_H + +#include "irq.h" + +typedef struct DeviceType DeviceType; + +#define QDEV_MAX_MMIO 5 +#define QDEV_MAX_IRQ 32 + +typedef struct DeviceProperty DeviceProperty; + +typedef void (*mmio_mapfunc)(DeviceState *dev, target_phys_addr_t addr); + +/* 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; + int num_irq; + qemu_irq irqs[QDEV_MAX_IRQ]; + qemu_irq *irqp[QDEV_MAX_IRQ]; + int num_irq_sink; + qemu_irq *irq_sink; + int num_mmio; + struct { + target_phys_addr_t addr; + target_phys_addr_t size; + mmio_mapfunc cb; + int iofunc; + } mmio[QDEV_MAX_MMIO]; + DeviceProperty *props; +}; + + +/*** Board API. This should go away once we have a machine config file. ***/ + +DeviceState *qdev_create(const char *name); +void qdev_init(DeviceState *dev); + +/* Set properties between creation and init. */ +void qdev_set_bus(DeviceState *dev, void *bus); +void qdev_set_prop_int(DeviceState *dev, const char *name, int value); +void qdev_set_prop_ptr(DeviceState *dev, const char *name, void *value); + +/* Configure device after init. */ +void qdev_connect_irq(DeviceState *dev, int n, qemu_irq irq); +void qdev_mmio_map(DeviceState *dev, int n, target_phys_addr_t addr); +qemu_irq qdev_get_irq_sink(DeviceState *dev, int n); + + +/*** 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_mmio(DeviceState *dev, target_phys_addr_t size, int iofunc); +void qdev_init_mmio_cb(DeviceState *dev, target_phys_addr_t size, + mmio_mapfunc cb); +void qdev_init_irq(DeviceState *dev, qemu_irq *p); +void qdev_pass_irq(DeviceState *dev, DeviceState *target); +void qdev_init_irq_sink(DeviceState *dev, qemu_irq_handler handler, int nirq); + +CharDriverState *qdev_init_chardev(DeviceState *dev); + +void *qdev_get_bus(DeviceState *dev); +int qdev_get_prop_int(DeviceState *dev, const char *name); +void *qdev_get_prop_ptr(DeviceState *dev, const char *name); + +/* Helper function for creating devices with no special properties. */ +DeviceState *qdev_create_varargs(const char *name, + target_phys_addr_t addr, ...); +static inline DeviceState *qdev_create_simple(const char *name, + target_phys_addr_t addr, + qemu_irq irq) +{ + return qdev_create_varargs(name, addr, irq, NULL); +} + +#ifdef DEVICE_NAME +void glue(DEVICE_NAME, _register)(void); +#endif + +#endif diff --git a/qemu-common.h b/qemu-common.h index c90c3e3..e792e94 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -178,6 +178,7 @@ typedef struct PCIDevice PCIDevice; typedef struct SerialState SerialState; typedef struct IRQState *qemu_irq; struct pcmcia_card_s; +typedef struct DeviceState DeviceState; /* CPU save/load. */ void cpu_save(QEMUFile *f, void *opaque); diff --git a/sysemu.h b/sysemu.h index 9bb9fbc..24c9fe6 100644 --- a/sysemu.h +++ b/sysemu.h @@ -259,4 +259,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 867111c..b21ea19 100644 --- a/vl.c +++ b/vl.c @@ -5904,6 +5904,8 @@ int main(int argc, char **argv, char **envp) } } + register_devices(); + machine->init(ram_size, vga_ram_size, boot_devices, kernel_filename, kernel_cmdline, initrd_filename, cpu_model);