qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] Re: [RFC] Machine description as data


From: Markus Armbruster
Subject: [Qemu-devel] Re: [RFC] Machine description as data
Date: Fri, 17 Apr 2009 18:04:30 +0200
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.3 (gnu/linux)

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

New:

* Support arbitrary interrupt trees.

* PIC and PIT have been split off pc-misc (Marcelo Tosatti)

* PIIX3 split into its functions: ISA-bridge, IDE, USB, ACPI.  ISA
  devices (other than pc-misc) are now on the ISA-bridge (Marcelo
  Tosatti)

* RTC has been split off pc-misc.

* The memory driver is no longer PC-specific.  Instead, PC-specific code
  adds appropriate memory nodes to the tree.

* Option ROMs work, including -kernel.

* Command line options -usb and -no-acpi now work.

* Backed out the new machine creation interface for now.  We don't need
  it to get machine-specific and generic parts seperated cleanly.  In
  fact, it got in the way, because it wasn't quite right.

* 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:
  - Audio
  - RAM above 4g

* 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 dt_hardcoded_config().
  It should be read from a configuration file.

* The new, unified device API is implemented as wrappers around the old
  APIs.  Done that way because we want to explore the new API with
  minimal impact to the rest of the code.  Once we're satisfied with the
  new API, the old APIs should be replaced.

* 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.

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

* The pc-misc driver should be split up completely.

* The drivers for the four PIIX3 functions share their common PCI device
  number via a global variable.

* BIOS larger than 128KiB isn't implemented.  pc.c loads the BIOS once
  and maps it in two locations.  pcdt.c loads and maps it twice.

Bugs (last checked in 7th iteration):

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

* If I configure both a virtio block device and a virtio console, the
  Linux guest kernel hangs.  The same happens when I move virtio code in
  pc.c in an otherwise unmodified QEMU so that balloon and console are
  initialized earlier.


 Makefile              |    1 
 Makefile.target       |    7 
 dt-fdt.c              |  123 ++++++
 dt-host.c             |  160 ++++++++
 dt.c                  |  619 +++++++++++++++++++++++++++++++++
 dt.h                  |  141 +++++++
 hw/boards.h           |    3 
 hw/pc.c               |   55 +--
 hw/pcdt.c             |  915 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pcint.h            |   50 ++
 net.c                 |    2 
 net.h                 |    1 
 target-i386/machine.c |    1 
 tree.c                |  285 +++++++++++++++
 tree.h                |   41 ++
 15 files changed, 2371 insertions(+), 33 deletions(-)


diff --git a/Makefile b/Makefile
index 76e83ba..6b6b090 100644
--- a/Makefile
+++ b/Makefile
@@ -99,6 +99,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 e9b039d..0d3f94d 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -499,6 +499,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
+ifdef FDT_LIBS
+OBJS+= dt-fdt.o
+endif
 ifdef CONFIG_KVM
 OBJS+=kvm.o kvm-all.o
 endif
@@ -530,6 +534,7 @@ endif
 ifdef CONFIG_OSS
 LIBS += $(CONFIG_OSS_LIB)
 endif
+LIBS+= $(FDT_LIBS)
 
 SOUND_HW = sb16.o es1370.o ac97.o
 ifdef CONFIG_ADLIB
@@ -576,6 +581,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
 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
 endif
