Migrate to libfdt

Replace our custom FDT parser implementation with libfdt while retaining
the original API as a thin wrapper around libfdt. This minimizes the
changes to the rest of our code base and hides differences in coding
styles.

As a byproduct, this fixes an issue with unaligned memory accesses while
parsing as libfdt handles these correctly.

Bug: 150587116
Change-Id: I8d305d7094b1be04608048009d73d7c448a578a0
diff --git a/src/fdt.c b/src/fdt.c
index 85d1d0a..8f57cc8 100644
--- a/src/fdt.c
+++ b/src/fdt.c
@@ -16,454 +16,261 @@
 
 #include "hf/fdt.h"
 
-#include <stdalign.h>
-#include <stdint.h>
+#include <libfdt.h>
 
-#include "hf/check.h"
-#include "hf/dlog.h"
-#include "hf/std.h"
+#include "hf/static_assert.h"
 
-struct fdt_header {
-	uint32_t magic;
-	uint32_t totalsize;
-	uint32_t off_dt_struct;
-	uint32_t off_dt_strings;
-	uint32_t off_mem_rsvmap;
-	uint32_t version;
-	uint32_t last_comp_version;
-	uint32_t boot_cpuid_phys;
-	uint32_t size_dt_strings;
-	uint32_t size_dt_struct;
-};
-
-struct fdt_reserve_entry {
-	uint64_t address;
-	uint64_t size;
-};
-
-enum fdt_token {
-	FDT_BEGIN_NODE = 1,
-	FDT_END_NODE = 2,
-	FDT_PROP = 3,
-	FDT_NOP = 4,
-	FDT_END = 9,
-};
-
-struct fdt_tokenizer {
-	const char *cur;
-	const char *end;
-	const char *strs;
-};
-
-#define FDT_VERSION 17
-#define FDT_MAGIC 0xd00dfeed
-
-#define FDT_PROPERTY_NAME_MAX_SIZE 32
-
-#define FDT_TOKEN_ALIGNMENT sizeof(uint32_t)
-
-static void fdt_tokenizer_init(struct fdt_tokenizer *t, const char *strs,
-			       const char *begin, const char *end)
+/** Returns pointer to the FDT buffer. */
+const void *fdt_base(const struct fdt *fdt)
 {
-	t->strs = strs;
-	t->cur = begin;
-	t->end = end;
+	return memiter_base(&fdt->buf);
 }
 
