Merge manifest into the board DT, add boot flow drivers

Hafnium currently supports two kinds of boot flow:
(a) Linux-like, where the board FDT is passed by the boot loader to
the OS kernel in first kernel arg (this is used by QEMU, FVP, RPi, ...),
(b) Android-like, where the FDT used by Hafnium is compiled into it and
the VMs have their own.

This used to be implemented using weak symbols and each board spec
overriding it with its own implementation. This patch introduces the
concept of boot-flow drivers in the main tree, and the corresponding
driver selected using a config option in board spec. Drivers for the two
boot-flows described above are implemented.

Simultaneously, the manifest is now read from the FDT available at
Hafnium init time. This required two notable changes:
(1) all values are copied into the manifest struct because FDT is
unmapped, modified and passed to the primary VM,
(2) manifest is now written as an overlay; QEMU and FVP test drivers
overlay it automatically.

Bug: 117551352
Change-Id: Ieae7fe4ef5b3047174ec0ad057e487660ccd5a03
diff --git a/src/BUILD.gn b/src/BUILD.gn
index f6f55dc..e87ac1b 100644
--- a/src/BUILD.gn
+++ b/src/BUILD.gn
@@ -27,14 +27,15 @@
 # src targets will merge!
 source_set("src_not_testable_yet") {
   sources = [
+    "boot_params.c",
     "cpio.c",
     "load.c",
     "main.c",
-    "plat.c",
   ]
   deps = [
     ":src_testable",
     "//project/${project}/${plat_name}",
+    plat_boot_flow,
     plat_console,
   ]
 }
@@ -61,6 +62,7 @@
     ":mm",
     ":std",
     "//src/arch/${plat_arch}/hypervisor",
+    plat_boot_flow,
     plat_console,
   ]
 
diff --git a/src/arch/aarch64/boot_flow/BUILD.gn b/src/arch/aarch64/boot_flow/BUILD.gn
new file mode 100644
index 0000000..d709a9e
--- /dev/null
+++ b/src/arch/aarch64/boot_flow/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2019 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.
+
+source_set("android") {
+  sources = [
+    "android.S",
+  ]
+}
+
+source_set("linux") {
+  sources = [
+    "linux.S",
+  ]
+}
diff --git a/src/arch/aarch64/boot_flow/android.S b/src/arch/aarch64/boot_flow/android.S
new file mode 100644
index 0000000..13f0530
--- /dev/null
+++ b/src/arch/aarch64/boot_flow/android.S
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2019 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.
+ */
+
+.section .init.plat_boot_flow_hook, "ax"
+.global plat_boot_flow_hook
+plat_boot_flow_hook:
+	/* Do nothing. */
+	ret
diff --git a/src/arch/aarch64/boot_flow/linux.S b/src/arch/aarch64/boot_flow/linux.S
new file mode 100644
index 0000000..581a656
--- /dev/null
+++ b/src/arch/aarch64/boot_flow/linux.S
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019 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.
+ */
+
+.section .init.plat_boot_flow_hook, "ax"
+.global plat_boot_flow_hook
+plat_boot_flow_hook:
+	/* Save the FDT pointer to a global variable. */
+	adrp x25, plat_fdt_addr
+	add x25, x25, :lo12:plat_fdt_addr
+	str x0, [x25]
+	ret
diff --git a/src/arch/aarch64/hypervisor/hypervisor_entry.S b/src/arch/aarch64/hypervisor/hypervisor_entry.S
index e797074..3ab25c2 100644
--- a/src/arch/aarch64/hypervisor/hypervisor_entry.S
+++ b/src/arch/aarch64/hypervisor/hypervisor_entry.S
@@ -20,7 +20,7 @@
 .global image_entry
 image_entry:
 	/* Interpret the registers passed from the loader. */
-	bl plat_entry
+	bl plat_boot_flow_hook
 
 	/* Get pointer to first cpu. */
 	adrp x0, cpus
diff --git a/src/arch/fake/BUILD.gn b/src/arch/fake/BUILD.gn
index 6eee2b8..a0c7219 100644
--- a/src/arch/fake/BUILD.gn
+++ b/src/arch/fake/BUILD.gn
@@ -19,6 +19,12 @@
   ]
 }
 
