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_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;
+}