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 = .;
+}