Allow projects to define their own build.

To create a new project, add a directory under `project` and pass the
project name to `make`:

    mkdir project/my_project
    # Describe the build for the project.
    PROJECT=my_project make

Projects can be maintained separately from the main repository.

Change-Id: I33a9d7ca801e2fb3dd9795ece44577b3b565e913
diff --git a/.gitmodules b/.gitmodules
index aecbc7a..d3d6c84 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -5,6 +5,10 @@
 [submodule "driver/linux"]
 	path = driver/linux
 	url = sso://hafnium/hafnium/driver/linux
+[submodule "project/reference"]
+	path = project/reference
+	url = sso://hafnium/hafnium/project/reference
+	shallow = true
 [submodule "third_party/googletest"]
 	path = third_party/googletest
 	url = sso://hafnium/hafnium/third_party/googletest
diff --git a/BUILD.gn b/BUILD.gn
index 50f3d6c..1ca02e7 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -12,17 +12,21 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-group("all") {
+# The root of the build redirects to a project build file so each project can
+# select the artifacts it needs to build.
+
+# The root of the build.
+group("root") {
+  deps = [
+    "//project/${project}:root",
+  ]
+}
+
+# The root of the build for test artifacts.
+group("test_root") {
   testonly = true
 
   deps = [
-    "//src:hafnium(//build/toolchain:aem_v8a_fvp_clang)",
-    "//src:hafnium(//build/toolchain:hikey_clang)",
-    "//src:hafnium(//build/toolchain:qemu_aarch64_clang)",
-    "//src:unit_tests(//build/toolchain:host_fake_clang)",
-    "//test/vm:gicv3_test(//build/toolchain:qemu_aarch64_clang)",
-    "//test/vm:vm_tests(//build/toolchain:qemu_aarch64_clang)",
+    "//project/${project}:test_root",
   ]
-
-  # TODO: add a gcc-4.9 or above prebuilt to check the gcc build too?
 }
diff --git a/Makefile b/Makefile
index dd78f86..0b1851c 100644
--- a/Makefile
+++ b/Makefile
@@ -19,8 +19,9 @@
 NINJA ?= $(PREBUILTS)/ninja/ninja
 export PATH := $(PREBUILTS)/clang/bin:$(PATH)
 
-# Place builds for different architectures and platforms in different
-# directories.
+# Select the project to build.
+PROJECT ?= reference
+
 OUT ?= out
 OUT_DIR = out
 
@@ -29,7 +30,7 @@
 	@$(NINJA) -C $(OUT_DIR)
 
 $(OUT_DIR)/build.ninja:
-	@$(GN) --export-compile-commands gen $(OUT_DIR)
+	@$(GN) --export-compile-commands gen --args='project="$(PROJECT)"' $(OUT_DIR)
 
 .PHONY: clean
 clean:
@@ -46,6 +47,7 @@
 	@find src/ -name \*.c -o -name \*.cc -o -name \*.h | xargs clang-format -style file -i
 	@find inc/ -name \*.c -o -name \*.cc -o -name \*.h | xargs clang-format -style file -i
 	@find test/ -name \*.c -o -name \*.cc -o -name \*.h | xargs clang-format -style file -i
+	@find project/ -name \*.c -o -name \*.cc -o -name \*.h | xargs clang-format -style file -i
 	@find . \( -name \*.gn -o -name \*.gni \) | xargs -n1 $(GN) format
 
 # see .clang-tidy.
diff --git a/build/BUILD.gn b/build/BUILD.gn
index 06cf616..1c753ff 100644
--- a/build/BUILD.gn
+++ b/build/BUILD.gn
@@ -36,12 +36,12 @@
   include_dirs = [
     "//inc",
     "//inc/vmapi",
-    "//src/arch/${arch}/inc",
+    "//src/arch/${plat_arch}/inc",
   ]
 
   defines = [
-    "MAX_CPUS=${platform_max_cpus}",
-    "MAX_VMS=${platform_max_vms}",
+    "MAX_CPUS=${plat_max_cpus}",
+    "MAX_VMS=${plat_max_vms}",
   ]
 
   if (is_debug) {
diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn
index aca2f24..ce3069d 100644
--- a/build/BUILDCONFIG.gn
+++ b/build/BUILDCONFIG.gn
@@ -14,11 +14,16 @@
 
 # Configuration of the build toolchain.
 declare_args() {
+  # The name of the project being built.
+  project = "reference"
+
   # Enable extra debugging.
   is_debug = true
 
-  # The architecture to build the bare metal images for.
-  arch = "host"
+  # Whether to build against the platform for embedded images consisting of
+  # include paths and defines. This is also used for host targets that simulate
+  # an embedded image.
+  use_platform = false
 }
 
 # Check that we support the attempted build.
@@ -45,7 +50,7 @@
 _shared_binary_target_configs = [ "//build:compiler_defaults" ]
 
 # If it's not building a host utility, it's building against the platform so apply the configuration.
-if (arch != "host") {
+if (use_platform) {
   _shared_binary_target_configs += [ "//build:platform" ]
 }
 
diff --git a/build/image/image.ld b/build/image/image.ld
index cede756..5973488 100644
--- a/build/image/image.ld
+++ b/build/image/image.ld
@@ -1,4 +1,20 @@
 /*
+ * Copyright 2018 Google LLC
+ *
+ * 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.
+ */
+
+/*
  * Code will start running at this symbol which is places at the start of the
  * image.
  */
@@ -84,6 +100,29 @@
 	rodata_end = .;
 
 	/*
+	 * A platform may choose to link blobs such as the FDT or the initrd
+	 * into the image rather than having them loaded separately. These are
+	 * placed at the end of the image and will not be mapped automatically
+	 * on boot so they can be treated as if they were loaded as separate
+	 * blobs. They are page aligned so they can be mapped individually.
+	 *
+	 * TODO: remove this when the loader can reliably deliver both the
+	 * binary and a separate blob for the initrd.
+	 */
+	. = ALIGN(4096);
+	initrd_begin = .;
+	.initrd : {
+		KEEP(*(.plat.initrd))
+	}
+	initrd_end = .;
+	. = ALIGN(4096);
+	fdt_begin = .;
+	.fdt : {
+		KEEP(*(.plat.fdt))
+	}
+	fdt_end = .;
+
+	/*
 	 * Collect together the read-write data including .bss at the end which
 	 * will be zero'd by the entry code. This is page aligned so it can be
 	 * mapped as non-executable.
diff --git a/build/toolchain/BUILD.gn b/build/toolchain/BUILD.gn
index 344d30e..916c1c7 100644
--- a/build/toolchain/BUILD.gn
+++ b/build/toolchain/BUILD.gn
@@ -12,42 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import("//build/toolchain/embedded.gni")
 import("//build/toolchain/host.gni")
 
 host_toolchain("host") {
-  use_fake_arch = false
-}
-
-host_toolchain("host_fake") {
-  use_fake_arch = true
-  max_cpus = 4
-  max_vms = 6
-}
-
-aarch64_toolchain("qemu_aarch64") {
-  cpu = "cortex-a57+nofp"
-  origin_address = "0x40001000"
-  use_pl011 = true
-  pl011_base_address = "0x09000000"
-  max_cpus = 8
-  max_vms = 16
-}
-
-aarch64_toolchain("hikey") {
-  cpu = "cortex-a53+nofp"
-  origin_address = "0x01000000"
-  use_pl011 = true
-  pl011_base_address = "0xf8015000"
-  max_cpus = 8
-  max_vms = 16
-}
-
-aarch64_toolchain("aem_v8a_fvp") {
-  cpu = "cortex-a57+nofp"
-  origin_address = "0x88000000"
-  use_pl011 = true
-  pl011_base_address = "0x1c090000"
-  max_cpus = 8
-  max_vms = 16
+  use_platform = false
 }
diff --git a/build/toolchain/embedded.gni b/build/toolchain/embedded.gni
index b9a5cdd..290f163 100644
--- a/build/toolchain/embedded.gni
+++ b/build/toolchain/embedded.gni
@@ -185,9 +185,11 @@
     extra_cflags = cflags
     extra_ldflags = ldflags
     toolchain_args = {
-      platform_max_cpus = invoker.max_cpus
-      platform_max_vms = invoker.max_vms
-      arch = invoker.arch
+      use_platform = true
+      plat_name = invoker.target_name
+      plat_arch = invoker.arch
+      plat_max_cpus = invoker.max_cpus
+      plat_max_vms = invoker.max_vms
       if (defined(invoker.toolchain_args)) {
         forward_variables_from(invoker.toolchain_args, "*")
       }
@@ -200,9 +202,11 @@
     extra_cflags = cflags
     extra_ldflags = ldflags
     toolchain_args = {
-      platform_max_cpus = invoker.max_cpus
-      platform_max_vms = invoker.max_vms
-      arch = invoker.arch
+      use_platform = true
+      plat_name = invoker.target_name
+      plat_arch = invoker.arch
+      plat_max_cpus = invoker.max_cpus
+      plat_max_vms = invoker.max_vms
       if (defined(invoker.toolchain_args)) {
         forward_variables_from(invoker.toolchain_args, "*")
       }
diff --git a/build/toolchain/host.gni b/build/toolchain/host.gni
index 8b39a77..11bf388 100644
--- a/build/toolchain/host.gni
+++ b/build/toolchain/host.gni
@@ -123,9 +123,9 @@
 }
 
 template("host_toolchain") {
-  assert(defined(invoker.use_fake_arch),
-         "\"use_fake_arch\" must be defined for ${target_name}.")
-  if (invoker.use_fake_arch) {
+  assert(defined(invoker.use_platform),
+         "\"use_platform\" must be defined for ${target_name}.")
+  if (invoker.use_platform) {
     assert(defined(invoker.max_cpus),
            "\"max_cpus\" must be defined for ${target_name}.")
     assert(defined(invoker.max_vms),
@@ -141,14 +141,15 @@
     # TODO: remove the need for this
     extra_defines = "-DPL011_BASE=0"
 
-    if (invoker.use_fake_arch) {
+    if (invoker.use_platform) {
       toolchain_args = {
-        platform_max_cpus = invoker.max_cpus
-        platform_max_vms = invoker.max_vms
+        use_platform = true
 
         # When building for the ${target_name}, use the fake architecture to make things
         # testable.
-        arch = "fake"
+        plat_arch = "fake"
+        plat_max_cpus = invoker.max_cpus
+        plat_max_vms = invoker.max_vms
       }
     }
   }
@@ -162,14 +163,15 @@
     # TODO: remove the need for this
     extra_defines = "-DPL011_BASE=0"
 
-    if (invoker.use_fake_arch) {
+    if (invoker.use_platform) {
       toolchain_args = {
-        platform_max_cpus = invoker.max_cpus
-        platform_max_vms = invoker.max_vms
+        use_platform = true
 
         # When building for the ${target_name}, use the fake architecture to make things
         # testable.
-        arch = "fake"
+        plat_arch = "fake"
+        plat_max_cpus = invoker.max_cpus
+        plat_max_vms = invoker.max_vms
       }
     }
   }
diff --git a/build/toolchain/platform.gni b/build/toolchain/platform.gni
index 7a1c8a9..4bcfdc3 100644
--- a/build/toolchain/platform.gni
+++ b/build/toolchain/platform.gni
@@ -14,9 +14,15 @@
 
 # Configuration of the build for the platform.
 declare_args() {
+  # The name of the platform.
+  plat_name = ""
+
+  # The architecture of the platform.
+  plat_arch = ""
+
   # The maximum number of CPUs available on the platform.
-  platform_max_cpus = 0
+  plat_max_cpus = 0
 
   # The maximum number of VMs required for the platform.
-  platform_max_vms = 0
+  plat_max_vms = 0
 }
diff --git a/inc/hf/fdt_handler.h b/inc/hf/fdt_handler.h
index 6d43d3f..531290b 100644
--- a/inc/hf/fdt_handler.h
+++ b/inc/hf/fdt_handler.h
@@ -20,5 +20,10 @@
 #include "hf/fdt.h"
 #include "hf/mm.h"
 
-bool fdt_get_boot_params(paddr_t fdt_addr, struct boot_params *p);
+struct fdt_header *fdt_map(paddr_t fdt_addr, struct fdt_node *n);
+bool fdt_unmap(struct fdt_header *fdt);
+void fdt_find_memory_ranges(const struct fdt_node *root, struct boot_params *p);
+bool fdt_find_initrd(struct fdt_node *n, paddr_t *begin, paddr_t *end);
+
+/** Apply an update to the FDT. */
 bool fdt_patch(paddr_t fdt_addr, struct boot_params_update *p);
diff --git a/inc/hf/layout.h b/inc/hf/layout.h
index 4c40223..9eabf4d 100644
--- a/inc/hf/layout.h
+++ b/inc/hf/layout.h
@@ -27,6 +27,12 @@
 paddr_t layout_data_begin(void);
 paddr_t layout_data_end(void);
 
+paddr_t layout_initrd_begin(void);
+paddr_t layout_initrd_end(void);
+
+paddr_t layout_fdt_begin(void);
+paddr_t layout_fdt_end(void);
+
 paddr_t layout_bin_end(void);
 
 paddr_t layout_primary_begin(void);
diff --git a/project/reference b/project/reference
new file mode 160000
index 0000000..2d527c2
--- /dev/null
+++ b/project/reference
@@ -0,0 +1 @@
+Subproject commit 2d527c2ecd4a071621db77275cb4bb795f3076b5
diff --git a/src/BUILD.gn b/src/BUILD.gn
index d3d1a34..83621a5 100644
--- a/src/BUILD.gn
+++ b/src/BUILD.gn
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import("//build/image/image.gni")
+import("//build/toolchain/platform.gni")
 
 # The hypervisor image.
 hypervisor("hafnium") {
@@ -31,9 +32,11 @@
     "cpio.c",
     "load.c",
     "main.c",
+    "plat.c",
   ]
   deps = [
     ":src_testable",
+    "//project/${project}/${plat_name}",
   ]
 }
 
@@ -55,7 +58,7 @@
     ":common",
     ":fdt",
     ":memiter",
-    "//src/arch/${arch}",
+    "//src/arch/${plat_arch}",
   ]
 
   if (is_debug) {
@@ -78,7 +81,7 @@
 
   deps = [
     ":common",
-    "//src/arch/${arch}:putchar",
+    "//src/arch/${plat_arch}:putchar",
   ]
 }
 
diff --git a/src/arch/aarch64/BUILD.gn b/src/arch/aarch64/BUILD.gn
index 1588e18..8909fb8 100644
--- a/src/arch/aarch64/BUILD.gn
+++ b/src/arch/aarch64/BUILD.gn
@@ -20,6 +20,7 @@
     "cpu_entry.S",
     "exceptions.S",
     "hypervisor_entry.S",
+    "plat_entry.S",
     "smc.S",
   ]
 