+# Empty implementation of platform boot flow.
+# Fake arch targets should not depend on the boot flow functions. Will fail to
+# compile if they do.
+source_set("boot_flow") {
+}
+
 # Fake implementation of putchar logs to the console.
 source_set("console") {
   sources = [
diff --git a/src/boot_flow/BUILD.gn b/src/boot_flow/BUILD.gn
new file mode 100644
index 0000000..9de339d
--- /dev/null
+++ b/src/boot_flow/BUILD.gn
@@ -0,0 +1,41 @@
+# Copyright 2019 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.
+
+import("//build/toolchain/platform.gni")
+
+source_set("common") {
+  sources = [
+    "common.c",
+  ]
+}
+
+source_set("android") {
+  sources = [
+    "android.c",
+  ]
+  deps = [
+    ":common",
+    "//src/arch/${plat_arch}/boot_flow:android",
+  ]
+}
+
+source_set("linux") {
+  sources = [
+    "linux.c",
+  ]
+  deps = [
+    ":common",
+    "//src/arch/${plat_arch}/boot_flow:linux",
+  ]
+}
diff --git a/src/boot_flow/android.c b/src/boot_flow/android.c
new file mode 100644
index 0000000..4917ef4
--- /dev/null
+++ b/src/boot_flow/android.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019 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/layout.h"
+#include "hf/plat/boot_flow.h"
+
+/**
+ * FDT was compiled into Hafnium. Return physical address of the `.plat.fdt`
+ * section of Hafnium image.
+ */
+paddr_t plat_get_fdt_addr(void)
+{
+	return layout_fdt_begin();
+}
+
+/**
+ * Android boot flow does not use kernel arguments. Pass zero.
+ */
+uintreg_t plat_get_kernel_arg(void)
+{
+	return 0;
+}
+
+/**
+ * Initrd was compiled into Hafnium. Return range of the '.plat.initrd' section.
+ */
+bool plat_get_initrd_range(const struct fdt_node *fdt_root, paddr_t *begin,
+			   paddr_t *end)
+{
+	(void)fdt_root;
+
+	*begin = layout_initrd_begin();
+	*end = layout_initrd_end();
+	return true;
+}
diff --git a/src/boot_flow/common.c b/src/boot_flow/common.c
new file mode 100644
index 0000000..d211df6
--- /dev/null
+++ b/src/boot_flow/common.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2019 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/boot_flow.h"
+#include "hf/dlog.h"
+#include "hf/fdt_handler.h"
+#include "hf/plat/boot_flow.h"
+
+/**
+ * Parses information from FDT needed to initialize Hafnium.
+ * FDT is mapped at the beginning and unmapped before exiting the function.
+ */
+bool boot_flow_init(struct mm_stage1_locked stage1_locked,
+		    struct manifest *manifest, struct boot_params *boot_params,
+		    struct mpool *ppool)
+{
+	bool ret = false;
+	struct fdt_header *fdt;
+	struct fdt_node fdt_root;
+	enum manifest_return_code manifest_ret;
+
+	/* Get the memory map from the FDT. */
+	fdt = fdt_map(stage1_locked, plat_get_fdt_addr(), &fdt_root, ppool);
+	if (fdt == NULL) {
+		dlog("Unable to map FDT.\n");
+		return false;
+	}
+
+	if (!fdt_find_child(&fdt_root, "")) {
+		dlog("Unable to find FDT root node.\n");
+		goto out_unmap_fdt;
+	}
+
+	manifest_ret = manifest_init(manifest, &fdt_root);
+	if (manifest_ret != MANIFEST_SUCCESS) {
+		dlog("Could not parse manifest: %s.\n",
+		     manifest_strerror(manifest_ret));
+		goto out_unmap_fdt;
+	}
+
+	if (!boot_params_init(boot_params, &fdt_root)) {
+		dlog("Could not parse boot params.\n");
+		goto out_unmap_fdt;
+	}
+
+	ret = true;
+
+out_unmap_fdt:
+	if (!fdt_unmap(stage1_locked, fdt, ppool)) {
+		dlog("Unable to unmap FDT.\n");
+		ret = false;
+	}
+
+	return ret;
+}
diff --git a/src/boot_flow/linux.c b/src/boot_flow/linux.c
new file mode 100644
index 0000000..52fcb8a
--- /dev/null
+++ b/src/boot_flow/linux.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019 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_handler.h"
+#include "hf/plat/boot_flow.h"
+
+/* Set by arch-specific boot-time hook. */
+uintreg_t plat_fdt_addr;
+
+/**
+ * Returns the physical address of board FDT. This was passed to Hafnium in the
+ * first kernel arg by the boot loader.
+ */
+paddr_t plat_get_fdt_addr(void)
+{
+	return pa_init((uintpaddr_t)plat_fdt_addr);
+}
+
+/**
+ * When handing over to the primary, give it the same FDT address that was given
+ * to Hafnium. The FDT may have been modified during Hafnium init.
+ */
+uintreg_t plat_get_kernel_arg(void)
+{
+	return plat_fdt_addr;
+}
+
+/**
+ * Load initrd range from the board FDT.
+ */
+bool plat_get_initrd_range(const struct fdt_node *fdt_root, paddr_t *begin,
+			   paddr_t *end)
+{
+	return fdt_find_initrd(fdt_root, begin, end);
+}
diff --git a/src/boot_params.c b/src/boot_params.c
new file mode 100644
index 0000000..8fbd77d
--- /dev/null
+++ b/src/boot_params.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 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/boot_params.h"
+
+#include "hf/dlog.h"
+#include "hf/fdt_handler.h"
+#include "hf/layout.h"
+#include "hf/manifest.h"
+#include "hf/plat/boot_flow.h"
+
+/**
+ * Extract the boot parameters from the FDT and the boot-flow driver.
+ */
+bool boot_params_init(struct boot_params *p, const struct fdt_node *fdt_root)
+{
+	p->mem_ranges_count = 0;
+	p->kernel_arg = plat_get_kernel_arg();
+
+	return plat_get_initrd_range(fdt_root, &p->initrd_begin,
+				     &p->initrd_end) &&
+	       fdt_find_cpus(fdt_root, p->cpu_ids, &p->cpu_count) &&
+	       fdt_find_memory_ranges(fdt_root, p);
+}
+
+/**
+ * Updates the FDT before being passed to the primary VM's kernel.
+ *
+ * TODO: in future, each VM will declare whether it expects an argument passed
+ * and that will be static data e.g. it will provide its own FDT so there will
+ * be no FDT modification. This is done because each VM has a very different
+ * view of the system and we don't want to force VMs to require loader code when
+ * another loader can load the data for it.
+ */
+bool boot_params_patch_fdt(struct mm_stage1_locked stage1_locked,
+			   struct boot_params_update *p, struct mpool *ppool)
+{
+	return fdt_patch(stage1_locked, plat_get_fdt_addr(), p, ppool);
+}
diff --git a/src/fdt_handler.c b/src/fdt_handler.c
index 900f766..057a2ca 100644
--- a/src/fdt_handler.c
+++ b/src/fdt_handler.c
@@ -80,25 +80,25 @@
 }
 
 /**
- * Finds the memory region where initrd is stored, and updates the fdt node
- * cursor to the node called "chosen".
+ * Finds the memory region where initrd is stored.
  */
-bool fdt_find_initrd(struct fdt_node *n, paddr_t *begin, paddr_t *end)
+bool fdt_find_initrd(const struct fdt_node *root, paddr_t *begin, paddr_t *end)
 {
+	struct fdt_node n = *root;
 	uint64_t initrd_begin;
 	uint64_t initrd_end;
 
-	if (!fdt_find_child(n, "chosen")) {
+	if (!fdt_find_child(&n, "chosen")) {
 		dlog("Unable to find 'chosen'\n");
 		return false;
 	}
 
-	if (!fdt_read_number(n, "linux,initrd-start", &initrd_begin)) {
+	if (!fdt_read_number(&n, "linux,initrd-start", &initrd_begin)) {
 		dlog("Unable to read linux,initrd-start\n");
 		return false;
 	}
 
-	if (!fdt_read_number(n, "linux,initrd-end", &initrd_end)) {
+	if (!fdt_read_number(&n, "linux,initrd-end", &initrd_end)) {
 		dlog("Unable to read linux,initrd-end\n");
 		return false;
 	}
@@ -109,7 +109,7 @@
 	return true;
 }
 
-void fdt_find_cpus(const struct fdt_node *root, cpu_id_t *cpu_ids,
+bool fdt_find_cpus(const struct fdt_node *root, cpu_id_t *cpu_ids,
 		   size_t *cpu_count)
 {
 	struct fdt_node n = *root;
@@ -120,7 +120,7 @@
 
 	if (!fdt_find_child(&n, "cpus")) {
 		dlog("Unable to find 'cpus'\n");
-		return;
+		return false;
 	}
 
 	if (fdt_read_number(&n, "#address-cells", &address_size)) {
@@ -130,7 +130,7 @@
 	}
 
 	if (!fdt_first_child(&n, &name)) {
-		return;
+		return false;
 	}
 
 	do {
@@ -150,12 +150,12 @@
 
 			if (*cpu_count >= MAX_CPUS) {
 				dlog("Found more than %d CPUs\n", MAX_CPUS);
-				return;
+				return false;
 			}
 
 			if (!fdt_parse_number(data, address_size, &value)) {
 				dlog("Could not parse CPU id\n");
-				return;
+				return false;
 			}
 			cpu_ids[(*cpu_count)++] = value;
 
@@ -163,9 +163,11 @@
 			data += address_size;
 		}
 	} while (fdt_next_sibling(&n, &name));
+
+	return true;
 }
 
-void fdt_find_memory_ranges(const struct fdt_node *root, struct boot_params *p)
+bool fdt_find_memory_ranges(const struct fdt_node *root, struct boot_params *p)
 {
 	struct fdt_node n = *root;
 	const char *name;
@@ -191,7 +193,7 @@
 
 	/* Look for nodes with the device_type set to "memory". */
 	if (!fdt_first_child(&n, &name)) {
-		return;
+		return false;
 	}
 
 	do {
@@ -234,6 +236,8 @@
 	p->mem_ranges_count = mem_range_index;
 
 	/* TODO: Check for "reserved-memory" nodes. */
+
+	return true;
 }
 
 struct fdt_header *fdt_map(struct mm_stage1_locked stage1_locked,
diff --git a/src/load.c b/src/load.c
index 341f276..25e593c 100644
--- a/src/load.c
+++ b/src/load.c
@@ -22,7 +22,6 @@
 #include "hf/boot_params.h"
 #include "hf/dlog.h"
 #include "hf/layout.h"
-#include "hf/manifest.h"
 #include "hf/memiter.h"
 #include "hf/mm.h"
 #include "hf/plat/console.h"
@@ -251,16 +250,13 @@
  * Memory reserved for the VMs is added to the `reserved_ranges` of `update`.
  */
 bool load_secondary(struct mm_stage1_locked stage1_locked,
-		    const struct memiter *cpio,
+		    const struct manifest *manifest, const struct memiter *cpio,
 		    const struct boot_params *params,
 		    struct boot_params_update *update, struct mpool *ppool)
 {
 	struct vm *primary;
-	struct manifest manifest;
-	struct memiter manifest_fdt;
 	struct mem_range mem_ranges_available[MAX_MEM_RANGES];
 	size_t i;
-	enum manifest_return_code manifest_ret;
 
 	static_assert(
 		sizeof(mem_ranges_available) == sizeof(params->mem_ranges),
@@ -279,24 +275,13 @@
 			pa_addr(mem_ranges_available[i].end), PAGE_SIZE));
 	}
 
-	if (!find_file(cpio, "manifest.dtb", &manifest_fdt)) {
-		dlog("Could not find \"manifest.dtb\" in cpio.\n");
-		return false;
-	}
-
-	manifest_ret = manifest_init(&manifest, &manifest_fdt);
-	if (manifest_ret != MANIFEST_SUCCESS) {
-		dlog("Could not parse manifest: %s.\n",
-		     manifest_strerror(manifest_ret));
-		return false;
-	}
-
-	for (i = 0; i < manifest.num_vms; ++i) {
-		struct manifest_vm *manifest_vm = &manifest.vm[i];
+	for (i = 0; i < manifest->num_vms; ++i) {
+		const struct manifest_vm *manifest_vm = &manifest->vm[i];
 		spci_vm_id_t vm_id = HF_VM_ID_OFFSET + i;
 		struct vm *vm;
 		struct vcpu *vcpu;
 		struct memiter kernel;
+		struct memiter kernel_filename;
 		uint64_t mem_size;
 		paddr_t secondary_mem_begin;
 		paddr_t secondary_mem_end;
@@ -306,17 +291,16 @@
 			continue;
 		}
 
-		dlog("Loading VM%d: ", (int)vm_id);
-		memiter_dlog_str(&manifest_vm->debug_name);
-		dlog(".\n");
+		dlog("Loading VM%d: %s.\n", (int)vm_id,
+		     manifest_vm->debug_name);
 
-		if (!memiter_find_file(cpio,
-				       &manifest_vm->secondary.kernel_filename,
-				       &kernel)) {
-			dlog("Could not find kernel file \"");
-			memiter_dlog_str(
-				&manifest_vm->secondary.kernel_filename);
-			dlog("\".\n");
+		memiter_init(&kernel_filename,
+			     manifest_vm->secondary.kernel_filename,
+			     strnlen_s(manifest_vm->secondary.kernel_filename,
+				       MANIFEST_MAX_STRING_LENGTH));
+		if (!memiter_find_file(cpio, &kernel_filename, &kernel)) {
+			dlog("Could not find kernel file \"%s\".\n",
+			     manifest_vm->secondary.kernel_filename);
 			continue;
 		}
 
diff --git a/src/main.c b/src/main.c
index 3ccf6d6..4b2c5b3 100644
--- a/src/main.c
+++ b/src/main.c
@@ -20,6 +20,7 @@
 #include "hf/arch/init.h"
 
 #include "hf/api.h"
+#include "hf/boot_flow.h"
 #include "hf/boot_params.h"
 #include "hf/cpio.h"
 #include "hf/cpu.h"
@@ -43,6 +44,7 @@
  */
 static void one_time_init(void)
 {
+	struct manifest manifest;
 	struct boot_params params;
 	struct boot_params_update update;
 	struct memiter primary_initrd;
@@ -74,9 +76,7 @@
 
 	mm_stage1_locked = mm_lock_stage1();
 
-	if (!plat_get_boot_params(mm_stage1_locked, &params, &ppool)) {
-		panic("unable to retrieve boot params");
-	}
+	boot_flow_init(mm_stage1_locked, &manifest, &params, &ppool);
 
 	cpu_module_init(params.cpu_ids, params.cpu_count);
 
@@ -112,13 +112,13 @@
 	update.initrd_begin = pa_from_va(va_from_ptr(primary_initrd.next));
 	update.initrd_end = pa_from_va(va_from_ptr(primary_initrd.limit));
 	update.reserved_ranges_count = 0;
-	if (!load_secondary(mm_stage1_locked, &cpio, &params, &update,
-			    &ppool)) {
+	if (!load_secondary(mm_stage1_locked, &manifest, &cpio, &params,
+			    &update, &ppool)) {
 		panic("unable to load secondary VMs");
 	}
 
 	/* Prepare to run by updating bootparams as seen by primary VM. */
-	if (!plat_update_boot_params(mm_stage1_locked, &update, &ppool)) {
+	if (!boot_params_patch_fdt(mm_stage1_locked, &update, &ppool)) {
 		panic("plat_update_boot_params failed");
 	}
 
diff --git a/src/manifest.c b/src/manifest.c
index 4cf490a..fb2b695 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -54,8 +54,8 @@
 }
 
 static enum manifest_return_code read_string(const struct fdt_node *node,
-					     const char *property,
-					     struct memiter *out)
+					     const char *property, char *out,
+					     rsize_t out_sz)
 {
 	const char *data;
 	uint32_t size;
@@ -72,7 +72,12 @@
 		return MANIFEST_ERROR_MALFORMED_STRING;
 	}
 
-	memiter_init(out, data, size - 1);
+	/* Check that the string fits into the buffer. */
+	if (size > out_sz) {
+		return MANIFEST_ERROR_STRING_TOO_LONG;
+	}
+
+	memcpy_s(out, out_sz, data, size);
 	return MANIFEST_SUCCESS;
 }
 
@@ -195,10 +200,12 @@
 					  struct manifest_vm *vm,
 					  spci_vm_id_t vm_id)
 {
-	TRY(read_string(node, "debug_name", &vm->debug_name));
+	TRY(read_string(node, "debug_name", vm->debug_name,
+			sizeof(vm->debug_name)));
 	if (vm_id != HF_PRIMARY_VM_ID) {
 		TRY(read_string(node, "kernel_filename",
-				&vm->secondary.kernel_filename));
+				vm->secondary.kernel_filename,
+				sizeof(vm->secondary.kernel_filename)));
 		TRY(read_uint64(node, "mem_size", &vm->secondary.mem_size));
 		TRY(read_uint16(node, "vcpu_count", &vm->secondary.vcpu_count));
 	}
@@ -209,7 +216,7 @@
  * Parse manifest from FDT.
  */
 enum manifest_return_code manifest_init(struct manifest *manifest,
-					struct memiter *fdt)
+					const struct fdt_node *fdt_root)
 {
 	char vm_name_buf[VM_NAME_BUF_SIZE];
 	struct fdt_node hyp_node;
@@ -220,13 +227,7 @@
 	memset_s(manifest, sizeof(*manifest), 0, sizeof(*manifest));
 
 	/* Find hypervisor node. */
-	if (!fdt_root_node(&hyp_node,
-			   (const struct fdt_header *)memiter_base(fdt))) {
-		return MANIFEST_ERROR_CORRUPTED_FDT;
-	}
-	if (!fdt_find_child(&hyp_node, "")) {
-		return MANIFEST_ERROR_NO_ROOT_FDT_NODE;
-	}
+	hyp_node = *fdt_root;
 	if (!fdt_find_child(&hyp_node, "hypervisor")) {
 		return MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE;
 	}
@@ -283,10 +284,6 @@
 	switch (ret_code) {
 	case MANIFEST_SUCCESS:
 		return "Success";
-	case MANIFEST_ERROR_CORRUPTED_FDT:
-		return "Manifest failed FDT validation";
-	case MANIFEST_ERROR_NO_ROOT_FDT_NODE:
-		return "Could not find root node of manifest";
 	case MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE:
 		return "Could not find \"hypervisor\" node in manifest";
 	case MANIFEST_ERROR_NOT_COMPATIBLE:
@@ -302,6 +299,8 @@
 		return "Property not found";
 	case MANIFEST_ERROR_MALFORMED_STRING:
 		return "Malformed string property";
+	case MANIFEST_ERROR_STRING_TOO_LONG:
+		return "String too long";
 	case MANIFEST_ERROR_MALFORMED_STRING_LIST:
 		return "Malformed string list property";
 	case MANIFEST_ERROR_MALFORMED_INTEGER:
diff --git a/src/manifest_test.cc b/src/manifest_test.cc
index 432b8f8..fcd111d 100644
--- a/src/manifest_test.cc
+++ b/src/manifest_test.cc
@@ -30,7 +30,7 @@
 using ::testing::NotNull;
 
 template <typename T>
-void exec(const char *program, char *const args[], const T &stdin,
+void exec(const char *program, const char *args[], const T &stdin,
 	  std::vector<char> *stdout)
 {
 	/* Create two pipes, one for stdin and one for stdout. */
@@ -93,7 +93,7 @@
 		close(parent_write_fd);
 
 		/* Execute the given program. */
-		execv(program, args);
+		execv(program, const_cast<char *const *>(args));
 	}
 }
 
@@ -122,13 +122,14 @@
 
 	std::vector<char> Build()
 	{
-		char *const dtc_args[] = {NULL};
+		const char *program = "./build/image/dtc.py";
+		const char *dtc_args[] = {program, "compile", NULL};
 		std::vector<char> dtc_stdout;
 
 		/* Finish root node. */
 		EndChild();
 
-		exec("./build/image/dtc.py", dtc_args, dts_.str(), &dtc_stdout);
+		exec(program, dtc_args, dts_.str(), &dtc_stdout);
 		return dtc_stdout;
 	}
 
@@ -207,21 +208,31 @@
 	std::stringstream dts_;
 };
 
+static bool get_fdt_root(const std::vector<char> &dtb,
+			 struct fdt_node *fdt_root)
+{
+	const struct fdt_header *fdt_header;
+
+	fdt_header = reinterpret_cast<const struct fdt_header *>(dtb.data());
+	return fdt_root_node(fdt_root, fdt_header) &&
+	       fdt_find_child(fdt_root, "");
+}
+
 TEST(manifest, no_hypervisor_node)
 {
 	struct manifest m;
-	struct memiter it;
+	struct fdt_node fdt_root;
 	std::vector<char> dtb = ManifestDtBuilder().Build();
 
-	memiter_init(&it, dtb.data(), dtb.size());
-	ASSERT_EQ(manifest_init(&m, &it),
+	ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
+	ASSERT_EQ(manifest_init(&m, &fdt_root),
 		  MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE);
 }
 
 TEST(manifest, no_compatible_property)
 {
 	struct manifest m;
-	struct memiter it;
+	struct fdt_node fdt_root;
 
 	/* clang-format off */
 	std::vector<char> dtb = ManifestDtBuilder()
@@ -230,14 +241,15 @@
 		.Build();
 	/* clang-format on */
 
-	memiter_init(&it, dtb.data(), dtb.size());
-	ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_PROPERTY_NOT_FOUND);
+	ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
+	ASSERT_EQ(manifest_init(&m, &fdt_root),
+		  MANIFEST_ERROR_PROPERTY_NOT_FOUND);
 }
 
 TEST(manifest, not_compatible)
 {
 	struct manifest m;
-	struct memiter it;
+	struct fdt_node fdt_root;
 
 	/* clang-format off */
 	std::vector<char> dtb = ManifestDtBuilder()
@@ -247,14 +259,14 @@
 		.Build();
 	/* clang-format on */
 
-	memiter_init(&it, dtb.data(), dtb.size());
-	ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_NOT_COMPATIBLE);
+	ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
+	ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_ERROR_NOT_COMPATIBLE);
 }
 
 TEST(manifest, compatible_one_of_many)
 {
 	struct manifest m;
-	struct memiter it;
+	struct fdt_node fdt_root;
 
 	/* clang-format off */
 	std::vector<char> dtb = ManifestDtBuilder()
@@ -267,14 +279,14 @@
 		.Build();
 	/* clang-format on */
 
-	memiter_init(&it, dtb.data(), dtb.size());
-	ASSERT_EQ(manifest_init(&m, &it), MANIFEST_SUCCESS);
+	ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
+	ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_SUCCESS);
 }
 
 TEST(manifest, no_vm_nodes)
 {
 	struct manifest m;
-	struct memiter it;
+	struct fdt_node fdt_root;
 
 	/* clang-format off */
 	std::vector<char> dtb = ManifestDtBuilder()
@@ -284,14 +296,48 @@
 		.Build();
 	/* clang-format on */
 
-	memiter_init(&it, dtb.data(), dtb.size());
-	ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_NO_PRIMARY_VM);
+	ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
+	ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_ERROR_NO_PRIMARY_VM);
+}
+
+static std::vector<char> gen_long_string_dtb(bool valid)
+{
+	const char last_valid[] = "1234567890123456789012345678901";
+	const char first_invalid[] = "12345678901234567890123456789012";
+	static_assert(sizeof(last_valid) == MANIFEST_MAX_STRING_LENGTH);
+	static_assert(sizeof(first_invalid) == MANIFEST_MAX_STRING_LENGTH + 1);
+
+	/* clang-format off */
+	return ManifestDtBuilder()
+		.StartChild("hypervisor")
+			.Compatible()
+			.StartChild("vm1")
+				.DebugName(valid ? last_valid : first_invalid)
+			.EndChild()
+		.EndChild()
+		.Build();
+	/* clang-format on */
+}
+
+TEST(manifest, long_string)
+{
+	struct manifest m;
+	struct fdt_node fdt_root;
+
+	std::vector<char> dtb_last_valid = gen_long_string_dtb(true);
+	std::vector<char> dtb_first_invalid = gen_long_string_dtb(false);
+
+	ASSERT_TRUE(get_fdt_root(dtb_last_valid, &fdt_root));
+	ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_SUCCESS);
+
+	ASSERT_TRUE(get_fdt_root(dtb_first_invalid, &fdt_root));
+	ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_ERROR_STRING_TOO_LONG);
 }
 
 TEST(manifest, reserved_vm_id)
 {
 	struct manifest m;
-	struct memiter it;
+	struct fdt_node fdt_root;
 
 	/* clang-format off */
 	std::vector<char> dtb = ManifestDtBuilder()
@@ -310,8 +356,8 @@
 		.Build();
 	/* clang-format on */
 
-	memiter_init(&it, dtb.data(), dtb.size());
-	ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_RESERVED_VM_ID);
+	ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
+	ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_ERROR_RESERVED_VM_ID);
 }
 
 static std::vector<char> gen_vcpu_count_limit_dtb(uint64_t vcpu_count)
