* [Qemu-devel] [PATCH 1/4] Include and build libfdt
2009-06-10 17:38 [Qemu-devel] [PATCH 0/4] Machine config files Paul Brook
@ 2009-06-10 17:38 ` Paul Brook
2009-06-10 19:08 ` Glauber Costa
2009-06-10 17:38 ` [Qemu-devel] [PATCH 2/4] Add device tree machine Paul Brook
` (5 subsequent siblings)
6 siblings, 1 reply; 61+ messages in thread
From: Paul Brook @ 2009-06-10 17:38 UTC (permalink / raw)
To: qemu-devel
Inlcude libfdt in source tree, and build it if not already available.
Signed-off-by: Paul Brook <paul@codesourcery.com>
---
Makefile | 17 +
Makefile.target | 14 -
configure | 24 -
hw/petalogix_s3adsp1800_mmu.c | 40 --
hw/ppc440_bamboo.c | 2
hw/ppce500_mpc8544ds.c | 4
libfdt/Makefile.libfdt | 14 +
libfdt/TODO | 3
libfdt/fdt.c | 156 ++++++
libfdt/fdt.h | 60 ++
libfdt/fdt_ro.c | 583 ++++++++++++++++++++++
libfdt/fdt_rw.c | 471 ++++++++++++++++++
libfdt/fdt_strerror.c | 96 ++++
libfdt/fdt_sw.c | 258 ++++++++++
libfdt/fdt_wip.c | 144 ++++++
libfdt/libfdt.h | 1070 +++++++++++++++++++++++++++++++++++++++++
libfdt/libfdt_env.h | 21 +
libfdt/libfdt_internal.h | 89 +++
libfdt/patch.libfdt | 20 +
target-ppc/kvm_ppc.c | 2
20 files changed, 3028 insertions(+), 60 deletions(-)
create mode 100644 libfdt/Makefile.libfdt
create mode 100644 libfdt/TODO
create mode 100644 libfdt/fdt.c
create mode 100644 libfdt/fdt.h
create mode 100644 libfdt/fdt_ro.c
create mode 100644 libfdt/fdt_rw.c
create mode 100644 libfdt/fdt_strerror.c
create mode 100644 libfdt/fdt_sw.c
create mode 100644 libfdt/fdt_wip.c
create mode 100644 libfdt/libfdt.h
create mode 100644 libfdt/libfdt_env.h
create mode 100644 libfdt/libfdt_internal.h
create mode 100644 libfdt/patch.libfdt
diff --git a/Makefile b/Makefile
index 3177616..6fc234c 100644
--- a/Makefile
+++ b/Makefile
@@ -57,12 +57,23 @@ SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
subdir-%:
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" all,)
-$(filter %-softmmu,$(SUBDIR_RULES)): libqemu_common.a
+$(filter %-softmmu,$(SUBDIR_RULES)): libqemu_common.a $(BUILD_LIBFDT)
$(filter %-user,$(SUBDIR_RULES)): libqemu_user.a
recurse-all: $(SUBDIR_RULES)
#######################################################################
+# Build libfdt
+ifdef BUILD_LIBFDT
+include $(SRC_PATH)/libfdt/Makefile.libfdt
+FDT_OBJS=$(addprefix libfdt/,$(LIBFDT_OBJS))
+
+$(FDT_OBJS): CFLAGS+=-I$(SRC_PATH)/libfdt
+
+libfdt/libfdt.a: $(FDT_OBJS)
+endif
+
+#######################################################################
# BLOCK_OBJS is code used by both qemu system emulation and qemu-img
BLOCK_OBJS=cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o
@@ -255,7 +266,9 @@ 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 slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d
+ for d in slirp audio block libfdt; do \
+ rm -f $$d/*.o $$d/*.d $$d/*.a ; \
+ done
rm -f qemu-img-cmds.h
$(MAKE) -C tests clean
for d in $(TARGET_DIRS) libhw32 libhw64; do \
diff --git a/Makefile.target b/Makefile.target
index 27de4b9..4e302c0 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -595,10 +595,6 @@ OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
OBJS+= ppc440.o ppc440_bamboo.o
# PowerPC E500 boards
OBJS+= ppce500_pci.o ppce500_mpc8544ds.o
-ifdef FDT_LIBS
-OBJS+= device_tree.o
-LIBS+= $(FDT_LIBS)
-endif
ifdef CONFIG_KVM
OBJS+= kvm_ppc.o
endif
@@ -624,10 +620,6 @@ OBJS+= xilinx_uartlite.o
OBJS+= xilinx_ethlite.o
OBJS+= pflash_cfi02.o
-ifdef FDT_LIBS
-OBJS+= device_tree.o
-LIBS+= $(FDT_LIBS)
-endif
endif
ifeq ($(TARGET_BASE_ARCH), cris)
# Boards
@@ -695,6 +687,12 @@ endif
ifdef CONFIG_SLIRP
CPPFLAGS+=-I$(SRC_PATH)/slirp
endif
+LIBS+=$(FDT_LIBS)
+OBJS+=device_tree.o
+ifdef BUILD_LIBFDT
+$(QEMU_PROG): $(FDT_LIBS)
+CPPFLAGS += -I$(SRC_PATH)/libfdt
+endif
LIBS+=$(PTHREADLIBS)
LIBS+=$(CLOCKLIBS)
diff --git a/configure b/configure
index 89e7f53..59ba8ef 100755
--- a/configure
+++ b/configure
@@ -194,7 +194,7 @@ kvm="yes"
kerneldir=""
aix="no"
blobs="yes"
-fdt="yes"
+build_fdt="yes"
sdl="yes"
sdl_x11="no"
xen="yes"
@@ -1218,15 +1218,13 @@ if $cc $ARCH_CFLAGS -o $TMPE $TMPC > /dev/null 2> /dev/null ; then
fi
##########################################
-# fdt probe
-if test "$fdt" = "yes" ; then
- fdt=no
- cat > $TMPC << EOF
+# System libfdt probe
+build_fdt=yes
+cat > $TMPC << EOF
int main(void) { return 0; }
EOF
- if $cc $ARCH_CFLAGS -o $TMPE ${OS_CFLAGS} $TMPC -lfdt 2> /dev/null > /dev/null ; then
- fdt=yes
- fi
+if $cc $ARCH_CFLAGS -o $TMPE ${OS_CFLAGS} $TMPC -lfdt 2> /dev/null > /dev/null ; then
+ build_fdt=no
fi
#
@@ -1380,7 +1378,7 @@ echo "AIO support $aio"
echo "IO thread $io_thread"
echo "Install blobs $blobs"
echo -e "KVM support $kvm"
-echo "fdt support $fdt"
+echo "Build libfdt $build_fdt"
echo "preadv support $preadv"
if test $sdl_too_old = "yes"; then
@@ -1712,8 +1710,10 @@ fi
if test "$preadv" = "yes" ; then
echo "#define HAVE_PREADV 1" >> $config_h
fi
-if test "$fdt" = "yes" ; then
- echo "#define HAVE_FDT 1" >> $config_h
+if test "$build_fdt" = "yes" ; then
+ echo "BUILD_LIBFDT=libfdt/libfdt.a" >> $config_mak
+ echo "FDT_LIBS=../libfdt/libfdt.a" >> $config_mak
+else
echo "FDT_LIBS=-lfdt" >> $config_mak
fi
@@ -2138,7 +2138,7 @@ done # for target in $targets
# build tree in object directory if source path is different from current one
if test "$source_path_used" = "yes" ; then
- DIRS="tests tests/cris slirp audio block"
+ DIRS="tests tests/cris slirp audio block libfdt"
FILES="Makefile tests/Makefile"
FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit"
FILES="$FILES tests/test-mmap.c"
diff --git a/hw/petalogix_s3adsp1800_mmu.c b/hw/petalogix_s3adsp1800_mmu.c
index 9ccd12b..25076b7 100644
--- a/hw/petalogix_s3adsp1800_mmu.c
+++ b/hw/petalogix_s3adsp1800_mmu.c
@@ -52,47 +52,27 @@ static int petalogix_load_device_tree(target_phys_addr_t addr,
target_phys_addr_t initrd_size,
const char *kernel_cmdline)
{
-#ifdef HAVE_FDT
void *fdt;
int r;
-#endif
char *path;
int fdt_size;
-#ifdef HAVE_FDT
- /* Try the local "mb.dtb" override. */
- fdt = load_device_tree("mb.dtb", &fdt_size);
+ path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
+ if (path) {
+ fdt = load_device_tree(path, &fdt_size);
+ qemu_free(path);
+ } else {
+ fdt = NULL;
+ }
if (!fdt) {
- path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
- if (path) {
- fdt = load_device_tree(path, &fdt_size);
- qemu_free(path);
- }
- if (!fdt)
- return 0;
+ return 0;
}
r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline);
- if (r < 0)
+ if (r < 0) {
fprintf(stderr, "couldn't set /chosen/bootargs\n");
- cpu_physical_memory_write (addr, (void *)fdt, fdt_size);
-#else
- /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob
- to the kernel. */
- fdt_size = load_image_targphys("mb.dtb", addr, 0x10000);
- if (fdt_size < 0) {
- path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
- if (path) {
- fdt_size = load_image_targphys(path, addr, 0x10000);
- qemu_free(path);
- }
}
-
- if (kernel_cmdline) {
- fprintf(stderr,
- "Warning: missing libfdt, cannot pass cmdline to kernel!\n");
- }
-#endif
+ cpu_physical_memory_write (addr, (void *)fdt, fdt_size);
return fdt_size;
}
diff --git a/hw/ppc440_bamboo.c b/hw/ppc440_bamboo.c
index 00aa2c7..5796570 100644
--- a/hw/ppc440_bamboo.c
+++ b/hw/ppc440_bamboo.c
@@ -32,7 +32,6 @@ static void *bamboo_load_device_tree(target_phys_addr_t addr,
const char *kernel_cmdline)
{
void *fdt = NULL;
-#ifdef HAVE_FDT
uint32_t mem_reg_property[] = { 0, 0, ramsize };
char *filename;
int fdt_size;
@@ -76,7 +75,6 @@ static void *bamboo_load_device_tree(target_phys_addr_t addr,
cpu_physical_memory_write (addr, (void *)fdt, fdt_size);
out:
-#endif
return fdt;
}
diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c
index d9ed36c..b1f1458 100644
--- a/hw/ppce500_mpc8544ds.c
+++ b/hw/ppce500_mpc8544ds.c
@@ -46,7 +46,6 @@
#define MPC8544_PCI_IO 0xE1000000
#define MPC8544_PCI_IOLEN 0x10000
-#ifdef HAVE_FDT
static int mpc8544_copy_soc_cell(void *fdt, const char *node, const char *prop)
{
uint32_t cell;
@@ -68,7 +67,6 @@ static int mpc8544_copy_soc_cell(void *fdt, const char *node, const char *prop)
out:
return ret;
}
-#endif
static void *mpc8544_load_device_tree(target_phys_addr_t addr,
uint32_t ramsize,
@@ -77,7 +75,6 @@ static void *mpc8544_load_device_tree(target_phys_addr_t addr,
const char *kernel_cmdline)
{
void *fdt = NULL;
-#ifdef HAVE_FDT
uint32_t mem_reg_property[] = {0, ramsize};
char *filename;
int fdt_size;
@@ -144,7 +141,6 @@ static void *mpc8544_load_device_tree(target_phys_addr_t addr,
cpu_physical_memory_write (addr, (void *)fdt, fdt_size);
out:
-#endif
return fdt;
}
diff --git a/libfdt/Makefile.libfdt b/libfdt/Makefile.libfdt
new file mode 100644
index 0000000..82f9c6a
--- /dev/null
+++ b/libfdt/Makefile.libfdt
@@ -0,0 +1,14 @@
+# Makefile.libfdt
+#
+# This is not a complete Makefile of itself. Instead, it is designed to
+# be easily embeddable into other systems of Makefiles.
+#
+LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c
+LIBFDT_INCLUDES = fdt.h libfdt.h
+LIBFDT_EXTRA = libfdt_internal.h
+LIBFDT_LIB = libfdt/libfdt.a
+
+LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o)
+
+$(LIBFDT_objdir)/$(LIBFDT_LIB): $(addprefix $(LIBFDT_objdir)/,$(LIBFDT_OBJS))
+
diff --git a/libfdt/TODO b/libfdt/TODO
new file mode 100644
index 0000000..288437e
--- /dev/null
+++ b/libfdt/TODO
@@ -0,0 +1,3 @@
+- Tree traversal functions
+- Graft function
+- Complete libfdt.h documenting comments
diff --git a/libfdt/fdt.c b/libfdt/fdt.c
new file mode 100644
index 0000000..586a361
--- /dev/null
+++ b/libfdt/fdt.c
@@ -0,0 +1,156 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+int fdt_check_header(const void *fdt)
+{
+ if (fdt_magic(fdt) == FDT_MAGIC) {
+ /* Complete tree */
+ if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
+ return -FDT_ERR_BADVERSION;
+ if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
+ return -FDT_ERR_BADVERSION;
+ } else if (fdt_magic(fdt) == SW_MAGIC) {
+ /* Unfinished sequential-write blob */
+ if (fdt_size_dt_struct(fdt) == 0)
+ return -FDT_ERR_BADSTATE;
+ } else {
+ return -FDT_ERR_BADMAGIC;
+ }
+
+ return 0;
+}
+
+const void *fdt_offset_ptr(const void *fdt, int offset, int len)
+{
+ const void *p;
+
+ if (fdt_version(fdt) >= 0x11)
+ if (((offset + len) < offset)
+ || ((offset + len) > fdt_size_dt_struct(fdt)))
+ return NULL;
+
+ p = _fdt_offset_ptr(fdt, offset);
+
+ if (p + len < p)
+ return NULL;
+ return p;
+}
+
+uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset)
+{
+ const uint32_t *tagp, *lenp;
+ uint32_t tag;
+ const char *p;
+
+ if (offset % FDT_TAGSIZE)
+ return -1;
+
+ tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
+ if (! tagp)
+ return FDT_END; /* premature end */
+ tag = fdt32_to_cpu(*tagp);
+ offset += FDT_TAGSIZE;
+
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ /* skip name */
+ do {
+ p = fdt_offset_ptr(fdt, offset++, 1);
+ } while (p && (*p != '\0'));
+ if (! p)
+ return FDT_END;
+ break;
+ case FDT_PROP:
+ lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
+ if (! lenp)
+ return FDT_END;
+ /* skip name offset, length and value */
+ offset += 2*FDT_TAGSIZE + fdt32_to_cpu(*lenp);
+ break;
+ }
+
+ if (nextoffset)
+ *nextoffset = ALIGN(offset, FDT_TAGSIZE);
+
+ return tag;
+}
+
+const char *_fdt_find_string(const char *strtab, int tabsize, const char *s)
+{
+ int len = strlen(s) + 1;
+ const char *last = strtab + tabsize - len;
+ const char *p;
+
+ for (p = strtab; p <= last; p++)
+ if (memeq(p, s, len))
+ return p;
+ return NULL;
+}
+
+int fdt_move(const void *fdt, void *buf, int bufsize)
+{
+ int err = fdt_check_header(fdt);
+
+ if (err)
+ return err;
+
+ if (fdt_totalsize(fdt) > bufsize)
+ return -FDT_ERR_NOSPACE;
+
+ memmove(buf, fdt, fdt_totalsize(fdt));
+ return 0;
+}
diff --git a/libfdt/fdt.h b/libfdt/fdt.h
new file mode 100644
index 0000000..48ccfd9
--- /dev/null
+++ b/libfdt/fdt.h
@@ -0,0 +1,60 @@
+#ifndef _FDT_H
+#define _FDT_H
+
+#ifndef __ASSEMBLY__
+
+struct fdt_header {
+ uint32_t magic; /* magic word FDT_MAGIC */
+ uint32_t totalsize; /* total size of DT block */
+ uint32_t off_dt_struct; /* offset to structure */
+ uint32_t off_dt_strings; /* offset to strings */
+ uint32_t off_mem_rsvmap; /* offset to memory reserve map */
+ uint32_t version; /* format version */
+ uint32_t last_comp_version; /* last compatible version */
+
+ /* version 2 fields below */
+ uint32_t boot_cpuid_phys; /* Which physical CPU id we're
+ booting on */
+ /* version 3 fields below */
+ uint32_t size_dt_strings; /* size of the strings block */
+
+ /* version 17 fields below */
+ uint32_t size_dt_struct; /* size of the structure block */
+};
+
+struct fdt_reserve_entry {
+ uint64_t address;
+ uint64_t size;
+};
+
+struct fdt_node_header {
+ uint32_t tag;
+ char name[0];
+};
+
+struct fdt_property {
+ uint32_t tag;
+ uint32_t len;
+ uint32_t nameoff;
+ char data[0];
+};
+
+#endif /* !__ASSEMBLY */
+
+#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
+#define FDT_TAGSIZE sizeof(uint32_t)
+
+#define FDT_BEGIN_NODE 0x1 /* Start node: full name */
+#define FDT_END_NODE 0x2 /* End node */
+#define FDT_PROP 0x3 /* Property: name off,
+ size, content */
+#define FDT_NOP 0x4 /* nop */
+#define FDT_END 0x9
+
+#define FDT_V1_SIZE (7*sizeof(uint32_t))
+#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(uint32_t))
+#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(uint32_t))
+#define FDT_V16_SIZE FDT_V3_SIZE
+#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(uint32_t))
+
+#endif /* _FDT_H */
diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c
new file mode 100644
index 0000000..afb00a2
--- /dev/null
+++ b/libfdt/fdt_ro.c
@@ -0,0 +1,583 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+#define CHECK_HEADER(fdt) \
+ { \
+ int err; \
+ if ((err = fdt_check_header(fdt)) != 0) \
+ return err; \
+ }
+
+static int nodename_eq(const void *fdt, int offset,
+ const char *s, int len)
+{
+ const char *p = fdt_offset_ptr(fdt, offset, len+1);
+
+ if (! p)
+ /* short match */
+ return 0;
+
+ if (memcmp(p, s, len) != 0)
+ return 0;
+
+ if (p[len] == '\0')
+ return 1;
+ else if (!memchr(s, '@', len) && (p[len] == '@'))
+ return 1;
+ else
+ return 0;
+}
+
+const char *fdt_string(const void *fdt, int stroffset)
+{
+ return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
+}
+
+int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
+{
+ CHECK_HEADER(fdt);
+ *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
+ *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
+ return 0;
+}
+
+int fdt_num_mem_rsv(const void *fdt)
+{
+ int i = 0;
+
+ while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
+ i++;
+ return i;
+}
+
+int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
+ const char *name, int namelen)
+{
+ int level = 0;
+ uint32_t tag;
+ int offset, nextoffset;
+
+ CHECK_HEADER(fdt);
+
+ tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
+ if (tag != FDT_BEGIN_NODE)
+ return -FDT_ERR_BADOFFSET;
+
+ do {
+ 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)
+ continue;
+ if (nodename_eq(fdt, offset+FDT_TAGSIZE, name, namelen))
+ /* Found it! */
+ return offset;
+ break;
+
+ case FDT_END_NODE:
+ level--;
+ break;
+
+ case FDT_PROP:
+ case FDT_NOP:
+ break;
+
+ default:
+ return -FDT_ERR_BADSTRUCTURE;
+ }
+ } while (level >= 0);
+
+ return -FDT_ERR_NOTFOUND;
+}
+
+int fdt_subnode_offset(const void *fdt, int parentoffset,
+ const char *name)
+{
+ return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
+}
+
+int fdt_path_offset(const void *fdt, const char *path)
+{
+ const char *end = path + strlen(path);
+ const char *p = path;
+ int offset = 0;
+
+ CHECK_HEADER(fdt);
+
+ if (*path != '/')
+ return -FDT_ERR_BADPATH;
+
+ while (*p) {
+ const char *q;
+
+ while (*p == '/')
+ p++;
+ if (! *p)
+ return offset;
+ q = strchr(p, '/');
+ if (! q)
+ q = end;
+
+ offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
+ if (offset < 0)
+ return offset;
+
+ p = q;
+ }
+
+ return offset;
+}
+
+const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
+{
+ const struct fdt_node_header *nh;
+ int err;
+
+ if ((err = fdt_check_header(fdt)) != 0)
+ goto fail;
+
+ err = -FDT_ERR_BADOFFSET;
+ nh = fdt_offset_ptr(fdt, nodeoffset, sizeof(*nh));
+ if (!nh || (fdt32_to_cpu(nh->tag) != FDT_BEGIN_NODE))
+ goto fail;
+
+ if (len)
+ *len = strlen(nh->name);
+
+ return nh->name;
+
+ fail:
+ if (len)
+ *len = err;
+ return NULL;
+}
+
+const struct fdt_property *fdt_get_property(const void *fdt,
+ int nodeoffset,
+ const char *name, int *lenp)
+{
+ uint32_t tag;
+ const struct fdt_property *prop;
+ int namestroff;
+ int offset, nextoffset;
+ int err;
+
+ if ((err = fdt_check_header(fdt)) != 0)
+ goto fail;
+
+ err = -FDT_ERR_BADOFFSET;
+ if (nodeoffset % FDT_TAGSIZE)
+ goto fail;
+
+ tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
+ if (tag != FDT_BEGIN_NODE)
+ goto fail;
+
+ do {
+ offset = nextoffset;
+
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ switch (tag) {
+ case FDT_END:
+ err = -FDT_ERR_TRUNCATED;
+ goto fail;
+
+ case FDT_BEGIN_NODE:
+ case FDT_END_NODE:
+ case FDT_NOP:
+ break;
+
+ case FDT_PROP:
+ err = -FDT_ERR_BADSTRUCTURE;
+ prop = fdt_offset_ptr(fdt, offset, sizeof(*prop));
+ if (! prop)
+ goto fail;
+ namestroff = fdt32_to_cpu(prop->nameoff);
+ if (streq(fdt_string(fdt, namestroff), name)) {
+ /* Found it! */
+ int len = fdt32_to_cpu(prop->len);
+ prop = fdt_offset_ptr(fdt, offset,
+ sizeof(*prop)+len);
+ if (! prop)
+ goto fail;
+
+ if (lenp)
+ *lenp = len;
+
+ return prop;
+ }
+ break;
+
+ default:
+ err = -FDT_ERR_BADSTRUCTURE;
+ goto fail;
+ }
+ } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
+
+ err = -FDT_ERR_NOTFOUND;
+ fail:
+ if (lenp)
+ *lenp = err;
+ return NULL;
+}
+
+const void *fdt_getprop(const void *fdt, int nodeoffset,
+ const char *name, int *lenp)
+{
+ const struct fdt_property *prop;
+
+ prop = fdt_get_property(fdt, nodeoffset, name, lenp);
+ if (! prop)
+ return NULL;
+
+ return prop->data;
+}
+
+uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
+{
+ const uint32_t *php;
+ int len;
+
+ php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
+ if (!php || (len != sizeof(*php)))
+ return 0;
+
+ return fdt32_to_cpu(*php);
+}
+
+int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
+{
+ uint32_t tag;
+ int p = 0, overflow = 0;
+ int offset, nextoffset, namelen;
+ const char *name;
+
+ CHECK_HEADER(fdt);
+
+ tag = fdt_next_tag(fdt, 0, &nextoffset);
+ if (tag != FDT_BEGIN_NODE)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ if (buflen < 2)
+ return -FDT_ERR_NOSPACE;
+ buf[0] = '/';
+ p = 1;
+
+ while (nextoffset <= nodeoffset) {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ switch (tag) {
+ case FDT_END:
+ return -FDT_ERR_BADOFFSET;
+
+ case FDT_BEGIN_NODE:
+ name = fdt_get_name(fdt, offset, &namelen);
+ if (!name)
+ return namelen;
+ if (overflow || ((p + namelen + 1) > buflen)) {
+ overflow++;
+ break;
+ }
+ memcpy(buf + p, name, namelen);
+ p += namelen;
+ buf[p++] = '/';
+ break;
+
+ case FDT_END_NODE:
+ if (overflow) {
+ overflow--;
+ break;
+ }
+ do {
+ p--;
+ } while (buf[p-1] != '/');
+ break;
+
+ case FDT_PROP:
+ case FDT_NOP:
+ break;
+
+ default:
+ return -FDT_ERR_BADSTRUCTURE;
+ }
+ }
+
+ if (overflow)
+ return -FDT_ERR_NOSPACE;
+
+ if (p > 1) /* special case so that root path is "/", not "" */
+ p--;
+ buf[p] = '\0';
+ return p;
+}
+
+int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
+ int supernodedepth, int *nodedepth)
+{
+ int level = -1;
+ uint32_t tag;
+ int offset, nextoffset = 0;
+ int supernodeoffset = -FDT_ERR_INTERNAL;
+
+ CHECK_HEADER(fdt);
+
+ if (supernodedepth < 0)
+ return -FDT_ERR_NOTFOUND;
+
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ switch (tag) {
+ case FDT_END:
+ return -FDT_ERR_BADOFFSET;
+
+ case FDT_BEGIN_NODE:
+ level++;
+ if (level == supernodedepth)
+ supernodeoffset = offset;
+ break;
+
+ case FDT_END_NODE:
+ level--;
+ break;
+
+ case FDT_PROP:
+ case FDT_NOP:
+ break;
+
+ default:
+ return -FDT_ERR_BADSTRUCTURE;
+ }
+ } while (offset < nodeoffset);
+
+ if (nodedepth)
+ *nodedepth = level;
+
+ if (supernodedepth > level)
+ return -FDT_ERR_NOTFOUND;
+ return supernodeoffset;
+}
+
+int fdt_node_depth(const void *fdt, int nodeoffset)
+{
+ int nodedepth;
+ int err;
+
+ err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
+ if (err)
+ return (err < 0) ? err : -FDT_ERR_INTERNAL;
+ return nodedepth;
+}
+
+int fdt_parent_offset(const void *fdt, int nodeoffset)
+{
+ int nodedepth = fdt_node_depth(fdt, nodeoffset);
+
+ if (nodedepth < 0)
+ return nodedepth;
+ return fdt_supernode_atdepth_offset(fdt, nodeoffset,
+ nodedepth - 1, NULL);
+}
+
+int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
+ const char *propname,
+ const void *propval, int proplen)
+{
+ uint32_t tag;
+ int offset, nextoffset;
+ const void *val;
+ int len;
+
+ CHECK_HEADER(fdt);
+
+ if (startoffset >= 0) {
+ tag = fdt_next_tag(fdt, startoffset, &nextoffset);
+ if (tag != FDT_BEGIN_NODE)
+ return -FDT_ERR_BADOFFSET;
+ } else {
+ nextoffset = 0;
+ }
+
+ /* FIXME: The algorithm here is pretty horrible: we scan each
+ * property of a node in fdt_getprop(), then if that didn't
+ * find what we want, we scan over them again making our way
+ * to the next node. Still it's the easiest to implement
+ * approach; performance can come later. */
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ val = fdt_getprop(fdt, offset, propname, &len);
+ if (val
+ && (len == proplen)
+ && (memcmp(val, propval, len) == 0))
+ return offset;
+ break;
+
+ case FDT_PROP:
+ case FDT_END:
+ case FDT_END_NODE:
+ case FDT_NOP:
+ break;
+
+ default:
+ return -FDT_ERR_BADSTRUCTURE;
+ }
+ } while (tag != FDT_END);
+
+ return -FDT_ERR_NOTFOUND;
+}
+
+int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
+{
+ if ((phandle == 0) || (phandle == -1))
+ return -FDT_ERR_BADPHANDLE;
+ phandle = cpu_to_fdt32(phandle);
+ return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle",
+ &phandle, sizeof(phandle));
+}
+
+static int _stringlist_contains(const void *strlist, int listlen, const char *str)
+{
+ int len = strlen(str);
+ const void *p;
+
+ while (listlen >= len) {
+ if (memcmp(str, strlist, len+1) == 0)
+ return 1;
+ p = memchr(strlist, '\0', listlen);
+ if (!p)
+ return 0; /* malformed strlist.. */
+ listlen -= (p-strlist) + 1;
+ strlist = p + 1;
+ }
+ return 0;
+}
+
+int fdt_node_check_compatible(const void *fdt, int nodeoffset,
+ const char *compatible)
+{
+ const void *prop;
+ int len;
+
+ prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
+ if (!prop)
+ return len;
+ if (_stringlist_contains(prop, len, compatible))
+ return 0;
+ else
+ return 1;
+}
+
+int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
+ const char *compatible)
+{
+ uint32_t tag;
+ int offset, nextoffset;
+ int err;
+
+ CHECK_HEADER(fdt);
+
+ if (startoffset >= 0) {
+ tag = fdt_next_tag(fdt, startoffset, &nextoffset);
+ if (tag != FDT_BEGIN_NODE)
+ return -FDT_ERR_BADOFFSET;
+ } else {
+ nextoffset = 0;
+ }
+
+ /* FIXME: The algorithm here is pretty horrible: we scan each
+ * property of a node in fdt_node_check_compatible(), then if
+ * that didn't find what we want, we scan over them again
+ * making our way to the next node. Still it's the easiest to
+ * implement approach; performance can come later. */
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ err = fdt_node_check_compatible(fdt, offset,
+ compatible);
+ if ((err < 0)
+ && (err != -FDT_ERR_NOTFOUND))
+ return err;
+ else if (err == 0)
+ return offset;
+ break;
+
+ case FDT_PROP:
+ case FDT_END:
+ case FDT_END_NODE:
+ case FDT_NOP:
+ break;
+
+ default:
+ return -FDT_ERR_BADSTRUCTURE;
+ }
+ } while (tag != FDT_END);
+
+ return -FDT_ERR_NOTFOUND;
+}
diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c
new file mode 100644
index 0000000..a1c70ff
--- /dev/null
+++ b/libfdt/fdt_rw.c
@@ -0,0 +1,471 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+static int _blocks_misordered(const void *fdt,
+ int mem_rsv_size, int struct_size)
+{
+ return (fdt_off_mem_rsvmap(fdt) < ALIGN(sizeof(struct fdt_header), 8))
+ || (fdt_off_dt_struct(fdt) <
+ (fdt_off_mem_rsvmap(fdt) + mem_rsv_size))
+ || (fdt_off_dt_strings(fdt) <
+ (fdt_off_dt_struct(fdt) + struct_size))
+ || (fdt_totalsize(fdt) <
+ (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
+}
+
+static int rw_check_header(void *fdt)
+{
+ int err;
+
+ if ((err = fdt_check_header(fdt)))
+ return err;
+ if (fdt_version(fdt) < 17)
+ return -FDT_ERR_BADVERSION;
+ if (_blocks_misordered(fdt, sizeof(struct fdt_reserve_entry),
+ fdt_size_dt_struct(fdt)))
+ return -FDT_ERR_BADLAYOUT;
+ if (fdt_version(fdt) > 17)
+ fdt_set_version(fdt, 17);
+
+ return 0;
+}
+
+#define RW_CHECK_HEADER(fdt) \
+ { \
+ int err; \
+ if ((err = rw_check_header(fdt)) != 0) \
+ return err; \
+ }
+
+static inline int _blob_data_size(void *fdt)
+{
+ return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
+}
+
+static int _blob_splice(void *fdt, void *p, int oldlen, int newlen)
+{
+ void *end = fdt + _blob_data_size(fdt);
+
+ if (((p + oldlen) < p) || ((p + oldlen) > end))
+ return -FDT_ERR_BADOFFSET;
+ if ((end - oldlen + newlen) > (fdt + fdt_totalsize(fdt)))
+ return -FDT_ERR_NOSPACE;
+ memmove(p + newlen, p + oldlen, end - p - oldlen);
+ return 0;
+}
+
+static int _blob_splice_mem_rsv(void *fdt, struct fdt_reserve_entry *p,
+ int oldn, int newn)
+{
+ int delta = (newn - oldn) * sizeof(*p);
+ int err;
+ err = _blob_splice(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));
+ if (err)
+ return err;
+ fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);
+ fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
+ return 0;
+}
+
+static int _blob_splice_struct(void *fdt, void *p,
+ int oldlen, int newlen)
+{
+ int delta = newlen - oldlen;
+ int err;
+
+ if ((err = _blob_splice(fdt, p, oldlen, newlen)))
+ return err;
+
+ fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);
+ fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
+ return 0;
+}
+
+static int _blob_splice_string(void *fdt, int newlen)
+{
+ void *p = fdt + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
+ int err;
+
+ if ((err = _blob_splice(fdt, p, 0, newlen)))
+ return err;
+
+ fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
+ return 0;
+}
+
+static int _find_add_string(void *fdt, const char *s)
+{
+ char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
+ const char *p;
+ char *new;
+ int len = strlen(s) + 1;
+ int err;
+
+ p = _fdt_find_string(strtab, fdt_size_dt_strings(fdt), s);
+ if (p)
+ /* found it */
+ return (p - strtab);
+
+ new = strtab + fdt_size_dt_strings(fdt);
+ err = _blob_splice_string(fdt, len);
+ if (err)
+ return err;
+
+ memcpy(new, s, len);
+ return (new - strtab);
+}
+
+int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
+{
+ struct fdt_reserve_entry *re;
+ int err;
+
+ if ((err = rw_check_header(fdt)))
+ return err;
+
+ re = _fdt_mem_rsv_w(fdt, fdt_num_mem_rsv(fdt));
+ err = _blob_splice_mem_rsv(fdt, re, 0, 1);
+ if (err)
+ return err;
+
+ re->address = cpu_to_fdt64(address);
+ re->size = cpu_to_fdt64(size);
+ return 0;
+}
+
+int fdt_del_mem_rsv(void *fdt, int n)
+{
+ struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n);
+ int err;
+
+ if ((err = rw_check_header(fdt)))
+ return err;
+ if (n >= fdt_num_mem_rsv(fdt))
+ return -FDT_ERR_NOTFOUND;
+
+ err = _blob_splice_mem_rsv(fdt, re, 1, 0);
+ if (err)
+ return err;
+ return 0;
+}
+
+static int _resize_property(void *fdt, int nodeoffset, const char *name, int len,
+ struct fdt_property **prop)
+{
+ int oldlen;
+ int err;
+
+ *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
+ if (! (*prop))
+ return oldlen;
+
+ if ((err = _blob_splice_struct(fdt, (*prop)->data,
+ ALIGN(oldlen, FDT_TAGSIZE),
+ ALIGN(len, FDT_TAGSIZE))))
+ return err;
+
+ (*prop)->len = cpu_to_fdt32(len);
+ return 0;
+}
+
+static int _add_property(void *fdt, int nodeoffset, const char *name, int len,
+ struct fdt_property **prop)
+{
+ uint32_t tag;
+ int proplen;
+ int nextoffset;
+ int namestroff;
+ int err;
+
+ tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
+ if (tag != FDT_BEGIN_NODE)
+ return -FDT_ERR_BADOFFSET;
+
+ namestroff = _find_add_string(fdt, name);
+ if (namestroff < 0)
+ return namestroff;
+
+ *prop = _fdt_offset_ptr_w(fdt, nextoffset);
+ proplen = sizeof(**prop) + ALIGN(len, FDT_TAGSIZE);
+
+ err = _blob_splice_struct(fdt, *prop, 0, proplen);
+ if (err)
+ return err;
+
+ (*prop)->tag = cpu_to_fdt32(FDT_PROP);
+ (*prop)->nameoff = cpu_to_fdt32(namestroff);
+ (*prop)->len = cpu_to_fdt32(len);
+ return 0;
+}
+
+int fdt_set_name(void *fdt, int nodeoffset, const char *name)
+{
+ char *namep;
+ int oldlen, newlen;
+ int err;
+
+ if ((err = rw_check_header(fdt)))
+ return err;
+
+ namep = (char *)fdt_get_name(fdt, nodeoffset, &oldlen);
+ if (!namep)
+ return oldlen;
+
+ newlen = strlen(name);
+
+ err = _blob_splice_struct(fdt, namep, ALIGN(oldlen+1, FDT_TAGSIZE),
+ ALIGN(newlen+1, FDT_TAGSIZE));
+ if (err)
+ return err;
+
+ memcpy(namep, name, newlen+1);
+ return 0;
+}
+
+int fdt_setprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ struct fdt_property *prop;
+ int err;
+
+ if ((err = rw_check_header(fdt)))
+ return err;
+
+ err = _resize_property(fdt, nodeoffset, name, len, &prop);
+ if (err == -FDT_ERR_NOTFOUND)
+ err = _add_property(fdt, nodeoffset, name, len, &prop);
+ if (err)
+ return err;
+
+ memcpy(prop->data, val, len);
+ return 0;
+}
+
+int fdt_delprop(void *fdt, int nodeoffset, const char *name)
+{
+ struct fdt_property *prop;
+ int len, proplen;
+
+ RW_CHECK_HEADER(fdt);
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
+ if (! prop)
+ return len;
+
+ proplen = sizeof(*prop) + ALIGN(len, FDT_TAGSIZE);
+ return _blob_splice_struct(fdt, prop, proplen, 0);
+}
+
+int fdt_add_subnode_namelen(void *fdt, int parentoffset,
+ const char *name, int namelen)
+{
+ struct fdt_node_header *nh;
+ int offset, nextoffset;
+ int nodelen;
+ int err;
+ uint32_t tag;
+ uint32_t *endtag;
+
+ RW_CHECK_HEADER(fdt);
+
+ offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
+ if (offset >= 0)
+ return -FDT_ERR_EXISTS;
+ else if (offset != -FDT_ERR_NOTFOUND)
+ return offset;
+
+ /* Try to place the new node after the parent's properties */
+ fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ } while (tag == FDT_PROP);
+
+ nh = _fdt_offset_ptr_w(fdt, offset);
+ nodelen = sizeof(*nh) + ALIGN(namelen+1, FDT_TAGSIZE) + FDT_TAGSIZE;
+
+ err = _blob_splice_struct(fdt, nh, 0, nodelen);
+ if (err)
+ return err;
+
+ nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
+ memset(nh->name, 0, ALIGN(namelen+1, FDT_TAGSIZE));
+ memcpy(nh->name, name, namelen);
+ endtag = (uint32_t *)((void *)nh + nodelen - FDT_TAGSIZE);
+ *endtag = cpu_to_fdt32(FDT_END_NODE);
+
+ return offset;
+}
+
+int fdt_add_subnode(void *fdt, int parentoffset, const char *name)
+{
+ return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));
+}
+
+int fdt_del_node(void *fdt, int nodeoffset)
+{
+ int endoffset;
+
+ RW_CHECK_HEADER(fdt);
+
+ endoffset = _fdt_node_end_offset(fdt, nodeoffset);
+ if (endoffset < 0)
+ return endoffset;
+
+ return _blob_splice_struct(fdt, _fdt_offset_ptr_w(fdt, nodeoffset),
+ endoffset - nodeoffset, 0);
+}
+
+static void _packblocks(const void *fdt, void *buf,
+ int mem_rsv_size, int struct_size)
+{
+ int mem_rsv_off, struct_off, strings_off;
+
+ mem_rsv_off = ALIGN(sizeof(struct fdt_header), 8);
+ struct_off = mem_rsv_off + mem_rsv_size;
+ strings_off = struct_off + struct_size;
+
+ memmove(buf + mem_rsv_off, fdt + fdt_off_mem_rsvmap(fdt), mem_rsv_size);
+ fdt_set_off_mem_rsvmap(buf, mem_rsv_off);
+
+ memmove(buf + struct_off, fdt + fdt_off_dt_struct(fdt), struct_size);
+ fdt_set_off_dt_struct(buf, struct_off);
+ fdt_set_size_dt_struct(buf, struct_size);
+
+ memmove(buf + strings_off, fdt + fdt_off_dt_strings(fdt),
+ fdt_size_dt_strings(fdt));
+ fdt_set_off_dt_strings(buf, strings_off);
+ fdt_set_size_dt_strings(buf, fdt_size_dt_strings(fdt));
+}
+
+int fdt_open_into(const void *fdt, void *buf, int bufsize)
+{
+ int err;
+ int mem_rsv_size, struct_size;
+ int newsize;
+ void *tmp;
+
+ err = fdt_check_header(fdt);
+ if (err)
+ return err;
+
+ mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
+ * sizeof(struct fdt_reserve_entry);
+
+ if (fdt_version(fdt) >= 17) {
+ struct_size = fdt_size_dt_struct(fdt);
+ } else {
+ struct_size = 0;
+ while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
+ ;
+ }
+
+ if (!_blocks_misordered(fdt, mem_rsv_size, struct_size)) {
+ /* no further work necessary */
+ err = fdt_move(fdt, buf, bufsize);
+ if (err)
+ return err;
+ fdt_set_version(buf, 17);
+ fdt_set_size_dt_struct(buf, struct_size);
+ fdt_set_totalsize(buf, bufsize);
+ return 0;
+ }
+
+ /* Need to reorder */
+ newsize = ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
+ + struct_size + fdt_size_dt_strings(fdt);
+
+ if (bufsize < newsize)
+ return -FDT_ERR_NOSPACE;
+
+ if (((buf + newsize) <= fdt)
+ || (buf >= (fdt + fdt_totalsize(fdt)))) {
+ tmp = buf;
+ } else {
+ tmp = (void *)fdt + fdt_totalsize(fdt);
+ if ((tmp + newsize) > (buf + bufsize))
+ return -FDT_ERR_NOSPACE;
+ }
+
+ _packblocks(fdt, tmp, mem_rsv_size, struct_size);
+ memmove(buf, tmp, newsize);
+
+ fdt_set_magic(buf, FDT_MAGIC);
+ fdt_set_totalsize(buf, bufsize);
+ fdt_set_version(buf, 17);
+ fdt_set_last_comp_version(buf, 16);
+ fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
+
+ return 0;
+}
+
+int fdt_pack(void *fdt)
+{
+ int mem_rsv_size;
+ int err;
+
+ err = rw_check_header(fdt);
+ if (err)
+ return err;
+
+ mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
+ * sizeof(struct fdt_reserve_entry);
+ _packblocks(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt));
+ fdt_set_totalsize(fdt, _blob_data_size(fdt));
+
+ return 0;
+}
diff --git a/libfdt/fdt_strerror.c b/libfdt/fdt_strerror.c
new file mode 100644
index 0000000..f9d32ef
--- /dev/null
+++ b/libfdt/fdt_strerror.c
@@ -0,0 +1,96 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+struct errtabent {
+ const char *str;
+};
+
+#define ERRTABENT(val) \
+ [(val)] = { .str = #val, }
+
+static struct errtabent errtable[] = {
+ ERRTABENT(FDT_ERR_NOTFOUND),
+ ERRTABENT(FDT_ERR_EXISTS),
+ ERRTABENT(FDT_ERR_NOSPACE),
+
+ ERRTABENT(FDT_ERR_BADOFFSET),
+ ERRTABENT(FDT_ERR_BADPATH),
+ ERRTABENT(FDT_ERR_BADSTATE),
+
+ ERRTABENT(FDT_ERR_TRUNCATED),
+ ERRTABENT(FDT_ERR_BADMAGIC),
+ ERRTABENT(FDT_ERR_BADVERSION),
+ ERRTABENT(FDT_ERR_BADSTRUCTURE),
+ ERRTABENT(FDT_ERR_BADLAYOUT),
+};
+#define ERRTABSIZE (sizeof(errtable) / sizeof(errtable[0]))
+
+const char *fdt_strerror(int errval)
+{
+ if (errval > 0)
+ return "<valid offset/length>";
+ else if (errval == 0)
+ return "<no error>";
+ else if (errval > -ERRTABSIZE) {
+ const char *s = errtable[-errval].str;
+
+ if (s)
+ return s;
+ }
+
+ return "<unknown error>";
+}
diff --git a/libfdt/fdt_sw.c b/libfdt/fdt_sw.c
new file mode 100644
index 0000000..dda2de3
--- /dev/null
+++ b/libfdt/fdt_sw.c
@@ -0,0 +1,258 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+static int check_header_sw(void *fdt)
+{
+ if (fdt_magic(fdt) != SW_MAGIC)
+ return -FDT_ERR_BADMAGIC;
+ return 0;
+}
+
+static void *grab_space(void *fdt, int len)
+{
+ int offset = fdt_size_dt_struct(fdt);
+ int spaceleft;
+
+ spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
+ - fdt_size_dt_strings(fdt);
+
+ if ((offset + len < offset) || (offset + len > spaceleft))
+ return NULL;
+
+ fdt_set_size_dt_struct(fdt, offset + len);
+ return fdt_offset_ptr_w(fdt, offset, len);
+}
+
+int fdt_create(void *buf, int bufsize)
+{
+ void *fdt = buf;
+
+ if (bufsize < sizeof(struct fdt_header))
+ return -FDT_ERR_NOSPACE;
+
+ memset(buf, 0, bufsize);
+
+ fdt_set_magic(fdt, SW_MAGIC);
+ fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
+ fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
+ fdt_set_totalsize(fdt, bufsize);
+
+ fdt_set_off_mem_rsvmap(fdt, ALIGN(sizeof(struct fdt_header),
+ sizeof(struct fdt_reserve_entry)));
+ fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
+ fdt_set_off_dt_strings(fdt, bufsize);
+
+ return 0;
+}
+
+int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
+{
+ struct fdt_reserve_entry *re;
+ int err = check_header_sw(fdt);
+ int offset;
+
+ if (err)
+ return err;
+ if (fdt_size_dt_struct(fdt))
+ return -FDT_ERR_BADSTATE;
+
+ offset = fdt_off_dt_struct(fdt);
+ if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
+ return -FDT_ERR_NOSPACE;
+
+ re = (struct fdt_reserve_entry *)(fdt + offset);
+ re->address = cpu_to_fdt64(addr);
+ re->size = cpu_to_fdt64(size);
+
+ fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
+
+ return 0;
+}
+
+int fdt_finish_reservemap(void *fdt)
+{
+ return fdt_add_reservemap_entry(fdt, 0, 0);
+}
+
+int fdt_begin_node(void *fdt, const char *name)
+{
+ struct fdt_node_header *nh;
+ int err = check_header_sw(fdt);
+ int namelen = strlen(name) + 1;
+
+ if (err)
+ return err;
+
+ nh = grab_space(fdt, sizeof(*nh) + ALIGN(namelen, FDT_TAGSIZE));
+ if (! nh)
+ return -FDT_ERR_NOSPACE;
+
+ nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
+ memcpy(nh->name, name, namelen);
+ return 0;
+}
+
+int fdt_end_node(void *fdt)
+{
+ uint32_t *en;
+ int err = check_header_sw(fdt);
+
+ if (err)
+ return err;
+
+ en = grab_space(fdt, FDT_TAGSIZE);
+ if (! en)
+ return -FDT_ERR_NOSPACE;
+
+ *en = cpu_to_fdt32(FDT_END_NODE);
+ return 0;
+}
+
+static int find_add_string(void *fdt, const char *s)
+{
+ char *strtab = (char *)fdt + fdt_totalsize(fdt);
+ const char *p;
+ int strtabsize = fdt_size_dt_strings(fdt);
+ int len = strlen(s) + 1;
+ int struct_top, offset;
+
+ p = _fdt_find_string(strtab - strtabsize, strtabsize, s);
+ if (p)
+ return p - strtab;
+
+ /* Add it */
+ offset = -strtabsize - len;
+ struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
+ if (fdt_totalsize(fdt) + offset < struct_top)
+ return 0; /* no more room :( */
+
+ memcpy(strtab + offset, s, len);
+ fdt_set_size_dt_strings(fdt, strtabsize + len);
+ return offset;
+}
+
+int fdt_property(void *fdt, const char *name, const void *val, int len)
+{
+ struct fdt_property *prop;
+ int err = check_header_sw(fdt);
+ int nameoff;
+
+ if (err)
+ return err;
+
+ nameoff = find_add_string(fdt, name);
+ if (nameoff == 0)
+ return -FDT_ERR_NOSPACE;
+
+ prop = grab_space(fdt, sizeof(*prop) + ALIGN(len, FDT_TAGSIZE));
+ if (! prop)
+ return -FDT_ERR_NOSPACE;
+
+ prop->tag = cpu_to_fdt32(FDT_PROP);
+ prop->nameoff = cpu_to_fdt32(nameoff);
+ prop->len = cpu_to_fdt32(len);
+ memcpy(prop->data, val, len);
+ return 0;
+}
+
+int fdt_finish(void *fdt)
+{
+ int err = check_header_sw(fdt);
+ char *p = (char *)fdt;
+ uint32_t *end;
+ int oldstroffset, newstroffset;
+ uint32_t tag;
+ int offset, nextoffset;
+
+ if (err)
+ return err;
+
+ /* Add terminator */
+ end = grab_space(fdt, sizeof(*end));
+ if (! end)
+ return -FDT_ERR_NOSPACE;
+ *end = cpu_to_fdt32(FDT_END);
+
+ /* Relocate the string table */
+ oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
+ newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
+ memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
+ fdt_set_off_dt_strings(fdt, newstroffset);
+
+ /* Walk the structure, correcting string offsets */
+ offset = 0;
+ while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
+ if (tag == FDT_PROP) {
+ struct fdt_property *prop =
+ fdt_offset_ptr_w(fdt, offset, sizeof(*prop));
+ int nameoff;
+
+ if (! prop)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ nameoff = fdt32_to_cpu(prop->nameoff);
+ nameoff += fdt_size_dt_strings(fdt);
+ prop->nameoff = cpu_to_fdt32(nameoff);
+ }
+ offset = nextoffset;
+ }
+
+ /* Finally, adjust the header */
+ fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
+ fdt_set_magic(fdt, FDT_MAGIC);
+ return 0;
+}
diff --git a/libfdt/fdt_wip.c b/libfdt/fdt_wip.c
new file mode 100644
index 0000000..88e24b8
--- /dev/null
+++ b/libfdt/fdt_wip.c
@@ -0,0 +1,144 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ void *propval;
+ int proplen;
+
+ propval = fdt_getprop_w(fdt, nodeoffset, name, &proplen);
+ if (! propval)
+ return proplen;
+
+ if (proplen != len)
+ return -FDT_ERR_NOSPACE;
+
+ memcpy(propval, val, len);
+ return 0;
+}
+
+static void nop_region(void *start, int len)
+{
+ uint32_t *p;
+
+ for (p = start; (void *)p < (start + len); p++)
+ *p = cpu_to_fdt32(FDT_NOP);
+}
+
+int fdt_nop_property(void *fdt, int nodeoffset, const char *name)
+{
+ struct fdt_property *prop;
+ int len;
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
+ if (! prop)
+ return len;
+
+ nop_region(prop, len + sizeof(*prop));
+
+ return 0;
+}
+
+int _fdt_node_end_offset(void *fdt, int nodeoffset)
+{
+ int level = 0;
+ uint32_t tag;
+ int offset, nextoffset;
+
+ tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
+ if (tag != FDT_BEGIN_NODE)
+ return -FDT_ERR_BADOFFSET;
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_END:
+ return offset;
+
+ case FDT_BEGIN_NODE:
+ level++;
+ break;
+
+ case FDT_END_NODE:
+ level--;
+ break;
+
+ case FDT_PROP:
+ case FDT_NOP:
+ break;
+
+ default:
+ return -FDT_ERR_BADSTRUCTURE;
+ }
+ } while (level >= 0);
+
+ return nextoffset;
+}
+
+int fdt_nop_node(void *fdt, int nodeoffset)
+{
+ int endoffset;
+
+ endoffset = _fdt_node_end_offset(fdt, nodeoffset);
+ if (endoffset < 0)
+ return endoffset;
+
+ nop_region(fdt_offset_ptr_w(fdt, nodeoffset, 0), endoffset - nodeoffset);
+ return 0;
+}
diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h
new file mode 100644
index 0000000..4b98d40
--- /dev/null
+++ b/libfdt/libfdt.h
@@ -0,0 +1,1070 @@
+#ifndef _LIBFDT_H
+#define _LIBFDT_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "libfdt_env.h"
+#include "fdt.h"
+
+#define FDT_FIRST_SUPPORTED_VERSION 0x10
+#define FDT_LAST_SUPPORTED_VERSION 0x11
+
+/* Error codes: informative error codes */
+#define FDT_ERR_NOTFOUND 1
+ /* FDT_ERR_NOTFOUND: The requested node or property does not exist */
+#define FDT_ERR_EXISTS 2
+ /* FDT_ERR_EXISTS: Attemped to create a node or property which
+ * already exists */
+#define FDT_ERR_NOSPACE 3
+ /* FDT_ERR_NOSPACE: Operation needed to expand the device
+ * tree, but its buffer did not have sufficient space to
+ * contain the expanded tree. Use fdt_open_into() to move the
+ * device tree to a buffer with more space. */
+
+/* Error codes: codes for bad parameters */
+#define FDT_ERR_BADOFFSET 4
+ /* FDT_ERR_BADOFFSET: Function was passed a structure block
+ * offset which is out-of-bounds, or which points to an
+ * unsuitable part of the structure for the operation. */
+#define FDT_ERR_BADPATH 5
+ /* FDT_ERR_BADPATH: Function was passed a badly formatted path
+ * (e.g. missing a leading / for a function which requires an
+ * absolute path) */
+#define FDT_ERR_BADPHANDLE 6
+ /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle
+ * value. phandle values of 0 and -1 are not permitted. */
+#define FDT_ERR_BADSTATE 7
+ /* FDT_ERR_BADSTATE: Function was passed an incomplete device
+ * tree created by the sequential-write functions, which is
+ * not sufficiently complete for the requested operation. */
+
+/* Error codes: codes for bad device tree blobs */
+#define FDT_ERR_TRUNCATED 8
+ /* FDT_ERR_TRUNCATED: Structure block of the given device tree
+ * ends without an FDT_END tag. */
+#define FDT_ERR_BADMAGIC 9
+ /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a
+ * device tree at all - it is missing the flattened device
+ * tree magic number. */
+#define FDT_ERR_BADVERSION 10
+ /* FDT_ERR_BADVERSION: Given device tree has a version which
+ * can't be handled by the requested operation. For
+ * read-write functions, this may mean that fdt_open_into() is
+ * required to convert the tree to the expected version. */
+#define FDT_ERR_BADSTRUCTURE 11
+ /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt
+ * structure block or other serious error (e.g. misnested
+ * nodes, or subnodes preceding properties). */
+#define FDT_ERR_BADLAYOUT 12
+ /* FDT_ERR_BADLAYOUT: For read-write functions, the given
+ * device tree has it's sub-blocks in an order that the
+ * function can't handle (memory reserve map, then structure,
+ * then strings). Use fdt_open_into() to reorganize the tree
+ * into a form suitable for the read-write operations. */
+
+/* "Can't happen" error indicating a bug in libfdt */
+#define FDT_ERR_INTERNAL 13
+ /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion.
+ * Should never be returned, if it is, it indicates a bug in
+ * libfdt itself. */
+
+#define FDT_ERR_MAX 13
+
+/**********************************************************************/
+/* Low-level functions (you probably don't need these) */
+/**********************************************************************/
+
+const void *fdt_offset_ptr(const void *fdt, int offset, int checklen);
+static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen)
+{
+ return (void *)fdt_offset_ptr(fdt, offset, checklen);
+}
+
+uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset);
+
+/**********************************************************************/
+/* General functions */
+/**********************************************************************/
+
+#define fdt_get_header(fdt, field) \
+ (fdt32_to_cpu(((const struct fdt_header *)(fdt))->field))
+#define fdt_magic(fdt) (fdt_get_header(fdt, magic))
+#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize))
+#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct))
+#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings))
+#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap))
+#define fdt_version(fdt) (fdt_get_header(fdt, version))
+#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version))
+#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys))
+#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings))
+#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct))
+
+#define __fdt_set_hdr(name) \
+ static inline void fdt_set_##name(void *fdt, uint32_t val) \
+ { \
+ struct fdt_header *fdth = fdt; \
+ fdth->name = cpu_to_fdt32(val); \
+ }
+__fdt_set_hdr(magic);
+__fdt_set_hdr(totalsize);
+__fdt_set_hdr(off_dt_struct);
+__fdt_set_hdr(off_dt_strings);
+__fdt_set_hdr(off_mem_rsvmap);
+__fdt_set_hdr(version);
+__fdt_set_hdr(last_comp_version);
+__fdt_set_hdr(boot_cpuid_phys);
+__fdt_set_hdr(size_dt_strings);
+__fdt_set_hdr(size_dt_struct);
+#undef __fdt_set_hdr
+
+/**
+ * fdt_check_header - sanity check a device tree or possible device tree
+ * @fdt: pointer to data which might be a flattened device tree
+ *
+ * fdt_check_header() checks that the given buffer contains what
+ * appears to be a flattened device tree with sane information in its
+ * header.
+ *
+ * returns:
+ * 0, if the buffer appears to contain a valid device tree
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings, as above
+ */
+int fdt_check_header(const void *fdt);
+
+/**
+ * fdt_move - move a device tree around in memory
+ * @fdt: pointer to the device tree to move
+ * @buf: pointer to memory where the device is to be moved
+ * @bufsize: size of the memory space at buf
+ *
+ * fdt_move() relocates, if possible, the device tree blob located at
+ * fdt to the buffer at buf of size bufsize. The buffer may overlap
+ * with the existing device tree blob at fdt. Therefore,
+ * fdt_move(fdt, fdt, fdt_totalsize(fdt))
+ * should always succeed.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_move(const void *fdt, void *buf, int bufsize);
+
+/**********************************************************************/
+/* Read-only functions */
+/**********************************************************************/
+
+/**
+ * fdt_string - retreive a string from the strings block of a device tree
+ * @fdt: pointer to the device tree blob
+ * @stroffset: offset of the string within the strings block (native endian)
+ *
+ * fdt_string() retrieves a pointer to a single string from the
+ * strings block of the device tree blob at fdt.
+ *
+ * returns:
+ * a pointer to the string, on success
+ * NULL, if stroffset is out of bounds
+ */
+const char *fdt_string(const void *fdt, int stroffset);
+
+/**
+ * fdt_num_mem_rsv - retreive the number of memory reserve map entries
+ * @fdt: pointer to the device tree blob
+ *
+ * Returns the number of entries in the device tree blob's memory
+ * reservation map. This does not include the terminating 0,0 entry
+ * or any other (0,0) entries reserved for expansion.
+ *
+ * returns:
+ * the number of entries
+ */
+int fdt_num_mem_rsv(const void *fdt);
+
+/**
+ * fdt_get_mem_rsv - retreive one memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @address, @size: pointers to 64-bit variables
+ *
+ * On success, *address and *size will contain the address and size of
+ * the n-th reserve map entry from the device tree blob, in
+ * native-endian format.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size);
+
+/**
+ * fdt_subnode_offset_namelen - find a subnode based on substring
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_subnode_offset(), but only examine the first
+ * namelen characters of name for matching the subnode name. This is
+ * useful for finding subnodes based on a portion of a larger string,
+ * such as a full path.
+ */
+int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
+ const char *name, int namelen);
+/**
+ * fdt_subnode_offset - find a subnode of a given node
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ *
+ * fdt_subnode_offset() finds a subnode of the node at structure block
+ * offset parentoffset with the given name. name may include a unit
+ * address, in which case fdt_subnode_offset() will find the subnode
+ * with that unit address, or the unit address may be omitted, in
+ * which case fdt_subnode_offset() will find an arbitrary subnode
+ * whose name excluding unit address matches the given name.
+ *
+ * returns:
+ * structure block offset of the requested subnode (>=0), on success
+ * -FDT_ERR_NOTFOUND, if the requested subnode does not exist
+ * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name);
+
+/**
+ * fdt_path_offset - find a tree node by its full path
+ * @fdt: pointer to the device tree blob
+ * @path: full path of the node to locate
+ *
+ * fdt_path_offset() finds a node of a given path in the device tree.
+ * Each path component may omit the unit address portion, but the
+ * results of this are undefined if any such path component is
+ * ambiguous (that is if there are multiple nodes at the relevant
+ * level matching the given component, differentiated only by unit
+ * address).
+ *
+ * returns:
+ * structure block offset of the node with the requested path (>=0), on success
+ * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid
+ * -FDT_ERR_NOTFOUND, if the requested node does not exist
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_path_offset(const void *fdt, const char *path);
+
+/**
+ * fdt_get_name - retreive the name of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of the starting node
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_name() retrieves the name (including unit address) of the
+ * device tree node at structure block offset nodeoffset. If lenp is
+ * non-NULL, the length of this name is also returned, in the integer
+ * pointed to by lenp.
+ *
+ * returns:
+ * pointer to the node's name, on success
+ * If lenp is non-NULL, *lenp contains the length of that name (>=0)
+ * NULL, on error
+ * if lenp is non-NULL *lenp contains an error code (<0):
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp);
+
+/**
+ * fdt_get_property - find a given property in a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_property() retrieves a pointer to the fdt_property
+ * structure within the device tree blob corresponding to the property
+ * named 'name' of the node at offset nodeoffset. If lenp is
+ * non-NULL, the length of the property value also returned, in the
+ * integer pointed to by lenp.
+ *
+ * returns:
+ * pointer to the structure representing the property
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_NOTFOUND, node does not have named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset,
+ const char *name, int *lenp);
+static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset,
+ const char *name,
+ int *lenp)
+{
+ return (struct fdt_property *)fdt_get_property(fdt, nodeoffset,
+ name, lenp);
+}
+
+/**
+ * fdt_getprop - retrieve the value of a given property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_getprop() retrieves a pointer to the value of the property
+ * named 'name' of the node at offset nodeoffset (this will be a
+ * pointer to within the device blob itself, not a copy of the value).
+ * If lenp is non-NULL, the length of the property value also
+ * returned, in the integer pointed to by lenp.
+ *
+ * returns:
+ * pointer to the property's value
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_NOTFOUND, node does not have named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+const void *fdt_getprop(const void *fdt, int nodeoffset,
+ const char *name, int *lenp);
+static inline void *fdt_getprop_w(void *fdt, int nodeoffset,
+ const char *name, int *lenp)
+{
+ return (void *)fdt_getprop(fdt, nodeoffset, name, lenp);
+}
+
+/**
+ * fdt_get_phandle - retreive the phandle of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of the node
+ *
+ * fdt_get_phandle() retrieves the phandle of the device tree node at
+ * structure block offset nodeoffset.
+ *
+ * returns:
+ * the phandle of the node at nodeoffset, on succes (!= 0, != -1)
+ * 0, if the node has no phandle, or another error occurs
+ */
+uint32_t fdt_get_phandle(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_get_path - determine the full path of a node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose path to find
+ * @buf: character buffer to contain the returned path (will be overwritten)
+ * @buflen: size of the character buffer at buf
+ *
+ * fdt_get_path() computes the full path of the node at offset
+ * nodeoffset, and records that path in the buffer at buf.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+ * 0, on success
+ * buf contains the absolute path of the node at
+ * nodeoffset, as a NUL-terminated string.
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1)
+ * characters and will not fit in the given buffer.
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen);
+
+/**
+ * fdt_supernode_atdepth_offset - find a specific ancestor of a node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ * @supernodedepth: depth of the ancestor to find
+ * @nodedepth: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_supernode_atdepth_offset() finds an ancestor of the given node
+ * at a specific depth from the root (where the root itself has depth
+ * 0, its immediate subnodes depth 1 and so forth). So
+ * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL);
+ * will always return 0, the offset of the root node. If the node at
+ * nodeoffset has depth D, then:
+ * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL);
+ * will return nodeoffset itself.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+
+ * structure block offset of the node at node offset's ancestor
+ * of depth supernodedepth (>=0), on success
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+* -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of nodeoffset
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
+ int supernodedepth, int *nodedepth);
+
+/**
+ * fdt_node_depth - find the depth of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ *
+ * fdt_node_depth() finds the depth of a given node. The root node
+ * has depth 0, its immediate subnodes depth 1 and so forth.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+ * depth of the node at nodeoffset (>=0), on success
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_depth(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_parent_offset - find the parent of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ *
+ * fdt_parent_offset() locates the parent node of a given node (that
+ * is, it finds the offset of the node which contains the node at
+ * nodeoffset as a subnode).
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset, *twice*.
+ *
+ * returns:
+ * stucture block offset of the parent of the node at nodeoffset
+ * (>=0), on success
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_parent_offset(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_node_offset_by_prop_value - find nodes with a given property value
+ * @fdt: pointer to the device tree blob
+ * @startoffset: only find nodes after this offset
+ * @propname: property name to check
+ * @propval: property value to search for
+ * @proplen: length of the value in propval
+ *
+ * fdt_node_offset_by_prop_value() returns the offset of the first
+ * node after startoffset, which has a property named propname whose
+ * value is of length proplen and has value equal to propval; or if
+ * startoffset is -1, the very first such node in the tree.
+ *
+ * To iterate through all nodes matching the criterion, the following
+ * idiom can be used:
+ * offset = fdt_node_offset_by_prop_value(fdt, -1, propname,
+ * propval, proplen);
+ * while (offset != -FDT_ERR_NOTFOUND) {
+ * // other code here
+ * offset = fdt_node_offset_by_prop_value(fdt, offset, propname,
+ * propval, proplen);
+ * }
+ *
+ * Note the -1 in the first call to the function, if 0 is used here
+ * instead, the function will never locate the root node, even if it
+ * matches the criterion.
+ *
+ * returns:
+ * structure block offset of the located node (>= 0, >startoffset),
+ * on success
+ * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
+ * tree after startoffset
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
+ const char *propname,
+ const void *propval, int proplen);
+
+/**
+ * fdt_node_offset_by_phandle - find the node with a given phandle
+ * @fdt: pointer to the device tree blob
+ * @phandle: phandle value
+ *
+ * fdt_node_offset_by_prop_value() returns the offset of the node
+ * which has the given phandle value. If there is more than one node
+ * in the tree with the given phandle (an invalid tree), results are
+ * undefined.
+ *
+ * returns:
+ * structure block offset of the located node (>= 0), on success
+ * -FDT_ERR_NOTFOUND, no node with that phandle exists
+ * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1)
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle);
+
+/**
+ * fdt_node_check_compatible: check a node's compatible property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @compatible: string to match against
+ *
+ *
+ * fdt_node_check_compatible() returns 0 if the given node contains a
+ * 'compatible' property with the given string as one of its elements,
+ * it returns non-zero otherwise, or on error.
+ *
+ * returns:
+ * 0, if the node has a 'compatible' property listing the given string
+ * 1, if the node has a 'compatible' property, but it does not list
+ * the given string
+ * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property
+ * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_check_compatible(const void *fdt, int nodeoffset,
+ const char *compatible);
+
+/**
+ * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value
+ * @fdt: pointer to the device tree blob
+ * @startoffset: only find nodes after this offset
+ * @compatible: 'compatible' string to match against
+ *
+ * fdt_node_offset_by_compatible() returns the offset of the first
+ * node after startoffset, which has a 'compatible' property which
+ * lists the given compatible string; or if startoffset is -1, the
+ * very first such node in the tree.
+ *
+ * To iterate through all nodes matching the criterion, the following
+ * idiom can be used:
+ * offset = fdt_node_offset_by_compatible(fdt, -1, compatible);
+ * while (offset != -FDT_ERR_NOTFOUND) {
+ * // other code here
+ * offset = fdt_node_offset_by_compatible(fdt, offset, compatible);
+ * }
+ *
+ * Note the -1 in the first call to the function, if 0 is used here
+ * instead, the function will never locate the root node, even if it
+ * matches the criterion.
+ *
+ * returns:
+ * structure block offset of the located node (>= 0, >startoffset),
+ * on success
+ * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
+ * tree after startoffset
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
+ const char *compatible);
+
+/**********************************************************************/
+/* Write-in-place functions */
+/**********************************************************************/
+
+/**
+ * fdt_setprop_inplace - change a property's value, but not its size
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: pointer to data to replace the property value with
+ * @len: length of the property value
+ *
+ * fdt_setprop_inplace() replaces the value of a given property with
+ * the data in val, of length len. This function cannot change the
+ * size of a property, and so will only work if len is equal to the
+ * current length of the property.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the given property value, and will not alter or move any other part
+ * of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if len is not equal to the property's current length
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len);
+
+/**
+ * fdt_setprop_inplace_cell - change the value of a single-cell property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: cell (32-bit integer) value to replace the property with
+ *
+ * fdt_setprop_inplace_cell() replaces the value of a given property
+ * with the 32-bit integer cell value in val, converting val to
+ * big-endian if necessary. This function cannot change the size of a
+ * property, and so will only work if the property already exists and
+ * has length 4.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the given property value, and will not alter or move any other part
+ * of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if the property's length is not equal to 4
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ val = cpu_to_fdt32(val);
+ return fdt_setprop_inplace(fdt, nodeoffset, name, &val, sizeof(val));
+}
+
+/**
+ * fdt_nop_property - replace a property with nop tags
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to nop
+ * @name: name of the property to nop
+ *
+ * fdt_nop_property() will replace a given property's representation
+ * in the blob with FDT_NOP tags, effectively removing it from the
+ * tree.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the property, and will not alter or move any other part of the
+ * tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_nop_property(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_nop_node - replace a node (subtree) with nop tags
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to nop
+ *
+ * fdt_nop_node() will replace a given node's representation in the
+ * blob, including all its subnodes, if any, with FDT_NOP tags,
+ * effectively removing it from the tree.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the node and its properties and subnodes, and will not alter or
+ * move any other part of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_nop_node(void *fdt, int nodeoffset);
+
+/**********************************************************************/
+/* Sequential write functions */
+/**********************************************************************/
+
+int fdt_create(void *buf, int bufsize);
+int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size);
+int fdt_finish_reservemap(void *fdt);
+int fdt_begin_node(void *fdt, const char *name);
+int fdt_property(void *fdt, const char *name, const void *val, int len);
+static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val)
+{
+ val = cpu_to_fdt32(val);
+ return fdt_property(fdt, name, &val, sizeof(val));
+}
+#define fdt_property_string(fdt, name, str) \
+ fdt_property(fdt, name, str, strlen(str)+1)
+int fdt_end_node(void *fdt);
+int fdt_finish(void *fdt);
+
+/**********************************************************************/
+/* Read-write functions */
+/**********************************************************************/
+
+int fdt_open_into(const void *fdt, void *buf, int bufsize);
+int fdt_pack(void *fdt);
+
+/**
+ * fdt_add_mem_rsv - add one memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @addres, @size: 64-bit values (native endian)
+ *
+ * Adds a reserve map entry to the given blob reserving a region at
+ * address address of length size.
+ *
+ * This function will insert data into the reserve map and will
+ * therfore change the indexes of some entries in the table.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new reservation entry
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size);
+
+/**
+ * fdt_del_mem_rsv - remove a memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @n: entry to remove
+ *
+ * fdt_del_mem_rsv() removes the n-th memory reserve map entry from
+ * the blob.
+ *
+ * This function will delete data from the reservation table and will
+ * therfore change the indexes of some entries in the table.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there
+ * are less than n+1 reserve map entries)
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_del_mem_rsv(void *fdt, int n);
+
+/**
+ * fdt_set_name - change the name of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of a node
+ * @name: name to give the node
+ *
+ * fdt_set_name() replaces the name (including unit address, if any)
+ * of the given node with the given string. NOTE: this function can't
+ * efficiently check if the new name is unique amongst the given
+ * node's siblings; results are undefined if this function is invoked
+ * with a name equal to one of the given node's siblings.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob
+ * to contain the new name
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_set_name(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_setprop - create or change a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: pointer to data to set the property value to
+ * @len: length of the property value
+ *
+ * fdt_setprop() sets the value of the named property in the given
+ * node to the given value and length, creeating the property if it
+ * does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_setprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len);
+
+/**
+ * fdt_setprop_cell - set a property to a single cell value
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 32-bit integer value for the property (native endian)
+ *
+ * fdt_setprop_cell() sets the value of the named property in the
+ * given node to the given cell value (converting to big-endian if
+ * necessary), or creates a new property with that value if it does
+ * not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name,
+ uint32_t val)
+{
+ val = cpu_to_fdt32(val);
+ return fdt_setprop(fdt, nodeoffset, name, &val, sizeof(val));
+}
+
+/**
+ * fdt_setprop_string - set a property to a string value
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @str: string value for the property
+ *
+ * fdt_setprop_string() sets the value of the named property in the
+ * given node to the given string value (using the length of the
+ * string to determine the new length of the property), or creates a
+ * new property with that value if it does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+#define fdt_setprop_string(fdt, nodeoffset, name, str) \
+ fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1)
+
+/**
+ * fdt_delprop - delete a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to nop
+ * @name: name of the property to nop
+ *
+ * fdt_del_property() will delete the given property.
+ *
+ * This function will delete data from the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_delprop(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_add_subnode_namelen - creates a new node based on substring
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_add_subnode(), but use only the first namelen
+ * characters of name as the name of the new node. This is useful for
+ * creating subnodes based on a portion of a larger string, such as a
+ * full path.
+ */
+int fdt_add_subnode_namelen(void *fdt, int parentoffset,
+ const char *name, int namelen);
+
+/**
+ * fdt_add_subnode - creates a new node
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ *
+ * fdt_add_subnode() creates a new node as a subnode of the node at
+ * structure block offset parentoffset, with the given name (which
+ * should include the unit address, if any).
+ *
+ * This function will insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+
+ * returns:
+ * structure block offset of the created nodeequested subnode (>=0), on success
+ * -FDT_ERR_NOTFOUND, if the requested subnode does not exist
+ * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag
+ * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of
+ * the given name
+ * -FDT_ERR_NOSPACE, if there is insufficient free space in the
+ * blob to contain the new node
+ * -FDT_ERR_NOSPACE
+ * -FDT_ERR_BADLAYOUT
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_add_subnode(void *fdt, int parentoffset, const char *name);
+
+/**
+ * fdt_del_node - delete a node (subtree)
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to nop
+ *
+ * fdt_del_node() will remove the given node, including all its
+ * subnodes if any, from the blob.
+ *
+ * This function will delete data from the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_del_node(void *fdt, int nodeoffset);
+
+/**********************************************************************/
+/* Debugging / informational functions */
+/**********************************************************************/
+
+const char *fdt_strerror(int errval);
+
+#endif /* _LIBFDT_H */
diff --git a/libfdt/libfdt_env.h b/libfdt/libfdt_env.h
new file mode 100644
index 0000000..81bcd7a
--- /dev/null
+++ b/libfdt/libfdt_env.h
@@ -0,0 +1,21 @@
+#ifndef _LIBFDT_ENV_H
+#define _LIBFDT_ENV_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include "bswap.h"
+
+#ifdef WORDS_BIGENDIAN
+#define fdt32_to_cpu(x) (x)
+#define cpu_to_fdt32(x) (x)
+#define fdt64_to_cpu(x) (x)
+#define cpu_to_fdt64(x) (x)
+#else
+#define fdt32_to_cpu(x) (bswap_32((x)))
+#define cpu_to_fdt32(x) (bswap_32((x)))
+#define fdt64_to_cpu(x) (bswap_64((x)))
+#define cpu_to_fdt64(x) (bswap_64((x)))
+#endif
+
+#endif /* _LIBFDT_ENV_H */
diff --git a/libfdt/libfdt_internal.h b/libfdt/libfdt_internal.h
new file mode 100644
index 0000000..1e60936
--- /dev/null
+++ b/libfdt/libfdt_internal.h
@@ -0,0 +1,89 @@
+#ifndef _LIBFDT_INTERNAL_H
+#define _LIBFDT_INTERNAL_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <fdt.h>
+
+#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
+#define PALIGN(p, a) ((void *)ALIGN((unsigned long)(p), (a)))
+
+#define memeq(p, q, n) (memcmp((p), (q), (n)) == 0)
+#define streq(p, q) (strcmp((p), (q)) == 0)
+
+uint32_t _fdt_next_tag(const void *fdt, int startoffset, int *nextoffset);
+const char *_fdt_find_string(const char *strtab, int tabsize, const char *s);
+int _fdt_node_end_offset(void *fdt, int nodeoffset);
+
+static inline const void *_fdt_offset_ptr(const void *fdt, int offset)
+{
+ return fdt + fdt_off_dt_struct(fdt) + offset;
+}
+
+static inline void *_fdt_offset_ptr_w(void *fdt, int offset)
+{
+ return (void *)_fdt_offset_ptr(fdt, offset);
+}
+
+static inline const struct fdt_reserve_entry *_fdt_mem_rsv(const void *fdt, int n)
+{
+ const struct fdt_reserve_entry *rsv_table =
+ fdt + fdt_off_mem_rsvmap(fdt);
+
+ return rsv_table + n;
+}
+static inline struct fdt_reserve_entry *_fdt_mem_rsv_w(void *fdt, int n)
+{
+ return (void *)_fdt_mem_rsv(fdt, n);
+}
+
+#define SW_MAGIC (~FDT_MAGIC)
+
+#endif /* _LIBFDT_INTERNAL_H */
diff --git a/libfdt/patch.libfdt b/libfdt/patch.libfdt
new file mode 100644
index 0000000..c911d10
--- /dev/null
+++ b/libfdt/patch.libfdt
@@ -0,0 +1,20 @@
+Minor changes are required to get libfdt to build as part of qemu.
+The patch below records modifications relative to upstream dtc-v1.2.0.
+
+Index: libfdt_env.h
+===================================================================
+--- libfdt_env.h (revision 230023)
++++ libfdt_env.h (working copy)
+@@ -4,10 +4,9 @@
+ #include <stddef.h>
+ #include <stdint.h>
+ #include <string.h>
+-#include <endian.h>
+-#include <byteswap.h>
++#include "bswap.h"
+
+-#if __BYTE_ORDER == __BIG_ENDIAN
++#ifdef WORDS_BIGENDIAN
+ #define fdt32_to_cpu(x) (x)
+ #define cpu_to_fdt32(x) (x)
+ #define fdt64_to_cpu(x) (x)
diff --git a/target-ppc/kvm_ppc.c b/target-ppc/kvm_ppc.c
index 10cfdb3..cc639c6 100644
--- a/target-ppc/kvm_ppc.c
+++ b/target-ppc/kvm_ppc.c
@@ -21,7 +21,6 @@
static QEMUTimer *kvmppc_timer;
static unsigned int kvmppc_timer_rate;
-#ifdef HAVE_FDT
int kvmppc_read_host_property(const char *node_path, const char *prop,
void *val, size_t len)
{
@@ -85,7 +84,6 @@ void kvmppc_fdt_update(void *fdt)
kvmppc_copy_host_cell(fdt, "/cpus/cpu@0", "clock-frequency");
kvmppc_copy_host_cell(fdt, "/cpus/cpu@0", "timebase-frequency");
}
-#endif
static void kvmppc_timer_hack(void *opaque)
{
^ permalink raw reply related [flat|nested] 61+ messages in thread
* [Qemu-devel] [PATCH 2/4] Add device tree machine
2009-06-10 17:38 [Qemu-devel] [PATCH 0/4] Machine config files Paul Brook
2009-06-10 17:38 ` [Qemu-devel] [PATCH 1/4] Include and build libfdt Paul Brook
@ 2009-06-10 17:38 ` Paul Brook
2009-06-10 18:30 ` Blue Swirl
` (3 more replies)
2009-06-10 17:38 ` [Qemu-devel] [PATCH 3/4] Stellaris machine config Paul Brook
` (4 subsequent siblings)
6 siblings, 4 replies; 61+ messages in thread
From: Paul Brook @ 2009-06-10 17:38 UTC (permalink / raw)
To: qemu-devel
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 <paul@codesourcery.com>
---
.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,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 = <c0000000>;
+ interrupt-controller;
+ interrupt-parent = <&cpu0>;
+ interrupts = <0>;
+ num-interrupts = <20>;
+ };
+ rtc@0 {
+ compatible = "syborg,rtc";
+ reg = <c0001000>;
+ };
+ timer@0 {
+ compatible = "syborg,timer";
+ reg = <c0002000>;
+ frequency = <d#1000000>;
+ interrupts = <1>;
+ interrupt-parent = <&intc>;
+ };
+ keyboard@0 {
+ compatible = "syborg,keyboard";
+ reg = <c0003000>;
+ interrupts = <2>;
+ interrupt-parent = <&intc>;
+ };
+ touchscreen@0 {
+ compatible = "syborg,pointer";
+ reg = <c0004000>;
+ interrupts = <3>;
+ interrupt-parent = <&intc>;
+ };
+ framebuffer@0 {
+ compatible = "syborg,framebuffer";
+ reg = <c0005000>;
+ interrupts = <4>;
+ interrupt-parent = <&intc>;
+ };
+ serial@0 {
+ device_type = "serial";
+ compatible = "syborg,serial";
+ chardev = "serial0";
+ reg = <c0006000>;
+ interrupts = <5>;
+ interrupt-parent = <&intc>;
+ };
+ serial@1 {
+ device_type = "serial";
+ compatible = "syborg,serial";
+ chardev = "serial1";
+ reg = <c0007000>;
+ interrupts = <6>;
+ interrupt-parent = <&intc>;
+ };
+ serial@2 {
+ device_type = "serial";
+ compatible = "syborg,serial";
+ chardev = "serial2";
+ reg = <c0008000>;
+ interrupts = <7>;
+ interrupt-parent = <&intc>;
+ };
+ serial@3 {
+ device_type = "serial";
+ compatible = "syborg,serial";
+ chardev = "serial3";
+ reg = <c0009000>;
+ interrupts = <8>;
+ interrupt-parent = <&intc>;
+ };
+/*
+ hostfs@0 {
+ compatible = "syborg,hostfs";
+ reg = <c000a000>;
+ host-path = "\\svphostfs\\";
+ drive-number = <d#19>;
+ };
+ ss@0 {
+ compatible = "syborg,snapshot";
+ reg = <c000b000>;
+ };
+*/
+ net@0 {
+ compatible = "syborg,virtio-net";
+ reg = <c000c000>;
+ interrupts = <9>;
+ interrupt-parent = <&intc>;
+ };
+/*
+ nand@0 {
+ compatible = "syborg,nand";
+ reg = <c000d000>;
+ size = <400>;
+ };
+ audio@0 {
+ compatible = "syborg,virtio-audio";
+ reg = <c000e000>;
+ interrupts = <a>;
+ interrupt-parent = <&intc>;
+ };
+ platform@0 {
+ 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++) {
^ permalink raw reply related [flat|nested] 61+ messages in thread
* [Qemu-devel] [PATCH 3/4] Stellaris machine config
2009-06-10 17:38 [Qemu-devel] [PATCH 0/4] Machine config files Paul Brook
2009-06-10 17:38 ` [Qemu-devel] [PATCH 1/4] Include and build libfdt Paul Brook
2009-06-10 17:38 ` [Qemu-devel] [PATCH 2/4] Add device tree machine Paul Brook
@ 2009-06-10 17:38 ` Paul Brook
2009-06-11 8:21 ` M P
2009-06-10 17:38 ` [Qemu-devel] [PATCH 4/4] Integrator " Paul Brook
` (3 subsequent siblings)
6 siblings, 1 reply; 61+ messages in thread
From: Paul Brook @ 2009-06-10 17:38 UTC (permalink / raw)
To: qemu-devel
Replace hardcoded stellaris boards with machine configs
Signed-off-by: Paul Brook <paul@codesourcery.com>
---
Makefile | 4 -
Makefile.hw | 2
hw/arm-cpu.c | 3
hw/armv7m.c | 61 +++++++++
hw/gpio-buttons.c | 124 ++++++++++++++++++
hw/pl011.c | 2
hw/pl061.c | 25 +++-
hw/stellaris.c | 272 +++++++++-------------------------------
hw/stellaris_enet.c | 2
hw/stellaris_input.c | 91 -------------
pc-bios/boards/lm3s6965evb.dts | 212 +++++++++++++++++++++++++++++++
pc-bios/boards/lm3s811evb.dts | 155 +++++++++++++++++++++++
12 files changed, 645 insertions(+), 308 deletions(-)
create mode 100644 hw/gpio-buttons.c
delete mode 100644 hw/stellaris_input.c
create mode 100644 pc-bios/boards/lm3s6965evb.dts
create mode 100644 pc-bios/boards/lm3s811evb.dts
diff --git a/Makefile b/Makefile
index 6d15c44..48a3ec3 100644
--- a/Makefile
+++ b/Makefile
@@ -46,7 +46,7 @@ endif
#######################################################################
# Board descriptions
-BOARDS = syborg
+BOARDS = syborg lm3s811evb lm3s6965evb
ifdef DTC
BOARDS_BIN = $(BOARDS:%=pc-bios/boards/%.dtb)
@@ -115,7 +115,7 @@ OBJS+=readline.o console.o
OBJS+=irq.o ptimer.o
OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o
-OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o twl92230.o
+OBJS+=ssd0303.o ssd0323.o ads7846.o twl92230.o
OBJS+=tmp105.o lm832x.o eeprom93xx.o tsc2005.o
OBJS+=scsi-disk.o cdrom.o
OBJS+=scsi-generic.o
diff --git a/Makefile.hw b/Makefile.hw
index 6accb3b..1953b85 100644
--- a/Makefile.hw
+++ b/Makefile.hw
@@ -28,6 +28,8 @@ OBJS+= lsi53c895a.o esp.o
OBJS+= dma-helpers.o sysbus.o
+OBJS+= gpio-buttons.o
+
all: $(HWLIB)
# Dummy command so that make thinks it has done something
@true
diff --git a/hw/arm-cpu.c b/hw/arm-cpu.c
index c15eb12..4c128d1 100644
--- a/hw/arm-cpu.c
+++ b/hw/arm-cpu.c
@@ -17,7 +17,8 @@ static const struct {
const char *cpuname;
} cpu_device_name_map[] = {
{"ARM,ARM926EJ-S", "arm926"},
- {"ARM,Cortex-A8", "cortex-a8"}
+ {"ARM,Cortex-A8", "cortex-a8"},
+ {"ARM,Cortex-M3", "cortex-m3"}
};
typedef struct {
diff --git a/hw/armv7m.c b/hw/armv7m.c
index c3c5b9e..d74a2fb 100644
--- a/hw/armv7m.c
+++ b/hw/armv7m.c
@@ -10,6 +10,7 @@
#include "sysbus.h"
#include "arm-misc.h"
#include "sysemu.h"
+#include "boards.h"
/* Bitbanded IO. Each word corresponds to a single bit. */
@@ -149,6 +150,47 @@ static void armv7m_bitband_init(void)
}
/* Board init. */
+/* Reset initialization and iage loading for ARMv7-M cores. */
+static void armv7m_bootstrap(ram_addr_t ram_size, const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename)
+{
+ CPUState *env = first_cpu;
+ uint32_t pc;
+ int image_size;
+ uint64_t entry;
+ uint64_t lowaddr;
+
+ image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL);
+ if (image_size < 0) {
+ image_size = load_image_targphys(kernel_filename, 0, ram_size);
+ lowaddr = 0;
+ }
+ if (image_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+
+ /* If the image was loaded at address zero then assume it is a
+ regular ROM image and perform the normal CPU reset sequence.
+ Otherwise jump directly to the entry point. */
+ if (lowaddr == 0) {
+ env->regs[13] = ldl_phys(0);
+ pc = ldl_phys(4);
+ } else {
+ pc = entry;
+ }
+ env->thumb = pc & 1;
+ env->regs[15] = pc & ~1;
+
+ /* Hack to map an additional page of ram at the top of the address
+ space. This stops qemu complaining about executing code outside RAM
+ when returning from an exception. */
+ cpu_register_physical_memory(0xfffff000, 0x1000,
+ qemu_ram_alloc(0x1000) | IO_MEM_RAM);
+}
+
/* Init CPU and memory for a v7-M based board.
flash_size and sram_size are in kb.
Returns the NVIC array. */
@@ -238,10 +280,25 @@ qemu_irq *armv7m_init(int flash_size, int sram_size,
return pic;
}
+static SysBusDeviceInfo bitband_info = {
+ .init = bitband_init,
+ .qdev.props = (DevicePropList[]) {
+ {.name = "base", .type = PROP_TYPE_INT},
+ {.name = NULL}
+ }
+};
+
static void armv7m_register_devices(void)
{
- sysbus_register_dev("ARM,bitband-memory", sizeof(BitBandState),
- bitband_init);
+ sysbus_register_withprop("ARM,bitband-memory", sizeof(BitBandState),
+ &bitband_info);
}
device_init(armv7m_register_devices)
+
+static void armv7m_boot_register(void)
+{
+ register_machine_bootstrap("ARMv7-M", armv7m_bootstrap);
+}
+
+machine_init(armv7m_boot_register);
diff --git a/hw/gpio-buttons.c b/hw/gpio-buttons.c
new file mode 100644
index 0000000..0627818
--- /dev/null
+++ b/hw/gpio-buttons.c
@@ -0,0 +1,124 @@
+/*
+ * Buttons connected directly to GPIO pins.
+ *
+ * Copyright (c) 2009 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GNU GPL v2.
+ */
+#include "sysbus.h"
+#include "console.h"
+
+#define KEY_INVERT 0x80000000u
+#define KEY_CODE_MASK 0x0000ffffu
+
+typedef struct {
+ SysBusDevice busdev;
+ qemu_irq *irq;
+ const uint32_t *keys;
+ int *pressed;
+ int num_buttons;
+ int extension;
+} GPIOButtonsState;
+
+static void gpio_buttons_put_key(void *opaque, int keycode)
+{
+ GPIOButtonsState *s = (GPIOButtonsState *)opaque;
+ int i;
+ int down;
+
+ if (keycode == 0xe0 && !s->extension) {
+ s->extension = 0x80;
+ return;
+ }
+
+ down = (keycode & 0x80) == 0;
+ keycode = (keycode & 0x7f) | s->extension;
+
+ for (i = 0; i < s->num_buttons; i++) {
+ if ((s->keys[i] & KEY_CODE_MASK) == keycode
+ && s->pressed[i] != down) {
+ s->pressed[i] = down;
+ if (s->keys[i] & KEY_INVERT) {
+ qemu_set_irq(s->irq[i], !down);
+ } else {
+ qemu_set_irq(s->irq[i], down);
+ }
+ }
+ }
+
+ s->extension = 0;
+}
+
+static void gpio_buttons_save(QEMUFile *f, void *opaque)
+{
+ GPIOButtonsState *s = (GPIOButtonsState *)opaque;
+ int i;
+
+ qemu_put_be32(f, s->extension);
+ for (i = 0; i < s->num_buttons; i++)
+ qemu_put_byte(f, s->pressed[i]);
+}
+
+static int gpio_buttons_load(QEMUFile *f, void *opaque, int version_id)
+{
+ GPIOButtonsState *s = (GPIOButtonsState *)opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->extension = qemu_get_be32(f);
+ for (i = 0; i < s->num_buttons; i++)
+ s->pressed[i] = qemu_get_byte(f);
+
+ return 0;
+}
+
+static void gpio_buttons_late_init(DeviceState *dev)
+{
+ GPIOButtonsState *s = FROM_SYSBUS(GPIOButtonsState, sysbus_from_qdev(dev));
+ int i;
+
+ for (i = 0; i < s->num_buttons; i++) {
+ if (s->keys[i] & KEY_INVERT) {
+ qemu_irq_raise(s->irq[i]);
+ }
+ }
+}
+
+static void gpio_buttons_init(SysBusDevice *dev)
+{
+ GPIOButtonsState *s = FROM_SYSBUS(GPIOButtonsState, dev);
+
+ s->num_buttons = qdev_get_prop_array(&dev->qdev, "keys", &s->keys);
+ if (s->num_buttons <= 0) {
+ hw_error("gpio-buttons: Missing keys property");
+ }
+
+ s->irq = qemu_mallocz(sizeof(qemu_irq) * s->num_buttons);
+ qdev_init_gpio_out(&dev->qdev, s->irq, s->num_buttons);
+ s->pressed = qemu_mallocz(sizeof(int) * s->num_buttons);
+ qemu_add_kbd_event_handler(gpio_buttons_put_key, s);
+ register_savevm("gpio-buttons", -1, 1,
+ gpio_buttons_save, gpio_buttons_load, s);
+}
+
+static SysBusDeviceInfo gpio_buttons_info = {
+ .init = gpio_buttons_init,
+ .qdev.late_init = gpio_buttons_late_init,
+ .qdev.props = (DevicePropList[]) {
+ {.name = "keys", .type = PROP_TYPE_ARRAY},
+ {.name = NULL}
+ }
+};
+
+static void gpio_buttons_register_devices(void)
+{
+ /* ??? This isn't really attached to the system bus, but it's as good
+ a place as any to put it. */
+ sysbus_register_withprop("qemu,gpio-buttons", sizeof(GPIOButtonsState),
+ &gpio_buttons_info);
+}
+
+device_init(gpio_buttons_register_devices)
diff --git a/hw/pl011.c b/hw/pl011.c
index 3a1a4cb..f167bec 100644
--- a/hw/pl011.c
+++ b/hw/pl011.c
@@ -323,7 +323,7 @@ static void pl011_register_devices(void)
{
sysbus_register_dev("pl011", sizeof(pl011_state),
pl011_init_arm);
- sysbus_register_dev("pl011_luminary", sizeof(pl011_state),
+ sysbus_register_dev("luminary,pl011", sizeof(pl011_state),
pl011_init_luminary);
}
diff --git a/hw/pl061.c b/hw/pl061.c
index aa0a322..8e38bd6 100644
--- a/hw/pl061.c
+++ b/hw/pl061.c
@@ -291,6 +291,18 @@ static int pl061_load(QEMUFile *f, void *opaque, int version_id)
return 0;
}
+static void pl061_late_init(DeviceState *dev)
+{
+ pl061_state *s = FROM_SYSBUS(pl061_state, sysbus_from_qdev(dev));
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ if (s->float_high & (1 << i)) {
+ qemu_irq_raise(s->out[i]);
+ }
+ }
+}
+
static void pl061_init(SysBusDevice *dev)
{
int iomemtype;
@@ -302,14 +314,23 @@ static void pl061_init(SysBusDevice *dev)
sysbus_init_irq(dev, &s->irq);
qdev_init_gpio_in(&dev->qdev, pl061_set_irq, 8);
qdev_init_gpio_out(&dev->qdev, s->out, 8);
+ s->float_high = qdev_get_prop_int(&dev->qdev, "float-high", 0);
pl061_reset(s);
register_savevm("pl061_gpio", -1, 1, pl061_save, pl061_load, s);
}
+static SysBusDeviceInfo pl061_info = {
+ .init = pl061_init,
+ .qdev.late_init = pl061_late_init,
+ .qdev.props = (DevicePropList[]) {
+ {.name = "float-high", .type = PROP_TYPE_INT},
+ {.name = NULL}
+ }
+};
static void pl061_register_devices(void)
{
- sysbus_register_dev("pl061", sizeof(pl061_state),
- pl061_init);
+ sysbus_register_withprop("pl061", sizeof(pl061_state),
+ &pl061_info);
}
device_init(pl061_register_devices)
diff --git a/hw/stellaris.c b/hw/stellaris.c
index 38b9830..ba5b11f 100644
--- a/hw/stellaris.c
+++ b/hw/stellaris.c
@@ -361,6 +361,7 @@ static void stellaris_gptm_init(SysBusDevice *dev)
/* System controller. */
typedef struct {
+ SysBusDevice busdev;
uint32_t pborctl;
uint32_t ldopctl;
uint32_t int_status;
@@ -374,8 +375,12 @@ typedef struct {
uint32_t ldoarst;
uint32_t user0;
uint32_t user1;
+ uint32_t did0;
+ uint32_t did1;
+ uint32_t dc[4];
qemu_irq irq;
- stellaris_board_info *board;
+ int got_mac;
+ DeviceState *netdev;
} ssys_state;
static void ssys_update(ssys_state *s)
@@ -421,25 +426,38 @@ static uint32_t pllcfg_fury[16] = {
0xb11c /* 8.192 Mhz */
};
+static void ssys_get_mac(ssys_state *s)
+{
+ uint8_t macaddr[6];
+
+ s->got_mac = 1;
+ if (!s->netdev) {
+ return;
+ }
+ qdev_get_macaddr(s->netdev, macaddr);
+ s->user0 = macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16);
+ s->user1 = macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16);
+}
+
static uint32_t ssys_read(void *opaque, target_phys_addr_t offset)
{
ssys_state *s = (ssys_state *)opaque;
switch (offset) {
case 0x000: /* DID0 */
- return s->board->did0;
+ return s->did0;
case 0x004: /* DID1 */
- return s->board->did1;
+ return s->did1;
case 0x008: /* DC0 */
- return s->board->dc0;
+ return s->dc[0];
case 0x010: /* DC1 */
- return s->board->dc1;
+ return s->dc[1];
case 0x014: /* DC2 */
- return s->board->dc2;
+ return s->dc[2];
case 0x018: /* DC3 */
- return s->board->dc3;
+ return s->dc[3];
case 0x01c: /* DC4 */
- return s->board->dc4;
+ return s->dc[4];
case 0x030: /* PBORCTL */
return s->pborctl;
case 0x034: /* LDOPCTL */
@@ -464,7 +482,7 @@ static uint32_t ssys_read(void *opaque, target_phys_addr_t offset)
{
int xtal;
xtal = (s->rcc >> 6) & 0xf;
- if (s->board->did0 & (1 << 16)) {
+ if (s->did0 & (1 << 16)) {
return pllcfg_fury[xtal];
} else {
return pllcfg_sandstorm[xtal];
@@ -493,8 +511,14 @@ static uint32_t ssys_read(void *opaque, target_phys_addr_t offset)
case 0x160: /* LDOARST */
return s->ldoarst;
case 0x1e0: /* USER0 */
+ if (!s->got_mac) {
+ ssys_get_mac(s);
+ }
return s->user0;
case 0x1e4: /* USER1 */
+ if (!s->got_mac) {
+ ssys_get_mac(s);
+ }
return s->user1;
default:
hw_error("ssys_read: Bad offset 0x%x\n", (int)offset);
@@ -654,23 +678,25 @@ static int ssys_load(QEMUFile *f, void *opaque, int version_id)
return 0;
}
-static void stellaris_sys_init(uint32_t base, qemu_irq irq,
- stellaris_board_info * board,
- uint8_t *macaddr)
+static void stellaris_sysctl_init(SysBusDevice *dev)
{
int iomemtype;
- ssys_state *s;
-
- s = (ssys_state *)qemu_mallocz(sizeof(ssys_state));
- s->irq = irq;
- s->board = board;
- /* Most devices come preprogrammed with a MAC address in the user data. */
- s->user0 = macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16);
- s->user1 = macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16);
+ ssys_state *s = FROM_SYSBUS(ssys_state, dev);
iomemtype = cpu_register_io_memory(0, ssys_readfn,
ssys_writefn, s);
- cpu_register_physical_memory(base, 0x00001000, iomemtype);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+ s->netdev = qdev_get_prop_dev(&dev->qdev, "enet");
+ s->got_mac = 0;
+ s->did0 = qdev_get_prop_int(&dev->qdev, "did0", 0);
+ s->did1 = qdev_get_prop_int(&dev->qdev, "did1", 0);
+ s->dc[0] = qdev_get_prop_int(&dev->qdev, "dc0", 0);
+ s->dc[1] = qdev_get_prop_int(&dev->qdev, "dc1", 0);
+ s->dc[2] = qdev_get_prop_int(&dev->qdev, "dc2", 0);
+ s->dc[3] = qdev_get_prop_int(&dev->qdev, "dc3", 0);
+ s->dc[4] = qdev_get_prop_int(&dev->qdev, "dc4", 0);
+
ssys_reset(s);
register_savevm("stellaris_sys", -1, 1, ssys_save, ssys_load, s);
}
@@ -1256,200 +1282,30 @@ static void stellaris_ssi_bus_init(SSISlave *dev)
stellaris_ssi_bus_save, stellaris_ssi_bus_load, s);
}
-/* Board init. */
-static stellaris_board_info stellaris_boards[] = {
- { "LM3S811EVB",
- 0,
- 0x0032000e,
- 0x001f001f, /* dc0 */
- 0x001132bf,
- 0x01071013,
- 0x3f0f01ff,
- 0x0000001f,
- BP_OLED_I2C
- },
- { "LM3S6965EVB",
- 0x10010002,
- 0x1073402e,
- 0x00ff007f, /* dc0 */
- 0x001133ff,
- 0x030f5317,
- 0x0f0f87ff,
- 0x5000007f,
- BP_OLED_SSI | BP_GAMEPAD
- }
-};
-
-static void stellaris_init(const char *kernel_filename, const char *cpu_model,
- stellaris_board_info *board)
-{
- static const int uart_irq[] = {5, 6, 33, 34};
- static const int timer_irq[] = {19, 21, 23, 35};
- static const uint32_t gpio_addr[7] =
- { 0x40004000, 0x40005000, 0x40006000, 0x40007000,
- 0x40024000, 0x40025000, 0x40026000};
- static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31};
-
- qemu_irq *pic;
- DeviceState *gpio_dev[7];
- qemu_irq gpio_in[7][8];
- qemu_irq gpio_out[7][8];
- qemu_irq adc;
- int sram_size;
- int flash_size;
- i2c_bus *i2c;
- DeviceState *dev;
- int i;
- int j;
-
- flash_size = ((board->dc0 & 0xffff) + 1) << 1;
- sram_size = (board->dc0 >> 18) + 1;
- pic = armv7m_init(flash_size, sram_size, kernel_filename, cpu_model);
-
- if (board->dc1 & (1 << 16)) {
- dev = sysbus_create_varargs("stellaris-adc", 0x40038000,
- pic[14], pic[15], pic[16], pic[17], NULL);
- adc = qdev_get_gpio_in(dev, 0);
- } else {
- adc = NULL;
- }
- for (i = 0; i < 4; i++) {
- if (board->dc2 & (0x10000 << i)) {
- dev = sysbus_create_simple("stellaris-gptm",
- 0x40030000 + i * 0x1000,
- pic[timer_irq[i]]);
- /* TODO: This is incorrect, but we get away with it because
- the ADC output is only ever pulsed. */
- qdev_connect_gpio_out(dev, 0, adc);
- }
- }
-
- stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr);
-
- for (i = 0; i < 7; i++) {
- if (board->dc4 & (1 << i)) {
- gpio_dev[i] = sysbus_create_simple("pl061", gpio_addr[i],
- pic[gpio_irq[i]]);
- for (j = 0; j < 8; j++) {
- gpio_in[i][j] = qdev_get_gpio_in(gpio_dev[i], j);
- gpio_out[i][j] = NULL;
- }
- }
- }
-
- if (board->dc2 & (1 << 12)) {
- dev = sysbus_create_simple("stellaris-i2c", 0x40020000, pic[8]);
- i2c = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
- if (board->peripherals & BP_OLED_I2C) {
- i2c_create_slave(i2c, "ssd0303", 0x3d);
- }
- }
-
- for (i = 0; i < 4; i++) {
- if (board->dc2 & (1 << i)) {
- sysbus_create_simple("pl011_luminary", 0x4000c000 + i * 0x1000,
- pic[uart_irq[i]]);
- }
- }
- if (board->dc2 & (1 << 4)) {
- dev = sysbus_create_simple("pl022", 0x40008000, pic[7]);
- if (board->peripherals & BP_OLED_SSI) {
- DeviceState *mux;
- void *bus;
-
- bus = qdev_get_child_bus(dev, "ssi");
- mux = ssi_create_slave(bus, "evb6965-ssi");
- gpio_out[GPIO_D][0] = qdev_get_gpio_in(mux, 0);
-
- bus = qdev_get_child_bus(mux, "ssi0");
- dev = ssi_create_slave(bus, "ssi-sd");
-
- bus = qdev_get_child_bus(mux, "ssi1");
- dev = ssi_create_slave(bus, "ssd0323");
- gpio_out[GPIO_C][7] = qdev_get_gpio_in(dev, 0);
-
- /* Make sure the select pin is high. */
- qemu_irq_raise(gpio_out[GPIO_D][0]);
- }
- }
- if (board->dc4 & (1 << 28)) {
- DeviceState *enet;
-
- qemu_check_nic_model(&nd_table[0], "stellaris");
-
- enet = qdev_create(NULL, "stellaris_enet");
- qdev_set_netdev(enet, &nd_table[0]);
- qdev_init(enet);
- sysbus_mmio_map(sysbus_from_qdev(enet), 0, 0x40048000);
- sysbus_connect_irq(sysbus_from_qdev(enet), 0, pic[42]);
- }
- if (board->peripherals & BP_GAMEPAD) {
- qemu_irq gpad_irq[5];
- static const int gpad_keycode[5] = { 0xc8, 0xd0, 0xcb, 0xcd, 0x1d };
-
- gpad_irq[0] = qemu_irq_invert(gpio_in[GPIO_E][0]); /* up */
- gpad_irq[1] = qemu_irq_invert(gpio_in[GPIO_E][1]); /* down */
- gpad_irq[2] = qemu_irq_invert(gpio_in[GPIO_E][2]); /* left */
- gpad_irq[3] = qemu_irq_invert(gpio_in[GPIO_E][3]); /* right */
- gpad_irq[4] = qemu_irq_invert(gpio_in[GPIO_F][1]); /* select */
-
- stellaris_gamepad_init(5, gpad_irq, gpad_keycode);
- }
- for (i = 0; i < 7; i++) {
- if (board->dc4 & (1 << i)) {
- for (j = 0; j < 8; j++) {
- if (gpio_out[i][j]) {
- qdev_connect_gpio_out(gpio_dev[i], j, gpio_out[i][j]);
- }
- }
- }
- }
-}
-
-/* FIXME: Figure out how to generate these from stellaris_boards. */
-static void lm3s811evb_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)
-{
- stellaris_init(kernel_filename, cpu_model, &stellaris_boards[0]);
-}
-
-static void lm3s6965evb_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)
-{
- stellaris_init(kernel_filename, cpu_model, &stellaris_boards[1]);
-}
-
-static QEMUMachine lm3s811evb_machine = {
- .name = "lm3s811evb",
- .desc = "Stellaris LM3S811EVB",
- .init = lm3s811evb_init,
-};
-
-static QEMUMachine lm3s6965evb_machine = {
- .name = "lm3s6965evb",
- .desc = "Stellaris LM3S6965EVB",
- .init = lm3s6965evb_init,
-};
-
-static void stellaris_machine_init(void)
-{
- qemu_register_machine(&lm3s811evb_machine);
- qemu_register_machine(&lm3s6965evb_machine);
-}
-
-machine_init(stellaris_machine_init);
-
static SSISlaveInfo stellaris_ssi_bus_info = {
.init = stellaris_ssi_bus_init,
.transfer = stellaris_ssi_bus_transfer
};
+static SysBusDeviceInfo ssys_info = {
+ .init = stellaris_sysctl_init,
+ .qdev.props = (DevicePropList[]) {
+ {.name = "enet", .type = PROP_TYPE_DEV},
+ {.name = "did0", .type = PROP_TYPE_INT},
+ {.name = "did1", .type = PROP_TYPE_INT},
+ {.name = "dc0", .type = PROP_TYPE_INT},
+ {.name = "dc1", .type = PROP_TYPE_INT},
+ {.name = "dc2", .type = PROP_TYPE_INT},
+ {.name = "dc3", .type = PROP_TYPE_INT},
+ {.name = "dc4", .type = PROP_TYPE_INT},
+ {.name = NULL}
+ }
+};
+
static void stellaris_register_devices(void)
{
+ sysbus_register_withprop("stellaris-sysctl", sizeof(ssys_state),
+ &ssys_info);
sysbus_register_dev("stellaris-i2c", sizeof(stellaris_i2c_state),
stellaris_i2c_init);
sysbus_register_dev("stellaris-gptm", sizeof(gptm_state),
diff --git a/hw/stellaris_enet.c b/hw/stellaris_enet.c
index 36fabd3..86df231 100644
--- a/hw/stellaris_enet.c
+++ b/hw/stellaris_enet.c
@@ -417,7 +417,7 @@ static void stellaris_enet_init(SysBusDevice *dev)
static void stellaris_enet_register_devices(void)
{
- sysbus_register_dev("stellaris_enet", sizeof(stellaris_enet_state),
+ sysbus_register_dev("stellaris-enet", sizeof(stellaris_enet_state),
stellaris_enet_init);
}
diff --git a/hw/stellaris_input.c b/hw/stellaris_input.c
deleted file mode 100644
index 33395a4..0000000
--- a/hw/stellaris_input.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Gamepad style buttons connected to IRQ/GPIO lines
- *
- * Copyright (c) 2007 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licenced under the GPL.
- */
-#include "hw.h"
-#include "devices.h"
-#include "console.h"
-
-typedef struct {
- qemu_irq irq;
- int keycode;
- int pressed;
-} gamepad_button;
-
-typedef struct {
- gamepad_button *buttons;
- int num_buttons;
- int extension;
-} gamepad_state;
-
-static void stellaris_gamepad_put_key(void * opaque, int keycode)
-{
- gamepad_state *s = (gamepad_state *)opaque;
- int i;
- int down;
-
- if (keycode == 0xe0 && !s->extension) {
- s->extension = 0x80;
- return;
- }
-
- down = (keycode & 0x80) == 0;
- keycode = (keycode & 0x7f) | s->extension;
-
- for (i = 0; i < s->num_buttons; i++) {
- if (s->buttons[i].keycode == keycode
- && s->buttons[i].pressed != down) {
- s->buttons[i].pressed = down;
- qemu_set_irq(s->buttons[i].irq, down);
- }
- }
-
- s->extension = 0;
-}
-
-static void stellaris_gamepad_save(QEMUFile *f, void *opaque)
-{
- gamepad_state *s = (gamepad_state *)opaque;
- int i;
-
- qemu_put_be32(f, s->extension);
- for (i = 0; i < s->num_buttons; i++)
- qemu_put_byte(f, s->buttons[i].pressed);
-}
-
-static int stellaris_gamepad_load(QEMUFile *f, void *opaque, int version_id)
-{
- gamepad_state *s = (gamepad_state *)opaque;
- int i;
-
- if (version_id != 1)
- return -EINVAL;
-
- s->extension = qemu_get_be32(f);
- for (i = 0; i < s->num_buttons; i++)
- s->buttons[i].pressed = qemu_get_byte(f);
-
- return 0;
-}
-
-/* Returns an array 5 ouput slots. */
-void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode)
-{
- gamepad_state *s;
- int i;
-
- s = (gamepad_state *)qemu_mallocz(sizeof (gamepad_state));
- s->buttons = (gamepad_button *)qemu_mallocz(n * sizeof (gamepad_button));
- for (i = 0; i < n; i++) {
- s->buttons[i].irq = irq[i];
- s->buttons[i].keycode = keycode[i];
- }
- s->num_buttons = n;
- qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s);
- register_savevm("stellaris_gamepad", -1, 1,
- stellaris_gamepad_save, stellaris_gamepad_load, s);
-}
diff --git a/pc-bios/boards/lm3s6965evb.dts b/pc-bios/boards/lm3s6965evb.dts
new file mode 100644
index 0000000..9bc0128
--- /dev/null
+++ b/pc-bios/boards/lm3s6965evb.dts
@@ -0,0 +1,212 @@
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cpu0: ARM,Cortex-M3@0 {
+ device_type = "cpu";
+ reg = <0>;
+ #interrupt-cells = <1>;
+ nvic = <&nvic>;
+ };
+ };
+ flash@0 {
+ device_type = "rom";
+ reg = <00000000 00040000>;
+ };
+ memory@0 {
+ device_type = "memory";
+ reg = <20000000 00010000>;
+ };
+ bitband@0 {
+ model = "ARM,bitband-memory";
+ reg = <22000000 02000000>;
+ base = <20000000>;
+ };
+ bitband@1 {
+ model = "ARM,bitband-memory";
+ reg = <42000000 02000000>;
+ base = <40000000>;
+ };
+ nvic: nvic@0 {
+ model = "armv7m_nvic";
+ interrupt-parent = <&cpu0>;
+ interrupts = <0>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ };
+ lm3s6965@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ qemu,fold-bus;
+ // TODO Watchdog at 0x40000000
+ gpioA: gpio@0 {
+ model = "pl061";
+ reg = <40004000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <0>;
+ qemu,gpio = <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ };
+ gpioB: gpio@1 {
+ model = "pl061";
+ reg = <40005000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <1>;
+ qemu,gpio = <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ };
+ gpioC: gpio@2 {
+ model = "pl061";
+ reg = <40006000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <2>;
+ qemu,gpio = <0 0 0 0 0 0 0 0 0 0 0 0 0 0 &oled 0>;
+ };
+ gpioD: gpio@3 {
+ model = "pl061";
+ reg = <40007000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <3>;
+ qemu,gpio = <&ssimux 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ float-high = <01>;
+ };
+ ssi@0 {
+ model = "pl022";
+ reg = <40008000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <7>;
+ ssimux: mux@0 {
+ model = "evb6965-ssi";
+ sd@0 {
+ model = "ssi-sd";
+ qemu,parent-bus = "ssi0";
+ };
+ oled: oled@1 {
+ model = "ssd0323";
+ qemu,parent-bus = "ssi1";
+ };
+ };
+ };
+ uart@0 {
+ model = "luminary,pl011";
+ reg = <4000c000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <5>;
+ };
+ uart@1 {
+ model = "luminary,pl011";
+ reg = <4000d000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <6>;
+ };
+ uart@2 {
+ model = "luminary,pl011";
+ reg = <4000e000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <d#33>;
+ };
+ i2c@0 {
+ model = "stellaris-i2c";
+ reg = <40020000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <8>;
+ };
+ i2c@1 {
+ model = "stellaris-i2c";
+ reg = <40021000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <d#37>;
+ };
+ gpioE: gpio@4 {
+ model = "pl061";
+ reg = <40024000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <4>;
+ qemu,gpio = <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ };
+ gpioF: gpio@5 {
+ model = "pl061";
+ reg = <40025000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <d#30>;
+ qemu,gpio = <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ };
+ gpioG: gpio@6 {
+ model = "pl061";
+ reg = <40026000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <d#31>;
+ qemu,gpio = <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ };
+ // TODO PWM at 0x40028000
+ // TODO QEI0 at 0x4002c000
+ // TODO QEI1 at 0x4002d000
+ gptm@0 {
+ model = "stellaris-gptm";
+ reg = <40030000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <d#19>;
+ qemu,gpio = <&adc0 0>;
+ };
+ gptm@1 {
+ model = "stellaris-gptm";
+ reg = <40031000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <d#21>;
+ qemu,gpio = <&adc0 0>;
+ };
+ gptm@2 {
+ model = "stellaris-gptm";
+ reg = <40032000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <d#23>;
+ qemu,gpio = <&adc0 0>;
+ };
+ gptm@3 {
+ model = "stellaris-gptm";
+ reg = <40033000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <d#35>;
+ qemu,gpio = <&adc0 0>;
+ };
+ adc0: adc@0 {
+ model = "stellaris-adc";
+ reg = <40038000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <d#14 d#15 d#16 d#17>;
+ };
+ // TODO Comparator at 0x4003c000
+ enet: enet@0 {
+ model = "stellaris-enet";
+ reg = <40048000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <d#42>;
+ };
+ // TODO Hybernation module at 0x400fc000
+ sysctl@0 {
+ model = "stellaris-sysctl";
+ reg = <400fe000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <d#28>;
+ did0 = <10010002>;
+ did1 = <1073402e>;
+ dc0 = <00ff007f>;
+ dc1 = <001133ff>;
+ dc2 = <030f5317>;
+ dc3 = <0f0f87ff>;
+ dc4 = <5000007f>;
+ enet = <&enet>;
+ };
+ buttons@0 {
+ model = "qemu,gpio-buttons";
+ keys = <800000c8 800000d0 800000cb 800000cd 8000001d>;
+ qemu,gpio = <&gpioE 0 &gpioE 1 &gpioE 2 &gpioE 3 &gpioF 1>;
+ };
+ };
+ chosen {
+ qemu {
+ bootstrap = "ARMv7-M";
+ };
+ };
+};
diff --git a/pc-bios/boards/lm3s811evb.dts b/pc-bios/boards/lm3s811evb.dts
new file mode 100644
index 0000000..977d080
--- /dev/null
+++ b/pc-bios/boards/lm3s811evb.dts
@@ -0,0 +1,155 @@
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cpu0: ARM,Cortex-M3@0 {
+ device_type = "cpu";
+ reg = <0>;
+ #interrupt-cells = <1>;
+ nvic = <&nvic>;
+ };
+ };
+ flash@0 {
+ device_type = "rom";
+ reg = <00000000 00010000>;
+ };
+ memory@0 {
+ device_type = "memory";
+ reg = <20000000 00002000>;
+ };
+ bitband@0 {
+ model = "ARM,bitband-memory";
+ reg = <22000000 02000000>;
+ base = <20000000>;
+ };
+ bitband@1 {
+ model = "ARM,bitband-memory";
+ reg = <42000000 02000000>;
+ base = <40000000>;
+ };
+ nvic: nvic@0 {
+ model = "armv7m_nvic";
+ interrupt-parent = <&cpu0>;
+ interrupts = <0>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ };
+ lm3s811@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ qemu,fold-bus;
+ // TODO Watchdog at 0x40000000
+ gpioA: gpio@0 {
+ model = "pl061";
+ reg = <40004000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <0>;
+ qemu,gpio = <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ };
+ gpioB: gpio@1 {
+ model = "pl061";
+ reg = <40005000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <1>;
+ qemu,gpio = <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ };
+ gpioC: gpio@2 {
+ model = "pl061";
+ reg = <40006000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <2>;
+ qemu,gpio = <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ };
+ gpioD: gpio@3 {
+ model = "pl061";
+ reg = <40007000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <3>;
+ qemu,gpio = <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ };
+ ssi@0 {
+ model = "pl022";
+ reg = <40008000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <7>;
+ };
+ uart@0 {
+ model = "luminary,pl011";
+ reg = <4000c000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <5>;
+ };
+ uart@1 {
+ model = "luminary,pl011";
+ reg = <4000d000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <6>;
+ };
+ i2c@0 {
+ model = "stellaris-i2c";
+ reg = <40020000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <8>;
+ oled@0 {
+ model = "ssd0303";
+ address = <3d>;
+ };
+ };
+ gpioE: gpio@4 {
+ model = "pl061";
+ reg = <40024000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <4>;
+ qemu,gpio = <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ };
+ // TODO PWM at 0x40028000
+ gptm@0 {
+ model = "stellaris-gptm";
+ reg = <40030000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <d#19>;
+ qemu,gpio = <&adc0 0>;
+ };
+ gptm@1 {
+ model = "stellaris-gptm";
+ reg = <40031000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <d#21>;
+ qemu,gpio = <&adc0 0>;
+ };
+ gptm@2 {
+ model = "stellaris-gptm";
+ reg = <40032000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <d#23>;
+ qemu,gpio = <&adc0 0>;
+ };
+ adc0: adc@0 {
+ model = "stellaris-adc";
+ reg = <40038000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <d#14 d#15 d#16 d#17>;
+ };
+ sysctl@0 {
+ model = "stellaris-sysctl";
+ reg = <400fe000>;
+ interrupt-parent = <&nvic>;
+ interrupts = <d#28>;
+ did0 = <00000000>;
+ did1 = <0032000e>;
+ dc0 = <001f001f>;
+ dc1 = <001132bf>;
+ dc2 = <01071013>;
+ dc3 = <3f0f01ff>;
+ dc4 = <0000001f>;
+ };
+ };
+ chosen {
+ qemu {
+ bootstrap = "ARMv7-M";
+ };
+ };
+};
^ permalink raw reply related [flat|nested] 61+ messages in thread