@@ -27,7 +28,6 @@
     "handler.c",
     "mm.c",
     "offsets.c",
-    "params.c",
   ]
 
   deps = [
diff --git a/src/arch/aarch64/hypervisor_entry.S b/src/arch/aarch64/hypervisor_entry.S
index 39061d8..468c684 100644
--- a/src/arch/aarch64/hypervisor_entry.S
+++ b/src/arch/aarch64/hypervisor_entry.S
@@ -17,10 +17,8 @@
 .section .init.image_entry, "ax"
 .global image_entry
 image_entry:
-	/* Save the FDT to a global variable. */
-	adrp x30, fdt_addr
-	add x30, x30, :lo12:fdt_addr
-	str x0, [x30]
+	/* Interpret the registers passed from the loader. */
+	bl plat_entry
 
 	/* Get pointer to first cpu. */
 	adrp x0, cpus
diff --git a/src/arch/aarch64/params.c b/src/arch/aarch64/params.c
deleted file mode 100644
index ab1400f..0000000
--- a/src/arch/aarch64/params.c
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2018 Google LLC
- *
- * 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/fdt_handler.h"
-
-/* This is set by entry.S. */
-uintpaddr_t fdt_addr;
-
-/*
- * The following are declared weak so that they can overwritten by platform code
- * if desired.
- */
-#pragma weak plat_get_boot_params
-bool plat_get_boot_params(struct boot_params *p)
-{
-	return fdt_get_boot_params(pa_init(fdt_addr), p);
-}
-
-#pragma weak plat_update_boot_params
-bool plat_update_boot_params(struct boot_params_update *p)
-{
-	return fdt_patch(pa_init(fdt_addr), p);
-}
diff --git a/src/arch/aarch64/plat_entry.S b/src/arch/aarch64/plat_entry.S
new file mode 100644
index 0000000..75d7dc4
--- /dev/null
+++ b/src/arch/aarch64/plat_entry.S
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * 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.image_entry, "ax"
+.global plat_entry
+.weak plat_entry
+plat_entry:
+	/* Do nothing. */
+	ret
diff --git a/src/fdt_handler.c b/src/fdt_handler.c
index c29a0e2..cba2a4e 100644
--- a/src/fdt_handler.c
+++ b/src/fdt_handler.c
@@ -99,34 +99,33 @@
  * Finds the memory region where initrd is stored, and updates the fdt node
  * cursor to the node called "chosen".
  */
-static bool find_initrd(struct fdt_node *n, struct boot_params *p)
+bool fdt_find_initrd(struct fdt_node *n, paddr_t *begin, paddr_t *end)
 {
-	uint64_t begin;
-	uint64_t end;
+	uint64_t initrd_begin;
+	uint64_t initrd_end;
 
 	if (!fdt_find_child(n, "chosen")) {
 		dlog("Unable to find 'chosen'\n");
 		return false;
 	}
 
-	if (!fdt_read_number(n, "linux,initrd-start", &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", &end)) {
+	if (!fdt_read_number(n, "linux,initrd-end", &initrd_end)) {
 		dlog("Unable to read linux,initrd-end\n");
 		return false;
 	}
 
-	p->initrd_begin = pa_init(begin);
-	p->initrd_end = pa_init(end);
+	*begin = pa_init(initrd_begin);
+	*end = pa_init(initrd_end);
 
 	return true;
 }
 
-static void find_memory_ranges(const struct fdt_node *root,
-			       struct boot_params *p)
+void fdt_find_memory_ranges(const struct fdt_node *root, struct boot_params *p)
 {
 	struct fdt_node n = *root;
 	const char *name;
@@ -193,23 +192,21 @@
 	/* TODO: Check for "reserved-memory" nodes. */
 }
 
-bool fdt_get_boot_params(paddr_t fdt_addr, struct boot_params *p)
+struct fdt_header *fdt_map(paddr_t fdt_addr, struct fdt_node *n)
 {
 	struct fdt_header *fdt;
-	struct fdt_node n;
-	bool ret = false;
 
 	/* Map the fdt header in. */
 	fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_header_size()),
 			      MM_MODE_R);
 	if (!fdt) {
 		dlog("Unable to map FDT header.\n");
-		goto err_unmap_fdt_header;
+		return NULL;
 	}
 
-	if (!fdt_root_node(&n, fdt)) {
+	if (!fdt_root_node(n, fdt)) {
 		dlog("FDT failed validation.\n");
-		goto err_unmap_fdt_header;
+		goto fail;
 	}
 
 	/* Map the rest of the fdt in. */
@@ -217,31 +214,20 @@
 			      MM_MODE_R);
 	if (!fdt) {
 		dlog("Unable to map full FDT.\n");
-		goto err_unmap_fdt_header;
+		goto fail;
 	}
 
-	if (!fdt_find_child(&n, "")) {
-		dlog("Unable to find FDT root node.\n");
-		goto out_unmap_fdt;
-	}
+	return fdt;
 
-	p->mem_ranges_count = 0;
-	find_memory_ranges(&n, p);
-
-	if (!find_initrd(&n, p)) {
-		goto out_unmap_fdt;
-	}
-
-	p->kernel_arg = (uintreg_t)fdt;
-	ret = true;
-
-out_unmap_fdt:
-	mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_total_size(fdt)), 0);
-	return ret;
-
-err_unmap_fdt_header:
+fail:
 	mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_header_size()), 0);