@@ -337,25 +383,26 @@
 TEST(manifest, vcpu_count_limit)
 {
 	struct manifest m;
-	struct memiter it;
+	struct fdt_node fdt_root;
 	std::vector<char> dtb_last_valid = gen_vcpu_count_limit_dtb(UINT16_MAX);
 	std::vector<char> dtb_first_invalid =
 		gen_vcpu_count_limit_dtb(UINT16_MAX + 1);
 
-	memiter_init(&it, dtb_last_valid.data(), dtb_last_valid.size());
-	ASSERT_EQ(manifest_init(&m, &it), MANIFEST_SUCCESS);
+	ASSERT_TRUE(get_fdt_root(dtb_last_valid, &fdt_root));
+	ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_SUCCESS);
 	ASSERT_EQ(m.num_vms, 2);
 	ASSERT_EQ(m.vm[1].secondary.vcpu_count, UINT16_MAX);
 
-	memiter_init(&it, dtb_first_invalid.data(), dtb_first_invalid.size());
-	ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_INTEGER_OVERFLOW);
+	ASSERT_TRUE(get_fdt_root(dtb_first_invalid, &fdt_root));
+	ASSERT_EQ(manifest_init(&m, &fdt_root),
+		  MANIFEST_ERROR_INTEGER_OVERFLOW);
 }
 
 TEST(manifest, valid)
 {
 	struct manifest m;
 	struct manifest_vm *vm;
-	struct memiter it;
+	struct fdt_node fdt_root;
 
 	/* clang-format off */
 	std::vector<char> dtb = ManifestDtBuilder()
@@ -380,27 +427,25 @@
 		.Build();
 	/* clang-format on */
 
-	memiter_init(&it, dtb.data(), dtb.size());
+	ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
 
-	ASSERT_EQ(manifest_init(&m, &it), MANIFEST_SUCCESS);
+	ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_SUCCESS);
 	ASSERT_EQ(m.num_vms, 3);
 
 	vm = &m.vm[0];
