summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore8
-rw-r--r--Makefile.am55
-rwxr-xr-xbootstrap.sh5
l---------ccan/endian/LICENSE1
-rw-r--r--ccan/endian/_info55
-rw-r--r--ccan/endian/endian.h346
-rw-r--r--ccan/endian/test/compile_ok-constant.c12
-rw-r--r--ccan/endian/test/run.c106
-rw-r--r--ccan/short_types/LICENSE28
-rw-r--r--ccan/short_types/_info87
-rw-r--r--ccan/short_types/short_types.h35
-rw-r--r--ccan/short_types/test/run-endian.c20
-rw-r--r--ccan/short_types/test/run.c30
-rw-r--r--ccan/str/LICENSE28
-rw-r--r--ccan/str/_info52
-rw-r--r--ccan/str/str.c13
-rw-r--r--ccan/str/str.h120
-rw-r--r--ccan/str/test/compile_fail-STR_MAX_CHARS.c23
-rw-r--r--ccan/str/test/compile_fail-isalnum.c22
-rw-r--r--ccan/str/test/compile_fail-isalpha.c22
-rw-r--r--ccan/str/test/compile_fail-isascii.c22
-rw-r--r--ccan/str/test/compile_fail-isblank.c26
-rw-r--r--ccan/str/test/compile_fail-iscntrl.c22
-rw-r--r--ccan/str/test/compile_fail-isdigit.c22
-rw-r--r--ccan/str/test/compile_fail-islower.c22
-rw-r--r--ccan/str/test/compile_fail-isprint.c22
-rw-r--r--ccan/str/test/compile_fail-ispunct.c22
-rw-r--r--ccan/str/test/compile_fail-isspace.c22
-rw-r--r--ccan/str/test/compile_fail-isupper.c22
-rw-r--r--ccan/str/test/compile_fail-isxdigit.c22
-rw-r--r--ccan/str/test/compile_fail-strchr.c18
-rw-r--r--ccan/str/test/compile_fail-strrchr.c18
-rw-r--r--ccan/str/test/compile_fail-strstr.c18
-rw-r--r--ccan/str/test/debug.c5
-rw-r--r--ccan/str/test/run-STR_MAX_CHARS.c66
-rw-r--r--ccan/str/test/run.c109
-rw-r--r--configure.ac3
-rw-r--r--fake.dts16
-rw-r--r--libfdt/Makefile.libfdt11
-rw-r--r--libfdt/TODO3
-rw-r--r--libfdt/fdt.c251
-rw-r--r--libfdt/fdt.h111
-rw-r--r--libfdt/fdt_addresses.c96
-rw-r--r--libfdt/fdt_empty_tree.c84
-rw-r--r--libfdt/fdt_overlay.c676
-rw-r--r--libfdt/fdt_ro.c703
-rw-r--r--libfdt/fdt_rw.c490
-rw-r--r--libfdt/fdt_strerror.c102
-rw-r--r--libfdt/fdt_sw.c288
-rw-r--r--libfdt/fdt_wip.c139
-rw-r--r--libfdt/libfdt.h1803
-rw-r--r--libfdt/libfdt_env.h112
-rw-r--r--libfdt/libfdt_internal.h95
-rw-r--r--libfdt/version.lds68
-rw-r--r--libpdbg/adu.c179
-rw-r--r--libpdbg/bmcfsi.c130
-rw-r--r--libpdbg/cfam.c191
-rw-r--r--libpdbg/chip.c242
-rw-r--r--libpdbg/compiler.h53
-rw-r--r--libpdbg/device.c943
-rw-r--r--libpdbg/device.h251
-rw-r--r--libpdbg/fakepib.c38
-rw-r--r--libpdbg/i2c.c41
-rw-r--r--libpdbg/kernel.c87
-rw-r--r--libpdbg/operations.h49
-rw-r--r--libpdbg/target.c278
-rw-r--r--libpdbg/target.h120
-rw-r--r--p8-fsi.dts.m449
-rw-r--r--p8-i2c.dts.m440
-rw-r--r--p8-pib.dts.m445
-rw-r--r--p9-fsi.dtsi47
-rw-r--r--p9-kernel.dts48
-rw-r--r--p9r-fsi.dts16
-rw-r--r--p9w-fsi.dts16
-rw-r--r--p9z-fsi.dts16
-rw-r--r--src/main.c906
76 files changed, 9273 insertions, 1089 deletions
diff --git a/.gitignore b/.gitignore
index 0a0f08a..3d0cb89 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,11 @@ config.h
config.log
config.status
libpdbg.a
+.libs
+config.guess
+config.sub
+*.la
+*.lo
+ltmain.sh
+libtool
+m4
diff --git a/Makefile.am b/Makefile.am
index 073c67b..8309ccb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,20 +1,57 @@
GIT_SHA1 = `git --work-tree=$(top_srcdir) --git-dir=$(top_srcdir)/.git describe --always --long --dirty || echo unknown`
+include libfdt/Makefile.libfdt
+
bin_PROGRAMS = pdbg
-lib_LIBRARIES = libpdbg.a
+ACLOCAL_AMFLAGS = -Im4
AM_CFLAGS = -I$(top_srcdir)/ccan/array_size -Wall
pdbg_SOURCES = \
src/main.c
-pdbg_LDADD = libpdbg.a
-pdbg_CFLAGS = -I$(top_srcdir)/libpdbg -Wall -DGIT_SHA1=\"${GIT_SHA1}\"
+pdbg_LDADD = fake.dtb.o p8-fsi.dtb.o p8-i2c.dtb.o p9w-fsi.dtb.o \
+ p9z-fsi.dtb.o p9r-fsi.dtb.o p9-kernel.dtb.o
-libpdbg_a_SOURCES = \
- libpdbg/target.c \
- libpdbg/cfam.c \
- libpdbg/adu.c \
- libpdbg/bmcfsi.c \
+pdbg_LDFLAGS = -L.libs -lpdbg -lfdt
+pdbg_CFLAGS = -I$(top_srcdir)/libpdbg -Wall -DGIT_SHA1=\"${GIT_SHA1}\" -g
+
+lib_LTLIBRARIES = libpdbg.la libfdt.la
+
+libfdt_la_CFLAGS = -I$(top_srcdir)/libfdt -DHAVE_LITTLE_ENDIAN
+libpdbg_la_CFLAGS = -I$(top_srcdir)/libfdt -DHAVE_LITTLE_ENDIAN
+
+libfdt_la_SOURCES = \
+ libfdt/fdt.c \
+ libfdt/fdt_ro.c \
+ libfdt/fdt_wip.c \
+ libfdt/fdt_sw.c \
+ libfdt/fdt_rw.c \
+ libfdt/fdt_strerror.c \
+ libfdt/fdt_empty_tree.c \
+ libfdt/fdt_addresses.c \
+ libfdt/fdt_overlay.c
+
+libpdbg_la_SOURCES = \
libpdbg/kernel.c \
+ libpdbg/fakepib.c \
+ libpdbg/chip.c \
+ libpdbg/bmcfsi.c \
+ libpdbg/cfam.c \
libpdbg/i2c.c \
- libpdbg/chip.c
+ libpdbg/adu.c \
+ libpdbg/device.c \
+ libpdbg/target.c
+
+%.dts: %.dts.m4
+ m4 -Isrc $< > $@
+
+%.dtb.o: %.dts
+ dtc -I dts $< -O dtb > $@.tmp
+
+# We need to align the output as some processor/kernel
+# combinations can't deal with the alignment errors when
+# unflattening the device-tree
+ dd if=$@.tmp of=$@ ibs=16 conv=sync
+ rm $@.tmp
+ objcopy -I binary -O elf32-littlearm -B arm $@ $@
+# objcopy -I binary -O elf64-x86-64 -B i386:x86-64 $@ $@
diff --git a/bootstrap.sh b/bootstrap.sh
index 600a900..7c590a3 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -1,6 +1,3 @@
#!/bin/sh
-aclocal
-autoconf
-autoheader
-automake --add-missing
+autoreconf -i
diff --git a/ccan/endian/LICENSE b/ccan/endian/LICENSE
new file mode 120000
index 0000000..b7951da
--- /dev/null
+++ b/ccan/endian/LICENSE
@@ -0,0 +1 @@
+../../licenses/CC0 \ No newline at end of file
diff --git a/ccan/endian/_info b/ccan/endian/_info
new file mode 100644
index 0000000..efe5a8b
--- /dev/null
+++ b/ccan/endian/_info
@@ -0,0 +1,55 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * endian - endian conversion macros for simple types
+ *
+ * Portable protocols (such as on-disk formats, or network protocols)
+ * are often defined to be a particular endian: little-endian (least
+ * significant bytes first) or big-endian (most significant bytes
+ * first).
+ *
+ * Similarly, some CPUs lay out values in memory in little-endian
+ * order (most commonly, Intel's 8086 and derivatives), or big-endian
+ * order (almost everyone else).
+ *
+ * This module provides conversion routines, inspired by the linux kernel.
+ * It also provides leint32_t, beint32_t etc typedefs, which are annotated for
+ * the sparse checker.
+ *
+ * Example:
+ * #include <stdio.h>
+ * #include <err.h>
+ * #include <ccan/endian/endian.h>
+ *
+ * //
+ * int main(int argc, char *argv[])
+ * {
+ * uint32_t value;
+ *
+ * if (argc != 2)
+ * errx(1, "Usage: %s <value>", argv[0]);
+ *
+ * value = atoi(argv[1]);
+ * printf("native: %08x\n", value);
+ * printf("little-endian: %08x\n", cpu_to_le32(value));
+ * printf("big-endian: %08x\n", cpu_to_be32(value));
+ * printf("byte-reversed: %08x\n", bswap_32(value));
+ * exit(0);
+ * }
+ *
+ * License: License: CC0 (Public domain)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0)
+ /* Nothing */
+ return 0;
+
+ return 1;
+}
diff --git a/ccan/endian/endian.h b/ccan/endian/endian.h
new file mode 100644
index 0000000..0c99cc8
--- /dev/null
+++ b/ccan/endian/endian.h
@@ -0,0 +1,346 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_ENDIAN_H
+#define CCAN_ENDIAN_H
+#include <stdint.h>
+#include "config.h"
+
+/**
+ * BSWAP_16 - reverse bytes in a constant uint16_t value.
+ * @val: constant value whose bytes to swap.
+ *
+ * Designed to be usable in constant-requiring initializers.
+ *
+ * Example:
+ * struct mystruct {
+ * char buf[BSWAP_16(0x1234)];
+ * };
+ */
+#define BSWAP_16(val) \
+ ((((uint16_t)(val) & 0x00ff) << 8) \
+ | (((uint16_t)(val) & 0xff00) >> 8))
+
+/**
+ * BSWAP_32 - reverse bytes in a constant uint32_t value.
+ * @val: constant value whose bytes to swap.
+ *
+ * Designed to be usable in constant-requiring initializers.
+ *
+ * Example:
+ * struct mystruct {
+ * char buf[BSWAP_32(0xff000000)];
+ * };
+ */
+#define BSWAP_32(val) \
+ ((((uint32_t)(val) & 0x000000ff) << 24) \
+ | (((uint32_t)(val) & 0x0000ff00) << 8) \
+ | (((uint32_t)(val) & 0x00ff0000) >> 8) \
+ | (((uint32_t)(val) & 0xff000000) >> 24))
+
+/**
+ * BSWAP_64 - reverse bytes in a constant uint64_t value.
+ * @val: constantvalue whose bytes to swap.
+ *
+ * Designed to be usable in constant-requiring initializers.
+ *
+ * Example:
+ * struct mystruct {
+ * char buf[BSWAP_64(0xff00000000000000ULL)];
+ * };
+ */
+#define BSWAP_64(val) \
+ ((((uint64_t)(val) & 0x00000000000000ffULL) << 56) \
+ | (((uint64_t)(val) & 0x000000000000ff00ULL) << 40) \
+ | (((uint64_t)(val) & 0x0000000000ff0000ULL) << 24) \
+ | (((uint64_t)(val) & 0x00000000ff000000ULL) << 8) \
+ | (((uint64_t)(val) & 0x000000ff00000000ULL) >> 8) \
+ | (((uint64_t)(val) & 0x0000ff0000000000ULL) >> 24) \
+ | (((uint64_t)(val) & 0x00ff000000000000ULL) >> 40) \
+ | (((uint64_t)(val) & 0xff00000000000000ULL) >> 56))
+
+#if HAVE_BYTESWAP_H
+#include <byteswap.h>
+#else
+/**
+ * bswap_16 - reverse bytes in a uint16_t value.
+ * @val: value whose bytes to swap.
+ *
+ * Example:
+ * // Output contains "1024 is 4 as two bytes reversed"
+ * printf("1024 is %u as two bytes reversed\n", bswap_16(1024));
+ */
+static inline uint16_t bswap_16(uint16_t val)
+{
+ return BSWAP_16(val);
+}
+
+/**
+ * bswap_32 - reverse bytes in a uint32_t value.
+ * @val: value whose bytes to swap.
+ *
+ * Example:
+ * // Output contains "1024 is 262144 as four bytes reversed"
+ * printf("1024 is %u as four bytes reversed\n", bswap_32(1024));
+ */
+static inline uint32_t bswap_32(uint32_t val)
+{
+ return BSWAP_32(val);
+}
+#endif /* !HAVE_BYTESWAP_H */
+
+#if !HAVE_BSWAP_64
+/**
+ * bswap_64 - reverse bytes in a uint64_t value.
+ * @val: value whose bytes to swap.
+ *
+ * Example:
+ * // Output contains "1024 is 1125899906842624 as eight bytes reversed"
+ * printf("1024 is %llu as eight bytes reversed\n",
+ * (unsigned long long)bswap_64(1024));
+ */
+static inline uint64_t bswap_64(uint64_t val)
+{
+ return BSWAP_64(val);
+}
+#endif
+
+/* Sanity check the defines. We don't handle weird endianness. */
+#if !HAVE_LITTLE_ENDIAN && !HAVE_BIG_ENDIAN
+#error "Unknown endian"
+#elif HAVE_LITTLE_ENDIAN && HAVE_BIG_ENDIAN
+#error "Can't compile for both big and little endian."
+#endif
+
+#ifdef __CHECKER__
+/* sparse needs forcing to remove bitwise attribute from ccan/short_types */
+#define ENDIAN_CAST __attribute__((force))
+#define ENDIAN_TYPE __attribute__((bitwise))
+#else
+#define ENDIAN_CAST
+#define ENDIAN_TYPE
+#endif
+
+typedef uint64_t ENDIAN_TYPE leint64_t;
+typedef uint64_t ENDIAN_TYPE beint64_t;
+typedef uint32_t ENDIAN_TYPE leint32_t;
+typedef uint32_t ENDIAN_TYPE beint32_t;
+typedef uint16_t ENDIAN_TYPE leint16_t;
+typedef uint16_t ENDIAN_TYPE beint16_t;
+
+#if HAVE_LITTLE_ENDIAN
+/**
+ * CPU_TO_LE64 - convert a constant uint64_t value to little-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_LE64(native) ((ENDIAN_CAST leint64_t)(native))
+
+/**
+ * CPU_TO_LE32 - convert a constant uint32_t value to little-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_LE32(native) ((ENDIAN_CAST leint32_t)(native))
+
+/**
+ * CPU_TO_LE16 - convert a constant uint16_t value to little-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_LE16(native) ((ENDIAN_CAST leint16_t)(native))
+
+/**
+ * LE64_TO_CPU - convert a little-endian uint64_t constant
+ * @le_val: little-endian constant to convert
+ */
+#define LE64_TO_CPU(le_val) ((ENDIAN_CAST uint64_t)(le_val))
+
+/**
+ * LE32_TO_CPU - convert a little-endian uint32_t constant
+ * @le_val: little-endian constant to convert
+ */
+#define LE32_TO_CPU(le_val) ((ENDIAN_CAST uint32_t)(le_val))
+
+/**
+ * LE16_TO_CPU - convert a little-endian uint16_t constant
+ * @le_val: little-endian constant to convert
+ */
+#define LE16_TO_CPU(le_val) ((ENDIAN_CAST uint16_t)(le_val))
+
+#else /* ... HAVE_BIG_ENDIAN */
+#define CPU_TO_LE64(native) ((ENDIAN_CAST leint64_t)BSWAP_64(native))
+#define CPU_TO_LE32(native) ((ENDIAN_CAST leint32_t)BSWAP_32(native))
+#define CPU_TO_LE16(native) ((ENDIAN_CAST leint16_t)BSWAP_16(native))
+#define LE64_TO_CPU(le_val) BSWAP_64((ENDIAN_CAST uint64_t)le_val)
+#define LE32_TO_CPU(le_val) BSWAP_32((ENDIAN_CAST uint32_t)le_val)
+#define LE16_TO_CPU(le_val) BSWAP_16((ENDIAN_CAST uint16_t)le_val)
+#endif /* HAVE_BIG_ENDIAN */
+
+#if HAVE_BIG_ENDIAN
+/**
+ * CPU_TO_BE64 - convert a constant uint64_t value to big-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_BE64(native) ((ENDIAN_CAST beint64_t)(native))
+
+/**
+ * CPU_TO_BE32 - convert a constant uint32_t value to big-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_BE32(native) ((ENDIAN_CAST beint32_t)(native))
+
+/**
+ * CPU_TO_BE16 - convert a constant uint16_t value to big-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_BE16(native) ((ENDIAN_CAST beint16_t)(native))
+
+/**
+ * BE64_TO_CPU - convert a big-endian uint64_t constant
+ * @le_val: big-endian constant to convert
+ */
+#define BE64_TO_CPU(le_val) ((ENDIAN_CAST uint64_t)(le_val))
+
+/**
+ * BE32_TO_CPU - convert a big-endian uint32_t constant
+ * @le_val: big-endian constant to convert
+ */
+#define BE32_TO_CPU(le_val) ((ENDIAN_CAST uint32_t)(le_val))
+
+/**
+ * BE16_TO_CPU - convert a big-endian uint16_t constant
+ * @le_val: big-endian constant to convert
+ */
+#define BE16_TO_CPU(le_val) ((ENDIAN_CAST uint16_t)(le_val))
+
+#else /* ... HAVE_LITTLE_ENDIAN */
+#define CPU_TO_BE64(native) ((ENDIAN_CAST beint64_t)BSWAP_64(native))
+#define CPU_TO_BE32(native) ((ENDIAN_CAST beint32_t)BSWAP_32(native))
+#define CPU_TO_BE16(native) ((ENDIAN_CAST beint16_t)BSWAP_16(native))
+#define BE64_TO_CPU(le_val) BSWAP_64((ENDIAN_CAST uint64_t)le_val)
+#define BE32_TO_CPU(le_val) BSWAP_32((ENDIAN_CAST uint32_t)le_val)
+#define BE16_TO_CPU(le_val) BSWAP_16((ENDIAN_CAST uint16_t)le_val)
+#endif /* HAVE_LITTE_ENDIAN */
+
+
+/**
+ * cpu_to_le64 - convert a uint64_t value to little-endian
+ * @native: value to convert
+ */
+static inline leint64_t cpu_to_le64(uint64_t native)
+{
+ return CPU_TO_LE64(native);
+}
+
+/**
+ * cpu_to_le32 - convert a uint32_t value to little-endian
+ * @native: value to convert
+ */
+static inline leint32_t cpu_to_le32(uint32_t native)
+{
+ return CPU_TO_LE32(native);
+}
+
+/**
+ * cpu_to_le16 - convert a uint16_t value to little-endian
+ * @native: value to convert
+ */
+static inline leint16_t cpu_to_le16(uint16_t native)
+{
+ return CPU_TO_LE16(native);
+}
+
+/**
+ * le64_to_cpu - convert a little-endian uint64_t value
+ * @le_val: little-endian value to convert
+ */
+static inline uint64_t le64_to_cpu(leint64_t le_val)
+{
+ return LE64_TO_CPU(le_val);
+}
+
+/**
+ * le32_to_cpu - convert a little-endian uint32_t value
+ * @le_val: little-endian value to convert
+ */
+static inline uint32_t le32_to_cpu(leint32_t le_val)
+{
+ return LE32_TO_CPU(le_val);
+}
+
+/**
+ * le16_to_cpu - convert a little-endian uint16_t value
+ * @le_val: little-endian value to convert
+ */
+static inline uint16_t le16_to_cpu(leint16_t le_val)
+{
+ return LE16_TO_CPU(le_val);
+}
+
+/**
+ * cpu_to_be64 - convert a uint64_t value to big endian.
+ * @native: value to convert
+ */
+static inline beint64_t cpu_to_be64(uint64_t native)
+{
+ return CPU_TO_BE64(native);
+}
+
+/**
+ * cpu_to_be32 - convert a uint32_t value to big endian.
+ * @native: value to convert
+ */
+static inline beint32_t cpu_to_be32(uint32_t native)
+{
+ return CPU_TO_BE32(native);
+}
+
+/**
+ * cpu_to_be16 - convert a uint16_t value to big endian.
+ * @native: value to convert
+ */
+static inline beint16_t cpu_to_be16(uint16_t native)
+{
+ return CPU_TO_BE16(native);
+}
+
+/**
+ * be64_to_cpu - convert a big-endian uint64_t value
+ * @be_val: big-endian value to convert
+ */
+static inline uint64_t be64_to_cpu(beint64_t be_val)
+{
+ return BE64_TO_CPU(be_val);
+}
+
+/**
+ * be32_to_cpu - convert a big-endian uint32_t value
+ * @be_val: big-endian value to convert
+ */
+static inline uint32_t be32_to_cpu(beint32_t be_val)
+{
+ return BE32_TO_CPU(be_val);
+}
+
+/**
+ * be16_to_cpu - convert a big-endian uint16_t value
+ * @be_val: big-endian value to convert
+ */
+static inline uint16_t be16_to_cpu(beint16_t be_val)
+{
+ return BE16_TO_CPU(be_val);
+}
+
+/* Whichever they include first, they get these definitions. */
+#ifdef CCAN_SHORT_TYPES_H
+/**
+ * be64/be32/be16 - 64/32/16 bit big-endian representation.
+ */
+typedef beint64_t be64;
+typedef beint32_t be32;
+typedef beint16_t be16;
+
+/**
+ * le64/le32/le16 - 64/32/16 bit little-endian representation.
+ */
+typedef leint64_t le64;
+typedef leint32_t le32;
+typedef leint16_t le16;
+#endif
+#endif /* CCAN_ENDIAN_H */
diff --git a/ccan/endian/test/compile_ok-constant.c b/ccan/endian/test/compile_ok-constant.c
new file mode 100644
index 0000000..1aef1dd
--- /dev/null
+++ b/ccan/endian/test/compile_ok-constant.c
@@ -0,0 +1,12 @@
+#include <ccan/endian/endian.h>
+
+struct foo {
+ char one[BSWAP_16(0xFF00)];
+ char two[BSWAP_32(0xFF000000)];
+ char three[BSWAP_64(0xFF00000000000000ULL)];
+};
+
+int main(void)
+{
+ return 0;
+}
diff --git a/ccan/endian/test/run.c b/ccan/endian/test/run.c
new file mode 100644
index 0000000..9bf47f1
--- /dev/null
+++ b/ccan/endian/test/run.c
@@ -0,0 +1,106 @@
+#include <ccan/endian/endian.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+ union {
+ uint64_t u64;
+ unsigned char u64_bytes[8];
+ } u64;
+ union {
+ uint32_t u32;
+ unsigned char u32_bytes[4];
+ } u32;
+ union {
+ uint16_t u16;
+ unsigned char u16_bytes[2];
+ } u16;
+
+ plan_tests(48);
+
+ /* Straight swap tests. */
+ u64.u64_bytes[0] = 0x00;
+ u64.u64_bytes[1] = 0x11;
+ u64.u64_bytes[2] = 0x22;
+ u64.u64_bytes[3] = 0x33;
+ u64.u64_bytes[4] = 0x44;
+ u64.u64_bytes[5] = 0x55;
+ u64.u64_bytes[6] = 0x66;
+ u64.u64_bytes[7] = 0x77;
+ u64.u64 = bswap_64(u64.u64);
+ ok1(u64.u64_bytes[7] == 0x00);
+ ok1(u64.u64_bytes[6] == 0x11);
+ ok1(u64.u64_bytes[5] == 0x22);
+ ok1(u64.u64_bytes[4] == 0x33);
+ ok1(u64.u64_bytes[3] == 0x44);
+ ok1(u64.u64_bytes[2] == 0x55);
+ ok1(u64.u64_bytes[1] == 0x66);
+ ok1(u64.u64_bytes[0] == 0x77);
+
+ u32.u32_bytes[0] = 0x00;
+ u32.u32_bytes[1] = 0x11;
+ u32.u32_bytes[2] = 0x22;
+ u32.u32_bytes[3] = 0x33;
+ u32.u32 = bswap_32(u32.u32);
+ ok1(u32.u32_bytes[3] == 0x00);
+ ok1(u32.u32_bytes[2] == 0x11);
+ ok1(u32.u32_bytes[1] == 0x22);
+ ok1(u32.u32_bytes[0] == 0x33);
+
+ u16.u16_bytes[0] = 0x00;
+ u16.u16_bytes[1] = 0x11;
+ u16.u16 = bswap_16(u16.u16);
+ ok1(u16.u16_bytes[1] == 0x00);
+ ok1(u16.u16_bytes[0] == 0x11);
+
+ /* Endian tests. */
+ u64.u64 = cpu_to_le64(0x0011223344556677ULL);
+ ok1(u64.u64_bytes[0] == 0x77);
+ ok1(u64.u64_bytes[1] == 0x66);
+ ok1(u64.u64_bytes[2] == 0x55);
+ ok1(u64.u64_bytes[3] == 0x44);
+ ok1(u64.u64_bytes[4] == 0x33);
+ ok1(u64.u64_bytes[5] == 0x22);
+ ok1(u64.u64_bytes[6] == 0x11);
+ ok1(u64.u64_bytes[7] == 0x00);
+ ok1(le64_to_cpu(u64.u64) == 0x0011223344556677ULL);
+
+ u64.u64 = cpu_to_be64(0x0011223344556677ULL);
+ ok1(u64.u64_bytes[7] == 0x77);
+ ok1(u64.u64_bytes[6] == 0x66);
+ ok1(u64.u64_bytes[5] == 0x55);
+ ok1(u64.u64_bytes[4] == 0x44);
+ ok1(u64.u64_bytes[3] == 0x33);
+ ok1(u64.u64_bytes[2] == 0x22);
+ ok1(u64.u64_bytes[1] == 0x11);
+ ok1(u64.u64_bytes[0] == 0x00);
+ ok1(be64_to_cpu(u64.u64) == 0x0011223344556677ULL);
+
+ u32.u32 = cpu_to_le32(0x00112233);
+ ok1(u32.u32_bytes[0] == 0x33);
+ ok1(u32.u32_bytes[1] == 0x22);
+ ok1(u32.u32_bytes[2] == 0x11);
+ ok1(u32.u32_bytes[3] == 0x00);
+ ok1(le32_to_cpu(u32.u32) == 0x00112233);
+
+ u32.u32 = cpu_to_be32(0x00112233);
+ ok1(u32.u32_bytes[3] == 0x33);
+ ok1(u32.u32_bytes[2] == 0x22);
+ ok1(u32.u32_bytes[1] == 0x11);
+ ok1(u32.u32_bytes[0] == 0x00);
+ ok1(be32_to_cpu(u32.u32) == 0x00112233);
+
+ u16.u16 = cpu_to_le16(0x0011);
+ ok1(u16.u16_bytes[0] == 0x11);
+ ok1(u16.u16_bytes[1] == 0x00);
+ ok1(le16_to_cpu(u16.u16) == 0x0011);
+
+ u16.u16 = cpu_to_be16(0x0011);
+ ok1(u16.u16_bytes[1] == 0x11);
+ ok1(u16.u16_bytes[0] == 0x00);
+ ok1(be16_to_cpu(u16.u16) == 0x0011);
+
+ exit(exit_status());
+}
diff --git a/ccan/short_types/LICENSE b/ccan/short_types/LICENSE
new file mode 100644
index 0000000..feb9b11
--- /dev/null
+++ b/ccan/short_types/LICENSE
@@ -0,0 +1,28 @@
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following:
+
+ the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work;
+ moral rights retained by the original author(s) and/or performer(s);
+ publicity and privacy rights pertaining to a person's image or likeness depicted in a Work;
+ rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below;
+ rights protecting the extraction, dissemination, use and reuse of data in a Work;
+ database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and
+ other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document.
+ Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law.
+ Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work.
+ Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.
diff --git a/ccan/short_types/_info b/ccan/short_types/_info
new file mode 100644
index 0000000..909e4e3
--- /dev/null
+++ b/ccan/short_types/_info
@@ -0,0 +1,87 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * short_types - shorter names for standard integer types
+ *
+ * "C is a Spartan language, and so should your naming be."
+ * -- Linus Torvalds
+ *
+ * The short_types header provides for convenient abbreviations for the
+ * posixly-damned uint32_t types. If ccan/endian/endian.h is included,
+ * it also provides be32/le32 for explicitly annotating types of specific
+ * endian.
+ *
+ * Include this header, if only to stop people using these identifiers
+ * for other things!
+ *
+ * Example:
+ * #include <stdint.h>
+ * #include <string.h>
+ * #include <stdio.h>
+ * #include <ccan/short_types/short_types.h>
+ *
+ * // Print nonsensical numerical comparison of POSIX vs. short_types.
+ * #define stringify_1(x) #x
+ * #define stringify(x) stringify_1(x)
+ *
+ * static void evaluate(size_t size, const char *posix, const char *sht,
+ * unsigned int *posix_total, unsigned int *sht_total,
+ * unsigned int *size_total)
+ * {
+ * printf("\t%ssigned %s: POSIX %zu%%, short %zu%%\n",
+ * sht[0] == 'u' ? "un" : "",
+ * sht+1,
+ * strlen(posix)*100 / size,
+ * strlen(sht)*100 / size);
+ * *posix_total += strlen(posix);
+ * *sht_total += strlen(sht);
+ * *size_total += size;
+ * }
+ *
+ * #define EVALUATE(psx, short, pt, st, t) \
+ * evaluate(sizeof(psx), stringify(psx), stringify(sht), pt, st, t)
+ *
+ * int main(void)
+ * {
+ * unsigned int posix_total = 0, sht_total = 0, size_total = 0;
+ *
+ * printf("Comparing size of type vs size of name:\n");
+ *
+ * EVALUATE(uint8_t, u8, &posix_total, &sht_total, &size_total);
+ * EVALUATE(int8_t, s8, &posix_total, &sht_total, &size_total);
+ * EVALUATE(uint16_t, u16, &posix_total, &sht_total, &size_total);
+ * EVALUATE(int16_t, s16, &posix_total, &sht_total, &size_total);
+ * EVALUATE(uint32_t, u32, &posix_total, &sht_total, &size_total);
+ * EVALUATE(int32_t, s32, &posix_total, &sht_total, &size_total);
+ * EVALUATE(uint64_t, u64, &posix_total, &sht_total, &size_total);
+ * EVALUATE(int64_t, s64, &posix_total, &sht_total, &size_total);
+ *
+ * printf("Conclusion:\n"
+ * "\tPOSIX is %u%% LESS efficient than binary.\n"
+ * "\tshort_types.h is %u%% MORE efficient than binary.\n",
+ * (posix_total - size_total) * 100 / size_total,
+ * (size_total - sht_total) * 100 / size_total);
+ * return 0;
+ * }
+ *
+ * License: CC0 (Public domain)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ return 0;
+ }
+
+ if (strcmp(argv[1], "testdepends") == 0) {
+ printf("ccan/endian\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/ccan/short_types/short_types.h b/ccan/short_types/short_types.h
new file mode 100644
index 0000000..175377e
--- /dev/null
+++ b/ccan/short_types/short_types.h
@@ -0,0 +1,35 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_SHORT_TYPES_H
+#define CCAN_SHORT_TYPES_H
+#include <stdint.h>
+
+/**
+ * u64/s64/u32/s32/u16/s16/u8/s8 - short names for explicitly-sized types.
+ */
+typedef uint64_t u64;
+typedef int64_t s64;
+typedef uint32_t u32;
+typedef int32_t s32;
+typedef uint16_t u16;
+typedef int16_t s16;
+typedef uint8_t u8;
+typedef int8_t s8;
+
+/* Whichever they include first, they get these definitions. */
+#ifdef CCAN_ENDIAN_H
+/**
+ * be64/be32/be16 - 64/32/16 bit big-endian representation.
+ */
+typedef beint64_t be64;
+typedef beint32_t be32;
+typedef beint16_t be16;
+
+/**
+ * le64/le32/le16 - 64/32/16 bit little-endian representation.
+ */
+typedef leint64_t le64;
+typedef leint32_t le32;
+typedef leint16_t le16;
+#endif
+
+#endif /* CCAN_SHORT_TYPES_H */
diff --git a/ccan/short_types/test/run-endian.c b/ccan/short_types/test/run-endian.c
new file mode 100644
index 0000000..108e3ab
--- /dev/null
+++ b/ccan/short_types/test/run-endian.c
@@ -0,0 +1,20 @@
+#include <ccan/endian/endian.h>
+#include <ccan/short_types/short_types.h>
+#include <ccan/tap/tap.h>
+#include <stdlib.h>
+#include <err.h>
+
+int main(void)
+{
+ plan_tests(6);
+
+ ok1(sizeof(be64) == 8);
+ ok1(sizeof(be32) == 4);
+ ok1(sizeof(be16) == 2);
+
+ ok1(sizeof(le64) == 8);
+ ok1(sizeof(le32) == 4);
+ ok1(sizeof(le16) == 2);
+
+ return exit_status();
+}
diff --git a/ccan/short_types/test/run.c b/ccan/short_types/test/run.c
new file mode 100644
index 0000000..2bff4b7
--- /dev/null
+++ b/ccan/short_types/test/run.c
@@ -0,0 +1,30 @@
+#include <ccan/short_types/short_types.h>
+#include <ccan/tap/tap.h>
+#include <stdlib.h>
+#include <err.h>
+
+int main(void)
+{
+ plan_tests(16);
+
+ ok1(sizeof(u64) == 8);
+ ok1(sizeof(s64) == 8);
+ ok1(sizeof(u32) == 4);
+ ok1(sizeof(s32) == 4);
+ ok1(sizeof(u16) == 2);
+ ok1(sizeof(s16) == 2);
+ ok1(sizeof(u8) == 1);
+ ok1(sizeof(s8) == 1);
+
+ /* Signedness tests. */
+ ok1((u64)-1 > 0);
+ ok1((u32)-1 > 0);
+ ok1((u16)-1 > 0);
+ ok1((u8)-1 > 0);
+ ok1((s64)-1 < 0);
+ ok1((s32)-1 < 0);
+ ok1((s16)-1 < 0);
+ ok1((s8)-1 < 0);
+
+ return exit_status();
+}
diff --git a/ccan/str/LICENSE b/ccan/str/LICENSE
new file mode 100644
index 0000000..feb9b11
--- /dev/null
+++ b/ccan/str/LICENSE
@@ -0,0 +1,28 @@
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following:
+
+ the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work;
+ moral rights retained by the original author(s) and/or performer(s);
+ publicity and privacy rights pertaining to a person's image or likeness depicted in a Work;
+ rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below;
+ rights protecting the extraction, dissemination, use and reuse of data in a Work;
+ database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and
+ other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document.
+ Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law.
+ Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work.
+ Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.
diff --git a/ccan/str/_info b/ccan/str/_info
new file mode 100644
index 0000000..548f059
--- /dev/null
+++ b/ccan/str/_info
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+
+/**
+ * str - string helper routines
+ *
+ * This is a grab bag of functions for string operations, designed to enhance
+ * the standard string.h.
+ *
+ * Note that if you define CCAN_STR_DEBUG, you will get extra compile
+ * checks on common misuses of the following functions (they will now
+ * be out-of-line, so there is a runtime penalty!).
+ *
+ * strstr, strchr, strrchr:
+ * Return const char * if first argument is const (gcc only).
+ *
+ * isalnum, isalpha, isascii, isblank, iscntrl, isdigit, isgraph,
+ * islower, isprint, ispunct, isspace, isupper, isxdigit:
+ * Static and runtime check that input is EOF or an *unsigned*
+ * char, as per C standard (really!).
+ *
+ * Example:
+ * #include <stdio.h>
+ * #include <ccan/str/str.h>
+ *
+ * int main(int argc, char *argv[])
+ * {
+ * if (argv[1] && streq(argv[1], "--verbose"))
+ * printf("verbose set\n");
+ * if (argv[1] && strstarts(argv[1], "--"))
+ * printf("Some option set\n");
+ * if (argv[1] && strends(argv[1], "cow-powers"))
+ * printf("Magic option set\n");
+ * return 0;
+ * }
+ *
+ * License: CC0 (Public domain)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/build_assert\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/ccan/str/str.c b/ccan/str/str.c
new file mode 100644
index 0000000..a9245c1
--- /dev/null
+++ b/ccan/str/str.c
@@ -0,0 +1,13 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#include <ccan/str/str.h>
+
+size_t strcount(const char *haystack, const char *needle)
+{
+ size_t i = 0, nlen = strlen(needle);
+
+ while ((haystack = strstr(haystack, needle)) != NULL) {
+ i++;
+ haystack += nlen;
+ }
+ return i;
+}
diff --git a/ccan/str/str.h b/ccan/str/str.h
new file mode 100644
index 0000000..89668c6
--- /dev/null
+++ b/ccan/str/str.h
@@ -0,0 +1,120 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_STR_H
+#define CCAN_STR_H
+#include "config.h"
+#include <string.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <ctype.h>
+
+/**
+ * streq - Are two strings equal?
+ * @a: first string
+ * @b: first string
+ *
+ * This macro is arguably more readable than "!strcmp(a, b)".
+ *
+ * Example:
+ * if (streq(somestring, ""))
+ * printf("String is empty!\n");
+ */
+#define streq(a,b) (strcmp((a),(b)) == 0)
+
+/**
+ * strstarts - Does this string start with this prefix?
+ * @str: string to test
+ * @prefix: prefix to look for at start of str
+ *
+ * Example:
+ * if (strstarts(somestring, "foo"))
+ * printf("String %s begins with 'foo'!\n", somestring);
+ */
+#define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0)
+
+/**
+ * strends - Does this string end with this postfix?
+ * @str: string to test
+ * @postfix: postfix to look for at end of str
+ *
+ * Example:
+ * if (strends(somestring, "foo"))
+ * printf("String %s end with 'foo'!\n", somestring);
+ */
+static inline bool strends(const char *str, const char *postfix)
+{
+ if (strlen(str) < strlen(postfix))
+ return false;
+
+ return streq(str + strlen(str) - strlen(postfix), postfix);
+}
+
+/**
+ * stringify - Turn expression into a string literal
+ * @expr: any C expression
+ *
+ * Example:
+ * #define PRINT_COND_IF_FALSE(cond) \
+ * ((cond) || printf("%s is false!", stringify(cond)))
+ */
+#define stringify(expr) stringify_1(expr)
+/* Double-indirection required to stringify expansions */
+#define stringify_1(expr) #expr
+
+/**
+ * strcount - Count number of (non-overlapping) occurrences of a substring.
+ * @haystack: a C string
+ * @needle: a substring
+ *
+ * Example:
+ * assert(strcount("aaa aaa", "a") == 6);
+ * assert(strcount("aaa aaa", "ab") == 0);
+ * assert(strcount("aaa aaa", "aa") == 2);
+ */
+size_t strcount(const char *haystack, const char *needle);
+
+/**
+ * STR_MAX_CHARS - Maximum possible size of numeric string for this type.
+ * @type_or_expr: a pointer or integer type or expression.
+ *
+ * This provides enough space for a nul-terminated string which represents the
+ * largest possible value for the type or expression.
+ *
+ * Note: The implementation adds extra space so hex values or negative
+ * values will fit (eg. sprintf(... "%p"). )
+ *
+ * Example:
+ * char str[STR_MAX_CHARS(int)];
+ *
+ * sprintf(str, "%i", 7);
+ */
+#define STR_MAX_CHARS(type_or_expr) \
+ ((sizeof(type_or_expr) * CHAR_BIT + 8) / 9 * 3 + 2 \
+ + STR_MAX_CHARS_TCHECK_(type_or_expr))
+
+#if HAVE_TYPEOF
+/* Only a simple type can have 0 assigned, so test that. */
+#define STR_MAX_CHARS_TCHECK_(type_or_expr) \
+ ({ typeof(type_or_expr) x = 0; (void)x; 0; })
+#else
+#define STR_MAX_CHARS_TCHECK_(type_or_expr) 0
+#endif
+
+/* These checks force things out of line, hence they are under DEBUG. */
+#ifdef CCAN_STR_DEBUG
+#if HAVE_TYPEOF
+/* With GNU magic, we can make const-respecting standard string functions. */
+#undef strstr
+#undef strchr
+#undef strrchr
+
+/* + 0 is needed to decay array into pointer. */
+#define strstr(haystack, needle) \
+ ((typeof((haystack) + 0))str_strstr((haystack), (needle)))
+#define strchr(haystack, c) \
+ ((typeof((haystack) + 0))str_strchr((haystack), (c)))
+#define strrchr(haystack, c) \
+ ((typeof((haystack) + 0))str_strrchr((haystack), (c)))
+#endif
+#endif /* CCAN_STR_DEBUG */
+
+#endif /* CCAN_STR_H */
diff --git a/ccan/str/test/compile_fail-STR_MAX_CHARS.c b/ccan/str/test/compile_fail-STR_MAX_CHARS.c
new file mode 100644
index 0000000..74448c1
--- /dev/null
+++ b/ccan/str/test/compile_fail-STR_MAX_CHARS.c
@@ -0,0 +1,23 @@
+#include <ccan/str/str.h>
+
+struct s {
+ int val;
+};
+
+int main(int argc, char *argv[])
+{
+ struct s
+#ifdef FAIL
+#if !HAVE_TYPEOF
+ #error We need typeof to check STR_MAX_CHARS.
+#endif
+#else
+ /* A pointer is OK. */
+ *
+#endif
+ val;
+ char str[STR_MAX_CHARS(val)];
+
+ str[0] = '\0';
+ return str[0] ? 0 : 1;
+}
diff --git a/ccan/str/test/compile_fail-isalnum.c b/ccan/str/test/compile_fail-isalnum.c
new file mode 100644
index 0000000..930deff
--- /dev/null
+++ b/ccan/str/test/compile_fail-isalnum.c
@@ -0,0 +1,22 @@
+#define CCAN_STR_DEBUG 1
+#include <ccan/str/str.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF
+#error We need typeof to check isalnum.
+#endif
+ char
+#else
+ unsigned char
+#endif
+ c = argv[0][0];
+
+#ifdef FAIL
+ /* Fake fail on unsigned char platforms. */
+ BUILD_ASSERT((char)255 < 0);
+#endif
+
+ return isalnum(c);
+}
diff --git a/ccan/str/test/compile_fail-isalpha.c b/ccan/str/test/compile_fail-isalpha.c
new file mode 100644
index 0000000..2005109
--- /dev/null
+++ b/ccan/str/test/compile_fail-isalpha.c
@@ -0,0 +1,22 @@
+#define CCAN_STR_DEBUG 1
+#include <ccan/str/str.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF
+#error We need typeof to check isalpha.
+#endif
+ char
+#else
+ unsigned char
+#endif
+ c = argv[0][0];
+
+#ifdef FAIL
+ /* Fake fail on unsigned char platforms. */
+ BUILD_ASSERT((char)255 < 0);
+#endif
+
+ return isalpha(c);
+}
diff --git a/ccan/str/test/compile_fail-isascii.c b/ccan/str/test/compile_fail-isascii.c
new file mode 100644
index 0000000..ee55e49
--- /dev/null
+++ b/ccan/str/test/compile_fail-isascii.c
@@ -0,0 +1,22 @@
+#define CCAN_STR_DEBUG 1
+#include <ccan/str/str.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF
+#error We need typeof to check isascii.
+#endif
+ char
+#else
+ unsigned char
+#endif
+ c = argv[0][0];
+
+#ifdef FAIL
+ /* Fake fail on unsigned char platforms. */
+ BUILD_ASSERT((char)255 < 0);
+#endif
+
+ return isascii(c);
+}
diff --git a/ccan/str/test/compile_fail-isblank.c b/ccan/str/test/compile_fail-isblank.c
new file mode 100644
index 0000000..f4cb961
--- /dev/null
+++ b/ccan/str/test/compile_fail-isblank.c
@@ -0,0 +1,26 @@
+#define CCAN_STR_DEBUG 1
+#include <ccan/str/str.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF || !HAVE_ISBLANK
+#error We need typeof to check isblank.
+#endif
+ char
+#else
+ unsigned char
+#endif
+ c = argv[0][0];
+
+#ifdef FAIL
+ /* Fake fail on unsigned char platforms. */
+ BUILD_ASSERT((char)255 < 0);
+#endif
+
+#if HAVE_ISBLANK
+ return isblank(c);
+#else
+ return c;
+#endif
+}
diff --git a/ccan/str/test/compile_fail-iscntrl.c b/ccan/str/test/compile_fail-iscntrl.c
new file mode 100644
index 0000000..bc74146
--- /dev/null
+++ b/ccan/str/test/compile_fail-iscntrl.c
@@ -0,0 +1,22 @@
+#define CCAN_STR_DEBUG 1
+#include <ccan/str/str.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF
+#error We need typeof to check iscntrl.
+#endif
+ char
+#else
+ unsigned char
+#endif
+ c = argv[0][0];
+
+#ifdef FAIL
+ /* Fake fail on unsigned char platforms. */
+ BUILD_ASSERT((char)255 < 0);
+#endif
+
+ return iscntrl(c);
+}
diff --git a/ccan/str/test/compile_fail-isdigit.c b/ccan/str/test/compile_fail-isdigit.c
new file mode 100644
index 0000000..71d1c71
--- /dev/null
+++ b/ccan/str/test/compile_fail-isdigit.c
@@ -0,0 +1,22 @@
+#define CCAN_STR_DEBUG 1
+#include <ccan/str/str.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF
+#error We need typeof to check isdigit.
+#endif
+ char
+#else
+ unsigned char
+#endif
+ c = argv[0][0];
+
+#ifdef FAIL
+ /* Fake fail on unsigned char platforms. */
+ BUILD_ASSERT((char)255 < 0);
+#endif
+
+ return isdigit(c);
+}
diff --git a/ccan/str/test/compile_fail-islower.c b/ccan/str/test/compile_fail-islower.c
new file mode 100644
index 0000000..ca3f990
--- /dev/null
+++ b/ccan/str/test/compile_fail-islower.c
@@ -0,0 +1,22 @@
+#define CCAN_STR_DEBUG 1
+#include <ccan/str/str.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF
+#error We need typeof to check islower.
+#endif
+ char
+#else
+ unsigned char
+#endif
+ c = argv[0][0];
+
+#ifdef FAIL
+ /* Fake fail on unsigned char platforms. */
+ BUILD_ASSERT((char)255 < 0);
+#endif
+
+ return islower(c);
+}
diff --git a/ccan/str/test/compile_fail-isprint.c b/ccan/str/test/compile_fail-isprint.c
new file mode 100644
index 0000000..6432e41
--- /dev/null
+++ b/ccan/str/test/compile_fail-isprint.c
@@ -0,0 +1,22 @@
+#define CCAN_STR_DEBUG 1
+#include <ccan/str/str.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF
+#error We need typeof to check isprint.
+#endif
+ char
+#else
+ unsigned char
+#endif
+ c = argv[0][0];
+
+#ifdef FAIL
+ /* Fake fail on unsigned char platforms. */
+ BUILD_ASSERT((char)255 < 0);
+#endif
+
+ return isprint(c);
+}
diff --git a/ccan/str/test/compile_fail-ispunct.c b/ccan/str/test/compile_fail-ispunct.c
new file mode 100644
index 0000000..5d941fc
--- /dev/null
+++ b/ccan/str/test/compile_fail-ispunct.c
@@ -0,0 +1,22 @@
+#define CCAN_STR_DEBUG 1
+#include <ccan/str/str.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF
+#error We need typeof to check ispunct.
+#endif
+ char
+#else
+ unsigned char
+#endif
+ c = argv[0][0];
+
+#ifdef FAIL
+ /* Fake fail on unsigned char platforms. */
+ BUILD_ASSERT((char)255 < 0);
+#endif
+
+ return ispunct(c);
+}
diff --git a/ccan/str/test/compile_fail-isspace.c b/ccan/str/test/compile_fail-isspace.c
new file mode 100644
index 0000000..bfee1f8
--- /dev/null
+++ b/ccan/str/test/compile_fail-isspace.c
@@ -0,0 +1,22 @@
+#define CCAN_STR_DEBUG 1
+#include <ccan/str/str.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF
+#error We need typeof to check isspace.
+#endif
+ char
+#else
+ unsigned char
+#endif
+ c = argv[0][0];
+
+#ifdef FAIL
+ /* Fake fail on unsigned char platforms. */
+ BUILD_ASSERT((char)255 < 0);
+#endif
+
+ return isspace(c);
+}
diff --git a/ccan/str/test/compile_fail-isupper.c b/ccan/str/test/compile_fail-isupper.c
new file mode 100644
index 0000000..4cf9fd3
--- /dev/null
+++ b/ccan/str/test/compile_fail-isupper.c
@@ -0,0 +1,22 @@
+#define CCAN_STR_DEBUG 1
+#include <ccan/str/str.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF
+#error We need typeof to check isupper.
+#endif
+ char
+#else
+ unsigned char
+#endif
+ c = argv[0][0];
+
+#ifdef FAIL
+ /* Fake fail on unsigned char platforms. */
+ BUILD_ASSERT((char)255 < 0);
+#endif
+
+ return isupper(c);
+}
diff --git a/ccan/str/test/compile_fail-isxdigit.c b/ccan/str/test/compile_fail-isxdigit.c
new file mode 100644
index 0000000..65e6006
--- /dev/null
+++ b/ccan/str/test/compile_fail-isxdigit.c
@@ -0,0 +1,22 @@
+#define CCAN_STR_DEBUG 1
+#include <ccan/str/str.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF
+#error We need typeof to check isxdigit.
+#endif
+ char
+#else
+ unsigned char
+#endif
+ c = argv[0][0];
+
+#ifdef FAIL
+ /* Fake fail on unsigned char platforms. */
+ BUILD_ASSERT((char)255 < 0);
+#endif
+
+ return isxdigit(c);
+}
diff --git a/ccan/str/test/compile_fail-strchr.c b/ccan/str/test/compile_fail-strchr.c
new file mode 100644
index 0000000..74a7314
--- /dev/null
+++ b/ccan/str/test/compile_fail-strchr.c
@@ -0,0 +1,18 @@
+#define CCAN_STR_DEBUG 1
+#include <ccan/str/str.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if !HAVE_TYPEOF
+ #error We need typeof to check strstr.
+#endif
+#else
+ const
+#endif
+ char *ret;
+ const char *str = "hello";
+
+ ret = strchr(str, 'l');
+ return ret ? 0 : 1;
+}
diff --git a/ccan/str/test/compile_fail-strrchr.c b/ccan/str/test/compile_fail-strrchr.c
new file mode 100644
index 0000000..ba7d17e
--- /dev/null
+++ b/ccan/str/test/compile_fail-strrchr.c
@@ -0,0 +1,18 @@
+#define CCAN_STR_DEBUG 1
+#include <ccan/str/str.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if !HAVE_TYPEOF
+ #error We need typeof to check strstr.
+#endif
+#else
+ const
+#endif
+ char *ret;
+ const char *str = "hello";
+
+ ret = strrchr(str, 'l');
+ return ret ? 0 : 1;
+}
diff --git a/ccan/str/test/compile_fail-strstr.c b/ccan/str/test/compile_fail-strstr.c
new file mode 100644
index 0000000..deefef6
--- /dev/null
+++ b/ccan/str/test/compile_fail-strstr.c
@@ -0,0 +1,18 @@
+#define CCAN_STR_DEBUG 1
+#include <ccan/str/str.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if !HAVE_TYPEOF
+ #error We need typeof to check strstr.
+#endif
+#else
+ const
+#endif
+ char *ret;
+ const char *str = "hello";
+
+ ret = strstr(str, "hell");
+ return ret ? 0 : 1;
+}
diff --git a/ccan/str/test/debug.c b/ccan/str/test/debug.c
new file mode 100644
index 0000000..4bd384f
--- /dev/null
+++ b/ccan/str/test/debug.c
@@ -0,0 +1,5 @@
+/* We can't use the normal "#include the .c file" trick, since this is
+ contaminated by str.h's macro overrides. So we put it in all tests
+ like this. */
+#define CCAN_STR_DEBUG 1
+#include <ccan/str/debug.c>
diff --git a/ccan/str/test/run-STR_MAX_CHARS.c b/ccan/str/test/run-STR_MAX_CHARS.c
new file mode 100644
index 0000000..1343e04
--- /dev/null
+++ b/ccan/str/test/run-STR_MAX_CHARS.c
@@ -0,0 +1,66 @@
+#include <ccan/str/str.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ccan/tap/tap.h>
+#include <stdint.h>
+
+int main(int argc, char *argv[])
+{
+ char *str = (char*)malloc(sizeof(char)*1000);
+ struct {
+ uint8_t u1byte;
+ int8_t s1byte;
+ uint16_t u2byte;
+ int16_t s2byte;
+ uint32_t u4byte;
+ int32_t s4byte;
+ uint64_t u8byte;
+ int64_t s8byte;
+ void *ptr;
+ } types;
+
+ (void)argc;
+ (void)argv;
+
+ assert(str);
+
+ plan_tests(13);
+
+ memset(&types, 0xFF, sizeof(types));
+
+ /* Hex versions */
+ sprintf(str, "0x%llx", (unsigned long long)types.u1byte);
+ ok1(strlen(str) < STR_MAX_CHARS(types.u1byte));
+ sprintf(str, "0x%llx", (unsigned long long)types.u2byte);
+ ok1(strlen(str) < STR_MAX_CHARS(types.u2byte));
+ sprintf(str, "0x%llx", (unsigned long long)types.u4byte);
+ ok1(strlen(str) < STR_MAX_CHARS(types.u4byte));
+ sprintf(str, "0x%llx", (unsigned long long)types.u8byte);
+ ok1(strlen(str) < STR_MAX_CHARS(types.u8byte));
+
+ /* Decimal versions */
+ sprintf(str, "%u", types.u1byte);
+ ok1(strlen(str) < STR_MAX_CHARS(types.u1byte));
+ sprintf(str, "%d", types.s1byte);
+ ok1(strlen(str) < STR_MAX_CHARS(types.s1byte));
+ sprintf(str, "%u", types.u2byte);
+ ok1(strlen(str) < STR_MAX_CHARS(types.u2byte));
+ sprintf(str, "%d", types.s2byte);
+ ok1(strlen(str) < STR_MAX_CHARS(types.s2byte));
+ sprintf(str, "%u", types.u4byte);
+ ok1(strlen(str) < STR_MAX_CHARS(types.u4byte));
+ sprintf(str, "%d", types.s4byte);
+ ok1(strlen(str) < STR_MAX_CHARS(types.s4byte));
+ sprintf(str, "%llu", (unsigned long long)types.u8byte);
+ ok1(strlen(str) < STR_MAX_CHARS(types.u8byte));
+ sprintf(str, "%lld", (long long)types.s8byte);
+ ok1(strlen(str) < STR_MAX_CHARS(types.s8byte));
+
+ /* Pointer version. */
+ sprintf(str, "%p", types.ptr);
+ ok1(strlen(str) < STR_MAX_CHARS(types.ptr));
+
+ free(str);
+
+ return exit_status();
+}
diff --git a/ccan/str/test/run.c b/ccan/str/test/run.c
new file mode 100644
index 0000000..216e141
--- /dev/null
+++ b/ccan/str/test/run.c
@@ -0,0 +1,109 @@
+#include <ccan/str/str.h>
+#include <ccan/str/str.c>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ccan/tap/tap.h>
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
+
+static const char *substrings[] = { "far", "bar", "baz", "b", "ba", "z", "ar",
+ NULL };
+
+#define NUM_SUBSTRINGS (ARRAY_SIZE(substrings) - 1)
+
+static char *strdup_rev(const char *s)
+{
+ char *ret = strdup(s);
+ unsigned int i;
+
+ for (i = 0; i < strlen(s); i++)
+ ret[i] = s[strlen(s) - i - 1];
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned int i, j, n;
+ char *strings[NUM_SUBSTRINGS * NUM_SUBSTRINGS];
+
+ (void)argc;
+ (void)argv;
+
+ n = 0;
+ for (i = 0; i < NUM_SUBSTRINGS; i++) {
+ for (j = 0; j < NUM_SUBSTRINGS; j++) {
+ strings[n] = malloc(strlen(substrings[i])
+ + strlen(substrings[j]) + 1);
+ sprintf(strings[n++], "%s%s",
+ substrings[i], substrings[j]);
+ }
+ }
+
+ plan_tests(n * n * 5 + 16);
+ for (i = 0; i < n; i++) {
+ for (j = 0; j < n; j++) {
+ unsigned int k, identical = 0;
+ char *reva, *revb;
+
+ /* Find first difference. */
+ for (k = 0; strings[i][k]==strings[j][k]; k++) {
+ if (k == strlen(strings[i])) {
+ identical = 1;
+ break;
+ }
+ }
+
+ if (identical)
+ ok1(streq(strings[i], strings[j]));
+ else
+ ok1(!streq(strings[i], strings[j]));
+
+ /* Postfix test should be equivalent to prefix
+ * test on reversed string. */
+ reva = strdup_rev(strings[i]);
+ revb = strdup_rev(strings[j]);
+
+ if (!strings[i][k]) {
+ ok1(strstarts(strings[j], strings[i]));
+ ok1(strends(revb, reva));
+ } else {
+ ok1(!strstarts(strings[j], strings[i]));
+ ok1(!strends(revb, reva));
+ }
+ if (!strings[j][k]) {
+ ok1(strstarts(strings[i], strings[j]));
+ ok1(strends(reva, revb));
+ } else {
+ ok1(!strstarts(strings[i], strings[j]));
+ ok1(!strends(reva, revb));
+ }
+ free(reva);
+ free(revb);
+ }
+ }
+
+ for (i = 0; i < n; i++)
+ free(strings[i]);
+
+ ok1(streq(stringify(NUM_SUBSTRINGS),
+ "((sizeof(substrings) / sizeof(substrings[0])) - 1)"));
+ ok1(streq(stringify(ARRAY_SIZE(substrings)),
+ "(sizeof(substrings) / sizeof(substrings[0]))"));
+ ok1(streq(stringify(i == 0), "i == 0"));
+
+ ok1(strcount("aaaaaa", "b") == 0);
+ ok1(strcount("aaaaaa", "a") == 6);
+ ok1(strcount("aaaaaa", "aa") == 3);
+ ok1(strcount("aaaaaa", "aaa") == 2);
+ ok1(strcount("aaaaaa", "aaaa") == 1);
+ ok1(strcount("aaaaaa", "aaaaa") == 1);
+ ok1(strcount("aaaaaa", "aaaaaa") == 1);
+ ok1(strcount("aaa aaa", "b") == 0);
+ ok1(strcount("aaa aaa", "a") == 6);
+ ok1(strcount("aaa aaa", "aa") == 2);
+ ok1(strcount("aaa aaa", "aaa") == 2);
+ ok1(strcount("aaa aaa", "aaaa") == 0);
+ ok1(strcount("aaa aaa", "aaaaa") == 0);
+
+ return exit_status();
+}
diff --git a/configure.ac b/configure.ac
index 7defdb2..8bd9c3b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,8 @@ AC_INIT([pdbg], [0.3])
AM_INIT_AUTOMAKE([subdir-objects])
AC_PROG_CC
-AC_PROG_RANLIB
+AC_PROG_LIBTOOL
+AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([Makefile])
diff --git a/fake.dts b/fake.dts
new file mode 100644
index 0000000..e6dd958
--- /dev/null
+++ b/fake.dts
@@ -0,0 +1,16 @@
+/dts-v1/;
+
+/ {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+
+ fsi@0 {
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ compatible = "ibm,fake-fsi";
+ reg = <0x0 0x0 0x0>;
+
+ index = <0x0>;
+ status = "hidden";
+ };
+};
diff --git a/libfdt/Makefile.libfdt b/libfdt/Makefile.libfdt
new file mode 100644
index 0000000..098b3f3
--- /dev/null
+++ b/libfdt/Makefile.libfdt
@@ -0,0 +1,11 @@
+# Makefile.libfdt
+#
+# This is not a complete Makefile of itself. Instead, it is designed to
+# be easily embeddable into other systems of Makefiles.
+#
+LIBFDT_soname = libfdt.$(SHAREDLIB_EXT).1
+LIBFDT_INCLUDES = fdt.h libfdt.h libfdt_env.h
+LIBFDT_VERSION = version.lds
+LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c fdt_empty_tree.c \
+ fdt_addresses.c fdt_overlay.c
+LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o)
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..22286a1
--- /dev/null
+++ b/libfdt/fdt.c
@@ -0,0 +1,251 @@
+/*
+ * 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) == 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, unsigned int len)
+{
+ unsigned absoffset = offset + fdt_off_dt_struct(fdt);
+
+ if ((absoffset < offset)
+ || ((absoffset + len) < absoffset)
+ || (absoffset + len) > fdt_totalsize(fdt))
+ return NULL;
+
+ if (fdt_version(fdt) >= 0x11)
+ if (((offset + len) < offset)
+ || ((offset + len) > fdt_size_dt_struct(fdt)))
+ return NULL;
+
+ return _fdt_offset_ptr(fdt, offset);
+}
+
+uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
+{
+ const fdt32_t *tagp, *lenp;
+ uint32_t tag;
+ int offset = startoffset;
+ const char *p;
+
+ *nextoffset = -FDT_ERR_TRUNCATED;
+ tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
+ if (!tagp)
+ return FDT_END; /* premature end */
+ tag = fdt32_to_cpu(*tagp);
+ offset += FDT_TAGSIZE;
+
+ *nextoffset = -FDT_ERR_BADSTRUCTURE;
+ 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; /* premature end */
+ break;
+
+ case FDT_PROP:
+ lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
+ if (!lenp)
+ return FDT_END; /* premature end */
+ /* skip-name offset, length and value */
+ offset += sizeof(struct fdt_property) - FDT_TAGSIZE
+ + fdt32_to_cpu(*lenp);
+ break;
+
+ case FDT_END:
+ case FDT_END_NODE:
+ case FDT_NOP:
+ break;
+
+ default:
+ return FDT_END;
+ }
+
+ if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
+ return FDT_END; /* premature end */
+
+ *nextoffset = FDT_TAGALIGN(offset);
+ return tag;
+}
+
+int _fdt_check_node_offset(const void *fdt, int offset)
+{
+ if ((offset < 0) || (offset % FDT_TAGSIZE)
+ || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
+ return -FDT_ERR_BADOFFSET;
+
+ return offset;
+}
+
+int _fdt_check_prop_offset(const void *fdt, int offset)
+{
+ if ((offset < 0) || (offset % FDT_TAGSIZE)
+ || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
+ return -FDT_ERR_BADOFFSET;
+
+ return offset;
+}
+
+int fdt_next_node(const void *fdt, int offset, int *depth)
+{
+ int nextoffset = 0;
+ uint32_t tag;
+
+ if (offset >= 0)
+ if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0)
+ return nextoffset;
+
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_PROP:
+ case FDT_NOP:
+ break;
+
+ case FDT_BEGIN_NODE:
+ if (depth)
+ (*depth)++;
+ break;
+
+ case FDT_END_NODE:
+ if (depth && ((--(*depth)) < 0))
+ return nextoffset;
+ break;
+
+ case FDT_END:
+ if ((nextoffset >= 0)
+ || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
+ return -FDT_ERR_NOTFOUND;
+ else
+ return nextoffset;
+ }
+ } while (tag != FDT_BEGIN_NODE);
+
+ return offset;
+}
+
+int fdt_first_subnode(const void *fdt, int offset)
+{
+ int depth = 0;
+
+ offset = fdt_next_node(fdt, offset, &depth);
+ if (offset < 0 || depth != 1)
+ return -FDT_ERR_NOTFOUND;
+
+ return offset;
+}
+
+int fdt_next_subnode(const void *fdt, int offset)
+{
+ int depth = 1;
+
+ /*
+ * With respect to the parent, the depth of the next subnode will be
+ * the same as the last.
+ */
+ do {
+ offset = fdt_next_node(fdt, offset, &depth);
+ if (offset < 0 || depth < 1)
+ return -FDT_ERR_NOTFOUND;
+ } while (depth > 1);
+
+ return offset;
+}
+
+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 (memcmp(p, s, len) == 0)
+ return p;
+ return NULL;
+}
+
+int fdt_move(const void *fdt, void *buf, int bufsize)
+{
+ FDT_CHECK_HEADER(fdt);
+
+ 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..526aedb
--- /dev/null
+++ b/libfdt/fdt.h
@@ -0,0 +1,111 @@
+#ifndef _FDT_H
+#define _FDT_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ * Copyright 2012 Kim Phillips, Freescale Semiconductor.
+ *
+ * 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.
+ */
+
+#ifndef __ASSEMBLY__
+
+struct fdt_header {
+ fdt32_t magic; /* magic word FDT_MAGIC */
+ fdt32_t totalsize; /* total size of DT block */
+ fdt32_t off_dt_struct; /* offset to structure */
+ fdt32_t off_dt_strings; /* offset to strings */
+ fdt32_t off_mem_rsvmap; /* offset to memory reserve map */
+ fdt32_t version; /* format version */
+ fdt32_t last_comp_version; /* last compatible version */
+
+ /* version 2 fields below */
+ fdt32_t boot_cpuid_phys; /* Which physical CPU id we're
+ booting on */
+ /* version 3 fields below */
+ fdt32_t size_dt_strings; /* size of the strings block */
+
+ /* version 17 fields below */
+ fdt32_t size_dt_struct; /* size of the structure block */
+};
+
+struct fdt_reserve_entry {
+ fdt64_t address;
+ fdt64_t size;
+};
+
+struct fdt_node_header {
+ fdt32_t tag;
+ char name[0];
+};
+
+struct fdt_property {
+ fdt32_t tag;
+ fdt32_t len;
+ fdt32_t nameoff;
+ char data[0];
+};
+
+#endif /* !__ASSEMBLY */
+
+#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
+#define FDT_TAGSIZE sizeof(fdt32_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(fdt32_t))
+#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t))
+#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))
+#define FDT_V16_SIZE FDT_V3_SIZE
+#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t))
+
+#endif /* _FDT_H */
diff --git a/libfdt/fdt_addresses.c b/libfdt/fdt_addresses.c
new file mode 100644
index 0000000..eff4dbc
--- /dev/null
+++ b/libfdt/fdt_addresses.c
@@ -0,0 +1,96 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2014 David Gibson <david@gibson.dropbear.id.au>
+ *
+ * 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_address_cells(const void *fdt, int nodeoffset)
+{
+ const fdt32_t *ac;
+ int val;
+ int len;
+
+ ac = fdt_getprop(fdt, nodeoffset, "#address-cells", &len);
+ if (!ac)
+ return 2;
+
+ if (len != sizeof(*ac))
+ return -FDT_ERR_BADNCELLS;
+
+ val = fdt32_to_cpu(*ac);
+ if ((val <= 0) || (val > FDT_MAX_NCELLS))
+ return -FDT_ERR_BADNCELLS;
+
+ return val;
+}
+
+int fdt_size_cells(const void *fdt, int nodeoffset)
+{
+ const fdt32_t *sc;
+ int val;
+ int len;
+
+ sc = fdt_getprop(fdt, nodeoffset, "#size-cells", &len);
+ if (!sc)
+ return 2;
+
+ if (len != sizeof(*sc))
+ return -FDT_ERR_BADNCELLS;
+
+ val = fdt32_to_cpu(*sc);
+ if ((val < 0) || (val > FDT_MAX_NCELLS))
+ return -FDT_ERR_BADNCELLS;
+
+ return val;
+}
diff --git a/libfdt/fdt_empty_tree.c b/libfdt/fdt_empty_tree.c
new file mode 100644
index 0000000..f72d13b
--- /dev/null
+++ b/libfdt/fdt_empty_tree.c
@@ -0,0 +1,84 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2012 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_create_empty_tree(void *buf, int bufsize)
+{
+ int err;
+
+ err = fdt_create(buf, bufsize);
+ if (err)
+ return err;
+
+ err = fdt_finish_reservemap(buf);
+ if (err)
+ return err;
+
+ err = fdt_begin_node(buf, "");
+ if (err)
+ return err;
+
+ err = fdt_end_node(buf);
+ if (err)
+ return err;
+
+ err = fdt_finish(buf);
+ if (err)
+ return err;
+
+ return fdt_open_into(buf, buf, bufsize);
+}
+
diff --git a/libfdt/fdt_overlay.c b/libfdt/fdt_overlay.c
new file mode 100644
index 0000000..56cb70e
--- /dev/null
+++ b/libfdt/fdt_overlay.c
@@ -0,0 +1,676 @@
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+/**
+ * overlay_get_target_phandle - retrieves the target phandle of a fragment
+ * @fdto: pointer to the device tree overlay blob
+ * @fragment: node offset of the fragment in the overlay
+ *
+ * overlay_get_target_phandle() retrieves the target phandle of an
+ * overlay fragment when that fragment uses a phandle (target
+ * property) instead of a path (target-path property).
+ *
+ * returns:
+ * the phandle pointed by the target property
+ * 0, if the phandle was not found
+ * -1, if the phandle was malformed
+ */
+static uint32_t overlay_get_target_phandle(const void *fdto, int fragment)
+{
+ const uint32_t *val;
+ int len;
+
+ val = fdt_getprop(fdto, fragment, "target", &len);
+ if (!val)
+ return 0;
+
+ if ((len != sizeof(*val)) || (*val == (uint32_t)-1))
+ return (uint32_t)-1;
+
+ return fdt32_to_cpu(*val);
+}
+
+/**
+ * overlay_get_target - retrieves the offset of a fragment's target
+ * @fdt: Base device tree blob
+ * @fdto: Device tree overlay blob
+ * @fragment: node offset of the fragment in the overlay
+ *
+ * overlay_get_target() retrieves the target offset in the base
+ * device tree of a fragment, no matter how the actual targetting is
+ * done (through a phandle or a path)
+ *
+ * returns:
+ * the targetted node offset in the base device tree
+ * Negative error code on error
+ */
+static int overlay_get_target(const void *fdt, const void *fdto,
+ int fragment)
+{
+ uint32_t phandle;
+ const char *path;
+ int path_len;
+
+ /* Try first to do a phandle based lookup */
+ phandle = overlay_get_target_phandle(fdto, fragment);
+ if (phandle == (uint32_t)-1)
+ return -FDT_ERR_BADPHANDLE;
+
+ if (phandle)
+ return fdt_node_offset_by_phandle(fdt, phandle);
+
+ /* And then a path based lookup */
+ path = fdt_getprop(fdto, fragment, "target-path", &path_len);
+ if (!path) {
+ /*
+ * If we haven't found either a target or a
+ * target-path property in a node that contains a
+ * __overlay__ subnode (we wouldn't be called
+ * otherwise), consider it a improperly written
+ * overlay
+ */
+ if (path_len == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_BADOVERLAY;
+
+ return path_len;
+ }
+
+ return fdt_path_offset(fdt, path);
+}
+
+/**
+ * overlay_phandle_add_offset - Increases a phandle by an offset
+ * @fdt: Base device tree blob
+ * @node: Device tree overlay blob
+ * @name: Name of the property to modify (phandle or linux,phandle)
+ * @delta: offset to apply
+ *
+ * overlay_phandle_add_offset() increments a node phandle by a given
+ * offset.
+ *
+ * returns:
+ * 0 on success.
+ * Negative error code on error
+ */
+static int overlay_phandle_add_offset(void *fdt, int node,
+ const char *name, uint32_t delta)
+{
+ const uint32_t *val;
+ uint32_t adj_val;
+ int len;
+
+ val = fdt_getprop(fdt, node, name, &len);
+ if (!val)
+ return len;
+
+ if (len != sizeof(*val))
+ return -FDT_ERR_BADPHANDLE;
+
+ adj_val = fdt32_to_cpu(*val);
+ if ((adj_val + delta) < adj_val)
+ return -FDT_ERR_NOPHANDLES;
+
+ adj_val += delta;
+ if (adj_val == (uint32_t)-1)
+ return -FDT_ERR_NOPHANDLES;
+
+ return fdt_setprop_inplace_u32(fdt, node, name, adj_val);
+}
+
+/**
+ * overlay_adjust_node_phandles - Offsets the phandles of a node
+ * @fdto: Device tree overlay blob
+ * @node: Offset of the node we want to adjust
+ * @delta: Offset to shift the phandles of
+ *
+ * overlay_adjust_node_phandles() adds a constant to all the phandles
+ * of a given node. This is mainly use as part of the overlay
+ * application process, when we want to update all the overlay
+ * phandles to not conflict with the overlays of the base device tree.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_adjust_node_phandles(void *fdto, int node,
+ uint32_t delta)
+{
+ int child;
+ int ret;
+
+ ret = overlay_phandle_add_offset(fdto, node, "phandle", delta);
+ if (ret && ret != -FDT_ERR_NOTFOUND)
+ return ret;
+
+ ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta);
+ if (ret && ret != -FDT_ERR_NOTFOUND)
+ return ret;
+
+ fdt_for_each_subnode(child, fdto, node) {
+ ret = overlay_adjust_node_phandles(fdto, child, delta);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay
+ * @fdto: Device tree overlay blob
+ * @delta: Offset to shift the phandles of
+ *
+ * overlay_adjust_local_phandles() adds a constant to all the
+ * phandles of an overlay. This is mainly use as part of the overlay
+ * application process, when we want to update all the overlay
+ * phandles to not conflict with the overlays of the base device tree.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_adjust_local_phandles(void *fdto, uint32_t delta)
+{
+ /*
+ * Start adjusting the phandles from the overlay root
+ */
+ return overlay_adjust_node_phandles(fdto, 0, delta);
+}
+
+/**
+ * overlay_update_local_node_references - Adjust the overlay references
+ * @fdto: Device tree overlay blob
+ * @tree_node: Node offset of the node to operate on
+ * @fixup_node: Node offset of the matching local fixups node
+ * @delta: Offset to shift the phandles of
+ *
+ * overlay_update_local_nodes_references() update the phandles
+ * pointing to a node within the device tree overlay by adding a
+ * constant delta.
+ *
+ * This is mainly used as part of a device tree application process,
+ * where you want the device tree overlays phandles to not conflict
+ * with the ones from the base device tree before merging them.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_update_local_node_references(void *fdto,
+ int tree_node,
+ int fixup_node,
+ uint32_t delta)
+{
+ int fixup_prop;
+ int fixup_child;
+ int ret;
+
+ fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
+ const uint32_t *fixup_val;
+ const char *tree_val;
+ const char *name;
+ int fixup_len;
+ int tree_len;
+ int i;
+
+ fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
+ &name, &fixup_len);
+ if (!fixup_val)
+ return fixup_len;
+
+ if (fixup_len % sizeof(uint32_t))
+ return -FDT_ERR_BADOVERLAY;
+
+ tree_val = fdt_getprop(fdto, tree_node, name, &tree_len);
+ if (!tree_val) {
+ if (tree_len == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_BADOVERLAY;
+
+ return tree_len;
+ }
+
+ for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) {
+ uint32_t adj_val, poffset;
+
+ poffset = fdt32_to_cpu(fixup_val[i]);
+
+ /*
+ * phandles to fixup can be unaligned.
+ *
+ * Use a memcpy for the architectures that do
+ * not support unaligned accesses.
+ */
+ memcpy(&adj_val, tree_val + poffset, sizeof(adj_val));
+
+ adj_val = fdt32_to_cpu(adj_val);
+ adj_val += delta;
+ adj_val = cpu_to_fdt32(adj_val);
+
+ ret = fdt_setprop_inplace_namelen_partial(fdto,
+ tree_node,
+ name,
+ strlen(name),
+ poffset,
+ &adj_val,
+ sizeof(adj_val));
+ if (ret == -FDT_ERR_NOSPACE)
+ return -FDT_ERR_BADOVERLAY;
+
+ if (ret)
+ return ret;
+ }
+ }
+
+ fdt_for_each_subnode(fixup_child, fdto, fixup_node) {
+ const char *fixup_child_name = fdt_get_name(fdto, fixup_child,
+ NULL);
+ int tree_child;
+
+ tree_child = fdt_subnode_offset(fdto, tree_node,
+ fixup_child_name);
+ if (ret == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_BADOVERLAY;
+ if (tree_child < 0)
+ return tree_child;
+
+ ret = overlay_update_local_node_references(fdto,
+ tree_child,
+ fixup_child,
+ delta);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * overlay_update_local_references - Adjust the overlay references
+ * @fdto: Device tree overlay blob
+ * @delta: Offset to shift the phandles of
+ *
+ * overlay_update_local_references() update all the phandles pointing
+ * to a node within the device tree overlay by adding a constant
+ * delta to not conflict with the base overlay.
+ *
+ * This is mainly used as part of a device tree application process,
+ * where you want the device tree overlays phandles to not conflict
+ * with the ones from the base device tree before merging them.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_update_local_references(void *fdto, uint32_t delta)
+{
+ int fixups;
+
+ fixups = fdt_path_offset(fdto, "/__local_fixups__");
+ if (fixups < 0) {
+ /* There's no local phandles to adjust, bail out */
+ if (fixups == -FDT_ERR_NOTFOUND)
+ return 0;
+
+ return fixups;
+ }
+
+ /*
+ * Update our local references from the root of the tree
+ */
+ return overlay_update_local_node_references(fdto, 0, fixups,
+ delta);
+}
+
+/**
+ * overlay_fixup_one_phandle - Set an overlay phandle to the base one
+ * @fdt: Base Device Tree blob
+ * @fdto: Device tree overlay blob
+ * @symbols_off: Node offset of the symbols node in the base device tree
+ * @path: Path to a node holding a phandle in the overlay
+ * @path_len: number of path characters to consider
+ * @name: Name of the property holding the phandle reference in the overlay
+ * @name_len: number of name characters to consider
+ * @poffset: Offset within the overlay property where the phandle is stored
+ * @label: Label of the node referenced by the phandle
+ *
+ * overlay_fixup_one_phandle() resolves an overlay phandle pointing to
+ * a node in the base device tree.
+ *
+ * This is part of the device tree overlay application process, when
+ * you want all the phandles in the overlay to point to the actual
+ * base dt nodes.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_fixup_one_phandle(void *fdt, void *fdto,
+ int symbols_off,
+ const char *path, uint32_t path_len,
+ const char *name, uint32_t name_len,
+ int poffset, const char *label)
+{
+ const char *symbol_path;
+ uint32_t phandle;
+ int symbol_off, fixup_off;
+ int prop_len;
+
+ if (symbols_off < 0)
+ return symbols_off;
+
+ symbol_path = fdt_getprop(fdt, symbols_off, label,
+ &prop_len);
+ if (!symbol_path)
+ return prop_len;
+
+ symbol_off = fdt_path_offset(fdt, symbol_path);
+ if (symbol_off < 0)
+ return symbol_off;
+
+ phandle = fdt_get_phandle(fdt, symbol_off);
+ if (!phandle)
+ return -FDT_ERR_NOTFOUND;
+
+ fixup_off = fdt_path_offset_namelen(fdto, path, path_len);
+ if (fixup_off == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_BADOVERLAY;
+ if (fixup_off < 0)
+ return fixup_off;
+
+ phandle = cpu_to_fdt32(phandle);
+ return fdt_setprop_inplace_namelen_partial(fdto, fixup_off,
+ name, name_len, poffset,
+ &phandle, sizeof(phandle));
+};
+
+/**
+ * overlay_fixup_phandle - Set an overlay phandle to the base one
+ * @fdt: Base Device Tree blob
+ * @fdto: Device tree overlay blob
+ * @symbols_off: Node offset of the symbols node in the base device tree
+ * @property: Property offset in the overlay holding the list of fixups
+ *
+ * overlay_fixup_phandle() resolves all the overlay phandles pointed
+ * to in a __fixups__ property, and updates them to match the phandles
+ * in use in the base device tree.
+ *
+ * This is part of the device tree overlay application process, when
+ * you want all the phandles in the overlay to point to the actual
+ * base dt nodes.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
+ int property)
+{
+ const char *value;
+ const char *label;
+ int len;
+
+ value = fdt_getprop_by_offset(fdto, property,
+ &label, &len);
+ if (!value) {
+ if (len == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_INTERNAL;
+
+ return len;
+ }
+
+ do {
+ const char *path, *name, *fixup_end;
+ const char *fixup_str = value;
+ uint32_t path_len, name_len;
+ uint32_t fixup_len;
+ char *sep, *endptr;
+ int poffset, ret;
+
+ fixup_end = memchr(value, '\0', len);
+ if (!fixup_end)
+ return -FDT_ERR_BADOVERLAY;
+ fixup_len = fixup_end - fixup_str;
+
+ len -= fixup_len + 1;
+ value += fixup_len + 1;
+
+ path = fixup_str;
+ sep = memchr(fixup_str, ':', fixup_len);
+ if (!sep || *sep != ':')
+ return -FDT_ERR_BADOVERLAY;
+
+ path_len = sep - path;
+ if (path_len == (fixup_len - 1))
+ return -FDT_ERR_BADOVERLAY;
+
+ fixup_len -= path_len + 1;
+ name = sep + 1;
+ sep = memchr(name, ':', fixup_len);
+ if (!sep || *sep != ':')
+ return -FDT_ERR_BADOVERLAY;
+
+ name_len = sep - name;
+ if (!name_len)
+ return -FDT_ERR_BADOVERLAY;
+
+ poffset = strtoul(sep + 1, &endptr, 10);
+ if ((*endptr != '\0') || (endptr <= (sep + 1)))
+ return -FDT_ERR_BADOVERLAY;
+
+ ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off,
+ path, path_len, name, name_len,
+ poffset, label);
+ if (ret)
+ return ret;
+ } while (len > 0);
+
+ return 0;
+}
+
+/**
+ * overlay_fixup_phandles - Resolve the overlay phandles to the base
+ * device tree
+ * @fdt: Base Device Tree blob
+ * @fdto: Device tree overlay blob
+ *
+ * overlay_fixup_phandles() resolves all the overlay phandles pointing
+ * to nodes in the base device tree.
+ *
+ * This is one of the steps of the device tree overlay application
+ * process, when you want all the phandles in the overlay to point to
+ * the actual base dt nodes.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_fixup_phandles(void *fdt, void *fdto)
+{
+ int fixups_off, symbols_off;
+ int property;
+
+ /* We can have overlays without any fixups */
+ fixups_off = fdt_path_offset(fdto, "/__fixups__");
+ if (fixups_off == -FDT_ERR_NOTFOUND)
+ return 0; /* nothing to do */
+ if (fixups_off < 0)
+ return fixups_off;
+
+ /* And base DTs without symbols */
+ symbols_off = fdt_path_offset(fdt, "/__symbols__");
+ if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND)))
+ return symbols_off;
+
+ fdt_for_each_property_offset(property, fdto, fixups_off) {
+ int ret;
+
+ ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * overlay_apply_node - Merges a node into the base device tree
+ * @fdt: Base Device Tree blob
+ * @target: Node offset in the base device tree to apply the fragment to
+ * @fdto: Device tree overlay blob
+ * @node: Node offset in the overlay holding the changes to merge
+ *
+ * overlay_apply_node() merges a node into a target base device tree
+ * node pointed.
+ *
+ * This is part of the final step in the device tree overlay
+ * application process, when all the phandles have been adjusted and
+ * resolved and you just have to merge overlay into the base device
+ * tree.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_apply_node(void *fdt, int target,
+ void *fdto, int node)
+{
+ int property;
+ int subnode;
+
+ fdt_for_each_property_offset(property, fdto, node) {
+ const char *name;
+ const void *prop;
+ int prop_len;
+ int ret;
+
+ prop = fdt_getprop_by_offset(fdto, property, &name,
+ &prop_len);
+ if (prop_len == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_INTERNAL;
+ if (prop_len < 0)
+ return prop_len;
+
+ ret = fdt_setprop(fdt, target, name, prop, prop_len);
+ if (ret)
+ return ret;
+ }
+
+ fdt_for_each_subnode(subnode, fdto, node) {
+ const char *name = fdt_get_name(fdto, subnode, NULL);
+ int nnode;
+ int ret;
+
+ nnode = fdt_add_subnode(fdt, target, name);
+ if (nnode == -FDT_ERR_EXISTS) {
+ nnode = fdt_subnode_offset(fdt, target, name);
+ if (nnode == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_INTERNAL;
+ }
+
+ if (nnode < 0)
+ return nnode;
+
+ ret = overlay_apply_node(fdt, nnode, fdto, subnode);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * overlay_merge - Merge an overlay into its base device tree
+ * @fdt: Base Device Tree blob
+ * @fdto: Device tree overlay blob
+ *
+ * overlay_merge() merges an overlay into its base device tree.
+ *
+ * This is the final step in the device tree overlay application
+ * process, when all the phandles have been adjusted and resolved and
+ * you just have to merge overlay into the base device tree.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_merge(void *fdt, void *fdto)
+{
+ int fragment;
+
+ fdt_for_each_subnode(fragment, fdto, 0) {
+ int overlay;
+ int target;
+ int ret;
+
+ /*
+ * Each fragments will have an __overlay__ node. If
+ * they don't, it's not supposed to be merged
+ */
+ overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
+ if (overlay == -FDT_ERR_NOTFOUND)
+ continue;
+
+ if (overlay < 0)
+ return overlay;
+
+ target = overlay_get_target(fdt, fdto, fragment);
+ if (target < 0)
+ return target;
+
+ ret = overlay_apply_node(fdt, target, fdto, overlay);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int fdt_overlay_apply(void *fdt, void *fdto)
+{
+ uint32_t delta = fdt_get_max_phandle(fdt);
+ int ret;
+
+ FDT_CHECK_HEADER(fdt);
+ FDT_CHECK_HEADER(fdto);
+
+ ret = overlay_adjust_local_phandles(fdto, delta);
+ if (ret)
+ goto err;
+
+ ret = overlay_update_local_references(fdto, delta);
+ if (ret)
+ goto err;
+
+ ret = overlay_fixup_phandles(fdt, fdto);
+ if (ret)
+ goto err;
+
+ ret = overlay_merge(fdt, fdto);
+ if (ret)
+ goto err;
+
+ /*
+ * The overlay has been damaged, erase its magic.
+ */
+ fdt_set_magic(fdto, ~0);
+
+ return 0;
+
+err:
+ /*
+ * The overlay might have been damaged, erase its magic.
+ */
+ fdt_set_magic(fdto, ~0);
+
+ /*
+ * The base device tree might have been damaged, erase its
+ * magic.
+ */
+ fdt_set_magic(fdt, ~0);
+
+ return ret;
+}
diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c
new file mode 100644
index 0000000..3d00d2e
--- /dev/null
+++ b/libfdt/fdt_ro.c
@@ -0,0 +1,703 @@
+/*
+ * 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 _fdt_nodename_eq(const void *fdt, int offset,
+ const char *s, int len)
+{
+ const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, 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 (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
+}
+
+static int _fdt_string_eq(const void *fdt, int stroffset,
+ const char *s, int len)
+{
+ const char *p = fdt_string(fdt, stroffset);
+
+ return (strlen(p) == len) && (memcmp(p, s, len) == 0);
+}
+
+uint32_t fdt_get_max_phandle(const void *fdt)
+{
+ uint32_t max_phandle = 0;
+ int offset;
+
+ for (offset = fdt_next_node(fdt, -1, NULL);;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ uint32_t phandle;
+
+ if (offset == -FDT_ERR_NOTFOUND)
+ return max_phandle;
+
+ if (offset < 0)
+ return (uint32_t)-1;
+
+ phandle = fdt_get_phandle(fdt, offset);
+ if (phandle == (uint32_t)-1)
+ continue;
+
+ if (phandle > max_phandle)
+ max_phandle = phandle;
+ }
+
+ return 0;
+}
+
+int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
+{
+ FDT_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;
+}
+
+static int _nextprop(const void *fdt, int offset)
+{
+ uint32_t tag;
+ int nextoffset;
+
+ do {
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_END:
+ if (nextoffset >= 0)
+ return -FDT_ERR_BADSTRUCTURE;
+ else
+ return nextoffset;
+
+ case FDT_PROP:
+ return offset;
+ }
+ offset = nextoffset;
+ } while (tag == FDT_NOP);
+
+ return -FDT_ERR_NOTFOUND;
+}
+
+int fdt_subnode_offset_namelen(const void *fdt, int offset,
+ const char *name, int namelen)
+{
+ int depth;
+
+ FDT_CHECK_HEADER(fdt);
+
+ for (depth = 0;
+ (offset >= 0) && (depth >= 0);
+ offset = fdt_next_node(fdt, offset, &depth))
+ if ((depth == 1)
+ && _fdt_nodename_eq(fdt, offset, name, namelen))
+ return offset;
+
+ if (depth < 0)
+ return -FDT_ERR_NOTFOUND;
+ return offset; /* error */
+}
+
+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_namelen(const void *fdt, const char *path, int namelen)
+{
+ const char *end = path + namelen;
+ const char *p = path;
+ int offset = 0;
+
+ FDT_CHECK_HEADER(fdt);
+
+ /* see if we have an alias */
+ if (*path != '/') {
+ const char *q = memchr(path, '/', end - p);
+
+ if (!q)
+ q = end;
+
+ p = fdt_get_alias_namelen(fdt, p, q - p);
+ if (!p)
+ return -FDT_ERR_BADPATH;
+ offset = fdt_path_offset(fdt, p);
+
+ p = q;
+ }
+
+ while (p < end) {
+ const char *q;
+
+ while (*p == '/') {
+ p++;
+ if (p == end)
+ return offset;
+ }
+ q = memchr(p, '/', end - p);
+ if (! q)
+ q = end;
+
+ offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
+ if (offset < 0)
+ return offset;
+
+ p = q;
+ }
+
+ return offset;
+}
+
+int fdt_path_offset(const void *fdt, const char *path)
+{
+ return fdt_path_offset_namelen(fdt, path, strlen(path));
+}
+
+const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
+{
+ const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
+ int err;
+
+ if (((err = fdt_check_header(fdt)) != 0)
+ || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
+ goto fail;
+
+ if (len)
+ *len = strlen(nh->name);
+
+ return nh->name;
+
+ fail:
+ if (len)
+ *len = err;
+ return NULL;
+}
+
+int fdt_first_property_offset(const void *fdt, int nodeoffset)
+{
+ int offset;
+
+ if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
+ return offset;
+
+ return _nextprop(fdt, offset);
+}
+
+int fdt_next_property_offset(const void *fdt, int offset)
+{
+ if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0)
+ return offset;
+
+ return _nextprop(fdt, offset);
+}
+
+const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
+ int offset,
+ int *lenp)
+{
+ int err;
+ const struct fdt_property *prop;
+
+ if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {
+ if (lenp)
+ *lenp = err;
+ return NULL;
+ }
+
+ prop = _fdt_offset_ptr(fdt, offset);
+
+ if (lenp)
+ *lenp = fdt32_to_cpu(prop->len);
+
+ return prop;
+}
+
+const struct fdt_property *fdt_get_property_namelen(const void *fdt,
+ int offset,
+ const char *name,
+ int namelen, int *lenp)
+{
+ for (offset = fdt_first_property_offset(fdt, offset);
+ (offset >= 0);
+ (offset = fdt_next_property_offset(fdt, offset))) {
+ const struct fdt_property *prop;
+
+ if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) {
+ offset = -FDT_ERR_INTERNAL;
+ break;
+ }
+ if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
+ name, namelen))
+ return prop;
+ }
+
+ if (lenp)
+ *lenp = offset;
+ return NULL;
+}
+
+const struct fdt_property *fdt_get_property(const void *fdt,
+ int nodeoffset,
+ const char *name, int *lenp)
+{
+ return fdt_get_property_namelen(fdt, nodeoffset, name,
+ strlen(name), lenp);
+}
+
+const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
+ const char *name, int namelen, int *lenp)
+{
+ const struct fdt_property *prop;
+
+ prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
+ if (! prop)
+ return NULL;
+
+ return prop->data;
+}
+
+const void *fdt_getprop_by_offset(const void *fdt, int offset,
+ const char **namep, int *lenp)
+{
+ const struct fdt_property *prop;
+
+ prop = fdt_get_property_by_offset(fdt, offset, lenp);
+ if (!prop)
+ return NULL;
+ if (namep)
+ *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
+ return prop->data;
+}
+
+const void *fdt_getprop(const void *fdt, int nodeoffset,
+ const char *name, int *lenp)
+{
+ return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
+}
+
+uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
+{
+ const fdt32_t *php;
+ int len;
+
+ /* FIXME: This is a bit sub-optimal, since we potentially scan
+ * over all the properties twice. */
+ php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
+ if (!php || (len != sizeof(*php))) {
+ php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
+ if (!php || (len != sizeof(*php)))
+ return 0;
+ }
+
+ return fdt32_to_cpu(*php);
+}
+
+const char *fdt_get_alias_namelen(const void *fdt,
+ const char *name, int namelen)
+{
+ int aliasoffset;
+
+ aliasoffset = fdt_path_offset(fdt, "/aliases");
+ if (aliasoffset < 0)
+ return NULL;
+
+ return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
+}
+
+const char *fdt_get_alias(const void *fdt, const char *name)
+{
+ return fdt_get_alias_namelen(fdt, name, strlen(name));
+}
+
+int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
+{
+ int pdepth = 0, p = 0;
+ int offset, depth, namelen;
+ const char *name;
+
+ FDT_CHECK_HEADER(fdt);
+
+ if (buflen < 2)
+ return -FDT_ERR_NOSPACE;
+
+ for (offset = 0, depth = 0;
+ (offset >= 0) && (offset <= nodeoffset);
+ offset = fdt_next_node(fdt, offset, &depth)) {
+ while (pdepth > depth) {
+ do {
+ p--;
+ } while (buf[p-1] != '/');
+ pdepth--;
+ }
+
+ if (pdepth >= depth) {
+ name = fdt_get_name(fdt, offset, &namelen);
+ if (!name)
+ return namelen;
+ if ((p + namelen + 1) <= buflen) {
+ memcpy(buf + p, name, namelen);
+ p += namelen;
+ buf[p++] = '/';
+ pdepth++;
+ }
+ }
+
+ if (offset == nodeoffset) {
+ if (pdepth < (depth + 1))
+ return -FDT_ERR_NOSPACE;
+
+ if (p > 1) /* special case so that root path is "/", not "" */
+ p--;
+ buf[p] = '\0';
+ return 0;
+ }
+ }
+
+ if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
+ return -FDT_ERR_BADOFFSET;
+ else if (offset == -FDT_ERR_BADOFFSET)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
+ int supernodedepth, int *nodedepth)
+{
+ int offset, depth;
+ int supernodeoffset = -FDT_ERR_INTERNAL;
+
+ FDT_CHECK_HEADER(fdt);
+
+ if (supernodedepth < 0)
+ return -FDT_ERR_NOTFOUND;
+
+ for (offset = 0, depth = 0;
+ (offset >= 0) && (offset <= nodeoffset);
+ offset = fdt_next_node(fdt, offset, &depth)) {
+ if (depth == supernodedepth)
+ supernodeoffset = offset;
+
+ if (offset == nodeoffset) {
+ if (nodedepth)
+ *nodedepth = depth;
+
+ if (supernodedepth > depth)
+ return -FDT_ERR_NOTFOUND;
+ else
+ return supernodeoffset;
+ }
+ }
+
+ if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
+ return -FDT_ERR_BADOFFSET;
+ else if (offset == -FDT_ERR_BADOFFSET)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ return offset; /* error from fdt_next_node() */
+}
+
+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)
+{
+ int offset;
+ const void *val;
+ int len;
+
+ FDT_CHECK_HEADER(fdt);
+
+ /* 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. */
+ for (offset = fdt_next_node(fdt, startoffset, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ val = fdt_getprop(fdt, offset, propname, &len);
+ if (val && (len == proplen)
+ && (memcmp(val, propval, len) == 0))
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
+{
+ int offset;
+
+ if ((phandle == 0) || (phandle == -1))
+ return -FDT_ERR_BADPHANDLE;
+
+ FDT_CHECK_HEADER(fdt);
+
+ /* FIXME: The algorithm here is pretty horrible: we
+ * potentially scan each property of a node in
+ * fdt_get_phandle(), 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. */
+ for (offset = fdt_next_node(fdt, -1, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ if (fdt_get_phandle(fdt, offset) == phandle)
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
+{
+ int len = strlen(str);
+ const char *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_stringlist_count(const void *fdt, int nodeoffset, const char *property)
+{
+ const char *list, *end;
+ int length, count = 0;
+
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
+ if (!list)
+ return length;
+
+ end = list + length;
+
+ while (list < end) {
+ length = strnlen(list, end - list) + 1;
+
+ /* Abort if the last string isn't properly NUL-terminated. */
+ if (list + length > end)
+ return -FDT_ERR_BADVALUE;
+
+ list += length;
+ count++;
+ }
+
+ return count;
+}
+
+int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
+ const char *string)
+{
+ int length, len, idx = 0;
+ const char *list, *end;
+
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
+ if (!list)
+ return length;
+
+ len = strlen(string) + 1;
+ end = list + length;
+
+ while (list < end) {
+ length = strnlen(list, end - list) + 1;
+
+ /* Abort if the last string isn't properly NUL-terminated. */
+ if (list + length > end)
+ return -FDT_ERR_BADVALUE;
+
+ if (length == len && memcmp(list, string, length) == 0)
+ return idx;
+
+ list += length;
+ idx++;
+ }
+
+ return -FDT_ERR_NOTFOUND;
+}
+
+const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
+ const char *property, int idx,
+ int *lenp)
+{
+ const char *list, *end;
+ int length;
+
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
+ if (!list) {
+ if (lenp)
+ *lenp = length;
+
+ return NULL;
+ }
+
+ end = list + length;
+
+ while (list < end) {
+ length = strnlen(list, end - list) + 1;
+
+ /* Abort if the last string isn't properly NUL-terminated. */
+ if (list + length > end) {
+ if (lenp)
+ *lenp = -FDT_ERR_BADVALUE;
+
+ return NULL;
+ }
+
+ if (idx == 0) {
+ if (lenp)
+ *lenp = length - 1;
+
+ return list;
+ }
+
+ list += length;
+ idx--;
+ }
+
+ if (lenp)
+ *lenp = -FDT_ERR_NOTFOUND;
+
+ return NULL;
+}
+
+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;
+
+ return !fdt_stringlist_contains(prop, len, compatible);
+}
+
+int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
+ const char *compatible)
+{
+ int offset, err;
+
+ FDT_CHECK_HEADER(fdt);
+
+ /* 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. */
+ for (offset = fdt_next_node(fdt, startoffset, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ err = fdt_node_check_compatible(fdt, offset, compatible);
+ if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
+ return err;
+ else if (err == 0)
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c
new file mode 100644
index 0000000..2eed4f5
--- /dev/null
+++ b/libfdt/fdt_rw.c
@@ -0,0 +1,490 @@
+/*
+ * 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 _fdt_blocks_misordered(const void *fdt,
+ int mem_rsv_size, int struct_size)
+{
+ return (fdt_off_mem_rsvmap(fdt) < 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 _fdt_rw_check_header(void *fdt)
+{
+ FDT_CHECK_HEADER(fdt);
+
+ if (fdt_version(fdt) < 17)
+ return -FDT_ERR_BADVERSION;
+ if (_fdt_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 FDT_RW_CHECK_HEADER(fdt) \
+ { \
+ int __err; \
+ if ((__err = _fdt_rw_check_header(fdt)) != 0) \
+ return __err; \
+ }
+
+static inline int _fdt_data_size(void *fdt)
+{
+ return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
+}
+
+static int _fdt_splice(void *fdt, void *splicepoint, int oldlen, int newlen)
+{
+ char *p = splicepoint;
+ char *end = (char *)fdt + _fdt_data_size(fdt);
+
+ if (((p + oldlen) < p) || ((p + oldlen) > end))
+ return -FDT_ERR_BADOFFSET;
+ if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt))
+ return -FDT_ERR_BADOFFSET;
+ if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt)))
+ return -FDT_ERR_NOSPACE;
+ memmove(p + newlen, p + oldlen, end - p - oldlen);
+ return 0;
+}
+
+static int _fdt_splice_mem_rsv(void *fdt, struct fdt_reserve_entry *p,
+ int oldn, int newn)
+{
+ int delta = (newn - oldn) * sizeof(*p);
+ int err;
+ err = _fdt_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 _fdt_splice_struct(void *fdt, void *p,
+ int oldlen, int newlen)
+{
+ int delta = newlen - oldlen;
+ int err;
+
+ if ((err = _fdt_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 _fdt_splice_string(void *fdt, int newlen)
+{
+ void *p = (char *)fdt
+ + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
+ int err;
+
+ if ((err = _fdt_splice(fdt, p, 0, newlen)))
+ return err;
+
+ fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
+ return 0;
+}
+
+static int _fdt_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 = _fdt_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;
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ re = _fdt_mem_rsv_w(fdt, fdt_num_mem_rsv(fdt));
+ err = _fdt_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);
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ if (n >= fdt_num_mem_rsv(fdt))
+ return -FDT_ERR_NOTFOUND;
+
+ return _fdt_splice_mem_rsv(fdt, re, 1, 0);
+}
+
+static int _fdt_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 = _fdt_splice_struct(fdt, (*prop)->data, FDT_TAGALIGN(oldlen),
+ FDT_TAGALIGN(len))))
+ return err;
+
+ (*prop)->len = cpu_to_fdt32(len);
+ return 0;
+}
+
+static int _fdt_add_property(void *fdt, int nodeoffset, const char *name,
+ int len, struct fdt_property **prop)
+{
+ int proplen;
+ int nextoffset;
+ int namestroff;
+ int err;
+
+ if ((nextoffset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
+ return nextoffset;
+
+ namestroff = _fdt_find_add_string(fdt, name);
+ if (namestroff < 0)
+ return namestroff;
+
+ *prop = _fdt_offset_ptr_w(fdt, nextoffset);
+ proplen = sizeof(**prop) + FDT_TAGALIGN(len);
+
+ err = _fdt_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;
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen);
+ if (!namep)
+ return oldlen;
+
+ newlen = strlen(name);
+
+ err = _fdt_splice_struct(fdt, namep, FDT_TAGALIGN(oldlen+1),
+ FDT_TAGALIGN(newlen+1));
+ 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;
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ err = _fdt_resize_property(fdt, nodeoffset, name, len, &prop);
+ if (err == -FDT_ERR_NOTFOUND)
+ err = _fdt_add_property(fdt, nodeoffset, name, len, &prop);
+ if (err)
+ return err;
+
+ memcpy(prop->data, val, len);
+ return 0;
+}
+
+int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ struct fdt_property *prop;
+ int err, oldlen, newlen;
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
+ if (prop) {
+ newlen = len + oldlen;
+ err = _fdt_splice_struct(fdt, prop->data,
+ FDT_TAGALIGN(oldlen),
+ FDT_TAGALIGN(newlen));
+ if (err)
+ return err;
+ prop->len = cpu_to_fdt32(newlen);
+ memcpy(prop->data + oldlen, val, len);
+ } else {
+ err = _fdt_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;
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
+ if (! prop)
+ return len;
+
+ proplen = sizeof(*prop) + FDT_TAGALIGN(len);
+ return _fdt_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;
+ fdt32_t *endtag;
+
+ FDT_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) || (tag == FDT_NOP));
+
+ nh = _fdt_offset_ptr_w(fdt, offset);
+ nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE;
+
+ err = _fdt_splice_struct(fdt, nh, 0, nodelen);
+ if (err)
+ return err;
+
+ nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
+ memset(nh->name, 0, FDT_TAGALIGN(namelen+1));
+ memcpy(nh->name, name, namelen);
+ endtag = (fdt32_t *)((char *)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;
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ endoffset = _fdt_node_end_offset(fdt, nodeoffset);
+ if (endoffset < 0)
+ return endoffset;
+
+ return _fdt_splice_struct(fdt, _fdt_offset_ptr_w(fdt, nodeoffset),
+ endoffset - nodeoffset, 0);
+}
+
+static void _fdt_packblocks(const char *old, char *new,
+ int mem_rsv_size, int struct_size)
+{
+ int mem_rsv_off, struct_off, strings_off;
+
+ mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);
+ struct_off = mem_rsv_off + mem_rsv_size;
+ strings_off = struct_off + struct_size;
+
+ memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);
+ fdt_set_off_mem_rsvmap(new, mem_rsv_off);
+
+ memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size);
+ fdt_set_off_dt_struct(new, struct_off);
+ fdt_set_size_dt_struct(new, struct_size);
+
+ memmove(new + strings_off, old + fdt_off_dt_strings(old),
+ fdt_size_dt_strings(old));
+ fdt_set_off_dt_strings(new, strings_off);
+ fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
+}
+
+int fdt_open_into(const void *fdt, void *buf, int bufsize)
+{
+ int err;
+ int mem_rsv_size, struct_size;
+ int newsize;
+ const char *fdtstart = fdt;
+ const char *fdtend = fdtstart + fdt_totalsize(fdt);
+ char *tmp;
+
+ FDT_CHECK_HEADER(fdt);
+
+ 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 (struct_size < 0)
+ return struct_size;
+ }
+
+ if (!_fdt_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 = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
+ + struct_size + fdt_size_dt_strings(fdt);
+
+ if (bufsize < newsize)
+ return -FDT_ERR_NOSPACE;
+
+ /* First attempt to build converted tree at beginning of buffer */
+ tmp = buf;
+ /* But if that overlaps with the old tree... */
+ if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {
+ /* Try right after the old tree instead */
+ tmp = (char *)(uintptr_t)fdtend;
+ if ((tmp + newsize) > ((char *)buf + bufsize))
+ return -FDT_ERR_NOSPACE;
+ }
+
+ _fdt_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;
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
+ * sizeof(struct fdt_reserve_entry);
+ _fdt_packblocks(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt));
+ fdt_set_totalsize(fdt, _fdt_data_size(fdt));
+
+ return 0;
+}
diff --git a/libfdt/fdt_strerror.c b/libfdt/fdt_strerror.c
new file mode 100644
index 0000000..9677a18
--- /dev/null
+++ b/libfdt/fdt_strerror.c
@@ -0,0 +1,102 @@
+/*
+ * 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 fdt_errtabent {
+ const char *str;
+};
+
+#define FDT_ERRTABENT(val) \
+ [(val)] = { .str = #val, }
+
+static struct fdt_errtabent fdt_errtable[] = {
+ FDT_ERRTABENT(FDT_ERR_NOTFOUND),
+ FDT_ERRTABENT(FDT_ERR_EXISTS),
+ FDT_ERRTABENT(FDT_ERR_NOSPACE),
+
+ FDT_ERRTABENT(FDT_ERR_BADOFFSET),
+ FDT_ERRTABENT(FDT_ERR_BADPATH),
+ FDT_ERRTABENT(FDT_ERR_BADPHANDLE),
+ FDT_ERRTABENT(FDT_ERR_BADSTATE),
+
+ FDT_ERRTABENT(FDT_ERR_TRUNCATED),
+ FDT_ERRTABENT(FDT_ERR_BADMAGIC),
+ FDT_ERRTABENT(FDT_ERR_BADVERSION),
+ FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE),
+ FDT_ERRTABENT(FDT_ERR_BADLAYOUT),
+ FDT_ERRTABENT(FDT_ERR_INTERNAL),
+ FDT_ERRTABENT(FDT_ERR_BADNCELLS),
+ FDT_ERRTABENT(FDT_ERR_BADVALUE),
+ FDT_ERRTABENT(FDT_ERR_BADOVERLAY),
+ FDT_ERRTABENT(FDT_ERR_NOPHANDLES),
+};
+#define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_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 > -FDT_ERRTABSIZE) {
+ const char *s = fdt_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..6a80485
--- /dev/null
+++ b/libfdt/fdt_sw.c
@@ -0,0 +1,288 @@
+/*
+ * 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 _fdt_sw_check_header(void *fdt)
+{
+ if (fdt_magic(fdt) != FDT_SW_MAGIC)
+ return -FDT_ERR_BADMAGIC;
+ /* FIXME: should check more details about the header state */
+ return 0;
+}
+
+#define FDT_SW_CHECK_HEADER(fdt) \
+ { \
+ int err; \
+ if ((err = _fdt_sw_check_header(fdt)) != 0) \
+ return err; \
+ }
+
+static void *_fdt_grab_space(void *fdt, size_t 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);
+}
+
+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, 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, 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_resize(void *fdt, void *buf, int bufsize)
+{
+ size_t headsize, tailsize;
+ char *oldtail, *newtail;
+
+ FDT_SW_CHECK_HEADER(fdt);
+
+ headsize = fdt_off_dt_struct(fdt);
+ tailsize = fdt_size_dt_strings(fdt);
+
+ if ((headsize + tailsize) > bufsize)
+ return -FDT_ERR_NOSPACE;
+
+ oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
+ newtail = (char *)buf + bufsize - tailsize;
+
+ /* Two cases to avoid clobbering data if the old and new
+ * buffers partially overlap */
+ if (buf <= fdt) {
+ memmove(buf, fdt, headsize);
+ memmove(newtail, oldtail, tailsize);
+ } else {
+ memmove(newtail, oldtail, tailsize);
+ memmove(buf, fdt, headsize);
+ }
+
+ fdt_set_off_dt_strings(buf, bufsize);
+ fdt_set_totalsize(buf, bufsize);
+
+ return 0;
+}
+
+int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
+{
+ struct fdt_reserve_entry *re;
+ int offset;
+
+ FDT_SW_CHECK_HEADER(fdt);
+
+ 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 *)((char *)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 namelen = strlen(name) + 1;
+
+ FDT_SW_CHECK_HEADER(fdt);
+
+ nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
+ 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)
+{
+ fdt32_t *en;
+
+ FDT_SW_CHECK_HEADER(fdt);
+
+ en = _fdt_grab_space(fdt, FDT_TAGSIZE);
+ if (! en)
+ return -FDT_ERR_NOSPACE;
+
+ *en = cpu_to_fdt32(FDT_END_NODE);
+ return 0;
+}
+
+static int _fdt_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 nameoff;
+
+ FDT_SW_CHECK_HEADER(fdt);
+
+ nameoff = _fdt_find_add_string(fdt, name);
+ if (nameoff == 0)
+ return -FDT_ERR_NOSPACE;
+
+ prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
+ 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)
+{
+ char *p = (char *)fdt;
+ fdt32_t *end;
+ int oldstroffset, newstroffset;
+ uint32_t tag;
+ int offset, nextoffset;
+
+ FDT_SW_CHECK_HEADER(fdt);
+
+ /* Add terminator */
+ end = _fdt_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);
+ int nameoff;
+
+ nameoff = fdt32_to_cpu(prop->nameoff);
+ nameoff += fdt_size_dt_strings(fdt);
+ prop->nameoff = cpu_to_fdt32(nameoff);
+ }
+ offset = nextoffset;
+ }
+ if (nextoffset < 0)
+ return 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..6aaab39
--- /dev/null
+++ b/libfdt/fdt_wip.c
@@ -0,0 +1,139 @@
+/*
+ * 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_namelen_partial(void *fdt, int nodeoffset,
+ const char *name, int namelen,
+ uint32_t idx, const void *val,
+ int len)
+{
+ void *propval;
+ int proplen;
+
+ propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen,
+ &proplen);
+ if (!propval)
+ return proplen;
+
+ if (proplen < (len + idx))
+ return -FDT_ERR_NOSPACE;
+
+ memcpy((char *)propval + idx, val, len);
+ return 0;
+}
+
+int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ const void *propval;
+ int proplen;
+
+ propval = fdt_getprop(fdt, nodeoffset, name, &proplen);
+ if (! propval)
+ return proplen;
+
+ if (proplen != len)
+ return -FDT_ERR_NOSPACE;
+
+ return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name,
+ strlen(name), 0,
+ val, len);
+}
+
+static void _fdt_nop_region(void *start, int len)
+{
+ fdt32_t *p;
+
+ for (p = start; (char *)p < ((char *)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;
+
+ _fdt_nop_region(prop, len + sizeof(*prop));
+
+ return 0;
+}
+
+int _fdt_node_end_offset(void *fdt, int offset)
+{
+ int depth = 0;
+
+ while ((offset >= 0) && (depth >= 0))
+ offset = fdt_next_node(fdt, offset, &depth);
+
+ return offset;
+}
+
+int fdt_nop_node(void *fdt, int nodeoffset)
+{
+ int endoffset;
+
+ endoffset = _fdt_node_end_offset(fdt, nodeoffset);
+ if (endoffset < 0)
+ return endoffset;
+
+ _fdt_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..c69e918
--- /dev/null
+++ b/libfdt/libfdt.h
@@ -0,0 +1,1803 @@
+#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: Attempted 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.
+ * This can be caused either by an invalid phandle property
+ * length, or the phandle value was either 0 or -1, which 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. */
+
+/* Errors in device tree content */
+#define FDT_ERR_BADNCELLS 14
+ /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells
+ * or similar property with a bad format or value */
+
+#define FDT_ERR_BADVALUE 15
+ /* FDT_ERR_BADVALUE: Device tree has a property with an unexpected
+ * value. For example: a property expected to contain a string list
+ * is not NUL-terminated within the length of its value. */
+
+#define FDT_ERR_BADOVERLAY 16
+ /* FDT_ERR_BADOVERLAY: The device tree overlay, while
+ * correctly structured, cannot be applied due to some
+ * unexpected or missing value, property or node. */
+
+#define FDT_ERR_NOPHANDLES 17
+ /* FDT_ERR_NOPHANDLES: The device tree doesn't have any
+ * phandle available anymore without causing an overflow */
+
+#define FDT_ERR_MAX 17
+
+/**********************************************************************/
+/* Low-level functions (you probably don't need these) */
+/**********************************************************************/
+
+const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen);
+static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen)
+{
+ return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen);
+}
+
+uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset);
+
+/**********************************************************************/
+/* Traversal functions */
+/**********************************************************************/
+
+int fdt_next_node(const void *fdt, int offset, int *depth);
+
+/**
+ * fdt_first_subnode() - get offset of first direct subnode
+ *
+ * @fdt: FDT blob
+ * @offset: Offset of node to check
+ * @return offset of first subnode, or -FDT_ERR_NOTFOUND if there is none
+ */
+int fdt_first_subnode(const void *fdt, int offset);
+
+/**
+ * fdt_next_subnode() - get offset of next direct subnode
+ *
+ * After first calling fdt_first_subnode(), call this function repeatedly to
+ * get direct subnodes of a parent node.
+ *
+ * @fdt: FDT blob
+ * @offset: Offset of previous subnode
+ * @return offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more
+ * subnodes
+ */
+int fdt_next_subnode(const void *fdt, int offset);
+
+/**
+ * fdt_for_each_subnode - iterate over all subnodes of a parent
+ *
+ * @node: child node (int, lvalue)
+ * @fdt: FDT blob (const void *)
+ * @parent: parent node (int)
+ *
+ * This is actually a wrapper around a for loop and would be used like so:
+ *
+ * fdt_for_each_subnode(node, fdt, parent) {
+ * Use node
+ * ...
+ * }
+ *
+ * if ((node < 0) && (node != -FDT_ERR_NOT_FOUND)) {
+ * Error handling
+ * }
+ *
+ * Note that this is implemented as a macro and @node is used as
+ * iterator in the loop. The parent variable be constant or even a
+ * literal.
+ *
+ */
+#define fdt_for_each_subnode(node, fdt, parent) \
+ for (node = fdt_first_subnode(fdt, parent); \
+ node >= 0; \
+ node = fdt_next_subnode(fdt, node))
+
+/**********************************************************************/
+/* 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 = (struct fdt_header *)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 - retrieve 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_get_max_phandle - retrieves the highest phandle in a tree
+ * @fdt: pointer to the device tree blob
+ *
+ * fdt_get_max_phandle retrieves the highest phandle in the given
+ * device tree. This will ignore badly formatted phandles, or phandles
+ * with a value of 0 or -1.
+ *
+ * returns:
+ * the highest phandle on success
+ * 0, if no phandle was found in the device tree
+ * -1, if an error occurred
+ */
+uint32_t fdt_get_max_phandle(const void *fdt);
+
+/**
+ * fdt_num_mem_rsv - retrieve 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 - retrieve 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_namelen - find a tree node by its full path
+ * @fdt: pointer to the device tree blob
+ * @path: full path of the node to locate
+ * @namelen: number of characters of path to consider
+ *
+ * Identical to fdt_path_offset(), but only consider the first namelen
+ * characters of path as the path name.
+ */
+int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen);
+
+/**
+ * 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 - retrieve 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_first_property_offset - find the offset of a node's first property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of a node
+ *
+ * fdt_first_property_offset() finds the first property of the node at
+ * the given structure block offset.
+ *
+ * returns:
+ * structure block offset of the property (>=0), on success
+ * -FDT_ERR_NOTFOUND, if the requested node has no properties
+ * -FDT_ERR_BADOFFSET, if nodeoffset 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_first_property_offset(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_next_property_offset - step through a node's properties
+ * @fdt: pointer to the device tree blob
+ * @offset: structure block offset of a property
+ *
+ * fdt_next_property_offset() finds the property immediately after the
+ * one at the given structure block offset. This will be a property
+ * of the same node as the given property.
+ *
+ * returns:
+ * structure block offset of the next property (>=0), on success
+ * -FDT_ERR_NOTFOUND, if the given property is the last in its node
+ * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_next_property_offset(const void *fdt, int offset);
+
+/**
+ * fdt_for_each_property_offset - iterate over all properties of a node
+ *
+ * @property_offset: property offset (int, lvalue)
+ * @fdt: FDT blob (const void *)
+ * @node: node offset (int)
+ *
+ * This is actually a wrapper around a for loop and would be used like so:
+ *
+ * fdt_for_each_property_offset(property, fdt, node) {
+ * Use property
+ * ...
+ * }
+ *
+ * if ((property < 0) && (property != -FDT_ERR_NOT_FOUND)) {
+ * Error handling
+ * }
+ *
+ * Note that this is implemented as a macro and property is used as
+ * iterator in the loop. The node variable can be constant or even a
+ * literal.
+ */
+#define fdt_for_each_property_offset(property, fdt, node) \
+ for (property = fdt_first_property_offset(fdt, node); \
+ property >= 0; \
+ property = fdt_next_property_offset(fdt, property))
+
+/**
+ * fdt_get_property_by_offset - retrieve the property at a given offset
+ * @fdt: pointer to the device tree blob
+ * @offset: offset of the property to retrieve
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_property_by_offset() retrieves a pointer to the
+ * fdt_property structure within the device tree blob at the given
+ * offset. If lenp is non-NULL, the length of the property value is
+ * 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_BADOFFSET, nodeoffset did not point to FDT_PROP 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_by_offset(const void *fdt,
+ int offset,
+ int *lenp);
+
+/**
+ * fdt_get_property_namelen - find a property based on substring
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @namelen: number of characters of name to consider
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * Identical to fdt_get_property(), but only examine the first namelen
+ * characters of name for matching the property name.
+ */
+const struct fdt_property *fdt_get_property_namelen(const void *fdt,
+ int nodeoffset,
+ const char *name,
+ int namelen, 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 is 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 *)(uintptr_t)
+ fdt_get_property(fdt, nodeoffset, name, lenp);
+}
+
+/**
+ * fdt_getprop_by_offset - retrieve the value of a property at a given offset
+ * @fdt: pointer to the device tree blob
+ * @ffset: offset of the property to read
+ * @namep: pointer to a string variable (will be overwritten) or NULL
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_getprop_by_offset() retrieves a pointer to the value of the
+ * property at structure block offset 'offset' (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 is also
+ * returned, in the integer pointed to by lenp. If namep is non-NULL,
+ * the property's namne will also be returned in the char * pointed to
+ * by namep (this will be a pointer to within the device tree's string
+ * block, not a new copy of the name).
+ *
+ * returns:
+ * pointer to the property's value
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * if namep is non-NULL *namep contiains a pointer to the property
+ * name.
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+const void *fdt_getprop_by_offset(const void *fdt, int offset,
+ const char **namep, int *lenp);
+
+/**
+ * fdt_getprop_namelen - get property value based on substring
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @namelen: number of characters of name to consider
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * Identical to fdt_getprop(), but only examine the first namelen
+ * characters of name for matching the property name.
+ */
+const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
+ const char *name, int namelen, int *lenp);
+static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset,
+ const char *name, int namelen,
+ int *lenp)
+{
+ return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name,
+ namelen, 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 is 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 *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp);
+}
+
+/**
+ * fdt_get_phandle - retrieve 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 success (!= 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_alias_namelen - get alias based on substring
+ * @fdt: pointer to the device tree blob
+ * @name: name of the alias th look up
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_get_alias(), but only examine the first namelen
+ * characters of name for matching the alias name.
+ */
+const char *fdt_get_alias_namelen(const void *fdt,
+ const char *name, int namelen);
+
+/**
+ * fdt_get_alias - retrieve the path referenced by a given alias
+ * @fdt: pointer to the device tree blob
+ * @name: name of the alias th look up
+ *
+ * fdt_get_alias() retrieves the value of a given alias. That is, the
+ * value of the property named 'name' in the node /aliases.
+ *
+ * returns:
+ * a pointer to the expansion of the alias named 'name', if it exists
+ * NULL, if the given alias or the /aliases node does not exist
+ */
+const char *fdt_get_alias(const void *fdt, const char *name);
+
+/**
+ * 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:
+ * structure 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_phandle() 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);
+
+/**
+ * fdt_stringlist_contains - check a string list property for a string
+ * @strlist: Property containing a list of strings to check
+ * @listlen: Length of property
+ * @str: String to search for
+ *
+ * This is a utility function provided for convenience. The list contains
+ * one or more strings, each terminated by \0, as is found in a device tree
+ * "compatible" property.
+ *
+ * @return: 1 if the string is found in the list, 0 not found, or invalid list
+ */
+int fdt_stringlist_contains(const char *strlist, int listlen, const char *str);
+
+/**
+ * fdt_stringlist_count - count the number of strings in a string list
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @property: name of the property containing the string list
+ * @return:
+ * the number of strings in the given property
+ * -FDT_ERR_BADVALUE if the property value is not NUL-terminated
+ * -FDT_ERR_NOTFOUND if the property does not exist
+ */
+int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property);
+
+/**
+ * fdt_stringlist_search - find a string in a string list and return its index
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @property: name of the property containing the string list
+ * @string: string to look up in the string list
+ *
+ * Note that it is possible for this function to succeed on property values
+ * that are not NUL-terminated. That's because the function will stop after
+ * finding the first occurrence of @string. This can for example happen with
+ * small-valued cell properties, such as #address-cells, when searching for
+ * the empty string.
+ *
+ * @return:
+ * the index of the string in the list of strings
+ * -FDT_ERR_BADVALUE if the property value is not NUL-terminated
+ * -FDT_ERR_NOTFOUND if the property does not exist or does not contain
+ * the given string
+ */
+int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
+ const char *string);
+
+/**
+ * fdt_stringlist_get() - obtain the string at a given index in a string list
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @property: name of the property containing the string list
+ * @index: index of the string to return
+ * @lenp: return location for the string length or an error code on failure
+ *
+ * Note that this will successfully extract strings from properties with
+ * non-NUL-terminated values. For example on small-valued cell properties
+ * this function will return the empty string.
+ *
+ * If non-NULL, the length of the string (on success) or a negative error-code
+ * (on failure) will be stored in the integer pointer to by lenp.
+ *
+ * @return:
+ * A pointer to the string at the given index in the string list or NULL on
+ * failure. On success the length of the string will be stored in the memory
+ * location pointed to by the lenp parameter, if non-NULL. On failure one of
+ * the following negative error codes will be returned in the lenp parameter
+ * (if non-NULL):
+ * -FDT_ERR_BADVALUE if the property value is not NUL-terminated
+ * -FDT_ERR_NOTFOUND if the property does not exist
+ */
+const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
+ const char *property, int index,
+ int *lenp);
+
+/**********************************************************************/
+/* Read-only functions (addressing related) */
+/**********************************************************************/
+
+/**
+ * FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells
+ *
+ * This is the maximum value for #address-cells, #size-cells and
+ * similar properties that will be processed by libfdt. IEE1275
+ * requires that OF implementations handle values up to 4.
+ * Implementations may support larger values, but in practice higher
+ * values aren't used.
+ */
+#define FDT_MAX_NCELLS 4
+
+/**
+ * fdt_address_cells - retrieve address size for a bus represented in the tree
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to find the address size for
+ *
+ * When the node has a valid #address-cells property, returns its value.
+ *
+ * returns:
+ * 0 <= n < FDT_MAX_NCELLS, on success
+ * 2, if the node has no #address-cells property
+ * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
+ * #address-cells property
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_address_cells(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_size_cells - retrieve address range size for a bus represented in the
+ * tree
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to find the address range size for
+ *
+ * When the node has a valid #size-cells property, returns its value.
+ *
+ * returns:
+ * 0 <= n < FDT_MAX_NCELLS, on success
+ * 2, if the node has no #address-cells property
+ * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
+ * #size-cells property
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_size_cells(const void *fdt, int nodeoffset);
+
+
+/**********************************************************************/
+/* Write-in-place functions */
+/**********************************************************************/
+
+/**
+ * fdt_setprop_inplace_namelen_partial - 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
+ * @namelen: number of characters of name to consider
+ * @idx: index of the property to change in the array
+ * @val: pointer to data to replace the property value with
+ * @len: length of the property value
+ *
+ * Identical to fdt_setprop_inplace(), but modifies the given property
+ * starting from the given index, and using only the first characters
+ * of the name. It is useful when you want to manipulate only one value of
+ * an array and you have a string that doesn't end with \0.
+ */
+int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
+ const char *name, int namelen,
+ uint32_t idx, const void *val,
+ int len);
+
+/**
+ * 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_u32 - change the value of a 32-bit integer 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: 32-bit integer value to replace the property with
+ *
+ * fdt_setprop_inplace_u32() replaces the value of a given property
+ * with the 32-bit integer 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_u32(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ fdt32_t tmp = cpu_to_fdt32(val);
+ return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_setprop_inplace_u64 - change the value of a 64-bit integer 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: 64-bit integer value to replace the property with
+ *
+ * fdt_setprop_inplace_u64() replaces the value of a given property
+ * with the 64-bit integer 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
+ * 8.
+ *
+ * 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 8
+ * -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_u64(void *fdt, int nodeoffset,
+ const char *name, uint64_t val)
+{
+ fdt64_t tmp = cpu_to_fdt64(val);
+ return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_setprop_inplace_cell - change the value of a single-cell property
+ *
+ * This is an alternative name for fdt_setprop_inplace_u32()
+ */
+static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ return fdt_setprop_inplace_u32(fdt, nodeoffset, name, 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_resize(void *fdt, 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_u32(void *fdt, const char *name, uint32_t val)
+{
+ fdt32_t tmp = cpu_to_fdt32(val);
+ return fdt_property(fdt, name, &tmp, sizeof(tmp));
+}
+static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val)
+{
+ fdt64_t tmp = cpu_to_fdt64(val);
+ return fdt_property(fdt, name, &tmp, sizeof(tmp));
+}
+static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val)
+{
+ return fdt_property_u32(fdt, name, 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_create_empty_tree(void *buf, int bufsize);
+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
+ * @address, @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
+ * therefore 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
+ * therefore 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, creating 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_u32 - set a property to a 32-bit integer
+ * @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_u32() sets the value of the named property in the given
+ * node to the given 32-bit integer 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_u32(void *fdt, int nodeoffset, const char *name,
+ uint32_t val)
+{
+ fdt32_t tmp = cpu_to_fdt32(val);
+ return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_setprop_u64 - set a property to a 64-bit integer
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 64-bit integer value for the property (native endian)
+ *
+ * fdt_setprop_u64() sets the value of the named property in the given
+ * node to the given 64-bit integer 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_u64(void *fdt, int nodeoffset, const char *name,
+ uint64_t val)
+{
+ fdt64_t tmp = cpu_to_fdt64(val);
+ return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_setprop_cell - set a property to a single cell value
+ *
+ * This is an alternative name for fdt_setprop_u32()
+ */
+static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name,
+ uint32_t val)
+{
+ return fdt_setprop_u32(fdt, nodeoffset, name, 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_appendprop - append to or create a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to append to
+ * @val: pointer to data to append to the property value
+ * @len: length of the data to append to the property value
+ *
+ * fdt_appendprop() appends the value to the named property in the
+ * given node, creating the property if it does not already exist.
+ *
+ * This function may insert data into 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_appendprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len);
+
+/**
+ * fdt_appendprop_u32 - append a 32-bit integer value to 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: 32-bit integer value to append to the property (native endian)
+ *
+ * fdt_appendprop_u32() appends the given 32-bit integer value
+ * (converting to big-endian if necessary) to the value of the named
+ * property in the given node, or creates a new property with that
+ * value if it does not already exist.
+ *
+ * This function may insert data into 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_appendprop_u32(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ fdt32_t tmp = cpu_to_fdt32(val);
+ return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_appendprop_u64 - append a 64-bit integer value to 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: 64-bit integer value to append to the property (native endian)
+ *
+ * fdt_appendprop_u64() appends the given 64-bit integer value
+ * (converting to big-endian if necessary) to the value of the named
+ * property in the given node, or creates a new property with that
+ * value if it does not already exist.
+ *
+ * This function may insert data into 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_appendprop_u64(void *fdt, int nodeoffset,
+ const char *name, uint64_t val)
+{
+ fdt64_t tmp = cpu_to_fdt64(val);
+ return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_appendprop_cell - append a single cell value to a property
+ *
+ * This is an alternative name for fdt_appendprop_u32()
+ */
+static inline int fdt_appendprop_cell(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ return fdt_appendprop_u32(fdt, nodeoffset, name, val);
+}
+
+/**
+ * fdt_appendprop_string - append a string to 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
+ * @str: string value to append to the property
+ *
+ * fdt_appendprop_string() appends the given string to the value of
+ * the named property in the given node, or creates a new property
+ * with that value if it does not already exist.
+ *
+ * This function may insert data into 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_appendprop_string(fdt, nodeoffset, name, str) \
+ fdt_appendprop((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);
+
+/**
+ * fdt_overlay_apply - Applies a DT overlay on a base DT
+ * @fdt: pointer to the base device tree blob
+ * @fdto: pointer to the device tree overlay blob
+ *
+ * fdt_overlay_apply() will apply the given device tree overlay on the
+ * given base device tree.
+ *
+ * Expect the base device tree to be modified, even if the function
+ * returns an error.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there's not enough space in the base device tree
+ * -FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or
+ * properties in the base DT
+ * -FDT_ERR_BADPHANDLE,
+ * -FDT_ERR_BADOVERLAY,
+ * -FDT_ERR_NOPHANDLES,
+ * -FDT_ERR_INTERNAL,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADOFFSET,
+ * -FDT_ERR_BADPATH,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_overlay_apply(void *fdt, void *fdto);
+
+/**********************************************************************/
+/* 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..99f936d
--- /dev/null
+++ b/libfdt/libfdt_env.h
@@ -0,0 +1,112 @@
+#ifndef _LIBFDT_ENV_H
+#define _LIBFDT_ENV_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ * Copyright 2012 Kim Phillips, Freescale Semiconductor.
+ *
+ * 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 <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __CHECKER__
+#define __force __attribute__((force))
+#define __bitwise __attribute__((bitwise))
+#else
+#define __force
+#define __bitwise
+#endif
+
+typedef uint16_t __bitwise fdt16_t;
+typedef uint32_t __bitwise fdt32_t;
+typedef uint64_t __bitwise fdt64_t;
+
+#define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n])
+#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1))
+#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \
+ (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3))
+#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \
+ (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \
+ (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \
+ (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7))
+
+static inline uint16_t fdt16_to_cpu(fdt16_t x)
+{
+ return (__force uint16_t)CPU_TO_FDT16(x);
+}
+static inline fdt16_t cpu_to_fdt16(uint16_t x)
+{
+ return (__force fdt16_t)CPU_TO_FDT16(x);
+}
+
+static inline uint32_t fdt32_to_cpu(fdt32_t x)
+{
+ return (__force uint32_t)CPU_TO_FDT32(x);
+}
+static inline fdt32_t cpu_to_fdt32(uint32_t x)
+{
+ return (__force fdt32_t)CPU_TO_FDT32(x);
+}
+
+static inline uint64_t fdt64_to_cpu(fdt64_t x)
+{
+ return (__force uint64_t)CPU_TO_FDT64(x);
+}
+static inline fdt64_t cpu_to_fdt64(uint64_t x)
+{
+ return (__force fdt64_t)CPU_TO_FDT64(x);
+}
+#undef CPU_TO_FDT64
+#undef CPU_TO_FDT32
+#undef CPU_TO_FDT16
+#undef EXTRACT_BYTE
+
+#endif /* _LIBFDT_ENV_H */
diff --git a/libfdt/libfdt_internal.h b/libfdt/libfdt_internal.h
new file mode 100644
index 0000000..02cfa6f
--- /dev/null
+++ b/libfdt/libfdt_internal.h
@@ -0,0 +1,95 @@
+#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 FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
+#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
+
+#define FDT_CHECK_HEADER(fdt) \
+ { \
+ int __err; \
+ if ((__err = fdt_check_header(fdt)) != 0) \
+ return __err; \
+ }
+
+int _fdt_check_node_offset(const void *fdt, int offset);
+int _fdt_check_prop_offset(const void *fdt, int offset);
+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 (const char *)fdt + fdt_off_dt_struct(fdt) + offset;
+}
+
+static inline void *_fdt_offset_ptr_w(void *fdt, int offset)
+{
+ return (void *)(uintptr_t)_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 =
+ (const struct fdt_reserve_entry *)
+ ((const char *)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 *)(uintptr_t)_fdt_mem_rsv(fdt, n);
+}
+
+#define FDT_SW_MAGIC (~FDT_MAGIC)
+
+#endif /* _LIBFDT_INTERNAL_H */
diff --git a/libfdt/version.lds b/libfdt/version.lds
new file mode 100644
index 0000000..cff0358
--- /dev/null
+++ b/libfdt/version.lds
@@ -0,0 +1,68 @@
+LIBFDT_1.2 {
+ global:
+ fdt_next_node;
+ fdt_check_header;
+ fdt_move;
+ fdt_string;
+ fdt_num_mem_rsv;
+ fdt_get_mem_rsv;
+ fdt_subnode_offset_namelen;
+ fdt_subnode_offset;
+ fdt_path_offset_namelen;
+ fdt_path_offset;
+ fdt_get_name;
+ fdt_get_property_namelen;
+ fdt_get_property;
+ fdt_getprop_namelen;
+ fdt_getprop;
+ fdt_get_phandle;
+ fdt_get_alias_namelen;
+ fdt_get_alias;
+ fdt_get_path;
+ fdt_supernode_atdepth_offset;
+ fdt_node_depth;
+ fdt_parent_offset;
+ fdt_node_offset_by_prop_value;
+ fdt_node_offset_by_phandle;
+ fdt_node_check_compatible;
+ fdt_node_offset_by_compatible;
+ fdt_setprop_inplace;
+ fdt_nop_property;
+ fdt_nop_node;
+ fdt_create;
+ fdt_add_reservemap_entry;
+ fdt_finish_reservemap;
+ fdt_begin_node;
+ fdt_property;
+ fdt_end_node;
+ fdt_finish;
+ fdt_open_into;
+ fdt_pack;
+ fdt_add_mem_rsv;
+ fdt_del_mem_rsv;
+ fdt_set_name;
+ fdt_setprop;
+ fdt_delprop;
+ fdt_add_subnode_namelen;
+ fdt_add_subnode;
+ fdt_del_node;
+ fdt_strerror;
+ fdt_offset_ptr;
+ fdt_next_tag;
+ fdt_appendprop;
+ fdt_create_empty_tree;
+ fdt_first_property_offset;
+ fdt_get_property_by_offset;
+ fdt_getprop_by_offset;
+ fdt_next_property_offset;
+ fdt_first_subnode;
+ fdt_next_subnode;
+ fdt_address_cells;
+ fdt_size_cells;
+ fdt_stringlist_contains;
+ fdt_resize;
+ fdt_overlay_apply;
+
+ local:
+ *;
+};
diff --git a/libpdbg/adu.c b/libpdbg/adu.c
index 466924d..4f2e9cb 100644
--- a/libpdbg/adu.c
+++ b/libpdbg/adu.c
@@ -20,11 +20,13 @@
#include "operations.h"
#include "bitutils.h"
+enum adu_retcode {ADU_DONE, ADU_RETRY, ADU_ERR};
+
/* ADU SCOM Register Definitions */
-#define ALTD_CONTROL_REG 0x2020000
-#define ALTD_CMD_REG 0x2020001
-#define ALTD_STATUS_REG 0x2020002
-#define ALTD_DATA_REG 0x2020003
+#define ALTD_CONTROL_REG 0x0
+#define ALTD_CMD_REG 0x1
+#define P8_ALTD_STATUS_REG 0x2
+#define P8_ALTD_DATA_REG 0x3
/* ALTD_CMD_REG fields */
#define FBC_ALTD_START_OP PPC_BIT(2)
@@ -58,31 +60,31 @@
#define TTYPE_DMA_PARTIAL_READ 0b110101
#define TTYPE_PBOPERATION 0b111111
-/* ALTD_STATUS_REG fields */
+/* P8_ALTD_STATUS_REG fields */
#define FBC_ALTD_ADDR_DONE PPC_BIT(2)
#define FBC_ALTD_DATA_DONE PPC_BIT(3)
#define FBC_ALTD_PBINIT_MISSING PPC_BIT(18)
-static int adu_lock(struct target *target)
+static int adu_lock(struct adu *adu)
{
uint64_t val;
- CHECK_ERR(read_target(target, ALTD_CMD_REG, &val));
+ CHECK_ERR(pib_read(&adu->target, ALTD_CMD_REG, &val));
if (val & FBC_LOCKED)
PR_INFO("ADU already locked! Ignoring.\n");
val |= FBC_LOCKED;
- CHECK_ERR(write_target(target, ALTD_CMD_REG, val));
+ CHECK_ERR(pib_write(&adu->target, ALTD_CMD_REG, val));
return 0;
}
-static int adu_unlock(struct target *target)
+static int adu_unlock(struct adu *adu)
{
uint64_t val;
- CHECK_ERR(read_target(target, ALTD_CMD_REG, &val));
+ CHECK_ERR(pib_read(&adu->target, ALTD_CMD_REG, &val));
if (!(val & FBC_LOCKED)) {
PR_INFO("ADU already unlocked!\n");
@@ -90,43 +92,72 @@ static int adu_unlock(struct target *target)
}
val &= ~FBC_LOCKED;
- CHECK_ERR(write_target(target, ALTD_CMD_REG, val));
+ CHECK_ERR(pib_write(&adu->target, ALTD_CMD_REG, val));
return 0;
}
-static int adu_reset(struct target *target)
+static int adu_reset(struct adu *adu)
{
uint64_t val;
- CHECK_ERR(read_target(target, ALTD_CMD_REG, &val));
+ CHECK_ERR(pib_read(&adu->target, ALTD_CMD_REG, &val));
val |= FBC_ALTD_CLEAR_STATUS | FBC_ALTD_RESET_AD_PCB;
- CHECK_ERR(write_target(target, ALTD_CMD_REG, val));
+ CHECK_ERR(pib_write(&adu->target, ALTD_CMD_REG, val));
return 0;
}
-/* Return size bytes of memory in *output. *output must point to an
- * array large enough to hold size bytes. */
-int adu_getmem(struct target *target, uint64_t start_addr, uint8_t *output, uint64_t size)
+static uint64_t p8_get_ctrl_reg(uint64_t ctrl_reg, uint64_t addr)
{
- int rc = 0;
- uint64_t addr, cmd_reg, ctrl_reg, val;
-
- /* P9 ADU is not currently supported */
- if (target->chip_type == CHIP_P9)
- return -1;
-
- CHECK_ERR(adu_lock(target));
-
- ctrl_reg = TTYPE_TREAD;
+ ctrl_reg |= TTYPE_TREAD;
ctrl_reg = SETFIELD(FBC_ALTD_TTYPE, ctrl_reg, TTYPE_DMA_PARTIAL_READ);
ctrl_reg = SETFIELD(FBC_ALTD_TSIZE, ctrl_reg, 8);
+ ctrl_reg = SETFIELD(FBC_ALTD_ADDRESS, ctrl_reg, addr);
+ return ctrl_reg;
+}
- CHECK_ERR(read_target(target, ALTD_CMD_REG, &cmd_reg));
+static uint64_t p8_get_cmd_reg(uint64_t cmd_reg, uint64_t addr)
+{
cmd_reg |= FBC_ALTD_START_OP;
cmd_reg = SETFIELD(FBC_ALTD_SCOPE, cmd_reg, SCOPE_SYSTEM);
cmd_reg = SETFIELD(FBC_ALTD_DROP_PRIORITY, cmd_reg, DROP_PRIORITY_MEDIUM);
+ return cmd_reg;
+}
+
+static enum adu_retcode p8_wait_completion(struct adu *adu)
+{
+ uint64_t val;
+
+ /* Wait for completion */
+ do {
+ CHECK_ERR(pib_read(&adu->target, P8_ALTD_STATUS_REG, &val));
+ } while (!val);
+
+ if( !(val & FBC_ALTD_ADDR_DONE) ||
+ !(val & FBC_ALTD_DATA_DONE)) {
+ /* PBINIT_MISSING is expected occasionally so just retry */
+ if (val & FBC_ALTD_PBINIT_MISSING)
+ return ADU_RETRY;
+ else {
+ PR_ERROR("ALTD_STATUS_REG = 0x%016llx\n", val);
+ return ADU_ERR;
+ }
+ }
+
+ return ADU_DONE;
+}
+
+static int p8_adu_getmem(struct adu *adu, uint64_t addr)
+{
+}
+
+/* Return size bytes of memory in *output. *output must point to an
+ * array large enough to hold size bytes. */
+static int _adu_getmem(struct adu *adu, uint64_t start_addr, uint8_t *output, uint64_t size)
+{
+ int rc = 0;
+ uint64_t addr, ctrl_reg, cmd_reg, val;
/* We read data in 8-byte aligned chunks */
for (addr = 8*(start_addr / 8); addr < start_addr + size; addr += 8) {
@@ -134,35 +165,34 @@ int adu_getmem(struct target *target, uint64_t start_addr, uint8_t *output, uint
retry:
/* Clear status bits */
- CHECK_ERR(adu_reset(target));
+ CHECK_ERR(adu_reset(adu));
/* Set the address */
- ctrl_reg = SETFIELD(FBC_ALTD_ADDRESS, ctrl_reg, addr);
- CHECK_ERR(write_target(target, ALTD_CONTROL_REG, ctrl_reg));
+ ctrl_reg = adu->_get_ctrl_reg(ctrl_reg, addr);
+ cmd_reg = adu->_get_cmd_reg(cmd_reg, addr);
+
+ CHECK_ERR(pib_write(&adu->target, ALTD_CONTROL_REG, ctrl_reg));
/* Start the command */
- CHECK_ERR(write_target(target, ALTD_CMD_REG, cmd_reg));
+ CHECK_ERR(pib_write(&adu->target, ALTD_CMD_REG, cmd_reg));
/* Wait for completion */
- do {
- CHECK_ERR(read_target(target, ALTD_STATUS_REG, &val));
- } while (!val);
-
- if( !(val & FBC_ALTD_ADDR_DONE) ||
- !(val & FBC_ALTD_DATA_DONE)) {
- /* PBINIT_MISSING is expected occasionally so just retry */
- if (val & FBC_ALTD_PBINIT_MISSING)
- goto retry;
- else {
- PR_ERROR("Unable to read memory. " \
- "ALTD_STATUS_REG = 0x%016llx\n", val);
- rc = -1;
- break;
- }
+ switch (adu->_wait_completion(adu)) {
+ case ADU_DONE:
+ break;
+ case ADU_RETRY:
+ goto retry;
+ break;
+ case ADU_ERR:
+ /* Fall through - other return codes also indicate error */
+ default:
+ PR_ERROR("Unable to read memory\n");
+ rc = ADU_ERR;
+ break;
}
/* Read data */
- CHECK_ERR(read_target(target, ALTD_DATA_REG, &data));
+ CHECK_ERR(pib_read(&adu->target, P8_ALTD_DATA_REG, &data));
/* ADU returns data in big-endian form in the register */
data = __builtin_bswap64(data);
@@ -178,26 +208,22 @@ int adu_getmem(struct target *target, uint64_t start_addr, uint8_t *output, uint
}
}
- adu_unlock(target);
+ adu_unlock(adu);
return rc;
}
-int adu_putmem(struct target *target, uint64_t start_addr, uint8_t *input, uint64_t size)
+int p8_adu_putmem(struct adu *adu, uint64_t start_addr, uint8_t *input, uint64_t size)
{
int rc = 0, tsize;
uint64_t addr, cmd_reg, ctrl_reg, val, data, end_addr;
- /* P9 ADU is not currently supported */
- if (target->chip_type == CHIP_P9)
- return -1;
-
- CHECK_ERR(adu_lock(target));
+ CHECK_ERR(adu_lock(adu));
ctrl_reg = TTYPE_TWRITE;
ctrl_reg = SETFIELD(FBC_ALTD_TTYPE, ctrl_reg, TTYPE_DMA_PARTIAL_WRITE);
- CHECK_ERR(read_target(target, ALTD_CMD_REG, &cmd_reg));
+ CHECK_ERR(pib_read(&adu->target, ALTD_CMD_REG, &cmd_reg));
cmd_reg |= FBC_ALTD_START_OP;
cmd_reg = SETFIELD(FBC_ALTD_SCOPE, cmd_reg, SCOPE_SYSTEM);
cmd_reg = SETFIELD(FBC_ALTD_DROP_PRIORITY, cmd_reg, DROP_PRIORITY_MEDIUM);
@@ -220,21 +246,21 @@ int adu_putmem(struct target *target, uint64_t start_addr, uint8_t *input, uint6
retry:
/* Clear status bits */
- CHECK_ERR(adu_reset(target));
+ CHECK_ERR(adu_reset(adu));
/* Set the address */
ctrl_reg = SETFIELD(FBC_ALTD_ADDRESS, ctrl_reg, addr);
- CHECK_ERR(write_target(target, ALTD_CONTROL_REG, ctrl_reg));
+ CHECK_ERR(pib_write(&adu->target, ALTD_CONTROL_REG, ctrl_reg));
/* Write the data */
- CHECK_ERR(write_target(target, ALTD_DATA_REG, data));
+ CHECK_ERR(pib_write(&adu->target, P8_ALTD_DATA_REG, data));
/* Start the command */
- CHECK_ERR(write_target(target, ALTD_CMD_REG, cmd_reg));
+ CHECK_ERR(pib_write(&adu->target, ALTD_CMD_REG, cmd_reg));
/* Wait for completion */
do {
- CHECK_ERR(read_target(target, ALTD_STATUS_REG, &val));
+ CHECK_ERR(pib_read(&adu->target, P8_ALTD_STATUS_REG, &val));
} while (!val);
if( !(val & FBC_ALTD_ADDR_DONE) ||
@@ -244,14 +270,41 @@ int adu_putmem(struct target *target, uint64_t start_addr, uint8_t *input, uint6
goto retry;
else {
PR_ERROR("Unable to write memory. " \
- "ALTD_STATUS_REG = 0x%016llx\n", val);
+ "P8_ALTD_STATUS_REG = 0x%016llx\n", val);
rc = -1;
break;
}
}
}
- adu_unlock(target);
+ adu_unlock(adu);
return rc;
}
+
+struct adu p8_adu = {
+ .target = {
+ .name = "POWER8 ADU",
+ .compatible = "ibm,power8-adu",
+ .class = "adu",
+ },
+ .getmem = _adu_getmem,
+ .putmem = p8_adu_putmem,
+ ._get_ctrl_reg = p8_get_ctrl_reg,
+ ._get_cmd_reg = p8_get_cmd_reg,
+ ._wait_completion = p8_wait_completion,
+};
+DECLARE_HW_UNIT(p8_adu);
+
+
+
+struct adu p9_adu = {
+ .target = {
+ .name = "POWER9 ADU",
+ .compatible = "ibm,power9-adu",
+ .class = "adu",
+ },
+// .getmem = p9_adu_getmem,
+// .putmem = p9_adu_putmem,
+};
+DECLARE_HW_UNIT(p9_adu);
diff --git a/libpdbg/bmcfsi.c b/libpdbg/bmcfsi.c
index a1f13a2..0687895 100644
--- a/libpdbg/bmcfsi.c
+++ b/libpdbg/bmcfsi.c
@@ -26,6 +26,7 @@
#include "bitutils.h"
#include "operations.h"
+#include "device.h"
#include "target.h"
#define GPIO_BASE 0x1e780000
@@ -50,54 +51,13 @@ enum gpio {
GPIO_CRONUS_SEL = 4,
};
-/* POWER8 GPIO mappings */
-struct gpio_pin p8_gpio_pins[] = {
- {0, 4}, /* FSI_CLK = A4*/
- {0, 5}, /* FSI_DAT = A5 */
- {0x20, 30}, /* FSI_DAT_EN = H6 */
- {0, 24}, /* FSI_ENABLE = D0 */
- {0, 6}, /* CRONUS_SEL = A6 */
-};
-/* Clock delay in a for loop, determined by trial and error with
- * -O2 */
-#define P8_CLOCK_DELAY 3
-
-/* POWER9 Witherspoon mappings */
-struct gpio_pin p9w_gpio_pins[] = {
- {0x1e0, 16}, /* FSI_CLK = AA0 */
- {0x20, 0}, /* FSI_DAT = E0 */
- {0x80, 10}, /* FSI_DAT_EN = R2 */
- {0, 24}, /* FSI_ENABLE = D0 */
- {0, 6}, /* CRONUS_SEL = A6 */
-};
-#define P9W_CLOCK_DELAY 20
-
-/* POWER9 Romulus mappings */
-struct gpio_pin p9r_gpio_pins[] = {
- {0x1e0, 16}, /* FSI_CLK = AA0 */
- {0x1e0, 18}, /* FSI_DAT = AA2 */
- {0x80, 10}, /* FSI_DAT_EN = R2 */
- {0, 24}, /* FSI_ENABLE = D0 */
- {0, 6}, /* CRONUS_SEL = A6 */
-};
-#define P9R_CLOCK_DELAY 20
-
-struct gpio_pin p9z_gpio_pins[] = {
- {0, 19}, /* FSI_CLK = C3 */
- {0, 18}, /* FSI_DAT = C2 */
- {0x78, 22}, /* FSI_DAT_EN = O6 */
- {0, 24}, /* FSI_ENABLE = D0 */
- {0x78, 30}, /* CRONUS_SEL = P6 */
-};
-#define P9Z_CLOCK_DELAY 20
-
/* Pointer to the GPIO pins to use for this system */
-static struct gpio_pin *gpio_pins;
#define FSI_CLK &gpio_pins[GPIO_FSI_CLK]
#define FSI_DAT &gpio_pins[GPIO_FSI_DAT]
#define FSI_DAT_EN &gpio_pins[GPIO_FSI_DAT_EN]
#define FSI_ENABLE &gpio_pins[GPIO_FSI_ENABLE]
#define CRONUS_SEL &gpio_pins[GPIO_CRONUS_SEL]
+static struct gpio_pin gpio_pins[GPIO_CRONUS_SEL + 1];
/* FSI result symbols */
enum fsi_result {
@@ -132,7 +92,7 @@ static int clock_delay = 0;
static void *gpio_reg = NULL;
static int mem_fd = 0;
-static void fsi_reset(struct target *target);
+static void fsi_reset(struct fsi *fsi);
static uint32_t readl(void *addr)
{
@@ -380,15 +340,12 @@ static enum fsi_result fsi_d_poll_wait(uint8_t slave_id, uint64_t *resp, int len
return rc;
}
-static int fsi_getcfam(struct target *target, uint64_t addr, uint64_t *value)
+static int fsi_getcfam(struct fsi *fsi, uint32_t addr, uint32_t *value)
{
uint64_t seq;
uint64_t resp;
enum fsi_result rc;
- /* This must be the last target in a chain */
- assert(!target->next);
-
/* Format of the read sequence is:
* 6666555555555544444444443333333333222222222211111111110000000000
* 3210987654321098765432109876543210987654321098765432109876543210
@@ -421,15 +378,12 @@ static int fsi_getcfam(struct target *target, uint64_t addr, uint64_t *value)
return rc;
}
-static int fsi_putcfam(struct target *target, uint64_t addr, uint64_t data)
+static int fsi_putcfam(struct fsi *fsi, uint32_t addr, uint32_t data)
{
uint64_t seq;
uint64_t resp;
enum fsi_result rc;
- /* This must be the last target in a chain */
- assert(!target->next);
-
/* Format of the sequence is:
* 6666555555555544444444443333333333222222222211111111110000000000
* 3210987654321098765432109876543210987654321098765432109876543210
@@ -461,22 +415,19 @@ static int fsi_putcfam(struct target *target, uint64_t addr, uint64_t data)
return rc;
}
-static void fsi_reset(struct target *target)
+static void fsi_reset(struct fsi *fsi)
{
- uint64_t val64, old_base = target->base;
-
- target->base = 0;
+ uint32_t val;
fsi_break();
/* Clear own id on the master CFAM to access hMFSI ports */
- fsi_getcfam(target, 0x800, &val64);
- val64 &= ~(PPC_BIT32(6) | PPC_BIT32(7));
- fsi_putcfam(target, 0x800, val64);
- target->base = old_base;
+ fsi_getcfam(fsi, 0x800, &val);
+ val &= ~(PPC_BIT32(6) | PPC_BIT32(7));
+ fsi_putcfam(fsi, 0x800, val);
}
-static void fsi_destroy(struct target *target)
+void fsi_destroy(struct target *target)
{
set_direction_out(FSI_CLK);
set_direction_out(FSI_DAT);
@@ -493,8 +444,9 @@ static void fsi_destroy(struct target *target)
write_gpio(CRONUS_SEL, 0);
}
-int fsi_target_init(struct target *target, const char *name, enum fsi_system_type type, struct target *next)
+int bmcfsi_probe(struct target *target)
{
+ struct fsi *fsi = target_to_fsi(target);
uint64_t value;
if (!mem_fd) {
@@ -506,27 +458,17 @@ int fsi_target_init(struct target *target, const char *name, enum fsi_system_typ
}
if (!gpio_reg) {
- switch (type) {
- case FSI_SYSTEM_P8:
- gpio_pins = p8_gpio_pins;
- clock_delay = P8_CLOCK_DELAY;
- break;
- case FSI_SYSTEM_P9W:
- gpio_pins = p9w_gpio_pins;
- clock_delay = P9W_CLOCK_DELAY;
- break;
- case FSI_SYSTEM_P9R:
- gpio_pins = p9r_gpio_pins;
- clock_delay = P9R_CLOCK_DELAY;
- break;
- case FSI_SYSTEM_P9Z:
- gpio_pins = p9z_gpio_pins;
- clock_delay = P9Z_CLOCK_DELAY;
- break;
- default:
- PR_ERROR("Unrecognized system type specified\n");
- exit(-1);
- }
+ gpio_pins[GPIO_FSI_CLK].offset = dt_prop_get_u32_index(target->dn, "fsi_clk", 0);
+ gpio_pins[GPIO_FSI_CLK].bit = dt_prop_get_u32_index(target->dn, "fsi_clk", 1);
+ gpio_pins[GPIO_FSI_DAT].offset = dt_prop_get_u32_index(target->dn, "fsi_dat", 0);
+ gpio_pins[GPIO_FSI_DAT].bit = dt_prop_get_u32_index(target->dn, "fsi_dat", 1);
+ gpio_pins[GPIO_FSI_DAT_EN].offset = dt_prop_get_u32_index(target->dn, "fsi_dat_en", 0);
+ gpio_pins[GPIO_FSI_DAT_EN].bit = dt_prop_get_u32_index(target->dn, "fsi_dat_en", 1);
+ gpio_pins[GPIO_FSI_ENABLE].offset = dt_prop_get_u32_index(target->dn, "fsi_enable", 0);
+ gpio_pins[GPIO_FSI_ENABLE].bit = dt_prop_get_u32_index(target->dn, "fsi_enable", 1);
+ gpio_pins[GPIO_CRONUS_SEL].offset = dt_prop_get_u32_index(target->dn, "cronus_sel", 0);
+ gpio_pins[GPIO_CRONUS_SEL].bit = dt_prop_get_u32_index(target->dn, "cronus_sel", 1);
+ clock_delay = dt_prop_get_u32(target->dn, "clock_delay");
/* We only have to do this init once per backend */
gpio_reg = mmap(NULL, getpagesize(),
@@ -543,17 +485,21 @@ int fsi_target_init(struct target *target, const char *name, enum fsi_system_typ
write_gpio(FSI_ENABLE, 1);
write_gpio(CRONUS_SEL, 1);
- fsi_reset(target);
+ fsi_reset(fsi);
}
- /* No cascaded devices after this one. */
- assert(next == NULL);
- target_init(target, name, 0, fsi_getcfam, fsi_putcfam, fsi_destroy,
- next);
-
- /* Read chip id */
- CHECK_ERR(read_target(target, 0xc09, &value));
- target->chip_type = get_chip_type(value);
-
return 0;
}
+
+struct fsi bmcfsi = {
+ .target = {
+ .name = "BMC GPIO bit-banging FSI master",
+ .compatible = "ibm,bmcfsi",
+ .class = "fsi",
+ .probe = bmcfsi_probe,
+
+ },
+ .read = fsi_getcfam,
+ .write = fsi_putcfam,
+};
+DECLARE_HW_UNIT(bmcfsi);
diff --git a/libpdbg/cfam.c b/libpdbg/cfam.c
index 15000b8..69bab80 100644
--- a/libpdbg/cfam.c
+++ b/libpdbg/cfam.c
@@ -22,6 +22,9 @@
#include "bitutils.h"
#include "operations.h"
+#undef PR_DEBUG
+#define PR_DEBUG(...)
+
#define FSI_DATA0_REG 0x0
#define FSI_DATA1_REG 0x1
#define FSI_CMD_REG 0x2
@@ -76,44 +79,48 @@
/* We try up to 1.2ms for an OPB access */
#define MFSI_OPB_MAX_TRIES 1200
-static int fsi2pib_getscom(struct target *target, uint64_t addr, uint64_t *value)
+static int fsi2pib_getscom(struct pib *pib, uint64_t addr, uint64_t *value)
{
- uint64_t result;
+ uint32_t result;
usleep(FSI2PIB_RELAX);
/* Get scom works by putting the address in FSI_CMD_REG and
* reading the result from FST_DATA[01]_REG. */
- CHECK_ERR(write_next_target(target, FSI_RESET_REG, FSI_RESET_CMD));
- CHECK_ERR(write_next_target(target, FSI_CMD_REG, addr));
- CHECK_ERR(read_next_target(target, FSI_DATA0_REG, &result));
- *value = result << 32;
- CHECK_ERR(read_next_target(target, FSI_DATA1_REG, &result));
+ CHECK_ERR(fsi_write(&pib->target, FSI_RESET_REG, FSI_RESET_CMD));
+ CHECK_ERR(fsi_write(&pib->target, FSI_CMD_REG, addr));
+ CHECK_ERR(fsi_read(&pib->target, FSI_DATA0_REG, &result));
+ *value = ((uint64_t) result) << 32;
+ CHECK_ERR(fsi_read(&pib->target, FSI_DATA1_REG, &result));
*value |= result;
return 0;
}
-static int fsi2pib_putscom(struct target *target, uint64_t addr, uint64_t value)
+static int fsi2pib_putscom(struct pib *pib, uint64_t addr, uint64_t value)
{
usleep(FSI2PIB_RELAX);
- CHECK_ERR(write_next_target(target, FSI_RESET_REG, FSI_RESET_CMD));
- CHECK_ERR(write_next_target(target, FSI_DATA0_REG, (value >> 32) & 0xffffffff));
- CHECK_ERR(write_next_target(target, FSI_DATA1_REG, value & 0xffffffff));
- CHECK_ERR(write_next_target(target, FSI_CMD_REG, FSI_CMD_REG_WRITE | addr));
+ CHECK_ERR(fsi_write(&pib->target, FSI_RESET_REG, FSI_RESET_CMD));
+ CHECK_ERR(fsi_write(&pib->target, FSI_DATA0_REG, (value >> 32) & 0xffffffff));
+ CHECK_ERR(fsi_write(&pib->target, FSI_DATA1_REG, value & 0xffffffff));
+ CHECK_ERR(fsi_write(&pib->target, FSI_CMD_REG, FSI_CMD_REG_WRITE | addr));
return 0;
}
-int fsi2pib_target_init(struct target *target, const char *name, uint64_t base, struct target *next)
-{
- target_init(target, name, base, fsi2pib_getscom, fsi2pib_putscom, NULL, next);
-
- return 0;
-}
-
-static uint64_t opb_poll(struct target *target, uint64_t *read_data)
+struct pib fsi_pib = {
+ .target = {
+ .name = "POWER FSI2PIB",
+ .compatible = "ibm,fsi-pib",
+ .class = "pib",
+ },
+ .read = fsi2pib_getscom,
+ .write = fsi2pib_putscom,
+};
+DECLARE_HW_UNIT(fsi_pib);
+
+static uint64_t opb_poll(struct opb *opb, uint32_t *read_data)
{
unsigned long retries = MFSI_OPB_MAX_TRIES;
uint64_t sval;
@@ -123,7 +130,7 @@ static uint64_t opb_poll(struct target *target, uint64_t *read_data)
/* We try again every 10us for a bit more than 1ms */
for (;;) {
/* Read OPB status register */
- rc = read_next_target(target, PIB2OPB_REG_STAT, &sval);
+ rc = pib_read(&opb->target, PIB2OPB_REG_STAT, &sval);
if (rc) {
/* Do something here ? */
PR_ERROR("XSCOM error %lld read OPB STAT\n", rc);
@@ -151,8 +158,8 @@ static uint64_t opb_poll(struct target *target, uint64_t *read_data)
* probing the system.
*/
if (stat & OPB_STAT_ANY_ERR) {
- write_next_target(target, PIB2OPB_REG_RESET, PPC_BIT(0));
- write_next_target(target, PIB2OPB_REG_STAT, PPC_BIT(0));
+ pib_write(&opb->target, PIB2OPB_REG_RESET, PPC_BIT(0));
+ pib_write(&opb->target, PIB2OPB_REG_STAT, PPC_BIT(0));
PR_DEBUG("OPB Error. Status 0x%08x\n", stat);
rc = -1;
} else if (read_data) {
@@ -166,7 +173,7 @@ static uint64_t opb_poll(struct target *target, uint64_t *read_data)
return rc;
}
-static int opb_read(struct target *target, uint64_t addr, uint64_t *data)
+static int p8_opb_read(struct opb *opb, uint32_t addr, uint32_t *data)
{
uint64_t opb_cmd = OPB_CMD_READ | OPB_CMD_32BIT;
int64_t rc;
@@ -179,18 +186,17 @@ static int opb_read(struct target *target, uint64_t addr, uint64_t *data)
opb_cmd |= addr;
opb_cmd <<= 32;
- PR_DEBUG("MFSI_OPB_READ: Writing 0x%16llx to XSCOM %llx\n",
- opb_cmd, target->base);
+ PR_DEBUG("MFSI_OPB_READ: Writing 0x%16llx\n", opb_cmd);
- rc = write_next_target(target, PIB2OPB_REG_CMD, opb_cmd);
+ rc = pib_write(&opb->target, PIB2OPB_REG_CMD, opb_cmd);
if (rc) {
PR_ERROR("XSCOM error %lld writing OPB CMD\n", rc);
return OPB_ERR_XSCOM_ERR;
}
- return opb_poll(target, data);
+ return opb_poll(opb, data);
}
-static int opb_write(struct target *target, uint64_t addr, uint64_t data)
+static int p8_opb_write(struct opb *opb, uint32_t addr, uint32_t data)
{
uint64_t opb_cmd = OPB_CMD_WRITE | OPB_CMD_32BIT;
int64_t rc;
@@ -203,27 +209,26 @@ static int opb_write(struct target *target, uint64_t addr, uint64_t data)
opb_cmd <<= 32;
opb_cmd |= data;
- PR_DEBUG("MFSI_OPB_WRITE: Writing 0x%16llx to XSCOM %llx\n",
- opb_cmd, target->base);
+ PR_DEBUG("MFSI_OPB_WRITE: Writing 0x%16llx\n", opb_cmd);
- rc = write_next_target(target, PIB2OPB_REG_CMD, opb_cmd);
+ rc = pib_write(&opb->target, PIB2OPB_REG_CMD, opb_cmd);
if (rc) {
PR_ERROR("XSCOM error %lld writing OPB CMD\n", rc);
return OPB_ERR_XSCOM_ERR;
}
- return opb_poll(target, NULL);
+ return opb_poll(opb, NULL);
}
-int opb_target_init(struct target *target, const char *name, uint64_t base, struct target *next)
-{
- target_init(target, name, base, opb_read, opb_write, NULL, next);
-
- /* Clear any outstanding errors */
- write_next_target(target, PIB2OPB_REG_RESET, PPC_BIT(0));
- write_next_target(target, PIB2OPB_REG_STAT, PPC_BIT(0));
-
- return 0;
-}
+struct opb p8_opb = {
+ .target = {
+ .name = "POWER8 OPB",
+ .compatible = "ibm,power8-opb",
+ .class = "opb",
+ },
+ .read = p8_opb_read,
+ .write = p8_opb_write,
+};
+DECLARE_HW_UNIT(p8_opb);
enum chip_type get_chip_type(uint64_t chip_id)
{
@@ -239,36 +244,90 @@ enum chip_type get_chip_type(uint64_t chip_id)
}
}
-int mfsi_target_init(struct target *target, const char *name, uint64_t base, struct target *next)
+static int p8_hmfsi_read(struct fsi *fsi, uint32_t addr, uint32_t *data)
+{
+ return opb_read(&fsi->target, addr, data);
+}
+
+static int p8_hmfsi_write(struct fsi *fsi, uint32_t addr, uint32_t data)
{
- target_init(target, name, base, NULL, NULL, NULL, next);
+ return opb_write(&fsi->target, addr, data);
+}
+
+static int p8_hmfsi_probe(struct target *target)
+{
+ struct fsi *fsi = target_to_fsi(target);
+ uint32_t value;
+ int rc;
+
+ if ((rc = opb_read(&fsi->target, 0xc09, &value)))
+ return rc;
+
+ fsi->chip_type = get_chip_type(value);
+
+ PR_DEBUG("Found chip type %x\n", fsi->chip_type);
+ if (fsi->chip_type == CHIP_UNKNOWN)
+ return -1;
return 0;
}
-#define HMFSI_STRIDE 0x80000
-int hmfsi_target_probe(struct target *cfam, struct target *targets, int max_target_count)
+struct fsi p8_opb_hmfsi = {
+ .target = {
+ .name = "POWER8 OPB attached hMFSI",
+ .compatible = "ibm,power8-opb-hmfsi",
+ .class = "fsi",
+ .probe = p8_hmfsi_probe,
+ },
+ .read = p8_hmfsi_read,
+ .write = p8_hmfsi_write,
+};
+DECLARE_HW_UNIT(p8_opb_hmfsi);
+
+static int cfam_hmfsi_read(struct fsi *fsi, uint32_t addr, uint32_t *data)
{
- struct target *target = targets;
- uint64_t value;
- int target_count = 0, i;
-
- for (i = 0; i < 8 && i < max_target_count; i++) {
- mfsi_target_init(target, "MFSI Port", 0x80000 + i * HMFSI_STRIDE, cfam);
- if (read_target(target, 0xc09, &value)) {
- target_del(target);
- continue;
- }
+ struct target *parent_fsi = fsi->target.dn->parent->target;
- target->chip_type = get_chip_type(value);
- if (target->chip_type == CHIP_UNKNOWN) {
- target_del(target);
- continue;
- }
+ addr += dt_get_address(fsi->target.dn, 0, NULL);
- target++;
- target_count++;
- }
+ return fsi_read(parent_fsi, addr, data);
+}
+
+static int cfam_hmfsi_write(struct fsi *fsi, uint32_t addr, uint32_t data)
+{
+ struct target *parent_fsi = fsi->target.dn->parent->target;
+
+ addr += dt_get_address(fsi->target.dn, 0, NULL);
- return target_count;
+ return fsi_write(parent_fsi, addr, data);
}
+
+static int cfam_hmfsi_probe(struct target *target)
+{
+ struct fsi *fsi = target_to_fsi(target);
+ uint32_t value;
+ int rc;
+
+ if ((rc = fsi_read(&fsi->target, 0xc09, &value)))
+ return rc;
+
+ fsi->chip_type = get_chip_type(value);
+
+ PR_DEBUG("Found chip type %x\n", fsi->chip_type);
+ if (fsi->chip_type == CHIP_UNKNOWN)
+ return -1;
+
+ return 0;
+}
+
+struct fsi cfam_hmfsi = {
+ .target = {
+ .name = "CFAM hMFSI Port",
+ .compatible = "ibm,fsi-hmfsi",
+ .class = "fsi",
+ .probe = cfam_hmfsi_probe,
+ },
+ .read = cfam_hmfsi_read,
+ .write = cfam_hmfsi_write,
+};
+DECLARE_HW_UNIT(cfam_hmfsi);
diff --git a/libpdbg/chip.c b/libpdbg/chip.c
index 8abc131..cde11a3 100644
--- a/libpdbg/chip.c
+++ b/libpdbg/chip.c
@@ -20,6 +20,7 @@
#include <ccan/array_size/array_size.h>
#include <unistd.h>
+#include "target.h"
#include "operations.h"
#include "bitutils.h"
@@ -68,7 +69,7 @@
#define SPECIAL_WKUP_DONE PPC_BIT(31)
/* How long (in us) to wait for a special wakeup to complete */
-#define SPECIAL_WKUP_TIMEOUT 1000
+#define SPECIAL_WKUP_TIMEOUT 10
/* Opcodes */
#define MTNIA_OPCODE 0x00000002UL
@@ -135,21 +136,22 @@ static uint64_t ld(uint64_t rt, uint64_t ds, uint64_t ra)
return LD_OPCODE | (rt << 21) | (ra << 16) | (ds << 2);
}
-static int assert_special_wakeup(struct target *chip)
+static int assert_special_wakeup(struct chiplet *chip)
{
int i = 0;
uint64_t gp0;
/* Assert special wakeup to prevent low power states */
- CHECK_ERR(write_target(chip, PMSPCWKUPFSP_REG, FSP_SPECIAL_WAKEUP));
+ CHECK_ERR(pib_write(&chip->target, PMSPCWKUPFSP_REG, FSP_SPECIAL_WAKEUP));
/* Poll for completion */
do {
usleep(1);
- CHECK_ERR(read_target(chip, EX_PM_GP0_REG, &gp0));
+ CHECK_ERR(pib_read(&chip->target, EX_PM_GP0_REG, &gp0));
if (i++ > SPECIAL_WKUP_TIMEOUT) {
- PR_ERROR("Timeout waiting for special wakeup\n");
+ PR_ERROR("Timeout waiting for special wakeup on %s@0x%08lx\n", chip->target.name,
+ dt_get_address(chip->target.dn, 0, NULL));
return -1;
}
} while (!(gp0 & SPECIAL_WKUP_DONE));
@@ -157,41 +159,41 @@ static int assert_special_wakeup(struct target *chip)
return 0;
}
-static int deassert_special_wakeup(struct target *chip)
+static int deassert_special_wakeup(struct chiplet *chip)
{
/* Assert special wakeup to prevent low power states */
- CHECK_ERR(write_target(chip, PMSPCWKUPFSP_REG, 0));
+ CHECK_ERR(pib_write(&chip->target, PMSPCWKUPFSP_REG, 0));
return 0;
}
-uint64_t chiplet_thread_status(struct target *thread)
+uint64_t thread_status(struct thread *thread)
{
return thread->status;
}
-static uint64_t get_thread_status(struct target *thread)
+static uint64_t get_thread_status(struct thread *thread)
{
uint64_t val, mode_reg, thread_status = thread->status;
/* Need to activete debug mode to get complete status */
- CHECK_ERR(read_target(thread, RAS_MODE_REG, &mode_reg));
+ CHECK_ERR(pib_read(&thread->target, RAS_MODE_REG, &mode_reg));
mode_reg |= MR_THREAD_IN_DEBUG;
- CHECK_ERR(write_target(thread, RAS_MODE_REG, mode_reg));
+ CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, mode_reg));
/* Read status */
- CHECK_ERR(read_target(thread, RAS_STATUS_REG, &val));
+ CHECK_ERR(pib_read(&thread->target, RAS_STATUS_REG, &val));
thread_status = SETFIELD(THREAD_STATUS_ACTIVE, thread_status, !!(val & RAS_STATUS_THREAD_ACTIVE));
thread_status = SETFIELD(THREAD_STATUS_QUIESCE, thread_status, !!(val & RAS_STATUS_TS_QUIESCE));
/* Read POW status */
- CHECK_ERR(read_target(thread, POW_STATUS_REG, &val));
+ CHECK_ERR(pib_read(&thread->target, POW_STATUS_REG, &val));
thread_status = SETFIELD(THREAD_STATUS_STATE, thread_status, GETFIELD(PMC_POW_STATE, val));
/* Clear debug mode */
mode_reg &= ~MR_THREAD_IN_DEBUG;
- CHECK_ERR(write_target(thread, RAS_MODE_REG, mode_reg));
+ CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, mode_reg));
return thread_status;
}
@@ -199,48 +201,48 @@ static uint64_t get_thread_status(struct target *thread)
/*
* Single step the thread count instructions.
*/
-int ram_step_thread(struct target *thread, int count)
+int ram_step_thread(struct thread *thread, int count)
{
int i;
uint64_t ras_mode, ras_status;
/* Activate single-step mode */
- CHECK_ERR(read_target(thread, RAS_MODE_REG, &ras_mode));
+ CHECK_ERR(pib_read(&thread->target, RAS_MODE_REG, &ras_mode));
ras_mode |= MR_DO_SINGLE_MODE;
- CHECK_ERR(write_target(thread, RAS_MODE_REG, ras_mode));
+ CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, ras_mode));
/* Step the core */
for (i = 0; i < count; i++) {
- CHECK_ERR(write_target(thread, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_STEP));
+ CHECK_ERR(pib_write(&thread->target, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_STEP));
/* Wait for step to complete */
do {
- CHECK_ERR(read_target(thread, RAS_STATUS_REG, &ras_status));
+ CHECK_ERR(pib_read(&thread->target, RAS_STATUS_REG, &ras_status));
} while (!(ras_status & RAS_STATUS_INST_COMPLETE));
}
/* Deactivate single-step mode */
ras_mode &= ~MR_DO_SINGLE_MODE;
- CHECK_ERR(write_target(thread, RAS_MODE_REG, ras_mode));
+ CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, ras_mode));
return 0;
}
-int ram_stop_thread(struct target *thread)
+int ram_stop_thread(struct thread *thread)
{
- int i = 0, thread_id = (thread->base >> 4) & 0x7;
+ int i = 0;
uint64_t val;
- struct target *chip = thread->next;
+ struct chiplet *chip = target_to_chiplet(thread->target.dn->parent->target);
do {
/* Quiese active thread */
- CHECK_ERR(write_target(thread, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_STOP));
+ CHECK_ERR(pib_write(&thread->target, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_STOP));
/* Wait for thread to quiese */
- CHECK_ERR(read_target(chip, RAS_STATUS_REG, &val));
+ CHECK_ERR(pib_read(&chip->target, RAS_STATUS_REG, &val));
if (i++ > RAS_STATUS_TIMEOUT) {
PR_ERROR("Unable to quiesce thread %d (0x%016llx).\n",
- thread->index, val);
+ thread->id, val);
PR_ERROR("Continuing anyway.\n");
if (val & PPC_BIT(48)) {
PR_ERROR("Unable to continue\n");
@@ -258,27 +260,42 @@ int ram_stop_thread(struct target *thread)
/* Make the threads RAM thread active */
- CHECK_ERR(read_target(chip, THREAD_ACTIVE_REG, &val));
- val |= PPC_BIT(8) >> thread_id;
- CHECK_ERR(write_target(chip, THREAD_ACTIVE_REG, val));
+ CHECK_ERR(pib_read(&chip->target, THREAD_ACTIVE_REG, &val));
+ val |= PPC_BIT(8) >> thread->id;
+ CHECK_ERR(pib_write(&chip->target, THREAD_ACTIVE_REG, val));
return 0;
}
-int ram_start_thread(struct target *thread)
+int ram_start_thread(struct thread *thread)
{
uint64_t val;
- struct target *chip = thread->next;
- int thread_id = (thread->base >> 4) & 0x7;
+ struct chiplet *chip = target_to_chiplet(thread->target.dn->parent->target);
/* Activate thread */
- CHECK_ERR(write_target(thread, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_START));
+ CHECK_ERR(pib_write(&thread->target, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_START));
/* Restore thread active */
- CHECK_ERR(read_target(chip, THREAD_ACTIVE_REG, &val));
- val &= ~(PPC_BIT(8) >> thread_id);
- val |= PPC_BIT(thread_id);
- CHECK_ERR(write_target(chip, THREAD_ACTIVE_REG, val));
+ CHECK_ERR(pib_read(&chip->target, THREAD_ACTIVE_REG, &val));
+ val &= ~(PPC_BIT(8) >> thread->id);
+ val |= PPC_BIT(thread->id);
+ CHECK_ERR(pib_write(&chip->target, THREAD_ACTIVE_REG, val));
+
+ return 0;
+}
+
+/* We can only ram a thread if all the threads on the core/chip are
+ * quiesced */
+int ram_status(struct chiplet *chip)
+{
+ struct dt_node *dn;
+
+ dt_for_each_compatible(chip->target.dn, dn, "ibm,power8-thread") {
+ struct thread *thread;
+ thread = target_to_thread(dn->target);
+ if (!(get_thread_status(thread) & THREAD_STATUS_QUIESCE))
+ return -1;
+ }
return 0;
}
@@ -291,26 +308,32 @@ int ram_start_thread(struct target *thread)
* data. Note that only register r0 is saved and restored so opcodes
* must not touch other registers.
*/
-static int ram_instructions(struct target *thread, uint64_t *opcodes,
+static int ram_instructions(struct thread *thread, uint64_t *opcodes,
uint64_t *results, int len, unsigned int lpar)
{
uint64_t ram_mode, val, opcode, r0 = 0, r1 = 0;
- struct target *chiplet = thread->next;
- int thread_id = (thread->base >> 4) & 0x7;
+ struct chiplet *chip = target_to_chiplet(thread->target.dn->parent->target);
int i;
int exception = 0;
+ /* Check to see if the parent chip is in a state that can RAM instructions */
+ if (ram_status(chip))
+ return 1;
+
+ if (!(thread_status(thread) & THREAD_STATUS_ACTIVE))
+ return 2;
+
/* Activate RAM mode */
- CHECK_ERR(read_target(chiplet, RAM_MODE_REG, &ram_mode));
+ CHECK_ERR(pib_read(&chip->target, RAM_MODE_REG, &ram_mode));
ram_mode |= RAM_MODE_ENABLE;
- CHECK_ERR(write_target(chiplet, RAM_MODE_REG, ram_mode));
+ CHECK_ERR(pib_write(&chip->target, RAM_MODE_REG, ram_mode));
/* Setup SPRC to use SPRD */
val = SPR_MODE_SPRC_WR_EN;
val = SETFIELD(SPR_MODE_SPRC_SEL, val, 1 << (3 - lpar));
- val = SETFIELD(SPR_MODE_SPRC_T_SEL, val, 1 << (7 - thread_id));
- CHECK_ERR(write_target(chiplet, SPR_MODE_REG, val));
- CHECK_ERR(write_target(chiplet, L0_SCOM_SPRC_REG, SCOM_SPRC_SCRATCH_SPR));
+ val = SETFIELD(SPR_MODE_SPRC_T_SEL, val, 1 << (7 - thread->id));
+ CHECK_ERR(pib_write(&chip->target, SPR_MODE_REG, val));
+ CHECK_ERR(pib_write(&chip->target, L0_SCOM_SPRC_REG, SCOM_SPRC_SCRATCH_SPR));
/* RAM instructions */
for (i = -2; i < len + 2; i++) {
@@ -320,26 +343,26 @@ static int ram_instructions(struct target *thread, uint64_t *opcodes,
/* Save r0 (assumes opcodes don't touch other registers) */
opcode = mtspr(277, 0);
else if (i < len) {
- CHECK_ERR(write_target(chiplet, SCR0_REG, results[i]));
+ CHECK_ERR(pib_write(&chip->target, SCR0_REG, results[i]));
opcode = opcodes[i];
} else if (i == len) {
/* Restore r0 */
- CHECK_ERR(write_target(chiplet, SCR0_REG, r0));
+ CHECK_ERR(pib_write(&chip->target, SCR0_REG, r0));
opcode = mfspr(0, 277);
} else if (i == len + 1) {
/* Restore r1 */
- CHECK_ERR(write_target(chiplet, SCR0_REG, r1));
+ CHECK_ERR(pib_write(&chip->target, SCR0_REG, r1));
opcode = mfspr(0, 277);
}
/* ram instruction */
- val = SETFIELD(RAM_THREAD_SELECT, 0ULL, thread_id);
+ val = SETFIELD(RAM_THREAD_SELECT, 0ULL, thread->id);
val = SETFIELD(RAM_INSTR, val, opcode);
- CHECK_ERR(write_target(chiplet, RAM_CTRL_REG, val));
+ CHECK_ERR(pib_write(&chip->target, RAM_CTRL_REG, val));
/* wait for completion */
do {
- CHECK_ERR(read_target(chiplet, RAM_STATUS_REG, &val));
+ CHECK_ERR(pib_read(&chip->target, RAM_STATUS_REG, &val));
} while (!((val & PPC_BIT(1)) || ((val & PPC_BIT(2)) && (val & PPC_BIT(3)))));
if (!(val & PPC_BIT(1))) {
@@ -353,7 +376,7 @@ static int ram_instructions(struct target *thread, uint64_t *opcodes,
}
/* Save the results */
- CHECK_ERR(read_target(chiplet, SCR0_REG, &val));
+ CHECK_ERR(pib_read(&chip->target, SCR0_REG, &val));
if (i == -2)
r1 = val;
else if (i == -1)
@@ -364,7 +387,7 @@ static int ram_instructions(struct target *thread, uint64_t *opcodes,
/* Disable RAM mode */
ram_mode &= ~RAM_MODE_ENABLE;
- CHECK_ERR(write_target(chiplet, RAM_MODE_REG, ram_mode));
+ CHECK_ERR(pib_write(&chip->target, RAM_MODE_REG, ram_mode));
return exception;
}
@@ -372,7 +395,7 @@ static int ram_instructions(struct target *thread, uint64_t *opcodes,
/*
* Get gpr value. Chip must be stopped.
*/
-int ram_getgpr(struct target *thread, int gpr, uint64_t *value)
+int ram_getgpr(struct thread *thread, int gpr, uint64_t *value)
{
uint64_t opcodes[] = {mtspr(277, gpr)};
uint64_t results[] = {0};
@@ -382,7 +405,7 @@ int ram_getgpr(struct target *thread, int gpr, uint64_t *value)
return 0;
}
-int ram_putgpr(struct target *thread, int gpr, uint64_t value)
+int ram_putgpr(struct thread *thread, int gpr, uint64_t value)
{
uint64_t opcodes[] = {mfspr(gpr, 277)};
uint64_t results[] = {value};
@@ -392,7 +415,7 @@ int ram_putgpr(struct target *thread, int gpr, uint64_t value)
return 0;
}
-int ram_getnia(struct target *thread, uint64_t *value)
+int ram_getnia(struct thread *thread, uint64_t *value)
{
uint64_t opcodes[] = {mfnia(0), mtspr(277, 0)};
uint64_t results[] = {0, 0};
@@ -402,7 +425,7 @@ int ram_getnia(struct target *thread, uint64_t *value)
return 0;
}
-int ram_putnia(struct target *thread, uint64_t value)
+int ram_putnia(struct thread *thread, uint64_t value)
{
uint64_t opcodes[] = {mfspr(0, 277), mtnia(0)};
uint64_t results[] = {value, 0};
@@ -411,7 +434,7 @@ int ram_putnia(struct target *thread, uint64_t value)
return 0;
}
-int ram_getspr(struct target *thread, int spr, uint64_t *value)
+int ram_getspr(struct thread *thread, int spr, uint64_t *value)
{
uint64_t opcodes[] = {mfspr(0, spr), mtspr(277, 0)};
uint64_t results[] = {0, 0};
@@ -421,7 +444,7 @@ int ram_getspr(struct target *thread, int spr, uint64_t *value)
return 0;
}
-int ram_putspr(struct target *thread, int spr, uint64_t value)
+int ram_putspr(struct thread *thread, int spr, uint64_t value)
{
uint64_t opcodes[] = {mfspr(0, 277), mtspr(spr, 0)};
uint64_t results[] = {value, 0};
@@ -430,7 +453,7 @@ int ram_putspr(struct target *thread, int spr, uint64_t value)
return 0;
}
-int ram_getmsr(struct target *thread, uint64_t *value)
+int ram_getmsr(struct thread *thread, uint64_t *value)
{
uint64_t opcodes[] = {mfmsr(0), mtspr(277, 0)};
uint64_t results[] = {0, 0};
@@ -440,7 +463,7 @@ int ram_getmsr(struct target *thread, uint64_t *value)
return 0;
}
-int ram_putmsr(struct target *thread, uint64_t value)
+int ram_putmsr(struct thread *thread, uint64_t value)
{
uint64_t opcodes[] = {mfspr(0, 277), mtmsr(0)};
uint64_t results[] = {value, 0};
@@ -449,7 +472,7 @@ int ram_putmsr(struct target *thread, uint64_t value)
return 0;
}
-int ram_getmem(struct target *thread, uint64_t addr, uint64_t *value)
+int ram_getmem(struct thread *thread, uint64_t addr, uint64_t *value)
{
uint64_t opcodes[] = {mfspr(0, 277), mfspr(1, 277), ld(0, 0, 1), mtspr(277, 0)};
uint64_t results[] = {0xdeaddeaddeaddead, addr, 0, 0};
@@ -459,81 +482,54 @@ int ram_getmem(struct target *thread, uint64_t addr, uint64_t *value)
return 0;
}
-int thread_target_init(struct target *thread, const char *name, uint64_t thread_id, struct target *next)
-{
- target_init(thread, name, 0x13000 | (thread_id << 4), NULL, NULL, NULL, next);
- thread->status = get_thread_status(thread);
-
- /* Threads always exist, although they may not be in a useful state for most operations */
- return 0;
-}
-
/*
* Initialise all viable threads for ramming on the given chiplet.
*/
-int thread_target_probe(struct target *chiplet, struct target *targets, int max_target_count)
+static int p8_thread_probe(struct target *target)
{
- struct target *thread = targets;
- int i, count = 0;
+ struct thread *thread = target_to_thread(target);
- for (i = 0; i < THREADS_PER_CORE && i < max_target_count; i++) {
- thread_target_init(thread, "Thread", i, chiplet);
- thread++;
- count++;
- }
+ thread->id = (dt_get_address(target->dn, 0, NULL) >> 4) & 0xf;
+ thread->status = get_thread_status(thread);
- return count;
+ return 0;
}
-int chiplet_target_init(struct target *target, const char *name, uint64_t chip_id, struct target *next)
+struct thread p8_thread = {
+ .target = {
+ .name = "POWER8 Thread",
+ .compatible = "ibm,power8-thread",
+ .class = "thread",
+ .probe = p8_thread_probe,
+ },
+};
+DECLARE_HW_UNIT(p8_thread);
+
+static int p8_chiplet_probe(struct target *target)
{
- uint64_t value, base;
-
- base = 0x10000000 | (chip_id << 24);
-
- target_init(target, name, base, NULL, NULL, NULL, next);
+ uint64_t value;
+ struct chiplet *chiplet = target_to_chiplet(target);
+ int i, count = 0, rc = 0;
/* Work out if this chip is actually present */
- if (read_target(target, SCOM_EX_GP3, &value)) {
+ if (pib_read(target, SCOM_EX_GP3, &value)) {
PR_DEBUG("Error reading chip GP3 register\n");
return -1;
}
- /* Return 0 is a chip is actually present. We still leave the
- target initialised even if it isn't present as a user may
- want to continue anyway. */
- return -!GETFIELD(PPC_BIT(0), value);
-}
-
-/* Initialises all possible chiplets on the given processor
- * target. *targets should point to pre-allocated memory with enough
- * free space for the maximum number of targets. Returns the number of
- * chips found. */
-int chiplet_target_probe(struct target *processor, struct target *targets, int max_target_count)
-{
- struct target *target = targets;
- int i, count = 0, rc = 0;
-
- /* P9 chiplets are not currently supported */
- if (processor->chip_type == CHIP_P9)
- return 0;
-
- for (i = 0; i <= 0xf && i < max_target_count; i++) {
- if (i == 0 || i == 7 || i == 8 || i == 0xf)
- /* 0, 7, 8 & 0xf are reserved */
- continue;
-
- if (!(rc = chiplet_target_init(target, "Chiplet", i, processor))) {
- assert_special_wakeup(target);
- target++;
- count++;
- } else
- target_del(target);
- }
-
- if (rc)
- /* The last target is invalid, zero it out */
- memset(target, 0, sizeof(*target));
+ if (!GETFIELD(PPC_BIT(0), value))
+ return -1;
- return count;
+ assert_special_wakeup(chiplet);
+ return 0;
}
+
+struct chiplet p8_chiplet = {
+ .target = {
+ .name = "POWER8 Chiplet",
+ .compatible = "ibm,power8-core",
+ .class = "chiplet",
+ .probe = p8_chiplet_probe,
+ },
+};
+DECLARE_HW_UNIT(p8_chiplet);
diff --git a/libpdbg/compiler.h b/libpdbg/compiler.h
new file mode 100644
index 0000000..35bf165
--- /dev/null
+++ b/libpdbg/compiler.h
@@ -0,0 +1,53 @@
+/* Copyright 2013-2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __COMPILER_H
+#define __COMPILER_H
+
+#ifndef __ASSEMBLY__
+
+#include <stddef.h>
+
+/* Macros for various compiler bits and pieces */
+#define __packed __attribute__((packed))
+#define __align(x) __attribute__((__aligned__(x)))
+#define __unused __attribute__((unused))
+#define __used __attribute__((used))
+#define __section(x) __attribute__((__section__(x)))
+#define __noreturn __attribute__((noreturn))
+/* not __const as this has a different meaning (const) */
+#define __attrconst __attribute__((const))
+#define __warn_unused_result __attribute__((warn_unused_result))
+
+#if 0 /* Provided by gcc stddef.h */
+#define offsetof(type,m) __builtin_offsetof(type,m)
+#endif
+
+#define __nomcount __attribute__((no_instrument_function))
+
+/* Compiler barrier */
+static inline void barrier(void)
+{
+ asm volatile("" : : : "memory");
+}
+
+#endif /* __ASSEMBLY__ */
+
+/* Stringification macro */
+#define __tostr(x) #x
+#define tostr(x) __tostr(x)
+
+#endif /* __COMPILER_H */
diff --git a/libpdbg/device.c b/libpdbg/device.c
new file mode 100644
index 0000000..725d656
--- /dev/null
+++ b/libpdbg/device.c
@@ -0,0 +1,943 @@
+/* Copyright 2013-2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "device.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <libfdt/libfdt.h>
+#include <libfdt/libfdt_internal.h>
+#include <ccan/str/str.h>
+#include <ccan/endian/endian.h>
+
+#undef PR_DEBUG
+#define PR_DEBUG(...)
+
+#define zalloc(size) calloc(1, size)
+#define prerror printf
+#define is_rodata(p) false
+
+/* Used to give unique handles. */
+u32 last_phandle = 0;
+
+struct dt_node *dt_root;
+struct dt_node *dt_chosen;
+
+static const char *take_name(const char *name)
+{
+ if (!is_rodata(name) && !(name = strdup(name))) {
+ prerror("Failed to allocate copy of name");
+ abort();
+ }
+ return name;
+}
+
+static void free_name(const char *name)
+{
+ if (!is_rodata(name))
+ free((char *)name);
+}
+
+static struct dt_node *new_node(const char *name)
+{
+ struct dt_node *node = malloc(sizeof *node);
+ if (!node) {
+ prerror("Failed to allocate node\n");
+ abort();
+ }
+
+ node->name = take_name(name);
+ node->parent = NULL;
+ list_head_init(&node->properties);
+ list_head_init(&node->children);
+ /* FIXME: locking? */
+ node->phandle = ++last_phandle;
+ return node;
+}
+
+struct dt_node *dt_new_root(const char *name)
+{
+ return new_node(name);
+}
+
+static const char *get_unitname(const struct dt_node *node)
+{
+ const char *c = strchr(node->name, '@');
+
+ if (!c)
+ return NULL;
+
+ return c + 1;
+}
+
+int dt_cmp_subnodes(const struct dt_node *a, const struct dt_node *b)
+{
+ const char *a_unit = get_unitname(a);
+ const char *b_unit = get_unitname(b);
+
+ ptrdiff_t basenamelen = a_unit - a->name;
+
+ /* sort hex unit addresses by number */
+ if (a_unit && b_unit && !strncmp(a->name, b->name, basenamelen)) {
+ unsigned long long a_num, b_num;
+ char *a_end, *b_end;
+
+ a_num = strtoul(a_unit, &a_end, 16);
+ b_num = strtoul(b_unit, &b_end, 16);
+
+ /* only compare if the unit addr parsed correctly */
+ if (*a_end == 0 && *b_end == 0)
+ return (a_num > b_num) - (a_num < b_num);
+ }
+
+ return strcmp(a->name, b->name);
+}
+
+bool dt_attach_root(struct dt_node *parent, struct dt_node *root)
+{
+ struct dt_node *node;
+
+ assert(!root->parent);
+
+ if (list_empty(&parent->children)) {
+ list_add(&parent->children, &root->list);
+ root->parent = parent;
+
+ return true;
+ }
+
+ dt_for_each_child(parent, node) {
+ int cmp = dt_cmp_subnodes(node, root);
+
+ /* Look for duplicates */
+ if (cmp == 0) {
+ prerror("DT: %s failed, duplicate %s\n",
+ __func__, root->name);
+ return false;
+ }
+
+ /* insert before the first node that's larger
+ * the the node we're inserting */
+ if (cmp > 0)
+ break;
+ }
+
+ list_add_before(&parent->children, &root->list, &node->list);
+ root->parent = parent;
+
+ return true;
+}
+
+static inline void dt_destroy(struct dt_node *dn)
+{
+ if (!dn)
+ return;
+
+ free_name(dn->name);
+ free(dn);
+}
+
+struct dt_node *dt_new(struct dt_node *parent, const char *name)
+{
+ struct dt_node *new;
+ assert(parent);
+
+ new = new_node(name);
+ if (!dt_attach_root(parent, new)) {
+ dt_destroy(new);
+ return NULL;
+ }
+ return new;
+}
+
+struct dt_node *dt_new_addr(struct dt_node *parent, const char *name,
+ uint64_t addr)
+{
+ char *lname;
+ struct dt_node *new;
+ size_t len;
+
+ assert(parent);
+ len = strlen(name) + STR_MAX_CHARS(addr) + 2;
+ lname = malloc(len);
+ if (!lname)
+ return NULL;
+ snprintf(lname, len, "%s@%llx", name, (long long)addr);
+ new = new_node(lname);
+ free(lname);
+ if (!dt_attach_root(parent, new)) {
+ dt_destroy(new);
+ return NULL;
+ }
+ return new;
+}
+
+struct dt_node *dt_new_2addr(struct dt_node *parent, const char *name,
+ uint64_t addr0, uint64_t addr1)
+{
+ char *lname;
+ struct dt_node *new;
+ size_t len;
+ assert(parent);
+
+ len = strlen(name) + 2*STR_MAX_CHARS(addr0) + 3;
+ lname = malloc(len);
+ if (!lname)
+ return NULL;
+ snprintf(lname, len, "%s@%llx,%llx",
+ name, (long long)addr0, (long long)addr1);
+ new = new_node(lname);
+ free(lname);
+ if (!dt_attach_root(parent, new)) {
+ dt_destroy(new);
+ return NULL;
+ }
+ return new;
+}
+
+static struct dt_node *__dt_copy(struct dt_node *node, struct dt_node *parent,
+ bool root)
+{
+ struct dt_property *prop, *new_prop;
+ struct dt_node *new_node, *child;
+
+ new_node = dt_new(parent, node->name);
+ if (!new_node)
+ return NULL;
+
+ list_for_each(&node->properties, prop, list) {
+ new_prop = dt_add_property(new_node, prop->name, prop->prop,
+ prop->len);
+ if (!new_prop)
+ goto fail;
+ }
+
+ list_for_each(&node->children, child, list) {
+ child = __dt_copy(child, new_node, false);
+ if (!child)
+ goto fail;
+ }
+
+ return new_node;
+
+fail:
+ /* dt_free will recurse for us, so only free when we unwind to the
+ * top-level failure */
+ if (root)
+ dt_free(new_node);
+ return NULL;
+}
+
+struct dt_node *dt_copy(struct dt_node *node, struct dt_node *parent)
+{
+ return __dt_copy(node, parent, true);
+}
+
+char *dt_get_path(const struct dt_node *node)
+{
+ unsigned int len = 0;
+ const struct dt_node *n;
+ char *path, *p;
+
+ /* Dealing with NULL is for test/debug purposes */
+ if (!node)
+ return strdup("<NULL>");
+
+ for (n = node; n; n = n->parent) {
+ len += strlen(n->name);
+ if (n->parent || n == node)
+ len++;
+ }
+ path = zalloc(len + 1);
+ assert(path);
+ p = path + len;
+ for (n = node; n; n = n->parent) {
+ len = strlen(n->name);
+ p -= len;
+ memcpy(p, n->name, len);
+ if (n->parent || n == node)
+ *(--p) = '/';
+ }
+ assert(p == path);
+
+ return p;
+}
+
+static const char *__dt_path_split(const char *p,
+ const char **namep, unsigned int *namel,
+ const char **addrp, unsigned int *addrl)
+{
+ const char *at, *sl;
+
+ *namel = *addrl = 0;
+
+ /* Skip initial '/' */
+ while (*p == '/')
+ p++;
+
+ /* Check empty path */
+ if (*p == 0)
+ return p;
+
+ at = strchr(p, '@');
+ sl = strchr(p, '/');
+ if (sl == NULL)
+ sl = p + strlen(p);
+ if (sl < at)
+ at = NULL;
+ if (at) {
+ *addrp = at + 1;
+ *addrl = sl - at - 1;
+ }
+ *namep = p;
+ *namel = at ? (at - p) : (sl - p);
+
+ return sl;
+}
+
+struct dt_node *dt_find_by_path(struct dt_node *root, const char *path)
+{
+ struct dt_node *n;
+ const char *pn, *pa, *p = path, *nn, *na;
+ unsigned int pnl, pal, nnl, nal;
+ bool match;
+
+ /* Walk path components */
+ while (*p) {
+ /* Extract next path component */
+ p = __dt_path_split(p, &pn, &pnl, &pa, &pal);
+ if (pnl == 0 && pal == 0)
+ break;
+
+ /* Compare with each child node */
+ match = false;
+ list_for_each(&root->children, n, list) {
+ match = true;
+ __dt_path_split(n->name, &nn, &nnl, &na, &nal);
+ if (pnl && (pnl != nnl || strncmp(pn, nn, pnl)))
+ match = false;
+ if (pal && (pal != nal || strncmp(pa, na, pal)))
+ match = false;
+ if (match) {
+ root = n;
+ break;
+ }
+ }
+
+ /* No child match */
+ if (!match)
+ return NULL;
+ }
+ return root;
+}
+
+struct dt_node *dt_find_by_name(struct dt_node *root, const char *name)
+{
+ struct dt_node *child, *match;
+
+ list_for_each(&root->children, child, list) {
+ if (!strcmp(child->name, name))
+ return child;
+
+ match = dt_find_by_name(child, name);
+ if (match)
+ return match;
+ }
+
+ return NULL;
+}
+
+struct dt_node *dt_find_by_phandle(struct dt_node *root, u32 phandle)
+{
+ struct dt_node *node;
+
+ dt_for_each_node(root, node)
+ if (node->phandle == phandle)
+ return node;
+ return NULL;
+}
+
+static struct dt_property *new_property(struct dt_node *node,
+ const char *name, size_t size)
+{
+ struct dt_property *p = malloc(sizeof(*p) + size);
+ char *path;
+
+ if (!p) {
+ path = dt_get_path(node);
+ prerror("Failed to allocate property \"%s\" for %s of %zu bytes\n",
+ name, path, size);
+ free(path);
+ abort();
+ }
+ if (dt_find_property(node, name)) {
+ path = dt_get_path(node);
+ prerror("Duplicate property \"%s\" in node %s\n",
+ name, path);
+ free(path);
+ abort();
+
+ }
+
+ p->name = take_name(name);
+ p->len = size;
+ list_add_tail(&node->properties, &p->list);
+ return p;
+}
+
+struct dt_property *dt_add_property(struct dt_node *node,
+ const char *name,
+ const void *val, size_t size)
+{
+ struct dt_property *p;
+
+ /*
+ * Filter out phandle properties, we re-generate them
+ * when flattening
+ */
+ if (strcmp(name, "linux,phandle") == 0 ||
+ strcmp(name, "phandle") == 0) {
+ assert(size == 4);
+ node->phandle = *(const u32 *)val;
+ if (node->phandle >= last_phandle)
+ last_phandle = node->phandle;
+ return NULL;
+ }
+
+ p = new_property(node, name, size);
+ if (size)
+ memcpy(p->prop, val, size);
+ return p;
+}
+
+void dt_resize_property(struct dt_property **prop, size_t len)
+{
+ size_t new_len = sizeof(**prop) + len;
+
+ *prop = realloc(*prop, new_len);
+
+ /* Fix up linked lists in case we moved. (note: not an empty list). */
+ (*prop)->list.next->prev = &(*prop)->list;
+ (*prop)->list.prev->next = &(*prop)->list;
+}
+
+struct dt_property *dt_add_property_string(struct dt_node *node,
+ const char *name,
+ const char *value)
+{
+ return dt_add_property(node, name, value, strlen(value)+1);
+}
+
+struct dt_property *dt_add_property_nstr(struct dt_node *node,
+ const char *name,
+ const char *value, unsigned int vlen)
+{
+ struct dt_property *p;
+ char *tmp = zalloc(vlen + 1);
+
+ if (!tmp)
+ return NULL;
+
+ strncpy(tmp, value, vlen);
+ p = dt_add_property(node, name, tmp, strlen(tmp)+1);
+ free(tmp);
+
+ return p;
+}
+
+struct dt_property *__dt_add_property_cells(struct dt_node *node,
+ const char *name,
+ int count, ...)
+{
+ struct dt_property *p;
+ u32 *val;
+ unsigned int i;
+ va_list args;
+
+ p = new_property(node, name, count * sizeof(u32));
+ val = (u32 *)p->prop;
+ va_start(args, count);
+ for (i = 0; i < count; i++)
+ val[i] = cpu_to_fdt32(va_arg(args, u32));
+ va_end(args);
+ return p;
+}
+
+struct dt_property *__dt_add_property_u64s(struct dt_node *node,
+ const char *name,
+ int count, ...)
+{
+ struct dt_property *p;
+ u64 *val;
+ unsigned int i;
+ va_list args;
+
+ p = new_property(node, name, count * sizeof(u64));
+ val = (u64 *)p->prop;
+ va_start(args, count);
+ for (i = 0; i < count; i++)
+ val[i] = cpu_to_fdt64(va_arg(args, u64));
+ va_end(args);
+ return p;
+}
+
+struct dt_property *__dt_add_property_strings(struct dt_node *node,
+ const char *name,
+ int count, ...)
+{
+ struct dt_property *p;
+ unsigned int i, size;
+ va_list args;
+ const char *sstr;
+ char *s;
+
+ va_start(args, count);
+ for (i = size = 0; i < count; i++) {
+ sstr = va_arg(args, const char *);
+ if (sstr)
+ size += strlen(sstr) + 1;
+ }
+ va_end(args);
+ if (!size)
+ size = 1;
+ p = new_property(node, name, size);
+ s = (char *)p->prop;
+ *s = 0;
+ va_start(args, count);
+ for (i = 0; i < count; i++) {
+ sstr = va_arg(args, const char *);
+ if (sstr) {
+ strcpy(s, sstr);
+ s = s + strlen(sstr) + 1;
+ }
+ }
+ va_end(args);
+ return p;
+}
+
+void dt_del_property(struct dt_node *node, struct dt_property *prop)
+{
+ list_del_from(&node->properties, &prop->list);
+ free_name(prop->name);
+ free(prop);
+}
+
+u32 dt_property_get_cell(const struct dt_property *prop, u32 index)
+{
+ assert(prop->len >= (index+1)*sizeof(u32));
+ /* Always aligned, so this works. */
+ return fdt32_to_cpu(((const u32 *)prop->prop)[index]);
+}
+
+/* First child of this node. */
+struct dt_node *dt_first(const struct dt_node *root)
+{
+ return list_top(&root->children, struct dt_node, list);
+}
+
+/* Return next node, or NULL. */
+struct dt_node *dt_next(const struct dt_node *root,
+ const struct dt_node *prev)
+{
+ /* Children? */
+ if (!list_empty(&prev->children))
+ return dt_first(prev);
+
+ do {
+ /* More siblings? */
+ if (prev->list.next != &prev->parent->children.n)
+ return list_entry(prev->list.next, struct dt_node,list);
+
+ /* No more siblings, move up to parent. */
+ prev = prev->parent;
+ } while (prev != root);
+
+ return NULL;
+}
+
+struct dt_property *__dt_find_property(struct dt_node *node, const char *name)
+{
+ struct dt_property *i;
+
+ list_for_each(&node->properties, i, list)
+ if (strcmp(i->name, name) == 0)
+ return i;
+ return NULL;
+}
+
+struct dt_property *dt_find_property(const struct dt_node *node,
+ const char *name)
+{
+ struct dt_property *i;
+
+ list_for_each(&node->properties, i, list)
+ if (strcmp(i->name, name) == 0)
+ return i;
+ return NULL;
+}
+
+void dt_check_del_prop(struct dt_node *node, const char *name)
+{
+ struct dt_property *p;
+
+ p = __dt_find_property(node, name);
+ if (p)
+ dt_del_property(node, p);
+}
+const struct dt_property *dt_require_property(const struct dt_node *node,
+ const char *name, int wanted_len)
+{
+ const struct dt_property *p = dt_find_property(node, name);
+
+ if (!p) {
+ const char *path = dt_get_path(node);
+
+ prerror("DT: Missing required property %s/%s\n",
+ path, name);
+ assert(false);
+ }
+ if (wanted_len >= 0 && p->len != wanted_len) {
+ const char *path = dt_get_path(node);
+
+ prerror("DT: Unexpected property length %s/%s\n",
+ path, name);
+ prerror("DT: Expected len: %d got len: %zu\n",
+ wanted_len, p->len);
+ assert(false);
+ }
+
+ return p;
+}
+
+bool dt_has_node_property(const struct dt_node *node,
+ const char *name, const char *val)
+{
+ const struct dt_property *p = dt_find_property(node, name);
+
+ if (!p)
+ return false;
+ if (!val)
+ return true;
+
+ return p->len == strlen(val) + 1 && memcmp(p->prop, val, p->len) == 0;
+}
+
+bool dt_prop_find_string(const struct dt_property *p, const char *s)
+{
+ const char *c, *end;
+
+ if (!p)
+ return false;
+ c = p->prop;
+ end = c + p->len;
+
+ while(c < end) {
+ if (!strcasecmp(s, c))
+ return true;
+ c += strlen(c) + 1;
+ }
+ return false;
+}
+
+bool dt_node_is_compatible(const struct dt_node *node, const char *compat)
+{
+ const struct dt_property *p = dt_find_property(node, "compatible");
+
+ return dt_prop_find_string(p, compat);
+}
+
+struct dt_node *dt_find_compatible_node(struct dt_node *root,
+ struct dt_node *prev,
+ const char *compat)
+{
+ struct dt_node *node;
+
+ node = prev ? dt_next(root, prev) : root;
+ for (; node; node = dt_next(root, node))
+ if (dt_node_is_compatible(node, compat))
+ return node;
+ return NULL;
+}
+
+u64 dt_prop_get_u64(const struct dt_node *node, const char *prop)
+{
+ const struct dt_property *p = dt_require_property(node, prop, 8);
+
+ return ((u64)dt_property_get_cell(p, 0) << 32)
+ | dt_property_get_cell(p, 1);
+}
+
+u64 dt_prop_get_u64_def(const struct dt_node *node, const char *prop, u64 def)
+{
+ const struct dt_property *p = dt_find_property(node, prop);
+
+ if (!p)
+ return def;
+
+ return ((u64)dt_property_get_cell(p, 0) << 32)
+ | dt_property_get_cell(p, 1);
+}
+
+u32 dt_prop_get_u32(const struct dt_node *node, const char *prop)
+{
+ const struct dt_property *p = dt_require_property(node, prop, 4);
+
+ return dt_property_get_cell(p, 0);
+}
+
+u32 dt_prop_get_u32_def(const struct dt_node *node, const char *prop, u32 def)
+{
+ const struct dt_property *p = dt_find_property(node, prop);
+
+ if (!p)
+ return def;
+
+ return dt_property_get_cell(p, 0);
+}
+
+u32 dt_prop_get_u32_index(const struct dt_node *node, const char *prop, u32 index)
+{
+ const struct dt_property *p = dt_require_property(node, prop, -1);
+
+ return dt_property_get_cell(p, index);
+}
+
+const void *dt_prop_get(const struct dt_node *node, const char *prop)
+{
+ const struct dt_property *p = dt_require_property(node, prop, -1);
+
+ return p->prop;
+}
+
+const void *dt_prop_get_def(const struct dt_node *node, const char *prop,
+ void *def)
+{
+ const struct dt_property *p = dt_find_property(node, prop);
+
+ return p ? p->prop : def;
+}
+
+const void *dt_prop_get_def_size(const struct dt_node *node, const char *prop,
+ void *def, size_t *len)
+{
+ const struct dt_property *p = dt_find_property(node, prop);
+ *len = 0;
+ if (p)
+ *len = p->len;
+
+ return p ? p->prop : def;
+}
+
+u32 dt_prop_get_cell(const struct dt_node *node, const char *prop, u32 cell)
+{
+ const struct dt_property *p = dt_require_property(node, prop, -1);
+
+ return dt_property_get_cell(p, cell);
+}
+
+u32 dt_prop_get_cell_def(const struct dt_node *node, const char *prop,
+ u32 cell, u32 def)
+{
+ const struct dt_property *p = dt_find_property(node, prop);
+
+ if (!p)
+ return def;
+
+ return dt_property_get_cell(p, cell);
+}
+
+void dt_free(struct dt_node *node)
+{
+ struct dt_node *child;
+ struct dt_property *p;
+
+ while ((child = list_top(&node->children, struct dt_node, list)))
+ dt_free(child);
+
+ while ((p = list_pop(&node->properties, struct dt_property, list))) {
+ free_name(p->name);
+ free(p);
+ }
+
+ if (node->parent)
+ list_del_from(&node->parent->children, &node->list);
+ dt_destroy(node);
+}
+
+int dt_expand_node(struct dt_node *node, const void *fdt, int fdt_node)
+{
+ const struct fdt_property *prop;
+ int offset, nextoffset, err;
+ struct dt_node *child;
+ const char *name;
+ uint32_t tag;
+
+ if (((err = fdt_check_header(fdt)) != 0)
+ || ((err = _fdt_check_node_offset(fdt, fdt_node)) < 0)) {
+ prerror("FDT: Error %d parsing node 0x%x\n", err, fdt_node);
+ return -1;
+ }
+
+ nextoffset = err;
+ do {
+ offset = nextoffset;
+
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ switch (tag) {
+ case FDT_PROP:
+ prop = _fdt_offset_ptr(fdt, offset);
+ name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
+ dt_add_property(node, name, prop->data,
+ fdt32_to_cpu(prop->len));
+ break;
+ case FDT_BEGIN_NODE:
+ name = fdt_get_name(fdt, offset, NULL);
+ child = dt_new_root(name);
+ assert(child);
+ nextoffset = dt_expand_node(child, fdt, offset);
+
+ /*
+ * This may fail in case of duplicate, keep it
+ * going for now, we may ultimately want to
+ * assert
+ */
+ (void)dt_attach_root(node, child);
+ break;
+ case FDT_END:
+ return -1;
+ }
+ } while (tag != FDT_END_NODE);
+
+ return nextoffset;
+}
+
+void dt_expand(const void *fdt)
+{
+ PR_DEBUG("FDT: Parsing fdt @%p\n", fdt);
+
+ if (dt_expand_node(dt_root, fdt, 0) < 0)
+ abort();
+}
+
+u64 dt_get_number(const void *pdata, unsigned int cells)
+{
+ const u32 *p = pdata;
+ u64 ret = 0;
+
+ while(cells--)
+ ret = (ret << 32) | be32_to_cpu(*(p++));
+ return ret;
+}
+
+u32 dt_n_address_cells(const struct dt_node *node)
+{
+ if (!node->parent)
+ return 0;
+ return dt_prop_get_u32_def(node->parent, "#address-cells", 2);
+}
+
+u32 dt_n_size_cells(const struct dt_node *node)
+{
+ if (!node->parent)
+ return 0;
+ return dt_prop_get_u32_def(node->parent, "#size-cells", 1);
+}
+
+u64 dt_get_address(const struct dt_node *node, unsigned int index,
+ u64 *out_size)
+{
+ const struct dt_property *p;
+ u32 na = dt_n_address_cells(node);
+ u32 ns = dt_n_size_cells(node);
+ u32 pos, n;
+
+ p = dt_require_property(node, "reg", -1);
+ n = (na + ns) * sizeof(u32);
+ pos = n * index;
+ assert((pos + n) <= p->len);
+ if (out_size)
+ *out_size = dt_get_number(p->prop + pos + na * sizeof(u32), ns);
+ return dt_get_number(p->prop + pos, na);
+}
+
+static u32 __dt_get_chip_id(const struct dt_node *node)
+{
+ const struct dt_property *prop;
+
+ for (; node; node = node->parent) {
+ prop = dt_find_property(node, "ibm,chip-id");
+ if (prop)
+ return dt_property_get_cell(prop, 0);
+ }
+ return 0xffffffff;
+}
+
+u32 dt_get_chip_id(const struct dt_node *node)
+{
+ u32 id = __dt_get_chip_id(node);
+ assert(id != 0xffffffff);
+ return id;
+}
+
+struct dt_node *dt_find_compatible_node_on_chip(struct dt_node *root,
+ struct dt_node *prev,
+ const char *compat,
+ uint32_t chip_id)
+{
+ struct dt_node *node;
+
+ node = prev ? dt_next(root, prev) : root;
+ for (; node; node = dt_next(root, node)) {
+ u32 cid = __dt_get_chip_id(node);
+ if (cid == chip_id &&
+ dt_node_is_compatible(node, compat))
+ return node;
+ }
+ return NULL;
+}
+
+unsigned int dt_count_addresses(const struct dt_node *node)
+{
+ const struct dt_property *p;
+ u32 na = dt_n_address_cells(node);
+ u32 ns = dt_n_size_cells(node);
+ u32 n;
+
+ p = dt_require_property(node, "reg", -1);
+ n = (na + ns) * sizeof(u32);
+
+ if (n == 0)
+ return 0;
+
+ return p->len / n;
+}
+
+u64 dt_translate_address(const struct dt_node *node, unsigned int index,
+ u64 *out_size)
+{
+ /* XXX TODO */
+ return dt_get_address(node, index, out_size);
+}
+
+bool dt_node_is_enabled(struct dt_node *node)
+{
+ const struct dt_property *p = dt_find_property(node, "status");
+
+ if (!p)
+ return true;
+
+ return p->len > 1 && p->prop[0] == 'o' && p->prop[1] == 'k';
+}
diff --git a/libpdbg/device.h b/libpdbg/device.h
new file mode 100644
index 0000000..3fcf5c3
--- /dev/null
+++ b/libpdbg/device.h
@@ -0,0 +1,251 @@
+/* Copyright 2013-2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __DEVICE_H
+#define __DEVICE_H
+#include <ccan/list/list.h>
+#include <ccan/short_types/short_types.h>
+#include "compiler.h"
+
+/* Any property or node with this prefix will not be passed to the kernel. */
+#define DT_PRIVATE "skiboot,"
+
+/*
+ * An in-memory representation of a node in the device tree.
+ *
+ * This is trivially flattened into an fdt.
+ *
+ * Note that the add_* routines will make a copy of the name if it's not
+ * a read-only string (ie. usually a string literal).
+ */
+struct dt_property {
+ struct list_node list;
+ const char *name;
+ size_t len;
+ char prop[/* len */];
+};
+
+struct dt_node {
+ const char *name;
+ struct list_node list;
+ struct list_head properties;
+ struct list_head children;
+ struct dt_node *parent;
+ u32 phandle;
+ struct target *target;
+};
+
+/* This is shared with device_tree.c .. make it static when
+ * the latter is gone (hopefully soon)
+ */
+extern u32 last_phandle;
+
+extern struct dt_node *dt_root;
+extern struct dt_node *dt_chosen;
+
+/* Create a root node: ie. a parentless one. */
+struct dt_node *dt_new_root(const char *name);
+
+/* Graft a root node into this tree. */
+bool dt_attach_root(struct dt_node *parent, struct dt_node *root);
+
+/* Add a child node. */
+struct dt_node *dt_new(struct dt_node *parent, const char *name);
+struct dt_node *dt_new_addr(struct dt_node *parent, const char *name,
+ uint64_t unit_addr);
+struct dt_node *dt_new_2addr(struct dt_node *parent, const char *name,
+ uint64_t unit_addr0, uint64_t unit_addr1);
+
+/* Copy node to new parent, including properties and subnodes */
+struct dt_node *dt_copy(struct dt_node *node, struct dt_node *parent);
+
+/* Add a property node, various forms. */
+struct dt_property *dt_add_property(struct dt_node *node,
+ const char *name,
+ const void *val, size_t size);
+struct dt_property *dt_add_property_string(struct dt_node *node,
+ const char *name,
+ const char *value);
+struct dt_property *dt_add_property_nstr(struct dt_node *node,
+ const char *name,
+ const char *value, unsigned int vlen);
+
+/* Given out enough GCC extensions, we will achieve enlightenment! */
+#define dt_add_property_strings(node, name, ...) \
+ __dt_add_property_strings((node), ((name)), \
+ sizeof((const char *[]) { __VA_ARGS__ })/sizeof(const char *), \
+ __VA_ARGS__)
+
+struct dt_property *__dt_add_property_strings(struct dt_node *node,
+ const char *name,
+ int count, ...);
+
+/* Given out enough GCC extensions, we will achieve enlightenment! */
+#define dt_add_property_cells(node, name, ...) \
+ __dt_add_property_cells((node), ((name)), \
+ sizeof((u32[]) { __VA_ARGS__ })/sizeof(u32), \
+ __VA_ARGS__)
+
+struct dt_property *__dt_add_property_cells(struct dt_node *node,
+ const char *name,
+ int count, ...);
+
+#define dt_add_property_u64s(node, name, ...) \
+ __dt_add_property_u64s((node), ((name)), \
+ sizeof((u64[]) { __VA_ARGS__ })/sizeof(u64), \
+ __VA_ARGS__)
+
+struct dt_property *__dt_add_property_u64s(struct dt_node *node,
+ const char *name,
+ int count, ...);
+
+static inline struct dt_property *dt_add_property_u64(struct dt_node *node,
+ const char *name, u64 val)
+{
+ return dt_add_property_cells(node, name, (u32)(val >> 32), (u32)val);
+}
+
+void dt_del_property(struct dt_node *node, struct dt_property *prop);
+
+void dt_check_del_prop(struct dt_node *node, const char *name);
+
+/* Warning: moves *prop! */
+void dt_resize_property(struct dt_property **prop, size_t len);
+
+u32 dt_property_get_cell(const struct dt_property *prop, u32 index);
+
+/* First child of this node. */
+struct dt_node *dt_first(const struct dt_node *root);
+
+/* Return next node, or NULL. */
+struct dt_node *dt_next(const struct dt_node *root, const struct dt_node *prev);
+
+/* Iterate nodes */
+#define dt_for_each_node(root, node) \
+ for (node = dt_first(root); node; node = dt_next(root, node))
+
+#define dt_for_each_child(parent, node) \
+ list_for_each(&parent->children, node, list)
+
+/* Find a string in a string list */
+bool dt_prop_find_string(const struct dt_property *p, const char *s);
+
+/* Check a compatible property */
+bool dt_node_is_compatible(const struct dt_node *node, const char *compat);
+
+/* Find a node based on compatible property */
+struct dt_node *dt_find_compatible_node(struct dt_node *root,
+ struct dt_node *prev,
+ const char *compat);
+
+#define dt_for_each_compatible(root, node, compat) \
+ for (node = NULL; \
+ (node = dt_find_compatible_node(root, node, compat)) != NULL;)
+
+struct dt_node *dt_find_compatible_node_on_chip(struct dt_node *root,
+ struct dt_node *prev,
+ const char *compat,
+ uint32_t chip_id);
+
+#define dt_for_each_compatible_on_chip(root, node, compat, chip_id) \
+ for (node = NULL; \
+ (node = dt_find_compatible_node_on_chip(root, node,\
+ compat, chip_id)) != NULL;)
+/* Check status property */
+bool dt_node_is_enabled(struct dt_node *node);
+
+/* Build the full path for a node. Return a new block of memory, caller
+ * shall free() it
+ */
+char *dt_get_path(const struct dt_node *node);
+
+/* Find a node by path */
+struct dt_node *dt_find_by_path(struct dt_node *root, const char *path);
+
+/* Find a child node by name */
+struct dt_node *dt_find_by_name(struct dt_node *root, const char *name);
+
+/* Find a node by phandle */
+struct dt_node *dt_find_by_phandle(struct dt_node *root, u32 phandle);
+
+/* Find a property by name. */
+struct dt_property *dt_find_property(const struct dt_node *node,\
+ const char *name);
+const struct dt_property *dt_require_property(const struct dt_node *node,
+ const char *name, int wanted_len);
+
+/* non-const variant */
+struct dt_property *__dt_find_property(struct dt_node *node, const char *name);
+
+/* Find a property by name, check if it's the same as val. */
+bool dt_has_node_property(const struct dt_node *node,
+ const char *name, const char *val);
+
+/* Free a node (and any children). */
+void dt_free(struct dt_node *node);
+
+/* Parse an initial fdt */
+void dt_expand(const void *fdt);
+int dt_expand_node(struct dt_node *node, const void *fdt, int fdt_node) __warn_unused_result;
+
+/* Simplified accessors */
+u64 dt_prop_get_u64(const struct dt_node *node, const char *prop);
+u64 dt_prop_get_u64_def(const struct dt_node *node, const char *prop, u64 def);
+u32 dt_prop_get_u32(const struct dt_node *node, const char *prop);
+u32 dt_prop_get_u32_def(const struct dt_node *node, const char *prop, u32 def);
+u32 dt_prop_get_u32_index(const struct dt_node *node, const char *prop, u32 index);
+const void *dt_prop_get(const struct dt_node *node, const char *prop);
+const void *dt_prop_get_def(const struct dt_node *node, const char *prop,
+ void *def);
+const void *dt_prop_get_def_size(const struct dt_node *node, const char *prop,
+ void *def, size_t *len);
+u32 dt_prop_get_cell(const struct dt_node *node, const char *prop, u32 cell);
+u32 dt_prop_get_cell_def(const struct dt_node *node, const char *prop, u32 cell, u32 def);
+
+/* Parsing helpers */
+u32 dt_n_address_cells(const struct dt_node *node);
+u32 dt_n_size_cells(const struct dt_node *node);
+u64 dt_get_number(const void *pdata, unsigned int cells);
+
+/* Find an ibm,chip-id property in this node; if not found, walk up the parent
+ * nodes. Returns -1 if no chip-id property exists. */
+u32 dt_get_chip_id(const struct dt_node *node);
+
+/* Address accessors ("reg" properties parsing). No translation,
+ * only support "simple" address forms (1 or 2 cells). Asserts
+ * if address doesn't exist
+ */
+u64 dt_get_address(const struct dt_node *node, unsigned int index,
+ u64 *out_size);
+
+/* Count "reg" property entries */
+unsigned int dt_count_addresses(const struct dt_node *node);
+
+/* Address translation
+ *
+ * WARNING: Current implementation is simplified and will not
+ * handle complex address formats with address space indicators
+ * nor will it handle "ranges" translations yet... (XX TODO)
+ */
+u64 dt_translate_address(const struct dt_node *node, unsigned int index,
+ u64 *out_size);
+
+/* compare function used to sort child nodes by name when added to the
+ * tree. This is mainly here for testing.
+ */
+int dt_cmp_subnodes(const struct dt_node *a, const struct dt_node *b);
+
+#endif /* __DEVICE_H */
diff --git a/libpdbg/fakepib.c b/libpdbg/fakepib.c
new file mode 100644
index 0000000..22ea3e7
--- /dev/null
+++ b/libpdbg/fakepib.c
@@ -0,0 +1,38 @@
+/* Copyright 2016 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "operations.h"
+
+static int fake_read(struct pib *pib, uint64_t addr, uint64_t *value)
+{
+ *value = 0xdeadbeef;
+ return 0;
+}
+
+static int fake_write(struct pib *pib, uint64_t addr, uint64_t value)
+{
+ return 0;
+}
+
+struct pib fake_pib = {
+ .target = {
+ .name = "Fake PIB",
+ .compatible = "ibm,fake-fsi",
+ .class = "fsi",
+ },
+ .read = fake_read,
+ .write = fake_write,
+};
+DECLARE_HW_UNIT(fake_pib);
diff --git a/libpdbg/i2c.c b/libpdbg/i2c.c
index a75056c..b2a913d 100644
--- a/libpdbg/i2c.c
+++ b/libpdbg/i2c.c
@@ -58,9 +58,9 @@ static int i2c_set_scom_addr(struct i2c_data *i2c_data, uint32_t addr)
return 0;
}
-static int i2c_getscom(struct target *target, uint64_t addr, uint64_t *value)
+static int i2c_getscom(struct pib *pib, uint64_t addr, uint64_t *value)
{
- struct i2c_data *i2c_data = target->priv;
+ struct i2c_data *i2c_data = pib->priv;
uint64_t data;
CHECK_ERR(i2c_set_scom_addr(i2c_data, addr));
@@ -75,9 +75,9 @@ static int i2c_getscom(struct target *target, uint64_t addr, uint64_t *value)
return 0;
}
-static int i2c_putscom(struct target *target, uint64_t addr, uint64_t value)
+static int i2c_putscom(struct pib *pib, uint64_t addr, uint64_t value)
{
- struct i2c_data *i2c_data = target->priv;
+ struct i2c_data *i2c_data = pib->priv;
uint8_t data[12];
/* Setup scom address */
@@ -106,21 +106,27 @@ static int i2c_putscom(struct target *target, uint64_t addr, uint64_t value)
return 0;
}
-static void i2c_destroy(struct target *target)
+static void i2c_destroy(struct pib *pib)
{
- struct i2c_data *i2c_data = target->priv;
+ struct i2c_data *i2c_data = pib->priv;
close(i2c_data->fd);
- free(target->priv);
+ free(i2c_data);
}
/*
* Initialise a i2c backend on the given bus at the given bus address.
*/
-int i2c_target_init(struct target *target, const char *name, struct target *next,
- const char *bus, int addr)
+int i2c_target_probe(struct target *target)
{
+ struct pib *pib = target_to_pib(target);
struct i2c_data *i2c_data;
+ const char *bus;
+ int addr;
+
+ bus = "/dev/i2c4";
+ addr = dt_get_address(pib->target.dn, 0, NULL);
+ assert(addr);
i2c_data = malloc(sizeof(*i2c_data));
if (!i2c_data)
@@ -136,10 +142,19 @@ int i2c_target_init(struct target *target, const char *name, struct target *next
if (i2c_set_addr(i2c_data->fd, addr) < 0)
return -1;
- target_init(target, name, addr, i2c_getscom, i2c_putscom, i2c_destroy,
- next);
- target->priv = i2c_data;
- target->chip_type = CHIP_P8;
+ pib->priv = i2c_data;
return 0;
}
+
+struct pib p8_i2c_pib = {
+ .target = {
+ .name = "POWER8 I2C Slave",
+ .compatible = "ibm,power8-i2c-slave",
+ .class = "pib",
+ .probe = i2c_target_probe,
+ },
+ .read = i2c_getscom,
+ .write = i2c_putscom,
+};
+DECLARE_HW_UNIT(p8_i2c_pib);
diff --git a/libpdbg/kernel.c b/libpdbg/kernel.c
index 559af08..6f16310 100644
--- a/libpdbg/kernel.c
+++ b/libpdbg/kernel.c
@@ -31,52 +31,10 @@
#define FSI_SCAN_PATH "/sys/devices/platform/fsi-master/scan"
#define FSI_CFAM_PATH "/sys/devices/platform/fsi-master/slave@00:00/raw"
-#define FSI_SCOM_PATH "/sys/devices/platform/fsi-master/slave@00:00/"
int fsi_fd;
-int scom_fd;
-static int kernel_putscom(struct target *target, uint64_t addr, uint64_t value)
-{
- int rc;
-
- rc = lseek(scom_fd, addr, SEEK_SET);
- if (rc < 0) {
- warn("Failed to seek %s", FSI_SCOM_PATH);
- return errno;
- }
-
- rc = write(scom_fd, &value, sizeof(value));
- if (rc < 0) {
- warn("Failed to write to 0x%016llx", addr);
- return errno;
- }
-
- return 0;
-
-}
-
-static int kernel_getscom(struct target *target, uint64_t addr, uint64_t *value)
-{
- int rc;
-
- rc = lseek(fsi_fd, addr, SEEK_SET);
- if (rc < 0) {
- warn("Failed to seek %s", FSI_SCOM_PATH);
- return errno;
- }
-
- rc = read(fsi_fd, value, sizeof(*value));
- if (rc < 0) {
- warn("Failed to read from 0x%016llx", addr);
- return errno;
- }
-
- return 0;
-
-}
-
-static int kernel_fsi_getcfam(struct target *target, uint64_t addr64, uint64_t *value)
+static int kernel_fsi_getcfam(struct fsi *fsi, uint32_t addr64, uint32_t *value)
{
int rc;
uint32_t addr = (addr64 & 0x7ffc00) | ((addr64 & 0x3ff) << 2);
@@ -100,7 +58,7 @@ static int kernel_fsi_getcfam(struct target *target, uint64_t addr64, uint64_t *
return 0;
}
-static int kernel_fsi_putcfam(struct target *target, uint64_t addr64, uint64_t data)
+static int kernel_fsi_putcfam(struct fsi *fsi, uint32_t addr64, uint32_t data)
{
int rc;
uint32_t addr = (addr64 & 0x7ffc00) | ((addr64 & 0x3ff) << 2);
@@ -141,9 +99,9 @@ static void kernel_fsi_scan_devices(void)
close(fd);
}
-int kernel_fsi_target_init(struct target *target, const char *name,
- struct target *next)
+int kernel_fsi_probe(struct target *target)
{
+ struct fsi *fsi = target_to_fsi(target);
uint64_t value;
if (!fsi_fd) {
@@ -153,36 +111,31 @@ int kernel_fsi_target_init(struct target *target, const char *name,
/* Open first raw device */
fsi_fd = open(FSI_CFAM_PATH, O_RDWR | O_SYNC);
if (fsi_fd >= 0)
- goto found;
+ return 0;
tries--;
/* Scan */
kernel_fsi_scan_devices();
sleep(1);
}
- if (fsi_fd < 0)
+ if (fsi_fd < 0) {
err(errno, "Unable to open %s", FSI_CFAM_PATH);
+ return -1;
+ }
}
-found:
- /* No cascaded devices after this one. */
- assert(next == NULL);
- target_init(target, name, 0, kernel_fsi_getcfam, kernel_fsi_putcfam,
- kernel_fsi_destroy, next);
-
- /* Read chip id */
- CHECK_ERR(read_target(target, 0xc09, &value));
- target->chip_type = get_chip_type(value);
- return 0;
+ return -1;
}
-int kernel_fsi2pib_target_init(struct target *target, const char *name,
- uint64_t base, struct target *next)
-{
- target_init(target, name, base, kernel_getscom, kernel_putscom, NULL,
- next);
-
- return 0;
-
-}
+struct fsi kernel_fsi = {
+ .target = {
+ .name = "Kernel based FSI master",
+ .compatible = "ibm,kernel-fsi",
+ .class = "fsi",
+ .probe = kernel_fsi_probe,
+ },
+ .read = kernel_fsi_getcfam,
+ .write = kernel_fsi_putcfam,
+};
+DECLARE_HW_UNIT(kernel_fsi);
diff --git a/libpdbg/operations.h b/libpdbg/operations.h
index 76a1ca8..4175af3 100644
--- a/libpdbg/operations.h
+++ b/libpdbg/operations.h
@@ -21,17 +21,10 @@
/* Error codes */
#define EFSI 1
-#define PR_DEBUG(x, args...) \
- //fprintf(stderr, x, ##args)
-#define PR_INFO(x, args...) \
- fprintf(stderr, x, ##args)
-#define PR_ERROR(x, args...) \
- fprintf(stderr, "%s: " x, __FUNCTION__, ##args)
-
#define CHECK_ERR(x) do { \
if (x) { \
PR_DEBUG("%s: %d\n", __FUNCTION__, __LINE__); \
- return -EFSI; \
+ return x; \
} \
} while(0)
@@ -52,37 +45,25 @@ int adu_putmem(struct target *target, uint64_t start_addr, uint8_t *input, uint6
#define THREAD_STATUS_SLEEP PPC_BITMASK(61, 62)
#define THREAD_STATUS_QUIESCE PPC_BIT(60)
-int ram_getgpr(struct target *thread, int gpr, uint64_t *value);
-int ram_putgpr(struct target *thread, int gpr, uint64_t value);
-int ram_getnia(struct target *thread, uint64_t *value);
-int ram_putnia(struct target *thread, uint64_t value);
-int ram_getspr(struct target *thread, int spr, uint64_t *value);
-int ram_putspr(struct target *thread, int spr, uint64_t value);
-int ram_getmsr(struct target *thread, uint64_t *value);
-int ram_putmsr(struct target *thread, uint64_t value);
-int ram_getmem(struct target *thread, uint64_t addr, uint64_t *value);
-uint64_t chiplet_thread_status(struct target *thread);
-int ram_stop_thread(struct target *thread);
-int ram_step_thread(struct target *thread, int count);
-int ram_start_thread(struct target *thread);
+int ram_getgpr(struct thread *thread, int gpr, uint64_t *value);
+int ram_putgpr(struct thread *thread, int gpr, uint64_t value);
+int ram_getnia(struct thread *thread, uint64_t *value);
+int ram_putnia(struct thread *thread, uint64_t value);
+int ram_getspr(struct thread *thread, int spr, uint64_t *value);
+int ram_putspr(struct thread *thread, int spr, uint64_t value);
+int ram_getmsr(struct thread *thread, uint64_t *value);
+int ram_putmsr(struct thread *thread, uint64_t value);
+int ram_getmem(struct thread *thread, uint64_t addr, uint64_t *value);
+uint64_t thread_status(struct thread *thread);
+int ram_stop_thread(struct thread *thread);
+int ram_step_thread(struct thread *thread, int count);
+int ram_start_thread(struct thread *thread);
+void fsi_destroy(struct target *target);
/* GDB server functionality */
int gdbserver_start(uint16_t port);
enum fsi_system_type {FSI_SYSTEM_P8, FSI_SYSTEM_P9W, FSI_SYSTEM_P9R, FSI_SYSTEM_P9Z};
-int fsi_target_init(struct target *target, const char *name, enum fsi_system_type tpye, struct target *next);
-int fsi_target_probe(struct target *targets, int max_target_count);
-int i2c_target_init(struct target *target, const char *name, struct target *next,
- const char *bus, int addr);
-int kernel_fsi_target_init(struct target *target, const char *name, struct target *next);
-int fsi2pib_target_init(struct target *target, const char *name, uint64_t base, struct target *next);
-int kernel_fsi2pib_target_init(struct target *target, const char *name, uint64_t base, struct target *next);
-int opb_target_init(struct target *target, const char *name, uint64_t base, struct target *next);
enum chip_type get_chip_type(uint64_t chip_id);
-int thread_target_init(struct target *thread, const char *name, uint64_t thread_id, struct target *next);
-int thread_target_probe(struct target *chiplet, struct target *targets, int max_target_count);
-int chiplet_target_init(struct target *target, const char *name, uint64_t chip_id, struct target *next);
-int chiplet_target_probe(struct target *processor, struct target *targets, int max_target_count);
-int hmfsi_target_probe(struct target *cfam, struct target *targets, int max_target_count);
#endif
diff --git a/libpdbg/target.c b/libpdbg/target.c
index a006f49..4235f4b 100644
--- a/libpdbg/target.c
+++ b/libpdbg/target.c
@@ -2,70 +2,266 @@
#include <stdint.h>
#include <assert.h>
#include <ccan/list/list.h>
+#include <libfdt/libfdt.h>
#include "target.h"
+#include "device.h"
-void target_init(struct target *target, const char *name, uint64_t base,
- target_read read, target_write write,
- target_destroy destroy, struct target *next)
+#undef PR_DEBUG
+#define PR_DEBUG(...)
+
+struct list_head empty_list = LIST_HEAD_INIT(empty_list);
+struct list_head target_classes = LIST_HEAD_INIT(target_classes);
+
+/* Work out the address to access based on the current target and
+ * final class name */
+static struct dt_node *get_class_target_addr(struct dt_node *dn, const char *name, uint64_t *addr)
+{
+ /* Check class */
+ while (strcmp(dn->target->class, name)) {
+ /* Keep walking the tree translating addresses */
+ *addr += dt_get_address(dn, 0, NULL);
+ dn = dn->parent;
+
+ /* The should always be a parent. If there isn't it
+ * means we traversed up the whole device tree and
+ * didn't find a parent matching the given class. */
+ assert(dn);
+ assert(dn->target);
+ }
+
+ return dn;
+}
+
+int pib_read(struct target *pib_dt, uint64_t addr, uint64_t *data)
+{
+ struct pib *pib;
+ struct dt_node *dn = pib_dt->dn;
+
+ dn = get_class_target_addr(dn, "pib", &addr);
+ pib_dt = dn->target;
+ pib = target_to_pib(pib_dt);
+ return pib->read(pib, addr, data);
+}
+
+int pib_write(struct target *pib_dt, uint64_t addr, uint64_t data)
+{
+ struct pib *pib;
+ struct dt_node *dn = pib_dt->dn;
+
+ dn = get_class_target_addr(dn, "pib", &addr);
+ pib_dt = dn->target;
+ pib = target_to_pib(pib_dt);
+ return pib->write(pib, addr, data);
+}
+
+int opb_read(struct target *opb_dt, uint32_t addr, uint32_t *data)
{
- target->name = name;
- target->index = -1;
- target->base = base;
- target->read = read;
- target->write = write;
- target->destroy = destroy;
- target->next = next;
- target->status = 0;
+ struct opb *opb;
+ struct dt_node *dn = opb_dt->dn;
+ uint64_t addr64 = addr;
- list_head_init(&target->children);
+ dn = get_class_target_addr(dn, "opb", &addr64);
+ opb_dt = dn->target;
+ opb = target_to_opb(opb_dt);
+ return opb->read(opb, addr64, data);
+}
+
+int opb_write(struct target *opb_dt, uint32_t addr, uint32_t data)
+{
+ struct opb *opb;
+ struct dt_node *dn = opb_dt->dn;
+ uint64_t addr64 = addr;
+
+ dn = get_class_target_addr(dn, "opb", &addr64);
+ opb_dt = dn->target;
+ opb = target_to_opb(opb_dt);
+
+ return opb->write(opb, addr64, data);
+}
+
+int fsi_read(struct target *fsi_dt, uint32_t addr, uint32_t *data)
+{
+ struct fsi *fsi;
+ struct dt_node *dn = fsi_dt->dn;
+ uint64_t addr64 = addr;
+
+ dn = get_class_target_addr(dn, "fsi", &addr64);
+ fsi_dt = dn->target;
+ fsi = target_to_fsi(fsi_dt);
+ return fsi->read(fsi, addr64, data);
+}
+
+int fsi_write(struct target *fsi_dt, uint32_t addr, uint32_t data)
+{
+ struct fsi *fsi;
+ struct dt_node *dn = fsi_dt->dn;
+ uint64_t addr64 = addr;
+
+ dn = get_class_target_addr(dn, "fsi", &addr64);
+ fsi_dt = dn->target;
+ fsi = target_to_fsi(fsi_dt);
+
+ return fsi->write(fsi, addr64, data);
+}
- if (next) {
- target->chip_type = target->next->chip_type;
- list_add_tail(&next->children, &target->link);
+int adu_getmem(struct target *adu_target, uint64_t addr, uint8_t *output, uint64_t size)
+{
+ struct adu *adu;
+
+ assert(!strcmp(adu_target->class, "adu"));
+ adu = target_to_adu(adu_target);
+ return adu->getmem(adu, addr, output, size);
+}
+
+int adu_putmem(struct target *adu_target, uint64_t start_addr, uint8_t *input, uint64_t size)
+{
+ struct adu *adu;
+
+ assert(!strcmp(adu_target->class, "adu"));
+ adu = target_to_adu(adu_target);
+ return adu->putmem(adu, start_addr, input, size);
+}
+
+/* Finds the given class. Returns NULL if not found. */
+struct target_class *find_target_class(const char *name)
+{
+ struct target_class *target_class;
+
+ list_for_each(&target_classes, target_class, class_head_link)
+ if (!strcmp(target_class->name, name))
+ return target_class;
+
+ return NULL;
+}
+
+/* Same as above but dies with an assert if the target class doesn't
+ * exist */
+struct target_class *require_target_class(const char *name)
+{
+ struct target_class *target_class;
+
+ target_class = find_target_class(name);
+ if (!target_class) {
+ PR_ERROR("Couldn't find class %s\n", name);
+ assert(0);
}
+ return target_class;
}
-void target_del(struct target *target)
+/* Returns the existing class or allocates space for a new one */
+static struct target_class *get_target_class(const char *name)
{
- if (target->destroy)
- target->destroy(target);
+ struct target_class *target_class;
- /* We don't recursively destroy things yet */
- assert(list_empty(&target->children));
- if (target->next)
- list_del(&target->link);
+ if ((target_class = find_target_class(name)))
+ return target_class;
+
+ /* Need to allocate a new class */
+ PR_DEBUG("Allocating %s target class\n", name);
+ target_class = calloc(1, sizeof(*target_class));
+ assert(target_class);
+ target_class->name = strdup(name);
+ list_head_init(&target_class->targets);
+ list_add(&target_classes, &target_class->class_head_link);
+ return target_class;
}
-int read_target(struct target *target, uint64_t addr, uint64_t *value)
+extern struct hw_unit_info *__start_hw_units;
+extern struct hw_init_info *__stop_hw_units;
+struct hw_unit_info *find_compatible_target(const char *compat)
{
-// printf("Target %s read 0x%0llx\n", target->name, addr);
+ struct hw_unit_info **p;
+ struct target *target, *tmp;
+
+ for (p = &__start_hw_units; p < &__stop_hw_units; p++) {
+ target = (*p)->hw_unit + (*p)->struct_target_offset;
+ if (!strcmp(target->compatible, compat))
+ return *p;
+ }
- if (target->read)
- return target->read(target, addr, value);
- else
- /* If there is no read method fall through to the next one */
- return read_next_target(target, addr, value);
+ return NULL;
}
-int read_next_target(struct target *target, uint64_t addr, uint64_t *value)
+void targets_init(void *fdt)
{
- assert(target->next);
- return read_target(target->next, target->base + addr, value);
+ struct dt_node *dn;
+ const struct dt_property *p;
+ struct target_class *target_class;
+ struct hw_unit_info *hw_unit_info;
+ void *new_hw_unit;
+ struct target *new_target;
+
+ dt_root = dt_new_root("");
+ dt_expand(fdt);
+
+ /* Now we need to walk the device-tree, assign struct targets
+ * to each of the nodes and add them to the appropriate target
+ * classes */
+ dt_for_each_node(dt_root, dn) {
+ p = dt_require_property(dn, "compatible", -1);
+ hw_unit_info = find_compatible_target(p->prop);
+ if (hw_unit_info) {
+ /* We need to allocate a new target */
+ new_hw_unit = malloc(hw_unit_info->size);
+ assert(new_hw_unit);
+ memcpy(new_hw_unit, hw_unit_info->hw_unit, hw_unit_info->size);
+ new_target = new_hw_unit + hw_unit_info->struct_target_offset;
+ new_target->dn = dn;
+ dn->target = new_target;
+ target_class = get_target_class(new_target->class);
+ list_add(&target_class->targets, &new_target->class_link);
+ PR_DEBUG("Found target %s for %s\n", new_target->name, dn->name);
+ } else
+ PR_DEBUG("No target found for %s\n", dn->name);
+ }
}
-int write_target(struct target *target, uint64_t addr, uint64_t value)
+/* Disable a node and all it's children */
+static void disable_node(struct dt_node *dn)
{
-// printf("Target %s write 0x%0llx\n", target->name, addr);
+ struct dt_node *next;
+ struct dt_property *p;
- if (target->write)
- return target->write(target, addr, value);
- else
- return write_next_target(target, addr, value);
+ p = dt_find_property(dn, "status");
+ if (p)
+ dt_del_property(dn, p);
+
+ dt_add_property_string(dn, "status", "disabled");
+ dt_for_each_child(dn, next)
+ disable_node(next);
+}
+
+static void _target_probe(struct dt_node *dn)
+{
+ int rc;
+ struct dt_node *next;
+ struct dt_property *p;
+
+ PR_DEBUG("Probe %s - ", dn->name);
+ if (!dn->target) {
+ PR_DEBUG("target not found\n");
+ return;
+ }
+
+ p = dt_find_property(dn, "status");
+ if ((p && !strcmp(p->prop, "disabled")) || (dn->target->probe && (rc = dn->target->probe(dn->target)))) {
+ if (rc)
+ PR_DEBUG("not found\n");
+ else
+ PR_DEBUG("disabled\n");
+
+ disable_node(dn);
+ } else {
+ PR_DEBUG("success\n");
+ dt_for_each_child(dn, next)
+ _target_probe(next);
+ }
}
-int write_next_target(struct target *target, uint64_t addr, uint64_t value)
+/* We walk the tree root down disabling targets which might/should
+ * exist but don't */
+void target_probe(void)
{
- assert(target->next);
- return write_target(target->next, target->base + addr, value);
+ _target_probe(dt_first(dt_root));
}
diff --git a/libpdbg/target.h b/libpdbg/target.h
index 9e73e3b..9f83b40 100644
--- a/libpdbg/target.h
+++ b/libpdbg/target.h
@@ -18,43 +18,109 @@
#include <stdint.h>
#include <ccan/list/list.h>
+#include <ccan/container_of/container_of.h>
+#include "compiler.h"
+#include "device.h"
-struct target;
-typedef int (*target_read)(struct target *target, uint64_t addr, uint64_t *value);
-typedef int (*target_write)(struct target *target, uint64_t addr, uint64_t value);
-typedef void (*target_destroy)(struct target *target);
+#define PR_DEBUG(x, args...) \
+ fprintf(stderr, x, ##args)
+#define PR_INFO(x, args...) \
+ fprintf(stderr, x, ##args)
+#define PR_ERROR(x, args...) \
+ fprintf(stderr, "%s: " x, __FUNCTION__, ##args)
enum chip_type {CHIP_UNKNOWN, CHIP_P8, CHIP_P8NV, CHIP_P9};
+struct target_class {
+ char *name;
+ struct list_head targets;
+ struct list_node class_head_link;
+};
+
struct target {
- const char *name;
- int index;
- uint64_t base;
- target_read read;
- target_write write;
- target_destroy destroy;
- enum chip_type chip_type;
- struct target *next;
- struct list_node link;
- struct list_head children;
+ char *name;
+ char *compatible;
+ char *class;
+ int (*probe)(struct target *target);
+ struct dt_node *dn;
struct list_node class_link;
+};
+
+struct target_class *find_target_class(const char *name);
+struct target_class *require_target_class(const char *name);
+
+extern struct list_head empty_list;
+#define for_each_class_target(class_name, target) \
+ list_for_each((find_target_class(class_name) ? &require_target_class(class_name)->targets : &empty_list), target, class_link)
+
+struct hw_unit_info {
+ void *hw_unit;
+ size_t size;
+ size_t struct_target_offset;
+};
+
+/* We can't pack the structs themselves directly into a special
+ * section because there doesn't seem to be any standard way of doing
+ * that due to alignment rules. So instead we pack pointers into a
+ * special section. */
+#define DECLARE_HW_UNIT(name) \
+ const struct hw_unit_info __used name ##_hw_unit = \
+ { .hw_unit = &name, .size = sizeof(name), .struct_target_offset = container_off(typeof(name), target) }; \
+ const struct hw_unit_info __used __section("hw_units") *name ##_hw_unit_p = &name ##_hw_unit
+
+struct adu {
+ struct target target;
+ int (*getmem)(struct adu *, uint64_t, uint8_t *, uint64_t);
+ int (*putmem)(struct adu *, uint64_t, uint8_t *, uint64_t);
+ uint64_t (*_get_ctrl_reg)(uint64_t, uint64_t);
+ uint64_t (*_get_cmd_reg)(uint64_t, uint64_t);
+ uint64_t (*_wait_completion)(struct adu *);
+};
+#define target_to_adu(x) container_of(x, struct adu, target)
+
+struct pib {
+ struct target target;
+ int (*read)(struct pib *, uint64_t, uint64_t *);
+ int (*write)(struct pib *, uint64_t, uint64_t);
void *priv;
- uint64_t status;
};
+#define target_to_pib(x) container_of(x, struct pib, target)
-struct target_class {
- const char *name;
- struct list_head targets;
+struct opb {
+ struct target target;
+ int (*read)(struct opb *, uint32_t, uint32_t *);
+ int (*write)(struct opb *, uint32_t, uint32_t);
+};
+#define target_to_opb(x) container_of(x, struct opb, target)
+
+struct fsi {
+ struct target target;
+ int (*read)(struct fsi *, uint32_t, uint32_t *);
+ int (*write)(struct fsi *, uint32_t, uint32_t);
+ enum chip_type chip_type;
};
+#define target_to_fsi(x) container_of(x, struct fsi, target)
+
+struct chiplet {
+ struct target target;
+};
+#define target_to_chiplet(x) container_of(x, struct chiplet, target)
+
+struct thread {
+ struct target target;
+ uint64_t status;
+ int id;
+};
+#define target_to_thread(x) container_of(x, struct thread, target)
+
+void targets_init(void *fdt);
+void target_probe(void);
-int read_target(struct target *target, uint64_t addr, uint64_t *value);
-int read_next_target(struct target *target, uint64_t addr, uint64_t *value);
-int write_target(struct target *target, uint64_t addr, uint64_t value);
-int write_next_target(struct target *target, uint64_t addr, uint64_t value);
-void target_init(struct target *target, const char *name, uint64_t base,
- target_read read, target_write write,
- target_destroy destroy, struct target *next);
-void target_class_add(struct target_class *class, struct target *target, int index);
-void target_del(struct target *target);
+int pib_read(struct target *pib_dt, uint64_t addr, uint64_t *data);
+int pib_write(struct target *pib_dt, uint64_t addr, uint64_t data);
+int opb_read(struct target *opb_dt, uint32_t addr, uint32_t *data);
+int opb_write(struct target *opb_dt, uint32_t addr, uint32_t data);
+int fsi_read(struct target *fsi_dt, uint32_t addr, uint32_t *data);
+int fsi_write(struct target *fsi_dt, uint32_t addr, uint32_t data);
#endif
diff --git a/p8-fsi.dts.m4 b/p8-fsi.dts.m4
new file mode 100644
index 0000000..e975313
--- /dev/null
+++ b/p8-fsi.dts.m4
@@ -0,0 +1,49 @@
+/dts-v1/;
+
+/ {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+
+ fsi@0 {
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ compatible = "ibm,bmcfsi";
+ reg = <0x0 0x0 0x0>;
+
+ /* GPIO pin definitions */
+ fsi_clk = <0x0 0x4>; /* A4 */
+ fsi_dat = <0x0 0x5>; /* A5 */
+ fsi_dat_en = <0x20 0x1e>; /* H6 */
+ fsi_enable = <0x0 0x18>; /* D0 */
+ cronus_sel = <0x0 0x6>; /* A6 */
+ clock_delay = <0x14>;
+
+ index = <0x0>;
+ status = "hidden";
+
+ pib@1000 {
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ reg = <0x0 0x1000 0x7>;
+ compatible = "ibm,fsi-pib", "ibm,power8-fsi-pib";
+ index = <0x0>;
+ include(p8-pib.dts.m4)dnl
+ };
+
+ hmfsi@100000 {
+ compatible = "ibm,fsi-hmfsi";
+ reg = <0x0 0x100000 0x8000>;
+ index = <0x1>;
+
+ pib@1000 {
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ reg = <0x0 0x1000 0x7>;
+ compatible = "ibm,fsi-pib", "ibm,power8-fsi-pib";
+ index = <0x1>;
+ include(p8-pib.dts.m4)dnl
+ };
+
+ };
+ };
+};
diff --git a/p8-i2c.dts.m4 b/p8-i2c.dts.m4
new file mode 100644
index 0000000..a11f5f0
--- /dev/null
+++ b/p8-i2c.dts.m4
@@ -0,0 +1,40 @@
+/dts-v1/;
+
+/ {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+
+ /* I2C attached pib */
+ pib@50 {
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ compatible = "ibm,power8-i2c-slave";
+ bus = "/dev/i2c4";
+ reg = <0x50>;
+ index = <0x0>;
+ status = "hidden";
+ include(p8-pib.dts.m4)dnl
+
+ opb@20010 {
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ reg = <0x0 0x20010 0xa>;
+ compatible = "ibm,power8-opb";
+
+ hmfsi@100000 {
+ compatible = "ibm,power8-opb-hmfsi";
+ reg = <0x100000 0x80000>;
+ index = <0x1>;
+
+ pib@1000 {
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ reg = <0x0 0x1000 0x7>;
+ compatible = "ibm,fsi-pib", "ibm,power8-fsi-pib";
+ index = <0x1>;
+ include(p8-pib.dts.m4)dnl
+ };
+ };
+ };
+ };
+};
diff --git a/p8-pib.dts.m4 b/p8-pib.dts.m4
new file mode 100644
index 0000000..de1d3bf
--- /dev/null
+++ b/p8-pib.dts.m4
@@ -0,0 +1,45 @@
+define(`CONCAT', `$1$2')dnl
+define(`HEX', `CONCAT(0x, $1)')dnl
+define(`CORE_BASE', `eval(0x10000000 + $1 * 0x1000000, 16)')dnl
+define(`CORE', `core@CORE_BASE($1) {
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ compatible = "ibm,power8-core";
+ reg = <0x0 HEX(CORE_BASE($1)) 0xfffff>;
+ index = <0x$2>;
+
+ THREAD(0);
+ THREAD(1);
+ THREAD(2);
+ THREAD(3);
+ THREAD(4);
+ THREAD(5);
+ THREAD(6);
+ THREAD(7);
+}')dnl
+define(`THREAD_BASE', `eval(0x13000 + $1 * 0x10, 16)')dnl
+define(`THREAD',`thread@THREAD_BASE($1) {
+ reg = <0x0 HEX(THREAD_BASE($1)) 0x10>;
+ compatible = "ibm,power8-thread";
+ index = <HEX(eval($1, 16))>;
+ }')dnl
+dnl
+define(`PROC_CORES', `CORE(1, 0);
+CORE(2, 1);
+CORE(3, 2);
+CORE(4, 3);
+CORE(5, 4);
+CORE(6, 5);
+CORE(9, 6);
+CORE(10, 7);
+CORE(11, 8);
+CORE(12, 9);
+CORE(13, 10);
+CORE(14, 11)')dnl
+
+adu@2020000 {
+ compatible = "ibm,power8-adu";
+ reg = <0x0 0x2020000 0x4>;
+};
+
+PROC_CORES;
diff --git a/p9-fsi.dtsi b/p9-fsi.dtsi
new file mode 100644
index 0000000..0181a19
--- /dev/null
+++ b/p9-fsi.dtsi
@@ -0,0 +1,47 @@
+/ {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+
+ fsi0: fsi@0 {
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ compatible = "ibm,bmcfsi";
+ reg = <0x0 0x0 0x0>;
+
+ index = <0x0>;
+ status = "hidden";
+
+ pib@1000 {
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ reg = <0x0 0x1000 0x7>;
+ compatible = "ibm,fsi-pib", "ibm,power9-fsi-pib";
+ index = <0x0>;
+
+ adu@90000 {
+ compatible = "ibm,power9-adu";
+ reg = <0x0 0x90000 0x5>;
+ };
+ };
+
+ hmfsi@100000 {
+ compatible = "ibm,fsi-hmfsi";
+ reg = <0x0 0x100000 0x8000>;
+ index = <0x1>;
+
+ pib@1000 {
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ reg = <0x0 0x1000 0x7>;
+ compatible = "ibm,fsi-pib", "ibm,power9-fsi-pib";
+ index = <0x1>;
+
+ adu@90000 {
+ compatible = "ibm,power9-adu";
+ reg = <0x0 0x90000 0x5>;
+ };
+ };
+ };
+
+ };
+};
diff --git a/p9-kernel.dts b/p9-kernel.dts
new file mode 100644
index 0000000..7223eb5
--- /dev/null
+++ b/p9-kernel.dts
@@ -0,0 +1,48 @@
+/dts-v1/;
+
+/ {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+
+ fsi0: kernelfsi@0 {
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ compatible = "ibm,kernel-fsi";
+ reg = <0x0 0x0 0x0>;
+
+ index = <0x0>;
+ status = "hidden";
+
+ pib@1000 {
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ reg = <0x0 0x1000 0x7>;
+ compatible = "ibm,fsi-pib", "ibm,power9-fsi-pib";
+ index = <0x0>;
+
+ adu@90000 {
+ compatible = "ibm,power9-adu";
+ reg = <0x0 0x90000 0x5>;
+ };
+ };
+
+ hmfsi@100000 {
+ compatible = "ibm,fsi-hmfsi";
+ reg = <0x0 0x100000 0x8000>;
+ index = <0x1>;
+
+ pib@1000 {
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ reg = <0x0 0x1000 0x7>;
+ compatible = "ibm,fsi-pib", "ibm,power9-fsi-pib";
+ index = <0x1>;
+
+ adu@90000 {
+ compatible = "ibm,power9-adu";
+ reg = <0x0 0x90000 0x5>;
+ };
+ };
+ };
+ };
+};
diff --git a/p9r-fsi.dts b/p9r-fsi.dts
new file mode 100644
index 0000000..2165bae
--- /dev/null
+++ b/p9r-fsi.dts
@@ -0,0 +1,16 @@
+/dts-v1/;
+
+/include/ "p9-fsi.dtsi"
+
+/ {
+};
+
+&fsi0 {
+ /* GPIO pin definitions */
+ fsi_clk = <0x1e0 0x10>; /* AA0 */
+ fsi_dat = <0x1e0 0x12>; /* AA2 */
+ fsi_dat_en = <0x80 0xa>; /* R2 */
+ fsi_enable = <0x0 0x18>; /* D0 */
+ cronus_sel = <0x0 0x6>; /* A6 */
+ clock_delay = <0x14>;
+};
diff --git a/p9w-fsi.dts b/p9w-fsi.dts
new file mode 100644
index 0000000..224c665
--- /dev/null
+++ b/p9w-fsi.dts
@@ -0,0 +1,16 @@
+/dts-v1/;
+
+/include/ "p9-fsi.dtsi"
+
+/ {
+};
+
+&fsi0 {
+ /* GPIO pin definitions */
+ fsi_clk = <0x1e0 0x10>; /* AA0 */
+ fsi_dat = <0x20 0x0>; /* E0 */
+ fsi_dat_en = <0x80 0xa>; /* R2 */
+ fsi_enable = <0x0 0x18>; /* D0 */
+ cronus_sel = <0x0 0x6>; /* A6 */
+ clock_delay = <0x14>;
+};
diff --git a/p9z-fsi.dts b/p9z-fsi.dts
new file mode 100644
index 0000000..87ad5c2
--- /dev/null
+++ b/p9z-fsi.dts
@@ -0,0 +1,16 @@
+/dts-v1/;
+
+/include/ "p9-fsi.dtsi"
+
+/ {
+};
+
+&fsi0 {
+ /* GPIO pin definitions */
+ fsi_clk = <0x0 0x13>; /* C3 */
+ fsi_dat = <0x0 0x12>; /* C2 */
+ fsi_dat_en = <0x78 0x16>; /* O6 */
+ fsi_enable = <0x0 0x18>; /* D0 */
+ cronus_sel = <0x78 0x1e>; /* P6 */
+ clock_delay = <0x14>;
+};
diff --git a/src/main.c b/src/main.c
index 9fcd6bc..29cd38f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -23,20 +23,25 @@
#include <errno.h>
#include <assert.h>
#include <limits.h>
+#include <inttypes.h>
#include <backend.h>
#include <operations.h>
#include <target.h>
+#include <device.h>
#include <config.h>
#include "bitutils.h"
+#undef PR_DEBUG
+#define PR_DEBUG(...)
+
enum command { GETCFAM = 1, PUTCFAM, GETSCOM, PUTSCOM, \
GETMEM, PUTMEM, GETGPR, GETNIA, GETSPR, \
GETMSR, PUTGPR, PUTNIA, PUTSPR, PUTMSR, \
- STOPCHIP, STARTCHIP, THREADSTATUS, STEP, \
- PROBE, GETVMEM };
+ STOP, START, THREADSTATUS, STEP, PROBE, \
+ GETVMEM };
#define MAX_CMD_ARGS 3
enum command cmd = 0;
@@ -47,43 +52,25 @@ static int cmd_max_arg_count = 0;
/* At the moment all commands only take some kind of number */
static uint64_t cmd_args[MAX_CMD_ARGS];
-enum backend { FSI, I2C, KERNEL };
+enum backend { FSI, I2C, KERNEL, FAKE };
static enum backend backend = KERNEL;
static char const *device_node;
static int i2c_addr = 0x50;
-#define MAX_TARGETS 400
-static struct target targets[MAX_TARGETS];
-
#define MAX_PROCESSORS 16
#define MAX_CHIPS 16
#define MAX_THREADS THREADS_PER_CORE
-static int **processor[MAX_PROCESSORS];
-static int *chip[MAX_PROCESSORS][MAX_CHIPS];
-static int thread[MAX_PROCESSORS][MAX_CHIPS][MAX_THREADS];
-
-struct target_class cfams = {
- .name = "CFAMS",
-};
+static int **processorsel[MAX_PROCESSORS];
+static int *chipsel[MAX_PROCESSORS][MAX_CHIPS];
+static int threadsel[MAX_PROCESSORS][MAX_CHIPS][MAX_THREADS];
-struct target_class processors = {
- .name = "Processors",
-};
-
-struct target_class chiplets = {
- .name = "Chiplets",
-};
-
-struct target_class threads = {
- .name = "Threads",
-};
-
-#define for_each_thread(x) list_for_each(&threads.targets, x, class_link)
-#define for_each_chiplet(x) list_for_each(&chiplets.targets, x, class_link)
-#define for_each_processor(x) list_for_each(&processors.targets, x, class_link)
-#define for_each_cfam(x) list_for_each(&cfams.targets, x, class_link)
+/* Convenience functions */
+#define for_each_thread(x) while(0)
+#define for_each_chiplet(x) while(0)
+#define for_each_processor(x) while(0)
+#define for_each_cfam(x) while(0)
static void print_usage(char *pname)
{
@@ -124,9 +111,9 @@ static void print_usage(char *pname)
printf("\tputnia <value>\n");
printf("\tgetspr <spr>\n");
printf("\tputspr <spr> <value>\n");
- printf("\tstartchip\n");
+ printf("\tstart\n");
printf("\tstep <count>\n");
- printf("\tstopchip\n");
+ printf("\tstop\n");
printf("\tthreadstatus\n");
printf("\tprobe\n");
}
@@ -188,14 +175,14 @@ enum command parse_cmd(char *optarg)
} else if (strcmp(optarg, "getvmem") == 0) {
cmd = GETVMEM;
cmd_min_arg_count = 1;
- } else if (strcmp(optarg, "startchip") == 0) {
- cmd = STARTCHIP;
+ } else if (strcmp(optarg, "start") == 0) {
+ cmd = START;
cmd_min_arg_count = 0;
} else if (strcmp(optarg, "step") == 0) {
cmd = STEP;
cmd_min_arg_count = 1;
- } else if (strcmp(optarg, "stopchip") == 0) {
- cmd = STOPCHIP;
+ } else if (strcmp(optarg, "stop") == 0) {
+ cmd = STOP;
cmd_min_arg_count = 0;
} else if (strcmp(optarg, "threadstatus") == 0) {
cmd = THREADSTATUS;
@@ -248,11 +235,11 @@ static bool parse_options(int argc, char *argv[])
case 'a':
opt_error = false;
for (current_processor = 0; current_processor < MAX_PROCESSORS; current_processor++) {
- processor[current_processor] = &chip[current_processor][0];
+ processorsel[current_processor] = &chipsel[current_processor][0];
for (current_chip = 0; current_chip < MAX_CHIPS; current_chip++) {
- chip[current_processor][current_chip] = &thread[current_processor][current_chip][0];
+ chipsel[current_processor][current_chip] = &threadsel[current_processor][current_chip][0];
for (current_thread = 0; current_thread < MAX_THREADS; current_thread++)
- thread[current_processor][current_chip][current_thread] = 1;
+ threadsel[current_processor][current_chip][current_thread] = 1;
}
}
break;
@@ -263,7 +250,7 @@ static bool parse_options(int argc, char *argv[])
if (current_processor >= MAX_PROCESSORS)
errno = -1;
else
- processor[current_processor] = &chip[current_processor][0];
+ processorsel[current_processor] = &chipsel[current_processor][0];
opt_error = errno;
break;
@@ -273,7 +260,7 @@ static bool parse_options(int argc, char *argv[])
if (current_chip >= MAX_CHIPS)
errno = -1;
else
- chip[current_processor][current_chip] = &thread[current_processor][current_chip][0];
+ chipsel[current_processor][current_chip] = &threadsel[current_processor][current_chip][0];
opt_error = errno;
break;
@@ -283,7 +270,7 @@ static bool parse_options(int argc, char *argv[])
if (current_thread >= MAX_THREADS)
errno = -1;
else
- thread[current_processor][current_chip][current_thread] = 1;
+ threadsel[current_processor][current_chip][current_thread] = 1;
opt_error = errno;
break;
@@ -335,666 +322,487 @@ static bool parse_options(int argc, char *argv[])
return opt_error;
}
-/*
- * Add a given target to the class
- */
-void target_class_add(struct target_class *class, struct target *target, int index)
+/* Returns the sum of return codes. This can be used to count how many targets the callback was run on. */
+static int for_each_child_target(char *class, struct target *parent,
+ int (*cb)(struct target *, uint32_t, uint64_t *, uint64_t *),
+ uint64_t *arg1, uint64_t *arg2)
{
- list_add_tail(&class->targets, &target->class_link);
- target->index = index;
-}
+ int rc = 0;
+ struct target *target;
+ uint32_t index;
+ struct dt_node *dn;
-/*
- * Initialises an I2C based backend. Returns the number of targets
- * added on success.
- */
-static int i2c_backend_targets_init(const char *bus, int addr)
-{
- int i, cfam_count;
+ for_each_class_target(class, target) {
+ struct dt_property *p;
- CHECK_ERR(i2c_target_init(&targets[0], "BMC I2C Backend", NULL, device_node, i2c_addr));
- CHECK_ERR(opb_target_init(&targets[1], "PIB2OPB", 0x20010, &targets[0]));
+ dn = target->dn;
+ if (parent && dn->parent != parent->dn)
+ continue;
- if (processor[0])
- target_class_add(&processors, &targets[0], 0);
+ /* Search up the tree for an index */
+ for (index = dt_prop_get_u32_def(dn, "index", -1); index == -1; dn = dn->parent);
+ assert(index != -1);
- /* Probe cascaded CFAMs on hMFSI ports and add FSI2PIB bridges */
- cfam_count = hmfsi_target_probe(&targets[1], &targets[2], MAX_TARGETS);
- for (i = 0; i < cfam_count; i++) {
- /* Skip prcessors that aren't selected */
- if (!(processor[i + 1]))
+ p = dt_find_property(dn, "status");
+ if (p && (!strcmp(p->prop, "disabled") || !strcmp(p->prop, "hidden")))
continue;
- fsi2pib_target_init(&targets[2 + cfam_count + i], "FSI2PIB", FSI2PIB_BASE, &targets[2 + i]);
-
- /* CFAM index should match processor index even though
- * we don't support cfam 0 on i2c */
- target_class_add(&cfams, &targets[2 + i], i + 1);
- target_class_add(&processors, &targets[2 + cfam_count + i], i + 1);
+ rc += cb(target, index, arg1, arg2);
}
- return 2 + 2*cfam_count;
+ return rc;
}
-static int kernel_backend_targets_init(void)
+static int for_each_target(char *class, int (*cb)(struct target *, uint32_t, uint64_t *, uint64_t *), uint64_t *arg1, uint64_t *arg2)
{
- struct target *cfam;
- int rc, i, cfam_count;
-
- rc = kernel_fsi_target_init(&targets[0], "Kernel FSI Backend", NULL);
- if (rc < 0)
- exit(1);
-
- /* The backend is directly connected to a processor CFAM */
- if (processor[0])
- target_class_add(&cfams, &targets[0], 0);
- cfam_count = 1;
-
- /* Probe cascaded CFAMs on hMFSI ports */
- cfam_count += hmfsi_target_probe(&targets[0], &targets[1], MAX_TARGETS);
- for (i = 1; i < cfam_count; i++)
- if (processor[i])
- target_class_add(&cfams, &targets[i], i);
-
- /* Add a FSI2PIB bridges for each CFAM */
- i = 0;
- for_each_cfam(cfam) {
- kernel_fsi2pib_target_init(&targets[cfam_count + i],
- "FSI2PIB", FSI2PIB_BASE, cfam);
-
- if (processor[i])
- target_class_add(&processors, &targets[cfam_count + i], i);
- i++;
- }
-
- return 2*cfam_count;
+ return for_each_child_target(class, NULL, cb, arg1, arg2);
}
-static int fsi_backend_targets_init(void)
+static int getcfam(struct target *target, uint32_t index, uint64_t *addr, uint64_t *unused)
{
- struct target *cfam;
- int i, cfam_count;
- enum fsi_system_type type;
-
- if (!strcmp(device_node, "p8"))
- type = FSI_SYSTEM_P8;
- else if (!strcmp(device_node, "p9w") || !strcmp(device_node, "witherspoon"))
- type = FSI_SYSTEM_P9W;
- else if (!strcmp(device_node, "p9r") || !strcmp(device_node, "romulus"))
- type = FSI_SYSTEM_P9R;
- else if (!strcmp(device_node, "p9z") || !strcmp(device_node, "zaius"))
- type = FSI_SYSTEM_P9Z;
- else {
- PR_ERROR("Unrecognized FSI system type %s\n", device_node);
- return -1;
- }
-
- fsi_target_init(&targets[0], "BMC FSI Backend", type, NULL);
-
- /* The backend is directly connected to a processor CFAM */
- if (processor[0])
- target_class_add(&cfams, &targets[0], 0);
- cfam_count = 1;
+ uint32_t value;
- /* Probe cascaded CFAMs on hMFSI ports */
- cfam_count += hmfsi_target_probe(&targets[0], &targets[1], MAX_TARGETS);
- for (i = 1; i < cfam_count; i++)
- if (processor[i])
- target_class_add(&cfams, &targets[i], i);
-
- /* Add a FSI2PIB bridges for each CFAM */
- i = 0;
- for_each_cfam(cfam) {
- fsi2pib_target_init(&targets[cfam_count + i], "FSI2PIB", FSI2PIB_BASE, cfam);
+ if (fsi_read(target, *addr, &value))
+ return 0;
- if (processor[cfam->index])
- target_class_add(&processors, &targets[cfam_count + i], cfam->index);
- i++;
- }
+ printf("p%d:0x%x = 0x%08x\n", index, (uint32_t) *addr, value);
- return 2*cfam_count;
+ return 1;
}
-static int backend_init(void)
+static int putcfam(struct target *target, uint32_t index, uint64_t *addr, uint64_t *data)
{
- int i, j, target_count = 0, chiplet_count = 0, thread_count = 0;
- struct target *target, *processor, *chiplet;
-
- list_head_init(&cfams.targets);
- list_head_init(&processors.targets);
- list_head_init(&chiplets.targets);
- list_head_init(&threads.targets);
-
- switch (backend) {
- case I2C:
- if ((target_count = i2c_backend_targets_init(device_node, i2c_addr)) < 0) {
- PR_ERROR("Unable to I2C initialise backend\n");
- return -1;
- }
- break;
-
- case FSI:
- if ((target_count = fsi_backend_targets_init()) < 0) {
- PR_ERROR("Unable to FSI initialise backend\n");
- return -1;
- }
- break;
-
- case KERNEL:
- if ((target_count = kernel_backend_targets_init()) < 0) {
- PR_ERROR("Unable to initialise kernel backend\n");
- return -1;
- }
- break;
-
- default:
- PR_ERROR("Invalid backend specified\n");
- return -1;
- }
-
- target = &targets[target_count];
- j = 0;
- for_each_processor(processor) {
- chiplet_count += chiplet_target_probe(processor, target, MAX_TARGETS - target_count);
- for (i = 0; j < chiplet_count; i++, j++) {
- /* Skip chips that aren't selected */
- if (chip[processor->index][j])
- target_class_add(&chiplets, target, i);
-
- target++;
- }
- }
-
- target_count += chiplet_count;
- target = &targets[target_count];
- j = 0;
- for_each_chiplet(chiplet) {
- thread_count += thread_target_probe(chiplet, target, MAX_TARGETS - target_count);
- for (i = 0; j < thread_count; i++, j++) {
- target_class_add(&threads, target, i);
- if (!chip[chiplet->next->index][chiplet->index][j])
- target->status |= THREAD_STATUS_DISABLED;
- target++;
- }
- }
+ if (fsi_write(target, *addr, *data))
+ return 0;
- return 0;
+ return 1;
}
-
-static void dump_targets(int indent, struct target *target)
+static int getscom(struct target *target, uint32_t index, uint64_t *addr, uint64_t *unused)
{
- int i;
- struct target *child;
+ uint64_t value;
- for (i = 0; i < indent; i++)
- printf(" ");
+ if (pib_read(target, *addr, &value))
+ return 0;
- printf("%s@%llx\n", target->name, target->base);
+ printf("p%d:0x%" PRIx64 " = 0x%016" PRIx64 "\n", index, *addr, value);
- if (!list_empty(&target->children))
- list_for_each(&target->children, child, link)
- dump_targets(indent + 1, child);
+ return 1;
}
-/* Dump all processors and chiplets */
-static uint64_t probe(void)
+static int putscom(struct target *target, uint32_t index, uint64_t *addr, uint64_t *data)
{
- struct target *cfam, *processor, *chiplet, *thread;
- int i, j;
-
- printf("Overall System Topology\n");
- printf("=======================\n\n");
- dump_targets(0, &targets[0]);
-
- i = 0;
- printf("\nCFAMs Present\n");
- printf("=============\n\n");
- for_each_cfam(cfam)
- printf("%d: %s@%llx\n", i++, cfam->name, cfam->base);
-
- i = 0;
- printf("\nProcessors/Chiplets Present\n");
- printf("===========================\n\n");
- for_each_processor(processor) {
- printf("Processor-ID %d: %s@%llx\n", processor->index, processor->name, processor->base);
- j = 0;
- for_each_chiplet(chiplet)
- if (chiplet->next == processor) {
- printf("\tChiplet-ID %d: %s@%llx\n", chiplet->index, chiplet->name, chiplet->base);
- for_each_thread(thread)
- if (thread->next == chiplet)
- printf("\t\tThread-ID %d: %s@%llx\n", thread->index, thread->name, thread->base);
- }
- }
+ if (pib_write(target, *addr, *data))
+ return 0;
- return 0;
+ return 1;
}
-static int filter_parent(struct target *target, void *priv)
+static int print_thread_status(struct target *thread_target, uint32_t index, uint64_t *status, uint64_t *unused1)
{
- struct target *parent = priv;
+ struct thread *thread = target_to_thread(thread_target);
- return target->next == parent;
+ *status = SETFIELD(0xf << (index * 4), *status, thread_status(thread) & 0xf);
+ return 1;
}
-/*
- * Call cb() on each member of the target class when filter() returns
- * true. Returns either the result from cb() (should be -ve on error)
- * or the number of time cb() was called.
- */
-static int for_each_class_call(struct target_class *class, int (*filter)(struct target *, void *),
- int (*cb)(struct target *, uint64_t, uint64_t *),
- void *priv, uint64_t addr, uint64_t *data)
+static int print_chiplet_thread_status(struct target *chiplet_target, uint32_t index, uint64_t *unused, uint64_t *unused1)
{
- struct target *target;
- int rc, count = 0;
+ uint64_t status = -1UL;
+ int i, rc;
+
+ printf("c%02d:", index);
+ rc = for_each_child_target("thread", chiplet_target, print_thread_status, &status, NULL);
+ for (i = 0; i < 8; i++)
+ switch ((status >> (i * 4)) & 0xf) {
+ case THREAD_STATUS_ACTIVE:
+ printf(" A");
+ break;
- list_for_each(&class->targets, target, class_link)
- if ((!filter || filter(target, priv)) && !(target->status & THREAD_STATUS_DISABLED)) {
- count++;
- if((rc = cb(target, addr, data)))
- return rc;
- }
+ case THREAD_STATUS_DOZE:
+ case THREAD_STATUS_QUIESCE | THREAD_STATUS_DOZE:
+ printf(" D");
+ break;
- return count;
-}
+ case THREAD_STATUS_NAP:
+ case THREAD_STATUS_QUIESCE | THREAD_STATUS_NAP:
+ printf(" N");
+ break;
-/* Same as above but calls cb() on all targets rather than just active ones */
-static int for_each_class_call_all(struct target_class *class, int (*filter)(struct target *, void *),
- int (*cb)(struct target *, uint64_t, uint64_t *),
- void *priv, uint64_t addr, uint64_t *data)
-{
- struct target *target;
- int rc, count = 0;
+ case THREAD_STATUS_SLEEP:
+ case THREAD_STATUS_QUIESCE | THREAD_STATUS_SLEEP:
+ printf(" S");
+ break;
+
+ case THREAD_STATUS_ACTIVE | THREAD_STATUS_QUIESCE:
+ printf(" Q");
+ break;
+
+ case 0xf:
+ printf(" ");
+ break;
- list_for_each(&class->targets, target, class_link)
- if (!filter || filter(target, priv)) {
- count++;
- if((rc = cb(target, addr, data)))
- return rc;
+ default:
+ printf(" U");
+ break;
}
+ printf("\n");
- return count;
+ return rc;
}
-static int print_thread_status(struct target *thread, uint64_t addr, uint64_t *data)
+static int print_proc_thread_status(struct target *pib_target, uint32_t index, uint64_t *unused, uint64_t *unused1)
{
- uint64_t status = chiplet_thread_status(thread);
- static int last_index = -1;
- int i;
-
- if (thread->index <= last_index)
- last_index = -1;
-
- for (i = last_index + 1; i < thread->index; i++)
- printf(" ");
-
- last_index = thread->index;
-
- switch (status)
- {
- case THREAD_STATUS_ACTIVE:
- printf(" A");
- break;
-
- case THREAD_STATUS_DOZE:
- case THREAD_STATUS_QUIESCE | THREAD_STATUS_DOZE:
- printf(" D");
- break;
-
- case THREAD_STATUS_NAP:
- case THREAD_STATUS_QUIESCE | THREAD_STATUS_NAP:
- printf(" N");
- break;
-
- case THREAD_STATUS_SLEEP:
- case THREAD_STATUS_QUIESCE | THREAD_STATUS_SLEEP:
- printf(" S");
- break;
-
- case THREAD_STATUS_ACTIVE | THREAD_STATUS_QUIESCE:
- printf(" Q");
- break;
-
- default:
- printf(" U");
- break;
- }
+ printf("\np%01dt: 0 1 2 3 4 5 6 7\n", index);
+ return for_each_child_target("chiplet", pib_target, print_chiplet_thread_status, NULL, NULL);
+};
- return 0;
+#define REG_MEM -3
+#define REG_MSR -2
+#define REG_NIA -1
+#define REG_R31 31
+static void print_proc_reg(struct thread *thread, uint64_t reg, uint64_t value, int rc)
+{
+ int proc_index, chip_index, thread_index;
+
+ thread_index = dt_prop_get_u32(thread->target.dn, "index");
+ chip_index = dt_prop_get_u32(thread->target.dn->parent, "index");
+ proc_index = dt_prop_get_u32(thread->target.dn->parent->parent, "index");
+ printf("p%d:c%d:t%d:", proc_index, chip_index, thread_index);
+
+ if (reg == REG_MSR)
+ printf("msr: ");
+ else if (reg == REG_NIA)
+ printf("nia: ");
+ else if (reg > REG_R31)
+ printf("spr%03" PRIu64 ": ", reg - REG_R31);
+ else if (reg >= 0 && reg <= 31)
+ printf("gpr%02" PRIu64 ": ", reg);
+
+ if (rc == 1)
+ printf("Chiplet in incorrect state\n");
+ else if (rc == 2)
+ printf("Thread in incorrect state\n");
+ else
+ printf("0x%016" PRIx64 "\n", value);
}
-static int print_chiplet_thread_status(struct target *chiplet, uint64_t addr, uint64_t *data)
+static int putprocreg(struct target *thread_target, uint32_t index, uint64_t *reg, uint64_t *value)
{
- printf("c%02d:", chiplet->index);
+ struct thread *thread = target_to_thread(thread_target);
+ int rc;
- for_each_class_call(&threads, filter_parent, print_thread_status, chiplet, 0, NULL);
- printf("\n");
+ if (*reg == REG_MSR)
+ rc = ram_putmsr(thread, *value);
+ else if (*reg == REG_NIA)
+ rc = ram_putnia(thread, *value);
+ else if (*reg > REG_R31)
+ rc = ram_putspr(thread, *reg - REG_R31, *value);
+ else if (*reg >= 0 && *reg <= 31)
+ rc = ram_putgpr(thread, *reg, *value);
+
+ print_proc_reg(thread, *reg, *value, rc);
return 0;
}
-static int getaddr(struct target *target, uint64_t addr, uint64_t *data)
+static int getprocreg(struct target *thread_target, uint32_t index, uint64_t *reg, uint64_t *unused)
{
+ struct thread *thread = target_to_thread(thread_target);
int rc;
+ uint64_t value;
- if ((rc = read_target(target, addr, data)))
- PR_ERROR("Error reading register\n");
- else
- printf("p%d:0x%llx: 0x%016llx\n", target->index, addr, *data);
+ if (*reg == REG_MSR)
+ rc = ram_getmsr(thread, &value);
+ else if (*reg == REG_NIA)
+ rc = ram_getnia(thread, &value);
+ else if (*reg > REG_R31)
+ rc = ram_getspr(thread, *reg - REG_R31, &value);
+ else if (*reg >= 0 && *reg <= 31)
+ rc = ram_getgpr(thread, *reg, &value);
- return rc;
+ print_proc_reg(thread, *reg, value, rc);
+
+ return !rc;
}
-static int putaddr(struct target *target, uint64_t addr, uint64_t *data)
+#define PUTMEM_BUF_SIZE 1024
+static int putmem(uint64_t addr)
{
- uint64_t val, mask = data[1];
- int rc;
-
- if (mask != -1ULL) {
- if ((rc = read_target(target, addr, &val))) {
- PR_ERROR("Unable to read register\n");
- return rc;
- }
-
- val &= ~mask;
- val |= data[0] & mask;
- } else
- val = data[0];
+ uint8_t *buf;
+ int read_size, rc = 0;
+ struct target *adu_target;
- if ((rc = write_target(target, addr, val)))
- PR_ERROR("Error writing register\n");
- else
- printf("p%d:0x%llx: 0x%016llx\n", target->index, addr, val);
+ for_each_class_target("adu", adu_target)
+ break;
- return rc;
+ buf = malloc(PUTMEM_BUF_SIZE);
+ assert(buf);
+ do {
+ read_size = read(STDIN_FILENO, buf, PUTMEM_BUF_SIZE);
+ if (adu_putmem(adu_target, addr, buf, read_size)) {
+ rc = 0;
+ PR_ERROR("Unable to write memory.\n");
+ break;
+ }
+ rc += read_size;
+ } while (read_size > 0);
+
+ free(buf);
+ return rc;
}
-#define START_CHIP 1
-#define STEP_CHIP 2
-#define STOP_CHIP 3
-static int startstopstep_thread(struct target *thread, uint64_t action, uint64_t *data)
+static int start_thread(struct target *thread_target, uint32_t index, uint64_t *unused, uint64_t *unused1)
{
- uint64_t status;
-
- if (action == START_CHIP)
- return ram_start_thread(thread);
- else if (action == STEP_CHIP) {
- status = chiplet_thread_status(thread);
- if (!(GETFIELD(THREAD_STATUS_QUIESCE, status) && GETFIELD(THREAD_STATUS_ACTIVE, status))) {
- PR_ERROR("Thread %d not stopped. Use stopchip first.\n", thread->index);
-
- /* Return 0 so we continue stepping other threads if requested */
- return 0;
- }
+ struct thread *thread = target_to_thread(thread_target);
- return ram_step_thread(thread, *data);
- } else
- return ram_stop_thread(thread);
+ return ram_start_thread(thread) ? 1 : 0;
}
-/*
- * Start/stop all threads on the chip.
- */
-static int startstopstep_chip(struct target *chiplet, uint64_t action, uint64_t *data)
+static int step_thread(struct target *thread_target, uint32_t index, uint64_t *count, uint64_t *unused1)
{
- if (action == STEP_CHIP)
- for_each_class_call(&threads, filter_parent, startstopstep_thread, chiplet, action, data);
- else
- for_each_class_call_all(&threads, filter_parent, startstopstep_thread, chiplet, action, data);
+ struct thread *thread = target_to_thread(thread_target);
- return 0;
+ return ram_step_thread(thread, *count) ? 1 : 0;
}
-static int check_thread_status(struct target *thread, uint64_t unused, uint64_t *unused1)
+static int stop_thread(struct target *thread_target, uint32_t index, uint64_t *unused, uint64_t *unused1)
{
- uint64_t status = chiplet_thread_status(thread);
+ struct thread *thread = target_to_thread(thread_target);
- if (status & THREAD_STATUS_QUIESCE)
- return 0;
- else
- return -1;
+ return ram_stop_thread(thread) ? 1 : 0;
}
-static int check_state(struct target *thread)
+static void enable_dn(struct dt_node *dn)
{
- struct target *chiplet = thread->next;
+ struct dt_property *p;
- if (for_each_class_call_all(&threads, filter_parent, check_thread_status, chiplet, 0, NULL) < 0) {
- PR_ERROR("Not all threads are quiesced. Use the stopchip command"
- " first to ensure all chiplet threads are quiesced\n");
- return -1;
- }
+ PR_DEBUG("Enabling %s\n", dn->name);
+ p = dt_find_property(dn, "status");
- if (!((thread->status & THREAD_STATUS_ACTIVE) && (thread->status & THREAD_STATUS_QUIESCE))) {
- PR_ERROR("Skipping thread %d as it is not active.\n", thread->index);
- return -2;
- }
+ if (!p)
+ /* Default assumption enabled */
+ return;
- return 0;
-}
+ /* We only override a status of "hidden" */
+ if (strcmp(p->prop, "hidden"))
+ return;
-#define REG_MEM -3ULL
-#define REG_MSR -2ULL
-#define REG_NIA -1ULL
-#define REG_R31 31ULL
+ dt_del_property(dn, p);
+}
-static int putreg(struct target *thread, uint64_t reg, uint64_t *data)
+static void disable_dn(struct dt_node *dn)
{
- int state;
+ struct dt_property *p;
- state = check_state(thread);
- if (state == -2)
- /* Return 0 so we keep attempting other non-sleeping
- * threads on the chiplet if requested */
- return 0;
- else if (state)
- return state;
-
- if (reg == REG_NIA)
- if (ram_putnia(thread, *data))
- PR_ERROR("Error reading register\n");
- else
- printf("c%02d:t%d:nia = 0x%016llx\n", thread->next->index, thread->index, *data);
- else if (reg == REG_MSR)
- if (ram_putmsr(thread, *data))
- PR_ERROR("Error reading register\n");
- else
- printf("c%02d:t%d:msr = 0x%016llx\n", thread->next->index, thread->index, *data);
- else if (reg <= REG_R31)
- if (ram_putgpr(thread, reg, *data))
- PR_ERROR("Error reading register\n");
- else
- printf("c%02d:t%d:r%lld = 0x%016llx\n", thread->next->index, thread->index, reg, *data);
- else {
- reg -= 32;
- if (ram_putspr(thread, reg, *data))
- PR_ERROR("Error reading register\n");
- else
- printf("c%02d:t%d:spr%lld = 0x%016llx\n", thread->next->index, thread->index, reg, *data);
- }
+ PR_DEBUG("Disabling %s\n", dn->name);
+ p = dt_find_property(dn, "status");
+ if (p)
+ /* We don't override hard-coded device tree
+ * status. This is needed to avoid disabling that
+ * backend. */
+ return;
- return 0;
+ dt_add_property_string(dn, "status", "disabled");
}
-static int getreg(struct target *thread, uint64_t reg, uint64_t *data)
+/* TODO: It would be nice to have a more dynamic way of doing this */
+extern unsigned char _binary_p8_i2c_dtb_o_start;
+extern unsigned char _binary_p8_i2c_dtb_o_end;
+extern unsigned char _binary_p8_fsi_dtb_o_start;
+extern unsigned char _binary_p8_fsi_dtb_o_end;
+extern unsigned char _binary_p9w_fsi_dtb_o_start;
+extern unsigned char _binary_p9w_fsi_dtb_o_end;
+extern unsigned char _binary_p9r_fsi_dtb_o_start;
+extern unsigned char _binary_p9r_fsi_dtb_o_end;
+extern unsigned char _binary_p9z_fsi_dtb_o_start;
+extern unsigned char _binary_p9z_fsi_dtb_o_end;
+extern unsigned char _binary_p9_kernel_dtb_o_start;
+extern unsigned char _binary_p9_kernel_dtb_o_end;
+extern unsigned char _binary_fake_dtb_o_start;
+extern unsigned char _binary_fake_dtb_o_end;
+static int target_select(void)
{
- uint64_t addr = *data;
- int state;
+ struct target *fsi, *pib, *chip, *thread;
- state = check_state(thread);
- if (state == -2)
- /* Return 0 so we keep attempting other non-sleeping
- * threads on the chiplet if requested */
- return 0;
- else if (state)
- return state;
+ switch (backend) {
+ case I2C:
+ targets_init(&_binary_p8_i2c_dtb_o_start);
+ break;
- if (reg == REG_NIA)
- if (ram_getnia(thread, data))
- PR_ERROR("Error reading register\n");
- else
- printf("c%02d:t%d:nia = 0x%016llx\n", thread->next->index, thread->index, *data);
- else if (reg == REG_MSR)
- if (ram_getmsr(thread, data))
- PR_ERROR("Error reading register\n");
- else
- printf("c%02d:t%d:msr = 0x%016llx\n", thread->next->index, thread->index, *data);
- else if (reg == REG_MEM)
- if (ram_getmem(thread, addr, data))
- PR_ERROR("Page fault reading memory\n");
- else
- printf("c%02d:t%d:0x%016llx = 0x%016llx\n", thread->next->index, thread->index, addr, *data);
- else if (reg <= REG_R31)
- if (ram_getgpr(thread, reg, data))
- PR_ERROR("Error reading register\n");
- else
- printf("c%02d:t%d:r%lld = 0x%016llx\n", thread->next->index, thread->index, reg, *data);
- else {
- reg -= 32;
- if (ram_getspr(thread, reg, data))
- PR_ERROR("Error reading register\n");
- else
- printf("c%02d:t%d:spr%lld = 0x%016llx\n", thread->next->index, thread->index, reg, *data);
- }
+ case FSI:
+ if (!strcmp(device_node, "p8"))
+ targets_init(&_binary_p8_fsi_dtb_o_start);
+ else if (!strcmp(device_node, "p9w") || !strcmp(device_node, "witherspoon"))
+ targets_init(&_binary_p9w_fsi_dtb_o_start);
+ else if (!strcmp(device_node, "p9r") || !strcmp(device_node, "romulus"))
+ targets_init(&_binary_p9r_fsi_dtb_o_start);
+ else if (!strcmp(device_node, "p9z") || !strcmp(device_node, "zaius"))
+ targets_init(&_binary_p9z_fsi_dtb_o_start);
+ else {
+ PR_ERROR("Invalid device type specified\n");
+ return -1;
+ }
+ break;
- return 0;
-}
+ case KERNEL:
+ targets_init(&_binary_p9_kernel_dtb_o_start);
+ break;
-#define PUTMEM_BUF_SIZE 1024
-static int putmem(uint64_t addr)
-{
- uint8_t *buf;
- int read_size, rc = 0;
- struct target *target;
+ case FAKE:
+ targets_init(&_binary_fake_dtb_o_start);
+ break;
- /* It doesn't matter which processor we execute on so
- * just use the primary one */
- if (list_empty(&processors.targets))
- return 0;
+ default:
+ PR_ERROR("Invalid backend specified\n");
+ return -1;
+ }
- target = list_entry(processors.targets.n.next, struct target, class_link);
- buf = malloc(PUTMEM_BUF_SIZE);
- assert(buf);
- do {
- read_size = read(STDIN_FILENO, buf, PUTMEM_BUF_SIZE);
- if (adu_putmem(target, addr, buf, read_size)) {
- rc = 0;
- PR_ERROR("Unable to write memory.\n");
- break;
- }
+ /* At this point we should have a device-tree loaded. We want
+ * to walk the tree and disabled nodes we don't care about
+ * prior to probing. */
+ for_each_class_target("pib", pib) {
+ int proc_index = dt_prop_get_u32(pib->dn, "index");
+
+ if (processorsel[proc_index]) {
+ enable_dn(pib->dn);
+ if (!find_target_class("chiplet"))
+ continue;
+ for_each_class_target("chiplet", chip) {
+ if (chip->dn->parent != pib->dn)
+ continue;
+ int chip_index = dt_prop_get_u32(chip->dn, "index");
+ if (chipsel[proc_index][chip_index]) {
+ enable_dn(chip->dn);
+ if (!find_target_class("thread"))
+ continue;
+ for_each_class_target("thread", thread) {
+ if (thread->dn->parent != chip->dn)
+ continue;
+
+ int thread_index = dt_prop_get_u32(thread->dn, "index");
+ if (threadsel[proc_index][chip_index][thread_index])
+ enable_dn(thread->dn);
+ else
+ disable_dn(thread->dn);
+ }
+ } else
+ disable_dn(chip->dn);
+ }
+ } else
+ disable_dn(pib->dn);
+ }
- rc += read_size;
- } while (read_size > 0);
+ for_each_class_target("fsi", fsi) {
+ int index = dt_prop_get_u32(fsi->dn, "index");
+ if (processorsel[index])
+ enable_dn(fsi->dn);
+ else
+ disable_dn(fsi->dn);
+ }
- free(buf);
- return rc;
+ return 0;
}
int main(int argc, char *argv[])
{
int rc = 0;
- uint64_t value = 0;
uint8_t *buf;
struct target *target;
if (parse_options(argc, argv))
return 1;
- if (backend_init())
+ /* Disable unselected targets */
+ if (target_select())
return 1;
+ target_probe();
+
switch(cmd) {
case GETCFAM:
- rc = for_each_class_call(&cfams, NULL, getaddr, NULL, cmd_args[0], &value);
- break;
- case GETSCOM:
- rc = for_each_class_call(&processors, NULL, getaddr, NULL, cmd_args[0], &value);
+ rc = for_each_target("fsi", getcfam, &cmd_args[0], NULL);
break;
case PUTCFAM:
- rc = for_each_class_call(&cfams, NULL, putaddr, NULL, cmd_args[0], &cmd_args[1]);
+ rc = for_each_target("fsi", putcfam, &cmd_args[0], &cmd_args[1]);
+ break;
+ case GETSCOM:
+ rc = for_each_target("pib", getscom, &cmd_args[0], NULL);
break;
case PUTSCOM:
- rc = for_each_class_call(&processors, NULL, putaddr, NULL, cmd_args[0], &cmd_args[1]);
+ rc = for_each_target("pib", putscom, &cmd_args[0], &cmd_args[1]);
break;
case GETMEM:
- /* It doesn't matter which processor we execute on so
- * just use the primary one */
- if (list_empty(&processors.targets))
- break;
+ buf = malloc(cmd_args[1]);
+ assert(buf);
+ for_each_class_target("adu", target) {
+ if (!adu_getmem(target, cmd_args[0], buf, cmd_args[1])) {
+ write(STDOUT_FILENO, buf, cmd_args[1]);
+ rc++;
+ } else
+ PR_ERROR("Unable to read memory.\n");
- target = list_entry(processors.targets.n.next, struct target, class_link);
- buf = malloc(cmd_args[1]);
- assert(buf);
- if (!adu_getmem(target, cmd_args[0], buf, cmd_args[1])) {
- rc = 1;
- write(STDOUT_FILENO, buf, cmd_args[1]);
- } else
- PR_ERROR("Unable to read memory.\n");
+ /* We only ever care about getting memory from a single processor */
+ break;
+ }
free(buf);
break;
case PUTMEM:
- rc = putmem(cmd_args[0]);
- printf("Wrote %d bytes starting at 0x%016llx\n", rc, cmd_args[0]);
+ rc = putmem(cmd_args[0]);
+ printf("Wrote %d bytes starting at 0x%016" PRIx64 "\n", rc, cmd_args[0]);
break;
case GETGPR:
- rc = for_each_class_call(&threads, NULL, getreg, NULL, cmd_args[0], &value);
+ for_each_target("thread", getprocreg, &cmd_args[0], NULL);
break;
case PUTGPR:
- rc = for_each_class_call(&threads, NULL, putreg, NULL, cmd_args[0], &cmd_args[1]);
+ for_each_target("thread", putprocreg, &cmd_args[0], &cmd_args[1]);
break;
case GETNIA:
- rc = for_each_class_call(&threads, NULL, getreg, NULL, REG_NIA, &value);
+ cmd_args[0] = REG_NIA;
+ for_each_target("thread", getprocreg, &cmd_args[0], NULL);
break;
case PUTNIA:
- rc = for_each_class_call(&threads, NULL, putreg, NULL, REG_NIA, &cmd_args[0]);
+ cmd_args[1] = cmd_args[0];
+ cmd_args[0] = REG_NIA;
+ for_each_target("thread", putprocreg, &cmd_args[0], &cmd_args[1]);
break;
case GETSPR:
- rc = for_each_class_call(&threads, NULL, getreg, NULL, cmd_args[0] + 32, &value);
+ cmd_args[0] += REG_R31;
+ for_each_target("thread", getprocreg, &cmd_args[0], NULL);
break;
case PUTSPR:
- rc = for_each_class_call(&threads, NULL, putreg, NULL, cmd_args[0] + 32, &cmd_args[1]);
+ cmd_args[0] += REG_R31;
+ for_each_target("thread", putprocreg, &cmd_args[0], &cmd_args[1]);
break;
case GETMSR:
- rc = for_each_class_call(&threads, NULL, getreg, NULL, REG_MSR, &value);
+ cmd_args[0] = REG_MSR;
+ for_each_target("thread", getprocreg, &cmd_args[0], NULL);
break;
case PUTMSR:
- rc = for_each_class_call(&threads, NULL, putreg, NULL, REG_MSR, &cmd_args[0]);
- break;
- case GETVMEM:
- rc = for_each_class_call(&threads, NULL, getreg, NULL, REG_MEM, &cmd_args[0]);
+ cmd_args[1] = cmd_args[0];
+ cmd_args[0] = REG_MSR;
+ for_each_target("thread", putprocreg, &cmd_args[0], &cmd_args[1]);
break;
case THREADSTATUS:
- printf(" t: 0 1 2 3 4 5 6 7\n");
- rc = for_each_class_call(&chiplets, NULL, print_chiplet_thread_status, NULL, 0, 0);
- printf("\nA - Active\nS - Sleep\nN - Nap\nD - Doze\nQ - Quiesced/Stopped\nU - Unknown\n");
+ for_each_target("pib", print_proc_thread_status, NULL, NULL);
break;
- case STARTCHIP:
- rc = for_each_class_call(&chiplets, NULL, startstopstep_chip, NULL, START_CHIP, NULL);
+ case START:
+ for_each_target("thread", start_thread, NULL, NULL);
break;
case STEP:
- rc = for_each_class_call(&chiplets, NULL, startstopstep_chip, NULL, STEP_CHIP, &cmd_args[0]);
+ for_each_target("thread", step_thread, &cmd_args[0], NULL);
break;
- case STOPCHIP:
- rc = for_each_class_call(&chiplets, NULL, startstopstep_chip, NULL, STOP_CHIP, NULL);
+ case STOP:
+ for_each_target("thread", stop_thread, NULL, NULL);
break;
- case PROBE:
- rc = 1;
- probe();
+ default:
+ PR_ERROR("Unsupported command\n");
break;
}
- if (rc <= 0) {
- printf("No valid targets found or specified. Try adding -p/-c/-t options to specify a target.\n");
- printf("Alternatively run %s -a probe to get a list of all valid targets\n", argv[0]);
- rc = -1;
- } else
- rc = 0;
-
- /* TODO: We don't properly tear down all the targets yet */
- targets[0].destroy(&targets[0]);
+ if (backend == FSI)
+ fsi_destroy(NULL);
return rc;
}
OpenPOWER on IntegriCloud