Switch from Makefile to GN

GN makes it easier for us to have a modular build where we can define
tests and reuse code without having to hack on Makefiles. It also has
tools to analyze the build and comes with extensive documentation.

Change-Id: Ib28bc7b68d429e3c3193784c7a80d05ee35c6295
diff --git a/build/image/convert_to_binary.py b/build/image/convert_to_binary.py
new file mode 100644
index 0000000..7900cf2
--- /dev/null
+++ b/build/image/convert_to_binary.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+
+"""Convert a file to binary format.
+
+Calls objcopy to convert a file into raw binary format.
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+def Main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument("--tool_prefix", required=True)
+  parser.add_argument("--input", required=True)
+  parser.add_argument("--output", required=True)
+  args = parser.parse_args()
+  raw = subprocess.check_output([
+      "{}objcopy".format(args.tool_prefix),
+      "-O",
+      "binary",
+      args.input,
+      args.output])
+  return 0
+
+if __name__ == "__main__":
+  sys.exit(Main())
diff --git a/build/image/extract_offsets.py b/build/image/extract_offsets.py
new file mode 100644
index 0000000..cf4f5c7
--- /dev/null
+++ b/build/image/extract_offsets.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+
+"""Extract embedded offsets from an object file.
+
+We let the compiler calculate the offsets it is going to use and have those
+emitted into and object file. This is the next pass which extracts those offsets
+and stores them in a header file for the assembly to include and use.
+"""
+
+import argparse
+import os
+import re
+import subprocess
+import sys
+
+def Main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument("--tool_prefix", required=True)
+  parser.add_argument("--input", required=True)
+  parser.add_argument("--output", required=True)
+  args = parser.parse_args()
+  raw = subprocess.check_output([
+      "{}objdump".format(args.tool_prefix),
+      "--disassemble-all",
+      "--section",
+      ".rodata",
+      args.input])
+  lines = raw.split('\n')
+  with open(args.output, 'w') as header:
+    header.write('#pragma once\n\n')
+    for n in range(len(lines)):
+      # Find a defined offset
+      match = re.match('.+DEFINE_OFFSET__([^>]+)', lines[n])
+      if not match:
+        continue
+      name = match.group(1)
+      # The next line tells the offset
+      if "..." in lines[n + 1]:
+        offset = 0
+      else:
+        offset = re.match('.+\.[\S]+\s+(.+)$', lines[n + 1]).group(1)
+      # Write the offset to the header
+      header.write("#define {} {}\n".format(name, offset))
+  return 0
+
+if __name__ == "__main__":
+  sys.exit(Main())
diff --git a/build/image/generate_offsets.gni b/build/image/generate_offsets.gni
new file mode 100644
index 0000000..61b77f4
--- /dev/null
+++ b/build/image/generate_offsets.gni
@@ -0,0 +1,78 @@
+# Calculates offsets of fields in C structures for use in assembly.
+template("generate_offsets") {
+  # There are 3 targets involved:
+  #  1. have the compiler calculate the offsets
+  #  2. extract those offsets to a header file
+  #  3. include the offsets in the build for validation
+  obj_target = "${target_name}__obj"
+  header_target = "${target_name}__header"
+  validate_target = target_name
+
+  # Have the compiler calculate the offsets and store that information in the
+  # object file to extract later.
+  source_set(obj_target) {
+    forward_variables_from(invoker,
+                           [
+                             "cflags",
+                             "cflags_c",
+                             "defines",
+                             "deps",
+                             "public_deps",
+                             "sources",
+                             "test_only",
+                           ])
+    defines = [ "GEN_OFFSETS" ]
+    visibility = [ ":${header_target}" ]
+  }
+
+  # Extract the offset information we've had the compiler emit into the object
+  # file.
+  action_foreach("${header_target}") {
+    forward_variables_from(invoker,
+                           [
+                             "cflags",
+                             "cflags_c",
+                             "defines",
+                             "deps",
+                             "public_deps",
+                             "sources",
+                             "test_only",
+                           ])
+
+    script = "//build/image/extract_offsets.py"
+    deps = [
+      ":${obj_target}",
+    ]
+    args = [
+      "--tool_prefix",
+      arch_tool_prefix,
+      "--input",
+      rebase_path("${target_out_dir}/${obj_target}.{{source_name_part}}.o"),
+      "--output",
+      rebase_path("${root_gen_dir}/inc/{{source_name_part}}.h"),
+    ]
+    outputs = [
+      "${root_gen_dir}/inc/{{source_name_part}}.h",
+    ]
+    visibility = [ ":${validate_target}" ]
+  }
+
+  # Include the offset source file in the build so the extracted offsets can be
+  # validated.
+  source_set(validate_target) {
+    forward_variables_from(invoker,
+                           [
+                             "cflags",
+                             "cflags_c",
+                             "defines",
+                             "deps",
+                             "public_deps",
+                             "sources",
+                             "test_only",
+                           ])
+
+    deps = [
+      ":${header_target}",
+    ]
+  }
+}
diff --git a/build/image/hypervisor.gni b/build/image/hypervisor.gni
new file mode 100644
index 0000000..a2bd03e
--- /dev/null
+++ b/build/image/hypervisor.gni
@@ -0,0 +1,48 @@
+import("//build/arch/${arch}/${arch_platform}.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=${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/hypervisor.ld b/build/image/hypervisor.ld
new file mode 100644
index 0000000..9949f03
--- /dev/null
+++ b/build/image/hypervisor.ld
@@ -0,0 +1,41 @@
+ENTRY(entry)
+SECTIONS
+{
+	. = PREFERRED_LOAD_ADDRESS;
+	_orig_base = ABSOLUTE(.);
+
+	text_begin = .;
+	.init : {
+		*(.init.entry)
+		*(.init)
+	}
+	.text : { *(.text) }
+	text_end = .;
+
+	. = ALIGN(4096);
+	rodata_begin = .;
+	.rodata : { *(.rodata) }
+	.rela : ALIGN(8) {
+		rela_begin = .;
+		*(.rela .rela*)
+		rela_end = .;
+	}
+	rodata_end = .;
+
+	. = ALIGN(4096);
+	data_begin = .;
+	.data : { *(.data) }
+
+	/* The entry point code assumes that bss is 16-byte aligned. */
+	.bss ALIGN(16) : {
+		file_size = ABSOLUTE(. - PREFERRED_LOAD_ADDRESS);
+		bss_begin = .;
+		*(.bss COMMON)
+		. = ALIGN(16);
+		bss_end = .;
+	}
+	data_end = .;
+
+	. = ALIGN(4096);
+	bin_end = .;
+}