From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1LlmTl-0001sO-EX for qemu-devel@nongnu.org; Mon, 23 Mar 2009 11:54:21 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1LlmTf-0001pt-V7 for qemu-devel@nongnu.org; Mon, 23 Mar 2009 11:54:20 -0400 Received: from [199.232.76.173] (port=48759 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LlmTf-0001pk-Kw for qemu-devel@nongnu.org; Mon, 23 Mar 2009 11:54:15 -0400 Received: from mx2.redhat.com ([66.187.237.31]:47022) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1LlmTR-0000Oe-OW for qemu-devel@nongnu.org; Mon, 23 Mar 2009 11:54:15 -0400 Received: from int-mx2.corp.redhat.com (int-mx2.corp.redhat.com [172.16.27.26]) by mx2.redhat.com (8.13.8/8.13.8) with ESMTP id n2NFrqBX014275 for ; Mon, 23 Mar 2009 11:53:52 -0400 Received: from ns3.rdu.redhat.com (ns3.rdu.redhat.com [10.11.255.199]) by int-mx2.corp.redhat.com (8.13.1/8.13.1) with ESMTP id n2NFrlfY000756 for ; Mon, 23 Mar 2009 11:53:47 -0400 Received: from pike.pond.sub.org (vpn-10-27.str.redhat.com [10.32.10.27]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id n2NFrjwl014681 for ; Mon, 23 Mar 2009 11:53:50 -0400 References: <87iqnh6kyv.fsf@pike.pond.sub.org> <87prg8s0f2.fsf@pike.pond.sub.org> From: Markus Armbruster Date: Mon, 23 Mar 2009 16:53:41 +0100 In-Reply-To: <87prg8s0f2.fsf@pike.pond.sub.org> (Markus Armbruster's message of "Mon\, 23 Mar 2009 16\:50\:57 +0100") Message-ID: <87eiwos0ai.fsf@pike.pond.sub.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Subject: [Qemu-devel] Re: [RFC] Machine description as data Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org 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 + * 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 +#include "block.h" +#include "cpu.h" +#include "dt.h" +#include "net.h" +#include "tree.h" +#include "sysemu.h" + +#ifdef HAVE_FDT +#include +#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 + * 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 +#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 + * + * 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 +#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 + +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;