Build: support kernels for VMs.

This shares some common code with the hypervisor to build a kernel that
can boot in a VM.

The basic entry code to set up the loaded image is common and image
specific entry starts at `image_entry`. The UART driver and debug
printing are also shared.

Change-Id: Ic0003d58bd2044add716212247ddeb38c07d2b4c
diff --git a/BUILD.gn b/BUILD.gn
index 8b03250..6d5dae0 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1,6 +1,15 @@
-import("//build/image/hypervisor.gni")
+import("//build/image/image.gni")
 
 group("arch_images") {
+  testonly = true
+
+  deps = [
+    ":hypervisor",
+    "//test/vm:test_vm($arch_toolchain)",
+  ]
+}
+
+group("hypervisor") {
   deps = [
     ":hafnium($arch_toolchain)",
   ]
diff --git a/Makefile b/Makefile
index 3847c07..cb70b7d 100644
--- a/Makefile
+++ b/Makefile
@@ -37,6 +37,7 @@
 format:
 	@find src/ -name *.c -o -name *.h | xargs clang-format -style file -i
 	@find inc/ -name *.c -o -name *.h | xargs clang-format -style file -i
+	@find test/ -name *.c -o -name *.h | xargs clang-format -style file -i
 	@find . -name *.gn -o -name *.gni -exec $(GN) format {} \;
 
 # TODO: get this working again. Need to extract a compile database to get the correct args.
diff --git a/build/arch/aarch64/hikey.args b/build/arch/aarch64/hikey.args
index 68261e1..dfdf0d6 100644
--- a/build/arch/aarch64/hikey.args
+++ b/build/arch/aarch64/hikey.args
@@ -1,5 +1,5 @@
 arch_cpu = "cortex-a57+nofp"
-arch_hypervisor_load_address = "0x01000000"
+arch_hypervisor_origin_address = "0x01000000"
 
 arch_aarch64_use_pl011 = true
 arch_aarch64_pl011_base_address = "0xf8015000"
diff --git a/build/arch/aarch64/qemu.args b/build/arch/aarch64/qemu.args
index 16d47f2..a822859 100644
--- a/build/arch/aarch64/qemu.args
+++ b/build/arch/aarch64/qemu.args
@@ -1,5 +1,5 @@
 arch_cpu = "cortex-a57+nofp"
-arch_hypervisor_load_address = "0x40001000"
+arch_hypervisor_origin_address = "0x40001000"
 
 arch_aarch64_use_pl011 = true
 arch_aarch64_pl011_base_address = "0x09000000"
diff --git a/build/arch/common.gni b/build/arch/common.gni
index 0eb4dc2..e4d3378 100644
--- a/build/arch/common.gni
+++ b/build/arch/common.gni
@@ -2,11 +2,11 @@
   # The specific CPU that runs the architecture.
   arch_cpu = ""
 
-  # The load address of the hypervisor
-  arch_hypervisor_load_address = ""
+  # The origin address of the hypervisor image
+  arch_hypervisor_origin_address = ""
 }
 
 assert(arch_cpu != "", "Must provide the CPU to build for as \"arch_cpu\".")
 assert(
-    arch_hypervisor_load_address != "",
-    "Must provide the load address of the hypervisor \"arch_hypervisor_load_address\".")
+    arch_hypervisor_origin_address != "",
+    "Must provide the origin address of the hypervisor image \"arch_hypervisor_origin_address\".")
diff --git a/build/image/generate_offsets.gni b/build/image/generate_offsets.gni
index 61b77f4..7116f87 100644
--- a/build/image/generate_offsets.gni
+++ b/build/image/generate_offsets.gni
@@ -19,7 +19,7 @@
                              "deps",
                              "public_deps",
                              "sources",
-                             "test_only",
+                             "testonly",
                            ])
     defines = [ "GEN_OFFSETS" ]
     visibility = [ ":${header_target}" ]
