[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH 2/4] Add device tree machine
From: |
Markus Armbruster |
Subject: |
Re: [Qemu-devel] [PATCH 2/4] Add device tree machine |
Date: |
Fri, 12 Jun 2009 18:25:33 +0200 |
User-agent: |
Gnus/5.11 (Gnus v5.11) Emacs/22.3 (gnu/linux) |
Paul Brook <address@hidden> writes:
> FDT based machine creation.
> When -M foo is specified look for and use foo.fdb.
> Build and ship board configs.
>
> Signed-off-by: Paul Brook <address@hidden>
> ---
>
> .gitignore | 1
> Makefile | 20 +-
> Makefile.target | 5
> configure | 17 +
> hw/arm-cpu.c | 78 ++++++
> hw/arm_boot.c | 22 ++
> hw/boards.h | 9 +
> hw/dt-machine.c | 582
> +++++++++++++++++++++++++++++++++++++++++++++
> hw/i2c.c | 8 +
> hw/pci.c | 1
> hw/qdev.c | 225 +++++++++++++++++
> hw/qdev.h | 50 +++-
> hw/ssi.c | 7 -
> hw/syborg.c | 112 ---------
> hw/sysbus.c | 5
> hw/sysbus.h | 15 +
> pc-bios/boards/syborg.dts | 134 ++++++++++
> rules.mak | 3
> sysemu.h | 3
> vl.c | 45 +++
> 20 files changed, 1179 insertions(+), 163 deletions(-)
> create mode 100644 hw/arm-cpu.c
> create mode 100644 hw/dt-machine.c
> delete mode 100644 hw/syborg.c
> create mode 100644 pc-bios/boards/syborg.dts
>
[...]
> diff --git a/configure b/configure
> index 59ba8ef..f20da35 100755
> --- a/configure
> +++ b/configure
> @@ -199,6 +199,7 @@ sdl="yes"
> sdl_x11="no"
> xen="yes"
> pkgversion=""
> +dtc=""
>
> # OS specific
> if check_define __linux__ ; then
> @@ -503,6 +504,8 @@ for opt do
> ;;
> --with-pkgversion=*) pkgversion=" ($optarg)"
> ;;
> + --with-dtc=*) dtc="$optarg"
> + ;;
> --disable-docs) build_docs="no"
> ;;
> *) echo "ERROR: unknown option $opt"; show_help="yes"
> @@ -1227,6 +1230,13 @@ if $cc $ARCH_CFLAGS -o $TMPE ${OS_CFLAGS} $TMPC -lfdt
> 2> /dev/null > /dev/null ;
> build_fdt=no
> fi
>
> +# Check for device tree compiler
> +if test -z "$dtc" ; then
> + dtc="`which dtc 2>/dev/null`"
> + if test ! -x "$dtc" ; then
> + dtc=""
> + fi
> +fi
New build dependency. Fine with me.
> #
> # Check for xxxat() functions when we are building linux-user
> # emulator. This is done because older glibc versions don't
> @@ -1380,6 +1390,7 @@ echo "Install blobs $blobs"
> echo -e "KVM support $kvm"
> echo "Build libfdt $build_fdt"
> echo "preadv support $preadv"
> +echo "dtc ${dtc:-(Not Found)}"
>
> if test $sdl_too_old = "yes"; then
> echo "-> Your SDL version is too old - please upgrade to have SDL support"
> @@ -1708,7 +1719,7 @@ if test "$iovec" = "yes" ; then
> echo "#define HAVE_IOVEC 1" >> $config_h
> fi
> if test "$preadv" = "yes" ; then
> - echo "#define HAVE_PREADV 1" >> $config_h
> + echo "#defne HAVE_PREADV 1" >> $config_h
> fi
> if test "$build_fdt" = "yes" ; then
> echo "BUILD_LIBFDT=libfdt/libfdt.a" >> $config_mak
> @@ -1717,6 +1728,10 @@ else
> echo "FDT_LIBS=-lfdt" >> $config_mak
> fi
>
> +if [ -n "$dtc" ] ; then
> + echo "DTC=$dtc" >> $config_mak
> +fi
> +
> # XXX: suppress that
> if [ "$bsd" = "yes" ] ; then
> echo "#define O_LARGEFILE 0" >> $config_h
[...]
> diff --git a/hw/dt-machine.c b/hw/dt-machine.c
> new file mode 100644
> index 0000000..b960a6c
> --- /dev/null
> +++ b/hw/dt-machine.c
> @@ -0,0 +1,582 @@
> +#include "sysbus.h"
> +#include "device_tree.h"
> +#include "boards.h"
> +
> +#include <libfdt.h>
> +
> +/* FIXME: Remove this. */
> +static void *the_dt;
> +static int bootstrap_offset;
> +
> +static void dt_walk_bus(DeviceState *parent, void *dt, int offset, int
> folded);
> +
> +static int fdt_next_node(const void *fdt, int offset)
> +{
> + int level = 1;
> + uint32_t tag;
> + int nextoffset;
> +
> + tag = fdt_next_tag(fdt, offset, &nextoffset);
> + if (tag != FDT_BEGIN_NODE)
> + return -FDT_ERR_BADOFFSET;
> +
> + while (level >= 0) {
> + offset = nextoffset;
> + tag = fdt_next_tag(fdt, offset, &nextoffset);
> +
> + switch (tag) {
> + case FDT_END:
> + return -FDT_ERR_TRUNCATED;
> + case FDT_BEGIN_NODE:
> + level++;
> + if (level == 1)
> + return offset;
> + break;
> + case FDT_END_NODE:
> + level--;
> + break;
> + case FDT_PROP:
> + case FDT_NOP:
> + break;
> + default:
> + return -FDT_ERR_BADSTRUCTURE;
> + }
> + }
> +
> + return -FDT_ERR_NOTFOUND;
> +}
> +
> +static int fdt_first_subnode(const void *fdt, int offset)
> +{
> + uint32_t tag;
> + int nextoffset;
> +
> + tag = fdt_next_tag(fdt, offset, &nextoffset);
> + if (tag != FDT_BEGIN_NODE)
> + return -FDT_ERR_BADOFFSET;
> +
> + while (tag != FDT_END_NODE) {
> + offset = nextoffset;
> + tag = fdt_next_tag(fdt, offset, &nextoffset);
> +
> + switch (tag) {
> + case FDT_END:
> + return -FDT_ERR_TRUNCATED;
> + case FDT_BEGIN_NODE:
> + return offset;
> + case FDT_END_NODE:
> + case FDT_PROP:
> + case FDT_NOP:
> + break;
> + default:
> + return -FDT_ERR_BADSTRUCTURE;
> + }
> + }
> +
> + return -FDT_ERR_NOTFOUND;
> +}
> +
> +static void dt_add_props(DeviceState *dev, void *dt, int offset)
> +{
> + DevicePropList *prop;
> + const void *p;
> + int prop_len;
> + uint64_t i;
> + const uint32_t *ip;
> +
> + prop = qdev_get_proplist(dev);
> + if (!prop) {
> + return;
> + }
> + for (; prop->name; prop++) {
> + p = fdt_getprop(dt, offset, prop->name, &prop_len);
> + if (!p) {
> + continue;
> + }
> + ip = p;
> + switch (prop->type) {
> + case PROP_TYPE_INT:
> + if (prop_len != 4 && prop_len != 8) {
> + hw_error("%s: Bad length for property '%s'\n",
> + fdt_get_name(dt, offset, NULL), prop->name);
> + }
> + i = fdt32_to_cpu(ip[0]);
> + if (prop_len == 8) {
> + i = (i << 32) | fdt32_to_cpu(ip[1]);
> + }
> + qdev_set_prop_int(dev, prop->name, i);
> + break;
> + case PROP_TYPE_DEV:
> + {
> + uint32_t phandle;
> + DeviceState *other_dev;
> + if (prop_len != 4) {
> + hw_error("%s: Bad length for property '%s'\n",
> + fdt_get_name(dt, offset, NULL), prop->name);
> + }
> + phandle = fdt32_to_cpu(ip[0]);
> + other_dev = qdev_from_phandle(phandle);
> + if (!other_dev) {
> + hw_error("%s: Device (%d) not found\n",
> + fdt_get_name(dt, offset, NULL), phandle);
> + }
> + qdev_set_prop_dev(dev, prop->name, other_dev);
> + }
> + break;
> + case PROP_TYPE_ARRAY:
> + {
> + uint32_t *data;
> + if (prop_len & 3) {
> + hw_error("%s: Bad length for property '%s'\n",
> + fdt_get_name(dt, offset, NULL), prop->name);
> + }
> + data = qemu_malloc(prop_len);
> + for (i = 0; i < prop_len >> 2; i++) {
> + data[i] = fdt32_to_cpu(ip[i]);
> + }
> + qdev_set_prop_array(dev, prop->name, data, prop_len >> 2);
> + qemu_free(data);
> + }
> + break;
> + default:
> + abort();
> + }
> + }
> +}
Here you copy the device's known properties from FDT to the
qdev->props.
Why have a dynamically typed property list in qdev? Why not make the
properties ordinary members of the device struct, and have the property
descriptor DevicePropList specify where they are (offset from qdev)?
Moves some type checking to compile-time.
Alternatively, why not get the properties straight from FDT, as Gerd
suggested?
> +
> +static void dt_create_device(BusState *bus, void *dt, int offset)
> +{
> + char namebuf[128];
> + const char *type;
> + const uint32_t *prop;
> + char *p;
> + DeviceState *dev;
> +
> + /* First try the "model" property. */
> + type = fdt_getprop(dt, offset, "model", NULL);
> + if (type && !qdev_device_exists(type)) {
> + type = NULL;
> + }
> + /* If that does not work then try "compatible". */
> + if (!type) {
> + type = fdt_getprop(dt, offset, "compatible", NULL);
> + if (type && !qdev_device_exists(type)) {
> + type = NULL;
> + }
> + }
> + /* If all else fails then resort to the device name. */
> + if (!type) {
> + type = fdt_get_name(dt, offset, NULL);
> + p = namebuf;
> + while (*type && *type != '@') {
> + *(p++) = *(type++);
> + }
> + *p = 0;
> + if (!qdev_device_exists(namebuf)) {
> + hw_error("Unrecognised device '%s'\n",
> + fdt_get_name(dt, offset, NULL));
> + }
> + type = namebuf;
> + }
> +
> + dev = qdev_create(bus, type);
> + dev->fdt_offset = offset;
> + prop = fdt_getprop(dt, offset, "linux,phandle", NULL);
> + if (prop) {
> + dev->phandle = fdt32_to_cpu(*prop);
> + } else {
> + dev->phandle = -1;
> + }
> +}
> +
> +static void dt_init_device(DeviceState *dev, void *opaque)
> +{
> + void *dt = opaque;
> + int offset;
> +
> + offset = dev->fdt_offset;
> + dt_add_props(dev, dt, offset);
> + qdev_init(dev);
> +
> + dt_walk_bus(dev, dt, offset, 0);
> +}
> +
> +static int dt_fold_bus(void *dt, int offset)
> +{
> + const char *name = fdt_get_name(dt, offset, NULL);
> +
> + if (strcmp(name, "cpus") == 0) {
> + return 1;
> + }
> + if (fdt_getprop(dt, offset, "qemu,fold-bus", NULL)) {
> + return 1;
> + }
> + return 0;
> +}
Please document all qemu,FOO properties.
> +
> +static int dt_ignore_node(void *dt, int offset)
> +{
> + const char *name = fdt_get_name(dt, offset, NULL);
> +
> + if (strcmp(name, "chosen") == 0) {
> + return 1;
> + }
> + if (strcmp(name, "aliases") == 0) {
> + return 1;
> + }
> + if (fdt_getprop(dt, offset, "qemu,ignore", NULL)) {
> + return 1;
> + }
> + return 0;
> +}
> +
> +static void dt_get_sizes(void *dt, int offset,
> + int *addr_cells, int *size_cells)
> +{
> + const uint32_t *prop;
> + int parent;
> +
> + parent = fdt_parent_offset(dt, offset);
> + assert(parent >= 0);
> +
> + if (addr_cells) {
> + prop = fdt_getprop(dt, parent, "#address-cells", NULL);
> + if (!prop) {
> + hw_error("%s: Missing #address-cells",
> + fdt_get_name(dt, offset, NULL));
> + }
> + *addr_cells = fdt32_to_cpu(*prop);
> + }
> +
> + if (size_cells) {
> + prop = fdt_getprop(dt, parent, "#size-cells", NULL);
> + if (!prop) {
> + hw_error("%s: Missing #size-cells",
> + fdt_get_name(dt, offset, NULL));
> + }
> + *size_cells = fdt32_to_cpu(*prop);
> + }
> +}
> +
> +static void dt_create_memory(void *dt, int offset, int flags)
> +{
> + const uint32_t *regs;
> + int regs_len;
> + const uint32_t *alias;
> + int alias_len;
> + int addr_cells;
> + int size_cells;
> + uint64_t addr;
> + uint64_t size;
> + ram_addr_t ram_offset;
> +
> + dt_get_sizes(dt, offset, &addr_cells, &size_cells);
> + regs = fdt_getprop(dt, offset, "reg", ®s_len);
> + if (!regs || regs_len == 0) {
> + return;
> + }
> + if ((regs_len % ((addr_cells + size_cells) * 4)) != 0) {
> + hw_error("%s: Bad reg size\n", fdt_get_name(dt, offset, NULL));
> + }
> + alias = fdt_getprop(dt, offset, "qemu,alias", &alias_len);
> + while (regs_len > 0) {
> + addr = fdt32_to_cpu(*(regs++));
> + if (addr_cells == 2) {
> + addr = (addr << 32) | fdt32_to_cpu(*(regs++));
> + }
> + size = fdt32_to_cpu(*(regs++));
> + if (size_cells == 2) {
> + size = (size << 32) | fdt32_to_cpu(*(regs++));
> + }
> + regs_len -= (addr_cells + size_cells) * 4;
> + if (size != (ram_addr_t)size) {
> + hw_error("Ram too big\n");
> + }
> + ram_offset = qemu_ram_alloc(size);
> + cpu_register_physical_memory(addr, size, ram_offset | flags);
> + if (alias) {
> + if (regs_len > 0) {
> + hw_error("%s: Aliased memory may only have a single region",
> + fdt_get_name(dt, offset, NULL));
> + }
> + if ((alias_len % addr_cells) != 0) {
> + hw_error("%s: Bad qemu,alias size\n",
> + fdt_get_name(dt, offset, NULL));
> + }
> + while (alias_len) {
> + addr = fdt32_to_cpu(*(alias++));
> + if (addr_cells== 2) {
> + addr = (addr << 32) | fdt32_to_cpu(*(alias++));
> + }
> + cpu_register_physical_memory(addr, size, ram_offset | flags);
> + alias_len -= addr_cells * 4;
> + }
> + }
> + }
> +}
> +
> +static void dt_walk_bus(DeviceState *parent, void *dt, int offset, int
> folded)
This seems to create the device tree for an FDT. Not obvious from the
name.
> +{
> + int next_offset;
> + const char *type;
> + const char *bus_name;
> + BusState *parent_bus;
> +
> + if (parent) {
> + parent_bus = LIST_FIRST(&parent->child_bus);
> + } else {
> + parent_bus = NULL;
> + }
> + next_offset = fdt_first_subnode(dt, offset);
> + while (next_offset > 0) {
> + offset = next_offset;
> + next_offset = fdt_next_node(dt, offset);
> + if (dt_ignore_node(dt, offset)) {
> + continue;
> + }
> + if (dt_fold_bus(dt, offset)) {
> + dt_walk_bus(parent, dt, offset, 1);
> + continue;
> + }
> + bus_name = fdt_getprop(dt, offset, "qemu,parent-bus", NULL);
> + if (bus_name) {
> + if (parent) {
> + parent_bus = qdev_get_child_bus(parent, bus_name);
> + }
> + if (!parent_bus) {
> + hw_error("%s: Unable to find parent bus\n",
> + fdt_get_name(dt, offset, NULL));
> + }
> + }
> + type = fdt_getprop(dt, offset, "device_type", NULL);
> + /* Special case for memory. */
> + if (type && strcmp(type, "memory") == 0) {
> + dt_create_memory(dt, offset, IO_MEM_RAM);
> + continue;
> + }
> + if (type && strcmp(type, "rom") == 0) {
> + dt_create_memory(dt, offset, IO_MEM_ROM);
> + continue;
> + }
> + dt_create_device(parent_bus, dt, offset);
> + }
> + if (!folded) {
> + qdev_enumerate_child_devices(parent, dt_init_device, dt);
> + }
> +}
> +
> +void dt_fixup_qdev(DeviceState *dev)
> +{
> + void *dt = the_dt;
> + int offset = dev->fdt_offset;
> + int n;
> + int prop_size;
> + const uint32_t *prop;
> +
> + if (dev->num_gpio_out) {
> + int parent;
> + int irqn;
> + qemu_irq pin;
> + DeviceState *parent_dev;
> +
> + prop = fdt_getprop(dt, offset, "qemu,gpio", &prop_size);
> + if (prop_size != 8 * dev->num_gpio_out) {
> + hw_error("%s: Bad GPIO size\n", fdt_get_name(dt, offset, NULL));
> + }
> + for (n = 0; n < dev->num_gpio_out; n++) {
> + parent = fdt32_to_cpu(*(prop++));
> + if (parent == 0) {
> + /* Assume zero phandle means disconnected. */
> + prop++;
> + continue;
> + }
> + parent_dev = qdev_from_phandle(parent);
> + if (!parent_dev) {
> + hw_error("%s: GPIO device (%d) not found\n",
> + fdt_get_name(dt, offset, NULL), parent);
> + }
> + irqn = fdt32_to_cpu(*(prop++));
> + if (irqn >= parent_dev->num_gpio_in) {
> + hw_error("%s: Invalid GPIO %d (%d)\n",
> + fdt_get_name(dt, offset, NULL), n, irqn);
> + }
> + pin = qdev_get_gpio_in(parent_dev, irqn);
> + qdev_connect_gpio_out(dev, n, pin);
> + }
> + }
> +}
> +
> +void dt_fixup_sysbus_device(DeviceState *dev)
> +{
> + SysBusDevice *s = sysbus_from_qdev(dev);
> + void *dt = the_dt;
> + int offset = dev->fdt_offset;
> + const uint32_t *prop;
> + int prop_size;
> + int n;
> +
> + if (s->num_mmio) {
> + int addr_cells;
> + int size_cells;
> + uint64_t addr;
> + dt_get_sizes(dt, offset, &addr_cells, &size_cells);
> + if (addr_cells < 1 || addr_cells > 2) {
> + hw_error("%s: unsupported #address-cells (%d)\n",
> + fdt_get_name(dt, offset, NULL), addr_cells);
> + }
> + prop = fdt_getprop(dt, offset, "reg", &prop_size);
> + if (prop_size != (addr_cells + size_cells) * 4 * s->num_mmio) {
> + hw_error("%s: Bad reg size\n", fdt_get_name(dt, offset, NULL));
> + }
> + for (n = 0; n < s->num_mmio; n++) {
> + addr = fdt32_to_cpu(*(prop++));
> + if (addr_cells == 2) {
> + addr = (addr << 32) | fdt32_to_cpu(*(prop++));
> + }
> + /* The device already told up how big the region is, so ignore
> + what the device tree says. */
> + prop += size_cells;
> + sysbus_mmio_map(s, n, addr);
> + }
> + }
> +
> + if (s->num_irq) {
> + int parent;
> + int irqn;
> + DeviceState *parent_dev;
> +
> + prop = fdt_getprop(dt, offset, "interrupt-parent", &prop_size);
> + if (!prop || prop_size != 4) {
> + hw_error("%s: Missing/bad interrupt-parent\n",
> + fdt_get_name(dt, offset, NULL));
> + }
> + parent = fdt32_to_cpu(*prop);
> + parent_dev = qdev_from_phandle(parent);
> + if (!parent_dev) {
> + hw_error("%s: interrupt-parent device (%d) not found\n",
> + fdt_get_name(dt, offset, NULL), parent);
> + }
> + prop = fdt_getprop(dt, parent_dev->fdt_offset, "#interrupt-cells",
> + &prop_size);
> + if (!prop || prop_size != 4) {
> + hw_error("%s: Missing #interrupt-cells\n",
> + fdt_get_name(dt, parent_dev->fdt_offset, NULL));
> + }
> + if (fdt32_to_cpu(*prop) != 1) {
> + hw_error("%s: unsupported #interrupt-cells\n",
> + fdt_get_name(dt, parent_dev->fdt_offset, NULL));
> + }
> + prop = fdt_getprop(dt, offset, "interrupts", &prop_size);
> + if (prop_size != 4 * s->num_irq) {
> + hw_error("%s: Bad interrupts size\n",
> + fdt_get_name(dt, offset, NULL));
> + }
> + for (n = 0; n < s->num_irq; n++) {
> + irqn = fdt32_to_cpu(*(prop++));
> + if (irqn == -1) {
> + continue;
> + }
> + if (irqn >= parent_dev->num_gpio_in) {
> + hw_error("%s: Invalid interrupt %d (%d)\n",
> + fdt_get_name(dt, offset, NULL), n, irqn);
> + }
> + sysbus_connect_irq(s, n, qdev_get_gpio_in(parent_dev, irqn));
> + }
> + }
> +}
> +
> +typedef struct MachineBootstrap {
> + const char *name;
> + machine_bootstrapfn fn;
> + LIST_ENTRY(MachineBootstrap) next;
> +} MachineBootstrap;
> +
> +LIST_HEAD(, MachineBootstrap) machine_bootstrap_list =
> + LIST_HEAD_INITIALIZER(machine_bootstrap_list);
> +
> +void register_machine_bootstrap(const char *name, machine_bootstrapfn fn)
> +{
> + MachineBootstrap *bootstrap;
> +
> + bootstrap = qemu_mallocz(sizeof(*bootstrap));
> + bootstrap->name = qemu_strdup(name);
> + bootstrap->fn = fn;
> + LIST_INSERT_HEAD(&machine_bootstrap_list, bootstrap, next);
> +}
> +
> +uint64_t get_bootstrap_arg_int(const char *name, uint64_t def)
> +{
> + void *dt = the_dt;
> + const uint32_t *p;
> + int prop_len;
> + uint64_t val;
> +
> + p = fdt_getprop(dt, bootstrap_offset, name, &prop_len);
> + if (!p) {
> + return def;
> + }
> + if (prop_len != 4 && prop_len != 8) {
> + hw_error("Bad length for property '%s'\n", name);
> + }
> + val = fdt32_to_cpu(p[0]);
> + if (prop_len == 8) {
> + val = (val << 32) | fdt32_to_cpu(p[1]);
> + }
> + return val;
> +}
> +
> +static void dt_init(ram_addr_t ram_size,
> + const char *boot_device,
> + const char *kernel_filename, const char *kernel_cmdline,
> + const char *initrd_filename, const char *cpu_model)
> +{
> + void *dt;
> + int dt_size;
> + const char *filename;
> +
> + filename = dt_machine.name;
> + /* FIXME: Allow user to specify filename. */
> + dt = load_device_tree(filename, &dt_size);
> + if (!dt) {
> + hw_error("Failed to load device tree\n");
> + }
> +
> + the_dt = dt;
> +
> + dt_walk_bus(NULL, dt, 0, 0);
> +
> + qdev_fixup_devices();
> +
> + {
> + const char *p;
> + MachineBootstrap *bootstrap;
> +
> + bootstrap_offset = fdt_path_offset(dt, "/chosen/qemu");
> + if (bootstrap_offset < 0) {
> + return;
> + }
> + p = fdt_getprop(dt, bootstrap_offset, "bootstrap", NULL);
> + if (!p) {
> + return;
> + }
> +
> + LIST_FOREACH(bootstrap, &machine_bootstrap_list, next) {
> + if (strcmp(bootstrap->name, p) == 0) {
> + break;
> + }
> + }
> + if (!bootstrap) {
> + hw_error("Unrecognised machine bootstrap '%s'\n", p);
> + }
> + bootstrap->fn(ram_size, boot_device, kernel_filename, kernel_cmdline,
> + initrd_filename);
> +
> + }
> +}
> +
> +/* This is used directly by vl.c, and not registered by normal means. */
> +QEMUMachine dt_machine = {
> + .name = "devtree",
> + .desc = "Device Tree",
> + .init = dt_init,
> +};
[...]
> diff --git a/hw/qdev.c b/hw/qdev.c
> index 636dc78..39e8ce8 100644
> --- a/hw/qdev.c
> +++ b/hw/qdev.c
> @@ -26,6 +26,25 @@
> inherit from a particular bus (e.g. PCI or I2C) rather than
> this API directly. */
>
> +/* Device instantiation occurs in several stages.
> +
> + 1) Device objects are created (qdev_create).
> + 2) Device properties are set.
> + 3) Device init routines are run.
> + 4) GPIO and IRQ lines are connected and MMIO regions are mapped.
> + 5) Device late init routines are run.
I see you discovered the need for a second initialization callback.
Good.
> +
> + It can be assumed that all siblings on a bus have been created before
> + any are initialized. Child devices are created after the parent
> + device is initialized. Thus PROP_TYPE_DEV may only refer to siblings or
> + siblings of direct ancestors. No dependency tracking is performed, so
> + a device may be initialized before the devices it references.
> +
> + Other than Within each stage, devices are processed in arbitrary order.
> +
> + Steps 3, 4 and 5 only occur after preceeding steps have been completed
> + for all devices. */
> +
> #include "net.h"
> #include "qdev.h"
> #include "sysemu.h"
> @@ -37,6 +56,10 @@ struct DeviceProperty {
> union {
> uint64_t i;
> void *ptr;
> + struct {
> + uint32_t *data;
> + int len;
> + } array;
> } value;
> DeviceProperty *next;
> };
> @@ -68,6 +91,23 @@ void qdev_register(const char *name, int size, DeviceInfo
> *info)
> t->info = info;
> }
>
> +static DeviceType *qdev_find_devtype(const char *name)
> +{
> + DeviceType *t;
> +
> + for (t = device_type_list; t; t = t->next) {
> + if (strcmp(t->name, name) == 0) {
> + return t;;
> + }
> + }
> + return NULL;
> +}
> +
> +int qdev_device_exists(const char *name)
> +{
> + return qdev_find_devtype(name) != NULL;
> +}
> +
> /* 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. */
> @@ -76,11 +116,7 @@ DeviceState *qdev_create(BusState *bus, const char *name)
> DeviceType *t;
> DeviceState *dev;
>
> - for (t = device_type_list; t; t = t->next) {
> - if (strcmp(t->name, name) == 0) {
> - break;
> - }
> - }
> + t = qdev_find_devtype(name);
> if (!t) {
> hw_error("Unknown device '%s'\n", name);
> }
> @@ -89,8 +125,8 @@ DeviceState *qdev_create(BusState *bus, const char *name)
> dev->type = t;
>
> if (!bus) {
> - /* ???: This assumes system busses have no additional state. */
> if (!main_system_bus) {
> + /* ???: This assumes system busses have no additional state. */
> main_system_bus = qbus_create(BUS_TYPE_SYSTEM, sizeof(BusState),
> NULL, "main-system-bus");
> }
> @@ -102,7 +138,9 @@ DeviceState *qdev_create(BusState *bus, const char *name)
> t->info->bus_type, bus->type);
> }
> dev->parent_bus = bus;
> - LIST_INSERT_HEAD(&bus->children, dev, sibling);
> + /* Keep devices in creation order for consistency between creation
> + and initialization passes. */
> + TAILQ_INSERT_TAIL(&bus->children, dev, sibling);
> return dev;
> }
>
> @@ -114,10 +152,32 @@ void qdev_init(DeviceState *dev)
> dev->type->info->init(dev, dev->type->info);
> }
>
> +static void qdev_late_init_bus(BusState *bus)
> +{
> + DeviceState *dev;
> + BusState *child_bus;
> +
> + TAILQ_FOREACH(dev, &bus->children, sibling) {
> + if (dev->type->info->late_init) {
> + dev->type->info->late_init(dev);
> + }
> + LIST_FOREACH(child_bus, &dev->child_bus, sibling) {
> + qdev_late_init_bus(child_bus);
> + }
> + }
> +}
> +
> +void qdev_do_late_init(void)
> +{
> + if (main_system_bus) {
> + qdev_late_init_bus(main_system_bus);
> + }
> +}
> +
> /* Unlink device from bus and free the structure. */
> void qdev_free(DeviceState *dev)
> {
> - LIST_REMOVE(dev, sibling);
> + TAILQ_REMOVE(&dev->parent_bus->children, dev, sibling);
> free(dev);
> }
>
> @@ -152,6 +212,17 @@ void qdev_set_prop_dev(DeviceState *dev, const char
> *name, DeviceState *value)
> prop->value.ptr = value;
> }
>
> +void qdev_set_prop_array(DeviceState *dev, const char *name, uint32_t *data,
> + int len)
> +{
> + DeviceProperty *prop;
> +
> + prop = create_prop(dev, name, PROP_TYPE_ARRAY);
> + prop->value.array.data = qemu_malloc(len * 4);
> + prop->value.array.len = len;
> + memcpy(prop->value.array.data, data, len * 4);
> +}
> +
> void qdev_set_prop_ptr(DeviceState *dev, const char *name, void *value)
> {
> DeviceProperty *prop;
> @@ -231,6 +302,21 @@ DeviceState *qdev_get_prop_dev(DeviceState *dev, const
> char *name)
> return prop->value.ptr;
> }
>
> +int qdev_get_prop_array(DeviceState *dev, const char *name,
> + const uint32_t **p)
> +{
> + DeviceProperty *prop;
> +
> + prop = find_prop(dev, name, PROP_TYPE_ARRAY);
> + if (!prop) {
> + return -1;
> + }
> + if (p) {
> + *p = prop->value.array.data;
> + }
> + return prop->value.array.len;
> +}
> +
> void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
> {
> assert(dev->num_gpio_in == 0);
> @@ -272,6 +358,17 @@ VLANClientState *qdev_get_vlan_client(DeviceState *dev,
>
> void qdev_get_macaddr(DeviceState *dev, uint8_t *macaddr)
> {
> + static int next_netdev;
> + if (!dev->nd) {
> + /* FIXME: This is just plain broken. */
> + if (next_netdev >= nb_nics) {
> + abort();
> + }
> + dev->nd = &nd_table[next_netdev];
> + next_netdev++;
> +
> + qemu_check_nic_model(dev->nd, dev->type->name);
> + }
> memcpy(macaddr, dev->nd->macaddr, 6);
> }
>
> @@ -332,13 +429,121 @@ BusState *qbus_create(BusType type, size_t size,
> bus->type = type;
> bus->parent = parent;
> bus->name = qemu_strdup(name);
> - LIST_INIT(&bus->children);
> + TAILQ_INIT(&bus->children);
> if (parent) {
> LIST_INSERT_HEAD(&parent->child_bus, bus, sibling);
> }
> return bus;
> }
>
> +static DeviceState *qdev_from_phandle1(BusState *bus, uint32_t phandle)
> +{
> + DeviceState *dev;
> + BusState *child_bus;
> + DeviceState *child_dev;
> +
> + TAILQ_FOREACH(dev, &bus->children, sibling) {
> + if (dev->phandle == phandle) {
> + return dev;
> + }
> + LIST_FOREACH(child_bus, &dev->child_bus, sibling) {
> + child_dev = qdev_from_phandle1(child_bus, phandle);
> + if (child_dev) {
> + return child_dev;
> + }
> + }
> + }
> + return NULL;
> +}
> +
> +DeviceState *qdev_from_phandle(uint32_t phandle)
> +{
> + if (!main_system_bus) {
> + return NULL;
> + }
> + return qdev_from_phandle1(main_system_bus, phandle);
> +}
> +
> +static void qdev_fixup_bus(BusState *bus)
> +{
> + DeviceState *dev;
> +
> + TAILQ_FOREACH(dev, &bus->children, sibling) {
> + if (!dev->fdt_offset) {
> + continue;
> + }
> + dt_fixup_qdev(dev);
> + switch (bus->type) {
> + case BUS_TYPE_SYSTEM:
> + dt_fixup_sysbus_device(dev);
> + break;
> + default:
> + break;
> + }
> + }
> +}
> +
> +void qdev_fixup_devices(void)
> +{
> + assert(main_system_bus);
> + qdev_fixup_bus(main_system_bus);
> +}
> +
> +static void enumerate_bus_devices(BusState *bus, enumdevfn cb, void *opaque)
> +{
> + DeviceState *dev;
> +
> + TAILQ_FOREACH(dev, &bus->children, sibling) {
> + cb(dev, opaque);
> + }
> +}
> +
> +void qdev_enumerate_child_devices(DeviceState *dev, enumdevfn cb, void
> *opaque)
> +{
> + BusState *bus;
> +
> + if (!dev) {
> + enumerate_bus_devices(main_system_bus, cb, opaque);
> + return;
> + }
> + LIST_FOREACH(bus, &dev->child_bus, sibling) {
> + enumerate_bus_devices(bus, cb, opaque);
> + }
> +}
> +
> +DevicePropList *qdev_get_proplist(DeviceState *dev)
> +{
> + return dev->type->info->props;
> +}
> +
> +DevicePropList *qdev_merge_props(const DevicePropList *a,
> + const DevicePropList *b)
> +{
> + int n;
> + const DevicePropList *p;
> + DevicePropList *q;
> + DevicePropList *ret;
> +
> + n = 0;
> + for (p = a; p && p->name; p++) {
> + n++;
> + }
> + for (p = b; p && p->name; p++) {
> + n++;
> + }
> + n++;
> + ret = qemu_malloc(sizeof(DevicePropList) * n);
> + q = ret;
> + for (p = a; p && p->name; p++) {
> + *(q++) = *p;
> + }
> + for (p = b; p && p->name; p++) {
> + *(q++) = *p;
> + }
> + q->name = NULL;
> + return ret;
> +}
> +
> static const char *bus_type_names[] = {
> [ BUS_TYPE_SYSTEM ] = "System",
> [ BUS_TYPE_PCI ] = "PCI",
> @@ -399,7 +604,7 @@ static void qbus_print(Monitor *mon, BusState *bus, int
> indent)
> qdev_printf("bus: %s\n", bus->name);
> indent += 2;
> qdev_printf("type %s\n", bus_type_names[bus->type]);
> - LIST_FOREACH(dev, &bus->children, sibling) {
> + TAILQ_FOREACH(dev, &bus->children, sibling) {
> qdev_print(mon, dev, indent);
> }
> }
[...]
There's a fair amount of IEEE-1275 gunk in here, which I believe QEMU
could well do without. Being able to pass the config straight on to the
PowerPC kernel is nice, but I'm not at all sure that can justify the
gunk.
[Qemu-devel] [PATCH 4/4] Integrator machine config, Paul Brook, 2009/06/10
Re: [Qemu-devel] [PATCH 0/4] Machine config files, Gerd Hoffmann, 2009/06/11
Re: [Qemu-devel] [PATCH 0/4] Machine config files, Gerd Hoffmann, 2009/06/11