[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 2/4] Add device tree machine
From: |
Paul Brook |
Subject: |
[Qemu-devel] [PATCH 2/4] Add device tree machine |
Date: |
Wed, 10 Jun 2009 18:38:21 +0100 |
User-agent: |
StGIT/0.14.2 |
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 <address@hidden>
---
.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 <libfdt.h>
+
+/* 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,address@hidden {
+ device_type = "cpu";
+ reg = <0>;
+ #interrupt-cells = <1>;
+ };
+ };
+ address@hidden {
+ device_type = "memory";
+ reg = <0 08000000>;
+ };
+ syborg {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ qemu,fold-bus;
+ intc: address@hidden {
+ compatible = "syborg,interrupt";
+ #interrupt-cells = <1>;
+ reg = <c0000000>;
+ interrupt-controller;
+ interrupt-parent = <&cpu0>;
+ interrupts = <0>;
+ num-interrupts = <20>;
+ };
+ address@hidden {
+ compatible = "syborg,rtc";
+ reg = <c0001000>;
+ };
+ address@hidden {
+ compatible = "syborg,timer";
+ reg = <c0002000>;
+ frequency = <d#1000000>;
+ interrupts = <1>;
+ interrupt-parent = <&intc>;
+ };
+ address@hidden {
+ compatible = "syborg,keyboard";
+ reg = <c0003000>;
+ interrupts = <2>;
+ interrupt-parent = <&intc>;
+ };
+ address@hidden {
+ compatible = "syborg,pointer";
+ reg = <c0004000>;
+ interrupts = <3>;
+ interrupt-parent = <&intc>;
+ };
+ address@hidden {
+ compatible = "syborg,framebuffer";
+ reg = <c0005000>;
+ interrupts = <4>;
+ interrupt-parent = <&intc>;
+ };
+ address@hidden {
+ device_type = "serial";
+ compatible = "syborg,serial";
+ chardev = "serial0";
+ reg = <c0006000>;
+ interrupts = <5>;
+ interrupt-parent = <&intc>;
+ };
+ address@hidden {
+ device_type = "serial";
+ compatible = "syborg,serial";
+ chardev = "serial1";
+ reg = <c0007000>;
+ interrupts = <6>;
+ interrupt-parent = <&intc>;
+ };
+ address@hidden {
+ device_type = "serial";
+ compatible = "syborg,serial";
+ chardev = "serial2";
+ reg = <c0008000>;
+ interrupts = <7>;
+ interrupt-parent = <&intc>;
+ };
+ address@hidden {
+ device_type = "serial";
+ compatible = "syborg,serial";
+ chardev = "serial3";
+ reg = <c0009000>;
+ interrupts = <8>;
+ interrupt-parent = <&intc>;
+ };
+/*
+ address@hidden {
+ compatible = "syborg,hostfs";
+ reg = <c000a000>;
+ host-path = "\\svphostfs\\";
+ drive-number = <d#19>;
+ };
+ address@hidden {
+ compatible = "syborg,snapshot";
+ reg = <c000b000>;
+ };
+*/
+ address@hidden {
+ compatible = "syborg,virtio-net";
+ reg = <c000c000>;
+ interrupts = <9>;
+ interrupt-parent = <&intc>;
+ };
+/*
+ address@hidden {
+ compatible = "syborg,nand";
+ reg = <c000d000>;
+ size = <400>;
+ };
+ address@hidden {
+ compatible = "syborg,virtio-audio";
+ reg = <c000e000>;
+ interrupts = <a>;
+ interrupt-parent = <&intc>;
+ };
+ address@hidden {
+ compatible = "syborg,platform";
+ reg = <c1000000>;
+ };
+*/
+ };
+ 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++) {
[Qemu-devel] [PATCH 2/4] Add device tree machine,
Paul Brook <=
Re: [Qemu-devel] [PATCH 2/4] Add device tree machine, Markus Armbruster, 2009/06/12
[Qemu-devel] [PATCH 3/4] Stellaris machine config, Paul Brook, 2009/06/10