[Top][All Lists]
[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: |
Mon, 23 Mar 2009 16:53:41 +0100 |
User-agent: |
Gnus/5.11 (Gnus v5.11) Emacs/22.3 (gnu/linux) |
Helps if I attach the patch instead of some random source file...
diff --git a/Makefile b/Makefile
index 82fec80..04026db 100644
--- a/Makefile
+++ b/Makefile
@@ -85,6 +85,7 @@ OBJS+=sd.o ssi-sd.o
OBJS+=bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o
OBJS+=buffered_file.o migration.o migration-tcp.o net.o qemu-sockets.o
OBJS+=qemu-char.o aio.o net-checksum.o savevm.o cache-utils.o
+OBJS+=tree.o
ifdef CONFIG_BRLAPI
OBJS+= baum.o
diff --git a/Makefile.target b/Makefile.target
index 41366ee..e6815e5 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -505,6 +505,7 @@ OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o
machine.o dma-helpers.o
# need to fix this properly
OBJS+=virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o
OBJS+=fw_cfg.o
+OBJS+=dt.o
ifdef CONFIG_KVM
OBJS+=kvm.o kvm-all.o
endif
@@ -536,6 +537,7 @@ endif
ifdef CONFIG_OSS
LIBS += $(CONFIG_OSS_LIB)
endif
+LIBS+= $(FDT_LIBS)
SOUND_HW = sb16.o es1370.o ac97.o
ifdef CONFIG_ADLIB
@@ -588,6 +590,7 @@ OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
OBJS+= cirrus_vga.o apic.o 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
@@ -611,7 +614,6 @@ OBJS+= ppc440.o ppc440_bamboo.o
OBJS+= ppce500_pci.o ppce500_mpc8544ds.o
ifdef FDT_LIBS
OBJS+= device_tree.o
-LIBS+= $(FDT_LIBS)
endif
ifdef CONFIG_KVM
OBJS+= kvm_ppc.o
diff --git a/dt.c b/dt.c
new file mode 100644
index 0000000..562deb3
--- /dev/null
+++ b/dt.c
@@ -0,0 +1,779 @@
+/*
+ * 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.
+ *
+ * Machine types using it implement QEMUMachine member drvtab[]
+ * instead of member init(). See hw/pcdt.c for an example.
+ */
+
+#include <assert.h>
+#include "block.h"
+#include "cpu.h"
+#include "dt.h"
+#include "net.h"
+#include "tree.h"
+#include "sysemu.h"
+
+#ifdef HAVE_FDT
+#include <libfdt.h>
+#endif
+
+/* Forward declarations */
+static void dt_parse_prop(dt_device *dev, tree_prop *prop);
+static void dt_add_dyn_devs(tree *conf, dt_host *host,
+ const dt_driver drvtab[], int vga_ram_size);
+static void dt_fdt_test(tree *conf);
+
+
+/* Host Configuration */
+
+struct dt_host {
+ /* connection NIC <-> VLAN */
+ int nics;
+ tree *nic[MAX_NICS];
+ VLANState *nic_vlan[MAX_NICS];
+ /* connection drive <-> block driver state */
+ int drives;
+ int virtio_drives;
+ tree *drive[MAX_DRIVES];
+ BlockDriverState *drive_state[MAX_DRIVES];
+};
+
+static 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;
+}
+
+static 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];
+ }
+}
+
+static 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);
+ }
+}
+
+
+/* Device Interface */
+
+static const dt_driver *dt_driver_by_name(const char *name,
+ const dt_driver drvtab[])
+{
+ int i;
+
+ for (i = 0; drvtab[i].name; i++) {
+ if (!strcmp(name, drvtab[i].name))
+ return &drvtab[i];
+ }
+ return NULL;
+}
+
+dt_device *dt_device_of(tree *conf)
+{
+ return tree_get_user(conf);
+}
+
+dt_device *dt_parent_device(dt_device *dev)
+{
+ tree *p = tree_parent(dev->conf);
+
+ return p ? dt_device_of(p) : NULL;
+}
+
+static dt_device *dt_do_find_bus(tree *conf, dt_bus_type bus_type, int *skip)
+{
+ dt_device *dev;
+ tree *child;
+
+ dev = dt_device_of(conf);
+ if (dev->drv->bus_type == bus_type && (*skip)-- == 0)
+ return dev;
+
+ TREE_FOREACH_CHILD(child, conf) {
+ dev = dt_do_find_bus(child, bus_type, skip);
+ if (dev)
+ return dev;
+ }
+
+ return NULL;
+}
+
+static dt_device *dt_find_bus(tree *conf, dt_bus_type bus_type, int busno)
+{
+ return dt_do_find_bus(conf, bus_type, &busno);
+}
+
+PCIBus *dt_get_pcibus(dt_device *dev)
+{
+ dt_device *bus = dt_parent_device(dev);
+
+ return bus->drv->get_pcibus(bus);
+}
+
+int dt_get_unit(dt_device *dev)
+{
+ return dev->drv->get_unit(dev);
+}
+
+static dt_device *dt_new_device(tree *conf, const dt_driver *drv)
+{
+ dt_device *dev;
+ tree_prop *prop;
+
+ dev = qemu_malloc(sizeof(*dev));
+ dev->conf = conf;
+ dev->drv = drv;
+ LIST_INIT(&dev->reqs);
+ dev->visit = 0;
+ dev->priv = qemu_malloc(drv->privsz);
+ tree_put_user(conf, dev);
+
+ TREE_FOREACH_PROP(prop, conf)
+ dt_parse_prop(dev, prop);
+
+ return dev;
+}
+
+static dt_device *dt_create(tree *conf, const dt_driver drvtab[])
+{
+ const dt_driver *drv;
+ dt_device *dev;
+ tree *child;
+
+ drv = dt_driver_by_name(tree_node_name(conf), drvtab);
+ if (!drv) {
+ fprintf(stderr, "No driver for device %s\n",
+ tree_node_name(conf));
+ exit(1);
+ }
+
+ assert((drv->bus_type == DT_BUS_PCI) == (drv->get_pcibus != NULL));
+
+ dev = dt_new_device(conf, drv);
+
+ TREE_FOREACH_CHILD(child, conf)
+ dt_create(child, drvtab);
+
+ return dev;
+}
+
+static void dt_config(tree *conf, dt_host *host)
+{
+ dt_device *dev = dt_device_of(conf);
+ dt_device *bus = dt_parent_device(dev);
+ tree *child;
+
+ if (dev->drv->parent_bus_type == DT_BUS_NONE
+ ? bus != NULL
+ : bus == NULL || bus->drv->bus_type != dev->drv->parent_bus_type) {
+ fprintf(stderr, "Device %s is not on a suitable bus\n",
+ dev->drv->name);
+ exit(1);
+ }
+
+ if (dev->drv->config)
+ dev->drv->config(dev, host);
+
+ TREE_FOREACH_CHILD(child, conf)
+ dt_config(child, host);
+}
+
+tree *dt_require_named(dt_device *dev, const char *reqname)
+{
+ dt_tree_list *l = qemu_malloc(sizeof(*l));
+
+ l->conf = tree_node_by_name(dev->conf, reqname);
+ LIST_INSERT_HEAD(&dev->reqs, l, link);
+ return l->conf;
+}
+
+static void dt_do_visit(dt_device *dev,
+ void (*fun)(dt_device *, void *arg),
+ void *arg, int visit)
+{
+ dt_device *parent, *req, *child;
+ dt_tree_list *l;
+ tree *k;
+
+ assert(dev->visit < visit - 1);
+ dev->visit = visit - 1;
+ parent = dt_parent_device(dev);
+ if (parent && parent->visit < visit)
+ dt_do_visit(parent, fun, arg, visit);
+ LIST_FOREACH(l, &dev->reqs, link) {
+ req = dt_device_of(l->conf);
+ if (req->visit < visit)
+ dt_do_visit(req, fun, arg, visit);
+ }
+ dev->visit = visit;
+ fun(dev, arg);
+ TREE_FOREACH_CHILD(k, dev->conf) {
+ child = dt_device_of(k);
+ if (child->visit < visit - 1)
+ dt_do_visit(child, fun, arg, visit);
+ }
+}
+
+static void dt_visit(tree *node,
+ void (*fun)(dt_device *, void *arg),
+ void *arg)
+{
+ static int visit;
+
+ visit += 2;
+ dt_do_visit(dt_device_of(node), fun, arg, visit);
+}
+
+static void dt_init_visitor(dt_device *dev, void *arg)
+{
+ if (dev->drv->init)
+ dev->drv->init(dev);
+}
+
+static void dt_init(tree *conf)
+{
+ dt_visit(conf, dt_init_visitor, NULL);
+}
+
+static void dt_start(tree *conf)
+{
+ dt_device *dev = dt_device_of(conf);
+ tree *child;
+
+ if (dev && dev->drv->start)
+ dev->drv->start(dev);
+
+ TREE_FOREACH_CHILD(child, conf)
+ dt_start(child);
+}
+
+void dt_create_machine(tree *conf)
+{
+ dt_fdt_test(conf);
+ dt_init(conf);
+ dt_start(conf);
+}
+
+
+/* Device properties */
+
+static const dt_prop_spec *dt_prop_spec_by_name(const dt_driver *drv,
+ const char *name)
+{
+ const dt_prop_spec *spec;
+
+ for (spec = drv->prop_spec; spec && spec->name; spec++) {
+ if (!strcmp(spec->name, name))
+ return spec;
+ }
+ return NULL;
+}
+
+static void dt_parse_prop(dt_device *dev, tree_prop *prop)
+{
+ const char *name = tree_prop_name(prop);
+ size_t size;
+ const char *val = tree_prop_value(prop, &size);
+ const dt_prop_spec *spec = dt_prop_spec_by_name(dev->drv, name);
+
+ if (!spec) {
+ fprintf(stderr, "A %s device has no property %s\n",
+ dev->drv->name, name);
+ exit(1);
+ }
+
+ if (memchr(val, 0, size) != val + size - 1
+ || spec->parse((char *)dev->priv + spec->offs, val, spec) < 0) {
+ fprintf(stderr, "Bad value %.*s for property %s of device %s\n",
+ size, val, name, dev->drv->name);
+ exit(1);
+ }
+}
+
+int dt_parse_string(void *dst, const char *src, const dt_prop_spec *spec)
+{
+ assert(spec->size == sizeof(char *));
+ *(const char **)dst = src;
+ return 0;
+}
+
+int dt_parse_int(void *dst, const char *src, const dt_prop_spec *spec)
+{
+ char *ep;
+ long val;
+
+ assert(spec->size == sizeof(int));
+ errno = 0;
+ val = strtol(src, &ep, 0);
+ if (*ep || ep == src || errno || (int)val != val)
+ return -1;
+ *(int *)dst = val;
+ return 0;
+}
+
+int dt_parse_ram_addr_t(void *dst, const char *src, const dt_prop_spec *spec)
+{
+ char *ep;
+ unsigned long val;
+
+ assert(spec->size == sizeof(ram_addr_t));
+ errno = 0;
+ val = strtoul(src, &ep, 0);
+ if (*ep || ep == src || errno || (ram_addr_t)val != val)
+ return -1;
+ *(ram_addr_t *)dst = val;
+ return 0;
+}
+
+int dt_parse_macaddr(void *dst, const char *src, const dt_prop_spec *spec)
+{
+ assert(spec->size == 6);
+ if (parse_macaddr(dst, src) < 0)
+ return -1;
+ return 0;
+}
+
+
+/* Dynamic Devices */
+
+static void dt_add_dyn_dev(tree *conf, tree *node, const dt_driver drvtab[],
+ int busno)
+{
+ dt_device *dev = dt_create(node, drvtab);
+ dt_device *bus = dt_find_bus(conf, dev->drv->parent_bus_type, busno);
+
+ if (!bus) {
+ fprintf(stderr, "No suitable bus for device %s\n", dev->drv->name);
+ exit(1);
+ }
+
+ tree_insert(bus->conf, node);
+}
+
+static void dt_add_vga(tree *conf, const dt_driver drvtab[],
+ const char *model, int vga_ram_size)
+{
+ tree *node = tree_new_child(NULL, "vga", NULL);
+
+ tree_put_propf(node, "model", "%s", model);
+ tree_put_propf(node, "ram", "%#x", vga_ram_size);
+ dt_add_dyn_dev(conf, node, drvtab, 0);
+}
+
+static void dt_add_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, host->virtio_drives++, 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]);
+}
+
+
+/* Create a configuration */
+
+tree *dt_read_config(const char *name)
+{
+#ifdef TARGET_X86_64
+#define CPU_MODEL_DEFAULT "qemu64"
+#else
+#define CPU_MODEL_DEFAULT "qemu32"
+#endif
+ tree *root, *pci, *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, "memory", NULL);
+ leaf = tree_new_child(root, "pc-misc", NULL);
+ pci = tree_new_child(root, "pci", NULL);
+ leaf = tree_new_child(pci, "piix3", NULL);
+ leaf = tree_new_child(pci, "virtio-balloon", NULL);
+ return root;
+#undef CPU_MODEL_DEFAULT
+}
+
+/*
+ * Extract configuration from arguments and various global variables
+ * and put it into our machine configuration.
+ */
+void dt_modify_config(tree *conf,
+ const dt_driver drvtab[],
+ ram_addr_t ram_size, int vga_ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ /*
+ * TODO This is still pretty cheesy: we insert stuff into the tree
+ * at hardcoded places. Replacing placeholders instead would be
+ * more flexible. Another idea is to mark certain parts of the
+ * initial tree optional, and remove them here.
+ */
+ tree *node;
+ dt_host host;
+
+ 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);
+
+ node = tree_node_by_name(conf, "/memory");
+ tree_put_propf(node, "ram", "%#lx", (unsigned long)ram_size);
+
+ node = tree_node_by_name(conf, "/pc-misc");
+ tree_put_propf(node, "boot-device", "%s", boot_device);
+
+ /* Unimplemented stuff */
+ if (kernel_filename)
+ abort(); /* TODO */
+
+ dt_create(conf, drvtab);
+ memset(&host, 0, sizeof(host));
+ dt_add_dyn_devs(conf, &host, drvtab, vga_ram_size);
+ dt_config(conf, &host);
+
+ dt_print_host_config(&host);
+ tree_print(conf);
+}
+
+
+/* Interfacing with FDT */
+
+/*
+ * Note: translation to FDT loses the association between
+ * configuration tree nodes and devices.
+ */
+
+#ifdef HAVE_FDT
+
+static int dt_fdt_chk(int res);
+static void dt_subtree_to_fdt(const tree *conf, void *fdt);
+
+static void *dt_tree_to_fdt(const tree *conf)
+{
+ int sz = 1024 * 1024; /* FIXME arbitrary limit */
+ void *fdt = qemu_malloc(sz);
+
+ dt_fdt_chk(fdt_create(fdt, sz));
+ dt_subtree_to_fdt(conf, fdt);
+ dt_fdt_chk(fdt_finish(fdt));
+ return fdt;
+}
+
+static void dt_subtree_to_fdt(const tree *conf, void *fdt)
+{
+ tree_prop *prop;
+ tree *child;
+ const void *pv;
+ size_t sz;
+
+ dt_fdt_chk(fdt_begin_node(fdt, tree_node_name(conf)));
+ TREE_FOREACH_PROP(prop, conf) {
+ pv = tree_prop_value(prop, &sz);
+ dt_fdt_chk(fdt_property(fdt, tree_prop_name(prop), pv, sz));
+ }
+ TREE_FOREACH_CHILD(child, conf)
+ dt_subtree_to_fdt(child, fdt);
+ dt_fdt_chk(fdt_end_node(fdt));
+}
+
+static tree *dt_fdt_to_tree(const void *fdt)
+{
+ int offs, next, depth;
+ uint32_t tag;
+ struct fdt_property *prop;
+ tree *stack[32]; /* FIXME arbitrary limit */
+
+ stack[0] = NULL; /* "parent" of root */
+ next = depth = 0;
+
+ for (;;) {
+ offs = next;
+ tag = fdt_next_tag(fdt, offs, &next);
+ switch (tag) {
+ case FDT_PROP:
+ /*
+ * libfdt apparently doesn't provide a way to get property
+ * by offset, do it by hand
+ */
+ assert(0 < depth && depth < ARRAY_SIZE(stack));
+ prop = (void *)(const char *)fdt + fdt_off_dt_struct(fdt) + offs;
+ tree_put_prop(stack[depth],
+ fdt_string(fdt, fdt32_to_cpu(prop->nameoff)),
+ prop->data,
+ fdt32_to_cpu(prop->len));
+ case FDT_NOP:
+ break;
+ case FDT_BEGIN_NODE:
+ depth++;
+ assert(0 < depth && depth < ARRAY_SIZE(stack));
+ stack[depth] = tree_new_child(stack[depth-1],
+ fdt_get_name(fdt, offs, NULL),
+ NULL);
+ break;
+ case FDT_END_NODE:
+ depth--;
+ break;
+ case FDT_END:
+ dt_fdt_chk(next);
+ return stack[1];
+ }
+ }
+}
+
+static int dt_fdt_chk(int res)
+{
+ if (res < 0) {
+ fprintf(stderr, "%s\n", fdt_strerror(res)); /* FIXME cryptic */
+ exit(1);
+ }
+ return res;
+}
+
+static void dt_fdt_test(tree *conf)
+{
+ void *fdt;
+
+ fdt = dt_tree_to_fdt(conf);
+ conf = dt_fdt_to_tree(fdt);
+ tree_print(conf);
+ free(fdt);
+}
+#else
+static void dt_fdt_test(tree *conf) { }
+#endif
diff --git a/dt.h b/dt.h
new file mode 100644
index 0000000..9814167
--- /dev/null
+++ b/dt.h
@@ -0,0 +1,115 @@
+#ifndef DT_H
+#define DT_H
+
+#include "sysemu.h"
+#include "net.h"
+#include "tree.h"
+
+typedef struct dt_host dt_host;
+typedef struct dt_device dt_device;
+typedef struct dt_tree_list dt_tree_list;
+typedef struct dt_prop_spec dt_prop_spec;
+
+
+/* Host Configuration */
+
+VLANState *dt_find_vlan(tree *conf, dt_host *host);
+void dt_find_drives(tree *conf, dt_host *host,
+ BlockDriverState *drive[], int n);
+
+
+/* Device Interface */
+
+/*
+ * Device life cycle:
+ *
+ * 1. Configuration: config() method runs after parent's. It should
+ * initialize the device's private data from its configuration
+ * sub-tree. It may edit the configuration sub-tree, and may declare
+ * initialization ordering constraints with dt_require_named().
+ *
+ * 2. Initialization: init() method runs after parent's and after that
+ * of devices declared required by config(). It should not touch the
+ * configuration tree.
+ *
+ * 3. Start: start() method runs, order is unspecified.
+ *
+ * Error handling in these driver methods: print to stderr and exit
+ * the program unsuccessfully.
+ *
+ * There is no device shutdown protocol yet.
+ */
+
+struct dt_device {
+ tree *conf; /* configuration sub-tree */
+ const dt_driver *drv; /* device driver */
+ LIST_HEAD(, dt_tree_list) reqs; /* required devices */
+ int visit; /* for dt_visit() */
+ void *priv; /* device private data */
+};
+
+struct dt_tree_list {
+ tree *conf;
+ LIST_ENTRY(dt_tree_list) link;
+};
+
+typedef enum dt_bus_type {
+ DT_BUS_NONE, DT_BUS_ROOT, DT_BUS_PCI, DT_BUS_IDE, DT_BUS_SCSI,
+ DT_BUS_FLOPPY, DT_BUS_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;
+ void (*config)(dt_device *, dt_host *);
+ void (*init)(dt_device *);
+ void (*start)(dt_device *);
+ PCIBus *(*get_pcibus)(dt_device *); /* iff device is a PCI bus */
+ int (*get_unit)(dt_device *);
+};
+
+dt_device *dt_device_of(tree *conf);
+dt_device *dt_parent_device(dt_device *dev);
+PCIBus *dt_get_pcibus(dt_device *dev);
+int dt_get_unit(dt_device *dev);
+tree *dt_require_named(dt_device *dev, const char *reqname);
+
+tree *dt_read_config(const char *name);
+void dt_modify_config(tree *conf,
+ const dt_driver drvtab[],
+ 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);
+void dt_create_machine(tree *conf);
+
+
+/* Device properties */
+
+/*
+ * This is for parsing configuration tree node properties into device
+ * private data.
+ */
+
+struct dt_prop_spec {
+ const char *name;
+ ptrdiff_t offs; /* offset in device private data */
+ size_t size; /* size there, for sanity checking */
+ int (*parse)(void *, const char *, const dt_prop_spec *);
+};
+
+#define DT_PROP_SPEC_INIT(name, strty, member, fmt) \
+ { name, offsetof(strty, member), sizeof(((strty *)0)->member), \
+ dt_parse_##fmt }
+
+/* Canned property parse methods */
+int dt_parse_string(void *dst, const char *src, const dt_prop_spec *spec);
+int dt_parse_int(void *dst, const char *src, const dt_prop_spec *spec);
+int dt_parse_ram_addr_t(void *dst, const char *src, const dt_prop_spec *spec);
+int dt_parse_macaddr(void *dst, const char *src, const dt_prop_spec *spec);
+
+#endif
diff --git a/hw/boards.h b/hw/boards.h
index 1e62594..b611e88 100644
--- a/hw/boards.h
+++ b/hw/boards.h
@@ -13,7 +13,8 @@ typedef void QEMUMachineInitFunc(ram_addr_t ram_size, int
vga_ram_size,
typedef struct QEMUMachine {
const char *name;
const char *desc;
- QEMUMachineInitFunc *init;
+ QEMUMachineInitFunc *init; /* traditional machine initialization */
+ const dt_driver *drvtab; /* new alternative, used if !init */
#define RAMSIZE_FIXED (1 << 0)
ram_addr_t ram_require;
int nodisk_ok;
@@ -35,6 +36,9 @@ extern QEMUMachine axisdev88_machine;
extern QEMUMachine pc_machine;
extern QEMUMachine isapc_machine;
+/* pcdt.c */
+extern QEMUMachine pcdt_machine;
+
/* ppc.c */
extern QEMUMachine prep_machine;
extern QEMUMachine core99_machine;
diff --git a/hw/pc.c b/hw/pc.c
index 69f25f3..41a0225 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -37,42 +37,35 @@
#include "virtio-balloon.h"
#include "virtio-console.h"
#include "hpet_emul.h"
+#include "pcint.h"
/* output Bochs bios info messages */
//#define DEBUG_BIOS
-#define BIOS_FILENAME "bios.bin"
-#define VGABIOS_FILENAME "vgabios.bin"
-#define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin"
-
-#define PC_MAX_BIOS_SIZE (4 * 1024 * 1024)
-
/* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables. */
#define ACPI_DATA_SIZE 0x10000
#define BIOS_CFG_IOPORT 0x510
#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
-#define MAX_IDE_BUS 2
-
-static fdctrl_t *floppy_controller;
-static RTCState *rtc_state;
+fdctrl_t *floppy_controller;
+RTCState *rtc_state;
static PITState *pit;
static IOAPICState *ioapic;
-static PCIDevice *i440fx_state;
+PCIDevice *i440fx_state;
-static void ioport80_write(void *opaque, uint32_t addr, uint32_t data)
+void ioport80_write(void *opaque, uint32_t addr, uint32_t data)
{
}
/* MSDOS compatibility mode FPU exception support */
-static qemu_irq ferr_irq;
+qemu_irq ferr_irq;
/* XXX: add IGNNE support */
void cpu_set_ferr(CPUX86State *s)
{
qemu_irq_raise(ferr_irq);
}
-static void ioportF0_write(void *opaque, uint32_t addr, uint32_t data)
+void ioportF0_write(void *opaque, uint32_t addr, uint32_t data)
{
qemu_irq_lower(ferr_irq);
}
@@ -121,7 +114,7 @@ int cpu_get_pic_interrupt(CPUState *env)
return intno;
}
-static void pic_irq_request(void *opaque, int irq, int level)
+void pic_irq_request(void *opaque, int irq, int level)
{
CPUState *env = first_cpu;
@@ -167,7 +160,7 @@ static int cmos_get_fd_drive_type(int fd0)
return val;
}
-static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd)
+void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd)
{
RTCState *s = rtc_state;
int cylinders, heads, sectors;
@@ -203,7 +196,7 @@ static int boot_device2nibble(char boot_device)
/* copy/pasted from cmos_init, should be made a general function
and used there as well */
-static int pc_boot_set(void *opaque, const char *boot_device)
+int pc_boot_set(void *opaque, const char *boot_device)
{
Monitor *mon = cur_mon;
#define PC_MAX_BOOT_DEVICES 3
@@ -230,8 +223,8 @@ static int pc_boot_set(void *opaque, const char
*boot_device)
}
/* hd_table must contain 4 block drivers */
-static void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
- const char *boot_device, BlockDriverState **hd_table)
+void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
+ const char *boot_device, BlockDriverState **hd_table)
{
RTCState *s = rtc_state;
int nbds, bds[3] = { 0, };
@@ -364,13 +357,13 @@ int ioport_get_a20(void)
return ((first_cpu->a20_mask >> 20) & 1);
}
-static void ioport92_write(void *opaque, uint32_t addr, uint32_t val)
+void ioport92_write(void *opaque, uint32_t addr, uint32_t val)
{
ioport_set_a20((val >> 1) & 1);
/* XXX: bit 0 is fast reset */
}
-static uint32_t ioport92_read(void *opaque, uint32_t addr)
+uint32_t ioport92_read(void *opaque, uint32_t addr)
{
return ioport_get_a20() << 1;
}
@@ -422,7 +415,7 @@ static void bochs_bios_write(void *opaque, uint32_t addr,
uint32_t val)
}
}
-static void bochs_bios_init(void)
+void bochs_bios_init(void)
{
void *fw_cfg;
@@ -691,7 +684,7 @@ static void load_linux(uint8_t *option_rom,
generate_bootsect(option_rom, gpr, seg, 0);
}
-static void main_cpu_reset(void *opaque)
+void main_cpu_reset(void *opaque)
{
CPUState *env = opaque;
cpu_reset(env);
@@ -706,11 +699,11 @@ static const int ide_irq[2] = { 14, 15 };
static int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280,
0x380 };
static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
-static int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
-static int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 };
+int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
+int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 };
-static int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
-static int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 };
+int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
+int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 };
#ifdef HAS_AUDIO
static void audio_init (PCIBus *pci_bus, qemu_irq *pic)
diff --git a/hw/pcdt.c b/hw/pcdt.c
new file mode 100644
index 0000000..aebbf9f
--- /dev/null
+++ b/hw/pcdt.c
@@ -0,0 +1,677 @@
+/*
+ * 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"
+
+
+static BlockDriverState **dt_piix3_bds(tree *piix3);
+
+/* CPUs Driver */
+
+typedef struct dt_device_cpus {
+ const char *model;
+ int num;
+} dt_device_cpus;
+
+static const dt_prop_spec dt_cpus_props[] = {
+ DT_PROP_SPEC_INIT("model", dt_device_cpus, model, string),
+ DT_PROP_SPEC_INIT("num", dt_device_cpus, num, int),
+};
+
+static void dt_cpus_init(dt_device *dev)
+{
+ dt_device_cpus *priv = dev->priv;
+ int i;
+ CPUState *env;
+
+ for(i = 0; i < priv->num; i++) {
+ env = cpu_init(priv->model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ if (i != 0)
+ env->halted = 1;
+ qemu_register_reset(main_cpu_reset, env);
+ }
+}
+
+
+/* Memory Ranges */
+
+typedef struct dt_device_memrng {
+ target_phys_addr_t phys_addr;
+ ram_addr_t size;
+ ram_addr_t host_offs;
+ ram_addr_t flags;
+} dt_device_memrng;
+
+static void dt_memrng(dt_device_memrng *rng,
+ target_phys_addr_t phys_addr, ram_addr_t size,
+ ram_addr_t host_offs, ram_addr_t flags)
+{
+ rng->phys_addr = phys_addr;
+ rng->size = size;
+ rng->host_offs = host_offs;
+ rng->flags = flags;
+}
+
+static void dt_memrng_ram(dt_device_memrng *rng,
+ target_phys_addr_t phys_addr, ram_addr_t size)
+{
+ dt_memrng(rng, phys_addr, size, qemu_ram_alloc(size), 0);
+}
+
+static void dt_memrng_rom(dt_device_memrng *rng,
+ target_phys_addr_t phys_addr, ram_addr_t maxsz,
+ const char *dir, const char *image, int top)
+{
+ char buf[1024];
+ int size;
+
+ snprintf(buf, sizeof(buf), "%s/%s", dir, image);
+ size = get_image_size(buf);
+ if (size < 0 || size > maxsz)
+ goto error;
+ if (top)
+ phys_addr = phys_addr + maxsz - size;
+ dt_memrng(rng, phys_addr, size, qemu_ram_alloc(size), IO_MEM_ROM);
+ if (load_image(buf, phys_ram_base + rng->host_offs) != size)
+ goto error;
+ return;
+
+error:
+ fprintf(stderr, "qemu: could not load image '%s'\n", buf);
+ exit(1);
+}
+
+static void dt_memrng_init(dt_device_memrng *rng, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ cpu_register_physical_memory(rng[i].phys_addr, rng[i].size,
+ rng[i].host_offs | rng[i].flags);
+}
+
+
+/* Memory Driver */
+
+typedef struct dt_device_memory {
+ ram_addr_t ram_size;
+ dt_device_memrng *rng;
+ int nrng;
+ /* TODO want a real memory map here */
+ ram_addr_t below_4g, above_4g;
+} dt_device_memory;
+
+static const dt_prop_spec dt_memory_props[] = {
+ DT_PROP_SPEC_INIT("ram", dt_device_memory, ram_size, ram_addr_t),
+};
+
+static void dt_memory_config(dt_device *dev, dt_host *host)
+{
+ /* TODO memory map hardcoded; get it from dev->conf instead */
+ dt_device_memory *priv = dev->priv;
+ dt_device_memrng *rng = qemu_malloc(sizeof(*rng) * 4);
+
+ if (priv->ram_size >= 0xe0000000 ) {
+ priv->above_4g = priv->ram_size - 0xe0000000;
+ priv->below_4g = 0xe0000000;
+ } else {
+ priv->below_4g = priv->ram_size;
+ priv->above_4g = 0;
+ }
+
+ dt_memrng_ram(&rng[0], 0, 0xa0000);
+ qemu_ram_alloc(0x60000);
+ dt_memrng_ram(&rng[1], 0x100000, priv->below_4g - 0x100000);
+ if (priv->above_4g)
+ abort(); /* TODO */
+ dt_memrng_rom(&rng[2], 0xe0000000, 0x20000000,
+ bios_dir, BIOS_FILENAME, 1);
+ /* TODO get name from dev->conf */
+ dt_memrng(&rng[3], 0xe0000, 0x20000,
+ rng[2].host_offs + rng[2].size - 0x20000, IO_MEM_ROM);
+ /* TODO option ROMs */
+
+ priv->rng = rng;
+ priv->nrng = 4;
+}
+
+static void dt_memory_init(dt_device *dev)
+{
+ dt_device_memory *priv = dev->priv;
+
+ dt_memrng_init(priv->rng, priv->nrng);
+ bochs_bios_init();
+}
+
+static ram_addr_t dt_memory_below_4g(tree *memory)
+{
+ dt_device *dev = dt_device_of(memory);
+ dt_device_memory *priv = dev->priv;
+ assert(dev->drv->init == dt_memory_init);
+ return priv->below_4g;
+}
+
+static ram_addr_t dt_memory_above_4g(tree *memory)
+{
+ dt_device *dev = dt_device_of(memory);
+ dt_device_memory *priv = dev->priv;
+ assert(dev->drv->init == dt_memory_init);
+ return priv->above_4g;
+}
+
+
+/* PC Miscellanous Driver */
+
+/*
+ * This is a driver for a whole collection of devices. Could be
+ * picked apart into separate drivers, I guess.
+ */
+
+typedef struct dt_device_pc_misc {
+ const char *boot_device;
+ int apic;
+ int hpet;
+ qemu_irq *i8259;
+ BlockDriverState *bds[MAX_FD];
+} dt_device_pc_misc;
+
+static const dt_prop_spec dt_pc_misc_props[] = {
+ DT_PROP_SPEC_INIT("boot-device", dt_device_pc_misc, boot_device,
+ string),
+};
+
+static void dt_pc_misc_config(dt_device *dev, dt_host *host)
+{
+ dt_device_pc_misc *priv = dev->priv;
+
+ priv->apic = 1;
+ priv->hpet = 1;
+ priv->i8259 = NULL;
+ dt_find_drives(dev->conf, host, priv->bds, ARRAY_SIZE(priv->bds));
+}
+
+static void dt_pc_misc_init(dt_device *dev)
+{
+ dt_device_pc_misc *priv = dev->priv;
+ CPUState *env;
+ qemu_irq *cpu_irq;
+ IOAPICState *ioapic;
+ PITState *pit;
+ int i;
+
+ if (priv->apic) {
+ for (env = first_cpu; env; env = env->next_cpu) {
+ env->cpuid_features |= CPUID_APIC;
+ apic_init(env);
+ }
+ }
+
+ vmport_init();
+
+ cpu_irq = qemu_allocate_irqs(pic_irq_request, NULL, 1);
+ priv->i8259 = i8259_init(cpu_irq[0]);
+ ferr_irq = priv->i8259[13];
+
+ register_ioport_write(0x80, 1, 1, ioport80_write, NULL);
+ register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL);
+
+ rtc_state = rtc_init(0x70, priv->i8259[8], 2000);
+ qemu_register_boot_set(pc_boot_set, rtc_state);
+
+ register_ioport_read(0x92, 1, 1, ioport92_read, NULL);
+ register_ioport_write(0x92, 1, 1, ioport92_write, NULL);
+
+ if (priv->apic) {
+ ioapic = ioapic_init();
+ pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic);
+ }
+
+ pit = pit_init(0x40, priv->i8259[0]);
+ pcspk_init(pit);
+ if (priv->hpet)
+ hpet_init(priv->i8259);
+
+ for(i = 0; i < MAX_SERIAL_PORTS; i++) {
+ if (serial_hds[i]) {
+ serial_init(serial_io[i], priv->i8259[serial_irq[i]], 115200,
+ serial_hds[i]);
+ }
+ }
+
+ for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
+ if (parallel_hds[i]) {
+ parallel_init(parallel_io[i], priv->i8259[parallel_irq[i]],
+ parallel_hds[i]);
+ }
+ }
+
+ qemu_system_hot_add_init();
+
+ i8042_init(priv->i8259[1], priv->i8259[12], 0x60);
+ DMA_init(0);
+
+ floppy_controller = fdctrl_init(priv->i8259[6], 2, 0, 0x3f0, priv->bds);
+}
+
+static void dt_pc_misc_start(dt_device *dev)
+{
+ dt_device_pc_misc *priv = dev->priv;
+ tree *memory = tree_node_by_name(dev->conf, "/memory");
+ tree *piix3 = tree_node_by_name(dev->conf, "/pci/piix3");
+
+ cmos_init(dt_memory_below_4g(memory),
+ dt_memory_above_4g(memory),
+ priv->boot_device,
+ dt_piix3_bds(piix3));
+}
+
+static qemu_irq *dt_pc_misc_i8259(tree *pc_misc)
+{
+ dt_device *dev = dt_device_of(pc_misc);
+ dt_device_pc_misc *priv = dev->priv;
+ assert(dev->drv->init == dt_pc_misc_init);
+ return priv->i8259;
+}
+
+
+/* PCI Bus Driver */
+
+typedef struct dt_device_pci {
+ PCIBus *pcibus;
+ tree *pc;
+} dt_device_pci;
+
+static void dt_pci_config(dt_device *dev, dt_host *host)
+{
+ dt_device_pci *priv = dev->priv;
+
+ priv->pcibus = NULL;
+ priv->pc = dt_require_named(dev, "/pc-misc");
+}
+
+static void dt_pci_init(dt_device *dev)
+{
+ dt_device_pci *priv = dev->priv;
+
+ priv->pcibus = i440fx_init(&i440fx_state, dt_pc_misc_i8259(priv->pc));
+}
+
+static void dt_pci_start(dt_device *dev)
+{
+ i440fx_init_memory_mappings(i440fx_state);
+}
+
+static PCIBus *dt_pci_get_pcibus(dt_device *dev)
+{
+ return ((dt_device_pci *)dev->priv)->pcibus;
+}
+
+
+/* PIIX3 Driver */
+
+typedef struct dt_device_piix3 {
+ int devfn;
+ int acpi;
+ int usb;
+ tree *pc;
+ BlockDriverState *bds[MAX_IDE_BUS * MAX_IDE_DEVS];
+} dt_device_piix3;
+
+static void dt_piix3_config(dt_device *dev, dt_host *host)
+{
+ dt_device_piix3 *priv = dev->priv;
+
+ priv->devfn = -1;
+ priv->acpi = 1;
+ priv->usb = 1;
+ priv->pc = dt_require_named(dev, "/pc-misc");
+ dt_find_drives(dev->conf, host, priv->bds, ARRAY_SIZE(priv->bds));
+}
+
+static void dt_piix3_init(dt_device *dev)
+{
+ dt_device_piix3 *priv = dev->priv;
+ PCIBus *pci_bus = dt_get_pcibus(dev);
+ qemu_irq *i8259 = dt_pc_misc_i8259(priv->pc);
+ int i;
+
+ priv->devfn = piix3_init(pci_bus, priv->devfn);
+
+ pci_piix3_ide_init(pci_bus, priv->bds, 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_bds(tree *piix3)
+{
+ dt_device *dev = dt_device_of(piix3);
+ dt_device_piix3 *priv = dev->priv;
+
+ assert(dev->drv->init == dt_piix3_init);
+ return priv->bds;
+}
+
+
+/* VGA Driver */
+
+typedef struct dt_driver_vga {
+ const char *model;
+ const char *bios;
+ void (*init)(PCIBus *, uint8_t *, ram_addr_t, int);
+} dt_driver_vga;
+
+static void pci_vga_init_(PCIBus *bus, uint8_t *vga_ram_base,
+ ram_addr_t vga_ram_offset, int vga_ram_size)
+{
+ pci_vga_init(bus, vga_ram_base, vga_ram_offset, vga_ram_size, 0, 0);
+}
+
+static dt_driver_vga dt_driver_vga_table[] = {
+ { "cirrus", VGABIOS_CIRRUS_FILENAME, pci_cirrus_vga_init },
+ { "vms", VGABIOS_FILENAME, pci_vmsvga_init },
+ { "std", VGABIOS_FILENAME, pci_vga_init_ },
+ { NULL, NULL, NULL }
+};
+
+typedef struct dt_device_vga {
+ const char *model;
+ ram_addr_t ram_size;
+ dt_device_memrng rng[1];
+ ram_addr_t ram_offs;
+ dt_driver_vga *vga_drv;
+} dt_device_vga;
+
+static const dt_prop_spec dt_vga_props[] = {
+ DT_PROP_SPEC_INIT("model", dt_device_vga, model, string),
+ DT_PROP_SPEC_INIT("ram", dt_device_vga, ram_size, ram_addr_t),
+};
+
+static void dt_vga_config(dt_device *dev, dt_host *host)
+{
+ dt_device_vga *priv = dev->priv;
+ int i;
+
+ dt_memrng_rom(&priv->rng[0], 0xc0000, 0x10000,
+ bios_dir, VGABIOS_CIRRUS_FILENAME, 0);
+ /* TODO get name from dev->conf */
+ priv->ram_offs = qemu_ram_alloc(priv->ram_size);
+
+ for (i = 0; dt_driver_vga_table[i].model; i++) {
+ if (!strcmp(dt_driver_vga_table[i].model, priv->model))
+ break;
+ }
+ if (!dt_driver_vga_table[i].model) {
+ fprintf(stderr, "Unknown VGA model %s\n", priv->model);
+ exit(1);
+ }
+ priv->vga_drv = &dt_driver_vga_table[i];
+}
+
+static void dt_vga_init(dt_device *dev)
+{
+ dt_device_vga *priv = dev->priv;
+
+ dt_memrng_init(priv->rng, 1);
+ priv->vga_drv->init(dt_get_pcibus(dev),
+ phys_ram_base + priv->ram_offs,
+ priv->ram_offs, priv->ram_size);
+}
+
+
+/* NIC Driver */
+
+typedef struct dt_device_nic {
+ NICInfo nd;
+} dt_device_nic;
+
+static const dt_prop_spec dt_nic_props[] = {
+ DT_PROP_SPEC_INIT("model", dt_device_nic, nd.model, string),
+ DT_PROP_SPEC_INIT("mac", dt_device_nic, nd.macaddr, macaddr),
+ DT_PROP_SPEC_INIT("name", dt_device_nic, nd.name, string),
+};
+
+static void dt_nic_config(dt_device *dev, dt_host *host)
+{
+ dt_device_nic *priv = dev->priv;
+
+ priv->nd.vlan = dt_find_vlan(dev->conf, host);
+}
+
+static void dt_nic_init(dt_device *dev)
+{
+ dt_device_nic *priv = dev->priv;
+
+ pci_nic_init(dt_get_pcibus(dev), &priv->nd, -1, NULL);
+}
+
+
+/* SCSI Driver */
+
+typedef struct dt_device_scsi {
+ void *opaque;
+ BlockDriverState *bds[LSI_MAX_DEVS];
+} dt_device_scsi;
+
+static void dt_scsi_config(dt_device *dev, dt_host *host)
+{
+ dt_device_scsi *priv = dev->priv;
+
+ priv->opaque = NULL;
+ dt_find_drives(dev->conf, host, priv->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),
+};
+
+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),
+};
+
+static int dt_drive_get_unit(dt_device *dev)
+{
+ return ((dt_device_drive *)dev->priv)->unit;
+}
+
+
+/* Machine Driver */
+
+static const dt_driver dt_driver_table[] = {
+ { "", 0, NULL, DT_BUS_ROOT, DT_BUS_NONE, NULL, NULL, NULL, NULL, NULL },
+ { "cpus", sizeof(dt_device_cpus), dt_cpus_props,
+ DT_BUS_NONE, DT_BUS_ROOT,
+ NULL, dt_cpus_init, NULL, NULL, NULL },
+ { "memory", sizeof(dt_device_memory), dt_memory_props,
+ DT_BUS_NONE, DT_BUS_ROOT,
+ dt_memory_config, dt_memory_init, NULL, NULL, NULL },
+ { "pc-misc", sizeof(dt_device_pc_misc), dt_pc_misc_props,
+ DT_BUS_FLOPPY, DT_BUS_ROOT,
+ dt_pc_misc_config, dt_pc_misc_init, dt_pc_misc_start, NULL, NULL },
+ { "pci", sizeof(dt_device_pci), NULL,
+ DT_BUS_PCI, DT_BUS_ROOT,
+ dt_pci_config, dt_pci_init, dt_pci_start, dt_pci_get_pcibus, NULL },
+ { "piix3", sizeof(dt_device_piix3), NULL,
+ DT_BUS_IDE, DT_BUS_PCI,
+ dt_piix3_config, dt_piix3_init, NULL, NULL, NULL },
+ { "vga", sizeof(dt_device_vga), dt_vga_props,
+ DT_BUS_NONE, DT_BUS_PCI,
+ dt_vga_config, dt_vga_init, NULL, NULL, NULL },
+ { "nic", sizeof(dt_device_nic), dt_nic_props,
+ DT_BUS_NONE, DT_BUS_PCI,
+ dt_nic_config, dt_nic_init, NULL, NULL, NULL },
+ { "scsi", sizeof(dt_device_scsi), NULL,
+ DT_BUS_SCSI, DT_BUS_PCI,
+ dt_scsi_config, dt_scsi_init, NULL, NULL, NULL },
+ { "virtio-block", sizeof(dt_device_virtio_block), NULL,
+ DT_BUS_VIRTIO, DT_BUS_PCI,
+ dt_virtio_block_config, dt_virtio_block_init, NULL, NULL, NULL },
+ { "virtio-balloon", 0, NULL,
+ DT_BUS_NONE, DT_BUS_PCI,
+ NULL, dt_virtio_balloon_init, NULL, NULL, NULL },
+ { "virtio-console", sizeof(dt_device_virtio_console),
dt_virtio_console_props,
+ DT_BUS_NONE, DT_BUS_PCI,
+ dt_virtio_console_config, dt_virtio_console_init, NULL, NULL, NULL },
+ { "ide-drive", sizeof(dt_device_drive), dt_drive_props,
+ DT_BUS_NONE, DT_BUS_IDE,
+ NULL, NULL, NULL, NULL, dt_drive_get_unit },
+ { "scsi-drive", sizeof(dt_device_drive), dt_drive_props,
+ DT_BUS_NONE, DT_BUS_SCSI,
+ NULL, NULL, NULL, NULL, dt_drive_get_unit },
+ { "floppy-drive", sizeof(dt_device_drive), dt_drive_props,
+ DT_BUS_NONE, DT_BUS_FLOPPY,
+ NULL, NULL, NULL, NULL, dt_drive_get_unit },
+ { "virtio-drive", sizeof(dt_device_drive), dt_drive_props,
+ DT_BUS_NONE, DT_BUS_VIRTIO,
+ NULL, NULL, NULL, NULL, dt_drive_get_unit },
+ { NULL, 0, NULL, DT_BUS_NONE, DT_BUS_NONE, NULL, NULL, NULL, NULL, NULL }
+};
+
+QEMUMachine pcdt_machine = {
+ .name = "pcdt",
+ .desc = "Standard PC (device tree)",
+ .drvtab = dt_driver_table,
+ .ram_require = VGA_RAM_SIZE + PC_MAX_BIOS_SIZE,
+ .max_cpus = 255,
+};
diff --git a/hw/pci.h b/hw/pci.h
index 4f24895..26fe59e 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -213,7 +213,7 @@ void *lsi_scsi_init(PCIBus *bus, int devfn);
/* vmware_vga.c */
void pci_vmsvga_init(PCIBus *bus, uint8_t *vga_ram_base,
- unsigned long vga_ram_offset, int vga_ram_size);
+ ram_addr_t vga_ram_offset, int vga_ram_size);
/* usb-uhci.c */
void usb_uhci_piix3_init(PCIBus *bus, int devfn);
diff --git a/hw/pcint.h b/hw/pcint.h
new file mode 100644
index 0000000..f18da67
--- /dev/null
+++ b/hw/pcint.h
@@ -0,0 +1,46 @@
+/*
+ * Stuff shared by pc.c and dt.c
+ *
+ * See dt.c for why this should go away eventually.
+ */
+
+#ifndef HW_PC_INT_H
+#define HW_PC_INT_H
+
+#define BIOS_FILENAME "bios.bin"
+#define VGABIOS_FILENAME "vgabios.bin"
+#define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin"
+
+#define PC_MAX_BIOS_SIZE (4 * 1024 * 1024)
+
+#define MAX_IDE_BUS 2
+
+/* TODO move to ferr stuff in cpu.h? */
+extern qemu_irq ferr_irq;
+void ioportF0_write(void *opaque, uint32_t addr, uint32_t data);
+
+/* TODO eliminate */
+extern RTCState *rtc_state;
+extern PCIDevice *i440fx_state;
+extern int serial_io[MAX_SERIAL_PORTS];
+extern int serial_irq[MAX_SERIAL_PORTS];
+extern int parallel_io[MAX_PARALLEL_PORTS];
+extern int parallel_irq[MAX_PARALLEL_PORTS];
+extern fdctrl_t *floppy_controller;
+
+/* TODO move to pic stuff in pc.h? */
+void pic_irq_request(void *opaque, int irq, int level);
+
+/* TODO move to a20 stuff in pc.h? */
+void ioport92_write(void *opaque, uint32_t addr, uint32_t val);
+uint32_t ioport92_read(void *opaque, uint32_t addr);
+
+void bochs_bios_init(void);
+void main_cpu_reset(void *opaque);
+void ioport80_write(void *opaque, uint32_t addr, uint32_t data);
+int pc_boot_set(void *opaque, const char *boot_device);
+void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd);
+void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
+ const char *boot_device, BlockDriverState **hd_table);
+
+#endif
diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c
index 5c271e6..45fdbc8 100644
--- a/hw/vmware_vga.c
+++ b/hw/vmware_vga.c
@@ -1122,7 +1122,7 @@ static int vmsvga_load(struct vmsvga_state_s *s, QEMUFile
*f)
}
static void vmsvga_init(struct vmsvga_state_s *s,
- uint8_t *vga_ram_base, unsigned long vga_ram_offset,
+ uint8_t *vga_ram_base, ram_addr_t vga_ram_offset,
int vga_ram_size)
{
s->vram = vga_ram_base;
@@ -1216,7 +1216,7 @@ static void pci_vmsvga_map_mem(PCIDevice *pci_dev, int
region_num,
#define PCI_CLASS_HEADERTYPE_00h 0x00
void pci_vmsvga_init(PCIBus *bus, uint8_t *vga_ram_base,
- unsigned long vga_ram_offset, int vga_ram_size)
+ ram_addr_t vga_ram_offset, int vga_ram_size)
{
struct pci_vmsvga_state_s *s;
diff --git a/net.c b/net.c
index c853daf..831b002 100644
--- a/net.c
+++ b/net.c
@@ -157,7 +157,7 @@ static void hex_dump(FILE *f, const uint8_t *buf, int size)
}
#endif
-static int parse_macaddr(uint8_t *macaddr, const char *p)
+int parse_macaddr(uint8_t *macaddr, const char *p)
{
int i;
char *last_char;
diff --git a/net.h b/net.h
index 1a51be7..54bdf80 100644
--- a/net.h
+++ b/net.h
@@ -47,6 +47,7 @@ int qemu_can_send_packet(VLANClientState *vc);
ssize_t qemu_sendv_packet(VLANClientState *vc, const struct iovec *iov,
int iovcnt);
void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size);
+int parse_macaddr(uint8_t *macaddr, const char *p);
void qemu_format_nic_info_str(VLANClientState *vc, uint8_t macaddr[6]);
void qemu_check_nic_model(NICInfo *nd, const char *model);
void qemu_check_nic_model_list(NICInfo *nd, const char * const *models,
diff --git a/qemu-common.h b/qemu-common.h
index 28f4791..3d1bcf3 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -178,6 +178,7 @@ typedef struct PCIDevice PCIDevice;
typedef struct SerialState SerialState;
typedef struct IRQState *qemu_irq;
struct pcmcia_card_s;
+typedef struct dt_driver dt_driver;
/* CPU save/load. */
void cpu_save(QEMUFile *f, void *opaque);
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
diff --git a/vl.c b/vl.c
index abc7f5d..b85e328 100644
--- a/vl.c
+++ b/vl.c
@@ -152,6 +152,7 @@ int main(int argc, char **argv)
#include "migration.h"
#include "kvm.h"
#include "balloon.h"
+#include "dt.h"
#include "disas.h"
@@ -5621,8 +5622,18 @@ int main(int argc, char **argv, char **envp)
}
}
- machine->init(ram_size, vga_ram_size, boot_devices,
- kernel_filename, kernel_cmdline, initrd_filename, cpu_model);
+ if (machine->init) {
+ machine->init(ram_size, vga_ram_size, boot_devices,
+ kernel_filename, kernel_cmdline, initrd_filename,
+ cpu_model);
+ } else {
+ tree *conf = dt_read_config(machine->name);
+ dt_modify_config(conf, machine->drvtab,
+ ram_size, vga_ram_size, boot_devices,
+ kernel_filename, kernel_cmdline, initrd_filename,
+ cpu_model);
+ dt_create_machine(conf);
+ }
current_machine = machine;