From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1La6AM-0004Ig-7p for qemu-devel@nongnu.org; Thu, 19 Feb 2009 05:30:02 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1La6AK-0004IU-O9 for qemu-devel@nongnu.org; Thu, 19 Feb 2009 05:30:01 -0500 Received: from [199.232.76.173] (port=52904 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1La6AK-0004IR-F6 for qemu-devel@nongnu.org; Thu, 19 Feb 2009 05:30:00 -0500 Received: from mx2.redhat.com ([66.187.237.31]:49953) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1La6AJ-00049B-Co for qemu-devel@nongnu.org; Thu, 19 Feb 2009 05:30:00 -0500 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 n1JATtdd024375 for ; Thu, 19 Feb 2009 05:29:56 -0500 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 n1JATt9u004617 for ; Thu, 19 Feb 2009 05:29:56 -0500 Received: from pike.pond.sub.org (vpn-10-63.str.redhat.com [10.32.10.63]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id n1JATqL1023384 for ; Thu, 19 Feb 2009 05:29:53 -0500 References: <87iqnh6kyv.fsf@pike.pond.sub.org> From: Markus Armbruster Date: Thu, 19 Feb 2009 11:29:52 +0100 In-Reply-To: <87iqnh6kyv.fsf@pike.pond.sub.org> (Markus Armbruster's message of "Wed\, 11 Feb 2009 16\:40\:08 +0100") Message-ID: <871vtuafdr.fsf@pike.pond.sub.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Subject: [Qemu-devel] Machine description as data prototype, take 3 (was: [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 Third iteration of the prototype. What about an early merge? If your answer to that is "yes, but", what exactly do you want changed? New: * Rebased to git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6626 c046a42c-6fe2-441c-8c8c-71466251a162 * Code duplication cleaned up. I chose minimizing the impact on pc.c over nice, clean interfaces. Happy to rework it if that was the wrong choice. I think there are a few opportunities for cleanup that would improve pc.c even without taking dt.c into consideration. I can work on patches if you like. * The "device required" edges moved from struct tree to struct dt_device to make the configuration tree more similar to FDTs structurally. * A bunch of pointless typedefs to hopefully blend in better stylistically. Tabs expanded. If style issues remain, please point them out to me! Shortcuts: * I didn't implement all the devices of the "pc" original. The devices I implemented might not support all existing command line options. * The initial configuration tree is hardcoded. It should be read from a configuration file. * Optional stuff is inserted into the initial configuration tree in hardcoded places. We should use suitable markers in the configuration file instead. * Linux gripes about ACPI, need to investigate. Notable qualities: * Linux still boots & shuts down cleanly (except for the ACPI gripes). * Machine and host configuration are cleanly separated. Machine configuration enumerates the components of the virtual machine, and how they are connected. It is a tree of devices nodes. Host configuration is about how the host implements virtual devices. Currently just a few flat tables. * Device drivers implement a common abstract interface. * Device drivers are cleanly separated from each other, and from the device-agnostic machine configuration and initialization code. * Each device driver specifies its configurable properties in a single place. Unknown properties are rejected. * A device driver gets its configuration from two sources: the device's node in the machine configuraton tree, and applicable host configuration tables. Makefile | 1 + Makefile.target | 4 + hw/dt.c | 1225 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/pc.c | 47 +-- hw/pcint.h | 46 ++ net.c | 2 +- net.h | 1 + target-i386/machine.c | 2 + tree.c | 298 ++++++++++++ tree.h | 40 ++ 10 files changed, 1638 insertions(+), 28 deletions(-) diff --git a/Makefile b/Makefile index 4f7a55a..2198bba 100644 --- a/Makefile +++ b/Makefile @@ -85,6 +85,7 @@ OBJS+=sd.o ssi-sd.o OBJS+=bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o OBJS+=buffered_file.o migration.o migration-tcp.o net.o qemu-sockets.o OBJS+=qemu-char.o aio.o net-checksum.o savevm.o cache-utils.o +OBJS+=tree.o ifdef CONFIG_BRLAPI OBJS+= baum.o diff --git a/Makefile.target b/Makefile.target index 9e7a1bb..ad254ad 100644 --- a/Makefile.target +++ b/Makefile.target @@ -583,8 +583,12 @@ OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o +OBJS+= dt.o OBJS += device-hotplug.o pci-hotplug.o CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE +ifdef FDT_LIBS +LIBS+= $(FDT_LIBS) +endif endif ifeq ($(TARGET_BASE_ARCH), ppc) CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE diff --git a/hw/dt.c b/hw/dt.c new file mode 100644 index 0000000..f57668b --- /dev/null +++ b/hw/dt.c @@ -0,0 +1,1225 @@ +/* + * QEMU PC System Emulator + * + * Copyright (C) 2009 Red Hat, Inc., Markus Armbruster + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Configure and build a machine from configuration data + * + * The idea is to have generic, device-independent code driven by + * device-dependent configuration data, talking to devices through an + * abstract device interface. + * + * For now, this lives in hw/, even though that's not where generic, + * device independent code belongs. This is just so we can minimize + * disruption by hiding completely behind the existing QEMUMachine + * abstraction. + * + * The configuration data currently is hardwired to a fairly limited + * PC, registered as machine type "pcdt". The nuts and bolts of PC + * emulation remain in pc.c, and that sharing makes the somewhat + * clumsy pcint.h necessary. 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 mechanism. + */ + +#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 "tree.h" + +#ifdef HAVE_FDT +#include +#endif + +/* Forward declarations */ +typedef struct dt_device dt_device; +typedef struct dt_tree_list dt_tree_list; +typedef struct dt_driver dt_driver; +typedef struct dt_prop_spec dt_prop_spec; +static void dt_parse_prop(dt_device *dev, tree_prop *prop); +static BlockDriverState **dt_piix3_hd(tree *piix3); + + +/* Host Configuration */ + +typedef struct dt_host { + /* connection NICs <-> VLAN */ + tree *nic[MAX_NICS]; + VLANState *nic_vlan[MAX_NICS]; + /* connection drives <-> controller */ + tree *drive_ctrl[MAX_DRIVES]; + BlockDriverState *drive_state[MAX_DRIVES]; +} dt_host; + +static void +dt_attach_nic(dt_host *host, int index, + tree *nic, VLANState *vlan) +{ + host->nic[index] = nic; + host->nic_vlan[index] = vlan; +} + +static VLANState * +dt_find_vlan(tree *conf, dt_host *host) +{ + int i; + + for (i = 0; i < MAX_NICS; i++) { + if (host->nic[i] == conf) + return host->nic_vlan[i]; + } + return NULL; +} + +static void +dt_attach_drive(dt_host *host, int index, + tree *controller, BlockDriverState *state) +{ + host->drive_ctrl[index] = controller; + host->drive_state[index] = state; +} + +static void +dt_drive_config(tree *conf, dt_host *host, + BlockDriverState *drive[], int n) +{ + int i, j; + + j = 0; + for (i = 0; i < MAX_DRIVES; i++) { + if (host->drive_ctrl[i] != conf) + continue; + assert(j < n); + drive[j++] = host->drive_state[i]; + } +} + +static void +dt_print_host_config(dt_host *host) +{ + char buf[1024]; + int i; + + for (i = 0; i < MAX_NICS; i++) { + if (!host->nic[i]) + continue; + tree_path(host->nic[i], buf, sizeof(buf)); + printf("nic#%d\tvlan %-4d\t%s\n", + i, host->nic_vlan[i]->id, buf); + } + + for (i = 0; i < MAX_DRIVES; i++) { + if (!host->drive_ctrl[i]) + continue; + tree_path(host->drive_ctrl[i], buf, sizeof(buf)); + printf("drive#%d\t%-15s %s\n", + i, bdrv_get_device_name(host->drive_state[i]), buf); + } +} + + +/* Device Interface */ + +/* + * Device life cycle: + * + * 1. Configuration: config() method runs after parent's. Except kids + * are skipped when the parent's config() returns non-zero. config() + * should initialize the device's private data from its configuration + * sub-tree. It may edit the configuration sub-tree, and may declare + * initialization ordering constraints with tree_require_named(). + * + * 2. Initialization: init() method runs after parent's and after that + * of devices declared required by config(). It should not touch the + * configuration tree. + * + * 3. Start: start() method runs, order is unspecified. + * + * Error handling in these driver methods: print to stderr and exit + * the program unsuccessfully. + * + * There is no device shutdown protocol yet. + */ + +struct dt_device { + tree *conf; /* configuration sub-tree */ + 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; +}; + +struct dt_driver { + const char *name; + size_t privsz; /* size of device private data */ + dt_prop_spec *prop_spec; /* recognized conf node properties */ + int (*config)(dt_device *, dt_host *); + void (*init)(dt_device *); + void (*start)(dt_device *); +}; + +static dt_driver dt_driver_table[]; + +static dt_driver * +dt_driver_by_name(const char *name) +{ + int i; + + for (i = 0; dt_driver_table[i].name; i++) { + if (!strcmp(name, dt_driver_table[i].name)) + return &dt_driver_table[i]; + } + return NULL; +} + +static dt_device * +dt_device_of(tree *conf) +{ + return tree_get_user(conf); +} + +static 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_new_device(tree *conf, 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 void +dt_config(tree *conf, dt_host *host) +{ + dt_driver *drv; + dt_device *dev; + tree *kid; + + drv = dt_driver_by_name(tree_node_name(conf)); + if (!drv) { + fprintf(stderr, "No driver for device %s\n", + tree_node_name(conf)); + exit(1); + } + dev = dt_new_device(conf, drv); + if (drv->config) { + if (drv->config(dev, host)) + return; + } + + TREE_FOREACH_KID(kid, conf) + dt_config(kid, host); +} + +static 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, *kid; + 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_KID(k, dev->conf) { + kid = dt_device_of(k); + if (kid->visit < visit - 1) + dt_do_visit(kid, 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 *kid; + + if (dev && dev->drv->start) + dev->drv->start(dev); + + TREE_FOREACH_KID(kid, conf) + dt_start(kid); +} + + +/* Device properties */ + +/* + * This is for parsing configuration tree node properties into device + * private data. + */ + +struct dt_prop_spec { + const char *name; + ptrdiff_t offs; /* offset in device private data */ + size_t size; /* size there, for sanity checking */ + int (*parse)(void *, const char *, dt_prop_spec *); +}; + +#define DT_PROP_SPEC_INIT(name, strty, member, fmt) \ + { name, offsetof(strty, member), sizeof(((strty *)0)->member), \ + dt_parse_##fmt } + +static dt_prop_spec * +dt_prop_spec_by_name(dt_driver *drv, const char *name) +{ + 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); + dt_prop_spec *spec = dt_prop_spec_by_name(dev->drv, name); + + if (!spec) { + fprintf(stderr, "A %s device has no property %s\n", + dev->drv->name, name); + exit(1); + } + + if (memchr(val, 0, size) != val + size - 1 + || spec->parse((char *)dev->priv + spec->offs, val, spec) < 0) { + fprintf(stderr, "Bad value %.*s for property %s of device %s\n", + size, val, name, dev->drv->name); + exit(1); + } +} + +static int +dt_parse_string(void *dst, const char *src, dt_prop_spec *spec) +{ + assert(spec->size == sizeof(char *)); + *(const char **)dst = src; + return 0; +} + +static int +dt_parse_int(void *dst, const char *src, dt_prop_spec *spec) +{ + char *ep; + long val; + + assert(spec->size == sizeof(int)); + errno = 0; + val = strtol(src, &ep, 0); + if (*ep || ep == src || errno || (int)val != val) + return -1; + *(int *)dst = val; + return 0; +} + +static int +dt_parse_ram_addr_t(void *dst, const char *src, dt_prop_spec *spec) +{ + char *ep; + unsigned long val; + + assert(spec->size == sizeof(ram_addr_t)); + errno = 0; + val = strtoul(src, &ep, 0); + if (*ep || ep == src || errno || (ram_addr_t)val != val) + return -1; + *(ram_addr_t *)dst = val; + return 0; +} + +static int +dt_parse_macaddr(void *dst, const char *src, dt_prop_spec *spec) +{ + assert(spec->size == 6); + if (parse_macaddr(dst, src) < 0) + return -1; + return 0; +} + + +/* 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 *kid; + const void *pv; + size_t sz; + + dt_fdt_chk(fdt_begin_node(fdt, tree_node_name(conf))); + TREE_FOREACH_PROP(prop, conf) { + pv = tree_prop_value(prop, &sz); + dt_fdt_chk(fdt_property(fdt, tree_prop_name(prop), pv, sz)); + } + TREE_FOREACH_KID(kid, conf) + dt_subtree_to_fdt(kid, fdt); + dt_fdt_chk(fdt_end_node(fdt)); +} + +static 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 < sizeof(stack) / sizeof(*stack)); + prop = (void *)(const char *)fdt + fdt_off_dt_struct(fdt) + offs; + tree_put_prop(stack[depth], + fdt_string(fdt, fdt32_to_cpu(prop->nameoff)), + prop->data, + fdt32_to_cpu(prop->len)); + case FDT_NOP: + break; + case FDT_BEGIN_NODE: + depth++; + assert(0 < depth && depth < sizeof(stack) / sizeof(*stack)); + stack[depth] = tree_new_kid(stack[depth-1], + fdt_get_name(fdt, offs, NULL), + NULL); + break; + case FDT_END_NODE: + depth--; + break; + case FDT_END: + dt_fdt_chk(next); + return stack[1]; + } + } +} + +static int +dt_fdt_chk(int res) +{ + if (res < 0) { + fprintf(stderr, "%s\n", fdt_strerror(res)); /* FIXME cryptic */ + exit(1); + } + return res; +} + +static void +dt_fdt_test(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 + +/* CPUs Driver */ + +typedef struct dt_device_cpus { + const char *model; + int num; +} dt_device_cpus; + +static 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 x86 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 dt_prop_spec dt_memory_props[] = { + DT_PROP_SPEC_INIT("ram", dt_device_memory, ram_size, ram_addr_t), +}; + +static int +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; + return 0; +} + +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 *fd[MAX_FD]; +} dt_device_pc_misc; + +static dt_prop_spec dt_pc_misc_props[] = { + DT_PROP_SPEC_INIT("boot-device", dt_device_pc_misc, boot_device, + string), +}; + +static int +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_drive_config(dev->conf, host, + priv->fd, sizeof(priv->fd) / sizeof(*priv->fd)); + return 1; +} + +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]); + } + } + + i8042_init(priv->i8259[1], priv->i8259[12], 0x60); + DMA_init(0); + + floppy_controller = fdctrl_init(priv->i8259[6], 2, 0, 0x3f0, priv->fd); +} + +static void +dt_pc_misc_start(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_hd(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 *bus; + tree *pc; +} dt_device_pci; + +static int +dt_pci_config(dt_device *dev, dt_host *host) +{ + dt_device_pci *priv = dev->priv; + + priv->bus = NULL; + priv->pc = dt_require_named(dev, "/pc-misc"); + return 0; +} + +static void +dt_pci_init(dt_device *dev) +{ + dt_device_pci *priv = dev->priv; + + priv->bus = 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 void +dt_must_be_on_pcibus(dt_device *dev) +{ + dt_device *bus = dt_parent_device(dev); + + if (bus->drv->init != dt_pci_init) { + fprintf(stderr, "Device %s must be on a PCI bus\n", dev->drv->name); + exit(1); + } +} + +static PCIBus * +dt_get_pcibus(dt_device *dev) +{ + dt_device *bus = dt_parent_device(dev); + + assert(bus->drv->init == dt_pci_init); + return ((dt_device_pci *)bus->priv)->bus; +} + + +/* PIIX3 Driver */ + +typedef struct dt_device_piix3 { + int devfn; + int acpi; + int usb; + tree *pc; + BlockDriverState *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; +} dt_device_piix3; + +static int +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_drive_config(dev->conf, host, + priv->hd, sizeof(priv->hd) / sizeof(*priv->hd)); + dt_must_be_on_pcibus(dev); + return 1; +} + +static void +dt_piix3_init(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->hd, priv->devfn + 1, i8259); + + if (priv->usb) + usb_uhci_piix3_init(pci_bus, priv->devfn + 2); + + if (priv->acpi) { + uint8_t *eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */ + i2c_bus *smbus; + + /* TODO: Populate SPD eeprom data. */ + smbus = piix4_pm_init(pci_bus, priv->devfn + 3, 0xb100, i8259[9]); + for (i = 0; i < 8; i++) + smbus_eeprom_device_init(smbus, 0x50 + i, eeprom_buf + (i * 256)); + } +} + +static BlockDriverState ** +dt_piix3_hd(tree *piix3) +{ + dt_device *dev = dt_device_of(piix3); + dt_device_piix3 *priv = dev->priv; + + assert(dev->drv->init == dt_piix3_init); + return priv->hd; +} + + +/* 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_vmsvga_init_(PCIBus *bus, uint8_t *vga_ram_base, + ram_addr_t vga_ram_offset, int vga_ram_size) +{ + pci_vmsvga_init(bus, vga_ram_base, vga_ram_offset, vga_ram_size); +} + +static void +pci_vga_init_(PCIBus *bus, uint8_t *vga_ram_base, + ram_addr_t vga_ram_offset, int vga_ram_size) +{ + pci_vga_init(bus, vga_ram_base, vga_ram_offset, vga_ram_size, 0, 0); +} + +static 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 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 int +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]; + dt_must_be_on_pcibus(dev); + return 0; +} + +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 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 int +dt_nic_config(dt_device *dev, dt_host *host) +{ + dt_device_nic *priv = dev->priv; + + priv->nd.vlan = dt_find_vlan(dev->conf, host); + dt_must_be_on_pcibus(dev); + return 0; +} + +static void +dt_nic_init(dt_device *dev) +{ + dt_device_nic *priv = dev->priv; + + pci_nic_init(dt_get_pcibus(dev), &priv->nd, -1, NULL); +} + + +/* Machine Driver */ + +static dt_driver dt_driver_table[] = { + { "", 0, NULL, NULL }, + { "cpus", sizeof(dt_device_cpus), dt_cpus_props, + NULL, dt_cpus_init, NULL }, + { "memory", sizeof(dt_device_memory), dt_memory_props, + dt_memory_config, dt_memory_init, NULL }, + { "pc-misc", sizeof(dt_device_pc_misc), dt_pc_misc_props, + dt_pc_misc_config, dt_pc_misc_init, dt_pc_misc_start }, + { "pci", sizeof(dt_device_pci), NULL, + dt_pci_config, dt_pci_init, dt_pci_start }, + { "piix3", sizeof(dt_device_piix3), NULL, + dt_piix3_config, dt_piix3_init, NULL }, + { "vga", sizeof(dt_device_vga), dt_vga_props, + dt_vga_config, dt_vga_init, NULL }, + { "nic", sizeof(dt_device_nic), dt_nic_props, + dt_nic_config, dt_nic_init, NULL }, + { NULL, 0, NULL, NULL, NULL } +}; + +static tree * +dt_read_config(void) +{ + tree *root, *pci, *leaf; + + /* + * TODO Read from config file. + * + * TODO Pretty far from a comprehensive machine configuration, but + * we need to start somewhere. + */ + root = tree_new_kid(NULL, "", NULL); + leaf = tree_new_kid(root, "cpus", NULL); + tree_put_propf(leaf, "model", "%s", "qemu32"); + leaf = tree_new_kid(root, "memory", NULL); + leaf = tree_new_kid(root, "pc-misc", NULL); + pci = tree_new_kid(root, "pci", NULL); + leaf = tree_new_kid(pci, "piix3", NULL); + return root; +} + +/* + * Extract configuration from arguments and various global variables + * and put it into our machine and host configuration. + */ +static void +dt_customize_config(tree *conf, + dt_host *host, + ram_addr_t ram_size, int vga_ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + /* + * 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 *pci = tree_node_by_name(conf, "/pci"); + tree *node; + int i, index; + + node = tree_node_by_name(conf, "/cpus"); + tree_put_propf(node, "num", "%d", smp_cpus); + if (cpu_model) + tree_put_propf(node, "model", "%s", cpu_model); + + node = tree_node_by_name(conf, "/memory"); + tree_put_propf(node, "ram", "%#lx", (unsigned long)ram_size); + + node = tree_node_by_name(conf, "/pc-misc"); + tree_put_propf(node, "boot-device", "%s", boot_device); + + /* Insert VGA node */ + if (cirrus_vga_enabled || vmsvga_enabled || std_vga_enabled) { + node = tree_new_kid(pci, "vga", NULL); + tree_put_propf(node, "model", "%s", + cirrus_vga_enabled ? "cirrus" : + vmsvga_enabled ? "vms" : "std"); + tree_put_propf(node, "ram", "%#x", vga_ram_size); + } + + /* Insert NIC nodes, connect to VLANs */ + for(i = 0; i < nb_nics; i++) { + /* TODO non-PCI NICs */ + NICInfo *n = &nd_table[i]; + + node = tree_new_kid(pci, "nic", NULL); + tree_put_propf(node, "mac", "%02x:%02x:%02x:%02x:%02x:%02x", + n->macaddr[0], n->macaddr[1], n->macaddr[2], + n->macaddr[3], n->macaddr[4], n->macaddr[5]); + tree_put_propf(node, "model", "%s", + n->model ? n->model : "ne2k_pci"); + if (n->name) + tree_put_propf(node, "name", "%s", n->name); + dt_attach_nic(host, i, node, n->vlan); + } + + /* Connect drives to their controller nodes */ + /* IDE */ + node = tree_node_by_name(pci, "piix3"); + for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) { + index = drive_get_index(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); + if (index != -1) + dt_attach_drive(host, index, node, drives_table[index].bdrv); + } + /* Floppy */ + node = tree_node_by_name(conf, "/pc-misc"); + for(i = 0; i < MAX_FD; i++) { + index = drive_get_index(IF_FLOPPY, 0, i); + if (index != -1) + dt_attach_drive(host, index, node, drives_table[index].bdrv); + } + + /* Unimplemented stuff */ + if (kernel_filename) + abort(); /* TODO */ +} + +static void +pc_init_dt(ram_addr_t ram_size, int vga_ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + tree *conf; + dt_host host; + + conf = dt_read_config(); + if (!conf) + exit(1); + tree_print(conf); + memset(&host, 0, sizeof(host)); + dt_customize_config(conf, &host, ram_size, vga_ram_size, boot_device, + kernel_filename, kernel_cmdline, initrd_filename, + cpu_model); + dt_config(conf, &host); + tree_print(conf); + dt_print_host_config(&host); + dt_fdt_test(conf); + dt_init(conf); + dt_start(conf); +} + +QEMUMachine pcdt_machine = { + .name = "pcdt", + .desc = "Standard PC (device tree)", + .init = pc_init_dt, + .ram_require = VGA_RAM_SIZE + PC_MAX_BIOS_SIZE, + .max_cpus = 255, +}; diff --git a/hw/pc.c b/hw/pc.c index 57ba803..107afb7 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -37,41 +37,34 @@ #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 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); } @@ -120,7 +113,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; @@ -166,7 +159,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; @@ -202,7 +195,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) { #define PC_MAX_BOOT_DEVICES 3 RTCState *s = (RTCState *)opaque; @@ -228,8 +221,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, }; @@ -362,13 +355,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; } @@ -420,7 +413,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; @@ -687,7 +680,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); @@ -702,11 +695,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/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/net.c b/net.c index 29beb28..5ee3ba4 100644 --- a/net.c +++ b/net.c @@ -153,7 +153,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 03c7f18..f672915 100644 --- a/net.h +++ b/net.h @@ -47,6 +47,7 @@ int qemu_can_send_packet(VLANClientState *vc); ssize_t qemu_sendv_packet(VLANClientState *vc, const struct iovec *iov, int iovcnt); void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size); +int parse_macaddr(uint8_t *macaddr, const char *p); void qemu_format_nic_info_str(VLANClientState *vc, uint8_t macaddr[6]); void qemu_check_nic_model(NICInfo *nd, const char *model); void qemu_check_nic_model_list(NICInfo *nd, const char * const *models, diff --git a/target-i386/machine.c b/target-i386/machine.c index 1cf49d5..01329d2 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -7,6 +7,8 @@ void register_machines(void) { + extern QEMUMachine pcdt_machine; + qemu_register_machine(&pcdt_machine); qemu_register_machine(&pc_machine); qemu_register_machine(&isapc_machine); } diff --git a/tree.c b/tree.c new file mode 100644 index 0000000..a906a6a --- /dev/null +++ b/tree.c @@ -0,0 +1,298 @@ +/* + * QEMU PC System Emulator + * + * Copyright (C) 2009 Red Hat, Inc., Markus Armbruster + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* 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) kids; + 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_kid(tree *parent, const char *name, void *user) +{ + tree *kid = qemu_malloc(sizeof(*kid)); + + assert(parent || !*name); + kid->name = name; + LIST_INIT(&kid->props); + kid->parent = parent; + TAILQ_INIT(&kid->kids); + if (parent) + TAILQ_INSERT_TAIL(&parent->kids, kid, siblings); + kid->user = user; + + return kid; +} + +const char * +tree_node_name(const tree *node) +{ + return node->name; +} + +static tree * +tree_kid_by_name(const tree *dt, const char *name) +{ + const char *slash = strchr(name, '/'); + size_t len = slash ? slash - name : strlen(name); + tree *kid; + + TAILQ_FOREACH(kid, &dt->kids, siblings) { + if (!memcmp(kid->name, name, len) && kid->name[len] == 0) + return kid; + } + return NULL; +} + +tree * +tree_node_by_name(const tree *node, const char *name) +{ + tree *kid; + size_t len; + + if (name[0] == '/') { + for (; node->parent; node = node->parent) ; + name++; + } + + if (name[0] == 0) + return (tree *)node; + + kid = tree_kid_by_name(node, name); + if (!kid) + return NULL; + + len = strlen(kid->name); + if (name[len] == 0) + return kid; + assert (name[len] == '/'); + + while (name[len] == '/') len++; + return tree_node_by_name(kid, name + len); +} + +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_kid(const tree *node) +{ + return TAILQ_FIRST(&node->kids); +} + +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 < 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 *kid; + + printf("%*s%s {\n", indent, "", node->name[0] ? node->name : "/"); + LIST_FOREACH(prop, &node->props, link) { + printf("%*s%s", indent + 4, "", prop->name); + pv = prop->val; + if (pv) { + printf(" = "); + use_str = pv[prop->sz - 1] == 0; + for (i = 0; i < prop->sz - 1; i++) { + if (!isprint(pv[i])) + use_str = 0; + } + if (use_str) + printf("\"%s\"", (const char *)prop->val); + else { + sep = '['; + for (i = 0; i < prop->sz; i++) { + printf("%c%02x", sep, pv[i]); + sep = ' '; + } + printf("]"); + } + } + printf(";\n"); + } + TAILQ_FOREACH(kid, &node->kids, siblings) + tree_print_sub(kid, indent + 4); + printf("%*s};\n", indent, ""); +} + +void +tree_print(const tree *node) +{ + tree_print_sub(node, 0); +} diff --git a/tree.h b/tree.h new file mode 100644 index 0000000..3e596f8 --- /dev/null +++ b/tree.h @@ -0,0 +1,40 @@ +#ifndef TREE_H +#define TREE_H + +#include + +typedef struct tree tree; +typedef struct tree_prop tree_prop; + +tree *tree_new_kid(tree *parent, const char *name, void *user); +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_kid(const tree *node); +tree *tree_sibling(const tree *node); +#define TREE_FOREACH_KID(var, node) \ + for (var = tree_first_kid(node); var; var = tree_sibling(var)) + +int tree_path(const tree *node, char *buf, size_t bufsz); +void tree_print(const tree *node); + +#endif