qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH 4/4] Machine description as data


From: Markus Armbruster
Subject: [Qemu-devel] [PATCH 4/4] Machine description as data
Date: Thu, 30 Apr 2009 18:03:48 +0200

Signed-off-by: Markus Armbruster <address@hidden>
---
 Makefile              |    1 +
 Makefile.target       |    7 +-
 dt-fdt.c              |  123 +++++++
 dt-host.c             |  176 +++++++++
 dt.c                  |  600 +++++++++++++++++++++++++++++++
 dt.h                  |  150 ++++++++
 hw/boards.h           |    3 +
 hw/pc.c               |   53 ++--
 hw/pcdt.c             |  941 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pcidt.c            |   67 ++++
 hw/pcint.h            |   47 +++
 target-i386/machine.c |    1 +
 tree.c                |  285 +++++++++++++++
 tree.h                |   41 +++
 14 files changed, 2464 insertions(+), 31 deletions(-)
 create mode 100644 dt-fdt.c
 create mode 100644 dt-host.c
 create mode 100644 dt.c
 create mode 100644 dt.h
 create mode 100644 hw/pcdt.c
 create mode 100644 hw/pcidt.c
 create mode 100644 hw/pcint.h
 create mode 100644 tree.c
 create mode 100644 tree.h

diff --git a/Makefile b/Makefile
index 0f40cda..7c06e12 100644
--- a/Makefile
+++ b/Makefile
@@ -100,6 +100,7 @@ OBJS+=bt-hci-csr.o
 OBJS+=buffered_file.o migration.o migration-tcp.o net.o qemu-sockets.o
 OBJS+=qemu-char.o aio.o net-checksum.o savevm.o cache-utils.o
 OBJS+=msmouse.o ps2.o
+OBJS+=tree.o
 
 ifdef CONFIG_BRLAPI
 OBJS+= baum.o
diff --git a/Makefile.target b/Makefile.target
index 82ada5a..7b7d77d 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -503,6 +503,10 @@ OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o 
machine.o dma-helpers.o
 # need to fix this properly
 OBJS+=virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o
 OBJS+=fw_cfg.o
+OBJS+=dt.o dt-host.o pcidt.o
+ifdef FDT_LIBS
+OBJS+= dt-fdt.o
+endif
 ifdef CONFIG_KVM
 OBJS+=kvm.o kvm-all.o
 endif
@@ -534,6 +538,7 @@ endif
 ifdef CONFIG_OSS
 LIBS += $(CONFIG_OSS_LIB)
 endif
+LIBS+= $(FDT_LIBS)
 
 SOUND_HW = sb16.o es1370.o ac97.o
 ifdef CONFIG_ADLIB
@@ -588,6 +593,7 @@ OBJS+= ide.o pckbd.o vga.o $(SOUND_HW) dma.o
 OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
 OBJS+= cirrus_vga.o apic.o ioapic.o parallel.o acpi.o piix_pci.o
 OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o
+OBJS+= pcdt.o
 OBJS += device-hotplug.o pci-hotplug.o smbios.o
 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
 endif
@@ -611,7 +617,6 @@ OBJS+= ppc440.o ppc440_bamboo.o
 OBJS+= ppce500_pci.o ppce500_mpc8544ds.o
 ifdef FDT_LIBS
 OBJS+= device_tree.o
-LIBS+= $(FDT_LIBS)
 endif
 ifdef CONFIG_KVM
 OBJS+= kvm_ppc.o
