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