qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] Machine description as data prototype, take 6 (was: [RFC] M


From: Markus Armbruster
Subject: [Qemu-devel] Machine description as data prototype, take 6 (was: [RFC] Machine description as data)
Date: Thu, 12 Mar 2009 19:43:25 +0100
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.3 (gnu/linux)

Sixth iteration of the prototype.  Work in progress, not quite ready for
merging.

New:

* SCSI.

* Multiple buses of the same kind.  Only used by SCSI for now.

* Rebased to git-svn-id: svn://svn.savannah.nongnu.org/qemu/address@hidden 
c046a42c-6fe2-441c-8c8c-71466251a162

Not in, but not forgotten either:

* A few more renames suggested by reviewers.

* Reduce unnecessary differences to IEEE 1275 trees.

Shortcuts:

* No support for systems without PCI bus.

* I didn't implement all the devices of the "pc" original.  Missing:
  - Option ROMs
  - Audio
  - Virtio block, balloon, console

* The configuration tree is simplistic.  I expect it to evolve, and I
  wouldn't exclude the possibility of wholesale replacement.

* The initial configuration tree is hardcoded in hw/pcdt.c.  It should
  be read from a configuration file.

* A bus is identified by its kind and number.  The bus number depends on
  its position in the tree.  Means for position-independent addressing
  would be nice.

* I'm hiding completely behind the existing QEMUMachine init method
  interface, in hw/pcdt.c.  I guess we'll want a QEMUMachine interface
  that allows us to move a bit more code out of hw/.

* The interface to the shared code in hw/pc.c (hw/pcint.h) is rather
  crude.

* The memory driver is PC-specific.  It should be generic and
  data-driven, but getting there isn't quite as easy as it sounds.
  Memory (and sometimes even holes) need to be allocated in just the
  right order to ensure guest physical address equals host offset for
  certain memory ranges.

* The pc-misc driver should most probably be split up some.

* hw/ppce500_mpc8544ds.c doesn't compile when I configure with fdt
  support.


 Makefile              |    1 +
 Makefile.target       |    4 +-
 dt.c                  |  645 +++++++++++++++++++++++++++++++++++++++++++++
 dt.h                  |  106 ++++++++
 hw/boards.h           |    3 +
 hw/pc.c               |   47 ++--
 hw/pcdt.c             |  691 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pci.h              |    2 +-
 hw/pcint.h            |   46 ++++
 hw/vmware_vga.c       |    4 +-
 net.c                 |    2 +-
 net.h                 |    1 +
 target-i386/machine.c |    1 +
 tree.c                |  285 ++++++++++++++++++++
 tree.h                |   41 +++
 15 files changed, 1847 insertions(+), 32 deletions(-)


diff --git a/Makefile b/Makefile
index 82fec80..04026db 100644
--- a/Makefile
+++ b/Makefile
@@ -85,6 +85,7 @@ OBJS+=sd.o ssi-sd.o
 OBJS+=bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.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+=tree.o
 
 ifdef CONFIG_BRLAPI
 OBJS+= baum.o
diff --git a/Makefile.target b/Makefile.target
index 9a2f123..221d5a3 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -505,6 +505,7 @@ 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
 ifdef CONFIG_KVM
 OBJS+=kvm.o kvm-all.o
 endif
@@ -536,6 +537,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 +590,7 @@ OBJS+= ide.o pckbd.o ps2.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 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
 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
 endif
@@ -611,7 +614,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.c b/dt.c
new file mode 100644
index 0000000..ff1ef30
--- /dev/null
+++ b/dt.c
@@ -0,0 +1,645 @@
+/*
+ * 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.
+ *
+ * The configuration data currently is hardwired to a fairly limited
+ * PC, registered as machine type "pcdt".  The nuts and bolts of PC
+ * emulation remain in pc.c, and that sharing makes the somewhat
+ * clumsy pcint.h necessary.  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 mechanism.
+ */
+
+#include <assert.h>
+#include "block.h"
+#include "cpu.h"
+#include "dt.h"
+#include "net.h"
+#include "tree.h"
+#include "sysemu.h"
+
+#ifdef HAVE_FDT
+#include <libfdt.h>
+#endif
+
+/* Forward declarations */
+static void dt_parse_prop(dt_device *dev, tree_prop *prop);
+static void dt_add_dyn_devs(tree *conf, dt_host *host,
+                            const dt_driver drvtab[], int vga_ram_size);
+static void dt_fdt_test(tree *conf);
+
+
+/* Host Configuration */
+
+struct dt_host {
+    /* connection NIC <-> VLAN */
+    tree *nic[MAX_NICS];
+    VLANState *nic_vlan[MAX_NICS];
+    /* connection drive <-> block driver state */
+    tree *drive[MAX_DRIVES];
+    BlockDriverState *drive_state[MAX_DRIVES];
+};
+
+static void dt_attach_nic(dt_host *host, int index, tree *nic, VLANState *vlan)
+{
+    host->nic[index] = nic;
+    host->nic_vlan[index] = vlan;
+}
+
+VLANState *dt_find_vlan(tree *conf, dt_host *host)
+{
+    int i;
+
+    for (i = 0; i < nb_nics; i++) {
+        if (host->nic[i] == conf)
+            return host->nic_vlan[i];
+    }
+    return NULL;
+}
+
+static void dt_attach_drive(dt_host *host, int index,
+                            tree *node, BlockDriverState *state)
+{
+    host->drive[index] = node;
+    host->drive_state[index] = state;
+}
+
+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 < nb_drives; i++) {
+        if (!host->drive[i])
+            continue;           /* TODO rm when all drive types implemented */
+        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];
+    }
+}
+
+static void dt_print_host_config(dt_host *host)
+{
+    char buf[1024];
+    int i;
+
+    for (i = 0; i < nb_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 < nb_drives; i++) {
+        if (!host->drive[i])
+            continue;
+        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);
+    }
+}
+
+
+/* Device Interface */
+
+static const dt_driver *dt_driver_by_name(const char *name,
+                                          const dt_driver drvtab[])
+{
+    int i;
+
+    for (i = 0; drvtab[i].name; i++) {
+        if (!strcmp(name, drvtab[i].name))
+            return &drvtab[i];
+    }
+    return NULL;
+}
+
+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;
+}
+
+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;
+}
+
+static 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_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;
+    LIST_INIT(&dev->reqs);
+    dev->visit = 0;
+    dev->priv = qemu_malloc(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, const dt_driver drvtab[])
+{
+    const dt_driver *drv;
+    dt_device *dev;
+    tree *child;
+
+    drv = dt_driver_by_name(tree_node_name(conf), drvtab);
+    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, drvtab);
+
+    return dev;
+}
+
+static void dt_config(tree *conf, dt_host *host)
+{
+    dt_device *dev = dt_device_of(conf);
+    dt_device *bus = dt_parent_device(dev);
+    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->drv->config)
+        dev->drv->config(dev, host);
+
+    TREE_FOREACH_CHILD(child, conf)
+        dt_config(child, host);
+}
+
+tree *dt_require_named(dt_device *dev, const char *reqname)
+{
+    dt_tree_list *l = qemu_malloc(sizeof(*l));
+
+    l->conf = tree_node_by_name(dev->conf, reqname);
+    LIST_INSERT_HEAD(&dev->reqs, l, link);
+    return l->conf;
+}
+
+static void dt_do_visit(dt_device *dev,
+                        void (*fun)(dt_device *, void *arg),
+                        void *arg, int visit)
+{
+    dt_device *parent, *req, *child;
+    dt_tree_list *l;
+    tree *k;
+
+    assert(dev->visit < visit - 1);
+    dev->visit = visit - 1;
+    parent = dt_parent_device(dev);
+    if (parent && parent->visit < visit)
+        dt_do_visit(parent, fun, arg, visit);
+    LIST_FOREACH(l, &dev->reqs, link) {
+        req = dt_device_of(l->conf);
+        if (req->visit < visit)
+            dt_do_visit(req, fun, arg, visit);
+    }
+    dev->visit = visit;
+    fun(dev, arg);
+    TREE_FOREACH_CHILD(k, dev->conf) {
+        child = dt_device_of(k);
+        if (child->visit < visit - 1)
+            dt_do_visit(child, fun, arg, visit);
+    }
+}
+
+static void dt_visit(tree *node,
+                     void (*fun)(dt_device *, void *arg),
+                     void *arg)
+{
+    static int visit;
+
+    visit += 2;
+    dt_do_visit(dt_device_of(node), fun, arg, visit);
+}
+
+static void dt_init_visitor(dt_device *dev, void *arg)
+{
+    if (dev->drv->init)
+        dev->drv->init(dev);
+}
+
+static void dt_init(tree *conf)
+{
+    dt_visit(conf, dt_init_visitor, NULL);
+}
+
+static void dt_start(tree *conf)
+{
+    dt_device *dev = dt_device_of(conf);
+    tree *child;
+
+    if (dev && dev->drv->start)
+        dev->drv->start(dev);
+
+    TREE_FOREACH_CHILD(child, conf)
+        dt_start(child);
+}
+
+void dt_create_machine(tree *conf, const dt_driver drvtab[], int vga_ram_size)
+{
+    dt_host host;
+
+    memset(&host, 0, sizeof(host));
+    dt_create(conf, drvtab);
+    dt_add_dyn_devs(conf, &host, drvtab, vga_ram_size);
+    dt_config(conf, &host);
+    tree_print(conf);
+    dt_print_host_config(&host);
+    dt_fdt_test(conf);
+    dt_init(conf);
+    dt_start(conf);
+}
+
+
+/* Device properties */
+
+static const dt_prop_spec *dt_prop_spec_by_name(const dt_driver *drv,
+                                                const char *name)
+{
+    const dt_prop_spec *spec;
+
+    for (spec = drv->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 = dt_prop_spec_by_name(dev->drv, name);
+
+    if (!spec) {
+        fprintf(stderr, "A %s device has no property %s\n",
+                dev->drv->name, 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);
+    }
+}
+
+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_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, tree *node, const dt_driver drvtab[],
+                           int busno)
+{
+    dt_device *dev = dt_create(node, drvtab);
+    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, const dt_driver drvtab[],
+                       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, node, drvtab, 0);
+}
+
+static void dt_add_nic(tree *conf, dt_host *host, const dt_driver drvtab[],
+                       int index)
+{
+    NICInfo *n = &nd_table[index];
+    tree *node = 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, node, drvtab, 0);
+    dt_attach_nic(host, index, node, n->vlan);
+}
+
+static void dt_add_scsi(tree *conf, const dt_driver drvtab[], int busno)
+{
+    tree *node = tree_new_child(NULL, "scsi", NULL);
+
+    dt_add_dyn_dev(conf, node, drvtab, 0);
+    assert(dt_find_bus(conf, DT_BUS_SCSI, 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_add_drive(tree *conf, dt_host *host, const dt_driver drvtab[],
+                         int index)
+{
+    DriveInfo *d = &drives_table[index];
+    int bus, unit;
+    char buf[32];
+    tree *node;
+
+    unit = d->unit;
+    bus = d->bus;
+
+    switch (d->type) {
+    case IF_IDE:
+        /* hack to hang all IDE drives off the same node for now */
+        unit = bus * MAX_IDE_DEVS + unit;
+        bus = 0;
+        /* fall through */
+    case IF_SCSI:
+    case IF_FLOPPY:
+    case IF_VIRTIO:
+        snprintf(buf, sizeof(buf), "%s-drive", block_if_name[d->type]);
+        node = tree_new_child(NULL, strdup(buf), NULL);
+        tree_put_propf(node, "unit", "%d", unit);
+        dt_add_dyn_dev(conf, node, drvtab, bus);
+        dt_attach_drive(host, index, node, d->bdrv);
+        break;
+    case IF_PFLASH:
+    case IF_MTD:
+    case IF_SD:
+        /* TODO implement */
+        fprintf(stderr, "Ignoring unimplemented drive %s\n",
+                drives_opt[drives_table[index].drive_opt_idx].opt);
+        break;
+    }
+}
+
+static void dt_add_dyn_devs(tree *conf, dt_host *host,
+                            const dt_driver drvtab[], int vga_ram_size)
+{
+    int i, max_bus;
+
+    if (cirrus_vga_enabled || vmsvga_enabled || std_vga_enabled) {
+        dt_add_vga(conf, drvtab,
+                   cirrus_vga_enabled ? "cirrus"
+                   : vmsvga_enabled ? "vms" : "std",
+                   vga_ram_size);
+    }
+
+    for(i = 0; i < nb_nics; i++)
+        dt_add_nic(conf, host, drvtab, i);
+
+    max_bus = drive_get_max_bus(IF_SCSI);
+    for (i = 0; i <= max_bus; i++)
+        dt_add_scsi(conf, drvtab, i);
+
+    for (i = 0; i < nb_drives; i++)
+        dt_add_drive(conf, host, drvtab, i);
+}
+
+
+/* Interfacing with FDT */
+
+/*
+ * Note: translation to FDT loses the association between
+ * configuration tree nodes and devices.
+ */
+
+#ifdef HAVE_FDT
+
+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;
+}
+
+static 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);
+}
+#else
+static void dt_fdt_test(tree *conf) { }
+#endif
diff --git a/dt.h b/dt.h
new file mode 100644
index 0000000..ae035c0
--- /dev/null
+++ b/dt.h
@@ -0,0 +1,106 @@
+#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_tree_list dt_tree_list;
+typedef struct dt_driver dt_driver;
+typedef struct dt_prop_spec dt_prop_spec;
+
+
+/* Host Configuration */
+
+VLANState *dt_find_vlan(tree *conf, dt_host *host);
+void dt_find_drives(tree *conf, dt_host *host,
+                    BlockDriverState *drive[], int n);
+
+
+/* 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, and may declare
+ * initialization ordering constraints with tree_require_named().
+ *
+ * 2. Initialization: init() method runs after parent's and after that
+ * of devices declared required by config().  It should not 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 */
+    LIST_HEAD(, dt_tree_list) reqs; /* required devices */
+    int visit;                  /* for dt_visit() */
+    void *priv;                 /* device private data */
+};
+
+struct dt_tree_list {
+    tree *conf;
+    LIST_ENTRY(dt_tree_list) link;
+};
+
+typedef enum dt_bus_type {
+    DT_BUS_NONE, DT_BUS_ROOT, DT_BUS_PCI, DT_BUS_IDE, DT_BUS_SCSI,
+    DT_BUS_FLOPPY,
+} 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;
+    void (*config)(dt_device *, dt_host *);
+    void (*init)(dt_device *);
+    void (*start)(dt_device *);
+    PCIBus *(*get_pcibus)(dt_device *); /* iff device is a PCI bus */
+    int (*get_unit)(dt_device *);
+};
+
+dt_device *dt_device_of(tree *conf);
+dt_device *dt_parent_device(dt_device *dev);
+PCIBus *dt_get_pcibus(dt_device *dev);
+int dt_get_unit(dt_device *dev);
+tree *dt_require_named(dt_device *dev, const char *reqname);
+void dt_create_machine(tree *conf, const dt_driver drvtab[], int vga_ram_size);
+
+
+/* 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 }
+
+/* Canned property parse methods */
+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_macaddr(void *dst, const char *src, const dt_prop_spec *spec);
+
+#endif
diff --git a/hw/boards.h b/hw/boards.h
index 1e62594..d75e518 100644
--- a/hw/boards.h
+++ b/hw/boards.h
@@ -35,6 +35,9 @@ extern QEMUMachine axisdev88_machine;
 extern QEMUMachine pc_machine;
 extern QEMUMachine isapc_machine;
 