-static void fdt_tokenizer_align(struct fdt_tokenizer *t)
+/** Returns size of the FDT buffer. */
+size_t fdt_size(const struct fdt *fdt)
 {
-	t->cur = (char *)align_up(t->cur, FDT_TOKEN_ALIGNMENT);
-}
-
-static bool fdt_tokenizer_uint32(struct fdt_tokenizer *t, uint32_t *res)
-{
-	const char *next = t->cur + sizeof(*res);
-
-	if (next > t->end) {
-		return false;
-	}
-
-	*res = be32toh(*(uint32_t *)t->cur);
-	t->cur = next;
-
-	return true;
-}
-
-static bool fdt_tokenizer_token(struct fdt_tokenizer *t, uint32_t *res)
-{
-	uint32_t v;
-
-	while (fdt_tokenizer_uint32(t, &v)) {
-		if (v != FDT_NOP) {
-			*res = v;
-			return true;
-		}
-	}
-	return false;
-}
-
-static bool fdt_tokenizer_bytes(struct fdt_tokenizer *t, const char **res,
-				size_t size)
-{
-	const char *next = t->cur + size;
-
-	if (next > t->end) {
-		return false;
-	}
-
-	*res = t->cur;
-	t->cur = next;
-	fdt_tokenizer_align(t);
-
-	return true;
-}
-
-static bool fdt_tokenizer_str(struct fdt_tokenizer *t, const char **res)
-{
-	const char *p;
-
-	for (p = t->cur; p < t->end; p++) {
-		if (!*p) {
-			/* Found the end of the string. */
-			*res = t->cur;
-			t->cur = p + 1;
-			fdt_tokenizer_align(t);
-			return true;
-		}
-	}
-
-	return false;
-}
-
-bool fdt_root_node(struct fdt_node *node, const struct fdt_header *hdr)
-{
-	uint32_t max_ver;
-	uint32_t min_ver;
-	uint32_t begin = be32toh(hdr->off_dt_struct);
-	uint32_t size = be32toh(hdr->size_dt_struct);
-
-	memset_s(node, sizeof(*node), 0, sizeof(*node));
-
-	/* Check the magic number before anything else. */
-	if (hdr->magic != be32toh(FDT_MAGIC)) {
-		return false;
-	}
-
-	/* Check the version. */
-	max_ver = be32toh(hdr->version);
-	min_ver = be32toh(hdr->last_comp_version);
-	if (FDT_VERSION < min_ver || FDT_VERSION > max_ver) {
-		return false;
-	}
-
-	/* TODO: Verify that it is all within the fdt. */
-	node->begin = (const char *)hdr + begin;
-	node->end = node->begin + size;
-
-	/* TODO: Verify strings as well. */
-	node->strs = (char *)hdr + be32toh(hdr->off_dt_strings);
-
-	return true;
-}
-
-static bool fdt_next_property(struct fdt_tokenizer *t, const char **name,
-			      const char **buf, uint32_t *size)
-{
-	uint32_t token;
-	uint32_t nameoff;
-
-	if (!fdt_tokenizer_token(t, &token)) {
-		return false;
-	}
-
-	if (token != FDT_PROP) {
-		/* Rewind so that caller will get the same token. */
-		t->cur -= sizeof(uint32_t);
-		return false;
-	}
-
-	if (!fdt_tokenizer_uint32(t, size) ||
-	    !fdt_tokenizer_uint32(t, &nameoff) ||
-	    !fdt_tokenizer_bytes(t, buf, *size)) {
-		/*
-		 * Move cursor to the end so that caller won't get any new
-		 * tokens.
-		 */
-		t->cur = t->end;
-		return false;
-	}
-
-	/* TODO: Need to verify the strings. */
-	*name = t->strs + nameoff;
-
-	return true;
-}
-
-static bool fdt_next_subnode(struct fdt_tokenizer *t, const char **name)
-{
-	uint32_t token;
-
-	if (!fdt_tokenizer_token(t, &token)) {
-		return false;
-	}
-
-	if (token != FDT_BEGIN_NODE) {
-		/* Rewind so that caller will get the same token. */
-		t->cur -= sizeof(uint32_t);
-		return false;
-	}
-
-	if (!fdt_tokenizer_str(t, name)) {
-		/*
-		 * Move cursor to the end so that caller won't get any new
-		 * tokens.
-		 */
-		t->cur = t->end;
-		return false;
-	}
-
-	return true;
-}
-
-static void fdt_skip_properties(struct fdt_tokenizer *t)
-{
-	const char *name;
-	const char *buf;
-	uint32_t size;
-
-	while (fdt_next_property(t, &name, &buf, &size)) {
-		/* do nothing */
-	}
-}
-
-static bool fdt_skip_node(struct fdt_tokenizer *t)
-{
-	const char *name;
-	uint32_t token;
-	size_t pending = 1;
-
-	fdt_skip_properties(t);
-
-	do {
-		while (fdt_next_subnode(t, &name)) {
-			fdt_skip_properties(t);
-			pending++;
-		}
-
-		if (!fdt_tokenizer_token(t, &token)) {
-			return false;
-		}
-
-		if (token != FDT_END_NODE) {
-			t->cur = t->end;
-			return false;
-		}
-
-		pending--;
-	} while (pending);
-
-	return true;
-}
-
-bool fdt_read_property(const struct fdt_node *node, const char *name,
-		       const char **buf, uint32_t *size)
-{
-	struct fdt_tokenizer t;
-	const char *prop_name;
-
-	fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
-
-	while (fdt_next_property(&t, &prop_name, buf, size)) {
-		if (!strncmp(prop_name, name, FDT_PROPERTY_NAME_MAX_SIZE)) {
-			return true;
-		}
-	}
-
-	return false;
+	return memiter_size(&fdt->buf);
 }
 
 /**
- * Helper method for parsing 32/64-bit uints from FDT data.
+ * Extracts total size of the FDT structure from its FDT header.
+ * Returns true on success, false if header validation failed.
  */
