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 2 (was: [RFC] M


From: Markus Armbruster
Subject: [Qemu-devel] Machine description as data prototype, take 2 (was: [RFC] Machine description as data)
Date: Mon, 16 Feb 2009 17:22:50 +0100
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.3 (gnu/linux)

Second iteration of the prototype.

New:

* Cleaner separation of machine and host configuration.

* Conversion of machine configuration between internal representation
  and FDT.  Compiled in only when configure finds libfdt; check the "fdt
  support" line in its output.

Shortcuts:

* Not yet rebased to current HEAD.  Easy enough.

* I put the "pcdt" code into the new file dt.c, and copied code from
  pc.c there.  I could have avoided that by putting my code in pc.c
  instead.  Putting it in a new file helped me pick apart the pc.c
  hairball.  To be cleaned up.  New code in that file starts below the
  line /* Host Configuration */

* I copied code from net.c.  Trivial to fix, just give it external
  linkage there.

* I didn't implement all the devices of the "pc" original.  The devices
  I implemented might not support all existing command line options.

* The initial configuration tree is hardcoded.  It should be read from a
  configuration file.

* Optional stuff is inserted into the initial configuration tree in
  hardcoded places.  The places should be marked in the configuration
  file instead.

Notable qualities:

* Linux still boots & shuts down cleanly.

* Machine and host configuration are cleanly separated.  Machine
  configuration enumerates the components of the virtual machine, and
  how they are connected.  It is a tree of devices nodes.  Host
  configuration is about how the host implements virtual devices.
  Currently just a few flat tables.

* Device drivers implement a common abstract interface.

* Device drivers are cleanly separated from each other, and from the
  device-agnostic machine configuration and initialization code.

* Each device driver specifies its configurable properties in a single
  place.  Unknown properties are rejected.

* A device driver gets its configuration from two sources: the device's
  node in the machine configuraton tree, and applicable host
  configuration tables.

Comments?


diff --git a/Makefile b/Makefile
index 4f7a55a..2198bba 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 a091ce9..790529b 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -580,7 +580,11 @@ 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+= dt.o
 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
+ifdef FDT_LIBS
+LIBS+= $(FDT_LIBS)
+endif
 endif
 ifeq ($(TARGET_BASE_ARCH), ppc)
 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
diff --git a/hw/dt.c b/hw/dt.c
new file mode 100644
index 0000000..f839964
--- /dev/null
+++ b/hw/dt.c
@@ -0,0 +1,1856 @@
+#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 "tree.h"
+
+#ifdef HAVE_FDT
+#include <libfdt.h>
+#endif
+
+/* Forward declarations */
+struct dt_device;
+struct dt_driver;
+struct dt_prop_spec;
+static void dt_parse_prop(struct dt_device *dev, struct tree_prop *prop);
+static BlockDriverState **dt_piix3_hd(struct tree *piix3);
+
+
+// FIXME copied from pc.c, external defs stripped, unused stuff #if 0'ed
+/* 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 MAX_IDE_BUS 2
+
+static fdctrl_t *floppy_controller;
+static RTCState *rtc_state;
+#if 0
+static PITState *pit;
+static IOAPICState *ioapic;
+#endif
+extern PCIDevice *i440fx_state;
+
+static void ioport80_write(void *opaque, uint32_t addr, uint32_t data)
+{
+}
+
+#if 0
+/* MSDOS compatibility mode FPU exception support */
+static 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)
+{
+    qemu_irq_lower(ferr_irq);
+}
+#else
+extern qemu_irq ferr_irq;
+void ioportF0_write(void *opaque, uint32_t addr, uint32_t data);
+#endif
+
+#if 0
+/* TSC handling */
+uint64_t cpu_get_tsc(CPUX86State *env)
+{
+    /* Note: when using kqemu, it is more logical to return the host TSC
+       because kqemu does not trap the RDTSC instruction for
+       performance reasons */
+#ifdef USE_KQEMU
+    if (env->kqemu_enabled) {
+        return cpu_get_real_ticks();
+    } else
+#endif
+    {
+        return cpu_get_ticks();
+    }
+}
+
+/* SMM support */
+void cpu_smm_update(CPUState *env)
+{
+    if (i440fx_state && env == first_cpu)
+        i440fx_set_smm(i440fx_state, (env->hflags >> HF_SMM_SHIFT) & 1);
+}
+
+/* IRQ handling */
+int cpu_get_pic_interrupt(CPUState *env)
+{
+    int intno;
+
+    intno = apic_get_interrupt(env);
+    if (intno >= 0) {
+        /* set irq request if a PIC irq is still pending */
+        /* XXX: improve that */
+        pic_update_irq(isa_pic);
+        return intno;
+    }
+    /* read the irq from the PIC */
+    if (!apic_accept_pic_intr(env))
+        return -1;
+
+    intno = pic_read_irq(isa_pic);
+    return intno;
+}
+#endif
+
+static void pic_irq_request(void *opaque, int irq, int level)
+{
+    CPUState *env = first_cpu;
+
+    if (env->apic_state) {
+        while (env) {
+            if (apic_accept_pic_intr(env))
+                apic_deliver_pic_intr(env, level);
+            env = env->next_cpu;
+        }
+    } else {
+        if (level)
+            cpu_interrupt(env, CPU_INTERRUPT_HARD);
+        else
+            cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+    }
+}
+
+/* PC cmos mappings */
+
+#define REG_EQUIPMENT_BYTE          0x14
+
+static int cmos_get_fd_drive_type(int fd0)
+{
+    int val;
+
+    switch (fd0) {
+    case 0:
+        /* 1.44 Mb 3"5 drive */
+        val = 4;
+        break;
+    case 1:
+        /* 2.88 Mb 3"5 drive */
+        val = 5;
+        break;
+    case 2:
+        /* 1.2 Mb 5"5 drive */
+        val = 2;
+        break;
+    default:
+        val = 0;
+        break;
+    }
+    return val;
+}
+
+static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd)
+{
+    RTCState *s = rtc_state;
+    int cylinders, heads, sectors;
+    bdrv_get_geometry_hint(hd, &cylinders, &heads, &sectors);
+    rtc_set_memory(s, type_ofs, 47);
+    rtc_set_memory(s, info_ofs, cylinders);
+    rtc_set_memory(s, info_ofs + 1, cylinders >> 8);
+    rtc_set_memory(s, info_ofs + 2, heads);
+    rtc_set_memory(s, info_ofs + 3, 0xff);
+    rtc_set_memory(s, info_ofs + 4, 0xff);
+    rtc_set_memory(s, info_ofs + 5, 0xc0 | ((heads > 8) << 3));
+    rtc_set_memory(s, info_ofs + 6, cylinders);
+    rtc_set_memory(s, info_ofs + 7, cylinders >> 8);
+    rtc_set_memory(s, info_ofs + 8, sectors);
+}
+
+/* convert boot_device letter to something recognizable by the bios */
+static int boot_device2nibble(char boot_device)
+{
+    switch(boot_device) {
+    case 'a':
+    case 'b':
+        return 0x01; /* floppy boot */
+    case 'c':
+        return 0x02; /* hard drive boot */
+    case 'd':
+        return 0x03; /* CD-ROM boot */
+    case 'n':
+        return 0x04; /* Network boot */
+    }
+    return 0;
+}
+
+/* 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)
+{
+#define PC_MAX_BOOT_DEVICES 3
+    RTCState *s = (RTCState *)opaque;
+    int nbds, bds[3] = { 0, };
+    int i;
+
+    nbds = strlen(boot_device);
+    if (nbds > PC_MAX_BOOT_DEVICES) {
+        term_printf("Too many boot devices for PC\n");
+        return(1);
+    }
+    for (i = 0; i < nbds; i++) {
+        bds[i] = boot_device2nibble(boot_device[i]);
+        if (bds[i] == 0) {
+            term_printf("Invalid boot device for PC: '%c'\n",
+                    boot_device[i]);
+            return(1);
+        }
+    }
+    rtc_set_memory(s, 0x3d, (bds[1] << 4) | bds[0]);
+    rtc_set_memory(s, 0x38, (bds[2] << 4));
+    return(0);
+}
+
+/* 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)
+{
+    RTCState *s = rtc_state;
+    int nbds, bds[3] = { 0, };
+    int val;
+    int fd0, fd1, nb;
+    int i;
+
+    /* various important CMOS locations needed by PC/Bochs bios */
+
+    /* memory size */
+    val = 640; /* base memory in K */
+    rtc_set_memory(s, 0x15, val);
+    rtc_set_memory(s, 0x16, val >> 8);
+
+    val = (ram_size / 1024) - 1024;
+    if (val > 65535)
+        val = 65535;
+    rtc_set_memory(s, 0x17, val);
+    rtc_set_memory(s, 0x18, val >> 8);
+    rtc_set_memory(s, 0x30, val);
+    rtc_set_memory(s, 0x31, val >> 8);
+
+    if (above_4g_mem_size) {
+        rtc_set_memory(s, 0x5b, (unsigned int)above_4g_mem_size >> 16);
+        rtc_set_memory(s, 0x5c, (unsigned int)above_4g_mem_size >> 24);
+        rtc_set_memory(s, 0x5d, (uint64_t)above_4g_mem_size >> 32);
+    }
+
+    if (ram_size > (16 * 1024 * 1024))
+        val = (ram_size / 65536) - ((16 * 1024 * 1024) / 65536);
+    else
+        val = 0;
+    if (val > 65535)
+        val = 65535;
+    rtc_set_memory(s, 0x34, val);
+    rtc_set_memory(s, 0x35, val >> 8);
+
+    /* set the number of CPU */
+    rtc_set_memory(s, 0x5f, smp_cpus - 1);
+
+    /* set boot devices, and disable floppy signature check if requested */
+#define PC_MAX_BOOT_DEVICES 3
+    nbds = strlen(boot_device);
+    if (nbds > PC_MAX_BOOT_DEVICES) {
+        fprintf(stderr, "Too many boot devices for PC\n");
+        exit(1);
+    }
+    for (i = 0; i < nbds; i++) {
+        bds[i] = boot_device2nibble(boot_device[i]);
+        if (bds[i] == 0) {
+            fprintf(stderr, "Invalid boot device for PC: '%c'\n",
+                    boot_device[i]);
+            exit(1);
+        }
+    }
+    rtc_set_memory(s, 0x3d, (bds[1] << 4) | bds[0]);
+    rtc_set_memory(s, 0x38, (bds[2] << 4) | (fd_bootchk ?  0x0 : 0x1));
+
+    /* floppy type */
+
+    fd0 = fdctrl_get_drive_type(floppy_controller, 0);
+    fd1 = fdctrl_get_drive_type(floppy_controller, 1);
+
+    val = (cmos_get_fd_drive_type(fd0) << 4) | cmos_get_fd_drive_type(fd1);
+    rtc_set_memory(s, 0x10, val);
+
+    val = 0;
+    nb = 0;
+    if (fd0 < 3)
+        nb++;
+    if (fd1 < 3)
+        nb++;
+    switch (nb) {
+    case 0:
+        break;
+    case 1:
+        val |= 0x01; /* 1 drive, ready for boot */
+        break;
+    case 2:
+        val |= 0x41; /* 2 drives, ready for boot */
+        break;
+    }
+    val |= 0x02; /* FPU is there */
+    val |= 0x04; /* PS/2 mouse installed */
+    rtc_set_memory(s, REG_EQUIPMENT_BYTE, val);
+
+    /* hard drives */
+
+    rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 
0));
+    if (hd_table[0])
+        cmos_init_hd(0x19, 0x1b, hd_table[0]);
+    if (hd_table[1])
+        cmos_init_hd(0x1a, 0x24, hd_table[1]);
+
+    val = 0;
+    for (i = 0; i < 4; i++) {
+        if (hd_table[i]) {
+            int cylinders, heads, sectors, translation;
+            /* NOTE: bdrv_get_geometry_hint() returns the physical
+                geometry.  It is always such that: 1 <= sects <= 63, 1
+                <= heads <= 16, 1 <= cylinders <= 16383. The BIOS
+                geometry can be different if a translation is done. */
+            translation = bdrv_get_translation_hint(hd_table[i]);
+            if (translation == BIOS_ATA_TRANSLATION_AUTO) {
+                bdrv_get_geometry_hint(hd_table[i], &cylinders, &heads, 
&sectors);
+                if (cylinders <= 1024 && heads <= 16 && sectors <= 63) {
+                    /* No translation. */
+                    translation = 0;
+                } else {
+                    /* LBA translation. */
+                    translation = 1;
+                }
+            } else {
+                translation--;
+            }
+            val |= translation << (i * 2);
+        }
+    }
+    rtc_set_memory(s, 0x39, val);
+}
+
+#if 0
+void ioport_set_a20(int enable)
+{
+    /* XXX: send to all CPUs ? */
+    cpu_x86_set_a20(first_cpu, enable);
+}
+
+int ioport_get_a20(void)
+{
+    return ((first_cpu->a20_mask >> 20) & 1);
+}
+#endif
+
+static 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)
+{
+    return ioport_get_a20() << 1;
+}
+
+/***********************************************************/
+/* Bochs BIOS debug ports */
+
+static void bochs_bios_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    static const char shutdown_str[8] = "Shutdown";
+    static int shutdown_index = 0;
+
+    switch(addr) {
+        /* Bochs BIOS messages */
+    case 0x400:
+    case 0x401:
+        fprintf(stderr, "BIOS panic at rombios.c, line %d\n", val);
+        exit(1);
+    case 0x402:
+    case 0x403:
+#ifdef DEBUG_BIOS
+        fprintf(stderr, "%c", val);
+#endif
+        break;
+    case 0x8900:
+        /* same as Bochs power off */
+        if (val == shutdown_str[shutdown_index]) {
+            shutdown_index++;
+            if (shutdown_index == 8) {
+                shutdown_index = 0;
+                qemu_system_shutdown_request();
+            }
+        } else {
+            shutdown_index = 0;
+        }
+        break;
+
+        /* LGPL'ed VGA BIOS messages */
+    case 0x501:
+    case 0x502:
+        fprintf(stderr, "VGA BIOS panic, line %d\n", val);
+        exit(1);
+    case 0x500:
+    case 0x503:
+#ifdef DEBUG_BIOS
+        fprintf(stderr, "%c", val);
+#endif
+        break;
+    }
+}
+
+static void bochs_bios_init(void)
+{
+    void *fw_cfg;
+
+    register_ioport_write(0x400, 1, 2, bochs_bios_write, NULL);
+    register_ioport_write(0x401, 1, 2, bochs_bios_write, NULL);
+    register_ioport_write(0x402, 1, 1, bochs_bios_write, NULL);
+    register_ioport_write(0x403, 1, 1, bochs_bios_write, NULL);
+    register_ioport_write(0x8900, 1, 1, bochs_bios_write, NULL);
+
+    register_ioport_write(0x501, 1, 2, bochs_bios_write, NULL);
+    register_ioport_write(0x502, 1, 2, bochs_bios_write, NULL);
+    register_ioport_write(0x500, 1, 1, bochs_bios_write, NULL);
+    register_ioport_write(0x503, 1, 1, bochs_bios_write, NULL);
+
+    fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0);
+    fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
+    fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
+}
+
+#if 0
+/* Generate an initial boot sector which sets state and jump to
+   a specified vector */
+static void generate_bootsect(uint8_t *option_rom,
+                              uint32_t gpr[8], uint16_t segs[6], uint16_t ip)
+{
+    uint8_t rom[512], *p, *reloc;
+    uint8_t sum;
+    int i;
+
+    memset(rom, 0, sizeof(rom));
+
+    p = rom;
+    /* Make sure we have an option rom signature */
+    *p++ = 0x55;
+    *p++ = 0xaa;
+
+    /* ROM size in sectors*/
+    *p++ = 1;
+
+    /* Hook int19 */
+
+    *p++ = 0x50;               /* push ax */
+    *p++ = 0x1e;               /* push ds */
+    *p++ = 0x31; *p++ = 0xc0;  /* xor ax, ax */
+    *p++ = 0x8e; *p++ = 0xd8;  /* mov ax, ds */
+
+    *p++ = 0xc7; *p++ = 0x06;   /* movvw _start,0x64 */
+    *p++ = 0x64; *p++ = 0x00;
+    reloc = p;
+    *p++ = 0x00; *p++ = 0x00;
+
+    *p++ = 0x8c; *p++ = 0x0e;   /* mov cs,0x66 */
+    *p++ = 0x66; *p++ = 0x00;
+
+    *p++ = 0x1f;               /* pop ds */
+    *p++ = 0x58;               /* pop ax */
+    *p++ = 0xcb;               /* lret */
+    
+    /* Actual code */
+    *reloc = (p - rom);
+
+    *p++ = 0xfa;               /* CLI */
+    *p++ = 0xfc;               /* CLD */
+
+    for (i = 0; i < 6; i++) {
+       if (i == 1)             /* Skip CS */
+           continue;
+
+       *p++ = 0xb8;            /* MOV AX,imm16 */
+       *p++ = segs[i];
+       *p++ = segs[i] >> 8;
+       *p++ = 0x8e;            /* MOV <seg>,AX */
+       *p++ = 0xc0 + (i << 3);
+    }
+
+    for (i = 0; i < 8; i++) {
+       *p++ = 0x66;            /* 32-bit operand size */
+       *p++ = 0xb8 + i;        /* MOV <reg>,imm32 */
+       *p++ = gpr[i];
+       *p++ = gpr[i] >> 8;
+       *p++ = gpr[i] >> 16;
+       *p++ = gpr[i] >> 24;
+    }
+
+    *p++ = 0xea;               /* JMP FAR */
+    *p++ = ip;                 /* IP */
+    *p++ = ip >> 8;
+    *p++ = segs[1];            /* CS */
+    *p++ = segs[1] >> 8;
+
+    /* sign rom */
+    sum = 0;
+    for (i = 0; i < (sizeof(rom) - 1); i++)
+        sum += rom[i];
+    rom[sizeof(rom) - 1] = -sum;
+
+    memcpy(option_rom, rom, sizeof(rom));
+}
+
+static long get_file_size(FILE *f)
+{
+    long where, size;
+
+    /* XXX: on Unix systems, using fstat() probably makes more sense */
+
+    where = ftell(f);
+    fseek(f, 0, SEEK_END);
+    size = ftell(f);
+    fseek(f, where, SEEK_SET);
+
+    return size;
+}
+
+static void load_linux(uint8_t *option_rom,
+                       const char *kernel_filename,
+                      const char *initrd_filename,
+                      const char *kernel_cmdline)
+{
+    uint16_t protocol;
+    uint32_t gpr[8];
+    uint16_t seg[6];
+    uint16_t real_seg;
+    int setup_size, kernel_size, initrd_size, cmdline_size;
+    uint32_t initrd_max;
+    uint8_t header[1024];
+    target_phys_addr_t real_addr, prot_addr, cmdline_addr, initrd_addr;
+    FILE *f, *fi;
+
+    /* Align to 16 bytes as a paranoia measure */
+    cmdline_size = (strlen(kernel_cmdline)+16) & ~15;
+
+    /* load the kernel header */
+    f = fopen(kernel_filename, "rb");
+    if (!f || !(kernel_size = get_file_size(f)) ||
+       fread(header, 1, 1024, f) != 1024) {
+       fprintf(stderr, "qemu: could not load kernel '%s'\n",
+               kernel_filename);
+       exit(1);
+    }
+
+    /* kernel protocol version */
+#if 0
+    fprintf(stderr, "header magic: %#x\n", ldl_p(header+0x202));
+#endif
+    if (ldl_p(header+0x202) == 0x53726448)
+       protocol = lduw_p(header+0x206);
+    else
+       protocol = 0;
+
+    if (protocol < 0x200 || !(header[0x211] & 0x01)) {
+       /* Low kernel */
+       real_addr    = 0x90000;
+       cmdline_addr = 0x9a000 - cmdline_size;
+       prot_addr    = 0x10000;
+    } else if (protocol < 0x202) {
+       /* High but ancient kernel */
+       real_addr    = 0x90000;
+       cmdline_addr = 0x9a000 - cmdline_size;
+       prot_addr    = 0x100000;
+    } else {
+       /* High and recent kernel */
+       real_addr    = 0x10000;
+       cmdline_addr = 0x20000;
+       prot_addr    = 0x100000;
+    }
+
+#if 0
+    fprintf(stderr,
+           "qemu: real_addr     = 0x" TARGET_FMT_plx "\n"
+           "qemu: cmdline_addr  = 0x" TARGET_FMT_plx "\n"
+           "qemu: prot_addr     = 0x" TARGET_FMT_plx "\n",
+           real_addr,
+           cmdline_addr,
+           prot_addr);
+#endif
+
+    /* highest address for loading the initrd */
+    if (protocol >= 0x203)
+       initrd_max = ldl_p(header+0x22c);
+    else
+       initrd_max = 0x37ffffff;
+
+    if (initrd_max >= ram_size-ACPI_DATA_SIZE)
+       initrd_max = ram_size-ACPI_DATA_SIZE-1;
+
+    /* kernel command line */
+    pstrcpy_targphys(cmdline_addr, 4096, kernel_cmdline);
+
+    if (protocol >= 0x202) {
+       stl_p(header+0x228, cmdline_addr);
+    } else {
+       stw_p(header+0x20, 0xA33F);
+       stw_p(header+0x22, cmdline_addr-real_addr);
+    }
+
+    /* loader type */
+    /* High nybble = B reserved for Qemu; low nybble is revision number.
+       If this code is substantially changed, you may want to consider
+       incrementing the revision. */
+    if (protocol >= 0x200)
+       header[0x210] = 0xB0;
+
+    /* heap */
+    if (protocol >= 0x201) {
+       header[0x211] |= 0x80;  /* CAN_USE_HEAP */
+       stw_p(header+0x224, cmdline_addr-real_addr-0x200);
+    }
+
+    /* load initrd */
+    if (initrd_filename) {
+       if (protocol < 0x200) {
+           fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n");
+           exit(1);
+       }
+
+       fi = fopen(initrd_filename, "rb");
+       if (!fi) {
+           fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+                   initrd_filename);
+           exit(1);
+       }
+
+       initrd_size = get_file_size(fi);
+       initrd_addr = (initrd_max-initrd_size) & ~4095;
+
+        fprintf(stderr, "qemu: loading initrd (%#x bytes) at 0x" TARGET_FMT_plx
+                "\n", initrd_size, initrd_addr);
+
+       if (!fread_targphys_ok(initrd_addr, initrd_size, fi)) {
+           fprintf(stderr, "qemu: read error on initial ram disk '%s'\n",
+                   initrd_filename);
+           exit(1);
+       }
+       fclose(fi);
+
+       stl_p(header+0x218, initrd_addr);
+       stl_p(header+0x21c, initrd_size);
+    }
+
+    /* store the finalized header and load the rest of the kernel */
+    cpu_physical_memory_write(real_addr, header, 1024);
+
+    setup_size = header[0x1f1];
+    if (setup_size == 0)
+       setup_size = 4;
+
+    setup_size = (setup_size+1)*512;
+    kernel_size -= setup_size; /* Size of protected-mode code */
+
+    if (!fread_targphys_ok(real_addr+1024, setup_size-1024, f) ||
+       !fread_targphys_ok(prot_addr, kernel_size, f)) {
+       fprintf(stderr, "qemu: read error on kernel '%s'\n",
+               kernel_filename);
+       exit(1);
+    }
+    fclose(f);
+
+    /* generate bootsector to set up the initial register state */
+    real_seg = real_addr >> 4;
+    seg[0] = seg[2] = seg[3] = seg[4] = seg[4] = real_seg;
+    seg[1] = real_seg+0x20;    /* CS */
+    memset(gpr, 0, sizeof gpr);
+    gpr[4] = cmdline_addr-real_addr-16;        /* SP (-16 is paranoia) */
+
+    generate_bootsect(option_rom, gpr, seg, 0);
+}
+#endif
+
+static void main_cpu_reset(void *opaque)
+{
+    CPUState *env = opaque;
+    cpu_reset(env);
+}
+
+static const int ide_iobase[2] = { 0x1f0, 0x170 };
+static const int ide_iobase2[2] = { 0x3f6, 0x376 };
+static const int ide_irq[2] = { 14, 15 };
+
+#define NE2000_NB_MAX 6
+
+static const int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 
0x280, 0x380 };
+static const int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
+
+static const int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
+static const int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 };
+
+static const int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
+static const int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 };
+
+#if 0 //def HAS_AUDIO
+static void audio_init (PCIBus *pci_bus, qemu_irq *pic)
+{
+    struct soundhw *c;
+    int audio_enabled = 0;
+
+    for (c = soundhw; !audio_enabled && c->name; ++c) {
+        audio_enabled = c->enabled;
+    }
+
+    if (audio_enabled) {
+        AudioState *s;
+
+        s = AUD_init ();
+        if (s) {
+            for (c = soundhw; c->name; ++c) {
+                if (c->enabled) {
+                    if (c->isa) {
+                        c->init.init_isa (s, pic);
+                    }
+                    else {
+                        if (pci_bus) {
+                            c->init.init_pci (pci_bus, s);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+static void pc_init_ne2k_isa(NICInfo *nd, qemu_irq *pic)
+{
+    static int nb_ne2k = 0;
+
+    if (nb_ne2k == NE2000_NB_MAX)
+        return;
+    isa_ne2000_init(ne2000_io[nb_ne2k], pic[ne2000_irq[nb_ne2k]], nd);
+    nb_ne2k++;
+}
+#endif
+
+
+// FIXME copied from net.c
+
+static int parse_macaddr(uint8_t *macaddr, const char *p)
+{
+    int i;
+    char *last_char;
+    long int offset;
+
+    errno = 0;
+    offset = strtol(p, &last_char, 0);    
+    if (0 == errno && '\0' == *last_char &&
+            offset >= 0 && offset <= 0xFFFFFF) {
+        macaddr[3] = (offset & 0xFF0000) >> 16;
+        macaddr[4] = (offset & 0xFF00) >> 8;
+        macaddr[5] = offset & 0xFF;
+        return 0;
+    } else {
+        for(i = 0; i < 6; i++) {
+            macaddr[i] = strtol(p, (char **)&p, 16);
+            if (i == 5) {
+                if (*p != '\0')
+                    return -1;
+            } else {
+                if (*p != ':' && *p != '-')
+                    return -1;
+                p++;
+            }
+        }
+        return 0;    
+    }
+
+    return -1;
+}
+
+/* Host Configuration */
+
+struct dt_host {
+    struct tree *nic[MAX_NICS];
+    VLANState *nic_vlan[MAX_NICS];
+    struct tree *drive_ctrl[MAX_DRIVES];
+    BlockDriverState *drive_state[MAX_DRIVES];
+};
+
+static void
+dt_attach_nic(struct dt_host *host, int index,
+             struct tree *nic, VLANState *vlan)
+{
+    host->nic[index] = nic;
+    host->nic_vlan[index] = vlan;
+}
+
+static VLANState *
+dt_find_vlan(struct tree *conf, struct dt_host *host)
+{
+    int i;
+
+    for (i = 0; i < MAX_NICS; i++) {
+       if (host->nic[i] == conf)
+           return host->nic_vlan[i];
+    }
+    return NULL;
+}
+
+static void
+dt_attach_drive(struct dt_host *host, int index,
+               struct tree *controller, BlockDriverState *state)
+{
+    host->drive_ctrl[index] = controller;
+    host->drive_state[index] = state;
+}
+
+static void
+dt_drive_config(struct tree *conf, struct dt_host *host,
+               BlockDriverState *drive[], int n)
+{
+    int i, j;
+
+    j = 0;
+    for (i = 0; i < MAX_DRIVES; i++) {
+       if (host->drive_ctrl[i] != conf)
+           continue;
+       assert(j < n);
+       drive[j++] = host->drive_state[i];
+    }
+}
+
+static void
+dt_print_host_config(struct dt_host *host)
+{
+    char buf[1024];
+    int i;
+
+    for (i = 0; i < MAX_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 < MAX_DRIVES; i++) {
+       if (!host->drive_ctrl[i])
+           continue;
+       tree_path(host->drive_ctrl[i], buf, sizeof(buf));
+       printf("drive#%d\t%-15s %s\n",
+              i, bdrv_get_device_name(host->drive_state[i]), buf);
+    }
+}
+
+
+/* Device Interface */
+
+/*
+ * Device life cycle:
+ *
+ * 1. Configuration: config() method runs after parent's.  Except kids
+ * are skipped when the parent's config() returns non-zero.  config()
+ * 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 {
+    struct tree *conf;         /* configuration sub-tree */
+    struct dt_driver *drv;     /* device driver */
+    void *priv;                        /* device private data */
+};
+
+struct dt_driver {
+    const char *name;
+    size_t privsz;             /* size of device private data */
+    struct dt_prop_spec *prop_spec; /* recognized conf node properties */
+    int (*config)(struct dt_device *, struct dt_host *);
+    void (*init)(struct dt_device *);
+    void (*start)(struct dt_device *);
+};
+
+static struct dt_driver dt_driver_table[];
+
+static struct dt_driver *
+dt_driver_by_name(const char *name)
+{
+    int i;
+
+    for (i = 0; dt_driver_table[i].name; i++) {
+       if (!strcmp(name, dt_driver_table[i].name))
+           return &dt_driver_table[i];
+    }
+    return NULL;
+}
+
+static struct dt_device *
+dt_device_of(struct tree *conf)
+{
+    return tree_get_user(conf);
+}
+
+static struct dt_device *
+dt_new_device(struct tree *conf, struct dt_driver *drv)
+{
+    struct dt_device *dev;
+    struct tree_prop *prop;
+
+    dev = qemu_malloc(sizeof(*dev));
+    dev->conf = conf;
+    dev->drv = drv;
+    dev->priv = qemu_malloc(drv->privsz);
+    tree_put_user(conf, dev);
+
+    TREE_FOREACH_PROP(prop, conf)
+       dt_parse_prop(dev, prop);
+
+    return dev;
+}
+
+static void
+dt_config(struct tree *conf, struct dt_host *host)
+{
+    struct dt_driver *drv;
+    struct dt_device *dev;
+    struct tree *kid;
+
+    drv = dt_driver_by_name(tree_node_name(conf));
+    if (!drv) {
+       fprintf(stderr, "No driver for device %s\n",
+               tree_node_name(conf));
+       exit(1);
+    }
+    dev = dt_new_device(conf, drv);
+    if (drv->config) {
+       if (drv->config(dev, host))
+           return;
+    }
+
+    TREE_FOREACH_KID(kid, conf)
+       dt_config(kid, host);
+}
+
+static void
+dt_init_visitor(struct tree *node, void *arg)
+{
+    struct dt_device *dev = dt_device_of(node);
+
+    if (dev && dev->drv->init)
+       dev->drv->init(dev);
+}
+
+static void
+dt_init(struct tree *conf)
+{
+    tree_visit(conf, dt_init_visitor, NULL);
+}
+
+static void
+dt_start(struct tree *conf)
+{
+    struct dt_device *dev = dt_device_of(conf);
+    struct tree *kid;
+
+    if (dev && dev->drv->start)
+       dev->drv->start(dev);
+
+    TREE_FOREACH_KID(kid, conf)
+       dt_start(kid);
+}
+
+
+/* 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 *, struct dt_prop_spec *);
+};
+
+#define DT_PROP_SPEC_INIT(name, strty, member, fmt)                    \
+    { name, offsetof(strty, member), sizeof(((strty *)0)->member),     \
+      dt_parse_##fmt }
+
+static struct dt_prop_spec *
+dt_prop_spec_by_name(struct dt_driver *drv, const char *name)
+{
+    struct 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(struct dt_device *dev, struct tree_prop *prop)
+{
+    const char *name = tree_prop_name(prop);
+    size_t size;
+    const char *val = tree_prop_value(prop, &size);
+    struct 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);
+    }
+}
+
+static int
+dt_parse_string(void *dst, const char *src, struct dt_prop_spec *spec)
+{
+    assert(spec->size == sizeof(char *));
+    *(const char **)dst = src;
+    return 0;
+}
+
+static int
+dt_parse_int(void *dst, const char *src, struct 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;
+}
+
+static int
+dt_parse_ram_addr_t(void *dst, const char *src, struct 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;
+}
+
+static int
+dt_parse_macaddr(void *dst, const char *src, struct dt_prop_spec *spec)
+{
+    assert(spec->size == 6);
+    if (parse_macaddr(dst, src) < 0)
+       return -1;
+    return 0;
+}
+
+
+/* Interfacing with FDT */
+
+#ifdef HAVE_FDT
+
+static int dt_fdt_chk(int res);
+static void dt_subtree_to_fdt(const struct tree *conf, void *fdt);
+
+static void *
+dt_tree_to_fdt(const struct 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 struct tree *conf, void *fdt)
+{
+    struct tree_prop *prop;
+    struct tree *kid;
+    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_KID(kid, conf)
+       dt_subtree_to_fdt(kid, fdt);
+    dt_fdt_chk(fdt_end_node(fdt));
+}
+
+static struct tree *
+dt_fdt_to_tree(const void *fdt)
+{
+    int offs, next, depth;
+    uint32_t tag;
+    struct fdt_property *prop;
+    struct 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 < sizeof(stack) / sizeof(*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 < sizeof(stack) / sizeof(*stack));
+           stack[depth] = tree_new_kid(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(struct 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(struct tree *conf) { }
+#endif
+
+/* CPUs Driver */
+
+struct dt_device_cpus {
+    const char *model;
+    int num;
+};
+
+static struct dt_prop_spec dt_cpus_props[] = {
+    DT_PROP_SPEC_INIT("model", struct dt_device_cpus, model, string),
+    DT_PROP_SPEC_INIT("num", struct dt_device_cpus, num, int),
+};
+
+static void
+dt_cpus_init(struct dt_device *dev)
+{
+    struct 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 x86 CPU definition\n");
+            exit(1);
+        }
+        if (i != 0)
+            env->halted = 1;
+        qemu_register_reset(main_cpu_reset, env);
+    }
+}
+
+
+/* Memory Ranges */
+
+struct dt_device_memrng {
+    target_phys_addr_t phys_addr;
+    ram_addr_t size;
+    ram_addr_t host_offs;
+    ram_addr_t flags;
+};
+
+static void
+dt_memrng(struct 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(struct 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(struct 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(struct 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 */
+
+struct dt_device_memory {
+    ram_addr_t ram_size;
+    struct dt_device_memrng *rng;
+    int nrng;
+    /* TODO want a real memory map here */
+    ram_addr_t below_4g, above_4g;
+};
+
+static struct dt_prop_spec dt_memory_props[] = {
+    DT_PROP_SPEC_INIT("ram", struct dt_device_memory, ram_size, ram_addr_t),
+};
+
+static int
+dt_memory_config(struct dt_device *dev, struct dt_host *host)
+{
+    /* TODO memory map hardcoded; get it from dev->conf instead */
+    struct dt_device_memory *priv = dev->priv;
+    struct 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;
+    return 0;
+}
+
+static void
+dt_memory_init(struct dt_device *dev)
+{
+    struct dt_device_memory *priv = dev->priv;
+
+    dt_memrng_init(priv->rng, priv->nrng);
+    bochs_bios_init();
+}
+
+static ram_addr_t
+dt_memory_below_4g(struct tree *memory)
+{
+    struct dt_device *dev = dt_device_of(memory);
+    struct 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(struct tree *memory)
+{
+    struct dt_device *dev = dt_device_of(memory);
+    struct 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.
+ */
+
+struct dt_device_pc_misc {
+    const char *boot_device;
+    int apic;
+    int hpet;
+    qemu_irq *i8259;
+    BlockDriverState *fd[MAX_FD];
+};
+
+static struct dt_prop_spec dt_pc_misc_props[] = {
+    DT_PROP_SPEC_INIT("boot-device", struct dt_device_pc_misc, boot_device,
+                     string),
+};
+
+static int
+dt_pc_misc_config(struct dt_device *dev, struct dt_host *host)
+{
+    struct dt_device_pc_misc *priv = dev->priv;
+
+    priv->apic = 1;
+    priv->hpet = 1;
+    priv->i8259 = NULL;
+    dt_drive_config(dev->conf, host,
+                   priv->fd, sizeof(priv->fd) / sizeof(*priv->fd));
+    return 1;
+}
+
+static void
+dt_pc_misc_init(struct dt_device *dev)
+{
+    struct 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]);
+        }
+    }
+
+    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(struct dt_device *dev)
+{
+    struct dt_device_pc_misc *priv = dev->priv;
+    struct tree *memory = tree_node_by_name(dev->conf, "/memory");
+    struct 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(struct tree *pc_misc)
+{
+    struct dt_device *dev = dt_device_of(pc_misc);
+    struct dt_device_pc_misc *priv = dev->priv;
+    assert(dev->drv->init == dt_pc_misc_init);
+    return priv->i8259;
+}
+
+
+/* PCI Bus Driver */
+
+struct dt_device_pci {
+    PCIBus *bus;
+    struct tree *pc;
+};
+
+static int
+dt_pci_config(struct dt_device *dev, struct dt_host *host)
+{
+    struct dt_device_pci *priv = dev->priv;
+
+    priv->bus = NULL;
+    priv->pc = tree_require_named(dev->conf, "/pc-misc");
+    return 0;
+}
+
+static void
+dt_pci_init(struct dt_device *dev)
+{
+    struct dt_device_pci *priv = dev->priv;
+
+    priv->bus = i440fx_init(&i440fx_state, dt_pc_misc_i8259(priv->pc));
+}
+
+static void
+dt_pci_start(struct dt_device *dev)
+{
+    i440fx_init_memory_mappings(i440fx_state);
+}
+
+static void
+dt_must_be_on_pcibus(struct dt_device *dev)
+{
+    struct dt_device *bus = dt_device_of(tree_parent(dev->conf));
+
+    if (bus->drv->init != dt_pci_init) {
+       fprintf(stderr, "Device %s must be on a PCI bus\n", dev->drv->name);
+       exit(1);
+    }
+}
+
+static struct PCIBus *
+dt_get_pcibus(struct dt_device *dev)
+{
+    struct dt_device *bus = dt_device_of(tree_parent(dev->conf));
+
+    assert(bus->drv->init == dt_pci_init);
+    return ((struct dt_device_pci *)bus->priv)->bus;
+}
+
+
+/* PIIX3 Driver */
+
+struct dt_device_piix3 {
+    int devfn;
+    int acpi;
+    int usb;
+    struct tree *pc;
+    BlockDriverState *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+};
+
+static int
+dt_piix3_config(struct dt_device *dev, struct dt_host *host)
+{
+    struct dt_device_piix3 *priv = dev->priv;
+
+    priv->devfn = -1;
+    priv->acpi = 1;
+    priv->usb = 1;
+    priv->pc = tree_require_named(dev->conf, "/pc-misc");
+    dt_drive_config(dev->conf, host,
+                   priv->hd, sizeof(priv->hd) / sizeof(*priv->hd));
+    dt_must_be_on_pcibus(dev);
+    return 1;
+}
+
+static void
+dt_piix3_init(struct dt_device *dev)
+{
+    struct 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(struct tree *piix3)
+{
+    struct dt_device *dev = dt_device_of(piix3);
+    struct dt_device_piix3 *priv = dev->priv;
+
+    assert(dev->drv->init == dt_piix3_init);
+    return priv->hd;
+}
+
+
+/* VGA Driver */
+
+struct dt_driver_vga {
+    const char *model;
+    const char *bios;
+    void (*init)(PCIBus *, uint8_t *, ram_addr_t, int);
+};
+
+static void
+pci_vmsvga_init_(PCIBus *bus, uint8_t *vga_ram_base,
+                ram_addr_t vga_ram_offset, int vga_ram_size)
+{
+    pci_vmsvga_init(bus, vga_ram_base, vga_ram_offset, vga_ram_size);
+}
+
+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 struct 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 }
+};
+
+struct dt_device_vga {
+    const char *model;
+    ram_addr_t ram_size;
+    struct dt_device_memrng rng[1];
+    ram_addr_t ram_offs;
+    struct dt_driver_vga *vga_drv;
+};
+
+static struct dt_prop_spec dt_vga_props[] = {
+    DT_PROP_SPEC_INIT("model", struct dt_device_vga, model, string),
+    DT_PROP_SPEC_INIT("ram", struct dt_device_vga, ram_size, ram_addr_t),
+};
+
+static int
+dt_vga_config(struct dt_device *dev, struct dt_host *host)
+{
+    struct 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];
+    dt_must_be_on_pcibus(dev);
+    return 0;
+}
+
+static void
+dt_vga_init(struct dt_device *dev)
+{
+    struct 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 */
+
+struct dt_device_nic {
+    NICInfo nd;
+};
+
+static struct dt_prop_spec dt_nic_props[] = {
+    DT_PROP_SPEC_INIT("model", struct dt_device_nic, nd.model, string),
+    DT_PROP_SPEC_INIT("mac", struct dt_device_nic, nd.macaddr, macaddr),
+    DT_PROP_SPEC_INIT("name", struct dt_device_nic, nd.name, string),
+};
+
+static int
+dt_nic_config(struct dt_device *dev, struct dt_host *host)
+{
+    struct dt_device_nic *priv = dev->priv;
+
+    priv->nd.vlan = dt_find_vlan(dev->conf, host);
+    dt_must_be_on_pcibus(dev);
+    return 0;
+}
+
+static void
+dt_nic_init(struct dt_device *dev)
+{
+    struct dt_device_nic *priv = dev->priv;
+
+    pci_nic_init(dt_get_pcibus(dev), &priv->nd, -1, NULL);
+}
+
+
+/* Machine Driver */
+
+static struct dt_driver dt_driver_table[] = {
+    { "", 0, NULL, NULL },
+    { "cpus", sizeof(struct dt_device_cpus), dt_cpus_props,
+      NULL, dt_cpus_init, NULL },
+    { "memory", sizeof(struct dt_device_memory), dt_memory_props,
+      dt_memory_config, dt_memory_init, NULL },
+    { "pc-misc", sizeof(struct dt_device_pc_misc), dt_pc_misc_props,
+      dt_pc_misc_config, dt_pc_misc_init, dt_pc_misc_start },
+    { "pci", sizeof(struct dt_device_pci), NULL,
+      dt_pci_config, dt_pci_init, dt_pci_start },
+    { "piix3", sizeof(struct dt_device_piix3), NULL,
+      dt_piix3_config, dt_piix3_init, NULL },
+    { "vga", sizeof(struct dt_device_vga), dt_vga_props,
+      dt_vga_config, dt_vga_init, NULL },
+    { "nic", sizeof(struct dt_device_nic), dt_nic_props,
+      dt_nic_config, dt_nic_init, NULL },
+    { NULL, 0, NULL, NULL, NULL }
+};
+
+static struct tree *
+dt_read_config(void)
+{
+    struct tree *root, *pci, *leaf;
+
+    /* TODO read from config file */
+    root = tree_new_kid(NULL, "", NULL);
+    leaf = tree_new_kid(root, "cpus", NULL);
+    tree_put_propf(leaf, "model", "%s", "qemu32");
+    leaf = tree_new_kid(root, "memory", NULL);
+    leaf = tree_new_kid(root, "pc-misc", NULL);
+    pci = tree_new_kid(root, "pci", NULL);
+    leaf = tree_new_kid(pci, "piix3", NULL);
+    return root;
+}
+
+/*
+ * Extract configuration from arguments and various global variables
+ * and put it into our machine and host configuration.
+ */
+static void
+dt_customize_config(struct tree *conf,
+                   struct dt_host *host,
+                   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)
+{
+    struct tree *pci = tree_node_by_name(conf, "/pci");
+    struct tree *node;
+    int i, index;
+
+    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);
+
+    /* Insert VGA node */
+    if (cirrus_vga_enabled || vmsvga_enabled || std_vga_enabled) {
+       node = tree_new_kid(pci, "vga", NULL);
+       tree_put_propf(node, "model", "%s",
+                         cirrus_vga_enabled ? "cirrus" :
+                         vmsvga_enabled ? "vms" : "std");
+       tree_put_propf(node, "ram", "%#x", vga_ram_size);
+    }
+
+    /* Insert NIC nodes, connect to VLANs */
+    for(i = 0; i < nb_nics; i++) {
+       /* TODO non-PCI NICs */
+       struct NICInfo *n = &nd_table[i];
+
+       node = tree_new_kid(pci, "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_attach_nic(host, i, node, n->vlan);
+    }
+
+    /* Connect drives to their controller nodes */
+    /* IDE */
+    node = tree_node_by_name(pci, "piix3");
+    for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) {
+        index = drive_get_index(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS);
+       if (index != -1)
+           dt_attach_drive(host, index, node, drives_table[index].bdrv);
+    }
+    /* Floppy */
+    node = tree_node_by_name(conf, "/pc-misc");
+    for(i = 0; i < MAX_FD; i++) {
+        index = drive_get_index(IF_FLOPPY, 0, i);
+       if (index != -1)
+           dt_attach_drive(host, index, node, drives_table[index].bdrv);
+    }
+
+    /* 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)
+{
+    struct tree *conf;
+    struct dt_host host;
+
+    conf = dt_read_config();
+    if (!conf)
+       exit(1);
+    tree_print(conf);
+    memset(&host, 0, sizeof(host));
+    dt_customize_config(conf, &host, ram_size, vga_ram_size, boot_device,
+                       kernel_filename, kernel_cmdline, initrd_filename,
+                       cpu_model);
+    dt_config(conf, &host);
+    tree_print(conf);
+    dt_print_host_config(&host);
+    dt_fdt_test(conf);
+    dt_init(conf);
+    dt_start(conf);
+}
+
+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/pc.c b/hw/pc.c
index 176730e..fc9ee20 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -57,21 +57,21 @@ static fdctrl_t *floppy_controller;
 static RTCState *rtc_state;
 static PITState *pit;
 static IOAPICState *ioapic;
-static PCIDevice *i440fx_state;
+PCIDevice *i440fx_state;
 
 static void ioport80_write(void *opaque, uint32_t addr, uint32_t data)
 {
 }
 
 /* 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);
 }
diff --git a/target-i386/machine.c b/target-i386/machine.c
index faab2eb..5a2a0c2 100644
--- a/target-i386/machine.c
+++ b/target-i386/machine.c
@@ -7,6 +7,8 @@
 
 void register_machines(void)
 {
+    extern QEMUMachine pcdt_machine;
+    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..ec7af0b
--- /dev/null
+++ b/tree.c
@@ -0,0 +1,333 @@
+/* Ye Olde Decorated Tree */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "tree.h"
+#include "qemu-common.h"
+#include "sys-queue.h"
+
+struct tree {
+    const char *name;
+    LIST_HEAD(, tree_prop) props;
+    struct tree *parent;
+    TAILQ_HEAD(, tree) kids;
+    TAILQ_ENTRY(tree) siblings;
+    void *user;
+    LIST_HEAD(, tree) reqs;
+    LIST_ENTRY(tree) reqlink;
+    int visit;
+};
+
+struct tree_prop {
+    const char *name;
+    const void *val;
+    int sz;
+    struct tree *owner;
+    LIST_ENTRY(tree_prop) link;
+};
+
+struct tree *
+tree_new_kid(struct tree *parent, const char *name, void *user)
+{
+    struct tree *kid = qemu_malloc(sizeof(*kid));
+
+    assert(parent || !*name);
+    kid->name = name;
+    LIST_INIT(&kid->props);
+    kid->parent = parent;
+    TAILQ_INIT(&kid->kids);
+    if (parent)
+       TAILQ_INSERT_TAIL(&parent->kids, kid, siblings);
+    kid->user = user;
+    LIST_INIT(&kid->reqs);
+    kid->visit = 0;
+
+    return kid;
+}
+
+const char *
+tree_node_name(const struct tree *node)
+{
+    return node->name;
+}
+
+static struct tree *
+tree_kid_by_name(const struct tree *dt, const char *name)
+{
+    const char *slash = strchr(name, '/');
+    size_t len = slash ? slash - name : strlen(name);
+    struct tree *kid;
+
+    TAILQ_FOREACH(kid, &dt->kids, siblings) {
+       if (!memcmp(kid->name, name, len) && kid->name[len] == 0)
+           return kid;
+    }
+    return NULL;
+}
+
+struct tree *
+tree_node_by_name(const struct tree *node, const char *name)
+{
+    struct tree *kid;
+    size_t len;
+
+    if (name[0] == '/') {
+       for (; node->parent; node = node->parent) ;
+       name++;
+    }
+
+    if (name[0] == 0)
+       return (struct tree *)node;
+
+    kid = tree_kid_by_name(node, name);
+    if (!kid)
+       return NULL;
+
+    len = strlen(kid->name);
+    if (name[len] == 0)
+       return kid;
+    assert (name[len] == '/');
+
+    while (name[len] == '/') len++;
+    return tree_node_by_name(kid, name + len);
+}
+
+struct tree_prop *
+tree_first_prop(const struct tree *node)
+{
+    return LIST_FIRST(&node->props);
+}
+
+struct tree_prop *
+tree_next_prop(const struct tree_prop *prop)
+{
+    return LIST_NEXT(prop, link);
+}
+
+struct tree_prop *
+tree_get_prop(const struct tree *node, const char *name)
+{
+    struct 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 struct tree *node, const char *name)
+{
+    struct 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 struct tree_prop *prop)
+{
+    return prop->name;
+}
+
+const void *
+tree_prop_value(const struct tree_prop *prop, size_t *size)
+{
+    if (size)
+       *size = prop->sz;
+    return prop->val;
+}
+
+void
+tree_put_prop(struct tree *node, const char *name,
+             const void *val, size_t sz)
+{
+    struct 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(struct 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(struct tree *node, void *user)
+{
+    node->user = user;
+}
+
+void *
+tree_get_user(const struct tree *node)
+{
+    return node->user;
+}
+
+struct tree *
+tree_parent(const struct tree *node)
+{
+    return node->parent;
+}
+
+struct tree *
+tree_first_kid(const struct tree *node)
+{
+    return TAILQ_FIRST(&node->kids);
+}
+
+struct tree *
+tree_sibling(const struct tree *node)
+{
+    return TAILQ_NEXT(node, siblings);
+}
+
+void
+tree_require(struct tree *node, struct tree *req)
+{
+    LIST_INSERT_HEAD(&node->reqs, req, reqlink);
+}
+
+struct tree *
+tree_require_named(struct tree *node, const char *reqname)
+{
+    struct tree *req = tree_node_by_name(node, reqname);
+    tree_require(node, req);
+    return req;
+}
+
+static void
+tree_do_visit(struct tree *node,
+             void (*fun)(struct tree *, void *arg),
+             void *arg, int visit)
+{
+    struct tree *req, *kid;
+
+    assert(node->visit < visit - 1);
+    node->visit = visit - 1;
+    if (node->parent && node->parent->visit < visit)
+       tree_do_visit(node->parent, fun, arg, visit);
+    LIST_FOREACH(req, &node->reqs, reqlink) {
+       if (req->visit < visit)
+           tree_do_visit(req, fun, arg, visit);
+    }
+    node->visit = visit;
+    fun(node, arg);
+    TAILQ_FOREACH(kid, &node->kids, siblings) {
+       if (kid->visit < visit - 1)
+           tree_do_visit(kid, fun, arg, visit);
+    }
+}
+
+void
+tree_visit(struct tree *node,
+          void (*fun)(struct tree *, void *arg),
+          void *arg)
+{
+    static int visit;
+
+    visit += 2;
+    tree_do_visit(node, fun, arg, visit);
+}
+
+int
+tree_path(const struct tree *node, char *buf, size_t bufsz)
+{
+    char *p;
+    const struct 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 < bufsz) {
+       memcpy(buf, p, res);
+       buf[res] = 0;
+    }
+
+    return res;
+}
+
+static void
+tree_print_sub(const struct tree *node, int indent)
+{
+    int i, use_str, sep;
+    const unsigned char *pv;
+    struct tree_prop *prop;
+    struct tree *kid;
+
+    printf("%*s%s {\n", indent, "", node->name[0] ? 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(kid, &node->kids, siblings)
+       tree_print_sub(kid, indent + 4);
+    printf("%*s};\n", indent, "");
+}
+
+void
+tree_print(const struct tree *node)
+{
+    tree_print_sub(node, 0);
+}
diff --git a/tree.h b/tree.h
new file mode 100644
index 0000000..092350b
--- /dev/null
+++ b/tree.h
@@ -0,0 +1,46 @@
+#ifndef TREE_H
+#define TREE_H
+
+#include <stddef.h>
+
+struct tree;
+struct tree_prop;
+
+struct tree *tree_new_kid(struct tree *parent, const char *name, void *user);
+const char *tree_node_name(const struct tree *node);
+struct tree *tree_node_by_name(const struct tree *node,
+                              const char *name);
+
+struct tree_prop *tree_first_prop(const struct tree *node);
+struct tree_prop *tree_next_prop(const struct tree_prop *prop);
+#define TREE_FOREACH_PROP(var, node) \
+    for (var = tree_first_prop(node); var; var = tree_next_prop(var))
+struct tree_prop *tree_get_prop(const struct tree *node, const char *name);
+const char *tree_get_prop_s(const struct tree *node, const char *name);
+const char *tree_prop_name(const struct tree_prop *prop);
+const void *tree_prop_value(const struct tree_prop *prop, size_t *size);
+void tree_put_prop(struct tree *node, const char *name,
+                  const void *val, size_t sz);
+void tree_put_propf(struct tree *node, const char *name,
+                   const char *fmt, ...)
+    __attribute__((format(printf,3,4)));
+
+void tree_put_user(struct tree *node, void *user);
+void *tree_get_user(const struct tree *node);
+
+struct tree *tree_parent(const struct tree *node);
+struct tree *tree_first_kid(const struct tree *node);
+struct tree *tree_sibling(const struct tree *node);
+#define TREE_FOREACH_KID(var, node)                                    \
+    for (var = tree_first_kid(node); var; var = tree_sibling(var))
+
+void tree_require(struct tree *node, struct tree *req);
+struct tree *tree_require_named(struct tree *node, const char *reqname);
+void tree_visit(struct tree *node,
+               void (*fun)(struct tree *, void *arg),
+               void *arg);
+
+int tree_path(const struct tree *node, char *buf, size_t bufsz);
+void tree_print(const struct tree *node);
+
+#endif




reply via email to

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