@@ -599,7 +605,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..0de08a3
--- /dev/null
+++ b/dt-host.c
@@ -0,0 +1,160 @@
+/*
+ * 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 {
+    /* 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(void)
+{
+    return qemu_mallocz(sizeof(dt_host));
+}
+
+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..20d53ac
--- /dev/null
+++ b/dt.c
@@ -0,0 +1,619 @@
+/*
+ * 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 "block.h"
+#include "cpu.h"
+#include "dt.h"
+#include "net.h"
+#include "tree.h"
+#include "sysemu.h"
+
+/* Forward declarations */
+static void dt_parse_prop(dt_device *dev, tree_prop *prop);
+
+
+/* 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;
+}
+
+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->visit = 0;
+    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, 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(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_create_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 *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 && !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, 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_virtio_console(tree *conf, const dt_driver drvtab[],
+                                  int index)
+{
+    tree *node = tree_new_child(NULL, "virtio-console", NULL);
+
+    tree_put_propf(node, "index", "%d", index);
+    dt_add_dyn_dev(conf, node, drvtab, 0);
+}
+
+static void dt_add_nic(tree *conf, dt_host *host, const dt_driver drvtab[],
+                       NICInfo *n)
+{
+    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, 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 void dt_add_virtio_block(tree *conf, const dt_driver drvtab[],
+                                int busno)
+{
+    tree *node = tree_new_child(NULL, "virtio-block", NULL);
+
+    dt_add_dyn_dev(conf, node, drvtab, 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,
+                            const dt_driver drvtab[],
+                            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, node, drvtab, busno);
+    dt_attach_drive(host, node, bdrv);
+}
+
+static void dt_add_drive(tree *conf, dt_host *host, const dt_driver drvtab[],
+                         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, drvtab,
+                        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, drvtab,
+                        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, drvtab,
+                        d->type, dt_alloc_virtio_bus(host), 0, d->bdrv);
+        break;
+    case IF_PFLASH:
+    case IF_MTD:
+    case IF_SD:
+        /* TODO implement */
+        fprintf(stderr, "Ignoring unimplemented drive %s\n",
+                drives_opt[d->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, busno;
+
+    /* VGA */
+    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);
+    }
+
+    /* Virtio consoles */
+    for (i = 0; i < MAX_VIRTIO_CONSOLES; i++) {
+        if (virtcon_hds[i])
+            dt_add_virtio_console(conf, drvtab, i);
+    }
+
+    /* NICs */
+    for(i = 0; i < nb_nics; i++)
+        dt_add_nic(conf, host, drvtab, &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, drvtab, 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, drvtab, busno++);
+    }
+
+    /* Drives */
+    for (i = 0; i < nb_drives; i++)
+        dt_add_drive(conf, host, drvtab, &drives_table[i]);
+}
+
+tree *dt_add_memory(tree *conf, target_phys_addr_t addr, ram_addr_t sz, int ro)
+{
+    tree *node;
+
+    node = tree_new_child(conf, "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);
+    return node;
+}
+
+
+/* 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_config_machine(tree *conf, dt_host *host,
+                             const dt_driver drvtab[],
+                             ram_addr_t ram_size, int vga_ram_size,
+                             const char *cpu_model)
+{
+    tree *node;
+    dt_device *root;
+
+    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);
+
+    root = dt_create(conf, drvtab);
+    dt_add_dyn_devs(conf, host, drvtab, vga_ram_size);
+    dt_config(root, host);
+
+    dt_print_host_config(host);
+    tree_print(conf);
+    dt_int_tree_print(root, 0);
+
+    dt_fdt_test(conf);
+    return root;
+}
diff --git a/dt.h b/dt.h
new file mode 100644
index 0000000..85cc6a8
--- /dev/null
+++ b/dt.h
@@ -0,0 +1,141 @@
+#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;
+
+
+/* Host Configuration */
+
+typedef void (*dt_mem_loader)(target_phys_addr_t, ram_addr_t, const void *);
+
+dt_host *dt_create_host(void);
+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;
+    int visit;                  /* for dt_walk_int_tree() */
+    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_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 *);
+};
+
+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_add_memory(tree *conf, target_phys_addr_t addr, ram_addr_t sz, int 
ro);
+tree *dt_read_config(const char *name);
+dt_device *dt_config_machine(tree *conf, dt_host *host,
+                             const dt_driver drvtab[],
+                             ram_addr_t ram_size, int vga_ram_size,
+                             const char *cpu_model);
+void dt_create_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);
+
+
+/* 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 7fada94..08c4591 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;
+
 /* ppc.c */
 extern QEMUMachine prep_machine;
 extern QEMUMachine core99_machine;
diff --git a/hw/pc.c b/hw/pc.c
index 6a1750e..fe3085c 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;
 