-bool fdt_parse_number(const char *data, uint32_t size, uint64_t *value)
+bool fdt_size_from_header(const void *ptr, size_t *val)
 {
-	union {
-		volatile uint64_t v;
-		char a[8];
-	} t;
+	if (fdt_check_header(ptr) != 0) {
+		return false;
+	}
 
-	/* FDT values should be aligned to 32-bit boundary. */
-	CHECK(is_aligned(data, FDT_TOKEN_ALIGNMENT));
+	*val = fdt_totalsize(ptr);
+	return true;
+}
+
+/**
+ * Initializes `struct fdt` to point to a given buffer.
+ * Returns true on success, false if FDT validation failed.
+ */
+bool fdt_init_from_ptr(struct fdt *fdt, const void *ptr, size_t len)
+{
+	if (fdt_check_full(ptr, len) != 0) {
+		return false;
+	}
+
+	memiter_init(&fdt->buf, ptr, len);
+	return true;
+}
+
+/**
+ * Initializes `struct fdt` to point to a given buffer.
+ * Returns true on success, false if FDT validation failed.
+ */
+bool fdt_init_from_memiter(struct fdt *fdt, const struct memiter *it)
+{
+	return fdt_init_from_ptr(fdt, memiter_base(it), memiter_size(it));
+}
+
+/**
+ * Invalidates the internal pointer to FDT buffer.
+ * This is meant to prevent use-after-free bugs.
+ */
+void fdt_fini(struct fdt *fdt)
+{
+	memiter_init(&fdt->buf, NULL, 0);
+}
+
+/**
+ * Finds a node of a given path in the device tree.
+ * Unit addresses of components may be omitted but result is undefined if
+ * the path is not unique.
+ * Returns true on success, false if not found or an error occurred.
+ */
+bool fdt_find_node(const struct fdt *fdt, const char *path,
+		   struct fdt_node *node)
+{
+	int offset = fdt_path_offset(fdt_base(fdt), path);
+
+	if (offset < 0) {
+		return false;
+	}
+
+	*node = (struct fdt_node){.fdt = *fdt, .offset = offset};
+	return true;
+}
+
+/**
+ * Retrieves address size for a bus represented in the device tree.
+ * Result is value of '#address-cells' at `node` multiplied by cell size.
+ * If '#address-cells' is not found, the default value is 2 cells.
+ * Returns true on success, false if an error occurred.
+ */
+bool fdt_address_size(const struct fdt_node *node, size_t *size)
+{
+	int s = fdt_address_cells(fdt_base(&node->fdt), node->offset);
+
+	if (s < 0) {
+		return false;
+	}
+
+	*size = (size_t)s * sizeof(uint32_t);
+	return true;
+}
+
+/**
+ * Retrieves address range size for a bus represented in the device tree.
+ * Result is value of '#size-cells' at `node` multiplied by cell size.
+ * If '#size-cells' is not found, the default value is 1 cell.
+ * Returns true on success, false if an error occurred.
+ */
+bool fdt_size_size(const struct fdt_node *node, size_t *size)
+{
+	int s = fdt_size_cells(fdt_base(&node->fdt), node->offset);
+
+	if (s < 0) {
+		return false;
+	}
+
+	*size = (size_t)s * sizeof(uint32_t);
+	return true;
+}
+
+/**
+ * Retrieves the buffer with value of property `name` at `node`.
+ * Returns true on success, false if not found or an error occurred.
+ */
+bool fdt_read_property(const struct fdt_node *node, const char *name,
+		       struct memiter *data)
+{
+	const void *ptr;
+	int lenp;
+
+	ptr = fdt_getprop(fdt_base(&node->fdt), node->offset, name, &lenp);
+	if (ptr == NULL) {
+		return false;
+	}
+
+	CHECK(lenp >= 0);
+	memiter_init(data, ptr, (size_t)lenp);
+	return true;
+}
+
+/**
+ * Reads the value of property `name` at `node` as a uint.
+ * The size of the uint is inferred from the size of the property's value.
+ * Returns true on success, false if property not found or an error occurred.
+ */
+bool fdt_read_number(const struct fdt_node *node, const char *name,
+		     uint64_t *val)
+{
+	struct memiter data;
+
+	return fdt_read_property(node, name, &data) &&
+	       fdt_parse_number(&data, memiter_size(&data), val) &&
+	       (memiter_size(&data) == 0);
+}
+
+/**
+ * Parses a uint of given `size` from the beginning of `data`.
+ * On success returns true and advances `data` by `size` bytes.
+ * Returns false if `data` is too short or uints of `size` are not supported.
+ */
+bool fdt_parse_number(struct memiter *data, size_t size, uint64_t *val)
+{
+	struct memiter data_int;
+	struct memiter data_rem;
+
+	data_rem = *data;
+	if (!memiter_consume(&data_rem, size, &data_int)) {
+		return false;
+	}
 
 	switch (size) {
-	case sizeof(uint32_t):
-		/*
-		 * Assert that `data` is already sufficiently aligned to
-		 * dereference as uint32_t. We cannot use static_assert()
-		 * because alignof() is not an expression under ISO C11.
-		 */
-		CHECK(alignof(uint32_t) <= FDT_TOKEN_ALIGNMENT);
-		*value = be32toh(*(uint32_t *)data);
-		return true;
-	case sizeof(uint64_t):
-		/*
-		 * Armv8 requires `data` to be realigned to 64-bit boundary
-		 * to dereference as uint64_t. May not be needed on other
-		 * architectures.
-		 */
-		memcpy_s(t.a, sizeof(t.a), data, sizeof(uint64_t));
-		*value = be64toh(t.v);
-		return true;
-	default:
+	case sizeof(uint32_t): {
+		static_assert(sizeof(uint32_t) == sizeof(fdt32_t),
+			      "Size mismatch");
+		*val = fdt32_ld((const fdt32_t *)memiter_base(&data_int));
+		break;
+	}
+	case sizeof(uint64_t): {
+		static_assert(sizeof(uint64_t) == sizeof(fdt64_t),
+			      "Size mismatch");
+		*val = fdt64_ld((const fdt64_t *)memiter_base(&data_int));
+		break;
+	}
+	default: {
 		return false;
 	}
-}
-
-bool fdt_first_child(struct fdt_node *node, const char **child_name)
-{
-	struct fdt_tokenizer t;
-
-	fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
-
-	fdt_skip_properties(&t);
-
-	if (!fdt_next_subnode(&t, child_name)) {
-		return false;
 	}
 
-	node->begin = t.cur;
-
+	*data = data_rem;
 	return true;
 }
 
-bool fdt_next_sibling(struct fdt_node *node, const char **sibling_name)
+/**
+ * Finds first direct subnode of `node`.
+ * If found, makes `node` point to the subnode and returns true.
+ * Returns false if no subnode is found.
+ */
+bool fdt_first_child(struct fdt_node *node)
 {
-	struct fdt_tokenizer t;
+	int child_off = fdt_first_subnode(fdt_base(&node->fdt), node->offset);
 
-	fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
-
-	if (!fdt_skip_node(&t)) {
+	if (child_off < 0) {
 		return false;
 	}
 
-	if (!fdt_next_subnode(&t, sibling_name)) {
-		return false;
-	}
-
-	node->begin = t.cur;
-
+	node->offset = child_off;
 	return true;
 }
 
-bool fdt_find_child(struct fdt_node *node, const char *child)
+/**
+ * Finds next sibling node of `node`. Call repeatedly to discover all siblings.
+ * If found, makes `node` point to the next sibling node and returns true.
+ * Returns false if no next sibling node is found.
+ */
+bool fdt_next_sibling(struct fdt_node *node)
 {
-	struct fdt_tokenizer t;
-	const char *name;
+	int sib_off = fdt_next_subnode(fdt_base(&node->fdt), node->offset);
 
-	fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
-
-	fdt_skip_properties(&t);
-
-	while (fdt_next_subnode(&t, &name)) {
-		if (!strncmp(name, child, FDT_PROPERTY_NAME_MAX_SIZE)) {
-			node->begin = t.cur;
-			return true;
-		}
-
-		fdt_skip_node(&t);
+	if (sib_off < 0) {
+		return false;
 	}
 
-	return false;
+	node->offset = sib_off;
+	return true;
 }
 
-void fdt_dump(const struct fdt_header *hdr)
+/**
+ * Finds a node named `name` among subnodes of `node`.
+ * Returns true if found, false if not found or an error occurred.
+ */
+bool fdt_find_child(struct fdt_node *node, const struct string *name)
 {
-	uint32_t token;
-	size_t depth = 0;
-	const char *name;
-	struct fdt_tokenizer t;
-	struct fdt_node node;
+	struct fdt_node child = *node;
+	const void *base = fdt_base(&node->fdt);
 
-	/* Traverse the whole thing. */
-	if (!fdt_root_node(&node, hdr)) {
-		dlog_error("FDT failed validation.\n");
-		return;
+	if (!fdt_first_child(&child)) {
+		return false;
 	}
 
-	fdt_tokenizer_init(&t, node.strs, node.begin, node.end);
-
 	do {
-		while (fdt_next_subnode(&t, &name)) {
-			const char *buf;
-			uint32_t size;
+		const char *child_name;
+		int lenp;
+		struct memiter it;
 
-			dlog("%*sNew node: \"%s\"\n", 2 * depth, "", name);
-			depth++;
-			while (fdt_next_property(&t, &name, &buf, &size)) {
-				uint32_t i;
-
-				dlog("%*sproperty: \"%s\" (", 2 * depth, "",
-				     name);
-				for (i = 0; i < size; i++) {
-					dlog("%s%02x", i == 0 ? "" : " ",
-					     buf[i]);
-				}
-				dlog(")\n");
-			}
+		child_name = fdt_get_name(base, child.offset, &lenp);
+		if (child_name == NULL) {
+			/* Error */
+			return false;
 		}
 
-		if (!fdt_tokenizer_token(&t, &token)) {
-			return;
+		CHECK(lenp >= 0);
+		memiter_init(&it, child_name, (size_t)lenp);
+		if (string_eq(name, &it)) {
+			node->offset = child.offset;
+			return true;
 		}
+	} while (fdt_next_sibling(&child));
 
-		if (token != FDT_END_NODE) {
-			return;
-		}
-
-		depth--;
-	} while (depth);
-
-	dlog("fdt: off_mem_rsvmap=%u\n", be32toh(hdr->off_mem_rsvmap));
-	{
-		struct fdt_reserve_entry *e =
-			(struct fdt_reserve_entry
-				 *)((uintptr_t)hdr +
-				    be32toh(hdr->off_mem_rsvmap));
-		while (e->address || e->size) {
-			dlog("Entry: %p (%#x bytes)\n", be64toh(e->address),
-			     be64toh(e->size));
-			e++;
-		}
-	}
-}
-
-void fdt_add_mem_reservation(struct fdt_header *hdr, uint64_t addr,
-			     uint64_t len)
-{
-	/* TODO: Clean this up. */
-	uint8_t *begin = (uint8_t *)hdr + be32toh(hdr->off_mem_rsvmap);
-	struct fdt_reserve_entry *e = (struct fdt_reserve_entry *)begin;
-	size_t old_size =
-		be32toh(hdr->totalsize) - be32toh(hdr->off_mem_rsvmap);
-
-	hdr->totalsize = htobe32(be32toh(hdr->totalsize) +
-				 sizeof(struct fdt_reserve_entry));
-	hdr->off_dt_struct = htobe32(be32toh(hdr->off_dt_struct) +
-				     sizeof(struct fdt_reserve_entry));
-	hdr->off_dt_strings = htobe32(be32toh(hdr->off_dt_strings) +
-				      sizeof(struct fdt_reserve_entry));
-	memmove_s(begin + sizeof(struct fdt_reserve_entry), old_size, begin,
-		  old_size);
-	e->address = htobe64(addr);
-	e->size = htobe64(len);
-}
-
-size_t fdt_header_size(void)
-{
-	return sizeof(struct fdt_header);
-}
-
-uint32_t fdt_total_size(const struct fdt_header *hdr)
-{
-	return be32toh(hdr->totalsize);
+	/* Not found */
+	return false;
 }