diff --git a/dt-fdt.c b/dt-fdt.c
new file mode 100644
index 0000000..375e50a
--- /dev/null
+++ b/dt-fdt.c
@@ -0,0 +1,123 @@
+/*
+ * QEMU PC System Emulator
+ *
+ * Copyright (C) 2009 Red Hat, Inc., Markus Armbruster <address@hidden>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Interfacing with FDT */
+
+/*
+ * Note: translation to FDT loses the association between
+ * configuration tree nodes and devices.
+ */
+
+#include <libfdt.h>
+#include "dt.h"
+
+static int dt_fdt_chk(int res);
+static void dt_subtree_to_fdt(const tree *conf, void *fdt);
+
+static void *dt_tree_to_fdt(const tree *conf)
+{
+    int sz = 1024 * 1024;       /* FIXME arbitrary limit */
+    void *fdt = qemu_malloc(sz);
+
+    dt_fdt_chk(fdt_create(fdt, sz));
+    dt_subtree_to_fdt(conf, fdt);
+    dt_fdt_chk(fdt_finish(fdt));
+    return fdt;
+}
+
+static void dt_subtree_to_fdt(const tree *conf, void *fdt)
+{
+    tree_prop *prop;
+    tree *child;
+    const void *pv;
+    size_t sz;
+
+    dt_fdt_chk(fdt_begin_node(fdt, tree_node_name(conf)));
+    TREE_FOREACH_PROP(prop, conf) {
+        pv = tree_prop_value(prop, &sz);
+        dt_fdt_chk(fdt_property(fdt, tree_prop_name(prop), pv, sz));
+    }
+    TREE_FOREACH_CHILD(child, conf)
+        dt_subtree_to_fdt(child, fdt);
+    dt_fdt_chk(fdt_end_node(fdt));
+}
+
+static tree *dt_fdt_to_tree(const void *fdt)
+{
+    int offs, next, depth;
+    uint32_t tag;
+    struct fdt_property *prop;
+    tree *stack[32];            /* FIXME arbitrary limit */
+
+    stack[0] = NULL;            /* "parent" of root */
+    next = depth = 0;
+
+    for (;;) {
+        offs = next;
+        tag = fdt_next_tag(fdt, offs, &next);
+        switch (tag) {
+        case FDT_PROP:
+            /*
+             * libfdt apparently doesn't provide a way to get property
+             * by offset, do it by hand
+             */
+            assert(0 < depth && depth < ARRAY_SIZE(stack));
+            prop = (void *)(const char *)fdt + fdt_off_dt_struct(fdt) + offs;
+            tree_put_prop(stack[depth],
+                          fdt_string(fdt, fdt32_to_cpu(prop->nameoff)),
+                          prop->data,
+                          fdt32_to_cpu(prop->len));
+        case FDT_NOP:
+            break;
+        case FDT_BEGIN_NODE:
+            depth++;
+            assert(0 < depth && depth < ARRAY_SIZE(stack));
+            stack[depth] = tree_new_child(stack[depth-1],
+                                          fdt_get_name(fdt, offs, NULL),
+                                          NULL);
+            break;
+        case FDT_END_NODE:
+            depth--;
+            break;
+        case FDT_END:
+            dt_fdt_chk(next);
+            return stack[1];
+        }
+    }
+}
+
+static int dt_fdt_chk(int res)
+{
+    if (res < 0) {
+        fprintf(stderr, "%s\n", fdt_strerror(res)); /* FIXME cryptic */
+        exit(1);
+    }
+    return res;
+}
+
+void dt_fdt_test(tree *conf)
+{
+    void *fdt;
+
+    fdt = dt_tree_to_fdt(conf);
+    conf = dt_fdt_to_tree(fdt);
+    tree_print(conf);
+    free(fdt);
+}
diff --git a/dt-host.c b/dt-host.c
new file mode 100644
index 0000000..eec8dc9
--- /dev/null
+++ b/dt-host.c
@@ -0,0 +1,176 @@
+/*
+ * QEMU PC System Emulator
+ *
+ * Copyright (C) 2009 Red Hat, Inc., Markus Armbruster <address@hidden>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Host Configuration */
+
+#include <assert.h>
+#include "block.h"
+#include "dt.h"
+
+/* TODO stupid arbitrary limit */
+#define MAX_MEM (MAX_OPTION_ROMS + 4)
+
+typedef struct dt_host_memory {
+    dt_mem_loader loader;
+    target_phys_addr_t addr;
+    ram_addr_t size;
+    const void *arg;
+} dt_host_memory;
+
+struct dt_host {
+    /* driver table */
+    const dt_driver *drvtab;
+    /* connection NIC <-> VLAN */
+    int nics;
+    tree *nic[MAX_NICS];
+    VLANState *nic_vlan[MAX_NICS];
+    /* connection drive <-> block driver state */
+    int drives;
+    tree *drive[MAX_DRIVES];
+    BlockDriverState *drive_state[MAX_DRIVES];
+    /* initial memory contents */
+    int mems;
+    dt_host_memory mem[MAX_MEM];
+    /* the rest isn't configuration, it's derived from other stuff */
+    int virtio_buses;
+};
+
+dt_host *dt_create_host(const dt_driver *drvtab)
+{
+    dt_host *host = qemu_mallocz(sizeof(dt_host));
+    host->drvtab = drvtab;
+    return host;
+}
+
+const dt_driver *dt_driver_by_name(dt_host *host, const char *name)
+{
+    const dt_driver *drvtab = host->drvtab;
+    int i;
+
+    for (i = 0; drvtab[i].name; i++) {
+        if (!strcmp(name, drvtab[i].name))
+            return &drvtab[i];
+    }
+    return NULL;
+}
+
+void dt_attach_nic(dt_host *host, tree *nic, VLANState *vlan)
+{
+    assert(host->nics < MAX_NICS);
+    host->nic[host->nics] = nic;
+    host->nic_vlan[host->nics] = vlan;
+    host->nics++;
+}
+
+VLANState *dt_find_vlan(tree *conf, dt_host *host)
+{
+    int i;
+
+    for (i = 0; i < host->nics; i++) {
+        if (host->nic[i] == conf)
+            return host->nic_vlan[i];
+    }
+    return NULL;
+}
+
+void dt_attach_drive(dt_host *host, tree *node, BlockDriverState *state)
+{
+    assert(host->drives < MAX_DRIVES);
+    host->drive[host->drives] = node;
+    host->drive_state[host->drives] = state;
+    host->drives++;
+}
+
+void dt_find_drives(tree *conf, dt_host *host,
+                    BlockDriverState *drive[], int n)
+{
+    int i, unit;
+
+    memset(drive, 0, n * sizeof(drive[0]));
+
+    for (i = 0; i < host->drives; i++) {
+        if (tree_parent(host->drive[i]) != conf)
+            continue;
+        unit = dt_get_unit(dt_device_of(host->drive[i]));
+        assert(unit < n && !drive[unit]);
+        drive[unit] = host->drive_state[i];
+    }
+}
+
+void dt_config_mem(dt_host *host, dt_mem_loader loader,
+                   target_phys_addr_t addr, ram_addr_t size, const void *arg)
+{
+    assert(host->mems < MAX_MEM);
+    host->mem[host->mems].loader = loader;
+    host->mem[host->mems].addr = addr;
+    host->mem[host->mems].size = size;
+    host->mem[host->mems].arg = arg;
+    host->mems++;
+}
+
+static void dt_init_mem(dt_host *host)
+{
+    dt_host_memory *p;
+
+    for (p = host->mem; p < host->mem + host->mems; p++)
+        p->loader(p->addr, p->size, p->arg);
+}
+
+void dt_image_loader(target_phys_addr_t addr, ram_addr_t size, const void *arg)
+{
+    if (load_image_targphys(arg, addr, size) < 0) {
+        fprintf(stderr, "qemu: could not load image '%s'\n", (char *)arg);
+        exit(1);
+    }
+}
+
+int dt_alloc_virtio_bus(dt_host *host)
+{
+    return host->virtio_buses++;
+}
+
+void dt_host_init(dt_host *host)
+{
+    dt_init_mem(host);
+}
+
+void dt_print_host_config(dt_host *host)
+{
+    char buf[1024];
+    int i;
+
+    for (i = 0; i < host->nics; i++) {
+        if (!host->nic[i])
+            continue;
+        tree_path(host->nic[i], buf, sizeof(buf));
+        printf("nic#%d\tvlan %-4d\t%s\n",
+               i, host->nic_vlan[i]->id, buf);
+    }
+
+    for (i = 0; i < host->drives; i++) {
+        tree_path(host->drive[i], buf, sizeof(buf));
+        printf("drive#%d\t%-15s %s\n",
+               i, bdrv_get_device_name(host->drive_state[i]), buf);
+    }
+
+    for (i = 0; i < host->mems; i++)
+        printf("image\t" TARGET_FMT_plx " %08lx\n",
+               host->mem[i].addr, (unsigned long)host->mem[i].size);
+}
diff --git a/dt.c b/dt.c
new file mode 100644
index 0000000..1353f5c
--- /dev/null
+++ b/dt.c
@@ -0,0 +1,600 @@
+/*
+ * QEMU PC System Emulator
+ *
+ * Copyright (C) 2009 Red Hat, Inc., Markus Armbruster <address@hidden>
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Configure and build a machine from configuration data
+ *
+ * This is generic, device-independent code driven by device-dependent
+ * configuration data, talking to devices through an abstract device
+ * interface.
+ */
+
+#include <assert.h>
+#include "dt.h"
+
+/* Forward declarations */
+static void dt_parse_prop(dt_device *dev, tree_prop *prop);
+
+
+/* Device Interface */
+
+dt_device *dt_device_of(tree *conf)
+{
+    return tree_get_user(conf);
+}
+
+dt_device *dt_parent_device(dt_device *dev)
+{
+    tree *p = tree_parent(dev->conf);
+
+    return p ? dt_device_of(p) : NULL;
+}
+
+dt_device *dt_root(dt_device *dev)
+{
+    tree *p, *r;
+
+    p = dev->conf;
+    do r = p; while ((p = tree_parent(p)));
+
+    return dt_device_of(r);
+}
+
+static dt_device *dt_do_find_bus(tree *conf, dt_bus_type bus_type, int *skip)
+{
+    dt_device *dev;
+    tree *child;
+
+    dev = dt_device_of(conf);
+    if (dev->drv->bus_type == bus_type && (*skip)-- == 0)
+        return dev;
+
+    TREE_FOREACH_CHILD(child, conf) {
+        dev = dt_do_find_bus(child, bus_type, skip);
+        if (dev)
+            return dev;
+    }
+
+    return NULL;
+}
+
+dt_device *dt_find_bus(tree *conf, dt_bus_type bus_type, int busno)
+{
+    return dt_do_find_bus(conf, bus_type, &busno);
+}
+
+PCIBus *dt_get_pcibus(dt_device *dev)
+{
+    dt_device *bus = dt_parent_device(dev);
+
+    return bus->drv->get_pcibus(bus);
+}
+
+int dt_get_unit(dt_device *dev)
+{
+    return dev->drv->get_unit(dev);
+}
+
+static dt_device *dt_int_parent(dt_device *dev)
+{
+    dt_device *res;
+
+    if (dev->int_parent)
+        return dt_device_of(dev->int_parent);
+
+    res = dt_parent_device(dev);
+    return res && res->drv->get_int ? res : NULL;
+}
+
+qemu_irq *dt_get_int(dt_device *dev, int n)
+{
+    dt_device *int_parent = dt_int_parent(dev);
+    int nirq;
+    qemu_irq *res;
+
+    if (!int_parent)
+        return NULL;
+
+    res = int_parent->drv->get_int(int_parent, &nirq);
+    assert(n <= nirq);
+    return res;
+}
+
+static dt_device *dt_new_device(tree *conf, const dt_driver *drv)
+{
+    dt_device *dev;
+    tree_prop *prop;
+
+    dev = qemu_malloc(sizeof(*dev));
+    dev->conf = conf;
+    dev->drv = drv;
+    dev->int_parent = NULL;
+    TAILQ_INIT(&dev->int_children);
+    dev->priv = qemu_mallocz(drv->privsz);
+    tree_put_user(conf, dev);
+
+    TREE_FOREACH_PROP(prop, conf)
+        dt_parse_prop(dev, prop);
+
+    return dev;
+}
+
+static dt_device *dt_create(tree *conf, dt_host *host)
+{
+    const dt_driver *drv;
+    dt_device *dev;
+    tree *child;
+
+    drv = dt_driver_by_name(host, tree_node_name(conf));
+    if (!drv) {
+        fprintf(stderr, "No driver for device %s\n",
+                tree_node_name(conf));
+        exit(1);
+    }
+
+    assert((drv->bus_type == DT_BUS_PCI) == (drv->get_pcibus != NULL));
+
+    dev = dt_new_device(conf, drv);
+
+    TREE_FOREACH_CHILD(child, conf)
+        dt_create(child, host);
+
+    return dev;
+}
+
+static void dt_config(dt_device *dev, dt_host *host)
+{
+    dt_device *bus = dt_parent_device(dev);
+    dt_device *int_parent = dt_int_parent(dev);
+    dt_device *up;
+    tree *child;
+
+    if (dev->drv->parent_bus_type == DT_BUS_NONE
+        ? bus != NULL
+        : bus == NULL || bus->drv->bus_type != dev->drv->parent_bus_type) {
+        fprintf(stderr, "Device %s is not on a suitable bus\n",
+                dev->drv->name);
+        exit(1);
+    }
+
+    if (dev->int_parent) {
+        if (!int_parent->drv->get_int) {
+            fprintf(stderr, "Device %s has an invalid interrupt-parent\n",
+                    dev->drv->name);
+            exit(1);
+        }
+        for (up = int_parent; up; up = dt_int_parent(up)) {
+            if (up == dev) {
+                fprintf(stderr, "Device %s is its own interrupt ancestor\n",
+                        dev->drv->name);
+                exit(1);
+            }
+        }
+    }
+
+    if (int_parent)
+        TAILQ_INSERT_TAIL(&int_parent->int_children, dev, int_siblings);
+    else if (dev->drv->get_int)
+        TAILQ_INSERT_TAIL(&dt_root(dev)->int_children, dev, int_siblings);
+
+    if (dev->drv->config)
+        dev->drv->config(dev, host);
+
+    TREE_FOREACH_CHILD(child, dev->conf)
+        dt_config(dt_device_of(child), host);
+}
+
+static void dt_int_init(dt_device *dev)
+{
+    dt_device *child;
+
+    if (dev->drv->int_init)
+        dev->drv->int_init(dev);
+
+    TAILQ_FOREACH(child, &dev->int_children, int_siblings)
+        dt_int_init(child);
+}
+
+static void dt_init(dt_device *dev)
+{
+    tree *child;
+
+    if (dev->drv->init)
+        dev->drv->init(dev);
+
+    TREE_FOREACH_CHILD(child, dev->conf)
+        dt_init(dt_device_of(child));
+}
+
+static void dt_start(dt_device *dev)
+{
+    tree *child;
+
+    if (dev->drv->start)
+        dev->drv->start(dev);
+
+    TREE_FOREACH_CHILD(child, dev->conf)
+        dt_start(dt_device_of(child));
+}
+
+void dt_init_machine(dt_device *root, dt_host *host)
+{
+    dt_int_init(root);
+    dt_init(root);
+    dt_host_init(host);
+    dt_start(root);
+}
+
+
+/* Device properties */
+
+static const dt_prop_spec *bus_prop_spec[DT_BUS_NUM] = {
+    [DT_BUS_PCI] = dt_bus_prop_spec_pci,
+};
+
+static const dt_prop_spec *dt_prop_spec_by_name(const dt_prop_spec *prop_spec,
+                                                const char *name)
+{
+    const dt_prop_spec *spec;
+
+    for (spec = prop_spec; spec && spec->name; spec++) {
+        if (!strcmp(spec->name, name))
+            return spec;
+    }
+    return NULL;
+}
+
+static void dt_parse_prop(dt_device *dev, tree_prop *prop)
+{
+    const char *name = tree_prop_name(prop);
+    size_t size;
+    const char *val = tree_prop_value(prop, &size);
+    const dt_prop_spec *spec;
+
+    spec = dt_prop_spec_by_name(dev->drv->prop_spec, name);
+
+    if (!spec)
+        spec = dt_prop_spec_by_name(bus_prop_spec[dev->drv->parent_bus_type],
+                                    name);
+
+    if (!spec && !strcmp(name, "interrupt-parent")) {
+        dev->int_parent = tree_node_by_name(dev->conf, val);
+        return;
+    }
+
+    if (!spec) {
+        fprintf(stderr, "A %s device has no property %s\n",
+                dev->drv->name, name);
+        exit(1);
+    }
+
+    if (spec->parse) {
+        if (!val) {
+            fprintf(stderr, "Property %s of device %s needs a value\n",
+                    name, dev->drv->name);
+            exit(1);
+        }
+        if (memchr(val, 0, size) != val + size - 1
+            || spec->parse((char *)dev->priv + spec->offs, val, spec) < 0) {
+            fprintf(stderr, "Bad value %.*s for property %s of device %s\n",
+                    size, val, name, dev->drv->name);
+            exit(1);
+        }
+    } else {
+        if (val) {
+            fprintf(stderr, "Property %s of device %s doesn't take a value\n",
+                    name, dev->drv->name);
+            exit(1);
+        }
+        assert(spec->size == sizeof(int));
+        *(int *)((char *)dev->priv + spec->offs) = 1;
+    }
+}
+
+int dt_parse_string(void *dst, const char *src, const dt_prop_spec *spec)
+{
+    assert(spec->size == sizeof(char *));
+    *(const char **)dst = src;
+    return 0;
+}
+
+int dt_parse_int(void *dst, const char *src, const dt_prop_spec *spec)
+{
+    char *ep;
+    long val;
+
+    assert(spec->size == sizeof(int));
+    errno = 0;
+    val = strtol(src, &ep, 0);
+    if (*ep || ep == src || errno || (int)val != val)
+        return -1;
+    *(int *)dst = val;
+    return 0;
+}
+
+int dt_parse_ram_addr_t(void *dst, const char *src, const dt_prop_spec *spec)
+{
+    char *ep;
+    unsigned long val;
+
+    assert(spec->size == sizeof(ram_addr_t));
+    errno = 0;
+    val = strtoul(src, &ep, 0);
+    if (*ep || ep == src || errno || (ram_addr_t)val != val)
+        return -1;
+    *(ram_addr_t *)dst = val;
+    return 0;
+}
+
+int dt_parse_target_phys_addr_t(void *dst, const char *src,
+                                const dt_prop_spec *spec)
+{
+    char *ep;
+    unsigned long long val;
+
+    assert(spec->size == sizeof(target_phys_addr_t));
+    errno = 0;
+    val = strtoull(src, &ep, 0);
+    if (*ep || ep == src || errno || (target_phys_addr_t)val != val)
+        return -1;
+    *(target_phys_addr_t *)dst = val;
+    return 0;
+}
+
+int dt_parse_macaddr(void *dst, const char *src, const dt_prop_spec *spec)
+{
+    assert(spec->size == 6);
+    if (parse_macaddr(dst, src) < 0)
+        return -1;
+    return 0;
+}
+
+
+/* Dynamic Devices */
+
+static void dt_add_dyn_dev(tree *conf, dt_host *host, tree *node, int busno)
+{
+    dt_device *dev = dt_create(node, host);
+    dt_device *bus = dt_find_bus(conf, dev->drv->parent_bus_type, busno);
+
+    if (!bus) {
+        fprintf(stderr, "No suitable bus for device %s\n", dev->drv->name);
+        exit(1);
+    }
+
+    tree_insert(bus->conf, node);
+}
+
+static void dt_add_vga(tree *conf, dt_host *host,
+                       const char *model, int vga_ram_size)
+{
+    tree *node = tree_new_child(NULL, "vga", NULL);
+
+    tree_put_propf(node, "model", "%s", model);
+    tree_put_propf(node, "ram", "%#x", vga_ram_size);
+    dt_add_dyn_dev(conf, host, node, 0);
+}
+
+static void dt_add_virtio_console(tree *conf, dt_host *host, int index)
+{
+    tree *node = tree_new_child(NULL, "virtio-console", NULL);
+
+    tree_put_propf(node, "index", "%d", index);
+    dt_add_dyn_dev(conf, host, node, 0);
+}
+
+static void dt_add_nic(tree *conf, dt_host *host, NICInfo *n)
+{
+    tree *node = tree_new_child(NULL, "nic", NULL);
+
+    tree_put_propf(node, "mac", "%02x:%02x:%02x:%02x:%02x:%02x",
+                   n->macaddr[0], n->macaddr[1], n->macaddr[2],
+                   n->macaddr[3], n->macaddr[4], n->macaddr[5]);
+    tree_put_propf(node, "model", "%s",
+                   n->model ? n->model : "ne2k_pci");
+    if (n->name)
+        tree_put_propf(node, "name", "%s", n->name);
+    dt_add_dyn_dev(conf, host, node, 0);
+    dt_attach_nic(host, node, n->vlan);
+}
+
+static void dt_add_scsi(tree *conf, dt_host *host, int busno)
+{
+    tree *node = tree_new_child(NULL, "scsi", NULL);
+
+    dt_add_dyn_dev(conf, host, node, 0);
+    assert(dt_find_bus(conf, DT_BUS_SCSI, busno)->conf == node);
+}
+
+static void dt_add_virtio_block(tree *conf, dt_host *host, int busno)
+{
+    tree *node = tree_new_child(NULL, "virtio-block", NULL);
+
+    dt_add_dyn_dev(conf, host, node, 0);
+    assert(dt_find_bus(conf, DT_BUS_VIRTIO, busno)->conf == node);
+}
+
+static const char *block_if_name[] = {
+    [IF_IDE] = "ide",
+    [IF_SCSI] = "scsi",
+    [IF_FLOPPY] = "floppy",
+    [IF_PFLASH] = "pflash",
+    [IF_MTD] = "mtd",
+    [IF_SD] = "sd",
+    [IF_VIRTIO] = "virtio",
+};
+
+static void dt_do_add_drive(tree *conf, dt_host *host,
+                            int bus_type, int busno, int unit,
+                            BlockDriverState *bdrv)
+{
+    char buf[32];
+    tree *node;
+
+    snprintf(buf, sizeof(buf), "%s-drive", block_if_name[bus_type]);
+    node = tree_new_child(NULL, strdup(buf), NULL);
+    tree_put_propf(node, "unit", "%d", unit);
+    dt_add_dyn_dev(conf, host, node, busno);
+    dt_attach_drive(host, node, bdrv);
+}
+
+static void dt_add_drive(tree *conf, dt_host *host, DriveInfo *d)
+{
+    switch (d->type) {
+    case IF_IDE:
+        /* hack to hang all IDE drives off the same node for now */
+        dt_do_add_drive(conf, host,
+                        d->type, 0, d->bus * MAX_IDE_DEVS + d->unit, d->bdrv);
+        break;
+    case IF_SCSI:
+    case IF_FLOPPY:
+        dt_do_add_drive(conf, host,
+                        d->type, d->bus, d->unit, d->bdrv);
+        break;
+    case IF_VIRTIO:
+        /* See comment in on virtio block in dt_add_dyn_devs() */
+        dt_do_add_drive(conf, host,
+                        d->type, dt_alloc_virtio_bus(host), 0, d->bdrv);
+        break;
+    default:
+        /* TODO implement */
+        fprintf(stderr, "Ignoring unimplemented drive %s\n",
+                drives_opt[d->drive_opt_idx].opt);
+        break;
+    }
+}
+
+void dt_add_dyn_devs(tree *conf, dt_host *host, int vga_ram_size)
+{
+    int i, max_bus, busno;
+
+    /* VGA */
+    if (cirrus_vga_enabled || vmsvga_enabled || std_vga_enabled) {
+        dt_add_vga(conf, host,
+                   cirrus_vga_enabled ? "cirrus"
+                   : vmsvga_enabled ? "vms" : "std",
+                   vga_ram_size);
+    }
+
+    /* Virtio consoles */
+    for (i = 0; i < MAX_VIRTIO_CONSOLES; i++) {
+        if (virtcon_hds[i])
+            dt_add_virtio_console(conf, host, i);
+    }
+
+    /* NICs */
+    for(i = 0; i < nb_nics; i++)
+        dt_add_nic(conf, host, &nd_table[i]);
+
+    /*
+     * SCSI controllers
+     *
+     * This creates all controllers 0..max_bus, whether they have
+     * drives or not.  Matches pc.c behavior.
+     */
+    max_bus = drive_get_max_bus(IF_SCSI);
+    for (i = 0; i <= max_bus; i++)
+        dt_add_scsi(conf, host, i);
+
+    /*
+     * Virtio block controllers
+     *
+     * Each virtio drive is its own PCI device.  Since the device tree
+     * should reflect that, we give each device on its own virtio
+     * block controller node.
+     *
+     * DriveInfo's bus and unit are a mess.  The user can specify any
+     * bus or unit number.  An unspecified bus number defaults to
+     * zero, and an unspecified unit number defaults to the first
+     * unused one (see drive_init()).  pc.c silently ignores all
+     * virtio drives with non-zero bus number, and all drives on bus
+     * zero after the first unused unit number.  Instead of
+     * replicating that questionable behavior, simply ignore bus and
+     * unit for these drives.
+     */
+    busno = 0;
+    for (i = 0; i < nb_drives; i++) {
+        if (drives_table[i].type == IF_VIRTIO)
+            dt_add_virtio_block(conf, host, busno++);
+    }
+
+    /* Drives */
+    for (i = 0; i < nb_drives; i++)
+        dt_add_drive(conf, host, &drives_table[i]);
+}
+
+void dt_add_memory(tree *conf, dt_host *host,
+                   target_phys_addr_t addr, ram_addr_t sz, int ro)
+{
+    tree *node = tree_new_child(NULL, "memory", NULL);
+
+    tree_put_propf(node, "address", "0x" TARGET_FMT_plx, addr);
+    tree_put_propf(node, "size", "%#lx", (unsigned long)sz);
+    if (ro)
+        tree_put_prop(node, "read-only", NULL, 0);
+    dt_add_dyn_dev(conf, host, node, 0);
+}
+
+
+/* Create a configuration */
+
+#if 0 /* TODO implement */
+tree *dt_read_config(const char *name)
+{
+}
+#endif
+
+static void
+dt_int_tree_print(dt_device *dev, int indent)
+{
+    const char *name = tree_node_name(dev->conf);
+    dt_device *child;
+
+    printf("%*s%s {\n", indent, "", *name ? name : "/");
+    TAILQ_FOREACH(child, &dev->int_children, int_siblings)
+        dt_int_tree_print(child, indent + 4);
+    printf("%*s};\n", indent, "");
+}
+
+dt_device *dt_create_machine(tree *conf, dt_host *host, const char *cpu_model)
+{
+    tree *node;
+
+    tree_print(conf);
+
+    node = tree_node_by_name(conf, "/cpus");
+    tree_put_propf(node, "num", "%d", smp_cpus);
+    if (cpu_model)
+        tree_put_propf(node, "model", "%s", cpu_model);
+
+    return dt_create(conf, host);
+}
+
+void dt_config_machine(dt_device *root, dt_host *host)
+{
+    dt_config(root, host);
+
+    dt_print_host_config(host);
+    tree_print(root->conf);
+    dt_int_tree_print(root, 0);
+
+    dt_fdt_test(root->conf);
+}
diff --git a/dt.h b/dt.h
new file mode 100644
index 0000000..003d8d1
--- /dev/null
+++ b/dt.h
@@ -0,0 +1,150 @@
+#ifndef DT_H
+#define DT_H
+
+#include "sysemu.h"
+#include "net.h"
+#include "tree.h"
+
+typedef struct dt_host dt_host;
+typedef struct dt_device dt_device;
+typedef struct dt_driver dt_driver;
+typedef struct dt_prop_spec dt_prop_spec;
+typedef struct dt_bus_options dt_bus_options;
+
+
+/* Host Configuration */
+
+typedef void (*dt_mem_loader)(target_phys_addr_t, ram_addr_t, const void *);
+
+dt_host *dt_create_host(const dt_driver *drvtab);
+const dt_driver *dt_driver_by_name(dt_host *host, const char *name);
+void dt_attach_nic(dt_host *host, tree *nic, VLANState *vlan);
+VLANState *dt_find_vlan(tree *conf, dt_host *host);
+void dt_attach_drive(dt_host *host, tree *node, BlockDriverState *state);
+void dt_find_drives(tree *conf, dt_host *host,
+                    BlockDriverState *drive[], int n);
+void dt_config_mem(dt_host *host, dt_mem_loader loader,
+                   target_phys_addr_t addr, ram_addr_t size, const void *);
+void dt_image_loader(target_phys_addr_t addr, ram_addr_t size, const void *);
+int dt_alloc_virtio_bus(dt_host *host);
+void dt_host_init(dt_host *host);
+void dt_print_host_config(dt_host *host);
+
+
+/* Device Interface */
+
+/*
+ * Device life cycle:
+ *
+ * 1. Configuration: config() method runs after parent's.  It should
+ * initialize the device's private data from its configuration
+ * sub-tree.  It may edit the configuration sub-tree.  Private data is
+ * zeroed before config() runs.
+ *
+ * 2. Initialization: int_init() method runs after interrupt parent's.
+ * Then init() method runs after parent's.  Neither should touch the
+ * configuration tree.
+ *
+ * 3. Start: start() method runs, order is unspecified.
+ *
+ * Error handling in these driver methods: print to stderr and exit
+ * the program unsuccessfully.
+ *
+ * There is no device shutdown protocol yet.
+ */
+
+struct dt_device {
+    tree *conf;                 /* configuration sub-tree */
+    const dt_driver *drv;       /* device driver */
+    tree *int_parent;           /* interrupt parent if != tree_parent(conf) */
+    TAILQ_HEAD(, dt_device) int_children;
+    TAILQ_ENTRY(dt_device) int_siblings;
+    void *priv;                 /* device private data */
+};
+
+typedef enum dt_bus_type {
+    DT_BUS_NONE, DT_BUS_ROOT, DT_BUS_PCI, DT_BUS_ISA, DT_BUS_IDE,
+    DT_BUS_SCSI, DT_BUS_FLOPPY, DT_BUS_VIRTIO, DT_BUS_NUM
+} dt_bus_type;
+
+struct dt_driver {
+    const char *name;
+    size_t privsz;              /* size of device private data */
+    const dt_prop_spec *prop_spec; /* recognized conf node properties */
+    dt_bus_type bus_type, parent_bus_type;
+    /* live cycle methods */
+    void (*config)(dt_device *, dt_host *);
+    void (*int_init)(dt_device *);
+    void (*init)(dt_device *);
+    void (*start)(dt_device *);
+    /* def'd iff device is a PCI bus, may return NULL until after init() */
+    PCIBus *(*get_pcibus)(dt_device *);
+    /* optional, always available */
+    int (*get_unit)(dt_device *);
+    /* def'd iff is an int. ctrlr., may return NULL until after int_init() */
+    qemu_irq *(*get_int)(dt_device *, int *);
+};
+
+typedef struct dt_pcidevice {
+    int devfn;
+} dt_pcidevice;
+
+dt_device *dt_device_of(tree *conf);
+dt_device *dt_parent_device(dt_device *dev);
+dt_device *dt_root(dt_device *dev);
+dt_device *dt_find_bus(tree *conf, dt_bus_type bus_type, int busno);
+PCIBus *dt_get_pcibus(dt_device *dev);
+int dt_get_unit(dt_device *dev);
+qemu_irq *dt_get_int(dt_device *dev, int n);
+
+tree *dt_read_config(const char *name);
+dt_device *dt_create_machine(tree *conf, dt_host *host, const char *cpu_model);
+void dt_config_machine(dt_device *root, dt_host *host);
+void dt_add_dyn_devs(tree *conf, dt_host *host, int vga_ram_size);
+void dt_add_memory(tree *conf, dt_host *host,
+                   target_phys_addr_t addr, ram_addr_t sz, int ro);
+void dt_init_machine(dt_device *root, dt_host *host);
+
+
+/* Device properties */
+
+/*
+ * This is for parsing configuration tree node properties into device
+ * private data.
+ */
+
+struct dt_prop_spec {
+    const char *name;
+    ptrdiff_t offs;             /* offset in device private data */
+    size_t size;                /* size there, for sanity checking */
+    int (*parse)(void *, const char *, const dt_prop_spec *);
+};
+
+#define DT_PROP_SPEC_INIT(name, strty, member, fmt)                     \
+    { name, offsetof(strty, member), sizeof(((strty *)0)->member),      \
+      dt_parse_##fmt }
+#define DT_PROP_SPEC_SENTINEL() { NULL, 0, 0, NULL }
+
+/* Canned property parse methods */
+#define dt_parse_void NULL
+int dt_parse_string(void *dst, const char *src, const dt_prop_spec *spec);
+int dt_parse_int(void *dst, const char *src, const dt_prop_spec *spec);
+int dt_parse_ram_addr_t(void *dst, const char *src, const dt_prop_spec *spec);
+int dt_parse_target_phys_addr_t(void *dst, const char *src,
+                                const dt_prop_spec *spec);
+int dt_parse_macaddr(void *dst, const char *src, const dt_prop_spec *spec);
+int dt_parse_pci_unitaddr(void *dst, const char *src, const dt_prop_spec 
*spec);
+
+/* Bus-specific device properties */
+extern const dt_prop_spec dt_bus_prop_spec_pci[];
+
+
+/* Interfacing with FDT */
+
+#ifdef HAVE_FDT
+void dt_fdt_test(tree *conf);
+#else
+static inline void dt_fdt_test(tree *conf) { }
+#endif
+
+#endif
diff --git a/hw/boards.h b/hw/boards.h
index 5611d2c..8f56b5f 100644
--- a/hw/boards.h
+++ b/hw/boards.h
@@ -32,6 +32,9 @@ extern QEMUMachine axisdev88_machine;
 extern QEMUMachine pc_machine;
 extern QEMUMachine isapc_machine;
 
+/* pcdt.c */
+extern QEMUMachine pcdt_machine;
+
 /* xen_machine.c */
 extern QEMUMachine xenpv_machine;
 
diff --git a/hw/pc.c b/hw/pc.c
index a9a39a9..f591919 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -38,42 +38,35 @@
 #include "virtio-console.h"
 #include "hpet_emul.h"
 #include "smbios.h"
+#include "pcint.h"
 
 /* output Bochs bios info messages */
 //#define DEBUG_BIOS
 
-#define BIOS_FILENAME "bios.bin"
-#define VGABIOS_FILENAME "vgabios.bin"
-#define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin"
-
-#define PC_MAX_BIOS_SIZE (4 * 1024 * 1024)
-
 /* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables.  */
 #define ACPI_DATA_SIZE       0x10000
 #define BIOS_CFG_IOPORT 0x510
 #define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
 #define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
 
-#define MAX_IDE_BUS 2
-
 static RTCState *rtc_state;
 static PITState *pit;
 static IOAPICState *ioapic;
-static PCIDevice *i440fx_state;
+PCIDevice *i440fx_state;
 
-static void ioport80_write(void *opaque, uint32_t addr, uint32_t data)
+void ioport80_write(void *opaque, uint32_t addr, uint32_t data)
 {
 }
 
 /* MSDOS compatibility mode FPU exception support */
-static qemu_irq ferr_irq;
+qemu_irq ferr_irq;
 /* XXX: add IGNNE support */
 void cpu_set_ferr(CPUX86State *s)
 {
     qemu_irq_raise(ferr_irq);
 }
 
-static void ioportF0_write(void *opaque, uint32_t addr, uint32_t data)
+void ioportF0_write(void *opaque, uint32_t addr, uint32_t data)
 {
     qemu_irq_lower(ferr_irq);
 }
@@ -122,7 +115,7 @@ int cpu_get_pic_interrupt(CPUState *env)
     return intno;
 }
 
