qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH 2/4] Add device tree machine


From: Paul Brook
Subject: [Qemu-devel] [PATCH 2/4] Add device tree machine
Date: Wed, 10 Jun 2009 18:38:21 +0100
User-agent: StGIT/0.14.2

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/.gitignore b/.gitignore
index a8da10e..225f705 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,7 @@ qemu-io
 *.vr
 *.d
 *.o
+pc-bios/boards/*.dtb
 .pc
 patches
 pc-bios/bios-pq/status
diff --git a/Makefile b/Makefile
index 6fc234c..6d15c44 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ endif
 .PHONY: all clean cscope distclean dvi html info install install-doc \
        recurse-all speed tar tarbin test
 
-VPATH=$(SRC_PATH):$(SRC_PATH)/hw
+VPATH=$(SRC_PATH):$(SRC_PATH)/hw:$(SRC_PATH)/pc-bios/boards
 
 
 CFLAGS += $(OS_CFLAGS) $(ARCH_CFLAGS)
@@ -43,7 +43,16 @@ ifdef CONFIG_WIN32
 LIBS+=-lwinmm -lws2_32 -liphlpapi
 endif
 
-build-all: $(TOOLS) $(DOCS) recurse-all
+#######################################################################
+# Board descriptions
+
+BOARDS = syborg
+
+ifdef DTC
+BOARDS_BIN = $(BOARDS:%=pc-bios/boards/%.dtb)
+endif
+
+build-all: $(TOOLS) $(DOCS) recurse-all $(BOARDS_BIN)
 
 config-host.mak: configure
 ifneq ($(wildcard config-host.mak),)
@@ -266,6 +275,7 @@ clean:
 # avoid old build problems by removing potentially incorrect old files
        rm -f config.mak config.h op-i386.h opc-i386.h gen-op-i386.h op-arm.h 
opc-arm.h gen-op-arm.h
        rm -f *.o *.d *.a $(TOOLS) TAGS cscope.* *.pod *~ */*~
