From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1MERkr-0004Gk-R3 for qemu-devel@nongnu.org; Wed, 10 Jun 2009 13:38:29 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1MERkn-0004FJ-RY for qemu-devel@nongnu.org; Wed, 10 Jun 2009 13:38:29 -0400 Received: from [199.232.76.173] (port=51293 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MERkn-0004FD-Jv for qemu-devel@nongnu.org; Wed, 10 Jun 2009 13:38:25 -0400 Received: from gateway.codesourcery.com ([65.74.133.9]:52177) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1MERkm-0001zF-F9 for qemu-devel@nongnu.org; Wed, 10 Jun 2009 13:38:25 -0400 Received: from wren.home (localhost [127.0.0.1]) by gateway.codesourcery.com (Postfix) with ESMTP id 812EC68053 for ; Wed, 10 Jun 2009 10:38:22 -0700 (PDT) From: Paul Brook Date: Wed, 10 Jun 2009 18:38:21 +0100 Message-ID: <20090610173821.4674.58171.stgit@wren.home> In-Reply-To: <20090610173803.4674.82538.stgit@wren.home> References: <20090610173803.4674.82538.stgit@wren.home> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Subject: [Qemu-devel] [PATCH 2/4] Add device tree machine List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org FDT based machine creation. When -M foo is specified look for and use foo.fdb. Build and ship board configs. Signed-off-by: Paul Brook --- .gitignore | 1 Makefile | 20 +- Makefile.target | 5 configure | 17 + hw/arm-cpu.c | 78 ++++++ hw/arm_boot.c | 22 ++ hw/boards.h | 9 + hw/dt-machine.c | 582 +++++++++++++++++++++++++++++++++++++++++++++ hw/i2c.c | 8 + hw/pci.c | 1 hw/qdev.c | 225 +++++++++++++++++ hw/qdev.h | 50 +++- hw/ssi.c | 7 - hw/syborg.c | 112 --------- hw/sysbus.c | 5 hw/sysbus.h | 15 + pc-bios/boards/syborg.dts | 134 ++++++++++ rules.mak | 3 sysemu.h | 3 vl.c | 45 +++ 20 files changed, 1179 insertions(+), 163 deletions(-) create mode 100644 hw/arm-cpu.c create mode 100644 hw/dt-machine.c delete mode 100644 hw/syborg.c create mode 100644 pc-bios/boards/syborg.dts diff --git a/.gitignore b/.gitignore index a8da10e..225f705 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ qemu-io *.vr *.d *.o +pc-bios/boards/*.dtb .pc patches pc-bios/bios-pq/status diff --git a/Makefile b/Makefile index 6fc234c..6d15c44 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ endif .PHONY: all clean cscope distclean dvi html info install install-doc \ recurse-all speed tar tarbin test -VPATH=$(SRC_PATH):$(SRC_PATH)/hw +VPATH=$(SRC_PATH):$(SRC_PATH)/hw:$(SRC_PATH)/pc-bios/boards CFLAGS += $(OS_CFLAGS) $(ARCH_CFLAGS) @@ -43,7 +43,16 @@ ifdef CONFIG_WIN32 LIBS+=-lwinmm -lws2_32 -liphlpapi endif -build-all: $(TOOLS) $(DOCS) recurse-all +####################################################################### +# Board descriptions + +BOARDS = syborg + +ifdef DTC +BOARDS_BIN = $(BOARDS:%=pc-bios/boards/%.dtb) +endif + +build-all: $(TOOLS) $(DOCS) recurse-all $(BOARDS_BIN) config-host.mak: configure ifneq ($(wildcard config-host.mak),) @@ -266,6 +275,7 @@ clean: # avoid old build problems by removing potentially incorrect old files rm -f config.mak config.h op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h rm -f *.o *.d *.a $(TOOLS) TAGS cscope.* *.pod *~ */*~ + rm -f $(BOARDS_BIN) for d in slirp audio block libfdt; do \ rm -f $$d/*.o $$d/*.d $$d/*.a ; \ done @@ -316,6 +326,12 @@ ifneq ($(BLOBS),) $(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \ done endif +ifneq ($(BOARDS_BIN),) + $(INSTALL_DIR) "$(DESTDIR)$(datadir)/boards" + set -e; for x in $(BOARDS_BIN); do \ + $(INSTALL_DATA) $(SRC_PATH)/$$x "$(DESTDIR)$(datadir)/boards"; \ + done +endif ifndef CONFIG_WIN32 $(INSTALL_DIR) "$(DESTDIR)$(datadir)/keymaps" set -e; for x in $(KEYMAPS); do \ diff --git a/Makefile.target b/Makefile.target index 4e302c0..4bc4c76 100644 --- a/Makefile.target +++ b/Makefile.target @@ -494,7 +494,7 @@ endif #CONFIG_BSD_USER ifndef CONFIG_USER_ONLY OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o \ - gdbstub.o gdbstub-xml.o + gdbstub.o gdbstub-xml.o dt-machine.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly OBJS+=virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o @@ -664,9 +664,10 @@ OBJS+= nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o OBJS+= mst_fpga.o mainstone.o OBJS+= musicpal.o pflash_cfi02.o OBJS+= framebuffer.o -OBJS+= syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o +OBJS+= syborg_fb.o syborg_interrupt.o syborg_keyboard.o OBJS+= syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o OBJS+= syborg_virtio.o +OBJS+= arm-cpu.o CPPFLAGS += -DHAS_AUDIO endif ifeq ($(TARGET_BASE_ARCH), sh4) diff --git a/configure b/configure index 59ba8ef..f20da35 100755 --- a/configure +++ b/configure @@ -199,6 +199,7 @@ sdl="yes" sdl_x11="no" xen="yes" pkgversion="" +dtc="" # OS specific if check_define __linux__ ; then @@ -503,6 +504,8 @@ for opt do ;; --with-pkgversion=*) pkgversion=" ($optarg)" ;; + --with-dtc=*) dtc="$optarg" + ;; --disable-docs) build_docs="no" ;; *) echo "ERROR: unknown option $opt"; show_help="yes" @@ -1227,6 +1230,13 @@ if $cc $ARCH_CFLAGS -o $TMPE ${OS_CFLAGS} $TMPC -lfdt 2> /dev/null > /dev/null ; build_fdt=no fi +# Check for device tree compiler +if test -z "$dtc" ; then + dtc="`which dtc 2>/dev/null`" + if test ! -x "$dtc" ; then + dtc="" + fi +fi # # Check for xxxat() functions when we are building linux-user # emulator. This is done because older glibc versions don't @@ -1380,6 +1390,7 @@ echo "Install blobs $blobs" echo -e "KVM support $kvm" echo "Build libfdt $build_fdt" echo "preadv support $preadv" +echo "dtc ${dtc:-(Not Found)}" if test $sdl_too_old = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -1708,7 +1719,7 @@ if test "$iovec" = "yes" ; then echo "#define HAVE_IOVEC 1" >> $config_h fi if test "$preadv" = "yes" ; then - echo "#define HAVE_PREADV 1" >> $config_h + echo "#defne HAVE_PREADV 1" >> $config_h fi if test "$build_fdt" = "yes" ; then echo "BUILD_LIBFDT=libfdt/libfdt.a" >> $config_mak @@ -1717,6 +1728,10 @@ else echo "FDT_LIBS=-lfdt" >> $config_mak fi +if [ -n "$dtc" ] ; then + echo "DTC=$dtc" >> $config_mak +fi + # XXX: suppress that if [ "$bsd" = "yes" ] ; then echo "#define O_LARGEFILE 0" >> $config_h diff --git a/hw/arm-cpu.c b/hw/arm-cpu.c new file mode 100644 index 0000000..c15eb12 --- /dev/null +++ b/hw/arm-cpu.c @@ -0,0 +1,78 @@ +/* + * CPU devices + * + * Copyright (c) 2009 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the GNU GPL v2. + */ + +#include "sysbus.h" +#include "arm-misc.h" +#include "boards.h" + +/* FIXME: Remove this and make the CPU emulation use the right names. */ +static const struct { + const char *devname; + const char *cpuname; +} cpu_device_name_map[] = { + {"ARM,ARM926EJ-S", "arm926"}, + {"ARM,Cortex-A8", "cortex-a8"} +}; + +typedef struct { + SysBusDeviceInfo sysbus; + const char *cpuname; +} CPUInfo; + +typedef struct { + SysBusDevice busdev; + qemu_irq *cpu_pic; +} CPUDevice; + +static void arm_cpu_dev_set_irq(void *opaque, int n, int level) +{ + CPUDevice *s = opaque; + assert(n >= 0 && n < 2); + qemu_set_irq(s->cpu_pic[n], level); +} + +static DevicePropList cpu_qdev_props[] = { + {.name = "nvic", .type = PROP_TYPE_DEV}, + {.name = ""} +}; + +static void arm_cpu_dev_init(SysBusDevice *dev) +{ + CPUDevice *s = FROM_SYSBUS(CPUDevice, dev); + CPUInfo *info = container_of(dev->info, CPUInfo, sysbus); + CPUState *env; + + env = cpu_init(info->cpuname); + s->cpu_pic = arm_pic_init_cpu(env); + qdev_init_gpio_in(&dev->qdev, arm_cpu_dev_set_irq, 2); + if (arm_feature(env, ARM_FEATURE_M)) { + env->v7m.nvic = qdev_get_prop_dev(&dev->qdev, "nvic"); + if (!env->v7m.nvic) { + hw_error("CPU requires NVIC"); + } + } +} + +static void arm_cpu_register_devices(void) +{ + int i; + CPUInfo *info; + + for (i = 0; i < ARRAY_SIZE(cpu_device_name_map); i++) { + info = qemu_mallocz(sizeof(*info)); + info->sysbus.qdev.props = cpu_qdev_props; + info->sysbus.init = arm_cpu_dev_init; + info->cpuname = cpu_device_name_map[i].cpuname; + sysbus_register_withprop(cpu_device_name_map[i].devname, + sizeof(CPUDevice), + &info->sysbus); + } +} + +device_init(arm_cpu_register_devices) diff --git a/hw/arm_boot.c b/hw/arm_boot.c index acfa67e..95dd532 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -10,6 +10,7 @@ #include "hw.h" #include "arm-misc.h" #include "sysemu.h" +#include "boards.h" #define KERNEL_ARGS_ADDR 0x100 #define KERNEL_LOAD_ADDR 0x00010000 @@ -260,3 +261,24 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info) set_kernel_args(info, initrd_size, info->loader_start); } } + +static struct arm_boot_info arm_linux_binfo; +static void arm_linux_bootstrap(ram_addr_t ram_size, const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) +{ + arm_linux_binfo.ram_size = ram_size; + arm_linux_binfo.kernel_filename = kernel_filename; + arm_linux_binfo.kernel_cmdline = kernel_cmdline; + arm_linux_binfo.initrd_filename = initrd_filename; + arm_linux_binfo.board_id = get_bootstrap_arg_int("board-id", 0); + arm_linux_binfo.loader_start = get_bootstrap_arg_int("loader-start", 0); + arm_load_kernel(first_cpu, &arm_linux_binfo); +} + +static void arm_boot_register(void) +{ + register_machine_bootstrap("arm-linux", arm_linux_bootstrap); +} + +machine_init(arm_boot_register); diff --git a/hw/boards.h b/hw/boards.h index f6733b7..1733e8b 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -23,5 +23,14 @@ typedef struct QEMUMachine { int qemu_register_machine(QEMUMachine *m); extern QEMUMachine *current_machine; +extern QEMUMachine dt_machine; + +typedef void (*machine_bootstrapfn)(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename); + +void register_machine_bootstrap(const char *name, machine_bootstrapfn fn); +uint64_t get_bootstrap_arg_int(const char *name, uint64_t def); #endif diff --git a/hw/dt-machine.c b/hw/dt-machine.c new file mode 100644 index 0000000..b960a6c --- /dev/null +++ b/hw/dt-machine.c @@ -0,0 +1,582 @@ +#include "sysbus.h" +#include "device_tree.h" +#include "boards.h" + +#include + +/* FIXME: Remove this. */ +static void *the_dt; +static int bootstrap_offset; + +static void dt_walk_bus(DeviceState *parent, void *dt, int offset, int folded); + +static int fdt_next_node(const void *fdt, int offset) +{ + int level = 1; + uint32_t tag; + int nextoffset; + + tag = fdt_next_tag(fdt, offset, &nextoffset); + if (tag != FDT_BEGIN_NODE) + return -FDT_ERR_BADOFFSET; + + while (level >= 0) { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + + switch (tag) { + case FDT_END: + return -FDT_ERR_TRUNCATED; + case FDT_BEGIN_NODE: + level++; + if (level == 1) + return offset; + break; + case FDT_END_NODE: + level--; + break; + case FDT_PROP: + case FDT_NOP: + break; + default: + return -FDT_ERR_BADSTRUCTURE; + } + } + + return -FDT_ERR_NOTFOUND; +} + +static int fdt_first_subnode(const void *fdt, int offset) +{ + uint32_t tag; + int nextoffset; + + tag = fdt_next_tag(fdt, offset, &nextoffset); + if (tag != FDT_BEGIN_NODE) + return -FDT_ERR_BADOFFSET; + + while (tag != FDT_END_NODE) { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + + switch (tag) { + case FDT_END: + return -FDT_ERR_TRUNCATED; + case FDT_BEGIN_NODE: + return offset; + case FDT_END_NODE: + case FDT_PROP: + case FDT_NOP: + break; + default: + return -FDT_ERR_BADSTRUCTURE; + } + } + + return -FDT_ERR_NOTFOUND; +} + +static void dt_add_props(DeviceState *dev, void *dt, int offset) +{ + DevicePropList *prop; + const void *p; + int prop_len; + uint64_t i; + const uint32_t *ip; + + prop = qdev_get_proplist(dev); + if (!prop) { + return; + } + for (; prop->name; prop++) { + p = fdt_getprop(dt, offset, prop->name, &prop_len); + if (!p) { + continue; + } + ip = p; + switch (prop->type) { + case PROP_TYPE_INT: + if (prop_len != 4 && prop_len != 8) { + hw_error("%s: Bad length for property '%s'\n", + fdt_get_name(dt, offset, NULL), prop->name); + } + i = fdt32_to_cpu(ip[0]); + if (prop_len == 8) { + i = (i << 32) | fdt32_to_cpu(ip[1]); + } + qdev_set_prop_int(dev, prop->name, i); + break; + case PROP_TYPE_DEV: + { + uint32_t phandle; + DeviceState *other_dev; + if (prop_len != 4) { + hw_error("%s: Bad length for property '%s'\n", + fdt_get_name(dt, offset, NULL), prop->name); + } + phandle = fdt32_to_cpu(ip[0]); + other_dev = qdev_from_phandle(phandle); + if (!other_dev) { + hw_error("%s: Device (%d) not found\n", + fdt_get_name(dt, offset, NULL), phandle); + } + qdev_set_prop_dev(dev, prop->name, other_dev); + } + break; + case PROP_TYPE_ARRAY: + { + uint32_t *data; + if (prop_len & 3) { + hw_error("%s: Bad length for property '%s'\n", + fdt_get_name(dt, offset, NULL), prop->name); + } + data = qemu_malloc(prop_len); + for (i = 0; i < prop_len >> 2; i++) { + data[i] = fdt32_to_cpu(ip[i]); + } + qdev_set_prop_array(dev, prop->name, data, prop_len >> 2); + qemu_free(data); + } + break; + default: + abort(); + } + } +} + +static void dt_create_device(BusState *bus, void *dt, int offset) +{ + char namebuf[128]; + const char *type; + const uint32_t *prop; + char *p; + DeviceState *dev; + + /* First try the "model" property. */ + type = fdt_getprop(dt, offset, "model", NULL); + if (type && !qdev_device_exists(type)) { + type = NULL; + } + /* If that does not work then try "compatible". */ + if (!type) { + type = fdt_getprop(dt, offset, "compatible", NULL); + if (type && !qdev_device_exists(type)) { + type = NULL; + } + } + /* If all else fails then resort to the device name. */ + if (!type) { + type = fdt_get_name(dt, offset, NULL); + p = namebuf; + while (*type && *type != '@') { + *(p++) = *(type++); + } + *p = 0; + if (!qdev_device_exists(namebuf)) { + hw_error("Unrecognised device '%s'\n", + fdt_get_name(dt, offset, NULL)); + } + type = namebuf; + } + + dev = qdev_create(bus, type); + dev->fdt_offset = offset; + prop = fdt_getprop(dt, offset, "linux,phandle", NULL); + if (prop) { + dev->phandle = fdt32_to_cpu(*prop); + } else { + dev->phandle = -1; + } +} + +static void dt_init_device(DeviceState *dev, void *opaque) +{ + void *dt = opaque; + int offset; + + offset = dev->fdt_offset; + dt_add_props(dev, dt, offset); + qdev_init(dev); + + dt_walk_bus(dev, dt, offset, 0); +} + +static int dt_fold_bus(void *dt, int offset) +{ + const char *name = fdt_get_name(dt, offset, NULL); + + if (strcmp(name, "cpus") == 0) { + return 1; + } + if (fdt_getprop(dt, offset, "qemu,fold-bus", NULL)) { + return 1; + } + return 0; +} + +static int dt_ignore_node(void *dt, int offset) +{ + const char *name = fdt_get_name(dt, offset, NULL); + + if (strcmp(name, "chosen") == 0) { + return 1; + } + if (strcmp(name, "aliases") == 0) { + return 1; + } + if (fdt_getprop(dt, offset, "qemu,ignore", NULL)) { + return 1; + } + return 0; +} + +static void dt_get_sizes(void *dt, int offset, + int *addr_cells, int *size_cells) +{ + const uint32_t *prop; + int parent; + + parent = fdt_parent_offset(dt, offset); + assert(parent >= 0); + + if (addr_cells) { + prop = fdt_getprop(dt, parent, "#address-cells", NULL); + if (!prop) { + hw_error("%s: Missing #address-cells", + fdt_get_name(dt, offset, NULL)); + } + *addr_cells = fdt32_to_cpu(*prop); + } + + if (size_cells) { + prop = fdt_getprop(dt, parent, "#size-cells", NULL); + if (!prop) { + hw_error("%s: Missing #size-cells", + fdt_get_name(dt, offset, NULL)); + } + *size_cells = fdt32_to_cpu(*prop); + } +} + +static void dt_create_memory(void *dt, int offset, int flags) +{ + const uint32_t *regs; + int regs_len; + const uint32_t *alias; + int alias_len; + int addr_cells; + int size_cells; + uint64_t addr; + uint64_t size; + ram_addr_t ram_offset; + + dt_get_sizes(dt, offset, &addr_cells, &size_cells); + regs = fdt_getprop(dt, offset, "reg", ®s_len); + if (!regs || regs_len == 0) { + return; + } + if ((regs_len % ((addr_cells + size_cells) * 4)) != 0) { + hw_error("%s: Bad reg size\n", fdt_get_name(dt, offset, NULL)); + } + alias = fdt_getprop(dt, offset, "qemu,alias", &alias_len); + while (regs_len > 0) { + addr = fdt32_to_cpu(*(regs++)); + if (addr_cells == 2) { + addr = (addr << 32) | fdt32_to_cpu(*(regs++)); + } + size = fdt32_to_cpu(*(regs++)); + if (size_cells == 2) { + size = (size << 32) | fdt32_to_cpu(*(regs++)); + } + regs_len -= (addr_cells + size_cells) * 4; + if (size != (ram_addr_t)size) { + hw_error("Ram too big\n"); + } + ram_offset = qemu_ram_alloc(size); + cpu_register_physical_memory(addr, size, ram_offset | flags); + if (alias) { + if (regs_len > 0) { + hw_error("%s: Aliased memory may only have a single region", + fdt_get_name(dt, offset, NULL)); + } + if ((alias_len % addr_cells) != 0) { + hw_error("%s: Bad qemu,alias size\n", + fdt_get_name(dt, offset, NULL)); + } + while (alias_len) { + addr = fdt32_to_cpu(*(alias++)); + if (addr_cells== 2) { + addr = (addr << 32) | fdt32_to_cpu(*(alias++)); + } + cpu_register_physical_memory(addr, size, ram_offset | flags); + alias_len -= addr_cells * 4; + } + } + } +} + +static void dt_walk_bus(DeviceState *parent, void *dt, int offset, int folded) +{ + int next_offset; + const char *type; + const char *bus_name; + BusState *parent_bus; + + if (parent) { + parent_bus = LIST_FIRST(&parent->child_bus); + } else { + parent_bus = NULL; + } + next_offset = fdt_first_subnode(dt, offset); + while (next_offset > 0) { + offset = next_offset; + next_offset = fdt_next_node(dt, offset); + if (dt_ignore_node(dt, offset)) { + continue; + } + if (dt_fold_bus(dt, offset)) { + dt_walk_bus(parent, dt, offset, 1); + continue; + } + bus_name = fdt_getprop(dt, offset, "qemu,parent-bus", NULL); + if (bus_name) { + if (parent) { + parent_bus = qdev_get_child_bus(parent, bus_name); + } + if (!parent_bus) { + hw_error("%s: Unable to find parent bus\n", + fdt_get_name(dt, offset, NULL)); + } + } + type = fdt_getprop(dt, offset, "device_type", NULL); + /* Special case for memory. */ + if (type && strcmp(type, "memory") == 0) { + dt_create_memory(dt, offset, IO_MEM_RAM); + continue; + } + if (type && strcmp(type, "rom") == 0) { + dt_create_memory(dt, offset, IO_MEM_ROM); + continue; + } + dt_create_device(parent_bus, dt, offset); + } + if (!folded) { + qdev_enumerate_child_devices(parent, dt_init_device, dt); + } +} + +void dt_fixup_qdev(DeviceState *dev) +{ + void *dt = the_dt; + int offset = dev->fdt_offset; + int n; + int prop_size; + const uint32_t *prop; + + if (dev->num_gpio_out) { + int parent; + int irqn; + qemu_irq pin; + DeviceState *parent_dev; + + prop = fdt_getprop(dt, offset, "qemu,gpio", &prop_size); + if (prop_size != 8 * dev->num_gpio_out) { + hw_error("%s: Bad GPIO size\n", fdt_get_name(dt, offset, NULL)); + } + for (n = 0; n < dev->num_gpio_out; n++) { + parent = fdt32_to_cpu(*(prop++)); + if (parent == 0) { + /* Assume zero phandle means disconnected. */ + prop++; + continue; + } + parent_dev = qdev_from_phandle(parent); + if (!parent_dev) { + hw_error("%s: GPIO device (%d) not found\n", + fdt_get_name(dt, offset, NULL), parent); + } + irqn = fdt32_to_cpu(*(prop++)); + if (irqn >= parent_dev->num_gpio_in) { + hw_error("%s: Invalid GPIO %d (%d)\n", + fdt_get_name(dt, offset, NULL), n, irqn); + } + pin = qdev_get_gpio_in(parent_dev, irqn); + qdev_connect_gpio_out(dev, n, pin); + } + } +} + +void dt_fixup_sysbus_device(DeviceState *dev) +{ + SysBusDevice *s = sysbus_from_qdev(dev); + void *dt = the_dt; + int offset = dev->fdt_offset; + const uint32_t *prop; + int prop_size; + int n; + + if (s->num_mmio) { + int addr_cells; + int size_cells; + uint64_t addr; + dt_get_sizes(dt, offset, &addr_cells, &size_cells); + if (addr_cells < 1 || addr_cells > 2) { + hw_error("%s: unsupported #address-cells (%d)\n", + fdt_get_name(dt, offset, NULL), addr_cells); + } + prop = fdt_getprop(dt, offset, "reg", &prop_size); + if (prop_size != (addr_cells + size_cells) * 4 * s->num_mmio) { + hw_error("%s: Bad reg size\n", fdt_get_name(dt, offset, NULL)); + } + for (n = 0; n < s->num_mmio; n++) { + addr = fdt32_to_cpu(*(prop++)); + if (addr_cells == 2) { + addr = (addr << 32) | fdt32_to_cpu(*(prop++)); + } + /* The device already told up how big the region is, so ignore + what the device tree says. */ + prop += size_cells; + sysbus_mmio_map(s, n, addr); + } + } + + if (s->num_irq) { + int parent; + int irqn; + DeviceState *parent_dev; + + prop = fdt_getprop(dt, offset, "interrupt-parent", &prop_size); + if (!prop || prop_size != 4) { + hw_error("%s: Missing/bad interrupt-parent\n", + fdt_get_name(dt, offset, NULL)); + } + parent = fdt32_to_cpu(*prop); + parent_dev = qdev_from_phandle(parent); + if (!parent_dev) { + hw_error("%s: interrupt-parent device (%d) not found\n", + fdt_get_name(dt, offset, NULL), parent); + } + prop = fdt_getprop(dt, parent_dev->fdt_offset, "#interrupt-cells", + &prop_size); + if (!prop || prop_size != 4) { + hw_error("%s: Missing #interrupt-cells\n", + fdt_get_name(dt, parent_dev->fdt_offset, NULL)); + } + if (fdt32_to_cpu(*prop) != 1) { + hw_error("%s: unsupported #interrupt-cells\n", + fdt_get_name(dt, parent_dev->fdt_offset, NULL)); + } + prop = fdt_getprop(dt, offset, "interrupts", &prop_size); + if (prop_size != 4 * s->num_irq) { + hw_error("%s: Bad interrupts size\n", + fdt_get_name(dt, offset, NULL)); + } + for (n = 0; n < s->num_irq; n++) { + irqn = fdt32_to_cpu(*(prop++)); + if (irqn == -1) { + continue; + } + if (irqn >= parent_dev->num_gpio_in) { + hw_error("%s: Invalid interrupt %d (%d)\n", + fdt_get_name(dt, offset, NULL), n, irqn); + } + sysbus_connect_irq(s, n, qdev_get_gpio_in(parent_dev, irqn)); + } + } +} + +typedef struct MachineBootstrap { + const char *name; + machine_bootstrapfn fn; + LIST_ENTRY(MachineBootstrap) next; +} MachineBootstrap; + +LIST_HEAD(, MachineBootstrap) machine_bootstrap_list = + LIST_HEAD_INITIALIZER(machine_bootstrap_list); + +void register_machine_bootstrap(const char *name, machine_bootstrapfn fn) +{ + MachineBootstrap *bootstrap; + + bootstrap = qemu_mallocz(sizeof(*bootstrap)); + bootstrap->name = qemu_strdup(name); + bootstrap->fn = fn; + LIST_INSERT_HEAD(&machine_bootstrap_list, bootstrap, next); +} + +uint64_t get_bootstrap_arg_int(const char *name, uint64_t def) +{ + void *dt = the_dt; + const uint32_t *p; + int prop_len; + uint64_t val; + + p = fdt_getprop(dt, bootstrap_offset, name, &prop_len); + if (!p) { + return def; + } + if (prop_len != 4 && prop_len != 8) { + hw_error("Bad length for property '%s'\n", name); + } + val = fdt32_to_cpu(p[0]); + if (prop_len == 8) { + val = (val << 32) | fdt32_to_cpu(p[1]); + } + return val; +} + +static void dt_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + void *dt; + int dt_size; + const char *filename; + + filename = dt_machine.name; + /* FIXME: Allow user to specify filename. */ + dt = load_device_tree(filename, &dt_size); + if (!dt) { + hw_error("Failed to load device tree\n"); + } + + the_dt = dt; + + dt_walk_bus(NULL, dt, 0, 0); + + qdev_fixup_devices(); + + { + const char *p; + MachineBootstrap *bootstrap; + + bootstrap_offset = fdt_path_offset(dt, "/chosen/qemu"); + if (bootstrap_offset < 0) { + return; + } + p = fdt_getprop(dt, bootstrap_offset, "bootstrap", NULL); + if (!p) { + return; + } + + LIST_FOREACH(bootstrap, &machine_bootstrap_list, next) { + if (strcmp(bootstrap->name, p) == 0) { + break; + } + } + if (!bootstrap) { + hw_error("Unrecognised machine bootstrap '%s'\n", p); + } + bootstrap->fn(ram_size, boot_device, kernel_filename, kernel_cmdline, + initrd_filename); + + } +} + +/* This is used directly by vl.c, and not registered by normal means. */ +QEMUMachine dt_machine = { + .name = "devtree", + .desc = "Device Tree", + .init = dt_init, +}; diff --git a/hw/i2c.c b/hw/i2c.c index 8a0c4d7..4e53c09 100644 --- a/hw/i2c.c +++ b/hw/i2c.c @@ -68,7 +68,7 @@ int i2c_start_transfer(i2c_bus *bus, int address, int recv) DeviceState *qdev; i2c_slave *slave = NULL; - LIST_FOREACH(qdev, &bus->qbus.children, sibling) { + TAILQ_FOREACH(qdev, &bus->qbus.children, sibling) { slave = I2C_SLAVE_FROM_QDEV(qdev); if (slave->address == address) break; @@ -152,9 +152,15 @@ static void i2c_slave_qdev_init(DeviceState *dev, DeviceInfo *base) info->init(s); } +static const DevicePropList i2c_bus_props[] = { + {.name = "address", .type = PROP_TYPE_INT}, + {.name = NULL} +}; void i2c_register_slave(const char *name, int size, I2CSlaveInfo *info) { + assert(!info->qdev.init); assert(size >= sizeof(i2c_slave)); + info->qdev.props = qdev_merge_props(info->qdev.props, i2c_bus_props); info->qdev.init = i2c_slave_qdev_init; info->qdev.bus_type = BUS_TYPE_I2C; qdev_register(name, size, &info->qdev); diff --git a/hw/pci.c b/hw/pci.c index 8c904ba..487017d 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -924,6 +924,7 @@ void pci_qdev_register(const char *name, int size, pci_qdev_initfn init) PCIDeviceInfo *info; info = qemu_mallocz(sizeof(*info)); + assert(!info->qdev.init); info->init = init; info->qdev.init = pci_qdev_init; info->qdev.bus_type = BUS_TYPE_PCI; diff --git a/hw/qdev.c b/hw/qdev.c index 636dc78..39e8ce8 100644 --- a/hw/qdev.c +++ b/hw/qdev.c @@ -26,6 +26,25 @@ inherit from a particular bus (e.g. PCI or I2C) rather than this API directly. */ +/* Device instantiation occurs in several stages. + + 1) Device objects are created (qdev_create). + 2) Device properties are set. + 3) Device init routines are run. + 4) GPIO and IRQ lines are connected and MMIO regions are mapped. + 5) Device late init routines are run. + + It can be assumed that all siblings on a bus have been created before + any are initialized. Child devices are created after the parent + device is initialized. Thus PROP_TYPE_DEV may only refer to siblings or + siblings of direct ancestors. No dependency tracking is performed, so + a device may be initialized before the devices it references. + + Other than Within each stage, devices are processed in arbitrary order. + + Steps 3, 4 and 5 only occur after preceeding steps have been completed + for all devices. */ + #include "net.h" #include "qdev.h" #include "sysemu.h" @@ -37,6 +56,10 @@ struct DeviceProperty { union { uint64_t i; void *ptr; + struct { + uint32_t *data; + int len; + } array; } value; DeviceProperty *next; }; @@ -68,6 +91,23 @@ void qdev_register(const char *name, int size, DeviceInfo *info) t->info = info; } +static DeviceType *qdev_find_devtype(const char *name) +{ + DeviceType *t; + + for (t = device_type_list; t; t = t->next) { + if (strcmp(t->name, name) == 0) { + return t;; + } + } + return NULL; +} + +int qdev_device_exists(const char *name) +{ + return qdev_find_devtype(name) != NULL; +} + /* Create a new device. This only initializes the device state structure and allows properties to be set. qdev_init should be called to initialize the actual device emulation. */ @@ -76,11 +116,7 @@ DeviceState *qdev_create(BusState *bus, const char *name) DeviceType *t; DeviceState *dev; - for (t = device_type_list; t; t = t->next) { - if (strcmp(t->name, name) == 0) { - break; - } - } + t = qdev_find_devtype(name); if (!t) { hw_error("Unknown device '%s'\n", name); } @@ -89,8 +125,8 @@ DeviceState *qdev_create(BusState *bus, const char *name) dev->type = t; if (!bus) { - /* ???: This assumes system busses have no additional state. */ if (!main_system_bus) { + /* ???: This assumes system busses have no additional state. */ main_system_bus = qbus_create(BUS_TYPE_SYSTEM, sizeof(BusState), NULL, "main-system-bus"); } @@ -102,7 +138,9 @@ DeviceState *qdev_create(BusState *bus, const char *name) t->info->bus_type, bus->type); } dev->parent_bus = bus; - LIST_INSERT_HEAD(&bus->children, dev, sibling); + /* Keep devices in creation order for consistency between creation + and initialization passes. */ + TAILQ_INSERT_TAIL(&bus->children, dev, sibling); return dev; } @@ -114,10 +152,32 @@ void qdev_init(DeviceState *dev) dev->type->info->init(dev, dev->type->info); } +static void qdev_late_init_bus(BusState *bus) +{ + DeviceState *dev; + BusState *child_bus; + + TAILQ_FOREACH(dev, &bus->children, sibling) { + if (dev->type->info->late_init) { + dev->type->info->late_init(dev); + } + LIST_FOREACH(child_bus, &dev->child_bus, sibling) { + qdev_late_init_bus(child_bus); + } + } +} + +void qdev_do_late_init(void) +{ + if (main_system_bus) { + qdev_late_init_bus(main_system_bus); + } +} + /* Unlink device from bus and free the structure. */ void qdev_free(DeviceState *dev) { - LIST_REMOVE(dev, sibling); + TAILQ_REMOVE(&dev->parent_bus->children, dev, sibling); free(dev); } @@ -152,6 +212,17 @@ void qdev_set_prop_dev(DeviceState *dev, const char *name, DeviceState *value) prop->value.ptr = value; } +void qdev_set_prop_array(DeviceState *dev, const char *name, uint32_t *data, + int len) +{ + DeviceProperty *prop; + + prop = create_prop(dev, name, PROP_TYPE_ARRAY); + prop->value.array.data = qemu_malloc(len * 4); + prop->value.array.len = len; + memcpy(prop->value.array.data, data, len * 4); +} + void qdev_set_prop_ptr(DeviceState *dev, const char *name, void *value) { DeviceProperty *prop; @@ -231,6 +302,21 @@ DeviceState *qdev_get_prop_dev(DeviceState *dev, const char *name) return prop->value.ptr; } +int qdev_get_prop_array(DeviceState *dev, const char *name, + const uint32_t **p) +{ + DeviceProperty *prop; + + prop = find_prop(dev, name, PROP_TYPE_ARRAY); + if (!prop) { + return -1; + } + if (p) { + *p = prop->value.array.data; + } + return prop->value.array.len; +} + void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n) { assert(dev->num_gpio_in == 0); @@ -272,6 +358,17 @@ VLANClientState *qdev_get_vlan_client(DeviceState *dev, void qdev_get_macaddr(DeviceState *dev, uint8_t *macaddr) { + static int next_netdev; + if (!dev->nd) { + /* FIXME: This is just plain broken. */ + if (next_netdev >= nb_nics) { + abort(); + } + dev->nd = &nd_table[next_netdev]; + next_netdev++; + + qemu_check_nic_model(dev->nd, dev->type->name); + } memcpy(macaddr, dev->nd->macaddr, 6); } @@ -332,13 +429,121 @@ BusState *qbus_create(BusType type, size_t size, bus->type = type; bus->parent = parent; bus->name = qemu_strdup(name); - LIST_INIT(&bus->children); + TAILQ_INIT(&bus->children); if (parent) { LIST_INSERT_HEAD(&parent->child_bus, bus, sibling); } return bus; } +static DeviceState *qdev_from_phandle1(BusState *bus, uint32_t phandle) +{ + DeviceState *dev; + BusState *child_bus; + DeviceState *child_dev; + + TAILQ_FOREACH(dev, &bus->children, sibling) { + if (dev->phandle == phandle) { + return dev; + } + LIST_FOREACH(child_bus, &dev->child_bus, sibling) { + child_dev = qdev_from_phandle1(child_bus, phandle); + if (child_dev) { + return child_dev; + } + } + } + return NULL; +} + +DeviceState *qdev_from_phandle(uint32_t phandle) +{ + if (!main_system_bus) { + return NULL; + } + return qdev_from_phandle1(main_system_bus, phandle); +} + +static void qdev_fixup_bus(BusState *bus) +{ + DeviceState *dev; + + TAILQ_FOREACH(dev, &bus->children, sibling) { + if (!dev->fdt_offset) { + continue; + } + dt_fixup_qdev(dev); + switch (bus->type) { + case BUS_TYPE_SYSTEM: + dt_fixup_sysbus_device(dev); + break; + default: + break; + } + } +} + +void qdev_fixup_devices(void) +{ + assert(main_system_bus); + qdev_fixup_bus(main_system_bus); +} + +static void enumerate_bus_devices(BusState *bus, enumdevfn cb, void *opaque) +{ + DeviceState *dev; + + TAILQ_FOREACH(dev, &bus->children, sibling) { + cb(dev, opaque); + } +} + +void qdev_enumerate_child_devices(DeviceState *dev, enumdevfn cb, void *opaque) +{ + BusState *bus; + + if (!dev) { + enumerate_bus_devices(main_system_bus, cb, opaque); + return; + } + LIST_FOREACH(bus, &dev->child_bus, sibling) { + enumerate_bus_devices(bus, cb, opaque); + } +} + +DevicePropList *qdev_get_proplist(DeviceState *dev) +{ + return dev->type->info->props; +} + +DevicePropList *qdev_merge_props(const DevicePropList *a, + const DevicePropList *b) +{ + int n; + const DevicePropList *p; + DevicePropList *q; + DevicePropList *ret; + + n = 0; + for (p = a; p && p->name; p++) { + n++; + } + for (p = b; p && p->name; p++) { + n++; + } + n++; + ret = qemu_malloc(sizeof(DevicePropList) * n); + q = ret; + for (p = a; p && p->name; p++) { + *(q++) = *p; + } + for (p = b; p && p->name; p++) { + *(q++) = *p; + } + q->name = NULL; + return ret; +} + static const char *bus_type_names[] = { [ BUS_TYPE_SYSTEM ] = "System", [ BUS_TYPE_PCI ] = "PCI", @@ -399,7 +604,7 @@ static void qbus_print(Monitor *mon, BusState *bus, int indent) qdev_printf("bus: %s\n", bus->name); indent += 2; qdev_printf("type %s\n", bus_type_names[bus->type]); - LIST_FOREACH(dev, &bus->children, sibling) { + TAILQ_FOREACH(dev, &bus->children, sibling) { qdev_print(mon, dev, indent); } } diff --git a/hw/qdev.h b/hw/qdev.h index 7291805..2e2c5f3 100644 --- a/hw/qdev.h +++ b/hw/qdev.h @@ -4,6 +4,18 @@ #include "hw.h" #include "sys-queue.h" +typedef enum { + PROP_TYPE_INT, + PROP_TYPE_PTR, + PROP_TYPE_DEV, + PROP_TYPE_ARRAY +} DevicePropType; + +typedef struct { + const char *name; + DevicePropType type; +} DevicePropList; + typedef struct DeviceType DeviceType; typedef struct DeviceProperty DeviceProperty; @@ -22,7 +34,9 @@ struct DeviceState { qemu_irq *gpio_in; LIST_HEAD(, BusState) child_bus; NICInfo *nd; - LIST_ENTRY(DeviceState) sibling; + TAILQ_ENTRY(DeviceState) sibling; + int fdt_offset; + uint32_t phandle; }; typedef enum { @@ -37,7 +51,7 @@ struct BusState { DeviceState *parent; const char *name; BusType type; - LIST_HEAD(, DeviceState) children; + TAILQ_HEAD(, DeviceState) children; LIST_ENTRY(BusState) sibling; }; @@ -50,6 +64,8 @@ void qdev_free(DeviceState *dev); /* Set properties between creation and init. */ void qdev_set_prop_int(DeviceState *dev, const char *name, uint64_t value); void qdev_set_prop_dev(DeviceState *dev, const char *name, DeviceState *value); +void qdev_set_prop_array(DeviceState *dev, const char *name, uint32_t *data, + int len); void qdev_set_prop_ptr(DeviceState *dev, const char *name, void *value); void qdev_set_netdev(DeviceState *dev, NICInfo *nd); @@ -58,27 +74,30 @@ void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin); BusState *qdev_get_child_bus(DeviceState *dev, const char *name); -/*** Device API. ***/ +/*** Internal device tree bits ***/ -typedef enum { - PROP_TYPE_INT, - PROP_TYPE_PTR, - PROP_TYPE_DEV -} DevicePropType; +int qdev_device_exists(const char *name); +DeviceState *qdev_from_phandle(uint32_t phandle); +void qdev_fixup_devices(void); +void dt_fixup_qdev(DeviceState *dev); +void dt_fixup_sysbus_device(DeviceState *dev); +DevicePropList *qdev_get_proplist(DeviceState *dev); -typedef struct { - const char *name; - DevicePropType type; -} DevicePropList; +typedef void (*enumdevfn)(DeviceState *dev, void *opaque); +void qdev_enumerate_child_devices(DeviceState *dev, enumdevfn cb, void *opaque); + +/*** Device API. ***/ typedef struct DeviceInfo DeviceInfo; typedef void (*qdev_initfn)(DeviceState *dev, DeviceInfo *info); +typedef void (*qdev_late_initfn)(DeviceState *dev); typedef void (*SCSIAttachFn)(DeviceState *host, BlockDriverState *bdrv, int unit); struct DeviceInfo { qdev_initfn init; + qdev_late_initfn late_init; BusType bus_type; DevicePropList *props; }; @@ -96,10 +115,17 @@ CharDriverState *qdev_init_chardev(DeviceState *dev); BusState *qdev_get_parent_bus(DeviceState *dev); uint64_t qdev_get_prop_int(DeviceState *dev, const char *name, uint64_t def); +/* Returns the number of elements in the array. */ +int qdev_get_prop_array(DeviceState *dev, const char *name, + const uint32_t **p); +/* NOTE: The returned device may not have been initialized yet. */ DeviceState *qdev_get_prop_dev(DeviceState *dev, const char *name); /* FIXME: Remove opaque pointer properties. */ void *qdev_get_prop_ptr(DeviceState *dev, const char *name); +DevicePropList *qdev_merge_props(const DevicePropList *a, + const DevicePropList *b); + /* Convery from a base type to a parent type, with compile time checking. */ #ifdef __GNUC__ #define DO_UPCAST(type, field, dev) ( __extension__ ( { \ diff --git a/hw/ssi.c b/hw/ssi.c index 52b7b7c..c086f02 100644 --- a/hw/ssi.c +++ b/hw/ssi.c @@ -20,8 +20,8 @@ static void ssi_slave_init(DeviceState *dev, DeviceInfo *base_info) SSIBus *bus; bus = FROM_QBUS(SSIBus, qdev_get_parent_bus(dev)); - if (LIST_FIRST(&bus->qbus.children) != dev - || LIST_NEXT(dev, sibling) != NULL) { + if (TAILQ_FIRST(&bus->qbus.children) != dev + || TAILQ_NEXT(dev, sibling) != NULL) { hw_error("Too many devices on SSI bus"); } @@ -32,6 +32,7 @@ static void ssi_slave_init(DeviceState *dev, DeviceInfo *base_info) void ssi_register_slave(const char *name, int size, SSISlaveInfo *info) { assert(size >= sizeof(SSISlave)); + assert(!info->qdev.init); info->qdev.init = ssi_slave_init; info->qdev.bus_type = BUS_TYPE_SSI; qdev_register(name, size, &info->qdev); @@ -56,7 +57,7 @@ uint32_t ssi_transfer(SSIBus *bus, uint32_t val) { DeviceState *dev; SSISlave *slave; - dev = LIST_FIRST(&bus->qbus.children); + dev = TAILQ_FIRST(&bus->qbus.children); if (!dev) { return 0; } diff --git a/hw/syborg.c b/hw/syborg.c deleted file mode 100644 index 5ca9977..0000000 --- a/hw/syborg.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Syborg (Symbian Virtual Platform) reference board - * - * Copyright (c) 2009 CodeSourcery - * - * 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. - */ - -#include "sysbus.h" -#include "boards.h" -#include "arm-misc.h" -#include "sysemu.h" -#include "net.h" - -static struct arm_boot_info syborg_binfo; - -static void syborg_init(ram_addr_t ram_size, - const char *boot_device, - const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename, const char *cpu_model) -{ - CPUState *env; - qemu_irq *cpu_pic; - qemu_irq pic[64]; - ram_addr_t ram_addr; - DeviceState *dev; - int i; - - if (!cpu_model) - cpu_model = "cortex-a8"; - env = cpu_init(cpu_model); - if (!env) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - - /* RAM at address zero. */ - ram_addr = qemu_ram_alloc(ram_size); - cpu_register_physical_memory(0, ram_size, ram_addr | IO_MEM_RAM); - - cpu_pic = arm_pic_init_cpu(env); - dev = sysbus_create_simple("syborg,interrupt", 0xC0000000, - cpu_pic[ARM_PIC_CPU_IRQ]); - for (i = 0; i < 64; i++) { - pic[i] = qdev_get_gpio_in(dev, i); - } - - sysbus_create_simple("syborg,rtc", 0xC0001000, NULL); - - dev = qdev_create(NULL, "syborg,timer"); - qdev_set_prop_int(dev, "frequency", 1000000); - qdev_init(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0xC0002000); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, pic[1]); - - sysbus_create_simple("syborg,keyboard", 0xC0003000, pic[2]); - sysbus_create_simple("syborg,pointer", 0xC0004000, pic[3]); - sysbus_create_simple("syborg,framebuffer", 0xC0005000, pic[4]); - sysbus_create_simple("syborg,serial", 0xC0006000, pic[5]); - sysbus_create_simple("syborg,serial", 0xC0007000, pic[6]); - sysbus_create_simple("syborg,serial", 0xC0008000, pic[7]); - sysbus_create_simple("syborg,serial", 0xC0009000, pic[8]); - - if (nd_table[0].vlan) { - DeviceState *dev; - SysBusDevice *s; - - qemu_check_nic_model(&nd_table[0], "virtio"); - dev = qdev_create(NULL, "syborg,virtio-net"); - qdev_set_netdev(dev, &nd_table[0]); - qdev_init(dev); - s = sysbus_from_qdev(dev); - sysbus_mmio_map(s, 0, 0xc000c000); - sysbus_connect_irq(s, 0, pic[9]); - } - - syborg_binfo.ram_size = ram_size; - syborg_binfo.kernel_filename = kernel_filename; - syborg_binfo.kernel_cmdline = kernel_cmdline; - syborg_binfo.initrd_filename = initrd_filename; - syborg_binfo.board_id = 0; - arm_load_kernel(env, &syborg_binfo); -} - -static QEMUMachine syborg_machine = { - .name = "syborg", - .desc = "Syborg (Symbian Virtual Platform)", - .init = syborg_init, -}; - -static void syborg_machine_init(void) -{ - qemu_register_machine(&syborg_machine); -} - -machine_init(syborg_machine_init); diff --git a/hw/sysbus.c b/hw/sysbus.c index fbd2ddf..4903747 100644 --- a/hw/sysbus.c +++ b/hw/sysbus.c @@ -101,13 +101,16 @@ void sysbus_init_mmio_cb(SysBusDevice *dev, target_phys_addr_t size, static void sysbus_device_init(DeviceState *dev, DeviceInfo *base) { SysBusDeviceInfo *info = container_of(base, SysBusDeviceInfo, qdev); + SysBusDevice *s = sysbus_from_qdev(dev); - info->init(sysbus_from_qdev(dev)); + s->info = info; + info->init(s); } void sysbus_register_withprop(const char *name, size_t size, SysBusDeviceInfo *info) { + assert(!info->qdev.init); info->qdev.init = sysbus_device_init; info->qdev.bus_type = BUS_TYPE_SYSTEM; diff --git a/hw/sysbus.h b/hw/sysbus.h index 2973661..11fa8ad 100644 --- a/hw/sysbus.h +++ b/hw/sysbus.h @@ -11,8 +11,16 @@ typedef struct SysBusDevice SysBusDevice; typedef void (*mmio_mapfunc)(SysBusDevice *dev, target_phys_addr_t addr); +typedef void (*sysbus_initfn)(SysBusDevice *dev); + +typedef struct { + DeviceInfo qdev; + sysbus_initfn init; +} SysBusDeviceInfo; + struct SysBusDevice { DeviceState qdev; + SysBusDeviceInfo *info; int num_irq; qemu_irq irqs[QDEV_MAX_IRQ]; qemu_irq *irqp[QDEV_MAX_IRQ]; @@ -25,17 +33,10 @@ struct SysBusDevice { } mmio[QDEV_MAX_MMIO]; }; -typedef void (*sysbus_initfn)(SysBusDevice *dev); - /* Macros to compensate for lack of type inheritance in C. */ #define sysbus_from_qdev(dev) ((SysBusDevice *)(dev)) #define FROM_SYSBUS(type, dev) DO_UPCAST(type, busdev, dev) -typedef struct { - DeviceInfo qdev; - sysbus_initfn init; -} SysBusDeviceInfo; - void sysbus_register_dev(const char *name, size_t size, sysbus_initfn init); void sysbus_register_withprop(const char *name, size_t size, SysBusDeviceInfo *info); diff --git a/pc-bios/boards/syborg.dts b/pc-bios/boards/syborg.dts new file mode 100644 index 0000000..39745c7 --- /dev/null +++ b/pc-bios/boards/syborg.dts @@ -0,0 +1,134 @@ +/ { + #address-cells = <1>; + #size-cells = <1>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu0: ARM,Cortex-A8@0 { + device_type = "cpu"; + reg = <0>; + #interrupt-cells = <1>; + }; + }; + memory@0 { + device_type = "memory"; + reg = <0 08000000>; + }; + syborg { + #address-cells = <1>; + #size-cells = <0>; + qemu,fold-bus; + intc: intc@0 { + compatible = "syborg,interrupt"; + #interrupt-cells = <1>; + reg = ; + interrupt-controller; + interrupt-parent = <&cpu0>; + interrupts = <0>; + num-interrupts = <20>; + }; + rtc@0 { + compatible = "syborg,rtc"; + reg = ; + }; + timer@0 { + compatible = "syborg,timer"; + reg = ; + frequency = ; + interrupts = <1>; + interrupt-parent = <&intc>; + }; + keyboard@0 { + compatible = "syborg,keyboard"; + reg = ; + interrupts = <2>; + interrupt-parent = <&intc>; + }; + touchscreen@0 { + compatible = "syborg,pointer"; + reg = ; + interrupts = <3>; + interrupt-parent = <&intc>; + }; + framebuffer@0 { + compatible = "syborg,framebuffer"; + reg = ; + interrupts = <4>; + interrupt-parent = <&intc>; + }; + serial@0 { + device_type = "serial"; + compatible = "syborg,serial"; + chardev = "serial0"; + reg = ; + interrupts = <5>; + interrupt-parent = <&intc>; + }; + serial@1 { + device_type = "serial"; + compatible = "syborg,serial"; + chardev = "serial1"; + reg = ; + interrupts = <6>; + interrupt-parent = <&intc>; + }; + serial@2 { + device_type = "serial"; + compatible = "syborg,serial"; + chardev = "serial2"; + reg = ; + interrupts = <7>; + interrupt-parent = <&intc>; + }; + serial@3 { + device_type = "serial"; + compatible = "syborg,serial"; + chardev = "serial3"; + reg = ; + interrupts = <8>; + interrupt-parent = <&intc>; + }; +/* + hostfs@0 { + compatible = "syborg,hostfs"; + reg = ; + host-path = "\\svphostfs\\"; + drive-number = ; + }; + ss@0 { + compatible = "syborg,snapshot"; + reg = ; + }; +*/ + net@0 { + compatible = "syborg,virtio-net"; + reg = ; + interrupts = <9>; + interrupt-parent = <&intc>; + }; +/* + nand@0 { + compatible = "syborg,nand"; + reg = ; + size = <400>; + }; + audio@0 { + compatible = "syborg,virtio-audio"; + reg = ; + interrupts = ; + interrupt-parent = <&intc>; + }; + platform@0 { + compatible = "syborg,platform"; + reg = ; + }; +*/ + }; + chosen { + qemu { + bootstrap = "arm-linux"; + }; + }; +}; + diff --git a/rules.mak b/rules.mak index 8d6d96e..d332209 100644 --- a/rules.mak +++ b/rules.mak @@ -16,4 +16,7 @@ LINK = $(call quiet-command,$(CC) $(LDFLAGS) -o $@ $(1) $(ARLIBS_BEGIN) $(ARLIBS %.a: $(call quiet-command,rm -f $@ && $(AR) rcs $@ $^," AR $(TARGET_DIR)$@") +%.dtb: %.dts + $(call quiet-command,$(DTC) -O dtb -o $@ $<, " DTC $(TARGET_DIR)$@") + quiet-command = $(if $(V),$1,$(if $(2),@echo $2 && $1, @$1)) diff --git a/sysemu.h b/sysemu.h index 658aeec..ec2961c 100644 --- a/sysemu.h +++ b/sysemu.h @@ -13,6 +13,7 @@ extern const char *bios_name; #define QEMU_FILE_TYPE_BIOS 0 #define QEMU_FILE_TYPE_KEYMAP 1 +#define QEMU_FILE_TYPE_BOARD 2 char *qemu_find_file(int type, const char *name); extern int vm_running; @@ -274,4 +275,6 @@ int check_params(const char * const *params, const char *str); void register_devices(void); +void qdev_do_late_init(void); + #endif diff --git a/vl.c b/vl.c index f08f0f3..b5eb7e1 100644 --- a/vl.c +++ b/vl.c @@ -3477,11 +3477,21 @@ int qemu_register_machine(QEMUMachine *m) static QEMUMachine *find_machine(const char *name) { QEMUMachine *m; + char *filename; + char *buf; for(m = first_machine; m != NULL; m = m->next) { if (!strcmp(m->name, name)) return m; } + buf = qemu_mallocz(strlen(name) + 5); + sprintf(buf, "%s.dtb", name); + filename = qemu_find_file(QEMU_FILE_TYPE_BOARD, buf); + qemu_free(buf); + if (filename) { + dt_machine.name = filename; + return &dt_machine; + } return NULL; } @@ -4907,6 +4917,9 @@ char *qemu_find_file(int type, const char *name) case QEMU_FILE_TYPE_KEYMAP: subdir = "keymaps/"; break; + case QEMU_FILE_TYPE_BOARD: + subdir = "boards/"; + break; default: abort(); } @@ -4950,6 +4963,7 @@ int main(int argc, char **argv, char **envp) const char *loadvm = NULL; QEMUMachine *machine; const char *cpu_model; + const char *machine_name; const char *usb_devices[MAX_USB_CMDLINE]; int usb_devices_index; #ifndef _WIN32 @@ -5001,7 +5015,7 @@ int main(int argc, char **argv, char **envp) #endif module_call_init(MODULE_INIT_MACHINE); - machine = find_default_machine(); + machine_name = NULL; cpu_model = NULL; initrd_filename = NULL; ram_size = 0; @@ -5085,17 +5099,7 @@ int main(int argc, char **argv, char **envp) switch(popt->index) { case QEMU_OPTION_M: - machine = find_machine(optarg); - if (!machine) { - QEMUMachine *m; - printf("Supported machines are:\n"); - for(m = first_machine; m != NULL; m = m->next) { - printf("%-10s %s%s\n", - m->name, m->desc, - m->is_default ? " (default)" : ""); - } - exit(*optarg != '?'); - } + machine_name = optarg; break; case QEMU_OPTION_cpu: /* hw initialization will check this */ @@ -5703,6 +5707,22 @@ int main(int argc, char **argv, char **envp) } #endif + if (machine_name) { + machine = find_machine(machine_name); + if (!machine) { + QEMUMachine *m; + printf("Supported machines are:\n"); + for(m = first_machine; m != NULL; m = m->next) { + printf("%-10s %s%s\n", + m->name, m->desc, + m->is_default ? " (default)" : ""); + } + exit(*machine_name != '?'); + } + } else { + machine = find_default_machine(); + } + machine->max_cpus = machine->max_cpus ?: 1; /* Default to UP */ if (smp_cpus > machine->max_cpus) { fprintf(stderr, "Number of SMP cpus requested (%d), exceeds max cpus " @@ -6045,6 +6065,7 @@ int main(int argc, char **argv, char **envp) machine->init(ram_size, boot_devices, kernel_filename, kernel_cmdline, initrd_filename, cpu_model); + qdev_do_late_init(); for (env = first_cpu; env != NULL; env = env->next_cpu) { for (i = 0; i < nb_numa_nodes; i++) {