-static void pic_irq_request(void *opaque, int irq, int level)
+void pic_irq_request(void *opaque, int irq, int level)
 {
     CPUState *env = first_cpu;
 
@@ -204,7 +197,7 @@ static int boot_device2nibble(char boot_device)
 
 /* copy/pasted from cmos_init, should be made a general function
  and used there as well */
-static int pc_boot_set(void *opaque, const char *boot_device)
+int pc_boot_set(void *opaque, const char *boot_device)
 {
     Monitor *mon = cur_mon;
 #define PC_MAX_BOOT_DEVICES 3
@@ -231,10 +224,10 @@ static int pc_boot_set(void *opaque, const char 
*boot_device)
 }
 
 /* hd_table must contain 4 block drivers */
-static void cmos_init(RTCState *s,
-                      ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
-                      const char *boot_device, BlockDriverState **hd_table,
-                      int fd0, int fd1)
+void cmos_init(RTCState *s,
+               ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
+               const char *boot_device, BlockDriverState **hd_table,
+               int fd0, int fd1)
 {
     int nbds, bds[3] = { 0, };
     int val;
@@ -364,13 +357,13 @@ int ioport_get_a20(void)
     return ((first_cpu->a20_mask >> 20) & 1);
 }
 
-static void ioport92_write(void *opaque, uint32_t addr, uint32_t val)
+void ioport92_write(void *opaque, uint32_t addr, uint32_t val)
 {
     ioport_set_a20((val >> 1) & 1);
     /* XXX: bit 0 is fast reset */
 }
 
-static uint32_t ioport92_read(void *opaque, uint32_t addr)
+uint32_t ioport92_read(void *opaque, uint32_t addr)
 {
     return ioport_get_a20() << 1;
 }
@@ -424,7 +417,7 @@ static void bochs_bios_write(void *opaque, uint32_t addr, 
uint32_t val)
 
 extern uint64_t node_cpumask[MAX_NODES];
 
-static void bochs_bios_init(void)
+void bochs_bios_init(void)
 {
     void *fw_cfg;
     uint8_t *smbios_table;
@@ -568,10 +561,10 @@ static long get_file_size(FILE *f)
     return size;
 }
 
-static void load_linux(target_phys_addr_t option_rom,
-                       const char *kernel_filename,
-                      const char *initrd_filename,
-                      const char *kernel_cmdline)
+void load_linux(target_phys_addr_t option_rom,
+                const char *kernel_filename,
+                const char *initrd_filename,
+                const char *kernel_cmdline)
 {
     uint16_t protocol;
     uint32_t gpr[8];
@@ -722,7 +715,7 @@ static void load_linux(target_phys_addr_t option_rom,
     generate_bootsect(option_rom, gpr, seg, 0);
 }
 
-static void main_cpu_reset(void *opaque)
+void main_cpu_reset(void *opaque)
 {
     CPUState *env = opaque;
     cpu_reset(env);
@@ -737,11 +730,11 @@ static const int ide_irq[2] = { 14, 15 };
 static int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 
0x380 };
 static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
 
-static int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
-static int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 };
+int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
+int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 };
 