+       rm -f $(BOARDS_BIN)
        for d in slirp audio block libfdt; do \
          rm -f $$d/*.o $$d/*.d $$d/*.a ; \
        done
@@ -316,6 +326,12 @@ ifneq ($(BLOBS),)
                $(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; 
\
        done
 endif
+ifneq ($(BOARDS_BIN),)
+       $(INSTALL_DIR) "$(DESTDIR)$(datadir)/boards"
+       set -e; for x in $(BOARDS_BIN); do \
+               $(INSTALL_DATA) $(SRC_PATH)/$$x "$(DESTDIR)$(datadir)/boards"; \
+       done
+endif
 ifndef CONFIG_WIN32
        $(INSTALL_DIR) "$(DESTDIR)$(datadir)/keymaps"
        set -e; for x in $(KEYMAPS); do \
diff --git a/Makefile.target b/Makefile.target
index 4e302c0..4bc4c76 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -494,7 +494,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 \
-     gdbstub.o gdbstub-xml.o
+     gdbstub.o gdbstub-xml.o dt-machine.o
 # virtio has to be here due to weird dependency between PCI and virtio-net.
 # need to fix this properly
 OBJS+=virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o
@@ -664,9 +664,10 @@ OBJS+= nseries.o blizzard.o onenand.o vga.o cbus.o 
tusb6010.o usb-musb.o
 OBJS+= mst_fpga.o mainstone.o
 OBJS+= musicpal.o pflash_cfi02.o
 OBJS+= framebuffer.o
-OBJS+= syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
+OBJS+= syborg_fb.o syborg_interrupt.o syborg_keyboard.o
 OBJS+= syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
 OBJS+= syborg_virtio.o
+OBJS+= arm-cpu.o
 CPPFLAGS += -DHAS_AUDIO
 endif
 ifeq ($(TARGET_BASE_ARCH), sh4)
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
 #
 # 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/arm-cpu.c b/hw/arm-cpu.c
new file mode 100644
index 0000000..c15eb12
--- /dev/null
+++ b/hw/arm-cpu.c
@@ -0,0 +1,78 @@
+/*
+ * CPU devices
+ *
+ * Copyright (c) 2009 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GNU GPL v2.
+ */
+
+#include "sysbus.h"
+#include "arm-misc.h"
+#include "boards.h"
+
+/* FIXME: Remove this and make the CPU emulation use the right names.  */
+static const struct {
+    const char *devname;
+    const char *cpuname;
+} cpu_device_name_map[] = {
+    {"ARM,ARM926EJ-S", "arm926"},
+    {"ARM,Cortex-A8", "cortex-a8"}
+};
+
+typedef struct {
+    SysBusDeviceInfo sysbus;
+    const char *cpuname;
+} CPUInfo;
+
+typedef struct {
+    SysBusDevice busdev;
+    qemu_irq *cpu_pic;
+} CPUDevice;
+
+static void arm_cpu_dev_set_irq(void *opaque, int n, int level)
+{
+    CPUDevice *s = opaque;
+    assert(n >= 0 && n < 2);
+    qemu_set_irq(s->cpu_pic[n], level);
+}
+
+static DevicePropList cpu_qdev_props[] = {
+    {.name = "nvic", .type = PROP_TYPE_DEV},
+    {.name = ""}
+};
+
+static void arm_cpu_dev_init(SysBusDevice *dev)
+{
+    CPUDevice *s = FROM_SYSBUS(CPUDevice, dev);
+    CPUInfo *info = container_of(dev->info, CPUInfo, sysbus);
+    CPUState *env;
+
+    env = cpu_init(info->cpuname);
+    s->cpu_pic = arm_pic_init_cpu(env);
+    qdev_init_gpio_in(&dev->qdev, arm_cpu_dev_set_irq, 2);
+    if (arm_feature(env, ARM_FEATURE_M)) {
+        env->v7m.nvic = qdev_get_prop_dev(&dev->qdev, "nvic");
+        if (!env->v7m.nvic) {
+            hw_error("CPU requires NVIC");
+        }
+    }
+}
+
+static void arm_cpu_register_devices(void)
+{
+    int i;
+    CPUInfo *info;
+
+    for (i = 0; i < ARRAY_SIZE(cpu_device_name_map); i++) {
+        info = qemu_mallocz(sizeof(*info));
+        info->sysbus.qdev.props = cpu_qdev_props;
+        info->sysbus.init = arm_cpu_dev_init;
+        info->cpuname = cpu_device_name_map[i].cpuname;
+        sysbus_register_withprop(cpu_device_name_map[i].devname,
+                                 sizeof(CPUDevice),
+                                 &info->sysbus);
+    }
+}
+
+device_init(arm_cpu_register_devices)
diff --git a/hw/arm_boot.c b/hw/arm_boot.c
index acfa67e..95dd532 100644
--- a/hw/arm_boot.c
+++ b/hw/arm_boot.c
@@ -10,6 +10,7 @@
 #include "hw.h"
 #include "arm-misc.h"
 #include "sysemu.h"
+#include "boards.h"
 
 #define KERNEL_ARGS_ADDR 0x100
 #define KERNEL_LOAD_ADDR 0x00010000
@@ -260,3 +261,24 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info 
*info)
             set_kernel_args(info, initrd_size, info->loader_start);
     }
 }