-	ASSERT_TRUE(memiter_iseq(&vm->debug_name, "primary_vm"));
+	ASSERT_STREQ(vm->debug_name, "primary_vm");
 
 	vm = &m.vm[1];
-	ASSERT_TRUE(memiter_iseq(&vm->debug_name, "first_secondary_vm"));
+	ASSERT_STREQ(vm->debug_name, "first_secondary_vm");
 	ASSERT_EQ(vm->secondary.vcpu_count, 42);
 	ASSERT_EQ(vm->secondary.mem_size, 12345);
-	ASSERT_TRUE(
-		memiter_iseq(&vm->secondary.kernel_filename, "first_kernel"));
+	ASSERT_STREQ(vm->secondary.kernel_filename, "first_kernel");
 
 	vm = &m.vm[2];
-	ASSERT_TRUE(memiter_iseq(&vm->debug_name, "second_secondary_vm"));
+	ASSERT_STREQ(vm->debug_name, "second_secondary_vm");
 	ASSERT_EQ(vm->secondary.vcpu_count, 43);
 	ASSERT_EQ(vm->secondary.mem_size, 0x12345);
-	ASSERT_TRUE(
-		memiter_iseq(&vm->secondary.kernel_filename, "second_kernel"));
+	ASSERT_STREQ(vm->secondary.kernel_filename, "second_kernel");
 }
 
 } /* namespace */