@@ -30,15 +30,9 @@
   action_foreach("${header_target}") {
     forward_variables_from(invoker,
                            [
-                             "cflags",
-                             "cflags_c",
-                             "defines",
-                             "deps",
-                             "public_deps",
                              "sources",
-                             "test_only",
+                             "testonly",
                            ])
-
     script = "//build/image/extract_offsets.py"
     deps = [
       ":${obj_target}",
@@ -68,7 +62,7 @@
                              "deps",
                              "public_deps",
                              "sources",
-                             "test_only",
+                             "testonly",
                            ])
 
     deps = [
diff --git a/build/image/hypervisor.gni b/build/image/hypervisor.gni
deleted file mode 100644
index 2a2a02a..0000000
--- a/build/image/hypervisor.gni
+++ /dev/null
@@ -1,48 +0,0 @@
-import("//build/arch/common.gni")
-
-# Helper to build a hypervisor image
-template("hypervisor") {
-  # Link objects together
-  executable("${target_name}__elf") {
-    forward_variables_from(invoker,
-                           [
-                             "cflags",
-                             "cflags_c",
-                             "defines",
-                             "deps",
-                             "public_deps",
-                             "sources",
-                             "test_only",
-                           ])
-    output_name = "${invoker.target_name}.elf"
-    ldflags = [
-      "-pie",
-      "-T",
-      rebase_path("//build/image/hypervisor.ld"),
-      "--defsym=PREFERRED_LOAD_ADDRESS=${arch_hypervisor_load_address}",
-    ]
-    visibility = [ ":${invoker.target_name}" ]
-  }
-
-  action(target_name) {
-    file_root = rebase_path("${root_out_dir}/${target_name}")
-    elf_file = "${file_root}.elf"
-    bin_file = "${file_root}.bin"
-
-    script = "//build/image/convert_to_binary.py"
-    deps = [
-      ":${target_name}__elf",
-    ]
-    args = [
-      "--tool_prefix",
-      arch_tool_prefix,
-      "--input",
-      elf_file,
-      "--output",
-      bin_file,
-    ]
-    outputs = [
-      "${target_out_dir}/${target_name}.bin",
-    ]
-  }
-}
diff --git a/build/image/image.gni b/build/image/image.gni
new file mode 100644
index 0000000..afc4e79
--- /dev/null
+++ b/build/image/image.gni
@@ -0,0 +1,100 @@
+import("//build/arch/common.gni")
+
+template("image_binary") {
+  assert(defined(invoker.image_name),
+         "image_binary() must specify a \"image_name\" value")
+  assert(defined(invoker.origin_address),
+         "image_binary() must specify a \"origin_address\" value")
+
+  output_root = ""
+  if (defined(invoker.output_path)) {
+    output_root += "${invoker.output_path}/"
+  }
+  output_root += invoker.image_name
+
+  # Link objects together
+  executable("${target_name}__elf") {
+    forward_variables_from(invoker,
+                           [
+                             "cflags",
+                             "cflags_c",
+                             "defines",
+                             "deps",
+                             "public_deps",
+                             "sources",
+                             "testonly",
+                           ])
+    output_name = "${output_root}.elf"
+    inputs = [
+      rebase_path("//build/image/image.ld"),
+    ]
+    ldflags = [
+      "-pie",
+      "-T",
+      rebase_path("//build/image/image.ld"),
+      "--defsym=ORIGIN_ADDRESS=${invoker.origin_address}",
+    ]
+    visibility = [ ":${invoker.target_name}" ]
+  }
+
+  action(target_name) {
+    forward_variables_from(invoker, [ "testonly" ])
+
+    file_root = "${root_out_dir}/${output_root}"
+    elf_file = "${file_root}.elf"
+    bin_file = "${file_root}.bin"
+
+    script = "//build/image/convert_to_binary.py"
+    deps = [
+      ":${target_name}__elf",
+    ]
+    args = [
+      "--tool_prefix",
+      arch_tool_prefix,
+      "--input",
+      rebase_path(elf_file),
+      "--output",
+      rebase_path(bin_file),
+    ]
+    outputs = [
+      bin_file,
+    ]
+  }
+}
+
+# Helper to build a hypervisor image
+template("hypervisor") {
+  image_binary(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "cflags",
+                             "cflags_c",
+                             "defines",
+                             "deps",
+                             "public_deps",
+                             "sources",
+                             "testonly",
+                           ])
+    image_name = target_name
+    origin_address = arch_hypervisor_origin_address
+  }
+}
+
+# Helper to build a virtual machine kernel
+template("vm_kernel") {
+  image_binary(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "cflags",
+                             "cflags_c",
+                             "defines",
+                             "deps",
+                             "public_deps",
+                             "sources",
+                             "testonly",
+                           ])
+    output_path = "vm"
+    image_name = target_name
+    origin_address = "0x1000"
+  }
+}
diff --git a/build/image/hypervisor.ld b/build/image/image.ld
similarity index 85%
rename from build/image/hypervisor.ld
rename to build/image/image.ld
index 9949f03..c7f3c60 100644
--- a/build/image/hypervisor.ld
+++ b/build/image/image.ld
@@ -1,13 +1,13 @@
 ENTRY(entry)
 SECTIONS
 {
-	. = PREFERRED_LOAD_ADDRESS;
+	. = ORIGIN_ADDRESS;
 	_orig_base = ABSOLUTE(.);
 
 	text_begin = .;
 	.init : {
 		*(.init.entry)
-		*(.init)
+		*(.init.image)
 	}
 	.text : { *(.text) }
 	text_end = .;
@@ -28,7 +28,7 @@
 
 	/* The entry point code assumes that bss is 16-byte aligned. */
 	.bss ALIGN(16) : {
-		file_size = ABSOLUTE(. - PREFERRED_LOAD_ADDRESS);
+		file_size = ABSOLUTE(. - ORIGIN_ADDRESS);
 		bss_begin = .;
 		*(.bss COMMON)
 		. = ALIGN(16);
diff --git a/src/BUILD.gn b/src/BUILD.gn
index 51edfd3..be4a8ba 100644
--- a/src/BUILD.gn
+++ b/src/BUILD.gn
@@ -1,3 +1,4 @@
+# Hypervisor specific code.
 source_set("src") {
   sources = [
     "alloc.c",
@@ -10,13 +11,35 @@
     "main.c",
     "memiter.c",
     "mm.c",
-    "std.c",
     "vm.c",
   ]
 
+  deps = [
+    ":common",
+  ]
+
   if (is_debug) {
-    sources += [
-      "dlog.c",
+    deps += [
+      ":common_debug",
     ]
   }
 }
+
+# Code that is not specific to a certain image so can be shared.
+source_set("common") {
+  sources = [
+    "std.c",
+  ]
+}
+
+# Debug code that is not specific to a certain image so can be shared.
+source_set("common_debug") {
+  sources = [
+    "dlog.c",
+  ]
+
+  deps = [
+    ":common",
+    "//src/arch/${arch}:putchar",
+  ]
+}
diff --git a/src/arch/aarch64/BUILD.gn b/src/arch/aarch64/BUILD.gn
index 8506372..6426f40 100644
--- a/src/arch/aarch64/BUILD.gn
+++ b/src/arch/aarch64/BUILD.gn
@@ -1,29 +1,45 @@
 import("//build/arch/aarch64.gni")
 import("//build/image/generate_offsets.gni")
 
+# Hypervisor specific code.
 source_set("aarch64") {
   sources = [
-    "entry.S",
     "exceptions.S",
+    "hypervisor_entry.S",
+  ]
+
+  sources += [
     "handler.c",
     "mm.c",
     "offsets.c",
     "params.c",
   ]
 
-  if (arch_aarch64_use_pl011) {
-    sources += [
-      "pl011.c",
-    ]
-  }
-
   deps = [
+    ":entry",
     ":offsets",
   ]
 }
 
+# Calculate struct field offsets for hypervisor_entry.S.
 generate_offsets("offsets") {
   sources = [
     "offsets.c",
   ]
 }
+
+# Entry code to prepare the loaded image to be run.
+source_set("entry") {
+  sources = [
+    "entry.S",
+  ]
+}
+
+# aarch64 implementation of putchar for debugging.
+source_set("putchar") {
+  if (arch_aarch64_use_pl011) {
+    sources = [
+      "pl011.c",
+    ]
+  }
+}
diff --git a/src/arch/aarch64/entry.S b/src/arch/aarch64/entry.S
index f1ca71a..3cf7463 100644
--- a/src/arch/aarch64/entry.S
+++ b/src/arch/aarch64/entry.S
@@ -1,7 +1,10 @@
-#include "offsets.h"
-
 .section .init.entry, "ax"
 
+/**
+ * This is a generic entry point for an image. It carries out the operations
+ * required to prepare the loaded image to be run. Specifically, it performs
+ * relocations and zeroing of the bss section using registers x25 and above.
+ */
 .global entry
 entry:
 	b 0f
@@ -61,48 +64,6 @@
 	stp xzr, xzr, [x29], #16
 	b 3b
 
-4:	/* Save the FDT to a global variable. */
-	adrp x30, fdt_addr
-	add x30, x30, :lo12:fdt_addr
-	str x0, [x30]
+	/* Branch to the entry point for the specific image. */
+4:	b image_entry
 
-	/* Get pointer to first cpu. */
-	adrp x0, cpus
-	add x0, x0, :lo12:cpus
-
-
-.globl cpu_entry
-cpu_entry:
-	/* Disable interrupts. */
-	msr DAIFSet, #0xf
-
-	/* Save pointer to CPU struct for later reference. */
-	msr tpidr_el2, x0
-
-	/* Use SPx (instead of SP0). */
-	msr spsel, #1
-
-	/* Prepare the stack. */
-	ldr x30, [x0, #CPU_STACK_BOTTOM]
-	mov sp, x30
-
-	/* Configure exception handlers. */
-	adrp x30, vector_table_el2
-	add x30, x30, :lo12:vector_table_el2
-	msr vbar_el2, x30
-
-	/* Call into C code. */
-	bl cpu_main
-
-	/* Run the vcpu returned by cpu_main. */
-	b vcpu_restore_all_and_run
-
-	/* Loop forever waiting for interrupts. */
-5:	wfi
-	b 5b
-
-/* TODO: Move this elsewhere. */
-.globl smc
-smc:
-	SMC #0
-	ret
diff --git a/src/arch/aarch64/hypervisor_entry.S b/src/arch/aarch64/hypervisor_entry.S
new file mode 100644
index 0000000..7d0396a
--- /dev/null
+++ b/src/arch/aarch64/hypervisor_entry.S
@@ -0,0 +1,50 @@
+#include "offsets.h"
+
+.section .init.image, "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]
+
+	/* Get pointer to first cpu. */
+	adrp x0, cpus
+	add x0, x0, :lo12:cpus
+
+.globl cpu_entry
+cpu_entry:
+	/* Disable interrupts. */
+	msr DAIFSet, #0xf
+
+	/* Save pointer to CPU struct for later reference. */
+	msr tpidr_el2, x0
+
+	/* Use SPx (instead of SP0). */
+	msr spsel, #1
+
+	/* Prepare the stack. */
+	ldr x30, [x0, #CPU_STACK_BOTTOM]
+	mov sp, x30
+
+	/* Configure exception handlers. */
+	adrp x30, vector_table_el2
+	add x30, x30, :lo12:vector_table_el2
+	msr vbar_el2, x30
+
+	/* Call into C code. */
+	bl cpu_main
+
+	/* Run the vcpu returned by cpu_main. */
+	b vcpu_restore_all_and_run
+
+	/* Loop forever waiting for interrupts. */
+5:	wfi
+	b 5b
+
+/* TODO: Move this elsewhere. */
+.globl smc
+smc:
+	SMC #0
+	ret
diff --git a/test/vm/BUILD.gn b/test/vm/BUILD.gn
new file mode 100644
index 0000000..7216b67
--- /dev/null
+++ b/test/vm/BUILD.gn
@@ -0,0 +1,16 @@
+import("//build/image/image.gni")
+
+vm_kernel("test_vm") {
+  testonly = true
+
+  sources = [
+    "vm_entry.S",
+    "kmain.c",
+  ]
+
+  deps = [
+    "//src:common",
+    "//src:common_debug",
+    "//src/arch/${arch}:entry",
+  ]
+}
diff --git a/test/vm/kmain.c b/test/vm/kmain.c
new file mode 100644
index 0000000..4f7e301
--- /dev/null
+++ b/test/vm/kmain.c
@@ -0,0 +1,14 @@
+#include <stddef.h>
+#include <stdint.h>
+
+#include "dlog.h"
+
+uint8_t kstack[4096] __attribute__((aligned(4096)));
+
+void kmain(void)
+{
+	dlog("Here we go!\n");
+	while (1) {
+		/* Do nothing */
+	}
+}
diff --git a/test/vm/vm_entry.S b/test/vm/vm_entry.S
new file mode 100644
index 0000000..99ceb26
--- /dev/null
+++ b/test/vm/vm_entry.S
@@ -0,0 +1,10 @@
+.section .init.image, "ax"
+
+.global image_entry
+image_entry:
+    adr         x0, kstack + 4096
+    mov         sp, x0
+2:
+    bl          kmain
+3:
+    b           3b