-	return false;
+	return NULL;
+}
+
+bool fdt_unmap(struct fdt_header *fdt)
+{
+	paddr_t fdt_addr = pa_from_va(va_from_ptr(fdt));
+	return mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_total_size(fdt)), 0);
 }
 
 bool fdt_patch(paddr_t fdt_addr, struct boot_params_update *p)
diff --git a/src/fdt_handler_test.cc b/src/fdt_handler_test.cc
index 9556ede..c544454 100644
--- a/src/fdt_handler_test.cc
+++ b/src/fdt_handler_test.cc
@@ -28,6 +28,7 @@
 namespace
 {
 using ::testing::Eq;
+using ::testing::NotNull;
 
 constexpr size_t TEST_HEAP_SIZE = PAGE_SIZE * 10;
 
@@ -92,15 +93,22 @@
 	0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x2c,
 	0x69, 0x6e, 0x69, 0x74, 0x72, 0x64, 0x2d, 0x65, 0x6e, 0x64, 0x00};
 
-TEST(fdt, get_boot_params)
+TEST(fdt, find_memory_ranges)
 {
 	std::unique_ptr<uint8_t[]> test_heap(new uint8_t[TEST_HEAP_SIZE]);
 	halloc_init((size_t)test_heap.get(), TEST_HEAP_SIZE);
 	ASSERT_TRUE(mm_init());
 
+	struct fdt_header *fdt;
+	struct fdt_node n;
 	struct boot_params params = {};
-	EXPECT_TRUE(
-		fdt_get_boot_params(pa_init((uintpaddr_t)&test_dtb), &params));
+
+	fdt = fdt_map(pa_init((uintpaddr_t)&test_dtb), &n);
+	ASSERT_THAT(fdt, NotNull());
+	ASSERT_TRUE(fdt_find_child(&n, ""));
+	fdt_find_memory_ranges(&n, &params);
+	ASSERT_TRUE(fdt_unmap(fdt));
+
 	EXPECT_THAT(params.mem_ranges_count, Eq(3));
 	EXPECT_THAT(pa_addr(params.mem_ranges[0].begin), Eq(0x00000000));
 	EXPECT_THAT(pa_addr(params.mem_ranges[0].end), Eq(0x20000000));
diff --git a/src/layout.c b/src/layout.c
index 68b645e..003a8ab 100644
--- a/src/layout.c
+++ b/src/layout.c
@@ -71,6 +71,42 @@
 }
 
 /**
+ * Get the address the .initrd section begins at.
+ */
+paddr_t layout_initrd_begin(void)
+{
+	extern uint8_t initrd_begin[];
+	return pa_init((uintpaddr_t)initrd_begin);
+}
+
+/**
+ * Get the address the .initrd section ends at.
+ */
+paddr_t layout_initrd_end(void)
+{
+	extern uint8_t initrd_end[];
+	return pa_init((uintpaddr_t)initrd_end);
+}
+
+/**
+ * Get the address the .fdt section begins at.
+ */
+paddr_t layout_fdt_begin(void)
+{
+	extern uint8_t fdt_begin[];
+	return pa_init((uintpaddr_t)fdt_begin);
+}
+
+/**
+ * Get the address the .fdt section ends at.
+ */
+paddr_t layout_fdt_end(void)
+{
+	extern uint8_t fdt_end[];
+	return pa_init((uintpaddr_t)fdt_end);
+}
+
+/**
  * Get the address the loaded image ends at.
  */
 paddr_t layout_bin_end(void)