+
+static struct arm_boot_info arm_linux_binfo;
+static void arm_linux_bootstrap(ram_addr_t ram_size, const char *boot_device,
+    const char *kernel_filename, const char *kernel_cmdline,
+    const char *initrd_filename)
+{
+    arm_linux_binfo.ram_size = ram_size;
+    arm_linux_binfo.kernel_filename = kernel_filename;
+    arm_linux_binfo.kernel_cmdline = kernel_cmdline;
+    arm_linux_binfo.initrd_filename = initrd_filename;
+    arm_linux_binfo.board_id = get_bootstrap_arg_int("board-id", 0);
+    arm_linux_binfo.loader_start = get_bootstrap_arg_int("loader-start", 0);
+    arm_load_kernel(first_cpu, &arm_linux_binfo);
+}
+
+static void arm_boot_register(void)
+{
+    register_machine_bootstrap("arm-linux", arm_linux_bootstrap);
+}
+
+machine_init(arm_boot_register);
diff --git a/hw/boards.h b/hw/boards.h
index f6733b7..1733e8b 100644
--- a/hw/boards.h
+++ b/hw/boards.h
@@ -23,5 +23,14 @@ typedef struct QEMUMachine {
 int qemu_register_machine(QEMUMachine *m);
 
 extern QEMUMachine *current_machine;
+extern QEMUMachine dt_machine;
+
+typedef void (*machine_bootstrapfn)(ram_addr_t ram_size,
+    const char *boot_device,
+    const char *kernel_filename, const char *kernel_cmdline,
+    const char *initrd_filename);
+
+void register_machine_bootstrap(const char *name, machine_bootstrapfn fn);
+uint64_t get_bootstrap_arg_int(const char *name, uint64_t def);
 
 #endif
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();
+        }
+    }
+}
+
+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;
+}
+
+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", &regs_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)
+{
+    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/i2c.c b/hw/i2c.c
index 8a0c4d7..4e53c09 100644
--- a/hw/i2c.c
+++ b/hw/i2c.c
@@ -68,7 +68,7 @@ int i2c_start_transfer(i2c_bus *bus, int address, int recv)
     DeviceState *qdev;
     i2c_slave *slave = NULL;
 
-    LIST_FOREACH(qdev, &bus->qbus.children, sibling) {
+    TAILQ_FOREACH(qdev, &bus->qbus.children, sibling) {
         slave = I2C_SLAVE_FROM_QDEV(qdev);
         if (slave->address == address)
             break;
@@ -152,9 +152,15 @@ static void i2c_slave_qdev_init(DeviceState *dev, 
DeviceInfo *base)
     info->init(s);
 }
 
+static const DevicePropList i2c_bus_props[] = {
+      {.name = "address", .type = PROP_TYPE_INT},
+      {.name = NULL}
+};
 void i2c_register_slave(const char *name, int size, I2CSlaveInfo *info)
 {
+    assert(!info->qdev.init);
     assert(size >= sizeof(i2c_slave));
+    info->qdev.props = qdev_merge_props(info->qdev.props, i2c_bus_props);
     info->qdev.init = i2c_slave_qdev_init;
     info->qdev.bus_type = BUS_TYPE_I2C;
     qdev_register(name, size, &info->qdev);
diff --git a/hw/pci.c b/hw/pci.c
index 8c904ba..487017d 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -924,6 +924,7 @@ void pci_qdev_register(const char *name, int size, 
pci_qdev_initfn init)
     PCIDeviceInfo *info;
 
     info = qemu_mallocz(sizeof(*info));
+    assert(!info->qdev.init);
     info->init = init;
     info->qdev.init = pci_qdev_init;
     info->qdev.bus_type = BUS_TYPE_PCI;
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.
+
+   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);
     }
 }
diff --git a/hw/qdev.h b/hw/qdev.h
index 7291805..2e2c5f3 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -4,6 +4,18 @@
 #include "hw.h"
 #include "sys-queue.h"
 
+typedef enum {
+    PROP_TYPE_INT,
+    PROP_TYPE_PTR,
+    PROP_TYPE_DEV,
+    PROP_TYPE_ARRAY
+} DevicePropType;
+
+typedef struct {
+    const char *name;
+    DevicePropType type;
+} DevicePropList;
+
 typedef struct DeviceType DeviceType;
 
 typedef struct DeviceProperty DeviceProperty;