diff --git a/src/memiter.c b/src/memiter.c
index 3331d37..d72250c 100644
--- a/src/memiter.c
+++ b/src/memiter.c
@@ -94,19 +94,6 @@
 }
 
 /**
- * Prints the contents of memory covered by the iterator to dlog. It does *not*
- * assume that the string is null-terminated.
- */
-void memiter_dlog_str(struct memiter *it)
-{
-	const char *p;
-
-	for (p = it->next; p < it->limit; ++p) {
-		dlog("%c", *p);
-	}
-}
-
-/**
  * Parses the next string that represents a 64-bit number.
  */
 bool memiter_parse_uint(struct memiter *it, uint64_t *value)
diff --git a/src/plat.c b/src/plat.c
deleted file mode 100644
index d6c5a4a..0000000
--- a/src/plat.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright 2018 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/boot_params.h"
-#include "hf/dlog.h"
-#include "hf/fdt_handler.h"
-#include "hf/layout.h"
-
-/**
- * Default implementation assumes the FDT has been linked into the image.
- *
- * This can be overridden e.g. to provide a fixed address or an address passed
- * by the loader.
- */
-#pragma weak plat_get_fdt_addr
-paddr_t plat_get_fdt_addr(void)
-{
-	return layout_fdt_begin();
-}
-
-/**
- * Default implementation assumes the initrd has been linked into the image.
- *
- * This can be overridden e.g. to provide a fixed address or an address passed
- * by the loader.
- */
-#pragma weak plat_get_initrd_range
-void plat_get_initrd_range(struct mm_stage1_locked stage1_locked,
-			   paddr_t *begin, paddr_t *end, struct mpool *ppool)
-{
-	(void)stage1_locked;
-	(void)ppool;
-
-	*begin = layout_initrd_begin();
-	*end = layout_initrd_end();
-}
-
-/**
- * Default implementation assumes the FDT address is passed to the kernel.
- *
- * TODO: make this part of the VM configuration as secondary VMs will also need
- * to take arguments.
- */
-#pragma weak plat_get_kernel_arg
-uintreg_t plat_get_kernel_arg(void)
-{
-	return (uintreg_t)pa_addr(plat_get_fdt_addr());
-}
-
-/**
- * Default implementation extracts the boot parameters from the FDT but the
- * initrd is provided separately.
- */
-#pragma weak plat_get_boot_params
-bool plat_get_boot_params(struct mm_stage1_locked stage1_locked,
-			  struct boot_params *p, struct mpool *ppool)
-{
-	struct fdt_header *fdt;
-	struct fdt_node n;
-	bool ret = false;
-
-	plat_get_initrd_range(stage1_locked, &p->initrd_begin, &p->initrd_end,
-			      ppool);
-	p->kernel_arg = plat_get_kernel_arg();
-
-	/* Get the memory map from the FDT. */
-	fdt = fdt_map(stage1_locked, plat_get_fdt_addr(), &n, ppool);
-	if (!fdt) {
-		return false;
-	}
-
-	if (!fdt_find_child(&n, "")) {
-		dlog("Unable to find FDT root node.\n");
-		goto out_unmap_fdt;
-	}
-
-	fdt_find_cpus(&n, p->cpu_ids, &p->cpu_count);
-
-	p->mem_ranges_count = 0;
-	fdt_find_memory_ranges(&n, p);
-
-	ret = true;
-
-out_unmap_fdt:
-	if (!fdt_unmap(stage1_locked, fdt, ppool)) {
-		dlog("Unable to unmap fdt.");
-		return false;
-	}
-
-	return ret;
-}
-
-/**
- * Default implementation updates the FDT which is the argument passed to the
- * primary VM's kernel.
- *
- * TODO: in future, each VM will declare whether it expects an argument passed
- * and that will be static data e.g. it will provide its own FDT so there will
- * be no FDT modification. This is done because each VM has a very different
- * view of the system and we don't want to force VMs to require loader code when
- * another loader can load the data for it.
- */
-#pragma weak plat_update_boot_params
-bool plat_update_boot_params(struct mm_stage1_locked stage1_locked,
-			     struct boot_params_update *p, struct mpool *ppool)
-{
-	return fdt_patch(stage1_locked, plat_get_fdt_addr(), p, ppool);
-}