@@ -537,10 +530,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];
@@ -691,7 +684,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);
@@ -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..fac7f63
--- /dev/null
+++ b/hw/pcdt.c
@@ -0,0 +1,915 @@
+/*
+ * 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 "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"
+
+
+/* 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);
+    }
+}
+
+
+/* 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 *i8259;
+    dt_device_i8254 *priv = dev->priv;
+
+    i8259 = dt_get_int(dev, 16);
+
+    pit = pit_init(0x40, i8259[0]);
+    pcspk_init(pit);
+    if (priv->hpet)
+        hpet_init(i8259);
+}
+
+
+/* i8259 PIC Driver */
+
+/*
+ * TODO Factor out the parts that are not PC-specific, and move them
+ * out, since there are other architectures that use it.
+ */
+
+typedef struct dt_device_i8259 {
+    qemu_irq *i8259;
+} dt_device_i8259;
+
+static void dt_i8259_int_init(dt_device *dev)
+{
+    dt_device_i8259 *priv = dev->priv;
+    qemu_irq *cpu_irq;
+
+    cpu_irq = qemu_allocate_irqs(pic_irq_request, NULL, 1);
+    priv->i8259 = i8259_init(cpu_irq[0]);
+    ferr_irq = priv->i8259[13];
+}
+
+static qemu_irq *dt_i8259_get_int(dt_device *dev, int *nirq)
+{
+    dt_device_i8259 *priv = dev->priv;
+
+    if (nirq)
+        *nirq = 16;
+    return priv->i8259;
+}
+
+
+/* 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 *bds[MAX_IDE_BUS * MAX_IDE_DEVS];
+} 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;
+
+    dt_find_drives(dt_find_bus(dt_root(dev)->conf, DT_BUS_IDE, 0)->conf,
+                   host, priv->bds, ARRAY_SIZE(priv->bds));
+}
+
+static void dt_MC146818_init(dt_device *dev)
+{
+    dt_device_MC146818 *priv = dev->priv;
+    qemu_irq *i8259 = dt_get_int(dev, 16);
+
+    rtc_state = rtc_init(0x70, i8259[8], 2000);
+    qemu_register_boot_set(pc_boot_set, rtc_state);
+    cmos_init(priv->ram_below_4g, priv->ram_above_4g, priv->boot_device,
+              priv->bds);
+}
+
+
+/* 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 {
+    int apic;
+    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;
+
+    priv->apic = 1;
+    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;
+    CPUState *env;
+    IOAPICState *ioapic;
+    qemu_irq *i8259;
+    int i;
+
+    i8259 = dt_get_int(dev, 16);
+
+    if (priv->apic) {
+        for (env = first_cpu; env; env = env->next_cpu) {
+            env->cpuid_features |= CPUID_APIC;
+            apic_init(env);
+        }
+    }
+
+    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);
+
+    if (priv->apic) {
+        ioapic = ioapic_init();
+        pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic);
+    }
+
+    for(i = 0; i < MAX_SERIAL_PORTS; i++) {
+        if (serial_hds[i]) {
+            serial_init(serial_io[i], 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], i8259[parallel_irq[i]],
+                          parallel_hds[i]);
+        }
+    }
+
+    qemu_system_hot_add_init();
+
+    i8042_init(i8259[1], i8259[12], 0x60);
+    DMA_init(0);
+
+    floppy_controller = fdctrl_init(i8259[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;
+}
+
+
+/*
+ * FIXME: this assumes the PIIX3 ISA bridge function is ->config'ed first
+ * followed by IDE, USB and ACPI, in this particular order. It just happens
+ * to work now. Fix is to grab PCI addresses from device tree, which should
+ * happen soon.
+ */
+static int piix3_devfn;
+
+/* PIIX3 IDE Driver */
+
+typedef struct dt_device_piix3_ide {
+    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 *i8259 = dt_get_int(dev, 16);
+
+    pci_piix3_ide_init(pci_bus, priv->bds, piix3_devfn + 1, i8259);
+}
+
+
+/* PIIX3 USB Driver */
+
+static void dt_piix3_usb_init(dt_device *dev)
+{
+    PCIBus *pci_bus = dt_get_pcibus(dev);
+
+    usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);
+}
+
+
+/* PIIX3 ACPI Driver */
+
+static void dt_piix3_acpi_init(dt_device *dev)
+{
+    int i;
+    uint8_t *eeprom_buf;
+    i2c_bus *smbus;
+    PCIBus *pci_bus = dt_get_pcibus(dev);
+    qemu_irq *i8259 = 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, piix3_devfn + 3, 0xb100, i8259[9]);
+    for (i = 0; i < 8; i++)
+        smbus_eeprom_device_init(smbus, 0x50 + i, eeprom_buf + (i * 256));
+}
+
+
+/* PIIX3 Driver */
+
+static void dt_piix3_init(dt_device *dev)
+{
+    PCIBus *pci_bus = dt_get_pcibus(dev);
+
+    piix3_devfn = piix3_init(pci_bus, -1);
+}
+
+
+/* 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 {
+    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 {
+    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 {
+    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 {
+    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 */
+
+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 {
+    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 },
+    { "i8259", sizeof(dt_device_i8259), NULL,
+      DT_BUS_NONE, DT_BUS_ISA,
+      NULL, dt_i8259_int_init, NULL, NULL,
+      NULL, NULL, dt_i8259_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", 0, NULL,
+      DT_BUS_ISA, DT_BUS_PCI,
+      NULL, NULL, dt_piix3_init, NULL,
+      NULL, NULL, NULL },
+    { "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", 0, 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", 0, 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", 0, 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, "pc-misc", NULL);
+    tree_put_propf(leaf, "interrupt-parent", "/pci/piix3-isa-bridge/i8259");
+
+    pci = tree_new_child(root, "pci", NULL);
+    tree_put_propf(pci, "interrupt-parent", "/pci/piix3-isa-bridge/i8259");
+
+    isa = tree_new_child(pci, "piix3-isa-bridge", NULL);
+    tree_put_propf(isa, "interrupt-parent", "/pci/piix3-isa-bridge/i8259");
+
+    leaf = tree_new_child(isa, "i8259", NULL);
+
+    leaf = tree_new_child(isa, "i8254", NULL);
+    tree_put_propf(leaf, "interrupt-parent", "/pci/piix3-isa-bridge/i8259");
+
+    leaf = tree_new_child(isa, "MC146818", NULL);
+    tree_put_propf(leaf, "interrupt-parent", "/pci/piix3-isa-bridge/i8259");
+
+    leaf = tree_new_child(pci, "piix3-ide", NULL);
+    tree_put_propf(leaf, "interrupt-parent", "/pci/piix3-isa-bridge/i8259");
+
+    if (usb_enabled) {
+        leaf = tree_new_child(pci, "piix3-usb", NULL);
+        tree_put_propf(leaf, "interrupt-parent", 
"/pci/piix3-isa-bridge/i8259");
+    }
+
+    if (acpi_enabled) {
+        leaf = tree_new_child(pci, "piix3-acpi", NULL);
+        tree_put_propf(leaf, "interrupt-parent", 
"/pci/piix3-isa-bridge/i8259");
+    }
+
+    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, ram_addr_t ram_size)
+{
+    ram_addr_t left, sz;
+
+    left = ram_size;
+    sz = MIN(left, 0xa0000);
+    dt_add_memory(conf, 0, sz, 0);
+    left -= sz;
+
+    sz = MIN(left, 0x60000);
+    left -= sz;
+
+    if (left) {
+        sz = MIN(left, 0xe0000000 - 0x100000);
+        dt_add_memory(conf, 0x100000, sz, 0);
+        left -= sz;
+    }
+
+#if TARGET_PHYS_ADDR_BITS > 32
+    if (left)
+        dt_add_memory(conf, 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, 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, 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, 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);
+
+    host = dt_create_host();
+
+    dt_add_ram(conf, ram_size);
+    dt_add_bios(conf, host);
+    dt_add_oprom(conf, host, kernel_filename, kernel_cmdline, initrd_filename);
+                 
+    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);
+
+    root = dt_config_machine(conf, host, dt_driver_table,
+                             ram_size, vga_ram_size, cpu_model);
+    dt_create_machine(root, host);
+}
+
+QEMUMachine pcdt_machine = {
+    .name = "pcdt",
+    .desc = "Standard PC (device tree)",
+    .init = pcdt_init,
+    .max_cpus = 255,
+};
diff --git a/hw/pcint.h b/hw/pcint.h
new file mode 100644
index 0000000..778d5a6
--- /dev/null
+++ b/hw/pcint.h
@@ -0,0 +1,50 @@
+/*
+ * 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);
+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/net.c b/net.c
index 5365891..d2163bd 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]