diff --git a/src/plat.c b/src/plat.c
new file mode 100644
index 0000000..c6c1018
--- /dev/null
+++ b/src/plat.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * 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(paddr_t *begin, paddr_t *end)
+{
+	*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 boot_params *p)
+{
+	struct fdt_header *fdt;
+	struct fdt_node n;
+	bool ret = false;
+
+	plat_get_initrd_range(&p->initrd_begin, &p->initrd_end);
+	p->kernel_arg = plat_get_kernel_arg();
+
+	/* Get the memory map from the FDT. */
+	fdt = fdt_map(plat_get_fdt_addr(), &n);
+	if (!fdt) {
+		return false;
+	}
+
+	if (!fdt_find_child(&n, "")) {
+		dlog("Unable to find FDT root node.\n");
+		goto out_unmap_fdt;
+	}
+
+	p->mem_ranges_count = 0;
+	fdt_find_memory_ranges(&n, p);
+
+	ret = true;
+
+out_unmap_fdt:
+	if (!fdt_unmap(fdt)) {
+		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 boot_params_update *p)
+{
+	return fdt_patch(plat_get_fdt_addr(), p);
+}
diff --git a/test/vm/BUILD.gn b/test/vm/BUILD.gn
index cd5ef2c..0ac7cde 100644
--- a/test/vm/BUILD.gn
+++ b/test/vm/BUILD.gn
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import("//build/image/image.gni")
+import("//build/toolchain/platform.gni")
 
 group("vm_tests") {
   testonly = true
@@ -37,10 +38,10 @@
     "//src:dlog",
     "//src:fdt",
     "//src:memiter",
-    "//src/arch/${arch}:entry",
-    "//src/arch/${arch}/vm:hf_call",
-    "//src/arch/${arch}/vm:shutdown",
-    "//src/arch/${arch}/vm:vm_entry",
+    "//src/arch/${plat_arch}:entry",
+    "//src/arch/${plat_arch}/vm:hf_call",
+    "//src/arch/${plat_arch}/vm:shutdown",
+    "//src/arch/${plat_arch}/vm:vm_entry",
   ]
 }
 
@@ -51,10 +52,10 @@
   deps = [
     "//src:common",
     "//src:dlog",
-    "//src/arch/${arch}:entry",
-    "//src/arch/${arch}/vm:hf_call",
-    "//src/arch/${arch}/vm:shutdown",
-    "//src/arch/${arch}/vm:vm_entry",
+    "//src/arch/${plat_arch}:entry",
+    "//src/arch/${plat_arch}/vm:hf_call",
+    "//src/arch/${plat_arch}/vm:shutdown",
+    "//src/arch/${plat_arch}/vm:vm_entry",
   ]
 }