1# Copyright 2018 The Hafnium Authors.
2#
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file or at
5# https://opensource.org/licenses/BSD-3-Clause.
6
7declare_args() {
8  # Set by arch toolchain. Prefix for binutils tools.
9  tool_prefix = ""
10
11  # Enable link time optimizations
12  use_lto = true
13}
14
15# Template for embedded toolchains; there is no support for C++ or libraries.
16# Instead, use source_set to group source together.
17template("embedded_cc_toolchain") {
18  toolchain(target_name) {
19    assert(defined(invoker.cc), "\"cc\" must be defined for ${target_name}.")
20    assert(defined(invoker.ld), "\"ld\" must be defined for ${target_name}.")
21
22    # Collect extra flags from the toolchain.
23    extra_defines = ""
24    extra_cflags = "-ffunction-sections -fdata-sections"
25    if (use_lto) {
26      extra_cflags += " -flto"
27    }
28    extra_ldflags = "-pie --gc-sections"
29
30    if (defined(invoker.extra_defines)) {
31      extra_defines += " ${invoker.extra_defines}"
32    }
33    if (defined(invoker.extra_cflags)) {
34      extra_cflags += " ${invoker.extra_cflags}"
35    }
36    if (defined(invoker.extra_ldflags)) {
37      extra_ldflags += " ${invoker.extra_ldflags}"
38    }
39
40    # Define the tools.
41    tool("cc") {
42      depfile = "{{output}}.d"
43      command = "${invoker.cc} -MMD -MF $depfile ${extra_defines} {{defines}} {{include_dirs}} ${extra_cflags} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
44      depsformat = "gcc"
45      description = "CC {{output}}"
46      outputs = [
47        "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
48      ]
49    }
50
51    tool("asm") {
52      depfile = "{{output}}.d"
53      command = "${invoker.cc} -MMD -MF $depfile ${extra_defines} {{defines}} {{include_dirs}} ${extra_cflags} {{asmflags}} -c {{source}} -o {{output}}"
54      depsformat = "gcc"
55      description = "ASM {{output}}"
56      outputs = [
57        "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
58      ]
59    }
60
61    tool("link") {
62      outfile = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
63      rspfile = "$outfile.rsp"
64      command = "${invoker.ld} ${extra_ldflags} {{ldflags}} -o $outfile --start-group @$rspfile --end-group"
65      description = "LINK $outfile"
66      default_output_dir = "{{root_out_dir}}"
67      rspfile_content = "{{inputs}}"
68      outputs = [
69        outfile,
70      ]
71    }
72
73    tool("alink") {
74      outfile = "{{target_out_dir}}/{{target_output_name}}.a"
75      command = "rm -f $outfile && ${invoker.ar} -rc $outfile {{inputs}}"
76      description = "ALINK $outfile"
77      outputs = [
78        outfile,
79      ]
80    }
81
82    tool("stamp") {
83      command = "touch {{output}}"
84      description = "STAMP {{output}}"
85    }
86
87    tool("copy") {
88      command = "cp -af {{source}} {{output}}"
89      description = "COPY {{source}} {{output}}"
90    }
91
92    toolchain_args = {
93      forward_variables_from(invoker.toolchain_args, "*")
94    }
95  }
96}
97
98# Specialize for clang.
99template("embedded_clang_toolchain") {
100  assert(defined(invoker.target),
101         "\"target\" must be defined for ${target_name}.")
102  assert(defined(invoker.extra_defines),
103         "\"extra_defines\" must be defined for ${target_name}")
104  assert(defined(invoker.extra_cflags),
105         "\"extra_cflags\" must be defined for ${target_name}")
106  assert(defined(invoker.extra_ldflags),
107         "\"extra_ldflags\" must be defined for ${target_name}")
108
109  embedded_cc_toolchain(target_name) {
110    cc = "clang"
111    ld = "ld.lld"
112    ar = "llvm-ar"
113
114    forward_variables_from(invoker,
115                           [
116                             "extra_defines",
117                             "extra_cflags",
118                             "extra_ldflags",
119                           ])
120
121    # TODO: Remove //inc/system if we can stop using the version of stdatomic.h
122    # from the Android prebuilt Clang.
123    extra_cflags += " -target ${invoker.target} -fcolor-diagnostics -nostdinc" +
124                    " -isystem ${toolchain_lib}/include" + " -isystem " +
125                    rebase_path("//inc/system")
126    extra_ldflags += " -O2 --icf=all --fatal-warnings --color-diagnostics"
127
128    toolchain_args = {
129      if (defined(invoker.toolchain_args)) {
130        forward_variables_from(invoker.toolchain_args, "*")
131      }
132    }
133  }
134}
135
136# Specialize for mixed toolchain with clang and bfd linker.
137template("embedded_clang_bfd_toolchain") {
138  assert(defined(invoker.target),
139         "\"target\" must be defined for ${target_name}.")
140  assert(defined(invoker.tool_prefix),
141         "\"tool_prefix\" must be defined for ${target_name}.")
142  assert(defined(invoker.extra_defines),
143         "\"extra_defines\" must be defined for ${target_name}")
144  assert(defined(invoker.extra_cflags),
145         "\"extra_cflags\" must be defined for ${target_name}")
146  assert(defined(invoker.extra_ldflags),
147         "\"extra_ldflags\" must be defined for ${target_name}")
148
149  embedded_cc_toolchain(target_name) {
150    cc = "clang"
151    ld = "${invoker.tool_prefix}ld.bfd"
152    ar = "llvm-ar"
153
154    forward_variables_from(invoker,
155                           [
156                             "extra_defines",
157                             "extra_cflags",
158                             "extra_ldflags",
159                           ])
160    extra_cflags += " -target ${invoker.target} -fcolor-diagnostics"
161    extra_ldflags += " --fatal-warnings"
162    if (use_lto) {
163      extra_ldflags += " -O2 --icf=all"
164    }
165
166    toolchain_args = {
167      if (defined(invoker.toolchain_args)) {
168        forward_variables_from(invoker.toolchain_args, "*")
169      }
170    }
171  }
172}
173
174# Expand to clang variants.
175template("embedded_platform_toolchain") {
176  assert(defined(invoker.arch), "\"arch\" must be defined for ${target_name}.")
177  assert(defined(invoker.target),
178         "\"target\" must be defined for ${target_name}.")
179  assert(defined(invoker.tool_prefix),
180         "\"tool_prefix\" must be defined for ${target_name}.")
181  assert(defined(invoker.origin_address),
182         "\"origin_address\" must be defined for ${target_name}.")
183  assert(defined(invoker.heap_pages),
184         "\"heap_pages\" must be defined for ${target_name}.")
185  assert(defined(invoker.max_cpus),
186         "\"max_cpus\" must be defined for ${target_name}.")
187  assert(defined(invoker.max_vms),
188         "\"max_vms\" must be defined for ${target_name}.")
189  assert(defined(invoker.platform_name),
190         "\"platform_name\" must be defined for ${target_name}.")
191
192  extra_defines = ""
193  extra_cflags = "-fno-builtin -ffreestanding -fpic"
194  extra_ldflags = "--defsym=ORIGIN_ADDRESS=${invoker.origin_address}"
195  if (defined(invoker.extra_defines)) {
196    extra_defines += " ${invoker.extra_defines}"
197  }
198  if (defined(invoker.extra_cflags)) {
199    extra_cflags += " ${invoker.extra_cflags}"
200  }
201  if (defined(invoker.extra_ldflags)) {
202    extra_ldflags += " ${invoker.extra_ldflags}"
203  }
204  toolchain_args = {
205    use_platform = true
206    plat_name = invoker.platform_name
207    plat_arch = invoker.arch
208    plat_heap_pages = invoker.heap_pages
209    plat_max_cpus = invoker.max_cpus
210    plat_max_vms = invoker.max_vms
211    if (defined(invoker.toolchain_args)) {
212      forward_variables_from(invoker.toolchain_args, "*")
213    }
214  }
215
216  embedded_clang_toolchain("${target_name}_clang") {
217    target = invoker.target
218  }
219
220  embedded_clang_bfd_toolchain("${target_name}_clang_bfd") {
221    target = invoker.target
222    tool_prefix = invoker.tool_prefix
223  }
224}
225
226# Specialize for different architectures.
227
228template("aarch64_common_toolchain") {
229  assert(defined(invoker.cpu), "\"cpu\" must be defined for ${target_name}.")
230  assert(defined(invoker.target),
231         "\"target\" must be defined for ${target_name}")
232  assert(defined(invoker.tool_prefix),
233         "\"tool_prefix\" must be defined for ${target_name}")
234  assert(defined(invoker.origin_address),
235         "\"origin_address\" must be defined for ${target_name}.")
236  assert(defined(invoker.console),
237         "\"console\" must be defined for ${target_name}.")
238  assert(defined(invoker.heap_pages),
239         "\"heap_pages\" must be defined for ${target_name}.")
240  assert(defined(invoker.max_cpus),
241         "\"max_cpus\" must be defined for ${target_name}.")
242  assert(defined(invoker.max_vms),
243         "\"max_vms\" must be defined for ${target_name}.")
244  if (invoker.gic_version == 3 || invoker.gic_version == 4) {
245    assert(defined(invoker.gicd_base_address),
246           "\"gicd_base_address\" must be defined for ${target_name}.")
247    assert(defined(invoker.gicr_base_address),
248           "\"gicr_base_address\" must be defined for ${target_name}.")
249    assert(defined(invoker.gicr_frames),
250           "\"gicr_frames\" must be defined for ${target_name}.")
251  }
252  assert(defined(invoker.platform_name),
253         "\"platform_name\" must be defined for ${target_name}.")
254
255  embedded_platform_toolchain(target_name) {
256    forward_variables_from(invoker,
257                           [
258                             "origin_address",
259                             "heap_pages",
260                             "max_cpus",
261                             "max_vms",
262                             "platform_name",
263                             "extra_ldflags",
264                           ])
265    arch = "aarch64"
266    target = invoker.target
267    tool_prefix = invoker.tool_prefix
268    extra_cflags = "-mcpu=${invoker.cpu} -mstrict-align"
269    if (defined(invoker.extra_cflags)) {
270      extra_cflags += " ${invoker.extra_cflags}"
271    }
272
273    extra_defines = ""
274    if (defined(invoker.extra_defines)) {
275      extra_defines += " ${invoker.extra_defines}"
276    }
277
278    if (invoker.gic_version > 0) {
279      extra_defines += " -DGIC_VERSION=${invoker.gic_version}"
280    }
281    if (invoker.gic_version == 3 || invoker.gic_version == 4) {
282      extra_defines += " -DGICD_BASE=${invoker.gicd_base_address} -DGICR_BASE=${invoker.gicr_base_address} -DGICR_FRAMES=${invoker.gicr_frames}"
283    }
284    if (defined(invoker.gic_enable_espi)) {
285      extra_defines += " -DGIC_EXT_INTID=${invoker.gic_enable_espi}"
286    }
287    if (defined(invoker.branch_protection)) {
288      extra_cflags += " -mbranch-protection=${invoker.branch_protection}"
289      extra_defines += " -DBRANCH_PROTECTION=1"
290    }
291
292    toolchain_args = {
293      plat_boot_flow = invoker.boot_flow
294      plat_console = invoker.console
295      plat_iommu = invoker.iommu
296      if (defined(invoker.stdout)) {
297        stdout = invoker.stdout
298      }
299      if (defined(invoker.max_image_size)) {
300        plat_max_image_size = invoker.max_image_size
301      }
302      forward_variables_from(invoker.toolchain_args, "*")
303    }
304
305    if (defined(toolchain_args.enable_mte)) {
306      extra_cflags += " -march=armv8.5-a+memtag"
307      extra_cflags += " -fsanitize=memtag"
308      extra_defines += " -DENABLE_MTE"
309    }
310  }
311}
312
313template("aarch64_toolchain") {
314  aarch64_common_toolchain("${target_name}") {
315    forward_variables_from(invoker, "*")
316    target = "aarch64-none-eabi"
317    tool_prefix = "aarch64-linux-gnu-"  # TODO: this isn't right for bare metal but it works.
318    platform_name = target_name
319  }
320}
321
322template("aarch64_toolchains") {
323  aarch64_toolchain("${target_name}") {
324    forward_variables_from(invoker,
325                           [
326                             "origin_address",
327                             "boot_flow",
328                             "console",
329                             "iommu",
330                             "gic_version",
331                             "gic_enable_espi",
332                             "gicd_base_address",
333                             "gicr_base_address",
334                             "gicr_frames",
335                             "heap_pages",
336                             "max_cpus",
337                             "max_image_size",
338                             "max_vms",
339                             "toolchain_args",
340                             "branch_protection",
341                           ])
342    cpu = "${invoker.cpu}+nofp"
343
344    # Add a macro so files can tell whether they are not being built for a VM.
345    extra_defines = " -DVM_TOOLCHAIN=0"
346    extra_cflags = " -mgeneral-regs-only"
347  }
348
349  # Toolchain for building test VMs which run under Hafnium.
350  aarch64_toolchain("${target_name}_vm") {
351    forward_variables_from(invoker,
352                           [
353                             "origin_address",
354                             "gic_version",
355                             "gic_enable_espi",
356                             "gicd_base_address",
357                             "gicr_base_address",
358                             "gicr_frames",
359                             "max_cpus",
360                             "toolchain_args",
361                             "console",
362                             "branch_protection",
363                           ])
364    cpu = "${invoker.cpu}+fp"
365    boot_flow = "//src/arch/fake:boot_flow"
366    iommu = "//src/iommu:absent"
367    stdout = "//src/arch/aarch64/hftest:stdout"
368
369    # Nonsense values because they are required but shouldn't be used.
370    heap_pages = 0
371    max_vms = 0
372
373    # Add a macro so files can tell whether they are being built for a VM.
374    extra_defines = " -DVM_TOOLCHAIN=1"
375  }
376}
377