@@ -22,7 +34,9 @@ struct DeviceState {
     qemu_irq *gpio_in;
     LIST_HEAD(, BusState) child_bus;
     NICInfo *nd;
-    LIST_ENTRY(DeviceState) sibling;
+    TAILQ_ENTRY(DeviceState) sibling;
+    int fdt_offset;
+    uint32_t phandle;
 };
 
 typedef enum {
@@ -37,7 +51,7 @@ struct BusState {
     DeviceState *parent;
     const char *name;
     BusType type;
-    LIST_HEAD(, DeviceState) children;
+    TAILQ_HEAD(, DeviceState) children;
     LIST_ENTRY(BusState) sibling;
 };
 
@@ -50,6 +64,8 @@ void qdev_free(DeviceState *dev);
 /* Set properties between creation and init.  */
 void qdev_set_prop_int(DeviceState *dev, const char *name, uint64_t value);
 void qdev_set_prop_dev(DeviceState *dev, const char *name, DeviceState *value);
+void qdev_set_prop_array(DeviceState *dev, const char *name, uint32_t *data,
+                         int len);
 void qdev_set_prop_ptr(DeviceState *dev, const char *name, void *value);
 void qdev_set_netdev(DeviceState *dev, NICInfo *nd);
 
@@ -58,27 +74,30 @@ void qdev_connect_gpio_out(DeviceState *dev, int n, 
qemu_irq pin);
 
 BusState *qdev_get_child_bus(DeviceState *dev, const char *name);
 
-/*** Device API.  ***/
+/*** Internal device tree bits  ***/
 
-typedef enum {
-    PROP_TYPE_INT,
-    PROP_TYPE_PTR,
-    PROP_TYPE_DEV
-} DevicePropType;
+int qdev_device_exists(const char *name);
+DeviceState *qdev_from_phandle(uint32_t phandle);
+void qdev_fixup_devices(void);
+void dt_fixup_qdev(DeviceState *dev);
+void dt_fixup_sysbus_device(DeviceState *dev);
+DevicePropList *qdev_get_proplist(DeviceState *dev);
 
-typedef struct {
-    const char *name;
-    DevicePropType type;
-} DevicePropList;
+typedef void (*enumdevfn)(DeviceState *dev, void *opaque);
+void qdev_enumerate_child_devices(DeviceState *dev, enumdevfn cb, void 
*opaque);
+
+/*** Device API.  ***/
 
 typedef struct DeviceInfo DeviceInfo;
 
 typedef void (*qdev_initfn)(DeviceState *dev, DeviceInfo *info);
+typedef void (*qdev_late_initfn)(DeviceState *dev);
 typedef void (*SCSIAttachFn)(DeviceState *host, BlockDriverState *bdrv,
               int unit);
 
 struct DeviceInfo {
     qdev_initfn init;
+    qdev_late_initfn late_init;
     BusType bus_type;
     DevicePropList *props;
 };
@@ -96,10 +115,17 @@ CharDriverState *qdev_init_chardev(DeviceState *dev);
 
 BusState *qdev_get_parent_bus(DeviceState *dev);
 uint64_t qdev_get_prop_int(DeviceState *dev, const char *name, uint64_t def);
+/* Returns the number of elements in the array.  */
+int qdev_get_prop_array(DeviceState *dev, const char *name,
+                        const uint32_t **p);
+/* NOTE: The returned device may not have been initialized yet.  */
 DeviceState *qdev_get_prop_dev(DeviceState *dev, const char *name);
 /* FIXME: Remove opaque pointer properties.  */
 void *qdev_get_prop_ptr(DeviceState *dev, const char *name);
 
+DevicePropList *qdev_merge_props(const DevicePropList *a,
+                                 const DevicePropList *b);
+
 /* Convery from a base type to a parent type, with compile time checking.  */
 #ifdef __GNUC__
 #define DO_UPCAST(type, field, dev) ( __extension__ ( { \
diff --git a/hw/ssi.c b/hw/ssi.c
index 52b7b7c..c086f02 100644
--- a/hw/ssi.c
+++ b/hw/ssi.c
@@ -20,8 +20,8 @@ static void ssi_slave_init(DeviceState *dev, DeviceInfo 
*base_info)
     SSIBus *bus;
 
     bus = FROM_QBUS(SSIBus, qdev_get_parent_bus(dev));
-    if (LIST_FIRST(&bus->qbus.children) != dev
-        || LIST_NEXT(dev, sibling) != NULL) {
+    if (TAILQ_FIRST(&bus->qbus.children) != dev
+        || TAILQ_NEXT(dev, sibling) != NULL) {
         hw_error("Too many devices on SSI bus");
     }
 
@@ -32,6 +32,7 @@ static void ssi_slave_init(DeviceState *dev, DeviceInfo 
*base_info)
 void ssi_register_slave(const char *name, int size, SSISlaveInfo *info)
 {
     assert(size >= sizeof(SSISlave));
+    assert(!info->qdev.init);
     info->qdev.init = ssi_slave_init;
     info->qdev.bus_type = BUS_TYPE_SSI;
     qdev_register(name, size, &info->qdev);
@@ -56,7 +57,7 @@ uint32_t ssi_transfer(SSIBus *bus, uint32_t val)
 {
     DeviceState *dev;
     SSISlave *slave;
-    dev = LIST_FIRST(&bus->qbus.children);
+    dev = TAILQ_FIRST(&bus->qbus.children);
     if (!dev) {
         return 0;
     }
diff --git a/hw/syborg.c b/hw/syborg.c
deleted file mode 100644
index 5ca9977..0000000
--- a/hw/syborg.c
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Syborg (Symbian Virtual Platform) reference board
- *
- * Copyright (c) 2009 CodeSourcery
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to 
deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "sysbus.h"
-#include "boards.h"
-#include "arm-misc.h"
-#include "sysemu.h"
-#include "net.h"
-
-static struct arm_boot_info syborg_binfo;
-
-static void syborg_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)
-{
-    CPUState *env;
-    qemu_irq *cpu_pic;
-    qemu_irq pic[64];
-    ram_addr_t ram_addr;
-    DeviceState *dev;
-    int i;
-
-    if (!cpu_model)
-        cpu_model = "cortex-a8";
-    env = cpu_init(cpu_model);
-    if (!env) {
-        fprintf(stderr, "Unable to find CPU definition\n");
-        exit(1);
-    }
-
-    /* RAM at address zero. */
-    ram_addr = qemu_ram_alloc(ram_size);
-    cpu_register_physical_memory(0, ram_size, ram_addr | IO_MEM_RAM);
-
-    cpu_pic = arm_pic_init_cpu(env);
-    dev = sysbus_create_simple("syborg,interrupt", 0xC0000000,
-                               cpu_pic[ARM_PIC_CPU_IRQ]);
-    for (i = 0; i < 64; i++) {
-        pic[i] = qdev_get_gpio_in(dev, i);
-    }
-
-    sysbus_create_simple("syborg,rtc", 0xC0001000, NULL);
-
-    dev = qdev_create(NULL, "syborg,timer");
-    qdev_set_prop_int(dev, "frequency", 1000000);
-    qdev_init(dev);
-    sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0xC0002000);
-    sysbus_connect_irq(sysbus_from_qdev(dev), 0, pic[1]);
-
-    sysbus_create_simple("syborg,keyboard", 0xC0003000, pic[2]);
-    sysbus_create_simple("syborg,pointer", 0xC0004000, pic[3]);
-    sysbus_create_simple("syborg,framebuffer", 0xC0005000, pic[4]);
-    sysbus_create_simple("syborg,serial", 0xC0006000, pic[5]);
-    sysbus_create_simple("syborg,serial", 0xC0007000, pic[6]);
-    sysbus_create_simple("syborg,serial", 0xC0008000, pic[7]);
-    sysbus_create_simple("syborg,serial", 0xC0009000, pic[8]);
-
-    if (nd_table[0].vlan) {
-        DeviceState *dev;
-        SysBusDevice *s;
-
-        qemu_check_nic_model(&nd_table[0], "virtio");
-        dev = qdev_create(NULL, "syborg,virtio-net");
-        qdev_set_netdev(dev, &nd_table[0]);
-        qdev_init(dev);
-        s = sysbus_from_qdev(dev);
-        sysbus_mmio_map(s, 0, 0xc000c000);
-        sysbus_connect_irq(s, 0, pic[9]);
-    }
-
-    syborg_binfo.ram_size = ram_size;
-    syborg_binfo.kernel_filename = kernel_filename;
-    syborg_binfo.kernel_cmdline = kernel_cmdline;
-    syborg_binfo.initrd_filename = initrd_filename;
-    syborg_binfo.board_id = 0;
-    arm_load_kernel(env, &syborg_binfo);
-}
-
-static QEMUMachine syborg_machine = {
-    .name = "syborg",
-    .desc = "Syborg (Symbian Virtual Platform)",
-    .init = syborg_init,
-};
-
-static void syborg_machine_init(void)
-{
-    qemu_register_machine(&syborg_machine);
-}
-
-machine_init(syborg_machine_init);
diff --git a/hw/sysbus.c b/hw/sysbus.c
index fbd2ddf..4903747 100644
--- a/hw/sysbus.c
+++ b/hw/sysbus.c
@@ -101,13 +101,16 @@ void sysbus_init_mmio_cb(SysBusDevice *dev, 
target_phys_addr_t size,
 static void sysbus_device_init(DeviceState *dev, DeviceInfo *base)
 {
     SysBusDeviceInfo *info = container_of(base, SysBusDeviceInfo, qdev);
+    SysBusDevice *s = sysbus_from_qdev(dev);
 
-    info->init(sysbus_from_qdev(dev));
+    s->info = info;
+    info->init(s);
 }
 
 void sysbus_register_withprop(const char *name, size_t size,
                               SysBusDeviceInfo *info)
 {
+    assert(!info->qdev.init);
     info->qdev.init = sysbus_device_init;
     info->qdev.bus_type = BUS_TYPE_SYSTEM;
 
diff --git a/hw/sysbus.h b/hw/sysbus.h
index 2973661..11fa8ad 100644
--- a/hw/sysbus.h
+++ b/hw/sysbus.h
@@ -11,8 +11,16 @@
 typedef struct SysBusDevice SysBusDevice;
 typedef void (*mmio_mapfunc)(SysBusDevice *dev, target_phys_addr_t addr);
 
+typedef void (*sysbus_initfn)(SysBusDevice *dev);
+
+typedef struct {
+    DeviceInfo qdev;
+    sysbus_initfn init;
+} SysBusDeviceInfo;
+
 struct SysBusDevice {
     DeviceState qdev;
+    SysBusDeviceInfo *info;
     int num_irq;
     qemu_irq irqs[QDEV_MAX_IRQ];
     qemu_irq *irqp[QDEV_MAX_IRQ];
@@ -25,17 +33,10 @@ struct SysBusDevice {
     } 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)
 
-typedef struct {
-    DeviceInfo qdev;
-    sysbus_initfn init;
-} SysBusDeviceInfo;
-
 void sysbus_register_dev(const char *name, size_t size, sysbus_initfn init);
 void sysbus_register_withprop(const char *name, size_t size,
                               SysBusDeviceInfo *info);
diff --git a/pc-bios/boards/syborg.dts b/pc-bios/boards/syborg.dts
new file mode 100644
index 0000000..39745c7
--- /dev/null
+++ b/pc-bios/boards/syborg.dts
@@ -0,0 +1,134 @@
+/ {
+    #address-cells = <1>;
+    #size-cells  = <1>;
+
+    cpus {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        cpu0: ARM,address@hidden {
+            device_type = "cpu";
+            reg = <0>;
+            #interrupt-cells = <1>;
+        };
+    };
+    address@hidden {
+        device_type = "memory";
+        reg = <0 08000000>;
+    };
+    syborg {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        qemu,fold-bus;
+        intc: address@hidden {
+            compatible = "syborg,interrupt";
+            #interrupt-cells = <1>;
+            reg = <c0000000>;
+            interrupt-controller;
+            interrupt-parent = <&cpu0>;
+            interrupts = <0>;
+            num-interrupts = <20>;
+        };
+        address@hidden {
+            compatible = "syborg,rtc";
+            reg = <c0001000>;
+        };
+        address@hidden {
+            compatible = "syborg,timer";
+            reg = <c0002000>;
+            frequency = <d#1000000>;
+            interrupts = <1>;
+            interrupt-parent = <&intc>;
+        };
+        address@hidden {
+            compatible = "syborg,keyboard";
+            reg = <c0003000>;
+            interrupts = <2>;
+            interrupt-parent = <&intc>;
+        };
+        address@hidden {
+            compatible = "syborg,pointer";
+            reg = <c0004000>;
+            interrupts = <3>;
+            interrupt-parent = <&intc>;
+        };
+        address@hidden {
+            compatible = "syborg,framebuffer";
+            reg = <c0005000>;
+            interrupts = <4>;
+            interrupt-parent = <&intc>;
+        };
+        address@hidden {
+            device_type = "serial";
+            compatible = "syborg,serial";
+            chardev = "serial0";
+            reg = <c0006000>;
+            interrupts = <5>;
+            interrupt-parent = <&intc>;
+        };
+        address@hidden {
+            device_type = "serial";
+            compatible = "syborg,serial";
+            chardev = "serial1";
+            reg = <c0007000>;
+            interrupts = <6>;
+            interrupt-parent = <&intc>;
+        };
+        address@hidden {
+            device_type = "serial";
+            compatible = "syborg,serial";
+            chardev = "serial2";
+            reg = <c0008000>;
+            interrupts = <7>;
+            interrupt-parent = <&intc>;
+        };
+        address@hidden {
+            device_type = "serial";
+            compatible = "syborg,serial";
+            chardev = "serial3";
+            reg = <c0009000>;
+            interrupts = <8>;
+            interrupt-parent = <&intc>;
+        };
+/*
+        address@hidden {
+            compatible = "syborg,hostfs";
+            reg = <c000a000>;
+            host-path = "\\svphostfs\\";
+            drive-number = <d#19>;
+        };
+        address@hidden {
+            compatible = "syborg,snapshot";
+            reg = <c000b000>;
+        };
+*/
+        address@hidden {
+            compatible = "syborg,virtio-net";
+            reg = <c000c000>;
+            interrupts = <9>;
+            interrupt-parent = <&intc>;
+        };
+/*
+        address@hidden {
+            compatible = "syborg,nand";
+            reg = <c000d000>;
+            size = <400>;
+        };
+        address@hidden {
+            compatible = "syborg,virtio-audio";
+            reg = <c000e000>;
+            interrupts = <a>;
+            interrupt-parent = <&intc>;
+        };
+        address@hidden {
+            compatible = "syborg,platform";
+            reg = <c1000000>;
+        };
+*/
+    };
+    chosen {
+        qemu {
+            bootstrap = "arm-linux";
+        };
+    };
+};
+
diff --git a/rules.mak b/rules.mak
index 8d6d96e..d332209 100644
--- a/rules.mak
+++ b/rules.mak
@@ -16,4 +16,7 @@ LINK = $(call quiet-command,$(CC) $(LDFLAGS) -o $@ $(1) 
$(ARLIBS_BEGIN) $(ARLIBS
 %.a:
        $(call quiet-command,rm -f $@ && $(AR) rcs $@ $^,"  AR    
$(TARGET_DIR)$@")
 
+%.dtb: %.dts
+       $(call quiet-command,$(DTC) -O dtb -o $@ $<, "  DTC   $(TARGET_DIR)$@")
+
 quiet-command = $(if $(V),$1,$(if $(2),@echo $2 && $1, @$1))
diff --git a/sysemu.h b/sysemu.h
index 658aeec..ec2961c 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -13,6 +13,7 @@ extern const char *bios_name;
 
 #define QEMU_FILE_TYPE_BIOS   0
 #define QEMU_FILE_TYPE_KEYMAP 1
+#define QEMU_FILE_TYPE_BOARD  2
 char *qemu_find_file(int type, const char *name);
 
 extern int vm_running;
@@ -274,4 +275,6 @@ int check_params(const char * const *params, const char 
*str);
 
 void register_devices(void);
 
+void qdev_do_late_init(void);
+
 #endif
diff --git a/vl.c b/vl.c
index f08f0f3..b5eb7e1 100644
--- a/vl.c
+++ b/vl.c
@@ -3477,11 +3477,21 @@ int qemu_register_machine(QEMUMachine *m)
 static QEMUMachine *find_machine(const char *name)
 {
     QEMUMachine *m;
+    char *filename;
+    char *buf;
 
     for(m = first_machine; m != NULL; m = m->next) {
         if (!strcmp(m->name, name))
             return m;
     }
+    buf = qemu_mallocz(strlen(name) + 5);
+    sprintf(buf, "%s.dtb", name);
+    filename = qemu_find_file(QEMU_FILE_TYPE_BOARD, buf);
+    qemu_free(buf);
+    if (filename) {
+        dt_machine.name = filename;
+        return &dt_machine;
+    }
     return NULL;
 }
 
@@ -4907,6 +4917,9 @@ char *qemu_find_file(int type, const char *name)
     case QEMU_FILE_TYPE_KEYMAP:
         subdir = "keymaps/";
         break;
+    case QEMU_FILE_TYPE_BOARD:
+        subdir = "boards/";
+        break;
     default:
         abort();
     }
@@ -4950,6 +4963,7 @@ int main(int argc, char **argv, char **envp)
     const char *loadvm = NULL;
     QEMUMachine *machine;
     const char *cpu_model;
+    const char *machine_name;
     const char *usb_devices[MAX_USB_CMDLINE];
     int usb_devices_index;
 #ifndef _WIN32
@@ -5001,7 +5015,7 @@ int main(int argc, char **argv, char **envp)
 #endif
 
     module_call_init(MODULE_INIT_MACHINE);
-    machine = find_default_machine();
+    machine_name = NULL;
     cpu_model = NULL;
     initrd_filename = NULL;
     ram_size = 0;
@@ -5085,17 +5099,7 @@ int main(int argc, char **argv, char **envp)
 
             switch(popt->index) {
             case QEMU_OPTION_M:
-                machine = find_machine(optarg);
-                if (!machine) {
-                    QEMUMachine *m;
-                    printf("Supported machines are:\n");
-                    for(m = first_machine; m != NULL; m = m->next) {
-                        printf("%-10s %s%s\n",
-                               m->name, m->desc,
-                               m->is_default ? " (default)" : "");
-                    }
-                    exit(*optarg != '?');
-                }
+                machine_name = optarg;
                 break;
             case QEMU_OPTION_cpu:
                 /* hw initialization will check this */
@@ -5703,6 +5707,22 @@ int main(int argc, char **argv, char **envp)
     }
 #endif
 
+    if (machine_name) {
+        machine = find_machine(machine_name);
+        if (!machine) {
+            QEMUMachine *m;
+            printf("Supported machines are:\n");
+            for(m = first_machine; m != NULL; m = m->next) {
+                printf("%-10s %s%s\n",
+                       m->name, m->desc,
+                       m->is_default ? " (default)" : "");
+            }
+            exit(*machine_name != '?');
+        }
+    } else {
+        machine = find_default_machine();
+    }
+
     machine->max_cpus = machine->max_cpus ?: 1; /* Default to UP */
     if (smp_cpus > machine->max_cpus) {
         fprintf(stderr, "Number of SMP cpus requested (%d), exceeds max cpus "
@@ -6045,6 +6065,7 @@ int main(int argc, char **argv, char **envp)
     machine->init(ram_size, boot_devices,
                   kernel_filename, kernel_cmdline, initrd_filename, cpu_model);
 
+    qdev_do_late_init();
 
     for (env = first_cpu; env != NULL; env = env->next_cpu) {
         for (i = 0; i < nb_numa_nodes; i++) {





reply via email to

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