From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1LzYjz-0007Zv-Hg for qemu-devel@nongnu.org; Thu, 30 Apr 2009 12:04:03 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1LzYju-0007XH-Cy for qemu-devel@nongnu.org; Thu, 30 Apr 2009 12:04:02 -0400 Received: from [199.232.76.173] (port=39902 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LzYju-0007XA-6f for qemu-devel@nongnu.org; Thu, 30 Apr 2009 12:03:58 -0400 Received: from oxygen.pond.sub.org ([213.239.205.148]:59686) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1LzYjr-0003HU-M8 for qemu-devel@nongnu.org; Thu, 30 Apr 2009 12:03:57 -0400 Received: from pike.pond.sub.org (pD9E38364.dip.t-dialin.net [217.227.131.100]) by oxygen.pond.sub.org (Postfix) with ESMTPA id 96590276D52 for ; Thu, 30 Apr 2009 18:03:49 +0200 (CEST) From: Markus Armbruster Date: Thu, 30 Apr 2009 18:03:48 +0200 Message-Id: <52c31337720cc40f8f7cbb6cbcd1b6a3f41407a0.1241106809.git.armbru@redhat.com> In-Reply-To: References: Subject: [Qemu-devel] [PATCH 4/4] Machine description as data List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Signed-off-by: Markus Armbruster --- Makefile | 1 + Makefile.target | 7 +- dt-fdt.c | 123 +++++++ dt-host.c | 176 +++++++++ dt.c | 600 +++++++++++++++++++++++++++++++ dt.h | 150 ++++++++ hw/boards.h | 3 + hw/pc.c | 53 ++-- hw/pcdt.c | 941 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/pcidt.c | 67 ++++ hw/pcint.h | 47 +++ target-i386/machine.c | 1 + tree.c | 285 +++++++++++++++ tree.h | 41 +++ 14 files changed, 2464 insertions(+), 31 deletions(-) create mode 100644 dt-fdt.c create mode 100644 dt-host.c create mode 100644 dt.c create mode 100644 dt.h create mode 100644 hw/pcdt.c create mode 100644 hw/pcidt.c create mode 100644 hw/pcint.h create mode 100644 tree.c create mode 100644 tree.h diff --git a/Makefile b/Makefile index 0f40cda..7c06e12 100644 --- a/Makefile +++ b/Makefile @@ -100,6 +100,7 @@ OBJS+=bt-hci-csr.o OBJS+=buffered_file.o migration.o migration-tcp.o net.o qemu-sockets.o OBJS+=qemu-char.o aio.o net-checksum.o savevm.o cache-utils.o OBJS+=msmouse.o ps2.o +OBJS+=tree.o ifdef CONFIG_BRLAPI OBJS+= baum.o diff --git a/Makefile.target b/Makefile.target index 82ada5a..7b7d77d 100644 --- a/Makefile.target +++ b/Makefile.target @@ -503,6 +503,10 @@ OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o dma-helpers.o # need to fix this properly OBJS+=virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o OBJS+=fw_cfg.o +OBJS+=dt.o dt-host.o pcidt.o +ifdef FDT_LIBS +OBJS+= dt-fdt.o +endif ifdef CONFIG_KVM OBJS+=kvm.o kvm-all.o endif @@ -534,6 +538,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 +593,7 @@ OBJS+= ide.o pckbd.o vga.o $(SOUND_HW) dma.o OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o OBJS+= cirrus_vga.o apic.o ioapic.o parallel.o acpi.o piix_pci.o OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o +OBJS+= pcdt.o OBJS += device-hotplug.o pci-hotplug.o smbios.o CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE endif @@ -611,7 +617,6 @@ OBJS+= ppc440.o ppc440_bamboo.o OBJS+= ppce500_pci.o ppce500_mpc8544ds.o ifdef FDT_LIBS OBJS+= device_tree.o -LIBS+= $(FDT_LIBS) endif ifdef CONFIG_KVM OBJS+= kvm_ppc.o diff --git a/dt-fdt.c b/dt-fdt.c new file mode 100644 index 0000000..375e50a --- /dev/null +++ b/dt-fdt.c @@ -0,0 +1,123 @@ +/* + * QEMU PC System Emulator + * + * Copyright (C) 2009 Red Hat, Inc., Markus Armbruster + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* Interfacing with FDT */ + +/* + * Note: translation to FDT loses the association between + * configuration tree nodes and devices. + */ + +#include +#include "dt.h" + +static int dt_fdt_chk(int res); +static void dt_subtree_to_fdt(const tree *conf, void *fdt); + +static void *dt_tree_to_fdt(const tree *conf) +{ + int sz = 1024 * 1024; /* FIXME arbitrary limit */ + void *fdt = qemu_malloc(sz); + + dt_fdt_chk(fdt_create(fdt, sz)); + dt_subtree_to_fdt(conf, fdt); + dt_fdt_chk(fdt_finish(fdt)); + return fdt; +} + +static void dt_subtree_to_fdt(const tree *conf, void *fdt) +{ + tree_prop *prop; + tree *child; + const void *pv; + size_t sz; + + dt_fdt_chk(fdt_begin_node(fdt, tree_node_name(conf))); + TREE_FOREACH_PROP(prop, conf) { + pv = tree_prop_value(prop, &sz); + dt_fdt_chk(fdt_property(fdt, tree_prop_name(prop), pv, sz)); + } + TREE_FOREACH_CHILD(child, conf) + dt_subtree_to_fdt(child, fdt); + dt_fdt_chk(fdt_end_node(fdt)); +} + +static tree *dt_fdt_to_tree(const void *fdt) +{ + int offs, next, depth; + uint32_t tag; + struct fdt_property *prop; + tree *stack[32]; /* FIXME arbitrary limit */ + + stack[0] = NULL; /* "parent" of root */ + next = depth = 0; + + for (;;) { + offs = next; + tag = fdt_next_tag(fdt, offs, &next); + switch (tag) { + case FDT_PROP: + /* + * libfdt apparently doesn't provide a way to get property + * by offset, do it by hand + */ + assert(0 < depth && depth < ARRAY_SIZE(stack)); + prop = (void *)(const char *)fdt + fdt_off_dt_struct(fdt) + offs; + tree_put_prop(stack[depth], + fdt_string(fdt, fdt32_to_cpu(prop->nameoff)), + prop->data, + fdt32_to_cpu(prop->len)); + case FDT_NOP: + break; + case FDT_BEGIN_NODE: + depth++; + assert(0 < depth && depth < ARRAY_SIZE(stack)); + stack[depth] = tree_new_child(stack[depth-1], + fdt_get_name(fdt, offs, NULL), + NULL); + break; + case FDT_END_NODE: + depth--; + break; + case FDT_END: + dt_fdt_chk(next); + return stack[1]; + } + } +} + +static int dt_fdt_chk(int res) +{ + if (res < 0) { + fprintf(stderr, "%s\n", fdt_strerror(res)); /* FIXME cryptic */ + exit(1); + } + return res; +} + +void dt_fdt_test(tree *conf) +{ + void *fdt; + + fdt = dt_tree_to_fdt(conf); + conf = dt_fdt_to_tree(fdt); + tree_print(conf); + free(fdt); +} diff --git a/dt-host.c b/dt-host.c new file mode 100644 index 0000000..eec8dc9 --- /dev/null +++ b/dt-host.c @@ -0,0 +1,176 @@ +/* + * 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. + */ + +/* Host Configuration */ + +#include +#include "block.h" +#include "dt.h" + +/* TODO stupid arbitrary limit */ +#define MAX_MEM (MAX_OPTION_ROMS + 4) + +typedef struct dt_host_memory { + dt_mem_loader loader; + target_phys_addr_t addr; + ram_addr_t size; + const void *arg; +} dt_host_memory; + +struct dt_host { + /* driver table */ + const dt_driver *drvtab; + /* connection NIC <-> VLAN */ + int nics; + tree *nic[MAX_NICS]; + VLANState *nic_vlan[MAX_NICS]; + /* connection drive <-> block driver state */ + int drives; + tree *drive[MAX_DRIVES]; + BlockDriverState *drive_state[MAX_DRIVES]; + /* initial memory contents */ + int mems; + dt_host_memory mem[MAX_MEM]; + /* the rest isn't configuration, it's derived from other stuff */ + int virtio_buses; +}; + +dt_host *dt_create_host(const dt_driver *drvtab) +{ + dt_host *host = qemu_mallocz(sizeof(dt_host)); + host->drvtab = drvtab; + return host; +} + +const dt_driver *dt_driver_by_name(dt_host *host, const char *name) +{ + const dt_driver *drvtab = host->drvtab; + int i; + + for (i = 0; drvtab[i].name; i++) { + if (!strcmp(name, drvtab[i].name)) + return &drvtab[i]; + } + return NULL; +} + +void dt_attach_nic(dt_host *host, tree *nic, VLANState *vlan) +{ + assert(host->nics < MAX_NICS); + host->nic[host->nics] = nic; + host->nic_vlan[host->nics] = vlan; + host->nics++; +} + +VLANState *dt_find_vlan(tree *conf, dt_host *host) +{ + int i; + + for (i = 0; i < host->nics; i++) { + if (host->nic[i] == conf) + return host->nic_vlan[i]; + } + return NULL; +} + +void dt_attach_drive(dt_host *host, tree *node, BlockDriverState *state) +{ + assert(host->drives < MAX_DRIVES); + host->drive[host->drives] = node; + host->drive_state[host->drives] = state; + host->drives++; +} + +void dt_find_drives(tree *conf, dt_host *host, + BlockDriverState *drive[], int n) +{ + int i, unit; + + memset(drive, 0, n * sizeof(drive[0])); + + for (i = 0; i < host->drives; i++) { + if (tree_parent(host->drive[i]) != conf) + continue; + unit = dt_get_unit(dt_device_of(host->drive[i])); + assert(unit < n && !drive[unit]); + drive[unit] = host->drive_state[i]; + } +} + +void dt_config_mem(dt_host *host, dt_mem_loader loader, + target_phys_addr_t addr, ram_addr_t size, const void *arg) +{ + assert(host->mems < MAX_MEM); + host->mem[host->mems].loader = loader; + host->mem[host->mems].addr = addr; + host->mem[host->mems].size = size; + host->mem[host->mems].arg = arg; + host->mems++; +} + +static void dt_init_mem(dt_host *host) +{ + dt_host_memory *p; + + for (p = host->mem; p < host->mem + host->mems; p++) + p->loader(p->addr, p->size, p->arg); +} + +void dt_image_loader(target_phys_addr_t addr, ram_addr_t size, const void *arg) +{ + if (load_image_targphys(arg, addr, size) < 0) { + fprintf(stderr, "qemu: could not load image '%s'\n", (char *)arg); + exit(1); + } +} + +int dt_alloc_virtio_bus(dt_host *host) +{ + return host->virtio_buses++; +} + +void dt_host_init(dt_host *host) +{ + dt_init_mem(host); +} + +void dt_print_host_config(dt_host *host) +{ + char buf[1024]; + int i; + + for (i = 0; i < host->nics; i++) { + if (!host->nic[i]) + continue; + tree_path(host->nic[i], buf, sizeof(buf)); + printf("nic#%d\tvlan %-4d\t%s\n", + i, host->nic_vlan[i]->id, buf); + } + + for (i = 0; i < host->drives; i++) { + tree_path(host->drive[i], buf, sizeof(buf)); + printf("drive#%d\t%-15s %s\n", + i, bdrv_get_device_name(host->drive_state[i]), buf); + } + + for (i = 0; i < host->mems; i++) + printf("image\t" TARGET_FMT_plx " %08lx\n", + host->mem[i].addr, (unsigned long)host->mem[i].size); +} diff --git a/dt.c b/dt.c new file mode 100644 index 0000000..1353f5c --- /dev/null +++ b/dt.c @@ -0,0 +1,600 @@ +/* + * 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. + */ + +#include +#include "dt.h" + +/* Forward declarations */ +static void dt_parse_prop(dt_device *dev, tree_prop *prop); + + +/* Device Interface */ + +dt_device *dt_device_of(tree *conf) +{ + return tree_get_user(conf); +} + +dt_device *dt_parent_device(dt_device *dev) +{ + tree *p = tree_parent(dev->conf); + + return p ? dt_device_of(p) : NULL; +} + +dt_device *dt_root(dt_device *dev) +{ + tree *p, *r; + + p = dev->conf; + do r = p; while ((p = tree_parent(p))); + + return dt_device_of(r); +} + +static dt_device *dt_do_find_bus(tree *conf, dt_bus_type bus_type, int *skip) +{ + dt_device *dev; + tree *child; + + dev = dt_device_of(conf); + if (dev->drv->bus_type == bus_type && (*skip)-- == 0) + return dev; + + TREE_FOREACH_CHILD(child, conf) { + dev = dt_do_find_bus(child, bus_type, skip); + if (dev) + return dev; + } + + return NULL; +} + +dt_device *dt_find_bus(tree *conf, dt_bus_type bus_type, int busno) +{ + return dt_do_find_bus(conf, bus_type, &busno); +} + +PCIBus *dt_get_pcibus(dt_device *dev) +{ + dt_device *bus = dt_parent_device(dev); + + return bus->drv->get_pcibus(bus); +} + +int dt_get_unit(dt_device *dev) +{ + return dev->drv->get_unit(dev); +} + +static dt_device *dt_int_parent(dt_device *dev) +{ + dt_device *res; + + if (dev->int_parent) + return dt_device_of(dev->int_parent); + + res = dt_parent_device(dev); + return res && res->drv->get_int ? res : NULL; +} + +qemu_irq *dt_get_int(dt_device *dev, int n) +{ + dt_device *int_parent = dt_int_parent(dev); + int nirq; + qemu_irq *res; + + if (!int_parent) + return NULL; + + res = int_parent->drv->get_int(int_parent, &nirq); + assert(n <= nirq); + return res; +} + +static dt_device *dt_new_device(tree *conf, const dt_driver *drv) +{ + dt_device *dev; + tree_prop *prop; + + dev = qemu_malloc(sizeof(*dev)); + dev->conf = conf; + dev->drv = drv; + dev->int_parent = NULL; + TAILQ_INIT(&dev->int_children); + dev->priv = qemu_mallocz(drv->privsz); + tree_put_user(conf, dev); + + TREE_FOREACH_PROP(prop, conf) + dt_parse_prop(dev, prop); + + return dev; +} + +static dt_device *dt_create(tree *conf, dt_host *host) +{ + const dt_driver *drv; + dt_device *dev; + tree *child; + + drv = dt_driver_by_name(host, tree_node_name(conf)); + 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, host); + + return dev; +} + +static void dt_config(dt_device *dev, dt_host *host) +{ + dt_device *bus = dt_parent_device(dev); + dt_device *int_parent = dt_int_parent(dev); + dt_device *up; + tree *child; + + if (dev->drv->parent_bus_type == DT_BUS_NONE + ? bus != NULL + : bus == NULL || bus->drv->bus_type != dev->drv->parent_bus_type) { + fprintf(stderr, "Device %s is not on a suitable bus\n", + dev->drv->name); + exit(1); + } + + if (dev->int_parent) { + if (!int_parent->drv->get_int) { + fprintf(stderr, "Device %s has an invalid interrupt-parent\n", + dev->drv->name); + exit(1); + } + for (up = int_parent; up; up = dt_int_parent(up)) { + if (up == dev) { + fprintf(stderr, "Device %s is its own interrupt ancestor\n", + dev->drv->name); + exit(1); + } + } + } + + if (int_parent) + TAILQ_INSERT_TAIL(&int_parent->int_children, dev, int_siblings); + else if (dev->drv->get_int) + TAILQ_INSERT_TAIL(&dt_root(dev)->int_children, dev, int_siblings); + + if (dev->drv->config) + dev->drv->config(dev, host); + + TREE_FOREACH_CHILD(child, dev->conf) + dt_config(dt_device_of(child), host); +} + +static void dt_int_init(dt_device *dev) +{ + dt_device *child; + + if (dev->drv->int_init) + dev->drv->int_init(dev); + + TAILQ_FOREACH(child, &dev->int_children, int_siblings) + dt_int_init(child); +} + +static void dt_init(dt_device *dev) +{ + tree *child; + + if (dev->drv->init) + dev->drv->init(dev); + + TREE_FOREACH_CHILD(child, dev->conf) + dt_init(dt_device_of(child)); +} + +static void dt_start(dt_device *dev) +{ + tree *child; + + if (dev->drv->start) + dev->drv->start(dev); + + TREE_FOREACH_CHILD(child, dev->conf) + dt_start(dt_device_of(child)); +} + +void dt_init_machine(dt_device *root, dt_host *host) +{ + dt_int_init(root); + dt_init(root); + dt_host_init(host); + dt_start(root); +} + + +/* Device properties */ + +static const dt_prop_spec *bus_prop_spec[DT_BUS_NUM] = { + [DT_BUS_PCI] = dt_bus_prop_spec_pci, +}; + +static const dt_prop_spec *dt_prop_spec_by_name(const dt_prop_spec *prop_spec, + const char *name) +{ + const dt_prop_spec *spec; + + for (spec = 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; + + spec = dt_prop_spec_by_name(dev->drv->prop_spec, name); + + if (!spec) + spec = dt_prop_spec_by_name(bus_prop_spec[dev->drv->parent_bus_type], + name); + + if (!spec && !strcmp(name, "interrupt-parent")) { + dev->int_parent = tree_node_by_name(dev->conf, val); + return; + } + + if (!spec) { + fprintf(stderr, "A %s device has no property %s\n", + dev->drv->name, name); + exit(1); + } + + if (spec->parse) { + if (!val) { + fprintf(stderr, "Property %s of device %s needs a value\n", + name, dev->drv->name); + exit(1); + } + if (memchr(val, 0, size) != val + size - 1 + || spec->parse((char *)dev->priv + spec->offs, val, spec) < 0) { + fprintf(stderr, "Bad value %.*s for property %s of device %s\n", + size, val, name, dev->drv->name); + exit(1); + } + } else { + if (val) { + fprintf(stderr, "Property %s of device %s doesn't take a value\n", + name, dev->drv->name); + exit(1); + } + assert(spec->size == sizeof(int)); + *(int *)((char *)dev->priv + spec->offs) = 1; + } +} + +int dt_parse_string(void *dst, const char *src, const dt_prop_spec *spec) +{ + assert(spec->size == sizeof(char *)); + *(const char **)dst = src; + return 0; +} + +int dt_parse_int(void *dst, const char *src, const dt_prop_spec *spec) +{ + char *ep; + long val; + + assert(spec->size == sizeof(int)); + errno = 0; + val = strtol(src, &ep, 0); + if (*ep || ep == src || errno || (int)val != val) + return -1; + *(int *)dst = val; + return 0; +} + +int dt_parse_ram_addr_t(void *dst, const char *src, const dt_prop_spec *spec) +{ + char *ep; + unsigned long val; + + assert(spec->size == sizeof(ram_addr_t)); + errno = 0; + val = strtoul(src, &ep, 0); + if (*ep || ep == src || errno || (ram_addr_t)val != val) + return -1; + *(ram_addr_t *)dst = val; + return 0; +} + +int dt_parse_target_phys_addr_t(void *dst, const char *src, + const dt_prop_spec *spec) +{ + char *ep; + unsigned long long val; + + assert(spec->size == sizeof(target_phys_addr_t)); + errno = 0; + val = strtoull(src, &ep, 0); + if (*ep || ep == src || errno || (target_phys_addr_t)val != val) + return -1; + *(target_phys_addr_t *)dst = val; + return 0; +} + +int dt_parse_macaddr(void *dst, const char *src, const dt_prop_spec *spec) +{ + assert(spec->size == 6); + if (parse_macaddr(dst, src) < 0) + return -1; + return 0; +} + + +/* Dynamic Devices */ + +static void dt_add_dyn_dev(tree *conf, dt_host *host, tree *node, int busno) +{ + dt_device *dev = dt_create(node, host); + 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, dt_host *host, + 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, host, node, 0); +} + +static void dt_add_virtio_console(tree *conf, dt_host *host, int index) +{ + tree *node = tree_new_child(NULL, "virtio-console", NULL); + + tree_put_propf(node, "index", "%d", index); + dt_add_dyn_dev(conf, host, node, 0); +} + +static void dt_add_nic(tree *conf, dt_host *host, NICInfo *n) +{ + tree *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, host, node, 0); + dt_attach_nic(host, node, n->vlan); +} + +static void dt_add_scsi(tree *conf, dt_host *host, int busno) +{ + tree *node = tree_new_child(NULL, "scsi", NULL); + + dt_add_dyn_dev(conf, host, node, 0); + assert(dt_find_bus(conf, DT_BUS_SCSI, busno)->conf == node); +} + +static void dt_add_virtio_block(tree *conf, dt_host *host, int busno) +{ + tree *node = tree_new_child(NULL, "virtio-block", NULL); + + dt_add_dyn_dev(conf, host, node, 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, + 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, host, node, busno); + dt_attach_drive(host, node, bdrv); +} + +static void dt_add_drive(tree *conf, dt_host *host, 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, + 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, + 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, + d->type, dt_alloc_virtio_bus(host), 0, d->bdrv); + break; + default: + /* TODO implement */ + fprintf(stderr, "Ignoring unimplemented drive %s\n", + drives_opt[d->drive_opt_idx].opt); + break; + } +} + +void dt_add_dyn_devs(tree *conf, dt_host *host, int vga_ram_size) +{ + int i, max_bus, busno; + + /* VGA */ + if (cirrus_vga_enabled || vmsvga_enabled || std_vga_enabled) { + dt_add_vga(conf, host, + 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, host, i); + } + + /* NICs */ + for(i = 0; i < nb_nics; i++) + dt_add_nic(conf, host, &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, host, 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, host, busno++); + } + + /* Drives */ + for (i = 0; i < nb_drives; i++) + dt_add_drive(conf, host, &drives_table[i]); +} + +void dt_add_memory(tree *conf, dt_host *host, + target_phys_addr_t addr, ram_addr_t sz, int ro) +{ + tree *node = tree_new_child(NULL, "memory", NULL); + + tree_put_propf(node, "address", "0x" TARGET_FMT_plx, addr); + tree_put_propf(node, "size", "%#lx", (unsigned long)sz); + if (ro) + tree_put_prop(node, "read-only", NULL, 0); + dt_add_dyn_dev(conf, host, node, 0); +} + + +/* Create a configuration */ + +#if 0 /* TODO implement */ +tree *dt_read_config(const char *name) +{ +} +#endif + +static void +dt_int_tree_print(dt_device *dev, int indent) +{ + const char *name = tree_node_name(dev->conf); + dt_device *child; + + printf("%*s%s {\n", indent, "", *name ? name : "/"); + TAILQ_FOREACH(child, &dev->int_children, int_siblings) + dt_int_tree_print(child, indent + 4); + printf("%*s};\n", indent, ""); +} + +dt_device *dt_create_machine(tree *conf, dt_host *host, const char *cpu_model) +{ + tree *node; + + 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); + + return dt_create(conf, host); +} + +void dt_config_machine(dt_device *root, dt_host *host) +{ + dt_config(root, host); + + dt_print_host_config(host); + tree_print(root->conf); + dt_int_tree_print(root, 0); + + dt_fdt_test(root->conf); +} diff --git a/dt.h b/dt.h new file mode 100644 index 0000000..003d8d1 --- /dev/null +++ b/dt.h @@ -0,0 +1,150 @@ +#ifndef DT_H +#define DT_H + +#include "sysemu.h" +#include "net.h" +#include "tree.h" + +typedef struct dt_host dt_host; +typedef struct dt_device dt_device; +typedef struct dt_driver dt_driver; +typedef struct dt_prop_spec dt_prop_spec; +typedef struct dt_bus_options dt_bus_options; + + +/* Host Configuration */ + +typedef void (*dt_mem_loader)(target_phys_addr_t, ram_addr_t, const void *); + +dt_host *dt_create_host(const dt_driver *drvtab); +const dt_driver *dt_driver_by_name(dt_host *host, const char *name); +void dt_attach_nic(dt_host *host, tree *nic, VLANState *vlan); +VLANState *dt_find_vlan(tree *conf, dt_host *host); +void dt_attach_drive(dt_host *host, tree *node, BlockDriverState *state); +void dt_find_drives(tree *conf, dt_host *host, + BlockDriverState *drive[], int n); +void dt_config_mem(dt_host *host, dt_mem_loader loader, + target_phys_addr_t addr, ram_addr_t size, const void *); +void dt_image_loader(target_phys_addr_t addr, ram_addr_t size, const void *); +int dt_alloc_virtio_bus(dt_host *host); +void dt_host_init(dt_host *host); +void dt_print_host_config(dt_host *host); + + +/* Device Interface */ + +/* + * Device life cycle: + * + * 1. Configuration: config() method runs after parent's. It should + * initialize the device's private data from its configuration + * sub-tree. It may edit the configuration sub-tree. Private data is + * zeroed before config() runs. + * + * 2. Initialization: int_init() method runs after interrupt parent's. + * Then init() method runs after parent's. Neither should touch the + * configuration tree. + * + * 3. Start: start() method runs, order is unspecified. + * + * Error handling in these driver methods: print to stderr and exit + * the program unsuccessfully. + * + * There is no device shutdown protocol yet. + */ + +struct dt_device { + tree *conf; /* configuration sub-tree */ + const dt_driver *drv; /* device driver */ + tree *int_parent; /* interrupt parent if != tree_parent(conf) */ + TAILQ_HEAD(, dt_device) int_children; + TAILQ_ENTRY(dt_device) int_siblings; + void *priv; /* device private data */ +}; + +typedef enum dt_bus_type { + DT_BUS_NONE, DT_BUS_ROOT, DT_BUS_PCI, DT_BUS_ISA, DT_BUS_IDE, + DT_BUS_SCSI, DT_BUS_FLOPPY, DT_BUS_VIRTIO, DT_BUS_NUM +} dt_bus_type; + +struct dt_driver { + const char *name; + size_t privsz; /* size of device private data */ + const dt_prop_spec *prop_spec; /* recognized conf node properties */ + dt_bus_type bus_type, parent_bus_type; + /* live cycle methods */ + void (*config)(dt_device *, dt_host *); + void (*int_init)(dt_device *); + void (*init)(dt_device *); + void (*start)(dt_device *); + /* def'd iff device is a PCI bus, may return NULL until after init() */ + PCIBus *(*get_pcibus)(dt_device *); + /* optional, always available */ + int (*get_unit)(dt_device *); + /* def'd iff is an int. ctrlr., may return NULL until after int_init() */ + qemu_irq *(*get_int)(dt_device *, int *); +}; + +typedef struct dt_pcidevice { + int devfn; +} dt_pcidevice; + +dt_device *dt_device_of(tree *conf); +dt_device *dt_parent_device(dt_device *dev); +dt_device *dt_root(dt_device *dev); +dt_device *dt_find_bus(tree *conf, dt_bus_type bus_type, int busno); +PCIBus *dt_get_pcibus(dt_device *dev); +int dt_get_unit(dt_device *dev); +qemu_irq *dt_get_int(dt_device *dev, int n); + +tree *dt_read_config(const char *name); +dt_device *dt_create_machine(tree *conf, dt_host *host, const char *cpu_model); +void dt_config_machine(dt_device *root, dt_host *host); +void dt_add_dyn_devs(tree *conf, dt_host *host, int vga_ram_size); +void dt_add_memory(tree *conf, dt_host *host, + target_phys_addr_t addr, ram_addr_t sz, int ro); +void dt_init_machine(dt_device *root, dt_host *host); + + +/* Device properties */ + +/* + * This is for parsing configuration tree node properties into device + * private data. + */ + +struct dt_prop_spec { + const char *name; + ptrdiff_t offs; /* offset in device private data */ + size_t size; /* size there, for sanity checking */ + int (*parse)(void *, const char *, const dt_prop_spec *); +}; + +#define DT_PROP_SPEC_INIT(name, strty, member, fmt) \ + { name, offsetof(strty, member), sizeof(((strty *)0)->member), \ + dt_parse_##fmt } +#define DT_PROP_SPEC_SENTINEL() { NULL, 0, 0, NULL } + +/* Canned property parse methods */ +#define dt_parse_void NULL +int dt_parse_string(void *dst, const char *src, const dt_prop_spec *spec); +int dt_parse_int(void *dst, const char *src, const dt_prop_spec *spec); +int dt_parse_ram_addr_t(void *dst, const char *src, const dt_prop_spec *spec); +int dt_parse_target_phys_addr_t(void *dst, const char *src, + const dt_prop_spec *spec); +int dt_parse_macaddr(void *dst, const char *src, const dt_prop_spec *spec); +int dt_parse_pci_unitaddr(void *dst, const char *src, const dt_prop_spec *spec); + +/* Bus-specific device properties */ +extern const dt_prop_spec dt_bus_prop_spec_pci[]; + + +/* Interfacing with FDT */ + +#ifdef HAVE_FDT +void dt_fdt_test(tree *conf); +#else +static inline void dt_fdt_test(tree *conf) { } +#endif + +#endif diff --git a/hw/boards.h b/hw/boards.h index 5611d2c..8f56b5f 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -32,6 +32,9 @@ extern QEMUMachine axisdev88_machine; extern QEMUMachine pc_machine; extern QEMUMachine isapc_machine; +/* pcdt.c */ +extern QEMUMachine pcdt_machine; + /* xen_machine.c */ extern QEMUMachine xenpv_machine; diff --git a/hw/pc.c b/hw/pc.c index a9a39a9..f591919 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -38,42 +38,35 @@ #include "virtio-console.h" #include "hpet_emul.h" #include "smbios.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 FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1) -#define MAX_IDE_BUS 2 - static RTCState *rtc_state; static PITState *pit; static IOAPICState *ioapic; -static PCIDevice *i440fx_state; +PCIDevice *i440fx_state; -static void ioport80_write(void *opaque, uint32_t addr, uint32_t data) +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); } @@ -122,7 +115,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; @@ -204,7 +197,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 @@ -231,10 +224,10 @@ static int pc_boot_set(void *opaque, const char *boot_device) } /* hd_table must contain 4 block drivers */ -static void cmos_init(RTCState *s, - ram_addr_t ram_size, ram_addr_t above_4g_mem_size, - const char *boot_device, BlockDriverState **hd_table, - int fd0, int fd1) +void cmos_init(RTCState *s, + ram_addr_t ram_size, ram_addr_t above_4g_mem_size, + const char *boot_device, BlockDriverState **hd_table, + int fd0, int fd1) { int nbds, bds[3] = { 0, }; int val; @@ -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; } @@ -424,7 +417,7 @@ static void bochs_bios_write(void *opaque, uint32_t addr, uint32_t val) extern uint64_t node_cpumask[MAX_NODES]; -static void bochs_bios_init(void) +void bochs_bios_init(void) { void *fw_cfg; uint8_t *smbios_table; @@ -568,10 +561,10 @@ static long get_file_size(FILE *f) return size; } -static void load_linux(target_phys_addr_t option_rom, - const char *kernel_filename, - const char *initrd_filename, - const char *kernel_cmdline) +void load_linux(target_phys_addr_t option_rom, + const char *kernel_filename, + const char *initrd_filename, + const char *kernel_cmdline) { uint16_t protocol; uint32_t gpr[8]; @@ -722,7 +715,7 @@ static void load_linux(target_phys_addr_t option_rom, generate_bootsect(option_rom, gpr, seg, 0); } -static void main_cpu_reset(void *opaque) +void main_cpu_reset(void *opaque) { CPUState *env = opaque; cpu_reset(env); @@ -737,11 +730,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..a2d0df1 --- /dev/null +++ b/hw/pcdt.c @@ -0,0 +1,941 @@ +/* + * 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 "virtio-blk.h" +#include "virtio-balloon.h" +#include "virtio-console.h" +#include "hpet_emul.h" +#include "pcint.h" +#include "dt.h" + + +/* CPUs Driver */ + +typedef struct dt_device_cpus { + const char *model; + int num; +} dt_device_cpus; + +static const dt_prop_spec dt_cpus_props[] = { + DT_PROP_SPEC_INIT("model", dt_device_cpus, model, string), + DT_PROP_SPEC_INIT("num", dt_device_cpus, num, int), + DT_PROP_SPEC_SENTINEL() +}; + +static void dt_cpus_init(dt_device *dev) +{ + dt_device_cpus *priv = dev->priv; + int i; + CPUState *env; + + for(i = 0; i < priv->num; i++) { + env = cpu_init(priv->model); + if (!env) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + if (i != 0) + env->halted = 1; + qemu_register_reset(main_cpu_reset, env); + if (1 /* TODO priv->apic */) { + env->cpuid_features |= CPUID_APIC; + apic_init(env); + } + } +} + + +/* Memory Driver */ + +typedef struct dt_device_memory { + target_phys_addr_t addr; + ram_addr_t size; + int read_only; +} dt_device_memory; + +static const dt_prop_spec dt_memory_props[] = { + DT_PROP_SPEC_INIT("address", dt_device_memory, addr, target_phys_addr_t), + DT_PROP_SPEC_INIT("size", dt_device_memory, size, ram_addr_t), + DT_PROP_SPEC_INIT("read-only", dt_device_memory, read_only, void), + DT_PROP_SPEC_SENTINEL() +}; + +static void dt_memory_init(dt_device *dev) +{ + dt_device_memory *priv = dev->priv; + ram_addr_t host_offs; + + host_offs = qemu_ram_alloc(priv->size); + cpu_register_physical_memory(priv->addr, priv->size, + host_offs | (priv->read_only ? IO_MEM_ROM : 0)); + + printf("memory " TARGET_FMT_plx " %8lx %8lx %s\n", + priv->addr, (unsigned long)priv->size, (unsigned long)host_offs, + priv->read_only ? "ro" : "rw"); +} + + +/* i8254 PIT Driver */ + +/* + * TODO Factor out the parts that are not PC-specific, and move them + * out, since there are other architectures that use it. + * + * TODO HPET should be separate from PIT. + */ + +typedef struct dt_device_i8254 { + int hpet; +} dt_device_i8254; + +static void dt_i8254_config(dt_device *dev, dt_host *host) +{ + dt_device_i8254 *priv = dev->priv; + + priv->hpet = 1; +} + +static void dt_i8254_init(dt_device *dev) +{ + PITState *pit; + qemu_irq *gsi; + dt_device_i8254 *priv = dev->priv; + + gsi = dt_get_int(dev, 16); + + pit = pit_init(0x40, gsi[0]); + pcspk_init(pit); + if (priv->hpet) + hpet_init(gsi); +} + + +/* PC APIC Driver */ + +/* + * This driver takes care of I/O APICs and the pair of i8259 PICs. + * Bunching them together that way keeps the interrupt graph a tree. + * The result is an interrupt controller for GSIs. + */ + +typedef struct dt_device_apic { + qemu_irq *gsi; +} dt_device_apic; + +static void dt_apic_int_init(dt_device *dev) +{ + dt_device_apic *priv = dev->priv; + qemu_irq *cpu_irq; + IOAPICState *ioapic; + + cpu_irq = qemu_allocate_irqs(pic_irq_request, NULL, 1); + priv->gsi = i8259_init(cpu_irq[0]); + ferr_irq = priv->gsi[13]; + if (1 /* TODO priv->ioapic */) { + ioapic = ioapic_init(); + pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic); + } +} + +static qemu_irq *dt_apic_get_int(dt_device *dev, int *nirq) +{ + dt_device_apic *priv = dev->priv; + + if (nirq) + *nirq = 16; + return priv->gsi; +} + + +/* MC146818 RTC Driver */ + +typedef struct dt_device_MC146818 { + const char *boot_device; + ram_addr_t ram_below_4g, ram_above_4g; + /* TODO store cylinders, heads, sectors, translation instead of bds[] */ + BlockDriverState *ide_bds[MAX_IDE_BUS * MAX_IDE_DEVS]; + int fd0, fd1; +} dt_device_MC146818; + +static const dt_prop_spec dt_MC146818_props[] = { + DT_PROP_SPEC_INIT("boot-device", dt_device_MC146818, boot_device, + string), + DT_PROP_SPEC_INIT("ram-below-4g", dt_device_MC146818, ram_below_4g, + ram_addr_t), + DT_PROP_SPEC_INIT("ram-above-4g", dt_device_MC146818, ram_above_4g, + ram_addr_t), + DT_PROP_SPEC_SENTINEL() +}; + +static void dt_MC146818_config(dt_device *dev, dt_host *host) +{ + dt_device_MC146818 *priv = dev->priv; + BlockDriverState *fd_bds[MAX_FD]; + + dt_find_drives(dt_find_bus(dt_root(dev)->conf, DT_BUS_IDE, 0)->conf, + host, priv->ide_bds, ARRAY_SIZE(priv->ide_bds)); + dt_find_drives(dt_find_bus(dt_root(dev)->conf, DT_BUS_FLOPPY, 0)->conf, + host, fd_bds, ARRAY_SIZE(fd_bds)); + priv->fd0 = fdctrl_get_drive_type(fd_bds[0]); + priv->fd1 = fdctrl_get_drive_type(fd_bds[1]); +} + +static void dt_MC146818_init(dt_device *dev) +{ + dt_device_MC146818 *priv = dev->priv; + qemu_irq *gsi = dt_get_int(dev, 16); + RTCState *rtc_state; + + rtc_state = rtc_init(0x70, gsi[8], 2000); + qemu_register_boot_set(pc_boot_set, rtc_state); + cmos_init(rtc_state, + priv->ram_below_4g, priv->ram_above_4g, priv->boot_device, + priv->ide_bds, priv->fd0, priv->fd1); +} + + +/* 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 { + BlockDriverState *bds[MAX_FD]; +} dt_device_pc_misc; + +static void dt_pc_misc_config(dt_device *dev, dt_host *host) +{ + dt_device_pc_misc *priv = dev->priv; + + 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; + qemu_irq *gsi; + int i; + + gsi = dt_get_int(dev, 16); + + vmport_init(); + + bochs_bios_init(); + + register_ioport_write(0x80, 1, 1, ioport80_write, NULL); + register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL); + + register_ioport_read(0x92, 1, 1, ioport92_read, NULL); + register_ioport_write(0x92, 1, 1, ioport92_write, NULL); + + for(i = 0; i < MAX_SERIAL_PORTS; i++) { + if (serial_hds[i]) { + serial_init(serial_io[i], gsi[serial_irq[i]], 115200, + serial_hds[i]); + } + } + + for(i = 0; i < MAX_PARALLEL_PORTS; i++) { + if (parallel_hds[i]) { + parallel_init(parallel_io[i], gsi[parallel_irq[i]], + parallel_hds[i]); + } + } + + qemu_system_hot_add_init(); + + i8042_init(gsi[1], gsi[12], 0x60); + DMA_init(0); + + fdctrl_init(gsi[6], 2, 0, 0x3f0, priv->bds); +} + + +/* PCI Bus Driver */ + +typedef struct dt_device_pci { + PCIBus *pcibus; +} dt_device_pci; + +static void dt_pci_init(dt_device *dev) +{ + dt_device_pci *priv = dev->priv; + + priv->pcibus = i440fx_init(&i440fx_state, dt_get_int(dev, 16)); +} + +static void dt_pci_start(dt_device *dev) +{ + i440fx_init_memory_mappings(i440fx_state); +} + +static PCIBus *dt_pci_get_pcibus(dt_device *dev) +{ + return ((dt_device_pci *)dev->priv)->pcibus; +} + + +/* PIIX3 IDE Driver */ + +typedef struct dt_device_piix3_ide { + dt_pcidevice pci_dev; + BlockDriverState *bds[MAX_IDE_BUS * MAX_IDE_DEVS]; +} dt_device_piix3_ide; + +static void dt_piix3_ide_config(dt_device *dev, dt_host *host) +{ + dt_device_piix3_ide *priv = dev->priv; + + dt_find_drives(dev->conf, host, priv->bds, ARRAY_SIZE(priv->bds)); +} + +static void dt_piix3_ide_init(dt_device *dev) +{ + dt_device_piix3_ide *priv = dev->priv; + PCIBus *pci_bus = dt_get_pcibus(dev); + qemu_irq *gsi = dt_get_int(dev, 16); + + pci_piix3_ide_init(pci_bus, priv->bds, priv->pci_dev.devfn, gsi); +} + + +/* PIIX3 USB Driver */ + +typedef struct dt_device_piix3_usb { + dt_pcidevice pci_dev; +} dt_device_piix3_usb; + +static void dt_piix3_usb_init(dt_device *dev) +{ + dt_device_piix3_usb *priv = dev->priv; + PCIBus *pci_bus = dt_get_pcibus(dev); + + usb_uhci_piix3_init(pci_bus, priv->pci_dev.devfn); +} + + +/* PIIX3 ACPI Driver */ + +typedef struct dt_device_piix3_acpi { + dt_pcidevice pci_dev; +} dt_device_piix3_acpi; + +static void dt_piix3_acpi_init(dt_device *dev) +{ + dt_device_piix3_acpi *priv = dev->priv; + int i; + uint8_t *eeprom_buf; + i2c_bus *smbus; + PCIBus *pci_bus = dt_get_pcibus(dev); + qemu_irq *gsi = dt_get_int(dev, 16); + + eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */ + + /* TODO: Populate SPD eeprom data. */ + smbus = piix4_pm_init(pci_bus, priv->pci_dev.devfn, 0xb100, gsi[9]); + for (i = 0; i < 8; i++) + smbus_eeprom_device_init(smbus, 0x50 + i, eeprom_buf + (i * 256)); +} + + +/* PIIX3 Driver */ + +typedef struct dt_device_piix3 { + dt_pcidevice pci_dev; +} dt_device_piix3; + +static void dt_piix3_init(dt_device *dev) +{ + dt_device_piix3 *priv = dev->priv; + PCIBus *pci_bus = dt_get_pcibus(dev); + + piix3_init(pci_bus, priv->pci_dev.devfn); +} + +static qemu_irq *dt_piix3_get_int(dt_device *dev, int *nirq) +{ + if (nirq) + *nirq = 16; + return dt_get_int(dev, 16); +} + + +/* VGA Driver */ + +typedef struct dt_driver_vga { + const char *model; + void (*init)(PCIBus *, int); +} dt_driver_vga; + +static void pci_vga_init_(PCIBus *bus, int vga_ram_size) +{ + pci_vga_init(bus, vga_ram_size, 0, 0); +} + +static dt_driver_vga dt_driver_vga_table[] = { + { "cirrus", pci_cirrus_vga_init }, + { "vms", pci_vmsvga_init }, + { "std", pci_vga_init_ }, + { NULL, NULL } +}; + +typedef struct dt_device_vga { + dt_pcidevice pci_dev; /* TODO unused */ + const char *model; + ram_addr_t ram_size; + dt_driver_vga *vga_drv; +} dt_device_vga; + +static const dt_prop_spec dt_vga_props[] = { + DT_PROP_SPEC_INIT("model", dt_device_vga, model, string), + DT_PROP_SPEC_INIT("ram", dt_device_vga, ram_size, ram_addr_t), + DT_PROP_SPEC_SENTINEL() +}; + +static void dt_vga_config(dt_device *dev, dt_host *host) +{ + dt_device_vga *priv = dev->priv; + int i; + + for (i = 0; dt_driver_vga_table[i].model; i++) { + if (!strcmp(dt_driver_vga_table[i].model, priv->model)) + break; + } + if (!dt_driver_vga_table[i].model) { + fprintf(stderr, "Unknown VGA model %s\n", priv->model); + exit(1); + } + priv->vga_drv = &dt_driver_vga_table[i]; +} + +static void dt_vga_init(dt_device *dev) +{ + dt_device_vga *priv = dev->priv; + + priv->vga_drv->init(dt_get_pcibus(dev), priv->ram_size); +} + + +/* NIC Driver */ + +typedef struct dt_device_nic { + dt_pcidevice pci_dev; /* TODO unused */ + NICInfo nd; +} dt_device_nic; + +static const dt_prop_spec dt_nic_props[] = { + DT_PROP_SPEC_INIT("model", dt_device_nic, nd.model, string), + DT_PROP_SPEC_INIT("mac", dt_device_nic, nd.macaddr, macaddr), + DT_PROP_SPEC_INIT("name", dt_device_nic, nd.name, string), + DT_PROP_SPEC_SENTINEL() +}; + +static void dt_nic_config(dt_device *dev, dt_host *host) +{ + dt_device_nic *priv = dev->priv; + + priv->nd.vlan = dt_find_vlan(dev->conf, host); +} + +static void dt_nic_init(dt_device *dev) +{ + dt_device_nic *priv = dev->priv; + + pci_nic_init(dt_get_pcibus(dev), &priv->nd, -1, NULL); +} + + +/* SCSI Driver */ + +typedef struct dt_device_scsi { + dt_pcidevice pci_dev; /* TODO unused */ + void *opaque; + BlockDriverState *bds[LSI_MAX_DEVS]; +} dt_device_scsi; + +static void dt_scsi_config(dt_device *dev, dt_host *host) +{ + dt_device_scsi *priv = dev->priv; + + dt_find_drives(dev->conf, host, priv->bds, ARRAY_SIZE(priv->bds)); +} + +static void dt_scsi_init(dt_device *dev) +{ + dt_device_scsi *priv = dev->priv; + int i; + + priv->opaque = lsi_scsi_init(dt_get_pcibus(dev), -1); + + for (i = 0; i < ARRAY_SIZE(priv->bds); i++) { + if (priv->bds[i]) + lsi_scsi_attach(priv->opaque, priv->bds[i], i); + } +} + + +/* Virtio Block Driver */ + +typedef struct dt_device_virtio_block { + dt_pcidevice pci_dev; /* TODO unused */ + 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 */ + +typedef struct dt_device_virtio_balloon { + dt_pcidevice pci_dev; /* TODO unused */ +} dt_device_virtio_balloon; + +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 { + dt_pcidevice pci_dev; /* TODO unused */ + int index; + CharDriverState *hds; +} dt_device_virtio_console; + +static const dt_prop_spec dt_virtio_console_props[] = { + DT_PROP_SPEC_INIT("index", dt_device_virtio_console, index, int), + DT_PROP_SPEC_SENTINEL() +}; + +static void dt_virtio_console_config(dt_device *dev, dt_host *host) +{ + dt_device_virtio_console *priv = dev->priv; + + priv->hds = virtcon_hds[priv->index]; +} + +static void dt_virtio_console_init(dt_device *dev) +{ + dt_device_virtio_console *priv = dev->priv; + + virtio_console_init(dt_get_pcibus(dev), priv->hds); +} + + +/* Drive Driver */ + +typedef struct dt_device_drive { + int unit; +} dt_device_drive; + +static const dt_prop_spec dt_drive_props[] = { + DT_PROP_SPEC_INIT("unit", dt_device_drive, unit, int), + DT_PROP_SPEC_SENTINEL() +}; + +static int dt_drive_get_unit(dt_device *dev) +{ + return ((dt_device_drive *)dev->priv)->unit; +} + + +/* Machine Driver */ + +static const dt_driver dt_driver_table[] = { + { "", 0, NULL, DT_BUS_ROOT, DT_BUS_NONE, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL }, + { "cpus", sizeof(dt_device_cpus), dt_cpus_props, + DT_BUS_NONE, DT_BUS_ROOT, + NULL, NULL, dt_cpus_init, NULL, + NULL, NULL, NULL }, + { "memory", sizeof(dt_device_memory), dt_memory_props, + DT_BUS_NONE, DT_BUS_ROOT, + NULL, NULL, dt_memory_init, NULL, + NULL, NULL, NULL }, + { "apic", sizeof(dt_device_apic), NULL, + DT_BUS_NONE, DT_BUS_ROOT, + NULL, dt_apic_int_init, NULL, NULL, + NULL, NULL, dt_apic_get_int }, + { "i8254", sizeof(dt_device_i8254), NULL, + DT_BUS_NONE, DT_BUS_ISA, + dt_i8254_config, NULL, dt_i8254_init, NULL, + NULL, NULL, NULL }, + { "MC146818", sizeof(dt_device_MC146818), dt_MC146818_props, + DT_BUS_NONE, DT_BUS_ISA, + dt_MC146818_config, NULL, dt_MC146818_init, NULL, + NULL, NULL, NULL }, + { "pc-misc", sizeof(dt_device_pc_misc), NULL, + DT_BUS_FLOPPY, DT_BUS_ROOT, + dt_pc_misc_config, NULL, dt_pc_misc_init, NULL, + NULL, NULL, NULL }, + { "pci", sizeof(dt_device_pci), NULL, + DT_BUS_PCI, DT_BUS_ROOT, + NULL, NULL, dt_pci_init, dt_pci_start, + dt_pci_get_pcibus, NULL, NULL }, + { "piix3-isa-bridge", sizeof(dt_device_piix3), NULL, + DT_BUS_ISA, DT_BUS_PCI, + NULL, NULL, dt_piix3_init, NULL, + NULL, NULL, dt_piix3_get_int }, + { "piix3-ide", sizeof(dt_device_piix3_ide), NULL, + DT_BUS_IDE, DT_BUS_PCI, + dt_piix3_ide_config, NULL, dt_piix3_ide_init, NULL, + NULL, NULL, NULL }, + { "piix3-usb", sizeof(dt_device_piix3_usb), NULL, + DT_BUS_NONE, DT_BUS_PCI, /* FIXME: DT_BUS_NONE -> BUS_USB ?*/ + NULL, NULL, dt_piix3_usb_init, NULL, + NULL, NULL, NULL }, + { "piix3-acpi", sizeof(dt_device_piix3_acpi), NULL, + DT_BUS_NONE, DT_BUS_PCI, + NULL, NULL, dt_piix3_acpi_init, NULL, + NULL, NULL, NULL }, + { "vga", sizeof(dt_device_vga), dt_vga_props, + DT_BUS_NONE, DT_BUS_PCI, + dt_vga_config, NULL, dt_vga_init, NULL, + NULL, NULL, NULL }, + { "nic", sizeof(dt_device_nic), dt_nic_props, + DT_BUS_NONE, DT_BUS_PCI, + dt_nic_config, NULL, dt_nic_init, NULL, + NULL, NULL, NULL }, + { "scsi", sizeof(dt_device_scsi), NULL, + DT_BUS_SCSI, DT_BUS_PCI, + dt_scsi_config, NULL, dt_scsi_init, NULL, + NULL, NULL, NULL }, + { "virtio-block", sizeof(dt_device_virtio_block), NULL, + DT_BUS_VIRTIO, DT_BUS_PCI, + dt_virtio_block_config, NULL, dt_virtio_block_init, NULL, + NULL, NULL, NULL }, + { "virtio-balloon", sizeof(dt_device_virtio_balloon), NULL, + DT_BUS_NONE, DT_BUS_PCI, + NULL, NULL, dt_virtio_balloon_init, NULL, + NULL, NULL, NULL }, + { "virtio-console", sizeof(dt_device_virtio_console), dt_virtio_console_props, + DT_BUS_NONE, DT_BUS_PCI, + dt_virtio_console_config, NULL, dt_virtio_console_init, NULL, + NULL, NULL, NULL }, + { "ide-drive", sizeof(dt_device_drive), dt_drive_props, + DT_BUS_NONE, DT_BUS_IDE, + NULL, NULL, NULL, NULL, + NULL, dt_drive_get_unit, NULL }, + { "scsi-drive", sizeof(dt_device_drive), dt_drive_props, + DT_BUS_NONE, DT_BUS_SCSI, + NULL, NULL, NULL, NULL, + NULL, dt_drive_get_unit, NULL }, + { "floppy-drive", sizeof(dt_device_drive), dt_drive_props, + DT_BUS_NONE, DT_BUS_FLOPPY, + NULL, NULL, NULL, NULL, + NULL, dt_drive_get_unit, NULL }, + { "virtio-drive", sizeof(dt_device_drive), dt_drive_props, + DT_BUS_NONE, DT_BUS_VIRTIO, + NULL, NULL, NULL, NULL, + NULL, dt_drive_get_unit, NULL }, + { NULL, 0, NULL, DT_BUS_NONE, DT_BUS_NONE, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL } +}; + +static tree *dt_hardcoded_config(const char *name) +{ +#ifdef TARGET_X86_64 +#define CPU_MODEL_DEFAULT "qemu64" +#else +#define CPU_MODEL_DEFAULT "qemu32" +#endif + tree *root, *pci, *isa, *leaf; + + /* + * TODO Read from config file. + * + * TODO Pretty far from a comprehensive machine configuration, but + * we need to start somewhere. + */ + if (strcmp(name, "pcdt")) { + fprintf(stderr, "qemu: machine %s not implemented", name); + exit(1); + } + root = tree_new_child(NULL, "", NULL); + + leaf = tree_new_child(root, "cpus", NULL); + tree_put_propf(leaf, "model", "%s", CPU_MODEL_DEFAULT); + + leaf = tree_new_child(root, "apic", NULL); + + leaf = tree_new_child(root, "pc-misc", NULL); + tree_put_propf(leaf, "interrupt-parent", "/apic"); + + pci = tree_new_child(root, "pci", NULL); + tree_put_propf(pci, "interrupt-parent", "/apic"); + + isa = tree_new_child(pci, "piix3-isa-bridge", NULL); + tree_put_propf(isa, "interrupt-parent", "/apic"); + tree_put_propf(isa, "unit-address", "1.0"); + + leaf = tree_new_child(isa, "i8254", NULL); + + leaf = tree_new_child(isa, "MC146818", NULL); + + leaf = tree_new_child(pci, "piix3-ide", NULL); + tree_put_propf(leaf, "interrupt-parent", "/apic"); + tree_put_propf(leaf, "unit-address", "1.1"); + + if (usb_enabled) { + leaf = tree_new_child(pci, "piix3-usb", NULL); + tree_put_propf(leaf, "unit-address", "1.2"); + } + + if (acpi_enabled) { + leaf = tree_new_child(pci, "piix3-acpi", NULL); + tree_put_propf(leaf, "interrupt-parent", "/apic"); + tree_put_propf(leaf, "unit-address", "1.3"); + } + + leaf = tree_new_child(pci, "virtio-balloon", NULL); + return root; +#undef CPU_MODEL_DEFAULT +} + +#define dt_read_config(name) dt_hardcoded_config((name)) + +static void dt_add_ram(tree *conf, dt_host *host, ram_addr_t ram_size) +{ + ram_addr_t left, sz; + + left = ram_size; + sz = MIN(left, 0xa0000); + dt_add_memory(conf, host, 0, sz, 0); + left -= sz; + + sz = MIN(left, 0x60000); + left -= sz; + + if (left) { + sz = MIN(left, 0xe0000000 - 0x100000); + dt_add_memory(conf, host, 0x100000, sz, 0); + left -= sz; + } + +#if TARGET_PHYS_ADDR_BITS > 32 + if (left) + dt_add_memory(conf, host, 0x100000000ull, left, 0); +#endif +} + +static char *bios_image_fname(const char *image) +{ + char *fname = qemu_malloc(strlen(bios_dir) + 1 + strlen(image) + 1); + sprintf(fname, "%s/%s", bios_dir, image); + return fname; +} + +static void dt_add_bios(tree *conf, dt_host *host) +{ + char *fname = bios_image_fname(bios_name ? bios_name : BIOS_FILENAME); + int size; + target_phys_addr_t addr; + + size = get_image_size(fname); + if (size <= 0 || size % 0x10000 != 0 || size > 0x20000) { + /* FIXME implement size > 0x20000 */ + fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", fname); + exit(1); + } + + addr = 0xffffffff - (size - 1); + dt_add_memory(conf, host, addr, size, 1); + dt_config_mem(host, dt_image_loader, addr, size, fname); + + /* TODO map the same memory again */ + addr = 0x100000 - size; + dt_add_memory(conf, host, addr, size, 1); + dt_config_mem(host, dt_image_loader, addr, size, fname); +} + +static int dt_config_oprom(dt_host *host, dt_mem_loader loader, + int offs, int size, const void *arg, + const char *what) +{ + offs = (offs + 2047) & ~2047; + + if (offs + size > 0x20000) { + if (what) + fprintf(stderr, "Not enough space for %s\n", what); + else + fprintf(stderr, "Not enough space for option rom '%s'\n", + (char *)arg); + exit(1); + } + + dt_config_mem(host, loader, 0xc0000 + offs, size, arg); + return offs + size; +} + +static int dt_config_oprom_image(dt_host *host, int offs, const char *fname) +{ + int size; + + size = get_image_size(fname); + if (size < 0) { + fprintf(stderr, "Could not load option rom '%s'\n", fname); + exit(1); + } + + return dt_config_oprom(host, dt_image_loader, offs, size, fname, fname); +} + +typedef struct dt_linux_loader_arg { + const char *kernel_filename; + const char *kernel_cmdline; + const char *initrd_filename; +} dt_linux_loader_arg; + +static void dt_linux_loader(target_phys_addr_t addr, ram_addr_t size, + const void *arg) +{ + const dt_linux_loader_arg *llarg = arg; + + load_linux(addr, llarg->kernel_filename, llarg->initrd_filename, + llarg->kernel_cmdline); +} + +static void dt_add_oprom(tree *conf, dt_host *host, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename) +{ + int offs, i; + dt_linux_loader_arg *llarg; + + offs = 0; + + if (cirrus_vga_enabled || vmsvga_enabled || std_vga_enabled) { + offs = dt_config_oprom_image(host, offs, + bios_image_fname(cirrus_vga_enabled + ? VGABIOS_CIRRUS_FILENAME + : VGABIOS_FILENAME)); + /* + * Although video roms can grow larger than 0x8000, the first + * 0x8000 bytes are reserved for them. It means we won't be + * looking for any other kind of option rom inside this area. + */ + offs = MAX(offs, 0x8000); + } + + if (kernel_filename) { + llarg = qemu_malloc(sizeof(*llarg)); + llarg->kernel_filename = kernel_filename; + llarg->kernel_cmdline = kernel_cmdline; + llarg->initrd_filename = initrd_filename; + offs = dt_config_oprom(host, dt_linux_loader, offs, 2048, llarg, + "kernel loader"); + } + + for (i = 0; i < nb_option_roms; i++) + offs = dt_config_oprom_image(host, offs, option_rom[i]); + + dt_add_memory(conf, host, 0xc0000, offs, 1); +} + +static void pcdt_init(ram_addr_t ram_size, int vga_ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + tree *conf; + tree *node; + dt_host *host; + dt_device *root; + + conf = dt_read_config(pcdt_machine.name); + if (!conf) + exit(1); + + node = tree_node_by_name(conf, "/pci/piix3-isa-bridge/MC146818"); + tree_put_propf(node, "boot-device", "%s", boot_device); + assert(ram_size <= 0xe0000000); /* TODO implement */ + tree_put_propf(node, "ram-below-4g", "%#lx", (unsigned long)ram_size); + + host = dt_create_host(dt_driver_table); + root = dt_create_machine(conf, host, cpu_model); + + dt_add_ram(conf, host, ram_size); + dt_add_bios(conf, host); + dt_add_oprom(conf, host, kernel_filename, kernel_cmdline, initrd_filename); + dt_add_dyn_devs(conf, host, vga_ram_size); + dt_config_machine(root, host); + + dt_init_machine(root, host); +} + +QEMUMachine pcdt_machine = { + .name = "pcdt", + .desc = "Standard PC (device tree)", + .init = pcdt_init, + .max_cpus = 255, +}; diff --git a/hw/pcidt.c b/hw/pcidt.c new file mode 100644 index 0000000..db37311 --- /dev/null +++ b/hw/pcidt.c @@ -0,0 +1,67 @@ +/* + * 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. + */ + +/* PCI Bus */ + +#include +#include "hw.h" +#include "pci.h" +#include "dt.h" + +int dt_parse_pci_unitaddr(void *dst, const char *src, const dt_prop_spec *spec) +{ + int devfn, slot, func; + unsigned long val; + const char *p; + char *e; + + assert(spec->size == sizeof(int)); + + p = src; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + + slot = val; + + if (*e == '.') { + p = e + 1; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + } else + func = 0; + + func = val; + + devfn = PCI_DEVFN(slot, func); + *(int *)dst = devfn; + + return 0; +} + +const dt_prop_spec dt_bus_prop_spec_pci[] = { + /* + * FIXME assumes every PCI device has a dt_pcidevice at offset 0 + * in dev->priv + */ + DT_PROP_SPEC_INIT("unit-address", dt_pcidevice, devfn, pci_unitaddr), + DT_PROP_SPEC_SENTINEL() +}; diff --git a/hw/pcint.h b/hw/pcint.h new file mode 100644 index 0000000..acd645b --- /dev/null +++ b/hw/pcint.h @@ -0,0 +1,47 @@ +/* + * 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 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 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]; + +/* 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(RTCState *s, + ram_addr_t ram_size, ram_addr_t above_4g_mem_size, + const char *boot_device, BlockDriverState **hd_table, + int fd0, int fd1); +void load_linux(target_phys_addr_t option_rom, + const char *kernel_filename, + const char *initrd_filename, + const char *kernel_cmdline); + +#endif diff --git a/target-i386/machine.c b/target-i386/machine.c index 1b0d36d..6465f77 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); #ifdef CONFIG_XEN 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 -- 1.6.0.6