+/* pcdt.c */
+extern QEMUMachine pcdt_machine;
+
 /* ppc.c */
 extern QEMUMachine prep_machine;
 extern QEMUMachine core99_machine;
diff --git a/hw/pc.c b/hw/pc.c
index 69f25f3..41a0225 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -37,42 +37,35 @@
 #include "virtio-balloon.h"
 #include "virtio-console.h"
 #include "hpet_emul.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 MAX_IDE_BUS 2
-
-static fdctrl_t *floppy_controller;
-static RTCState *rtc_state;
+fdctrl_t *floppy_controller;
+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);
 }
@@ -121,7 +114,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;
 
@@ -167,7 +160,7 @@ static int cmos_get_fd_drive_type(int fd0)
     return val;
 }
 
-static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd)
+void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd)
 {
     RTCState *s = rtc_state;
     int cylinders, heads, sectors;
@@ -203,7 +196,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
@@ -230,8 +223,8 @@ static int pc_boot_set(void *opaque, const char 
*boot_device)
 }
 
 /* hd_table must contain 4 block drivers */
-static void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
-                      const char *boot_device, BlockDriverState **hd_table)
+void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
+               const char *boot_device, BlockDriverState **hd_table)
 {
     RTCState *s = rtc_state;
     int nbds, bds[3] = { 0, };
@@ -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;
 }