-static int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
-static int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 };
+int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
+int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 };
 
 #ifdef HAS_AUDIO
 static void audio_init (PCIBus *pci_bus, qemu_irq *pic)
diff --git a/hw/pcdt.c b/hw/pcdt.c
new file mode 100644
index 0000000..a2d0df1
--- /dev/null
+++ b/hw/pcdt.c
@@ -0,0 +1,941 @@
+/*
+ * QEMU PC System Emulator
+ *
+ * Copyright (C) 2009 Red Hat, Inc., Markus Armbruster <address@hidden>
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * This is a PC configured and built using the new dt.h interface.
+ * Having two PC machine types makes no sense in the long run, of
+ * course.  We want to replace pc.c eventually, and also convert other
+ * machine types to this interface.
+ *
+ * The configuration data currently is hardwired, and fairly limited.
+ *
+ * The nuts and bolts of PC emulation remain in pc.c for now, and
+ * using the stuff there makes the somewhat clumsy pcint.h necessary.
+ *
+ * The drivers here generally don't do the actual work, they just
+ * provide a common interface to existing device code.  Arguably, they
+ * should be integrated into that device code, with the goal of
+ * eventually replacing the old, ad hoc interfaces.
+ *
+ * Several drivers here are not PC-specific, e.g. drivers for various
+ * PCI devices.
+ */
+
+#include <assert.h>
+#include "hw.h"
+#include "pc.h"
+#include "fdc.h"
+#include "pci.h"
+#include "block.h"
+#include "sysemu.h"
+#include "audio/audio.h"
+#include "net.h"
+#include "smbus.h"
+#include "boards.h"
+#include "virtio-blk.h"
+#include "virtio-balloon.h"
+#include "virtio-console.h"
+#include "hpet_emul.h"
+#include "pcint.h"
+#include "dt.h"
+
+
+/* CPUs Driver */
+
+typedef struct dt_device_cpus {
+    const char *model;
+    int num;
+} dt_device_cpus;
+
+static const dt_prop_spec dt_cpus_props[] = {
+    DT_PROP_SPEC_INIT("model", dt_device_cpus, model, string),
+    DT_PROP_SPEC_INIT("num", dt_device_cpus, num, int),
+    DT_PROP_SPEC_SENTINEL()
+};
+
+static void dt_cpus_init(dt_device *dev)
+{
+    dt_device_cpus *priv = dev->priv;
+    int i;
+    CPUState *env;
+
+    for(i = 0; i < priv->num; i++) {
+        env = cpu_init(priv->model);
+        if (!env) {
+            fprintf(stderr, "Unable to find CPU definition\n");
+            exit(1);
+        }
+        if (i != 0)
+            env->halted = 1;
+        qemu_register_reset(main_cpu_reset, env);
+        if (1 /* TODO priv->apic */) {
+            env->cpuid_features |= CPUID_APIC;
+            apic_init(env);
+        }
+    }
+}
+
+
+/* Memory Driver */
+
+typedef struct dt_device_memory {
+    target_phys_addr_t addr;
+    ram_addr_t size;
+    int read_only;
+} dt_device_memory;
+
+static const dt_prop_spec dt_memory_props[] = {
+    DT_PROP_SPEC_INIT("address", dt_device_memory, addr, target_phys_addr_t),
+    DT_PROP_SPEC_INIT("size", dt_device_memory, size, ram_addr_t),
+    DT_PROP_SPEC_INIT("read-only", dt_device_memory, read_only, void),
+    DT_PROP_SPEC_SENTINEL()
+};
+
+static void dt_memory_init(dt_device *dev)
+{
+    dt_device_memory *priv = dev->priv;
+    ram_addr_t host_offs;
+
+    host_offs = qemu_ram_alloc(priv->size);
+    cpu_register_physical_memory(priv->addr, priv->size,
+                        host_offs | (priv->read_only ? IO_MEM_ROM : 0));
+
+    printf("memory " TARGET_FMT_plx " %8lx %8lx %s\n",
+           priv->addr, (unsigned long)priv->size, (unsigned long)host_offs,
+           priv->read_only ? "ro" : "rw");
+}
+
+
+/* i8254 PIT Driver */
+
+/*
+ * TODO Factor out the parts that are not PC-specific, and move them
+ * out, since there are other architectures that use it.
+ *
+ * TODO HPET should be separate from PIT.
+ */
+
+typedef struct dt_device_i8254 {
+    int hpet;
+} dt_device_i8254;
+
+static void dt_i8254_config(dt_device *dev, dt_host *host)
+{
+    dt_device_i8254 *priv = dev->priv;
+
+    priv->hpet = 1;
+}
+
+static void dt_i8254_init(dt_device *dev)
+{
+    PITState *pit;
+    qemu_irq *gsi;
+    dt_device_i8254 *priv = dev->priv;
+
+    gsi = dt_get_int(dev, 16);
+
+    pit = pit_init(0x40, gsi[0]);
+    pcspk_init(pit);
+    if (priv->hpet)
+        hpet_init(gsi);
+}
+
+
+/* PC APIC Driver */
+
+/*
+ * This driver takes care of I/O APICs and the pair of i8259 PICs.
+ * Bunching them together that way keeps the interrupt graph a tree.
+ * The result is an interrupt controller for GSIs.
+ */
+
+typedef struct dt_device_apic {
+    qemu_irq *gsi;
+} dt_device_apic;
+
+static void dt_apic_int_init(dt_device *dev)
+{
+    dt_device_apic *priv = dev->priv;
+    qemu_irq *cpu_irq;
+    IOAPICState *ioapic;
+
+    cpu_irq = qemu_allocate_irqs(pic_irq_request, NULL, 1);
+    priv->gsi = i8259_init(cpu_irq[0]);
+    ferr_irq = priv->gsi[13];
+    if (1 /* TODO priv->ioapic */) {
+        ioapic = ioapic_init();
+        pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic);
+    }
+}
+
+static qemu_irq *dt_apic_get_int(dt_device *dev, int *nirq)
+{
+    dt_device_apic *priv = dev->priv;
+
+    if (nirq)
+        *nirq = 16;
+    return priv->gsi;
+}
+
+
+/* MC146818 RTC Driver */
+
+typedef struct dt_device_MC146818 {
+    const char *boot_device;
+    ram_addr_t ram_below_4g, ram_above_4g;
+    /* TODO store cylinders, heads, sectors, translation instead of bds[] */
+    BlockDriverState *ide_bds[MAX_IDE_BUS * MAX_IDE_DEVS];
+    int fd0, fd1;
+} dt_device_MC146818;
+
+static const dt_prop_spec dt_MC146818_props[] = {
+    DT_PROP_SPEC_INIT("boot-device", dt_device_MC146818, boot_device,
+                      string),
+    DT_PROP_SPEC_INIT("ram-below-4g", dt_device_MC146818, ram_below_4g,
+                      ram_addr_t),
+    DT_PROP_SPEC_INIT("ram-above-4g", dt_device_MC146818, ram_above_4g,
+                      ram_addr_t),
+    DT_PROP_SPEC_SENTINEL()
+};
+
+static void dt_MC146818_config(dt_device *dev, dt_host *host)
+{
+    dt_device_MC146818 *priv = dev->priv;
+    BlockDriverState *fd_bds[MAX_FD];
+
+    dt_find_drives(dt_find_bus(dt_root(dev)->conf, DT_BUS_IDE, 0)->conf,
+                   host, priv->ide_bds, ARRAY_SIZE(priv->ide_bds));
+    dt_find_drives(dt_find_bus(dt_root(dev)->conf, DT_BUS_FLOPPY, 0)->conf,
+                   host, fd_bds, ARRAY_SIZE(fd_bds));
+    priv->fd0 = fdctrl_get_drive_type(fd_bds[0]);
+    priv->fd1 = fdctrl_get_drive_type(fd_bds[1]);
+}
+
+static void dt_MC146818_init(dt_device *dev)
+{
+    dt_device_MC146818 *priv = dev->priv;
+    qemu_irq *gsi = dt_get_int(dev, 16);
+    RTCState *rtc_state;
+
+    rtc_state = rtc_init(0x70, gsi[8], 2000);
+    qemu_register_boot_set(pc_boot_set, rtc_state);
+    cmos_init(rtc_state,
+              priv->ram_below_4g, priv->ram_above_4g, priv->boot_device,
+              priv->ide_bds, priv->fd0, priv->fd1);
+}
+
+
+/* PC Miscellanous Driver */
+
+/*
+ * This is a driver for a whole collection of devices.  Could be
+ * picked apart into separate drivers, I guess.
+ */
+
+typedef struct dt_device_pc_misc {
+    BlockDriverState *bds[MAX_FD];
+} dt_device_pc_misc;
+
+static void dt_pc_misc_config(dt_device *dev, dt_host *host)
+{
+    dt_device_pc_misc *priv = dev->priv;
+
+    dt_find_drives(dev->conf, host, priv->bds, ARRAY_SIZE(priv->bds));
+}
+
+static void dt_pc_misc_init(dt_device *dev)
+{
+    dt_device_pc_misc *priv = dev->priv;
+    qemu_irq *gsi;
+    int i;
+
+    gsi = dt_get_int(dev, 16);
+
+    vmport_init();
+
+    bochs_bios_init();
+
+    register_ioport_write(0x80, 1, 1, ioport80_write, NULL);
+    register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL);
+
+    register_ioport_read(0x92, 1, 1, ioport92_read, NULL);
+    register_ioport_write(0x92, 1, 1, ioport92_write, NULL);
+
+    for(i = 0; i < MAX_SERIAL_PORTS; i++) {
+        if (serial_hds[i]) {
+            serial_init(serial_io[i], gsi[serial_irq[i]], 115200,
+                        serial_hds[i]);
+        }
+    }
+
+    for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
+        if (parallel_hds[i]) {
+            parallel_init(parallel_io[i], gsi[parallel_irq[i]],
+                          parallel_hds[i]);
+        }
+    }
+
+    qemu_system_hot_add_init();
+
+    i8042_init(gsi[1], gsi[12], 0x60);
+    DMA_init(0);
+
+    fdctrl_init(gsi[6], 2, 0, 0x3f0, priv->bds);
+}
+
+
+/* PCI Bus Driver */
+
+typedef struct dt_device_pci {
+    PCIBus *pcibus;
+} dt_device_pci;
+
+static void dt_pci_init(dt_device *dev)
+{
+    dt_device_pci *priv = dev->priv;
+
+    priv->pcibus = i440fx_init(&i440fx_state, dt_get_int(dev, 16));
+}
+
+static void dt_pci_start(dt_device *dev)
+{
+    i440fx_init_memory_mappings(i440fx_state);
+}
+
+static PCIBus *dt_pci_get_pcibus(dt_device *dev)
+{
+    return ((dt_device_pci *)dev->priv)->pcibus;
+}
+
+
+/* PIIX3 IDE Driver */
+
+typedef struct dt_device_piix3_ide {
+    dt_pcidevice pci_dev;
+    BlockDriverState *bds[MAX_IDE_BUS * MAX_IDE_DEVS];
+} dt_device_piix3_ide;
+
+static void dt_piix3_ide_config(dt_device *dev, dt_host *host)
+{
+    dt_device_piix3_ide *priv = dev->priv;
+
+    dt_find_drives(dev->conf, host, priv->bds, ARRAY_SIZE(priv->bds));
+}
+
+static void dt_piix3_ide_init(dt_device *dev)
+{
+    dt_device_piix3_ide *priv = dev->priv;
+    PCIBus *pci_bus = dt_get_pcibus(dev);
+    qemu_irq *gsi = dt_get_int(dev, 16);
+
+    pci_piix3_ide_init(pci_bus, priv->bds, priv->pci_dev.devfn, gsi);
+}
+
+
+/* PIIX3 USB Driver */
+
+typedef struct dt_device_piix3_usb {
+    dt_pcidevice pci_dev;
+} dt_device_piix3_usb;
+
+static void dt_piix3_usb_init(dt_device *dev)
+{
+    dt_device_piix3_usb *priv = dev->priv;
+    PCIBus *pci_bus = dt_get_pcibus(dev);
+
+    usb_uhci_piix3_init(pci_bus, priv->pci_dev.devfn);
+}
+
+
+/* PIIX3 ACPI Driver */
+
+typedef struct dt_device_piix3_acpi {
+    dt_pcidevice pci_dev;
+} dt_device_piix3_acpi;
+
+static void dt_piix3_acpi_init(dt_device *dev)
+{
+    dt_device_piix3_acpi *priv = dev->priv;
+    int i;
+    uint8_t *eeprom_buf;
+    i2c_bus *smbus;
+    PCIBus *pci_bus = dt_get_pcibus(dev);
+    qemu_irq *gsi = dt_get_int(dev, 16);
+
+    eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */
+
+    /* TODO: Populate SPD eeprom data.  */
+    smbus = piix4_pm_init(pci_bus, priv->pci_dev.devfn, 0xb100, gsi[9]);
+    for (i = 0; i < 8; i++)
+        smbus_eeprom_device_init(smbus, 0x50 + i, eeprom_buf + (i * 256));
+}
+
+
+/* PIIX3 Driver */
+
+typedef struct dt_device_piix3 {
+    dt_pcidevice pci_dev;
+} dt_device_piix3;
+
+static void dt_piix3_init(dt_device *dev)
+{
+    dt_device_piix3 *priv = dev->priv;
+    PCIBus *pci_bus = dt_get_pcibus(dev);
+
+    piix3_init(pci_bus, priv->pci_dev.devfn);
+}
+
+static qemu_irq *dt_piix3_get_int(dt_device *dev, int *nirq)
+{
+    if (nirq)
+        *nirq = 16;
+    return dt_get_int(dev, 16);
+}
+
+
+/* VGA Driver */
+
+typedef struct dt_driver_vga {
+    const char *model;
+    void (*init)(PCIBus *, int);
+} dt_driver_vga;
+
+static void pci_vga_init_(PCIBus *bus, int vga_ram_size)
+{
+    pci_vga_init(bus, vga_ram_size, 0, 0);
+}
+
+static dt_driver_vga dt_driver_vga_table[] = {
+    { "cirrus", pci_cirrus_vga_init },
+    { "vms", pci_vmsvga_init },
+    { "std", pci_vga_init_ },
+    { NULL, NULL }
+};
+
+typedef struct dt_device_vga {
+    dt_pcidevice pci_dev;       /* TODO unused */
+    const char *model;
+    ram_addr_t ram_size;
+    dt_driver_vga *vga_drv;
+} dt_device_vga;
+
+static const dt_prop_spec dt_vga_props[] = {
+    DT_PROP_SPEC_INIT("model", dt_device_vga, model, string),
+    DT_PROP_SPEC_INIT("ram", dt_device_vga, ram_size, ram_addr_t),
+    DT_PROP_SPEC_SENTINEL()
+};
+
+static void dt_vga_config(dt_device *dev, dt_host *host)
+{
+    dt_device_vga *priv = dev->priv;
+    int i;
+
+    for (i = 0; dt_driver_vga_table[i].model; i++) {
+        if (!strcmp(dt_driver_vga_table[i].model, priv->model))
+            break;
+    }
+    if (!dt_driver_vga_table[i].model) {
+        fprintf(stderr, "Unknown VGA model %s\n", priv->model);
+        exit(1);
+    }
+    priv->vga_drv = &dt_driver_vga_table[i];
+}
+
+static void dt_vga_init(dt_device *dev)
+{
+    dt_device_vga *priv = dev->priv;
+
+    priv->vga_drv->init(dt_get_pcibus(dev), priv->ram_size);
+}
+
+
+/* NIC Driver */
+
+typedef struct dt_device_nic {
+    dt_pcidevice pci_dev;       /* TODO unused */
+    NICInfo nd;
+} dt_device_nic;
+
+static const dt_prop_spec dt_nic_props[] = {
+    DT_PROP_SPEC_INIT("model", dt_device_nic, nd.model, string),
+    DT_PROP_SPEC_INIT("mac", dt_device_nic, nd.macaddr, macaddr),
+    DT_PROP_SPEC_INIT("name", dt_device_nic, nd.name, string),
+    DT_PROP_SPEC_SENTINEL()
+};
+
+static void dt_nic_config(dt_device *dev, dt_host *host)
+{
+    dt_device_nic *priv = dev->priv;
+
+    priv->nd.vlan = dt_find_vlan(dev->conf, host);
+}
+
+static void dt_nic_init(dt_device *dev)
+{
+    dt_device_nic *priv = dev->priv;
+
+    pci_nic_init(dt_get_pcibus(dev), &priv->nd, -1, NULL);
+}
+
+
+/* SCSI Driver */
+
+typedef struct dt_device_scsi {
+    dt_pcidevice pci_dev;       /* TODO unused */
+    void *opaque;
+    BlockDriverState *bds[LSI_MAX_DEVS];
+} dt_device_scsi;
+
+static void dt_scsi_config(dt_device *dev, dt_host *host)
+{
+    dt_device_scsi *priv = dev->priv;
+
+    dt_find_drives(dev->conf, host, priv->bds, ARRAY_SIZE(priv->bds));
+}
+
+static void dt_scsi_init(dt_device *dev)
+{
+    dt_device_scsi *priv = dev->priv;
+    int i;
+
+    priv->opaque = lsi_scsi_init(dt_get_pcibus(dev), -1);
+
+    for (i = 0; i < ARRAY_SIZE(priv->bds); i++) {
+        if (priv->bds[i])
+            lsi_scsi_attach(priv->opaque, priv->bds[i], i);
+    }
+}
+
+
+/* Virtio Block Driver */
+
+typedef struct dt_device_virtio_block {
+    dt_pcidevice pci_dev;       /* TODO unused */
+    BlockDriverState *bds[1];
+} dt_device_virtio_block;
+
+static void dt_virtio_block_config(dt_device *dev, dt_host *host)
+{
+    dt_device_virtio_block *priv = dev->priv;
+
+    dt_find_drives(dev->conf, host, priv->bds, ARRAY_SIZE(priv->bds));
+}
+
+static void dt_virtio_block_init(dt_device *dev)
+{
+    dt_device_virtio_block *priv = dev->priv;
+
+    virtio_blk_init(dt_get_pcibus(dev), priv->bds[0]);
+}
+
+
+/* Virtio Balloon Driver */
+
+typedef struct dt_device_virtio_balloon {
+    dt_pcidevice pci_dev;       /* TODO unused */
+} dt_device_virtio_balloon;
+
+static void dt_virtio_balloon_init(dt_device *dev)
+{
+    virtio_balloon_init(dt_get_pcibus(dev));
+}
+
+
+/* Virtio Console Driver */
+
+typedef struct dt_device_virtio_console {
+    dt_pcidevice pci_dev;       /* TODO unused */
+    int index;
+    CharDriverState *hds;
+} dt_device_virtio_console;
+
+static const dt_prop_spec dt_virtio_console_props[] = {
+    DT_PROP_SPEC_INIT("index", dt_device_virtio_console, index, int),
+    DT_PROP_SPEC_SENTINEL()
+};
+
+static void dt_virtio_console_config(dt_device *dev, dt_host *host)
+{
+    dt_device_virtio_console *priv = dev->priv;
+
+    priv->hds = virtcon_hds[priv->index];
+}
+
+static void dt_virtio_console_init(dt_device *dev)
+{
+    dt_device_virtio_console *priv = dev->priv;
+
+    virtio_console_init(dt_get_pcibus(dev), priv->hds);
+}
+
+
+/* Drive Driver */
+
+typedef struct dt_device_drive {
+    int unit;
+} dt_device_drive;
+
+static const dt_prop_spec dt_drive_props[] = {
+    DT_PROP_SPEC_INIT("unit", dt_device_drive, unit, int),
+    DT_PROP_SPEC_SENTINEL()
+};
+
+static int dt_drive_get_unit(dt_device *dev)
+{
+    return ((dt_device_drive *)dev->priv)->unit;
+}
+
+
+/* Machine Driver */
+
+static const dt_driver dt_driver_table[] = {
+    { "", 0, NULL, DT_BUS_ROOT, DT_BUS_NONE,
+      NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL },
+    { "cpus", sizeof(dt_device_cpus), dt_cpus_props,
+      DT_BUS_NONE, DT_BUS_ROOT,
+      NULL, NULL, dt_cpus_init, NULL,
+      NULL, NULL, NULL },
+    { "memory", sizeof(dt_device_memory), dt_memory_props,
+      DT_BUS_NONE, DT_BUS_ROOT,
+      NULL, NULL, dt_memory_init, NULL,
+      NULL, NULL, NULL },
+    { "apic", sizeof(dt_device_apic), NULL,
+      DT_BUS_NONE, DT_BUS_ROOT,
+      NULL, dt_apic_int_init, NULL, NULL,
+      NULL, NULL, dt_apic_get_int },
+    { "i8254", sizeof(dt_device_i8254), NULL,
+      DT_BUS_NONE, DT_BUS_ISA,
+      dt_i8254_config, NULL, dt_i8254_init, NULL,
+      NULL, NULL, NULL },
+    { "MC146818", sizeof(dt_device_MC146818), dt_MC146818_props,
+      DT_BUS_NONE, DT_BUS_ISA,
+      dt_MC146818_config, NULL, dt_MC146818_init, NULL,
+      NULL, NULL, NULL },
+    { "pc-misc", sizeof(dt_device_pc_misc), NULL,
+      DT_BUS_FLOPPY, DT_BUS_ROOT,
+      dt_pc_misc_config, NULL, dt_pc_misc_init, NULL,
+      NULL, NULL, NULL },
+    { "pci", sizeof(dt_device_pci), NULL,
+      DT_BUS_PCI, DT_BUS_ROOT,
+      NULL, NULL, dt_pci_init, dt_pci_start,
+      dt_pci_get_pcibus, NULL, NULL },
+    { "piix3-isa-bridge", sizeof(dt_device_piix3), NULL,
+      DT_BUS_ISA, DT_BUS_PCI,
+      NULL, NULL, dt_piix3_init, NULL,
+      NULL, NULL, dt_piix3_get_int },
+    { "piix3-ide", sizeof(dt_device_piix3_ide), NULL,
+      DT_BUS_IDE, DT_BUS_PCI,
+      dt_piix3_ide_config, NULL, dt_piix3_ide_init, NULL,
+      NULL, NULL, NULL },
+    { "piix3-usb", sizeof(dt_device_piix3_usb), NULL,
+      DT_BUS_NONE, DT_BUS_PCI, /* FIXME: DT_BUS_NONE -> BUS_USB ?*/
+      NULL, NULL, dt_piix3_usb_init, NULL,
+      NULL, NULL, NULL },
+    { "piix3-acpi", sizeof(dt_device_piix3_acpi), NULL,
+      DT_BUS_NONE, DT_BUS_PCI,
+      NULL, NULL, dt_piix3_acpi_init, NULL,
+      NULL, NULL, NULL },
+    { "vga", sizeof(dt_device_vga), dt_vga_props,
+      DT_BUS_NONE, DT_BUS_PCI,
+      dt_vga_config, NULL, dt_vga_init, NULL,
+      NULL, NULL, NULL },
+    { "nic", sizeof(dt_device_nic), dt_nic_props,
+      DT_BUS_NONE, DT_BUS_PCI,
+      dt_nic_config, NULL, dt_nic_init, NULL,
+      NULL, NULL, NULL },
+    { "scsi", sizeof(dt_device_scsi), NULL,
+      DT_BUS_SCSI, DT_BUS_PCI,
+      dt_scsi_config, NULL, dt_scsi_init, NULL,
+      NULL, NULL, NULL },
+    { "virtio-block", sizeof(dt_device_virtio_block), NULL,
+      DT_BUS_VIRTIO, DT_BUS_PCI,
+      dt_virtio_block_config, NULL, dt_virtio_block_init, NULL,
+      NULL, NULL, NULL },
+    { "virtio-balloon", sizeof(dt_device_virtio_balloon), NULL,
+      DT_BUS_NONE, DT_BUS_PCI,
+      NULL, NULL, dt_virtio_balloon_init, NULL,
+      NULL, NULL, NULL },
+    { "virtio-console", sizeof(dt_device_virtio_console), 
dt_virtio_console_props,
+      DT_BUS_NONE, DT_BUS_PCI,
+      dt_virtio_console_config, NULL, dt_virtio_console_init, NULL,
+      NULL, NULL, NULL },
+    { "ide-drive", sizeof(dt_device_drive), dt_drive_props,
+      DT_BUS_NONE, DT_BUS_IDE,
+      NULL, NULL, NULL, NULL,
+      NULL, dt_drive_get_unit, NULL },
+    { "scsi-drive", sizeof(dt_device_drive), dt_drive_props,
+      DT_BUS_NONE, DT_BUS_SCSI,
+      NULL, NULL, NULL, NULL,
+      NULL, dt_drive_get_unit, NULL },
+    { "floppy-drive", sizeof(dt_device_drive), dt_drive_props,
+      DT_BUS_NONE, DT_BUS_FLOPPY,
+      NULL, NULL, NULL, NULL,
+      NULL, dt_drive_get_unit, NULL },
+    { "virtio-drive", sizeof(dt_device_drive), dt_drive_props,
+      DT_BUS_NONE, DT_BUS_VIRTIO,
+      NULL, NULL, NULL, NULL,
+      NULL, dt_drive_get_unit, NULL },
+    { NULL, 0, NULL, DT_BUS_NONE, DT_BUS_NONE,
+      NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL }
+};
+
+static tree *dt_hardcoded_config(const char *name)
+{
+#ifdef TARGET_X86_64
+#define CPU_MODEL_DEFAULT "qemu64"
+#else
+#define CPU_MODEL_DEFAULT "qemu32"
+#endif
+    tree *root, *pci, *isa, *leaf;
+
+    /*
+     * TODO Read from config file.
+     *
+     * TODO Pretty far from a comprehensive machine configuration, but
+     * we need to start somewhere.
+     */
+    if (strcmp(name, "pcdt")) {
+        fprintf(stderr, "qemu: machine %s not implemented", name);
+        exit(1);
+    }
+    root = tree_new_child(NULL, "", NULL);
+
+    leaf = tree_new_child(root, "cpus", NULL);
+    tree_put_propf(leaf, "model", "%s", CPU_MODEL_DEFAULT);
+
+    leaf = tree_new_child(root, "apic", NULL);
+
+    leaf = tree_new_child(root, "pc-misc", NULL);
+    tree_put_propf(leaf, "interrupt-parent", "/apic");
+
+    pci = tree_new_child(root, "pci", NULL);
+    tree_put_propf(pci, "interrupt-parent", "/apic");
+
+    isa = tree_new_child(pci, "piix3-isa-bridge", NULL);
+    tree_put_propf(isa, "interrupt-parent", "/apic");
+    tree_put_propf(isa, "unit-address", "1.0");
+
+    leaf = tree_new_child(isa, "i8254", NULL);
+
+    leaf = tree_new_child(isa, "MC146818", NULL);
+
+    leaf = tree_new_child(pci, "piix3-ide", NULL);
+    tree_put_propf(leaf, "interrupt-parent", "/apic");
+    tree_put_propf(leaf, "unit-address", "1.1");
+
+    if (usb_enabled) {
+        leaf = tree_new_child(pci, "piix3-usb", NULL);
+        tree_put_propf(leaf, "unit-address", "1.2");
+    }
+
+    if (acpi_enabled) {
+        leaf = tree_new_child(pci, "piix3-acpi", NULL);
+        tree_put_propf(leaf, "interrupt-parent", "/apic");
+        tree_put_propf(leaf, "unit-address", "1.3");
+    }
+
+    leaf = tree_new_child(pci, "virtio-balloon", NULL);
+    return root;
+#undef CPU_MODEL_DEFAULT
+}
+
+#define dt_read_config(name) dt_hardcoded_config((name))
+
+static void dt_add_ram(tree *conf, dt_host *host, ram_addr_t ram_size)
+{
+    ram_addr_t left, sz;
+
+    left = ram_size;
+    sz = MIN(left, 0xa0000);
+    dt_add_memory(conf, host, 0, sz, 0);
+    left -= sz;
+
+    sz = MIN(left, 0x60000);
+    left -= sz;
+
+    if (left) {
+        sz = MIN(left, 0xe0000000 - 0x100000);
+        dt_add_memory(conf, host, 0x100000, sz, 0);
+        left -= sz;
+    }
+
+#if TARGET_PHYS_ADDR_BITS > 32
+    if (left)
+        dt_add_memory(conf, host, 0x100000000ull, left, 0);
+#endif
+}
+
+static char *bios_image_fname(const char *image)
+{
+    char *fname = qemu_malloc(strlen(bios_dir) + 1 + strlen(image) + 1);
+    sprintf(fname, "%s/%s", bios_dir, image);
+    return fname;
+}
+
+static void dt_add_bios(tree *conf, dt_host *host)
+{
+    char *fname = bios_image_fname(bios_name ? bios_name : BIOS_FILENAME);
+    int size;
+    target_phys_addr_t addr;
+
+    size = get_image_size(fname);
+    if (size <= 0 || size % 0x10000 != 0 || size > 0x20000) {
+                                /* FIXME implement size > 0x20000 */
+        fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", fname);
+        exit(1);
+    }
+
+    addr = 0xffffffff - (size - 1);
+    dt_add_memory(conf, host, addr, size, 1);
+    dt_config_mem(host, dt_image_loader, addr, size, fname);
+
+    /* TODO map the same memory again */
+    addr = 0x100000 - size;
+    dt_add_memory(conf, host, addr, size, 1);
+    dt_config_mem(host, dt_image_loader, addr, size, fname);
+}
+
+static int dt_config_oprom(dt_host *host, dt_mem_loader loader,
+                           int offs, int size, const void *arg,
+                           const char *what)
+{
+    offs = (offs + 2047) & ~2047;
+
+    if (offs + size > 0x20000) {
+        if (what)
+            fprintf(stderr, "Not enough space for %s\n", what);
+        else
+            fprintf(stderr, "Not enough space for option rom '%s'\n",
+                    (char *)arg);
+        exit(1);
+    }
+
+    dt_config_mem(host, loader, 0xc0000 + offs, size, arg);
+    return offs + size;
+}
+
+static int dt_config_oprom_image(dt_host *host, int offs, const char *fname)
+{
+    int size;
+
+    size = get_image_size(fname);
+    if (size < 0) {
+        fprintf(stderr, "Could not load option rom '%s'\n", fname);
+        exit(1);
+    }
+
+    return dt_config_oprom(host, dt_image_loader, offs, size, fname, fname);
+}
+
+typedef struct dt_linux_loader_arg {
+    const char *kernel_filename;
+    const char *kernel_cmdline;
+    const char *initrd_filename;
+} dt_linux_loader_arg;
+
+static void dt_linux_loader(target_phys_addr_t addr, ram_addr_t size,
+                             const void *arg)
+{
+    const dt_linux_loader_arg *llarg = arg;
+
+    load_linux(addr, llarg->kernel_filename, llarg->initrd_filename,
+               llarg->kernel_cmdline);
+}
+
+static void dt_add_oprom(tree *conf, dt_host *host,
+                         const char *kernel_filename,
+                         const char *kernel_cmdline,
+                         const char *initrd_filename)
+{
+    int offs, i;
+    dt_linux_loader_arg *llarg;
+
+    offs = 0;
+
+    if (cirrus_vga_enabled || vmsvga_enabled || std_vga_enabled) {
+        offs = dt_config_oprom_image(host, offs, 
+                                     bios_image_fname(cirrus_vga_enabled
+                                                      ? VGABIOS_CIRRUS_FILENAME
+                                                      : VGABIOS_FILENAME));
+        /*
+         * Although video roms can grow larger than 0x8000, the first
+         * 0x8000 bytes are reserved for them.  It means we won't be
+         * looking for any other kind of option rom inside this area.
+         */
+        offs = MAX(offs, 0x8000);
+    }
+
+    if (kernel_filename) {
+        llarg = qemu_malloc(sizeof(*llarg));
+        llarg->kernel_filename = kernel_filename;
+        llarg->kernel_cmdline = kernel_cmdline;
+        llarg->initrd_filename = initrd_filename;
+        offs = dt_config_oprom(host, dt_linux_loader, offs, 2048, llarg,
+                               "kernel loader");
+    }
+
+    for (i = 0; i < nb_option_roms; i++)
+        offs = dt_config_oprom_image(host, offs, option_rom[i]);
+
+    dt_add_memory(conf, host, 0xc0000, offs, 1);
+}
+
+static void pcdt_init(ram_addr_t ram_size, int vga_ram_size,
+                      const char *boot_device,
+                      const char *kernel_filename,
+                      const char *kernel_cmdline,
+                      const char *initrd_filename,
+                      const char *cpu_model)
+{
+    tree *conf;
+    tree *node;
+    dt_host *host;
+    dt_device *root;
+
+    conf = dt_read_config(pcdt_machine.name);
+    if (!conf)
+        exit(1);
+
+    node = tree_node_by_name(conf, "/pci/piix3-isa-bridge/MC146818");
+    tree_put_propf(node, "boot-device", "%s", boot_device);
+    assert(ram_size <= 0xe0000000); /* TODO implement */
+    tree_put_propf(node, "ram-below-4g", "%#lx", (unsigned long)ram_size);
+
+    host = dt_create_host(dt_driver_table);
+    root = dt_create_machine(conf, host, cpu_model);
+
+    dt_add_ram(conf, host, ram_size);
+    dt_add_bios(conf, host);
+    dt_add_oprom(conf, host, kernel_filename, kernel_cmdline, initrd_filename);
+    dt_add_dyn_devs(conf, host, vga_ram_size);
+    dt_config_machine(root, host);
+
+    dt_init_machine(root, host);
+}
+
+QEMUMachine pcdt_machine = {
+    .name = "pcdt",
+    .desc = "Standard PC (device tree)",
+    .init = pcdt_init,
+    .max_cpus = 255,
+};
diff --git a/hw/pcidt.c b/hw/pcidt.c
new file mode 100644
index 0000000..db37311
--- /dev/null
+++ b/hw/pcidt.c
@@ -0,0 +1,67 @@
+/*
+ * QEMU PC System Emulator
+ *
+ * Copyright (C) 2009 Red Hat, Inc., Markus Armbruster <address@hidden>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* PCI Bus */
+
+#include <assert.h>
+#include "hw.h"
+#include "pci.h"
+#include "dt.h"
+
+int dt_parse_pci_unitaddr(void *dst, const char *src, const dt_prop_spec *spec)
+{
+    int devfn, slot, func;
+    unsigned long val;
+    const char *p;
+    char *e;
+
+    assert(spec->size == sizeof(int));
+
+    p = src;
+    val = strtoul(p, &e, 16);
+    if (e == p)
+        return -1;
+
+    slot = val;
+
+    if (*e == '.') {
+        p = e + 1;
+        val = strtoul(p, &e, 16);
+        if (e == p)
+            return -1;
+    } else
+        func = 0;
+
+    func = val;
+
+    devfn = PCI_DEVFN(slot, func);
+    *(int *)dst = devfn;
+
+    return 0;
+}
+
+const dt_prop_spec dt_bus_prop_spec_pci[] = {
+    /*
+     * FIXME assumes every PCI device has a dt_pcidevice at offset 0
+     * in dev->priv
+     */
+    DT_PROP_SPEC_INIT("unit-address", dt_pcidevice, devfn, pci_unitaddr),
+    DT_PROP_SPEC_SENTINEL()
+};
diff --git a/hw/pcint.h b/hw/pcint.h
new file mode 100644
index 0000000..acd645b
--- /dev/null
+++ b/hw/pcint.h
@@ -0,0 +1,47 @@
+/*
+ * Stuff shared by pc.c and dt.c
+ *
+ * See dt.c for why this should go away eventually.
+ */
+
+#ifndef HW_PC_INT_H
+#define HW_PC_INT_H
+
+#define BIOS_FILENAME "bios.bin"
+#define VGABIOS_FILENAME "vgabios.bin"
+#define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin"
+
+#define MAX_IDE_BUS 2
+
+/* TODO move to ferr stuff in cpu.h? */
+extern qemu_irq ferr_irq;
+void ioportF0_write(void *opaque, uint32_t addr, uint32_t data);
+
+/* TODO eliminate */
+extern PCIDevice *i440fx_state;
+extern int serial_io[MAX_SERIAL_PORTS];
+extern int serial_irq[MAX_SERIAL_PORTS];
+extern int parallel_io[MAX_PARALLEL_PORTS];
+extern int parallel_irq[MAX_PARALLEL_PORTS];
+
+/* TODO move to pic stuff in pc.h? */
+void pic_irq_request(void *opaque, int irq, int level);
+
+/* TODO move to a20 stuff in pc.h? */
+void ioport92_write(void *opaque, uint32_t addr, uint32_t val);
+uint32_t ioport92_read(void *opaque, uint32_t addr);
+
+void bochs_bios_init(void);
+void main_cpu_reset(void *opaque);
+void ioport80_write(void *opaque, uint32_t addr, uint32_t data);
+int pc_boot_set(void *opaque, const char *boot_device);
+void cmos_init(RTCState *s,
+               ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
+               const char *boot_device, BlockDriverState **hd_table,
+               int fd0, int fd1);
+void load_linux(target_phys_addr_t option_rom,
+                const char *kernel_filename,
+                const char *initrd_filename,
+                const char *kernel_cmdline);
+
+#endif
diff --git a/target-i386/machine.c b/target-i386/machine.c
index 1b0d36d..6465f77 100644
--- a/target-i386/machine.c
+++ b/target-i386/machine.c
@@ -7,6 +7,7 @@
 
 void register_machines(void)
 {
+    qemu_register_machine(&pcdt_machine);
     qemu_register_machine(&pc_machine);
     qemu_register_machine(&isapc_machine);
 #ifdef CONFIG_XEN
diff --git a/tree.c b/tree.c
new file mode 100644
index 0000000..da07b76
--- /dev/null
+++ b/tree.c
@@ -0,0 +1,285 @@
+/*
+ * QEMU PC System Emulator
+ *
+ * Copyright (C) 2009 Red Hat, Inc., Markus Armbruster <address@hidden>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Ye Olde Decorated Tree */
+
+#include <assert.h>
+#include "tree.h"
+#include "qemu-common.h"
+#include "sys-queue.h"
+
+struct tree {
+    const char *name;
+    LIST_HEAD(, tree_prop) props;
+    tree *parent;
+    TAILQ_HEAD(, tree) children;
+    TAILQ_ENTRY(tree) siblings;
+    void *user;
+};
+
+struct tree_prop {
+    const char *name;
+    const void *val;
+    int sz;
+    tree *owner;
+    LIST_ENTRY(tree_prop) link;
+};
+
+tree *tree_new_child(tree *parent, const char *name, void *user)
+{
+    tree *child = qemu_malloc(sizeof(*child));
+
+    child->name = name;
+    LIST_INIT(&child->props);
+    child->parent = NULL;
+    TAILQ_INIT(&child->children);
+    child->user = user;
+    if (parent)
+        tree_insert(parent, child);
+
+    return child;
+}
+
+void tree_insert(tree *parent, tree *child)
+{
+    assert(!child->parent);
+    child->parent = parent;
+    TAILQ_INSERT_TAIL(&parent->children, child, siblings);
+}
+
+const char *tree_node_name(const tree *node)
+{
+    return node->name;
+}
+
+static tree *tree_child_by_name(const tree *parent, const char *name)
+{
+    const char *slash = strchr(name, '/');
+    size_t len = slash ? slash - name : strlen(name);
+    tree *child;
+
+    TAILQ_FOREACH(child, &parent->children, siblings) {
+        if (!memcmp(child->name, name, len) && child->name[len] == 0)
+            return child;
+    }
+    return NULL;
+}
+
+tree *tree_node_by_name(const tree *node, const char *name)
+{
+    tree *child;
+    size_t len;
+
+    if (name[0] == '/') {
+        for (; node->parent; node = node->parent) ;
+        while (*name == '/') name++;
+    }
+
+    if (name[0] == 0)
+        return (tree *)node;
+
+    child = tree_child_by_name(node, name);
+    if (!child)
+        return NULL;
+
+    len = strlen(child->name);
+    if (name[len] == 0)
+        return child;
+    assert (name[len] == '/');
+
+    while (name[len] == '/') len++;
+    return tree_node_by_name(child, name + len);
+}
+
+tree_prop *tree_first_prop(const tree *node)
+{
+    return LIST_FIRST(&node->props);
+}
+
+tree_prop *tree_next_prop(const tree_prop *prop)
+{
+    return LIST_NEXT(prop, link);
+}
+
+tree_prop *tree_get_prop(const tree *node, const char *name)
+{
+    tree_prop *prop;
+
+    LIST_FOREACH(prop, &node->props, link) {
+        if (!strcmp(prop->name, name))
+            return prop;
+    }
+    return NULL;
+}
+
+const char *tree_get_prop_s(const tree *node, const char *name)
+{
+    tree_prop *prop = tree_get_prop(node, name);
+    if (!prop
+        || memchr(prop->val, 0, prop->sz) != prop->val + prop->sz - 1) {
+        errno = EINVAL;
+        return NULL;
+    }
+    return prop->val;
+}
+
+const char *tree_prop_name(const tree_prop *prop)
+{
+    return prop->name;
+}
+
+const void *tree_prop_value(const tree_prop *prop, size_t *size)
+{
+    if (size)
+        *size = prop->sz;
+    return prop->val;
+}
+
+void tree_put_prop(tree *node, const char *name,
+                   const void *val, size_t sz)
+{
+    tree_prop *prop;
+
+    prop = tree_get_prop(node, name);
+    if (!prop) {
+        prop = qemu_malloc(sizeof(*prop));
+        prop->name = name;
+        prop->owner = node;
+        LIST_INSERT_HEAD(&node->props, prop, link);
+    }
+    /* FIXME need a destructor for val */
+    prop->val = val;
+    prop->sz = sz;
+}
+
+void tree_put_propf(tree *node, const char *name, const char *fmt, ...)
+{
+    va_list ap;
+    size_t len;
+    char *buf;
+
+    va_start(ap, fmt);
+    len = vsnprintf(NULL, 0, fmt, ap);
+    va_end(ap);
+
+    buf = qemu_malloc(len + 1);
+    va_start(ap, fmt);
+    vsnprintf(buf, len + 1, fmt, ap);
+    va_end(ap);
+
+    tree_put_prop(node, name, buf, len + 1);
+}
+
+void tree_put_user(tree *node, void *user)
+{
+    node->user = user;
+}
+
+void *tree_get_user(const tree *node)
+{
+    return node->user;
+}
+
+tree *tree_parent(const tree *node)
+{
+    return node->parent;
+}
+
+tree *tree_first_child(const tree *node)
+{
+    return TAILQ_FIRST(&node->children);
+}
+
+tree *tree_sibling(const tree *node)
+{
+    return TAILQ_NEXT(node, siblings);
+}
+
+int tree_path(const tree *node, char *buf, size_t bufsz)
+{
+    char *p;
+    const tree *np;
+    size_t len, res;
+
+    p = buf + bufsz;
+    res = 0;
+    for (np = node; np->parent; np = np->parent) {
+        len = 1 + strlen(np->name);
+        res += len;
+        if (res >= bufsz)
+            continue;
+        p -= len;
+        memcpy(p + 1, np->name, len - 1);
+        p[0] = '/';
+    }
+
+    if (res == 0) {
+        if (++res < bufsz)
+            *--p = '/';
+    }
+
+    if (res < bufsz) {
+        memcpy(buf, p, res);
+        buf[res] = 0;
+    }
+
+    return res;
+}
+
+static void tree_print_sub(const tree *node, int indent)
+{
+    int i, use_str, sep;
+    const unsigned char *pv;
+    tree_prop *prop;
+    tree *child;
+
+    printf("%*s%s {\n", indent, "", node->parent ? node->name : "/");
+    LIST_FOREACH(prop, &node->props, link) {
+        printf("%*s%s", indent + 4, "", prop->name);
+        pv = prop->val;
+        if (pv) {
+            printf(" = ");
+            use_str = pv[prop->sz - 1] == 0;
+            for (i = 0; i < prop->sz - 1; i++) {
+                if (!isprint(pv[i]))
+                    use_str = 0;
+            }
+            if (use_str)
+                printf("\"%s\"", (const char *)prop->val);
+            else {
+                sep = '[';
+                for (i = 0; i < prop->sz; i++) {
+                    printf("%c%02x", sep, pv[i]);
+                    sep = ' ';
+                }
+                printf("]");
+            }
+        }
+        printf(";\n");
+    }
+    TAILQ_FOREACH(child, &node->children, siblings)
+        tree_print_sub(child, indent + 4);
+    printf("%*s};\n", indent, "");
+}
+
+void tree_print(const tree *node)
+{
+    tree_print_sub(node, 0);
+}
diff --git a/tree.h b/tree.h
new file mode 100644
index 0000000..3f3b367
--- /dev/null
+++ b/tree.h
@@ -0,0 +1,41 @@
+#ifndef TREE_H
+#define TREE_H
+
+#include <stddef.h>
+
+typedef struct tree tree;
+typedef struct tree_prop tree_prop;
+
+tree *tree_new_child(tree *parent, const char *name, void *user);
+void tree_insert(tree *parent, tree *child);
+const char *tree_node_name(const tree *node);
+tree *tree_node_by_name(const tree *node,
+                        const char *name);
+
+tree_prop *tree_first_prop(const tree *node);
+tree_prop *tree_next_prop(const tree_prop *prop);
+#define TREE_FOREACH_PROP(var, node) \
+    for (var = tree_first_prop(node); var; var = tree_next_prop(var))
+tree_prop *tree_get_prop(const tree *node, const char *name);
+const char *tree_get_prop_s(const tree *node, const char *name);
+const char *tree_prop_name(const tree_prop *prop);
+const void *tree_prop_value(const tree_prop *prop, size_t *size);
+void tree_put_prop(tree *node, const char *name,
+                   const void *val, size_t sz);
+void tree_put_propf(tree *node, const char *name,
+                    const char *fmt, ...)
+    __attribute__((format(printf,3,4)));
+
+void tree_put_user(tree *node, void *user);
+void *tree_get_user(const tree *node);
+
+tree *tree_parent(const tree *node);
+tree *tree_first_child(const tree *node);
+tree *tree_sibling(const tree *node);
+#define TREE_FOREACH_CHILD(var, node) \
+    for (var = tree_first_child(node); var; var = tree_sibling(var))
+
+int tree_path(const tree *node, char *buf, size_t bufsz);
+void tree_print(const tree *node);
+
+#endif
-- 
1.6.0.6





reply via email to

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