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/.gn b/.gn
new file mode 100644
index 0000000..e5b6d4a
--- /dev/null
+++ b/.gn
@@ -0,0 +1,2 @@
+# The location of the build configuration file.
+buildconfig = "//build/BUILDCONFIG.gn"
diff --git a/BUILD.gn b/BUILD.gn
new file mode 100644
index 0000000..4c818a3
--- /dev/null
+++ b/BUILD.gn
@@ -0,0 +1,23 @@
+import("//build/image/hypervisor.gni")
+
+group("host_tools") {
+ deps = [
+ ":hafnium($arch_toolchain)",
+ ]
+}
+
+executable("test") {
+ sources = [
+ "test.cpp",
+ ]
+}
+
+# Only build the image for the arch
+if (current_toolchain == arch_toolchain) {
+ hypervisor("hafnium") {
+ deps = [
+ "//src",
+ "//src/arch/${arch}",
+ ]
+ }
+}
diff --git a/Makefile b/Makefile
index 888e823..8817e03 100644
--- a/Makefile
+++ b/Makefile
@@ -1,177 +1,32 @@
-ROOT_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
-ifeq ($(ROOT_DIR),./)
- ROOT_DIR :=
-endif
+OUT ?= out
-#
-# Defaults.
-#
-ARCH ?= aarch64
-PLAT ?= qemu
-DEBUG ?= 1
-NAME := hafnium
+GN ?= ../gn/out/gn
+NINJA ?= ninja
-# Toolchain
-CROSS_COMPILE ?= aarch64-linux-gnu-
-TARGET := $(patsubst %-,%,$(CROSS_COMPILE))
+all: $(OUT)/build.ninja
+ @$(NINJA) -C $(OUT)
-ifeq ($(CLANG),1)
- CLANG := clang
-endif
-GCC ?= gcc
+out/build.ninja: $(GN)
+ @$(GN) gen $(OUT)
-ifdef CLANG
- CC := $(CLANG) -target $(TARGET)
-else
- CC := $(CROSS_COMPILE)$(GCC)
-endif
-
-# Output
-OUT := $(ROOT_DIR)out/$(ARCH)/$(PLAT)
-
-all: $(OUT)/$(NAME).bin
-
-# Include platform-specific constants.
-include $(ROOT_DIR)src/arch/$(ARCH)/$(PLAT).mk
-
-define include_module
- SRCS :=
- OFFSET_SRCS :=
- include $(ROOT_DIR)$(1)/rules.mk
- GLOBAL_SRCS += $$(addprefix $(1)/,$$(SRCS))
- GLOBAL_OFFSET_SRCS += $$(addprefix $(1)/,$$(OFFSET_SRCS))
-endef
-
-#
-# Include each module.
-#
-MODULES := src
-MODULES += src/arch/$(ARCH)
-GLOBAL_SRCS :=
-GLOBAL_OFFSET_SRCS :=
-$(foreach mod,$(MODULES),$(eval $(call include_module,$(mod))))
-
-#
-# Rules to build C files.
-#
-COPTS = -mcpu=cortex-a57+nofp
-COPTS += -fno-stack-protector
-COPTS += -fno-builtin -ffreestanding
-COPTS += -g
-COPTS += -O2
-COPTS += -fpic
-COPTS += -std=c11
-COPTS += -Wall -Wpedantic -Werror
-COPTS += -Wno-extended-offsetof
-COPTS += -DDEBUG=$(DEBUG)
-COPTS += -DMAX_CPUS=8
-COPTS += -DMAX_VMS=16
-COPTS += -DSTACK_SIZE=4096
-COPTS += -I$(ROOT_DIR)inc
-COPTS += -I$(ROOT_DIR)src/arch/$(ARCH)/inc
-COPTS += -I$(OUT)/arch/$(ARCH)/inc
-COPTS += -DGICD_BASE=$(GICD_BASE)
-COPTS += -DGICC_BASE=$(GICC_BASE)
-COPTS += -DGICR_BASE=$(GICR_BASE)
-COPTS += -DTIMER_IRQ=$(TIMER_IRQ)
-
-ifeq ($(PL011),1)
- COPTS += -DPL011_BASE=$(PL011_BASE)
-endif
-
-DEP_GEN = -MMD -MP -MF $$(patsubst %,%.d,$$@)
-
-define build_c
- TGT := $(patsubst %.c,%.o,$(OUT)/$(patsubst src/%,%,$(1)))
- GLOBAL_OBJS += $$(TGT)
- REMAIN_SRCS := $$(filter-out $(1),$$(REMAIN_SRCS))
-$$(TGT): $(ROOT_DIR)$(1) $(GLOBAL_OFFSETS) | $$(dir $$(TGT))
- $$(info CC $(ROOT_DIR)$1)
- @$(CC) $(COPTS) $(DEP_GEN) -c $(ROOT_DIR)$(1) -o $$@
-endef
-
-#
-# Rules to generate offsets.
-#
-define gen_offsets
- TMP := $(patsubst src/%,%,$(1))
- TMP := $$(dir $$(TMP))inc/$$(notdir $$(TMP))
- TGT := $$(patsubst %.c,%.h,$(OUT)/$$(TMP))
- GLOBAL_OFFSETS += $$(TGT)
-$$(TGT): $(ROOT_DIR)$(1) | $$(dir $$(TGT))
- $$(info GENOFFSET $(ROOT_DIR)$1)
- @$(CC) -DGEN_OFFSETS $(COPTS) $(DEP_GEN) -MT $$@ -S -c $(ROOT_DIR)$(1) -o - | \
- grep ^DEFINE_OFFSET -A1 | \
- grep -v ^--$ | \
- sed 's/^DEFINE_OFFSET__\([^:]*\):/#define \1 \\/' | \
- sed 's/\.zero.*/0/' | \
- sed 's/\.[^\t][^\t]*//' > $$@
-endef
-
-#
-# Rules to build S files.
-#
-define build_S
- TGT := $(patsubst %.S,%.o,$(OUT)/$(patsubst src/%,%,$(1)))
- GLOBAL_OBJS += $$(TGT)
- REMAIN_SRCS := $$(filter-out $(1),$$(REMAIN_SRCS))
-$$(TGT): $(ROOT_DIR)$(1) $(GLOBAL_OFFSETS) | $$(dir $$(TGT))
- $$(info AS $(ROOT_DIR)$1)
- @$(CC) $(COPTS) $(DEP_GEN) -c $(ROOT_DIR)$(1) -o $$@
-endef
-
-#
-# Generate the build rules for all .c and .S files.
-#
-GLOBAL_OBJS :=
-GLOBAL_OFFSETS :=
-REMAIN_SRCS := $(GLOBAL_SRCS)
-$(foreach file,$(filter %.c,$(GLOBAL_OFFSET_SRCS)),$(eval $(call gen_offsets,$(file))))
-$(foreach file,$(filter %.c,$(GLOBAL_SRCS)),$(eval $(call build_c,$(file))))
-$(foreach file,$(filter %.S,$(GLOBAL_SRCS)),$(eval $(call build_S,$(file))))
-
-#
-# Check if there are any source files which we don't know to handle.
-#
-ifneq ($(REMAIN_SRCS),)
- $(error Don't know how to handle $(REMAIN_SRCS))
-endif
-
-#
-# Rule to create all output directories.
-#
-define create_dir
-$1:
- @mkdir -p $1
-endef
-$(foreach name,$(sort $(dir $(GLOBAL_OBJS))),$(eval $(call create_dir,$(name))))
-$(foreach name,$(sort $(dir $(GLOBAL_OFFSETS))),$(eval $(call create_dir,$(name))))
-
-#
-# Rules to build the hypervisor.
-#
-$(OUT)/$(NAME): $(GLOBAL_OBJS) $(ROOT_DIR)src/$(NAME).ld
- $(info LD $(ROOT_DIR)src/$(NAME).ld)
- @$(CROSS_COMPILE)ld -g -pie $(GLOBAL_OBJS) -T$(ROOT_DIR)src/$(NAME).ld --defsym PREFERRED_LOAD_ADDRESS=$(LOAD_ADDRESS) -o $@
-
-$(OUT)/$(NAME).bin: $(OUT)/$(NAME)
- $(info OBJCOPY $@)
- @$(CROSS_COMPILE)objcopy -O binary $< $@
+$(GN):
+ git clone https://gn.googlesource.com/gn ../gn
+ cd ../gn && python build/gen.py
+ ninja -C ../gn/out
clean:
- rm -rf $(ROOT_DIR)out
+ @$(NINJA) -C $(OUT) -t clean
-#
-# Rules for code health
-#
+clobber:
+ rm -rf $(OUT)
# see .clang-format
format:
- @find $(ROOT_DIR)src/ -name *.c -o -name *.h | xargs clang-format -style file -i
- @find $(ROOT_DIR)inc/ -name *.c -o -name *.h | xargs clang-format -style file -i
+ @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 . -name *.gn -o -name *.gni -exec $(GN) format {} \;
+# TODO: get this working again. Need to extract a compile database to get the correct args.
# see .clang-tidy
-tidy: $(GLOBAL_OFFSETS)
- @find $(ROOT_DIR)src/ -name *.c -exec clang-tidy {} -fix -- -target $(TARGET) $(COPTS) \;
-
--include $(patsubst %,%.d,$(GLOBAL_OBJS),$(GLOBAL_OFFSETS))
+# tidy: $(GLOBAL_OFFSETS)
+# @find $(ROOT_DIR)src/ -name *.c -exec clang-tidy {} -fix -- -target $(TARGET) $(COPTS) \;
diff --git a/build/BUILD.gn b/build/BUILD.gn
new file mode 100644
index 0000000..9a0bb6e
--- /dev/null
+++ b/build/BUILD.gn
@@ -0,0 +1,67 @@
+import("//build/arch/${arch}/${arch_platform}.gni")
+
+config("compiler_defaults") {
+ cflags = [
+ "-g",
+ "-O2",
+ "-Wall",
+ #"-Wextra",
+ "-Wpedantic",
+ "-Werror",
+ ]
+
+ cflags_c = [
+ "-std=c11",
+ ]
+
+ cflags_cc = [
+ "-std=c++14",
+ ]
+
+ if (is_debug) {
+ defines = [
+ "DEBUG=1",
+ ]
+ } else {
+ defines = [
+ "DEBUG=0",
+ ]
+ }
+
+ # Configuration specific for the bare metal images
+ if (current_toolchain == arch_toolchain) {
+ include_dirs = [
+ "//inc",
+ "//src/arch/${arch}/inc",
+ "${root_gen_dir}/inc",
+ ]
+
+ cflags += [
+ "-mcpu=${arch_cpu}",
+ "-fno-stack-protector",
+ "-fno-builtin",
+ "-ffreestanding",
+ "-fpic",
+ "-Wno-extended-offsetof", # have clang give us some slack
+ ]
+
+ defines += [
+ "MAX_CPUS=8",
+ "MAX_VMS=16",
+ "STACK_SIZE=4096",
+ ]
+
+ # TODO: this should be arch specific but it is currenly used by the
+ # platform generic code to map the memory
+ if (use_pl011) {
+ defines += [
+ "PL011_BASE=${pl011_base_address}",
+ ]
+ }
+ }
+}
+
+config("executable_ldconfig") {
+ ldflags = [
+ ]
+}
diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn
new file mode 100644
index 0000000..677544e
--- /dev/null
+++ b/build/BUILDCONFIG.gn
@@ -0,0 +1,87 @@
+# This build configuration extends GN's cross-compilation concepts of "target",
+# "host" and "current" with "arch" which is used to refer to the embedded
+# architecture that the bare metal images are being built for.
+#
+# The concepts of "target", "host" and "current" are used when bulding tests
+# and utilities for the non-embedded device. Note that there hasn't been any
+# thought given to support building for anything other than the host so it
+# probably won't work.
+#
+# In summary:
+# - host{_os,_cpu,_toolchain} is for the system running the build
+# - target{_os,_cpu,_toolchain} is for the system that will run utilities and tests
+# - arch{,_toolchain} is for the embedded system that will run the bare metal images
+
+# Configuration of the build toolchain.
+declare_args() {
+ # Enable extra debugging.
+ is_debug = true
+
+ # Set to true to build with gcc (default is clang).
+ use_gcc = false
+
+ # The architecture to build the bare metal images for.
+ arch = "aarch64"
+
+ # The platform to build the bare metal images for.
+ arch_platform = "qemu"
+
+ # Set by arch toolchain. Prefix for binutils tools.
+ arch_tool_prefix = ""
+}
+
+# Check that we support the attempted build
+assert(host_os == "linux", "Only linux builds are currently supported")
+
+# Check that the requested architecture is supported
+assert(arch == "aarch64", "Unsupported arch: $arch")
+
+# Setup the standard variables
+if (target_os == "") {
+ target_os = host_os
+}
+if (target_cpu == "") {
+ target_cpu = host_cpu
+}
+if (current_os == "") {
+ current_os = target_os
+}
+if (current_cpu == "") {
+ current_cpu = target_cpu
+}
+
+assert(target_os == host_os, "Cross compiles not yet supported")
+assert(target_cpu == host_cpu, "Cross compiles not yet supported")
+
+# All binary targets will get this list of configs by default
+_shared_binary_target_configs = [ "//build:compiler_defaults" ]
+
+# Apply that default list to the binary target types.
+set_defaults("executable") {
+ configs = _shared_binary_target_configs
+ # Executables get this additional configuration
+ configs += [ "//build:executable_ldconfig" ]
+}
+set_defaults("static_library") {
+ configs = _shared_binary_target_configs
+}
+set_defaults("shared_library") {
+ configs = _shared_binary_target_configs
+}
+set_defaults("source_set") {
+ configs = _shared_binary_target_configs
+}
+
+# Select host, target and arch toolchains
+if (use_gcc) {
+ host_toolchain = "//build/toolchain/host:gcc"
+ target_toolchain = "//build/toolchain/host:gcc"
+ arch_toolchain = "//build/toolchain/arch:gcc_${arch}"
+} else {
+ host_toolchain = "//build/toolchain/host:clang"
+ target_toolchain = "//build/toolchain/host:clang"
+ arch_toolchain = "//build/toolchain/arch:clang_${arch}"
+}
+
+# The default toolchain is the target toolchain for building utilities and tests
+set_default_toolchain(target_toolchain)
diff --git a/build/arch/aarch64/hikey.gni b/build/arch/aarch64/hikey.gni
new file mode 100644
index 0000000..f870611
--- /dev/null
+++ b/build/arch/aarch64/hikey.gni
@@ -0,0 +1,13 @@
+declare_args() {
+ # The specific CPU that runs the architecture.
+ arch_cpu = "cortex-a57+nofp"
+
+ # The load address of the hypervisor
+ hypervisor_load_address = "0x01000000"
+
+ # Whether to include the PrimeCell UART (PL011) driver.
+ use_pl011 = true
+
+ # The base address of the PrimeCell UART (PL011) device.
+ pl011_base_address = "0xf8015000"
+}
diff --git a/build/arch/aarch64/qemu.gni b/build/arch/aarch64/qemu.gni
new file mode 100644
index 0000000..4962ab8
--- /dev/null
+++ b/build/arch/aarch64/qemu.gni
@@ -0,0 +1,13 @@
+declare_args() {
+ # The specific CPU that runs the architecture.
+ arch_cpu = "cortex-a57+nofp"
+
+ # The load address of the hypervisor
+ hypervisor_load_address = "0x40001000"
+
+ # Whether to include the PrimeCell UART (PL011) driver.
+ use_pl011 = true
+
+ # The base address of the PrimeCell UART (PL011) device.
+ pl011_base_address = "0x09000000"
+}
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/src/hafnium.ld b/build/image/hypervisor.ld
similarity index 100%
rename from src/hafnium.ld
rename to build/image/hypervisor.ld
diff --git a/build/toolchain/arch/BUILD.gn b/build/toolchain/arch/BUILD.gn
new file mode 100644
index 0000000..f85c3af
--- /dev/null
+++ b/build/toolchain/arch/BUILD.gn
@@ -0,0 +1,112 @@
+# Target toolchain specific build arguments.
+declare_args() {
+ # Build with a specific compiler version e.g. when building with clang, set
+ # to "3.9" to build with `clang-3.9`.
+ arch_cc_version = ""
+}
+
+# Template for target toolchains; there is no support for C++ or libraries.
+# Instead, use source_set to group source together.
+template("cc_toolchain") {
+ toolchain(target_name) {
+ assert(defined(invoker.cc), "cc_toolchain() must specify a \"cc\" value")
+ assert(defined(invoker.ld), "cc_toolchain() must specify a \"ld\" value")
+
+ # Combine compiler version and compiler specific flags for this toolchain.
+ cc = "${invoker.cc}"
+ if (arch_cc_version != "") {
+ cc += "-${arch_cc_version}"
+ }
+ if (defined(invoker.cflags)) {
+ cc += " ${invoker.cflags}"
+ }
+
+ tool("cc") {
+ depfile = "{{output}}.d"
+ command = "${cc} -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
+ depsformat = "gcc"
+ description = "CC {{output}}"
+ outputs = [
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
+ ]
+ }
+
+ tool("asm") {
+ depfile = "{{output}}.d"
+ command = "${cc} -MMD -MF $depfile {{defines}} {{include_dirs}} {{asmflags}} -c {{source}} -o {{output}}"
+ depsformat = "gcc"
+ description = "ASM {{output}}"
+ outputs = [
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
+ ]
+ }
+
+ tool("link") {
+ outfile = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
+ rspfile = "$outfile.rsp"
+ command = "${invoker.ld} {{ldflags}} -o $outfile --start-group @$rspfile --end-group"
+ description = "LINK $outfile"
+ default_output_dir = "{{root_out_dir}}"
+ rspfile_content = "{{inputs}}"
+ outputs = [
+ outfile,
+ ]
+ }
+
+ tool("stamp") {
+ command = "touch {{output}}"
+ description = "STAMP {{output}}"
+ }
+
+ tool("copy") {
+ command = "cp -af {{source}} {{output}}"
+ description = "COPY {{source}} {{output}}"
+ }
+
+ toolchain_args = {
+ forward_variables_from(invoker.toolchain_args, "*")
+ }
+ }
+}
+
+# Specialize for clang or gcc
+template("clang_toolchain") {
+ assert(defined(invoker.target), "clang_toolchain() must specify a \"target\" value")
+ assert(defined(invoker.arch_tool_prefix), "gcc_toolchain() must specify a \"arch_tool_prefix\" value")
+
+ cc_toolchain(target_name) {
+ cc = "clang"
+ cflags = "-target ${invoker.target}"
+ ld = "${invoker.arch_tool_prefix}ld"
+
+ toolchain_args = {
+ arch_tool_prefix = invoker.arch_tool_prefix
+ }
+ }
+}
+
+template("gcc_toolchain") {
+ assert(defined(invoker.arch_tool_prefix), "gcc_toolchain() must specify a \"arch_tool_prefix\" value")
+
+ cc_toolchain(target_name) {
+ cc = "${invoker.arch_tool_prefix}gcc"
+ ld = "${invoker.arch_tool_prefix}ld"
+
+ toolchain_args = {
+ arch_tool_prefix = invoker.arch_tool_prefix
+ }
+ }
+}
+
+# Specialize for different architectures
+
+clang_toolchain("clang_aarch64") {
+ target = "aarch64-none-eabi"
+ # TODO: below isn't right for bare metal code, but it works fine
+ arch_tool_prefix = "aarch64-linux-gnu-"
+}
+
+gcc_toolchain("gcc_aarch64") {
+ # TODO: below isn't right for bare metal code, but it works fine
+ arch_tool_prefix = "aarch64-linux-gnu-"
+}
diff --git a/build/toolchain/host/BUILD.gn b/build/toolchain/host/BUILD.gn
new file mode 100644
index 0000000..0c4e52e
--- /dev/null
+++ b/build/toolchain/host/BUILD.gn
@@ -0,0 +1,102 @@
+# Template for host toolchains.
+template("cc_toolchain") {
+ toolchain(target_name) {
+ assert(defined(invoker.ar), "cc_toolchain() must specify a \"ar\" value")
+ assert(defined(invoker.cc), "cc_toolchain() must specify a \"cc\" value")
+ assert(defined(invoker.cxx), "cc_toolchain() must specify a \"cxx\" value")
+
+ tool("cc") {
+ depfile = "{{output}}.d"
+ command = "${invoker.cc} -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
+ depsformat = "gcc"
+ description = "CC {{output}}"
+ outputs = [
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
+ ]
+ }
+
+ tool("cxx") {
+ depfile = "{{output}}.d"
+ command = "${invoker.cxx} -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}"
+ depsformat = "gcc"
+ description = "CXX {{output}}"
+ outputs = [
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
+ ]
+ }
+
+ tool("alink") {
+ rspfile = "{{output}}.rsp"
+ command = "rm -f {{output}} && ${invoker.ar} rcs {{output}} @$rspfile"
+ description = "AR {{target_output_name}}{{output_extension}}"
+ rspfile_content = "{{inputs}}"
+ outputs = [
+ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}",
+ ]
+ default_output_extension = ".a"
+ output_prefix = "lib"
+ }
+
+ tool("solink") {
+ soname = "{{target_output_name}}{{output_extension}}" # e.g. "libfoo.so".
+ sofile = "{{output_dir}}/$soname"
+ rspfile = soname + ".rsp"
+
+ command = "${invoker.cxx} -shared {{ldflags}} -o $sofile -Wl,-soname=$soname @$rspfile"
+ rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}"
+
+ description = "SOLINK $soname"
+
+ # Use this for {{output_extension}} expansions unless a target manually
+ # overrides it (in which case {{output_extension}} will be what the target
+ # specifies).
+ default_output_extension = ".so"
+
+ # Use this for {{output_dir}} expansions unless a target manually overrides
+ # it (in which case {{output_dir}} will be what the target specifies).
+ default_output_dir = "{{root_out_dir}}"
+
+ outputs = [
+ sofile,
+ ]
+ link_output = sofile
+ depend_output = sofile
+ output_prefix = "lib"
+ }
+
+ tool("link") {
+ outfile = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
+ rspfile = "$outfile.rsp"
+ command = "${invoker.cxx} {{ldflags}} -o $outfile -Wl,--start-group @$rspfile {{solibs}} -Wl,--end-group {{libs}}"
+ description = "LINK $outfile"
+ default_output_dir = "{{root_out_dir}}"
+ rspfile_content = "{{inputs}}"
+ outputs = [
+ outfile,
+ ]
+ }
+
+ tool("stamp") {
+ command = "touch {{output}}"
+ description = "STAMP {{output}}"
+ }
+
+ tool("copy") {
+ command = "cp -af {{source}} {{output}}"
+ description = "COPY {{source}} {{output}}"
+ }
+ }
+}
+
+# Specialize for clang or gcc
+cc_toolchain("clang") {
+ ar = "llvm-ar"
+ cc = "clang"
+ cxx = "clang++"
+}
+
+cc_toolchain("gcc") {
+ ar = "ar"
+ cc = "gcc"
+ cxx = "g++"
+}
diff --git a/kokoro/ubuntu/build.sh b/kokoro/ubuntu/build.sh
index 85f4f98..596ca4f 100755
--- a/kokoro/ubuntu/build.sh
+++ b/kokoro/ubuntu/build.sh
@@ -11,14 +11,16 @@
then
# Server
cd git/hafnium
- export CLANG="clang-3.9"
+ mkdir out
+ echo "arch_cc_version = \"3.9\"" > out/args.gn
else
# Local
- export CLANG=1
+ echo "Testing kokoro build locally..."
fi
+# TODO: Kokoro is missing ninja, gcc-4.9 or above and qemu
# Check the build works
-make
+# make
# # Check to code looks healthy, failing if any changes were made
# make format
diff --git a/src/BUILD.gn b/src/BUILD.gn
new file mode 100644
index 0000000..51edfd3
--- /dev/null
+++ b/src/BUILD.gn
@@ -0,0 +1,22 @@
+source_set("src") {
+ sources = [
+ "alloc.c",
+ "api.c",
+ "cpio.c",
+ "cpu.c",
+ "fdt.c",
+ "fdt_handler.c",
+ "load.c",
+ "main.c",
+ "memiter.c",
+ "mm.c",
+ "std.c",
+ "vm.c",
+ ]
+
+ if (is_debug) {
+ sources += [
+ "dlog.c",
+ ]
+ }
+}
diff --git a/src/arch/aarch64/BUILD.gn b/src/arch/aarch64/BUILD.gn
new file mode 100644
index 0000000..046778c
--- /dev/null
+++ b/src/arch/aarch64/BUILD.gn
@@ -0,0 +1,29 @@
+import("//build/arch/${arch}/${arch_platform}.gni")
+import("//build/image/generate_offsets.gni")
+
+source_set("aarch64") {
+ sources = [
+ "entry.S",
+ "exceptions.S",
+ "handler.c",
+ "mm.c",
+ "offsets.c",
+ "params.c",
+ ]
+
+ if (use_pl011) {
+ sources += [
+ "pl011.c",
+ ]
+ }
+
+ deps = [
+ ":offsets",
+ ]
+}
+
+generate_offsets("offsets") {
+ sources = [
+ "offsets.c",
+ ]
+}
diff --git a/src/arch/aarch64/hikey.mk b/src/arch/aarch64/hikey.mk
deleted file mode 100644
index ec9eff9..0000000
--- a/src/arch/aarch64/hikey.mk
+++ /dev/null
@@ -1,11 +0,0 @@
-USE_FDT := 1
-
-LOAD_ADDRESS := 0x1000000
-PL011_BASE := 0xf8015000
-PL011 := 1
-GICV2 := 1
-
-GICD_BASE := 0xf6801000
-GICC_BASE := 0xf6802000
-
-TIMER_IRQ := 26
diff --git a/src/arch/aarch64/qemu.mk b/src/arch/aarch64/qemu.mk
deleted file mode 100644
index 41bcd3c..0000000
--- a/src/arch/aarch64/qemu.mk
+++ /dev/null
@@ -1,12 +0,0 @@
-USE_FDT := 1
-
-LOAD_ADDRESS := 0x40001000
-PL011_BASE := 0x09000000
-PL011 := 1
-GICV3 := 1
-
-GICD_BASE := 0x08000000
-GICC_BASE := 0x08010000
-GICR_BASE := 0x080A0000
-
-TIMER_IRQ := 26
diff --git a/src/arch/aarch64/rules.mk b/src/arch/aarch64/rules.mk
deleted file mode 100644
index a006751..0000000
--- a/src/arch/aarch64/rules.mk
+++ /dev/null
@@ -1,12 +0,0 @@
-SRCS += entry.S
-SRCS += exceptions.S
-SRCS += handler.c
-SRCS += mm.c
-SRCS += offsets.c
-SRCS += params.c
-
-OFFSET_SRCS += offsets.c
-
-ifeq ($(PL011),1)
- SRCS += pl011.c
-endif
diff --git a/src/rules.mk b/src/rules.mk
deleted file mode 100644
index 7d56def..0000000
--- a/src/rules.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-SRCS += alloc.c
-SRCS += api.c
-SRCS += cpio.c
-SRCS += cpu.c
-SRCS += load.c
-SRCS += main.c
-SRCS += memiter.c
-SRCS += mm.c
-SRCS += std.c
-SRCS += vm.c
-
-ifeq ($(DEBUG),1)
- SRCS += dlog.c
-endif
-
-ifeq ($(USE_FDT),1)
- SRCS += fdt.c
- SRCS += fdt_handler.c
-endif
diff --git a/test.cpp b/test.cpp
new file mode 100644
index 0000000..64f0d09
--- /dev/null
+++ b/test.cpp
@@ -0,0 +1,6 @@
+#include <iostream>
+
+int main(void) {
+ std::cout << "This looks like it worked!" << std::endl;
+ return 0;
+}