@@ -422,7 +415,7 @@ static void bochs_bios_write(void *opaque, uint32_t addr, 
uint32_t val)
     }
 }
 
-static void bochs_bios_init(void)
+void bochs_bios_init(void)
 {
     void *fw_cfg;
 
@@ -691,7 +684,7 @@ static void load_linux(uint8_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);
@@ -706,11 +699,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..dde6980
--- /dev/null
+++ b/hw/pcdt.c
@@ -0,0 +1,691 @@
+/*
+ * 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_ infrastructure.
+ * 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 infrastructure.
+ *
+ * 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 "console.h"
+#include "fw_cfg.h"
+#include "virtio-blk.h"
+#include "virtio-balloon.h"
+#include "virtio-console.h"
+#include "hpet_emul.h"
+#include "pcint.h"
+#include "dt.h"
+
+#ifdef TARGET_X86_64
+#define CPU_MODEL_DEFAULT "qemu32"
+#else
+#define CPU_MODEL_DEFAULT "qemu64"
+#endif
+
+
+static BlockDriverState **dt_piix3_hd(tree *piix3);
+
+/* 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),
+};
+
+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);
+    }
+}
+
+
+/* Memory Ranges */
+
+typedef struct dt_device_memrng {
+    target_phys_addr_t phys_addr;
+    ram_addr_t size;
+    ram_addr_t host_offs;
+    ram_addr_t flags;
+} dt_device_memrng;
+
+static void dt_memrng(dt_device_memrng *rng,
+                      target_phys_addr_t phys_addr, ram_addr_t size,
+                      ram_addr_t host_offs, ram_addr_t flags)
+{
+    rng->phys_addr = phys_addr;
+    rng->size = size;
+    rng->host_offs = host_offs;
+    rng->flags = flags;
+}
+
+static void dt_memrng_ram(dt_device_memrng *rng,
+                          target_phys_addr_t phys_addr, ram_addr_t size)
+{
+    dt_memrng(rng, phys_addr, size, qemu_ram_alloc(size), 0);
+}
+
+static void dt_memrng_rom(dt_device_memrng *rng,
+                          target_phys_addr_t phys_addr, ram_addr_t maxsz,
+                          const char *dir, const char *image, int top)
+{
+    char buf[1024];
+    int size;
+
+    snprintf(buf, sizeof(buf), "%s/%s", dir, image);
+    size = get_image_size(buf);
+    if (size < 0 || size > maxsz)
+        goto error;
+    if (top)
+        phys_addr = phys_addr + maxsz - size;
+    dt_memrng(rng, phys_addr, size, qemu_ram_alloc(size), IO_MEM_ROM);
+    if (load_image(buf, phys_ram_base + rng->host_offs) != size)
+        goto error;
+    return;
+
+error:
+    fprintf(stderr, "qemu: could not load image '%s'\n", buf);
+    exit(1);
+}
+
+static void dt_memrng_init(dt_device_memrng *rng, int n)
+{
+    int i;
+
+    for (i = 0; i < n; i++)
+        cpu_register_physical_memory(rng[i].phys_addr, rng[i].size,
+                                     rng[i].host_offs | rng[i].flags);
+}
+
+
+/* Memory Driver */
+
+typedef struct dt_device_memory {
+    ram_addr_t ram_size;
+    dt_device_memrng *rng;
+    int nrng;
+    /* TODO want a real memory map here */
+    ram_addr_t below_4g, above_4g;
+} dt_device_memory;
+
+static const dt_prop_spec dt_memory_props[] = {
+    DT_PROP_SPEC_INIT("ram", dt_device_memory, ram_size, ram_addr_t),
+};
+
+static void dt_memory_config(dt_device *dev, dt_host *host)
+{
+    /* TODO memory map hardcoded; get it from dev->conf instead */
+    dt_device_memory *priv = dev->priv;
+    dt_device_memrng *rng = qemu_malloc(sizeof(*rng) * 4);
+
+    if (priv->ram_size >= 0xe0000000 ) {
+        priv->above_4g = priv->ram_size - 0xe0000000;
+        priv->below_4g = 0xe0000000;
+    } else {
+        priv->below_4g = priv->ram_size;
+        priv->above_4g = 0;
+    }
+
+    dt_memrng_ram(&rng[0], 0, 0xa0000);
+    qemu_ram_alloc(0x60000);
+    dt_memrng_ram(&rng[1], 0x100000, priv->below_4g - 0x100000);
+    if (priv->above_4g)
+        abort();                /* TODO */
+    dt_memrng_rom(&rng[2], 0xe0000000, 0x20000000,
+                  bios_dir, BIOS_FILENAME, 1);
+                                /* TODO get name from dev->conf */
+    dt_memrng(&rng[3], 0xe0000, 0x20000,
+              rng[2].host_offs + rng[2].size - 0x20000, IO_MEM_ROM);
+    /* TODO option ROMs */
+
+    priv->rng = rng;
+    priv->nrng = 4;
+}
+
+static void dt_memory_init(dt_device *dev)
+{
+    dt_device_memory *priv = dev->priv;
+
+    dt_memrng_init(priv->rng, priv->nrng);
+    bochs_bios_init();
+}
+
+static ram_addr_t dt_memory_below_4g(tree *memory)
+{
+    dt_device *dev = dt_device_of(memory);
+    dt_device_memory *priv = dev->priv;
+    assert(dev->drv->init == dt_memory_init);
+    return priv->below_4g;
+}
+
+static ram_addr_t dt_memory_above_4g(tree *memory)
+{
+    dt_device *dev = dt_device_of(memory);
+    dt_device_memory *priv = dev->priv;
+    assert(dev->drv->init == dt_memory_init);
+    return priv->above_4g;
+}
+
+
+/* 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 {
+    const char *boot_device;
+    int apic;
+    int hpet;
+    qemu_irq *i8259;
+    BlockDriverState *fd[MAX_FD];
+} dt_device_pc_misc;
+
+static const dt_prop_spec dt_pc_misc_props[] = {
+    DT_PROP_SPEC_INIT("boot-device", dt_device_pc_misc, boot_device,
+                      string),
+};
+
+static void dt_pc_misc_config(dt_device *dev, dt_host *host)
+{
+    dt_device_pc_misc *priv = dev->priv;
+
+    priv->apic = 1;
+    priv->hpet = 1;
+    priv->i8259 = NULL;
+    dt_find_drives(dev->conf, host, priv->fd, ARRAY_SIZE(priv->fd));
+}
+
+static void dt_pc_misc_init(dt_device *dev)
+{
+    dt_device_pc_misc *priv = dev->priv;
+    CPUState *env;
+    qemu_irq *cpu_irq;
+    IOAPICState *ioapic;
+    PITState *pit;
+    int i;
+
+    if (priv->apic) {
+        for (env = first_cpu; env; env = env->next_cpu) {
+            env->cpuid_features |= CPUID_APIC;
+            apic_init(env);
+        }
+    }
+
+    vmport_init();
+
+    cpu_irq = qemu_allocate_irqs(pic_irq_request, NULL, 1);
+    priv->i8259 = i8259_init(cpu_irq[0]);
+    ferr_irq = priv->i8259[13];
+
+    register_ioport_write(0x80, 1, 1, ioport80_write, NULL);
+    register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL);
+
+    rtc_state = rtc_init(0x70, priv->i8259[8], 2000);
+    qemu_register_boot_set(pc_boot_set, rtc_state);
+
+    register_ioport_read(0x92, 1, 1, ioport92_read, NULL);
+    register_ioport_write(0x92, 1, 1, ioport92_write, NULL);
+
+    if (priv->apic) {
+        ioapic = ioapic_init();
+        pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic);
+    }
+
+    pit = pit_init(0x40, priv->i8259[0]);
+    pcspk_init(pit);
+    if (priv->hpet)
+        hpet_init(priv->i8259);
+
+    for(i = 0; i < MAX_SERIAL_PORTS; i++) {
+        if (serial_hds[i]) {
+            serial_init(serial_io[i], priv->i8259[serial_irq[i]], 115200,
+                        serial_hds[i]);
+        }
+    }
+
+    for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
+        if (parallel_hds[i]) {
+            parallel_init(parallel_io[i], priv->i8259[parallel_irq[i]],
+                          parallel_hds[i]);
+        }
+    }
+
+    qemu_system_hot_add_init();
+
+    i8042_init(priv->i8259[1], priv->i8259[12], 0x60);
+    DMA_init(0);
+
+    floppy_controller = fdctrl_init(priv->i8259[6], 2, 0, 0x3f0, priv->fd);
+}
+
+static void dt_pc_misc_start(dt_device *dev)
+{
+    dt_device_pc_misc *priv = dev->priv;
+    tree *memory = tree_node_by_name(dev->conf, "/memory");
+    tree *piix3 = tree_node_by_name(dev->conf, "/pci/piix3");
+
+    cmos_init(dt_memory_below_4g(memory),
+              dt_memory_above_4g(memory),
+              priv->boot_device,
+              dt_piix3_hd(piix3));
+}
+
+static qemu_irq *dt_pc_misc_i8259(tree *pc_misc)
+{
+    dt_device *dev = dt_device_of(pc_misc);
+    dt_device_pc_misc *priv = dev->priv;
+    assert(dev->drv->init == dt_pc_misc_init);
+    return priv->i8259;
+}
+
+
+/* PCI Bus Driver */
+
+typedef struct dt_device_pci {
+    PCIBus *pcibus;
+    tree *pc;
+} dt_device_pci;
+
+static void dt_pci_config(dt_device *dev, dt_host *host)
+{
+    dt_device_pci *priv = dev->priv;
+
+    priv->pcibus = NULL;
+    priv->pc = dt_require_named(dev, "/pc-misc");
+}
+
+static void dt_pci_init(dt_device *dev)
+{
+    dt_device_pci *priv = dev->priv;
+
+    priv->pcibus = i440fx_init(&i440fx_state, dt_pc_misc_i8259(priv->pc));
+}
+
+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 Driver */
+
+typedef struct dt_device_piix3 {
+    int devfn;
+    int acpi;
+    int usb;
+    tree *pc;
+    BlockDriverState *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+} dt_device_piix3;
+
+static void dt_piix3_config(dt_device *dev, dt_host *host)
+{
+    dt_device_piix3 *priv = dev->priv;
+
+    priv->devfn = -1;
+    priv->acpi = 1;
+    priv->usb = 1;
+    priv->pc = dt_require_named(dev, "/pc-misc");
+    dt_find_drives(dev->conf, host, priv->hd, ARRAY_SIZE(priv->hd));
+}
+
+static void dt_piix3_init(dt_device *dev)
+{
+    dt_device_piix3 *priv = dev->priv;
+    PCIBus *pci_bus = dt_get_pcibus(dev);
+    qemu_irq *i8259 = dt_pc_misc_i8259(priv->pc);
+    int i;
+
+    priv->devfn = piix3_init(pci_bus, priv->devfn);
+
+    pci_piix3_ide_init(pci_bus, priv->hd, priv->devfn + 1, i8259);
+
+    if (priv->usb)
+        usb_uhci_piix3_init(pci_bus, priv->devfn + 2);
+
+    if (priv->acpi) {
+        uint8_t *eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this 
persistent */
+        i2c_bus *smbus;
+
+        /* TODO: Populate SPD eeprom data.  */
+        smbus = piix4_pm_init(pci_bus, priv->devfn + 3, 0xb100, i8259[9]);
+        for (i = 0; i < 8; i++)
+            smbus_eeprom_device_init(smbus, 0x50 + i, eeprom_buf + (i * 256));
+    }
+}
+
+static BlockDriverState **dt_piix3_hd(tree *piix3)
+{
+    dt_device *dev = dt_device_of(piix3);
+    dt_device_piix3 *priv = dev->priv;
+
+    assert(dev->drv->init == dt_piix3_init);
+    return priv->hd;
+}
+
+
+/* VGA Driver */
+
+typedef struct dt_driver_vga {
+    const char *model;
+    const char *bios;
+    void (*init)(PCIBus *, uint8_t *, ram_addr_t, int);
+} dt_driver_vga;
+
+static void pci_vga_init_(PCIBus *bus, uint8_t *vga_ram_base,
+                          ram_addr_t vga_ram_offset, int vga_ram_size)
+{
+    pci_vga_init(bus, vga_ram_base, vga_ram_offset, vga_ram_size, 0, 0);
+}
+
+static dt_driver_vga dt_driver_vga_table[] = {
+    { "cirrus", VGABIOS_CIRRUS_FILENAME, pci_cirrus_vga_init },
+    { "vms", VGABIOS_FILENAME, pci_vmsvga_init },
+    { "std", VGABIOS_FILENAME, pci_vga_init_ },
+    { NULL, NULL, NULL }
+};
+
+typedef struct dt_device_vga {
+    const char *model;
+    ram_addr_t ram_size;
+    dt_device_memrng rng[1];
+    ram_addr_t ram_offs;
+    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),
+};
+
+static void dt_vga_config(dt_device *dev, dt_host *host)
+{
+    dt_device_vga *priv = dev->priv;
+    int i;
+
+    dt_memrng_rom(&priv->rng[0], 0xc0000, 0x10000,
+                  bios_dir, VGABIOS_CIRRUS_FILENAME, 0);
+                                /* TODO get name from dev->conf */
+    priv->ram_offs = qemu_ram_alloc(priv->ram_size);
+
+    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;
+
+    dt_memrng_init(priv->rng, 1);
+    priv->vga_drv->init(dt_get_pcibus(dev),
+                        phys_ram_base + priv->ram_offs,
+                        priv->ram_offs, priv->ram_size);
+}
+
+
+/* NIC Driver */
+
+typedef struct dt_device_nic {
+    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),
+};
+
+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 {
+    void *opaque;
+    BlockDriverState *hd[LSI_MAX_DEVS];
+} dt_device_scsi;
+
+static void dt_scsi_config(dt_device *dev, dt_host *host)
+{
+    dt_device_scsi *priv = dev->priv;
+
+    priv->opaque = NULL;
+    dt_find_drives(dev->conf, host, priv->hd, ARRAY_SIZE(priv->hd));
+}
+
+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->hd); i++) {
+        if (priv->hd[i])
+            lsi_scsi_attach(priv->opaque, priv->hd[i], i);
+    }
+}
+
+
+/* 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),
+};
+
+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 },
+    { "cpus", sizeof(dt_device_cpus), dt_cpus_props,
+      DT_BUS_NONE, DT_BUS_ROOT,
+      NULL, dt_cpus_init, NULL, NULL, NULL },
+    { "memory", sizeof(dt_device_memory), dt_memory_props,
+      DT_BUS_NONE, DT_BUS_ROOT,
+      dt_memory_config, dt_memory_init, NULL, NULL, NULL },
+    { "pc-misc", sizeof(dt_device_pc_misc), dt_pc_misc_props,
+      DT_BUS_FLOPPY, DT_BUS_ROOT,
+      dt_pc_misc_config, dt_pc_misc_init, dt_pc_misc_start, NULL, NULL },
+    { "pci", sizeof(dt_device_pci), NULL,
+      DT_BUS_PCI, DT_BUS_ROOT,
+      dt_pci_config, dt_pci_init, dt_pci_start, dt_pci_get_pcibus, NULL },
+    { "piix3", sizeof(dt_device_piix3), NULL,
+      DT_BUS_IDE, DT_BUS_PCI,
+      dt_piix3_config, dt_piix3_init, NULL, NULL, NULL },
+    { "vga", sizeof(dt_device_vga), dt_vga_props,
+      DT_BUS_NONE, DT_BUS_PCI,
+      dt_vga_config, dt_vga_init, NULL, NULL, NULL },
+    { "nic", sizeof(dt_device_nic), dt_nic_props,
+      DT_BUS_NONE, DT_BUS_PCI,
+      dt_nic_config, dt_nic_init, NULL, NULL, NULL },
+    { "scsi", sizeof(dt_device_scsi), NULL,
+      DT_BUS_SCSI, DT_BUS_PCI,
+      dt_scsi_config, dt_scsi_init, NULL, NULL, NULL },
+    { "ide-drive", sizeof(dt_device_drive), dt_drive_props,
+      DT_BUS_NONE, DT_BUS_IDE,
+      NULL, NULL, NULL, NULL, dt_drive_get_unit },
+    { "scsi-drive", sizeof(dt_device_drive), dt_drive_props,
+      DT_BUS_NONE, DT_BUS_SCSI,
+      NULL, NULL, NULL, NULL, dt_drive_get_unit },
+    { "floppy-drive", sizeof(dt_device_drive), dt_drive_props,
+      DT_BUS_NONE, DT_BUS_FLOPPY,
+      NULL, NULL, NULL, NULL, dt_drive_get_unit },
+    { NULL, 0, NULL, DT_BUS_NONE, DT_BUS_NONE, NULL, NULL, NULL, NULL, NULL }
+};
+
+static tree *dt_read_config(void)
+{
+    tree *root, *pci, *leaf;
+
+    /*
+     * TODO Read from config file.
+     *
+     * TODO Pretty far from a comprehensive machine configuration, but
+     * we need to start somewhere.
+     */
+    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, "memory", NULL);
+    leaf = tree_new_child(root, "pc-misc", NULL);
+    pci = tree_new_child(root, "pci", NULL);
+    leaf = tree_new_child(pci, "piix3", NULL);
+    return root;
+}
+
+/*
+ * Extract configuration from arguments and various global variables
+ * and put it into our machine configuration.
+ */
+static void dt_customize_config(tree *conf,
+                                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)
+{
+    /*
+     * TODO This is still pretty cheesy: we insert stuff into the tree
+     * at hardcoded places.  Replacing placeholders instead would be
+     * more flexible.  Another idea is to mark certain parts of the
+     * initial tree optional, and remove them here.
+     */
+    tree *node;
+
+    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);
+
+    node = tree_node_by_name(conf, "/memory");
+    tree_put_propf(node, "ram", "%#lx", (unsigned long)ram_size);
+
+    node = tree_node_by_name(conf, "/pc-misc");
+    tree_put_propf(node, "boot-device", "%s", boot_device);
+
+    /* Unimplemented stuff */
+    if (kernel_filename)
+        abort();                /* TODO */
+}
+
+static void pc_init_dt(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;
+
+    conf = dt_read_config();
+    if (!conf)
+        exit(1);
+    tree_print(conf);
+    dt_customize_config(conf, ram_size, vga_ram_size, boot_device,
+                        kernel_filename, kernel_cmdline, initrd_filename,
+                        cpu_model);
+    dt_create_machine(conf, dt_driver_table, vga_ram_size);
+}
+
+QEMUMachine pcdt_machine = {
+    .name = "pcdt",
+    .desc = "Standard PC (device tree)",
+    .init = pc_init_dt,
+    .ram_require = VGA_RAM_SIZE + PC_MAX_BIOS_SIZE,
+    .max_cpus = 255,
+};
diff --git a/hw/pci.h b/hw/pci.h
index b955f39..935fafd 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -273,7 +273,7 @@ void *lsi_scsi_init(PCIBus *bus, int devfn);
 
 /* vmware_vga.c */
 void pci_vmsvga_init(PCIBus *bus, uint8_t *vga_ram_base,
-                     unsigned long vga_ram_offset, int vga_ram_size);
+                     ram_addr_t vga_ram_offset, int vga_ram_size);
 
 /* usb-uhci.c */
 void usb_uhci_piix3_init(PCIBus *bus, int devfn);
