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/inc/hf/boot_flow.h b/inc/hf/boot_flow.h
index 9bf82c1..0e6a597 100644
--- a/inc/hf/boot_flow.h
+++ b/inc/hf/boot_flow.h
@@ -21,8 +21,7 @@
#include "hf/memiter.h"
#include "hf/mm.h"
-bool boot_flow_get_params(struct boot_params *p,
- const struct fdt_node *fdt_root);
+bool boot_flow_get_params(struct boot_params *p, const struct fdt *fdt);
bool boot_flow_update(struct mm_stage1_locked stage1_locked,
const struct manifest *manifest,
diff --git a/inc/hf/fdt.h b/inc/hf/fdt.h
index 2dbb1d9..08f1741 100644
--- a/inc/hf/fdt.h
+++ b/inc/hf/fdt.h
@@ -16,27 +16,49 @@
#pragma once
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
+#include "hf/memiter.h"
+#include "hf/string.h"
-struct fdt_node {
- const struct fdt_header *hdr;
- const char *begin;
- const char *end;
- const char *strs;
+/**
+ * Wrapper around a pointer to a Flattened Device Tree (FDT) structure located
+ * somewhere in mapped main memory. Sanity checks are performed on initilization
+ * to ensure it is pointing to a valid FDT and most libfdt API calls check for
+ * the presence of the FDT magic.
+ */
+struct fdt {
+ struct memiter buf;
};
-size_t fdt_header_size(void);
-uint32_t fdt_total_size(const struct fdt_header *hdr);
-void fdt_dump(const struct fdt_header *hdr);
-bool fdt_root_node(struct fdt_node *node, const struct fdt_header *hdr);
-bool fdt_find_child(struct fdt_node *node, const char *child);
-bool fdt_first_child(struct fdt_node *node, const char **child_name);
-bool fdt_next_sibling(struct fdt_node *node, const char **sibling_name);
-bool fdt_read_property(const struct fdt_node *node, const char *name,
- const char **buf, uint32_t *size);
-bool fdt_parse_number(const char *data, uint32_t size, uint64_t *value);
+/**
+ * Wrapper around a pointer to a valid Device Tree node inside a FDT structure.
+ */
+struct fdt_node {
+ struct fdt fdt;
+ int offset;
+};
-void fdt_add_mem_reservation(struct fdt_header *hdr, uint64_t addr,
- uint64_t len);
+#define FDT_V17_HEADER_SIZE (10 * sizeof(uint32_t))
+
+bool fdt_size_from_header(const void *ptr, size_t *val);
+
+bool fdt_init_from_ptr(struct fdt *fdt, const void *ptr, size_t len);
+bool fdt_init_from_memiter(struct fdt *fdt, const struct memiter *it);
+void fdt_fini(struct fdt *fdt);
+
+const void *fdt_base(const struct fdt *fdt);
+size_t fdt_size(const struct fdt *fdt);
+
+bool fdt_find_node(const struct fdt *fdt, const char *path,
+ struct fdt_node *node);
+bool fdt_address_size(const struct fdt_node *node, size_t *addr_size);
+bool fdt_size_size(const struct fdt_node *node, size_t *size);
+
+bool fdt_first_child(struct fdt_node *node);
+bool fdt_next_sibling(struct fdt_node *node);
+bool fdt_find_child(struct fdt_node *node, const struct string *name);
+
+bool fdt_read_property(const struct fdt_node *node, const char *name,
+ struct memiter *data);
+bool fdt_read_number(const struct fdt_node *node, const char *name,
+ uint64_t *val);
+bool fdt_parse_number(struct memiter *data, size_t size, uint64_t *val);
diff --git a/inc/hf/fdt_handler.h b/inc/hf/fdt_handler.h
index 4e18afc..8cf3a4a 100644
--- a/inc/hf/fdt_handler.h
+++ b/inc/hf/fdt_handler.h
@@ -22,19 +22,15 @@
#include "hf/mpool.h"
#include "hf/string.h"
-struct fdt_header *fdt_map(struct mm_stage1_locked stage1_locked,
- paddr_t fdt_addr, struct fdt_node *n,
- struct mpool *ppool);
-bool fdt_unmap(struct mm_stage1_locked stage1_locked, struct fdt_header *fdt,
+#define FDT_PROP_INITRD_START "linux,initrd-start"
+#define FDT_PROP_INITRD_END "linux,initrd-end"
+
+bool fdt_map(struct fdt *fdt, struct mm_stage1_locked stage1_locked,
+ paddr_t fdt_addr, struct mpool *ppool);
+bool fdt_unmap(struct fdt *fdt, struct mm_stage1_locked stage1_locked,
struct mpool *ppool);
-bool fdt_find_cpus(const struct fdt_node *root, cpu_id_t *cpu_ids,
- size_t *cpu_count);
-bool fdt_find_memory_ranges(const struct fdt_node *root,
- struct string *device_type,
+bool fdt_find_cpus(const struct fdt *fdt, cpu_id_t *cpu_ids, size_t *cpu_count);
+bool fdt_find_memory_ranges(const struct fdt *fdt, struct string *device_type,
struct mem_range *mem_ranges,
size_t *mem_ranges_count, size_t mem_range_limit);
-bool fdt_find_initrd(const struct fdt_node *root, paddr_t *begin, paddr_t *end);
-
-/** Apply an update to the FDT. */
-bool fdt_patch(struct mm_stage1_locked stage1_locked, paddr_t fdt_addr,
- struct boot_params_update *p, struct mpool *ppool);
+bool fdt_find_initrd(const struct fdt *fdt, paddr_t *begin, paddr_t *end);
diff --git a/inc/hf/fdt_patch.h b/inc/hf/fdt_patch.h
new file mode 100644
index 0000000..5c639fe
--- /dev/null
+++ b/inc/hf/fdt_patch.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2020 The Hafnium Authors.
+ *
+ * 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
+ *
+ * https://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.
+ */
+
+#pragma once
+
+#include "hf/boot_params.h"
+#include "hf/mm.h"
+#include "hf/mpool.h"
+
+/** Apply an update to the FDT. */
+bool fdt_patch(struct mm_stage1_locked stage1_locked, paddr_t fdt_addr,
+ struct boot_params_update *p, struct mpool *ppool);
diff --git a/inc/hf/memiter.h b/inc/hf/memiter.h
index e3f0db8..d0a830c 100644
--- a/inc/hf/memiter.h
+++ b/inc/hf/memiter.h
@@ -30,6 +30,8 @@
bool memiter_parse_str(struct memiter *it, struct memiter *str);
bool memiter_iseq(const struct memiter *it, const char *str);
bool memiter_advance(struct memiter *it, size_t v);
+bool memiter_restrict(struct memiter *it, size_t v);
+bool memiter_consume(struct memiter *it, size_t v, struct memiter *newit);
const void *memiter_base(const struct memiter *it);
size_t memiter_size(const struct memiter *it);
diff --git a/inc/hf/plat/boot_flow.h b/inc/hf/plat/boot_flow.h
index b5d8145..13409f7 100644
--- a/inc/hf/plat/boot_flow.h
+++ b/inc/hf/plat/boot_flow.h
@@ -25,8 +25,8 @@
paddr_t plat_boot_flow_get_fdt_addr(void);
uintreg_t plat_boot_flow_get_kernel_arg(void);
-bool plat_boot_flow_get_initrd_range(const struct fdt_node *fdt_root,
- paddr_t *begin, paddr_t *end);
+bool plat_boot_flow_get_initrd_range(const struct fdt *fdt, paddr_t *begin,
+ paddr_t *end);
bool plat_boot_flow_update(struct mm_stage1_locked stage1_locked,
const struct manifest *manifest,
struct boot_params_update *p, struct memiter *cpio,
diff --git a/inc/hf/plat/iommu.h b/inc/hf/plat/iommu.h
index 0655232..b9eccf2 100644
--- a/inc/hf/plat/iommu.h
+++ b/inc/hf/plat/iommu.h
@@ -25,7 +25,7 @@
* so that the driver can read from it. This can be used to map IOMMU devices
* into the hypervisor's address space so they are accessible by the driver.
*/
-bool plat_iommu_init(const struct fdt_node *fdt_root,
+bool plat_iommu_init(const struct fdt *fdt,
struct mm_stage1_locked stage1_locked,
struct mpool *ppool);
diff --git a/inc/hf/string.h b/inc/hf/string.h
index 9c64632..6ab6323 100644
--- a/inc/hf/string.h
+++ b/inc/hf/string.h
@@ -19,6 +19,8 @@
#include <stdbool.h>
#include <stddef.h>
+#include "hf/memiter.h"
+
/**
* Maximum length of a string including the NULL terminator.
* This is an arbitrary number and can be adjusted to fit use cases.
@@ -48,8 +50,9 @@
*/
#define STRING_INIT(str) ((struct string){.data = str})
-enum string_return_code string_init(struct string *str, const char *data,
- size_t size);
+enum string_return_code string_init(struct string *str,
+ const struct memiter *data);
void string_init_empty(struct string *str);
bool string_is_empty(const struct string *str);
const char *string_data(const struct string *str);
+bool string_eq(const struct string *str, const struct memiter *data);
diff --git a/src/BUILD.gn b/src/BUILD.gn
index e74fd1e..04e0923 100644
--- a/src/BUILD.gn
+++ b/src/BUILD.gn
@@ -57,7 +57,6 @@
"manifest.c",
"panic.c",
"spci_memory.c",
- "string.c",
"vcpu.c",
"vm.c",
]
@@ -70,6 +69,7 @@
":memiter",
":mm",
":std",
+ ":string",
"//src/arch/${plat_arch}/hypervisor",
plat_boot_flow,
plat_console,
@@ -124,6 +124,16 @@
]
}
+source_set("string") {
+ sources = [
+ "string.c",
+ ]
+ deps = [
+ ":memiter",
+ ":std",
+ ]
+}
+
source_set("fdt_handler") {
sources = [
"fdt_handler.c",
@@ -131,6 +141,7 @@
deps = [
":dlog",
":fdt",
+ ":fdt_patch",
]
}
@@ -141,8 +152,20 @@
]
deps = [
+ ":memiter",
+ ":string",
+ "//third_party/dtc:libfdt",
+ ]
+}
+
+source_set("fdt_patch") {
+ sources = [
+ "fdt_patch.c",
+ ]
+ deps = [
":dlog",
- ":std",
+ ":fdt",
+ "//third_party/dtc:libfdt",
]
}
diff --git a/src/boot_flow/android.c b/src/boot_flow/android.c
index 88939bc..910d3bb 100644
--- a/src/boot_flow/android.c
+++ b/src/boot_flow/android.c
@@ -41,10 +41,10 @@
* (b) a fixed address range known at build time (INITRD_ADDR and INITRD_SIZE
* are not zero).
*/
-bool plat_boot_flow_get_initrd_range(const struct fdt_node *fdt_root,
- paddr_t *begin, paddr_t *end)
+bool plat_boot_flow_get_initrd_range(const struct fdt *fdt, paddr_t *begin,
+ paddr_t *end)
{
- (void)fdt_root;
+ (void)fdt;
uintpaddr_t initrd_addr = (uintpaddr_t)(INITRD_ADDR);
size_t initrd_size = (size_t)(INITRD_SIZE);
diff --git a/src/boot_flow/common.c b/src/boot_flow/common.c
index dace9af..0f263a9 100644
--- a/src/boot_flow/common.c
+++ b/src/boot_flow/common.c
@@ -22,8 +22,7 @@
/**
* Extract the boot parameters from the FDT and the boot-flow driver.
*/
-bool boot_flow_get_params(struct boot_params *p,
- const struct fdt_node *fdt_root)
+bool boot_flow_get_params(struct boot_params *p, const struct fdt *fdt)
{
struct string memory = STRING_INIT("memory");
struct string device_memory = STRING_INIT("device-memory");
@@ -31,14 +30,14 @@
p->mem_ranges_count = 0;
p->kernel_arg = plat_boot_flow_get_kernel_arg();
- return plat_boot_flow_get_initrd_range(fdt_root, &p->initrd_begin,
+ return plat_boot_flow_get_initrd_range(fdt, &p->initrd_begin,
&p->initrd_end) &&
- fdt_find_cpus(fdt_root, p->cpu_ids, &p->cpu_count) &&
- fdt_find_memory_ranges(fdt_root, &memory, p->mem_ranges,
+ fdt_find_cpus(fdt, p->cpu_ids, &p->cpu_count) &&
+ fdt_find_memory_ranges(fdt, &memory, p->mem_ranges,
&p->mem_ranges_count, MAX_MEM_RANGES) &&
- fdt_find_memory_ranges(
- fdt_root, &device_memory, p->device_mem_ranges,
- &p->device_mem_ranges_count, MAX_DEVICE_MEM_RANGES);
+ fdt_find_memory_ranges(fdt, &device_memory, p->device_mem_ranges,
+ &p->device_mem_ranges_count,
+ MAX_DEVICE_MEM_RANGES);
}
/**
diff --git a/src/boot_flow/linux.c b/src/boot_flow/linux.c
index a3dff65..ffbfbec 100644
--- a/src/boot_flow/linux.c
+++ b/src/boot_flow/linux.c
@@ -18,6 +18,7 @@
#include "hf/cpio.h"
#include "hf/dlog.h"
#include "hf/fdt_handler.h"
+#include "hf/fdt_patch.h"
#include "hf/plat/boot_flow.h"
#include "hf/std.h"
@@ -45,10 +46,10 @@
/**
* Load initrd range from the board FDT.
*/
-bool plat_boot_flow_get_initrd_range(const struct fdt_node *fdt_root,
- paddr_t *begin, paddr_t *end)
+bool plat_boot_flow_get_initrd_range(const struct fdt *fdt, paddr_t *begin,
+ paddr_t *end)
{
- return fdt_find_initrd(fdt_root, begin, end);
+ return fdt_find_initrd(fdt, begin, end);
}
bool plat_boot_flow_update(struct mm_stage1_locked stage1_locked,
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;
}
diff --git a/src/fdt_handler.c b/src/fdt_handler.c
index 8af1401..75051ee 100644
--- a/src/fdt_handler.c
+++ b/src/fdt_handler.c
@@ -16,90 +16,34 @@
#include "hf/fdt_handler.h"
-#include "hf/boot_params.h"
#include "hf/check.h"
#include "hf/cpu.h"
#include "hf/dlog.h"
#include "hf/fdt.h"
-#include "hf/layout.h"
#include "hf/mm.h"
#include "hf/std.h"
-static bool fdt_read_number(const struct fdt_node *node, const char *name,
- uint64_t *value)
-{
- const char *data;
- uint32_t size;
-
- if (!fdt_read_property(node, name, &data, &size)) {
- return false;
- }
-
- switch (size) {
- case sizeof(uint32_t):
- case sizeof(uint64_t):
- CHECK(fdt_parse_number(data, size, value));
- break;
-
- default:
- return false;
- }
-
- return true;
-}
-
-static bool fdt_write_number(struct fdt_node *node, const char *name,
- uint64_t value)
-{
- const char *data;
- uint32_t size;
- union {
- volatile uint64_t v;
- char a[8];
- } t;
-
- if (!fdt_read_property(node, name, &data, &size)) {
- return false;
- }
-
- switch (size) {
- case sizeof(uint32_t):
- *(uint32_t *)data = be32toh(value);
- break;
-
- case sizeof(uint64_t):
- t.v = be64toh(value);
- memcpy_s((void *)data, size, t.a, sizeof(uint64_t));
- break;
-
- default:
- return false;
- }
-
- return true;
-}
-
/**
* Finds the memory region where initrd is stored.
*/
-bool fdt_find_initrd(const struct fdt_node *root, paddr_t *begin, paddr_t *end)
+bool fdt_find_initrd(const struct fdt *fdt, paddr_t *begin, paddr_t *end)
{
- struct fdt_node n = *root;
+ struct fdt_node n;
uint64_t initrd_begin;
uint64_t initrd_end;
- if (!fdt_find_child(&n, "chosen")) {
- dlog_error("Unable to find 'chosen'\n");
+ if (!fdt_find_node(fdt, "/chosen", &n)) {
+ dlog_error("Unable to find '/chosen'\n");
return false;
}
- if (!fdt_read_number(&n, "linux,initrd-start", &initrd_begin)) {
- dlog_error("Unable to read linux,initrd-start\n");
+ if (!fdt_read_number(&n, FDT_PROP_INITRD_START, &initrd_begin)) {
+ dlog_error("Unable to read " FDT_PROP_INITRD_START "\n");
return false;
}
- if (!fdt_read_number(&n, "linux,initrd-end", &initrd_end)) {
- dlog_error("Unable to read linux,initrd-end\n");
+ if (!fdt_read_number(&n, FDT_PROP_INITRD_END, &initrd_end)) {
+ dlog_error("Unable to read " FDT_PROP_INITRD_END "\n");
return false;
}
@@ -109,43 +53,38 @@
return true;
}
-bool fdt_find_cpus(const struct fdt_node *root, cpu_id_t *cpu_ids,
- size_t *cpu_count)
+bool fdt_find_cpus(const struct fdt *fdt, cpu_id_t *cpu_ids, size_t *cpu_count)
{
- struct fdt_node n = *root;
- const char *name;
- uint64_t address_size;
+ static const struct string str_cpu = STRING_INIT("cpu");
+ struct fdt_node n;
+ size_t addr_size;
*cpu_count = 0;
- if (!fdt_find_child(&n, "cpus")) {
- dlog_error("Unable to find 'cpus'\n");
+ if (!fdt_find_node(fdt, "/cpus", &n)) {
+ dlog_error("Unable to find '/cpus'\n");
return false;
}
- if (fdt_read_number(&n, "#address-cells", &address_size)) {
- address_size *= sizeof(uint32_t);
- } else {
- address_size = sizeof(uint32_t);
+ if (!fdt_address_size(&n, &addr_size)) {
+ return false;
}
- if (!fdt_first_child(&n, &name)) {
+ if (!fdt_first_child(&n)) {
return false;
}
do {
- const char *data;
- uint32_t size;
+ struct memiter data;
- if (!fdt_read_property(&n, "device_type", &data, &size) ||
- size != sizeof("cpu") ||
- memcmp(data, "cpu", sizeof("cpu")) != 0 ||
- !fdt_read_property(&n, "reg", &data, &size)) {
+ if (!fdt_read_property(&n, "device_type", &data) ||
+ !string_eq(&str_cpu, &data) ||
+ !fdt_read_property(&n, "reg", &data)) {
continue;
}
/* Get all entries for this CPU. */
- while (size >= address_size) {
+ while (memiter_size(&data)) {
uint64_t value;
if (*cpu_count >= MAX_CPUS) {
@@ -154,71 +93,52 @@
return false;
}
- if (!fdt_parse_number(data, address_size, &value)) {
+ if (!fdt_parse_number(&data, addr_size, &value)) {
dlog_error("Could not parse CPU id\n");
return false;
}
cpu_ids[(*cpu_count)++] = value;
-
- size -= address_size;
- data += address_size;
}
- } while (fdt_next_sibling(&n, &name));
+ } while (fdt_next_sibling(&n));
return true;
}
-bool fdt_find_memory_ranges(const struct fdt_node *root,
- struct string *device_type,
+bool fdt_find_memory_ranges(const struct fdt *fdt, struct string *device_type,
struct mem_range *mem_ranges,
size_t *mem_ranges_count, size_t mem_range_limit)
{
- struct fdt_node n = *root;
- const char *name;
- uint64_t address_size;
- uint64_t size_size;
- uint64_t entry_size;
+ struct fdt_node n;
+ size_t addr_size;
+ size_t size_size;
size_t mem_range_index = 0;
- /* Get the sizes of memory range addresses and sizes. */
- if (fdt_read_number(&n, "#address-cells", &address_size)) {
- address_size *= sizeof(uint32_t);
- } else {
- address_size = sizeof(uint32_t);
+ if (!fdt_find_node(fdt, "/", &n) || !fdt_address_size(&n, &addr_size) ||
+ !fdt_size_size(&n, &size_size)) {
+ return false;
}
- if (fdt_read_number(&n, "#size-cells", &size_size)) {
- size_size *= sizeof(uint32_t);
- } else {
- size_size = sizeof(uint32_t);
- }
-
- entry_size = address_size + size_size;
-
- /* Look for nodes with the device_type set to "memory". */
- if (!fdt_first_child(&n, &name)) {
+ /* Look for nodes with the device_type set to `device_type`. */
+ if (!fdt_first_child(&n)) {
return false;
}
do {
- const char *data;
- uint32_t size;
+ struct memiter data;
- if (!fdt_read_property(&n, "device_type", &data, &size) ||
- strncmp(data, string_data(device_type), STRING_MAX_SIZE) !=
- 0 ||
- !fdt_read_property(&n, "reg", &data, &size)) {
+ if (!fdt_read_property(&n, "device_type", &data) ||
+ !string_eq(device_type, &data) ||
+ !fdt_read_property(&n, "reg", &data)) {
continue;
}
/* Traverse all memory ranges within this node. */
- while (size >= entry_size) {
+ while (memiter_size(&data)) {
uintpaddr_t addr;
size_t len;
- CHECK(fdt_parse_number(data, address_size, &addr));
- CHECK(fdt_parse_number(data + address_size, size_size,
- &len));
+ CHECK(fdt_parse_number(&data, addr_size, &addr));
+ CHECK(fdt_parse_number(&data, size_size, &len));
if (mem_range_index < mem_range_limit) {
mem_ranges[mem_range_index].begin =
@@ -234,151 +154,69 @@
string_data(device_type),
mem_range_index, mem_range_limit, len);
}
-
- size -= entry_size;
- data += entry_size;
}
- } while (fdt_next_sibling(&n, &name));
+ } while (fdt_next_sibling(&n));
*mem_ranges_count = mem_range_index;
return true;
}
-struct fdt_header *fdt_map(struct mm_stage1_locked stage1_locked,
- paddr_t fdt_addr, struct fdt_node *n,
- struct mpool *ppool)
+bool fdt_map(struct fdt *fdt, struct mm_stage1_locked stage1_locked,
+ paddr_t fdt_addr, struct mpool *ppool)
{
- struct fdt_header *fdt;
+ const void *fdt_ptr;
+ size_t fdt_len;
/* Map the fdt header in. */
- fdt = mm_identity_map(stage1_locked, fdt_addr,
- pa_add(fdt_addr, fdt_header_size()), MM_MODE_R,
- ppool);
- if (!fdt) {
+ fdt_ptr = mm_identity_map(stage1_locked, fdt_addr,
+ pa_add(fdt_addr, FDT_V17_HEADER_SIZE),
+ MM_MODE_R, ppool);
+ if (!fdt_ptr) {
dlog_error("Unable to map FDT header.\n");
return NULL;
}
- if (!fdt_root_node(n, fdt)) {
- dlog_error("FDT failed validation.\n");
+ if (!fdt_size_from_header(fdt_ptr, &fdt_len)) {
+ dlog_error("FDT failed header validation.\n");
goto fail;
}
/* Map the rest of the fdt in. */
- fdt = mm_identity_map(stage1_locked, fdt_addr,
- pa_add(fdt_addr, fdt_total_size(fdt)), MM_MODE_R,
- ppool);
- if (!fdt) {
+ fdt_ptr = mm_identity_map(stage1_locked, fdt_addr,
+ pa_add(fdt_addr, fdt_len), MM_MODE_R, ppool);
+ if (!fdt_ptr) {
dlog_error("Unable to map full FDT.\n");
goto fail;
}
- return fdt;
+ if (!fdt_init_from_ptr(fdt, fdt_ptr, fdt_len)) {
+ dlog_error("FDT failed validation.\n");
+ goto fail_full;
+ }
+
+ return true;
+
+fail_full:
+ mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, fdt_len), ppool);
+ return false;
fail:
- mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, fdt_header_size()),
- ppool);
- return NULL;
-}
-
-bool fdt_unmap(struct mm_stage1_locked stage1_locked, struct fdt_header *fdt,
- struct mpool *ppool)
-{
- paddr_t fdt_addr = pa_from_va(va_from_ptr(fdt));
-
- return mm_unmap(stage1_locked, fdt_addr,
- pa_add(fdt_addr, fdt_total_size(fdt)), ppool);
-}
-
-bool fdt_patch(struct mm_stage1_locked stage1_locked, paddr_t fdt_addr,
- struct boot_params_update *p, struct mpool *ppool)
-{
- struct fdt_header *fdt;
- struct fdt_node n;
- bool ret = false;
- size_t i;
-
- /* Map the fdt header in. */
- fdt = mm_identity_map(stage1_locked, fdt_addr,
- pa_add(fdt_addr, fdt_header_size()), MM_MODE_R,
- ppool);
- if (!fdt) {
- dlog_error("Unable to map FDT header.\n");
- return false;
- }
-
- if (!fdt_root_node(&n, fdt)) {
- dlog_error("FDT failed validation.\n");
- goto err_unmap_fdt_header;
- }
-
- /* Map the fdt (+ a page) in r/w mode in preparation for updating it. */
- fdt = mm_identity_map(stage1_locked, fdt_addr,
- pa_add(fdt_addr, fdt_total_size(fdt) + PAGE_SIZE),
- MM_MODE_R | MM_MODE_W, ppool);
- if (!fdt) {
- dlog_error("Unable to map FDT in r/w mode.\n");
- goto err_unmap_fdt_header;
- }
-
- if (!fdt_find_child(&n, "")) {
- dlog_error("Unable to find FDT root node.\n");
- goto out_unmap_fdt;
- }
-
- if (!fdt_find_child(&n, "chosen")) {
- dlog_error("Unable to find 'chosen'\n");
- goto out_unmap_fdt;
- }
-
- /* Patch FDT to point to new ramdisk. */
- if (!fdt_write_number(&n, "linux,initrd-start",
- pa_addr(p->initrd_begin))) {
- dlog_error("Unable to write linux,initrd-start\n");
- goto out_unmap_fdt;
- }
-
- if (!fdt_write_number(&n, "linux,initrd-end", pa_addr(p->initrd_end))) {
- dlog_error("Unable to write linux,initrd-end\n");
- goto out_unmap_fdt;
- }
-
- /*
- * Patch FDT to reserve hypervisor memory so the primary VM doesn't try
- * to use it.
- */
- fdt_add_mem_reservation(
- fdt, pa_addr(layout_text_begin()),
- pa_difference(layout_text_begin(), layout_text_end()));
- fdt_add_mem_reservation(
- fdt, pa_addr(layout_rodata_begin()),
- pa_difference(layout_rodata_begin(), layout_rodata_end()));
- fdt_add_mem_reservation(
- fdt, pa_addr(layout_data_begin()),
- pa_difference(layout_data_begin(), layout_data_end()));
-
- /* Patch FDT to reserve memory for secondary VMs. */
- for (i = 0; i < p->reserved_ranges_count; ++i) {
- fdt_add_mem_reservation(
- fdt, pa_addr(p->reserved_ranges[i].begin),
- pa_addr(p->reserved_ranges[i].end) -
- pa_addr(p->reserved_ranges[i].begin));
- }
-
- ret = true;
-
-out_unmap_fdt:
- /* Unmap FDT. */
- if (!mm_unmap(stage1_locked, fdt_addr,
- pa_add(fdt_addr, fdt_total_size(fdt) + PAGE_SIZE),
- ppool)) {
- dlog_error("Unable to unmap writable FDT.\n");
- return false;
- }
- return ret;
-
-err_unmap_fdt_header:
- mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, fdt_header_size()),
+ mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, FDT_V17_HEADER_SIZE),
ppool);
return false;
}
+
+bool fdt_unmap(struct fdt *fdt, struct mm_stage1_locked stage1_locked,
+ struct mpool *ppool)
+{
+ paddr_t begin = pa_from_va(va_from_ptr(fdt_base(fdt)));
+ paddr_t end = pa_add(begin, fdt_size(fdt));
+
+ if (!mm_unmap(stage1_locked, begin, end, ppool)) {
+ return false;
+ }
+
+ /* Invalidate pointer to the buffer. */
+ fdt_fini(fdt);
+ return true;
+}
diff --git a/src/fdt_handler_test.cc b/src/fdt_handler_test.cc
index 8788f77..21ab4d3 100644
--- a/src/fdt_handler_test.cc
+++ b/src/fdt_handler_test.cc
@@ -101,19 +101,16 @@
mpool_add_chunk(&ppool, test_heap.get(), TEST_HEAP_SIZE);
mm_init(&ppool);
- struct fdt_header *fdt;
- struct fdt_node n;
+ struct fdt fdt;
struct boot_params params = {};
struct mm_stage1_locked mm_stage1_locked = mm_lock_stage1();
struct string memory = STRING_INIT("memory");
- fdt = fdt_map(mm_stage1_locked, pa_init((uintpaddr_t)&test_dtb), &n,
- &ppool);
- ASSERT_THAT(fdt, NotNull());
- ASSERT_TRUE(fdt_find_child(&n, ""));
- fdt_find_memory_ranges(&n, &memory, params.mem_ranges,
+ ASSERT_TRUE(fdt_map(&fdt, mm_stage1_locked,
+ pa_init((uintpaddr_t)&test_dtb), &ppool));
+ fdt_find_memory_ranges(&fdt, &memory, params.mem_ranges,
¶ms.mem_ranges_count, MAX_MEM_RANGES);
- ASSERT_TRUE(fdt_unmap(mm_stage1_locked, fdt, &ppool));
+ ASSERT_TRUE(fdt_unmap(&fdt, mm_stage1_locked, &ppool));
mm_unlock_stage1(&mm_stage1_locked);
EXPECT_THAT(params.mem_ranges_count, Eq(3));
diff --git a/src/fdt_patch.c b/src/fdt_patch.c
new file mode 100644
index 0000000..5161192
--- /dev/null
+++ b/src/fdt_patch.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2020 The Hafnium Authors.
+ *
+ * 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
+ *
+ * https://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 "hf/fdt_patch.h"
+
+#include <libfdt.h>
+
+#include "hf/boot_params.h"
+#include "hf/dlog.h"
+#include "hf/fdt.h"
+#include "hf/fdt_handler.h"
+#include "hf/layout.h"
+#include "hf/mm.h"
+
+static bool patch_uint(void *fdt, int off, const char *prop, uint64_t val)
+{
+ const void *data;
+ int lenp;
+
+ data = fdt_getprop(fdt, off, prop, &lenp);
+ if (data == NULL) {
+ return false;
+ }
+
+ switch (lenp) {
+ case sizeof(uint64_t): {
+ return fdt_setprop_inplace_u64(fdt, off, prop, val) == 0;
+ }
+ case sizeof(uint32_t): {
+ return (val <= UINT32_MAX) &&
+ (fdt_setprop_inplace_u32(fdt, off, prop, val) == 0);
+ }
+ default: {
+ return false;
+ }
+ }
+}
+
+static bool add_mem_reservation(void *fdt, paddr_t begin, paddr_t end)
+{
+ size_t len = pa_difference(begin, end);
+
+ return fdt_add_mem_rsv(fdt, pa_addr(begin), len) == 0;
+}
+
+bool fdt_patch(struct mm_stage1_locked stage1_locked, paddr_t fdt_addr,
+ struct boot_params_update *p, struct mpool *ppool)
+{
+ void *fdt;
+ size_t buf_size;
+ int off;
+ bool ret = false;
+ bool rsv;
+ size_t i;
+
+ /* Map the fdt header in. */
+ fdt = mm_identity_map(stage1_locked, fdt_addr,
+ pa_add(fdt_addr, FDT_V17_HEADER_SIZE), MM_MODE_R,
+ ppool);
+ if (!fdt) {
+ dlog_error("Unable to map FDT header.\n");
+ return false;
+ }
+
+ if (fdt_check_header(fdt) != 0) {
+ dlog_error("FDT failed header validation.\n");
+ goto err_unmap_fdt_header;
+ }
+
+ /* Map the fdt (+ a page) in r/w mode in preparation for updating it. */
+ buf_size = fdt_totalsize(fdt) + PAGE_SIZE;
+ fdt = mm_identity_map(stage1_locked, fdt_addr,
+ pa_add(fdt_addr, buf_size), MM_MODE_R | MM_MODE_W,
+ ppool);
+ if (!fdt) {
+ dlog_error("Unable to map FDT in r/w mode.\n");
+ goto err_unmap_fdt_header;
+ }
+
+ if (fdt_check_full(fdt, buf_size) != 0) {
+ dlog_error("FDT failed validation.\n");
+ goto out_unmap_fdt;
+ }
+
+ off = fdt_path_offset(fdt, "/chosen");
+ if (off < 0) {
+ dlog_error("Unable to find FDT '/chosen' node.\n");
+ goto out_unmap_fdt;
+ }
+
+ /* Patch FDT to point to new ramdisk. */
+ if (!patch_uint(fdt, off, FDT_PROP_INITRD_START,
+ pa_addr(p->initrd_begin))) {
+ dlog_error("Unable to write" FDT_PROP_INITRD_START "\n");
+ goto out_unmap_fdt;
+ }
+
+ if (!patch_uint(fdt, off, FDT_PROP_INITRD_END,
+ pa_addr(p->initrd_end))) {
+ dlog_error("Unable to write " FDT_PROP_INITRD_END "\n");
+ goto out_unmap_fdt;
+ }
+
+ /*
+ * Patch FDT to reserve hypervisor memory so the primary VM doesn't try
+ * to use it.
+ */
+ rsv = true;
+ rsv &= add_mem_reservation(fdt, layout_text_begin(), layout_text_end());
+ rsv &= add_mem_reservation(fdt, layout_rodata_begin(),
+ layout_rodata_end());
+ rsv &= add_mem_reservation(fdt, layout_data_begin(), layout_data_end());
+
+ /* Patch FDT to reserve memory for secondary VMs. */
+ for (i = 0; i < p->reserved_ranges_count; ++i) {
+ struct mem_range range = p->reserved_ranges[i];
+
+ rsv &= add_mem_reservation(fdt, range.begin, range.end);
+ }
+
+ if (!rsv) {
+ dlog_error("Unable to add memory reservations to FDT.\n");
+ goto out_unmap_fdt;
+ }
+
+ ret = true;
+
+out_unmap_fdt:
+ /* Unmap FDT. */
+ if (!mm_unmap(stage1_locked, fdt_addr,
+ pa_add(fdt_addr, fdt_totalsize(fdt) + PAGE_SIZE),
+ ppool)) {
+ dlog_error("Unable to unmap writable FDT.\n");
+ return false;
+ }
+ return ret;
+
+err_unmap_fdt_header:
+ mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, FDT_V17_HEADER_SIZE),
+ ppool);
+ return false;
+}
diff --git a/src/fdt_test.cc b/src/fdt_test.cc
index 0ee534d..295821a 100644
--- a/src/fdt_test.cc
+++ b/src/fdt_test.cc
@@ -81,8 +81,10 @@
TEST(fdt, total_size)
{
- EXPECT_THAT(fdt_total_size((struct fdt_header *)&test_dtb[0]),
- Eq(sizeof(test_dtb)));
+ size_t size;
+
+ EXPECT_TRUE(fdt_size_from_header(&test_dtb[0], &size));
+ EXPECT_THAT(size, Eq(sizeof(test_dtb)));
}
} /* namespace */
diff --git a/src/init.c b/src/init.c
index 9f8f829..702235b 100644
--- a/src/init.c
+++ b/src/init.c
@@ -72,8 +72,7 @@
void one_time_init(void)
{
struct string manifest_fname = STRING_INIT("manifest.dtb");
- struct fdt_header *fdt;
- struct fdt_node fdt_root;
+ struct fdt fdt;
struct manifest manifest;
enum manifest_return_code manifest_ret;
struct boot_params params;
@@ -92,17 +91,12 @@
mm_stage1_locked = mm_lock_stage1();
- fdt = fdt_map(mm_stage1_locked, plat_boot_flow_get_fdt_addr(),
- &fdt_root, &ppool);
- if (fdt == NULL) {
+ if (!fdt_map(&fdt, mm_stage1_locked, plat_boot_flow_get_fdt_addr(),
+ &ppool)) {
panic("Unable to map FDT.");
}
- if (!fdt_find_child(&fdt_root, "")) {
- panic("Unable to find FDT root node.");
- }
-
- if (!boot_flow_get_params(¶ms, &fdt_root)) {
+ if (!boot_flow_get_params(¶ms, &fdt)) {
panic("Could not parse boot params.");
}
@@ -141,11 +135,11 @@
manifest_strerror(manifest_ret));
}
- if (!plat_iommu_init(&fdt_root, mm_stage1_locked, &ppool)) {
+ if (!plat_iommu_init(&fdt, mm_stage1_locked, &ppool)) {
panic("Could not initialize IOMMUs.");
}
- if (!fdt_unmap(mm_stage1_locked, fdt, &ppool)) {
+ if (!fdt_unmap(&fdt, mm_stage1_locked, &ppool)) {
panic("Unable to unmap FDT.");
}
diff --git a/src/iommu/absent.c b/src/iommu/absent.c
index 80f3883..aa2ebdc 100644
--- a/src/iommu/absent.c
+++ b/src/iommu/absent.c
@@ -16,10 +16,10 @@
#include "hf/plat/iommu.h"
-bool plat_iommu_init(const struct fdt_node *fdt_root,
+bool plat_iommu_init(const struct fdt *fdt,
struct mm_stage1_locked stage1_locked, struct mpool *ppool)
{
- (void)fdt_root;
+ (void)fdt;
(void)stage1_locked;
(void)ppool;
diff --git a/src/manifest.c b/src/manifest.c
index 7a8474b..f83c0f9 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -31,20 +31,39 @@
} \
} while (0)
-#define VM_NAME_BUF_SIZE (2 + 5 + 1) /* "vm" + number + null terminator */
-static_assert(MAX_VMS <= 99999, "Insufficient VM_NAME_BUF_SIZE");
-static_assert(HF_TEE_VM_ID > MAX_VMS,
+#define VM_ID_MAX (HF_VM_ID_OFFSET + MAX_VMS - 1)
+#define VM_ID_MAX_DIGITS (5)
+#define VM_NAME_EXTRA_CHARS (3) /* "vm" + number + '\0' */
+#define VM_NAME_MAX_SIZE (VM_ID_MAX_DIGITS + VM_NAME_EXTRA_CHARS)
+static_assert(VM_NAME_MAX_SIZE <= STRING_MAX_SIZE,
+ "VM name does not fit into a struct string.");
+static_assert(VM_ID_MAX <= 99999, "Insufficient VM_NAME_BUF_SIZE");
+static_assert(HF_TEE_VM_ID > VM_ID_MAX,
"TrustZone VM ID clashes with normal VM range.");
+static inline size_t count_digits(spci_vm_id_t vm_id)
+{
+ size_t digits = 0;
+
+ do {
+ digits++;
+ vm_id /= 10;
+ } while (vm_id);
+ return digits;
+}
+
/**
* Generates a string with the two letters "vm" followed by an integer.
* Assumes `buf` is of size VM_NAME_BUF_SIZE.
*/
-static const char *generate_vm_node_name(char *buf, spci_vm_id_t vm_id)
+static void generate_vm_node_name(struct string *str, spci_vm_id_t vm_id)
{
static const char *digits = "0123456789";
- char *ptr = buf + VM_NAME_BUF_SIZE;
+ size_t vm_id_digits = count_digits(vm_id);
+ char *base = str->data;
+ char *ptr = base + (VM_NAME_EXTRA_CHARS + vm_id_digits);
+ CHECK(vm_id_digits <= VM_ID_MAX_DIGITS);
*(--ptr) = '\0';
do {
*(--ptr) = digits[vm_id % 10];
@@ -52,8 +71,7 @@
} while (vm_id);
*(--ptr) = 'm';
*(--ptr) = 'v';
-
- return ptr;
+ CHECK(ptr == base);
}
/**
@@ -63,11 +81,10 @@
static enum manifest_return_code read_bool(const struct fdt_node *node,
const char *property, bool *out)
{
- const char *data;
- uint32_t size;
- bool present = fdt_read_property(node, property, &data, &size);
+ struct memiter data;
+ bool present = fdt_read_property(node, property, &data);
- if (present && size != 0) {
+ if (present && memiter_size(&data) != 0) {
return MANIFEST_ERROR_MALFORMED_BOOLEAN;
}
@@ -79,14 +96,13 @@
const char *property,
struct string *out)
{
- const char *data;
- uint32_t size;
+ struct memiter data;
- if (!fdt_read_property(node, property, &data, &size)) {
+ if (!fdt_read_property(node, property, &data)) {
return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
}
- switch (string_init(out, data, size)) {
+ switch (string_init(out, &data)) {
case STRING_SUCCESS:
return MANIFEST_SUCCESS;
case STRING_ERROR_INVALID_INPUT:
@@ -113,14 +129,13 @@
const char *property,
uint64_t *out)
{
- const char *data;
- uint32_t size;
+ struct memiter data;
- if (!fdt_read_property(node, property, &data, &size)) {
+ if (!fdt_read_property(node, property, &data)) {
return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
}
- if (!fdt_parse_number(data, size, out)) {
+ if (!fdt_parse_number(&data, memiter_size(&data), out)) {
return MANIFEST_ERROR_MALFORMED_INTEGER;
}
@@ -165,19 +180,18 @@
const struct fdt_node *node, const char *property,
struct uint32list_iter *out)
{
- const char *data;
- uint32_t size;
+ struct memiter data;
- if (!fdt_read_property(node, property, &data, &size)) {
+ if (!fdt_read_property(node, property, &data)) {
memiter_init(&out->mem_it, NULL, 0);
return MANIFEST_SUCCESS;
}
- if ((size % sizeof(uint32_t)) != 0) {
+ if ((memiter_size(&data) % sizeof(uint32_t)) != 0) {
return MANIFEST_ERROR_MALFORMED_INTEGER_LIST;
}
- memiter_init(&out->mem_it, data, size);
+ out->mem_it = data;
return MANIFEST_SUCCESS;
}
@@ -193,22 +207,27 @@
const char *property,
struct stringlist_iter *out)
{
- const char *data;
- uint32_t size;
+ struct memiter data;
+ const char *str;
+ size_t size;
- if (!fdt_read_property(node, property, &data, &size)) {
+ if (!fdt_read_property(node, property, &data)) {
return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
}
+ str = memiter_base(&data);
+ size = memiter_size(&data);
+
/*
* Require that the value ends with a NULL terminator. Other NULL
* characters separate the string list entries.
*/
- if (data[size - 1] != '\0') {
+ if ((size < 1) || (str[size - 1] != '\0')) {
return MANIFEST_ERROR_MALFORMED_STRING_LIST;
}
- memiter_init(&out->mem_it, data, size - 1);
+ CHECK(memiter_restrict(&data, 1));
+ out->mem_it = data;
return MANIFEST_SUCCESS;
}
@@ -220,16 +239,13 @@
static enum manifest_return_code uint32list_get_next(
struct uint32list_iter *list, uint32_t *out)
{
- const char *mem_base = memiter_base(&list->mem_it);
uint64_t num;
CHECK(uint32list_has_next(list));
-
- if (!fdt_parse_number(mem_base, sizeof(uint32_t), &num)) {
+ if (!fdt_parse_number(&list->mem_it, sizeof(uint32_t), &num)) {
return MANIFEST_ERROR_MALFORMED_INTEGER;
}
- memiter_advance(&list->mem_it, sizeof(uint32_t));
*out = (uint32_t)num;
return MANIFEST_SUCCESS;
}
@@ -284,7 +300,7 @@
return false;
}
-static enum manifest_return_code parse_vm(struct fdt_node *node,
+static enum manifest_return_code parse_vm(const struct fdt_node *node,
struct manifest_vm *vm,
spci_vm_id_t vm_id)
{
@@ -328,8 +344,8 @@
enum manifest_return_code manifest_init(struct manifest *manifest,
struct memiter *manifest_fdt)
{
- char vm_name_buf[VM_NAME_BUF_SIZE];
- const struct fdt_header *fdt;
+ struct string vm_name;
+ struct fdt fdt;
struct fdt_node hyp_node;
struct stringlist_iter compatible_list;
size_t i = 0;
@@ -337,19 +353,12 @@
memset_s(manifest, sizeof(*manifest), 0, sizeof(*manifest));
- fdt = (const struct fdt_header *)memiter_base(manifest_fdt);
- if (memiter_size(manifest_fdt) != fdt_total_size(fdt)) {
- return MANIFEST_ERROR_FILE_SIZE;
+ if (!fdt_init_from_memiter(&fdt, manifest_fdt)) {
+ return MANIFEST_ERROR_FILE_SIZE; /* TODO */
}
/* Find hypervisor node. */
- if (!fdt_root_node(&hyp_node, fdt)) {
- return MANIFEST_ERROR_NO_ROOT_NODE;
- }
- if (!fdt_find_child(&hyp_node, "")) {
- return MANIFEST_ERROR_NO_ROOT_NODE;
- }
- if (!fdt_find_child(&hyp_node, "hypervisor")) {
+ if (!fdt_find_node(&fdt, "/hypervisor", &hyp_node)) {
return MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE;
}
@@ -363,9 +372,9 @@
for (i = 0; i < HF_VM_ID_OFFSET; i++) {
spci_vm_id_t vm_id = (spci_vm_id_t)i;
struct fdt_node vm_node = hyp_node;
- const char *vm_name = generate_vm_node_name(vm_name_buf, vm_id);
- if (fdt_find_child(&vm_node, vm_name)) {
+ generate_vm_node_name(&vm_name, vm_id);
+ if (fdt_find_child(&vm_node, &vm_name)) {
return MANIFEST_ERROR_RESERVED_VM_ID;
}
}
@@ -374,9 +383,9 @@
for (i = 0; i <= MAX_VMS; ++i) {
spci_vm_id_t vm_id = HF_VM_ID_OFFSET + i;
struct fdt_node vm_node = hyp_node;
- const char *vm_name = generate_vm_node_name(vm_name_buf, vm_id);
- if (!fdt_find_child(&vm_node, vm_name)) {
+ generate_vm_node_name(&vm_name, vm_id);
+ if (!fdt_find_child(&vm_node, &vm_name)) {
break;
}
diff --git a/src/memiter.c b/src/memiter.c
index d72250c..577bc61 100644
--- a/src/memiter.c
+++ b/src/memiter.c
@@ -16,6 +16,7 @@
#include "hf/memiter.h"
+#include "hf/check.h"
#include "hf/dlog.h"
#include "hf/std.h"
@@ -136,7 +137,41 @@
}
it->next = p;
+ return true;
+}
+/**
+ * Decrements the limit of iterator by the given number of bytes. Returns true
+ * if the limit was reduced without going over the base; returns false and
+ * leaves the iterator unmodified otherwise.
+ */
+bool memiter_restrict(struct memiter *it, size_t v)
+{
+ size_t s = memiter_size(it);
+
+ if (v > s) {
+ return false;
+ }
+
+ it->limit = it->next + (s - v);
+ return true;
+}
+
+/**
+ * Initializes `newit` with the first `v` bytes of `it` and advances `it` by
+ * the same number of bytes. This splits the original range into two iterators
+ * after `v` bytes.
+ * Returns true on success; returns false and leaves `it` unmodified and `newit`
+ * uninitialized otherwise.
+ */
+bool memiter_consume(struct memiter *it, size_t v, struct memiter *newit)
+{
+ if (v > memiter_size(it)) {
+ return false;
+ }
+
+ memiter_init(newit, memiter_base(it), v);
+ CHECK(memiter_advance(it, v));
return true;
}
diff --git a/src/string.c b/src/string.c
index 4876a3f..f64d87c 100644
--- a/src/string.c
+++ b/src/string.c
@@ -30,14 +30,17 @@
* The constructor checks that it fits into the internal buffer and copies
* the string there.
*/
-enum string_return_code string_init(struct string *str, const char *data,
- size_t size)
+enum string_return_code string_init(struct string *str,
+ const struct memiter *data)
{
+ const char *base = memiter_base(data);
+ size_t size = memiter_size(data);
+
/*
* Require that the value contains exactly one NULL character and that
* it is the last byte.
*/
- if (size < 1 || memchr(data, '\0', size) != &data[size - 1]) {
+ if (size < 1 || memchr(base, '\0', size) != &base[size - 1]) {
return STRING_ERROR_INVALID_INPUT;
}
@@ -45,7 +48,7 @@
return STRING_ERROR_TOO_LONG;
}
- memcpy_s(str->data, sizeof(str->data), data, size);
+ memcpy_s(str->data, sizeof(str->data), base, size);
return STRING_SUCCESS;
}
@@ -58,3 +61,16 @@
{
return str->data;
}
+
+/**
+ * Returns true if the iterator `data` contains string `str`.
+ * Only characters until the first null terminator are compared.
+ */
+bool string_eq(const struct string *str, const struct memiter *data)
+{
+ const char *base = memiter_base(data);
+ size_t len = memiter_size(data);
+
+ return (len <= sizeof(str->data)) &&
+ (strncmp(str->data, base, len) == 0);
+}
diff --git a/src/string_test.cc b/src/string_test.cc
index f814636..222f5d8 100644
--- a/src/string_test.cc
+++ b/src/string_test.cc
@@ -25,13 +25,15 @@
TEST(string, valid)
{
struct string str;
+ struct memiter it;
constexpr const char data[] = "test";
string_init_empty(&str);
ASSERT_TRUE(string_is_empty(&str));
ASSERT_STREQ(string_data(&str), "");
- ASSERT_EQ(string_init(&str, data, sizeof(data)), STRING_SUCCESS);
+ memiter_init(&it, data, sizeof(data));
+ ASSERT_EQ(string_init(&str, &it), STRING_SUCCESS);
ASSERT_FALSE(string_is_empty(&str));
ASSERT_STRNE(string_data(&str), "");
ASSERT_STREQ(string_data(&str), "test");
@@ -40,27 +42,31 @@
TEST(string, data_zero_size)
{
struct string str;
+ struct memiter it;
constexpr const char data[] = "test";
- ASSERT_EQ(string_init(&str, data, 0), STRING_ERROR_INVALID_INPUT);
+ memiter_init(&it, data, 0);
+ ASSERT_EQ(string_init(&str, &it), STRING_ERROR_INVALID_INPUT);
}
TEST(string, data_no_null_terminator)
{
struct string str;
+ struct memiter it;
constexpr const char data[] = {'t', 'e', 's', 't'};
- ASSERT_EQ(string_init(&str, data, sizeof(data)),
- STRING_ERROR_INVALID_INPUT);
+ memiter_init(&it, data, sizeof(data));
+ ASSERT_EQ(string_init(&str, &it), STRING_ERROR_INVALID_INPUT);
}
TEST(string, data_two_null_terminators)
{
struct string str;
+ struct memiter it;
constexpr const char data[] = {'\0', 't', 'e', 's', 't', '\0'};
- ASSERT_EQ(string_init(&str, data, sizeof(data)),
- STRING_ERROR_INVALID_INPUT);
+ memiter_init(&it, data, sizeof(data));
+ ASSERT_EQ(string_init(&str, &it), STRING_ERROR_INVALID_INPUT);
}
} /* namespace */
diff --git a/test/hftest/common.c b/test/hftest/common.c
index 877f1db..9a9d306 100644
--- a/test/hftest/common.c
+++ b/test/hftest/common.c
@@ -135,7 +135,7 @@
}
static void run_test(hftest_test_fn set_up, hftest_test_fn test,
- hftest_test_fn tear_down, const struct fdt_header *fdt)
+ hftest_test_fn tear_down, const struct fdt *fdt)
{
/* Prepare the context. */
struct hftest_context *ctx = hftest_get_context();
@@ -172,7 +172,7 @@
* Runs the given test case.
*/
void hftest_run(struct memiter suite_name, struct memiter test_name,
- const struct fdt_header *fdt)
+ const struct fdt *fdt)
{
size_t i;
hftest_test_fn suite_set_up = NULL;
@@ -247,8 +247,7 @@
uintptr_t hftest_get_cpu_id(size_t index)
{
struct boot_params params;
- struct fdt_node n;
- const struct fdt_header *fdt = hftest_get_context()->fdt;
+ const struct fdt *fdt = hftest_get_context()->fdt;
if (fdt == NULL) {
/*
@@ -259,13 +258,7 @@
}
/* Find physical CPU ID from FDT. */
- if (!fdt_root_node(&n, fdt)) {
- FAIL("FDT failed validation.");
- }
- if (!fdt_find_child(&n, "")) {
- FAIL("Unable to find FDT root node.");
- }
- fdt_find_cpus(&n, params.cpu_ids, ¶ms.cpu_count);
+ fdt_find_cpus(fdt, params.cpu_ids, ¶ms.cpu_count);
return params.cpu_ids[index];
}
diff --git a/test/hftest/ctrl_fdt.c b/test/hftest/ctrl_fdt.c
index 85fe1cb..eb47272 100644
--- a/test/hftest/ctrl_fdt.c
+++ b/test/hftest/ctrl_fdt.c
@@ -14,36 +14,28 @@
* limitations under the License.
*/
+#include "hf/check.h"
+
#include "test/hftest.h"
-bool hftest_ctrl_start(const struct fdt_header *fdt, struct memiter *cmd)
+bool hftest_ctrl_start(const struct fdt *fdt, struct memiter *cmd)
{
struct fdt_node n;
- const char *bootargs;
- uint32_t bootargs_size;
+ struct memiter bootargs;
- if (!fdt_root_node(&n, fdt)) {
- HFTEST_LOG("FDT failed validation.");
+ if (!fdt_find_node(fdt, "/chosen", &n)) {
+ HFTEST_LOG("Could not find '/chosen' node.");
return false;
}
- if (!fdt_find_child(&n, "")) {
- HFTEST_LOG("Unable to find root node in FDT.");
- return false;
- }
-
- if (!fdt_find_child(&n, "chosen")) {
- HFTEST_LOG("Unable to find 'chosen' node in FDT.");
- return false;
- }
-
- if (!fdt_read_property(&n, "bootargs", &bootargs, &bootargs_size)) {
+ if (!fdt_read_property(&n, "bootargs", &bootargs)) {
HFTEST_LOG("Unable to read bootargs.");
return false;
}
/* Remove null terminator. */
- memiter_init(cmd, bootargs, bootargs_size - 1);
+ CHECK(memiter_restrict(&bootargs, 1));
+ *cmd = bootargs;
return true;
}
diff --git a/test/hftest/ctrl_uart.c b/test/hftest/ctrl_uart.c
index 4bddf81..b72e29f 100644
--- a/test/hftest/ctrl_uart.c
+++ b/test/hftest/ctrl_uart.c
@@ -53,7 +53,7 @@
}
}
-bool hftest_ctrl_start(const struct fdt_header *fdt, struct memiter *cmd)
+bool hftest_ctrl_start(const struct fdt *fdt, struct memiter *cmd)
{
(void)fdt;
diff --git a/test/hftest/hftest_common.h b/test/hftest/hftest_common.h
index fba4a54..a4bbcbe 100644
--- a/test/hftest/hftest_common.h
+++ b/test/hftest/hftest_common.h
@@ -24,5 +24,5 @@
void hftest_json(void);
void hftest_run(struct memiter suite_name, struct memiter test_name,
- const struct fdt_header *fdt);
+ const struct fdt *fdt);
void hftest_help(void);
diff --git a/test/hftest/standalone_main.c b/test/hftest/standalone_main.c
index 8f0cec9..c7c008d 100644
--- a/test/hftest/standalone_main.c
+++ b/test/hftest/standalone_main.c
@@ -29,8 +29,10 @@
extern struct hftest_test hftest_begin[];
extern struct hftest_test hftest_end[];
-void kmain(const struct fdt_header *fdt)
+void kmain(const void *fdt_ptr)
{
+ struct fdt fdt;
+ size_t fdt_len;
struct memiter command_line;
struct memiter command;
@@ -50,7 +52,13 @@
hftest_use_list(hftest_begin, hftest_end - hftest_begin);
- if (!hftest_ctrl_start(fdt, &command_line)) {
+ if (!fdt_size_from_header(fdt_ptr, &fdt_len) ||
+ !fdt_init_from_ptr(&fdt, fdt_ptr, fdt_len)) {
+ HFTEST_LOG("Unable to init FDT.");
+ goto out;
+ }
+
+ if (!hftest_ctrl_start(&fdt, &command_line)) {
HFTEST_LOG("Unable to read the command line.");
goto out;
}
@@ -83,7 +91,7 @@
HFTEST_LOG("Unable to parse test.");
goto out;
}
- hftest_run(suite_name, test_name, fdt);
+ hftest_run(suite_name, test_name, &fdt);
goto out;
}
diff --git a/test/inc/test/hftest.h b/test/inc/test/hftest.h
index d4779d0..af25041 100644
--- a/test/inc/test/hftest.h
+++ b/test/inc/test/hftest.h
@@ -119,7 +119,7 @@
* Inform a host that this is the start of a test run and obtain the command
* line arguments for it.
*/
-bool hftest_ctrl_start(const struct fdt_header *fdt, struct memiter *cmd);
+bool hftest_ctrl_start(const struct fdt *fdt, struct memiter *cmd);
/** Inform a host that this test run has finished and clean up. */
void hftest_ctrl_finish(void);
diff --git a/test/inc/test/hftest_impl.h b/test/inc/test/hftest_impl.h
index 2c997c8..55bb88c 100644
--- a/test/inc/test/hftest_impl.h
+++ b/test/inc/test/hftest_impl.h
@@ -138,7 +138,7 @@
noreturn void (*abort)(void);
/* These are used in primary VMs. */
- const struct fdt_header *fdt;
+ const struct fdt *fdt;
/* These are used in services. */
void *send;