diff --git a/hw/pcint.h b/hw/pcint.h
new file mode 100644
index 0000000..f18da67
--- /dev/null
+++ b/hw/pcint.h
@@ -0,0 +1,46 @@
+/*
+ * 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 PC_MAX_BIOS_SIZE (4 * 1024 * 1024)
+
+#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 RTCState *rtc_state;
+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];
+extern fdctrl_t *floppy_controller;
+
+/* 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_hd(int type_ofs, int info_ofs, BlockDriverState *hd);
+void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
+               const char *boot_device, BlockDriverState **hd_table);
+
+#endif
diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c
index 5c271e6..45fdbc8 100644
--- a/hw/vmware_vga.c
+++ b/hw/vmware_vga.c
@@ -1122,7 +1122,7 @@ static int vmsvga_load(struct vmsvga_state_s *s, QEMUFile 
*f)
 }
 
 static void vmsvga_init(struct vmsvga_state_s *s,
-                uint8_t *vga_ram_base, unsigned long vga_ram_offset,
+                uint8_t *vga_ram_base, ram_addr_t vga_ram_offset,
                 int vga_ram_size)
 {
     s->vram = vga_ram_base;
@@ -1216,7 +1216,7 @@ static void pci_vmsvga_map_mem(PCIDevice *pci_dev, int 
region_num,
 #define PCI_CLASS_HEADERTYPE_00h       0x00
 
 void pci_vmsvga_init(PCIBus *bus, uint8_t *vga_ram_base,
-                     unsigned long vga_ram_offset, int vga_ram_size)
+                     ram_addr_t vga_ram_offset, int vga_ram_size)
 {
     struct pci_vmsvga_state_s *s;
 
diff --git a/net.c b/net.c
index c853daf..831b002 100644
--- a/net.c
+++ b/net.c
@@ -157,7 +157,7 @@ static void hex_dump(FILE *f, const uint8_t *buf, int size)
 }
 #endif
 
-static int parse_macaddr(uint8_t *macaddr, const char *p)
+int parse_macaddr(uint8_t *macaddr, const char *p)
 {
     int i;
     char *last_char;
diff --git a/net.h b/net.h
index 1a51be7..54bdf80 100644
--- a/net.h
+++ b/net.h
@@ -47,6 +47,7 @@ int qemu_can_send_packet(VLANClientState *vc);
 ssize_t qemu_sendv_packet(VLANClientState *vc, const struct iovec *iov,
                           int iovcnt);
 void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size);
+int parse_macaddr(uint8_t *macaddr, const char *p);
 void qemu_format_nic_info_str(VLANClientState *vc, uint8_t macaddr[6]);
 void qemu_check_nic_model(NICInfo *nd, const char *model);
 void qemu_check_nic_model_list(NICInfo *nd, const char * const *models,
diff --git a/target-i386/machine.c b/target-i386/machine.c
index 1cf49d5..34a7b4d 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);
 }
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




reply via email to

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