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
7import("//build/toolchain/embedded.gni")
8import("//build/toolchain/platform.gni")
9
10# Build image, link to an ELF file then convert to plain binary.
11template("image_binary") {
12  assert(defined(invoker.image_name),
13         "image_binary() must specify an \"image_name\" value")
14
15  output_root = ""
16  if (defined(invoker.output_path)) {
17    output_root += "${invoker.output_path}/"
18  }
19  output_root += invoker.image_name
20
21  file_root = "${root_out_dir}/${output_root}"
22  elf_file = "${file_root}.elf"
23  bin_file = "${file_root}.bin"
24
25  elf_target = "${target_name}__elf"
26  checked_elf_target = "${target_name}__checked_elf"
27
28  # Link objects together
29  executable(elf_target) {
30    forward_variables_from(invoker,
31                           [
32                             "cflags",
33                             "cflags_c",
34                             "defines",
35                             "deps",
36                             "include_dirs",
37                             "public_configs",
38                             "public_deps",
39                             "sources",
40                             "testonly",
41                           ])
42    output_name = "${output_root}.elf"
43    inputs = [
44      rebase_path("//build/image/image.ld"),
45    ]
46    ldflags = [
47      "-T",
48      rebase_path("//build/image/image.ld"),
49    ]
50    visibility = [
51      ":${checked_elf_target}",
52      ":${invoker.target_name}",
53    ]
54  }
55
56  # Analyze the generated ELF file and check that assembly-level fixes, e.g.
57  # for CPU errata, have been properly applied.
58  action(checked_elf_target) {
59    forward_variables_from(invoker, [ "testonly" ])
60    stamp_file = elf_file + "_check.stamp"
61
62    script = "//build/image/check_elf.py"
63    deps = [
64      ":${elf_target}",
65    ]
66    args = [
67      rebase_path(elf_file),
68      rebase_path(stamp_file),
69      "--max-image-size",
70      "$plat_max_image_size",
71    ]
72    outputs = [
73      stamp_file,
74    ]
75    visibility = [ ":${invoker.target_name}" ]
76  }
77
78  action(target_name) {
79    forward_variables_from(invoker, [ "testonly" ])
80
81    script = "//build/image/convert_to_binary.py"
82
83    if (defined(invoker.check_binary) && invoker.check_binary == true) {
84      deps = [
85        ":${checked_elf_target}",
86      ]
87    } else {
88      deps = [
89        ":${elf_target}",
90      ]
91    }
92    args = [
93      "--input",
94      rebase_path(elf_file),
95      "--output",
96      rebase_path(bin_file),
97    ]
98    outputs = [
99      bin_file,
100    ]
101  }
102}
103
104# Helper to build a hypervisor image
105template("hypervisor") {
106  image_binary(target_name) {
107    forward_variables_from(invoker,
108                           [
109                             "cflags",
110                             "cflags_c",
111                             "defines",
112                             "deps",
113                             "public_deps",
114                             "sources",
115                             "testonly",
116                           ])
117    image_name = target_name
118
119    # Perform checks on the generated binary to prevent regressing on some
120    # classes of bugs, typically CPU erratas.
121    check_binary = true
122  }
123}
124
125# Helper to build a virtual machine kernel
126template("vm_kernel") {
127  image_binary(target_name) {
128    forward_variables_from(invoker,
129                           [
130                             "cflags",
131                             "cflags_c",
132                             "defines",
133                             "deps",
134                             "include_dirs",
135                             "public_configs",
136                             "public_deps",
137                             "sources",
138                             "testonly",
139                           ])
140    output_path = rebase_path(".", root_out_dir, target_out_dir)
141    image_name = target_name
142  }
143}
144
145# Build the initial RAM disk for the Linux VM.
146template("linux_initrd") {
147  initrd_base = "${target_out_dir}/${target_name}/initrd"
148  initrd_file = "${initrd_base}.img"
149  initrd_staging = "${initrd_base}"
150
151  copy_sources = []
152  copy_deps = []
153
154  # Add the files that need to be packaged into the RAM disk. The information
155  # about these files is encoded in lists with the following elements:
156  #
157  # - Source file: the target will have the same name as the source file.
158  # - Build target for the file (dependency)
159  foreach(file, invoker.files) {
160    copy_sources += [ file[0] ]
161    copy_deps += [ file[1] ]
162  }
163
164  copy("${target_name}__staging") {
165    forward_variables_from(invoker, [ "testonly" ])
166    sources = copy_sources
167    deps = copy_deps
168    outputs = [
169      "${initrd_staging}/{{source_file_part}}",
170    ]
171  }
172
173  action(target_name) {
174    forward_variables_from(invoker, [ "testonly" ])
175    script = "//build/image/generate_linux_initrd.py"
176    args = [
177      "--staging",
178      rebase_path(initrd_staging),
179      "--output",
180      rebase_path(initrd_file),
181    ]
182    deps = [
183      ":${target_name}__staging",
184    ]
185    outputs = [
186      initrd_file,
187    ]
188  }
189}
190
191template("device_tree") {
192  action(target_name) {
193    forward_variables_from(invoker,
194                           [
195                             "testonly",
196                             "deps",
197                           ])
198    script = "//build/image/dtc.py"
199
200    sources = [
201      invoker.source,
202    ]
203    outputs = [
204      invoker.output,
205    ]
206    args = [
207      "compile",
208      "-i",
209      rebase_path(sources[0]),
210      "-o",
211      rebase_path(outputs[0]),
212    ]
213  }
214}
215
216template("fdt_overlay") {
217  action(target_name) {
218    forward_variables_from(invoker,
219                           [
220                             "testonly",
221                             "deps",
222                           ])
223    script = "//build/image/dtc.py"
224
225    sources = [
226      invoker.base,
227      invoker.overlay,
228    ]
229    outputs = [
230      invoker.output,
231    ]
232    args = [
233      "overlay",
234      rebase_path(outputs[0]),
235      rebase_path(sources[0]),
236      rebase_path(sources[1]),
237    ]
238  }
239}
240
241template("incbin") {
242  source_set(target_name) {
243    forward_variables_from(invoker,
244                           [
245                             "testonly",
246                             "deps",
247                           ])
248
249    sources = [
250      "//build/image/incbin.S",
251    ]
252    inputs = invoker.sources
253    defines = [
254      "SECTION_NAME=" + invoker.section,
255      "FILE_PATH=\"" + rebase_path(inputs[0]) + "\"",
256    ]
257  }
258}
259
260template("incbin_target") {
261  incbin(target_name) {
262    forward_variables_from(invoker,
263                           [
264                             "testonly",
265                             "deps",
266                             "section",
267                           ])
268    target_outputs = get_target_outputs(invoker.target)
269    target_file = target_outputs[0]
270    sources = [
271      target_file,
272    ]
273    deps = [
274      invoker.target,
275    ]
276  }
277}
278
279# Generate the manifest for the hypervisor and apply overlay if specified.
280template("manifest") {
281  # Check if the invoker specified an overlay for the manifest.
282  if (defined(invoker.overlay) && invoker.overlay != "") {
283    base_out_dir = invoker.base_out_dir
284
285    base_target = "${target_name}__manifest_base"
286    base_file = "${base_out_dir}/manifest_base.dtb"
287    overlay_target = "${target_name}__manifest_overlay"
288    overlay_file = "${base_out_dir}/manifest_overlay.dtbo"
289
290    # Compile the base manifest.
291    device_tree(base_target) {
292      source = invoker.source
293      output = base_file
294    }
295
296    # Compile the manifest overlay.
297    device_tree(overlay_target) {
298      source = invoker.overlay
299      output = overlay_file
300    }
301
302    # Merge the base manifest and the overlay, producing the final manifest.
303    fdt_overlay(target_name) {
304      base = base_file
305      overlay = overlay_file
306      output = "${target_out_dir}" + "/" + invoker.output
307      deps = [
308        ":$base_target",
309        ":$overlay_target",
310      ]
311    }
312  } else {
313    # No overlay specified. Compile the manifest to DTB.
314    device_tree(target_name) {
315      source = invoker.source
316      output = "${target_out_dir}" + "/" + invoker.output
317    }
318  }
319}
320
321# Build the initial RAM disk for the hypervisor.
322template("initrd") {
323  base_out_dir = "${target_out_dir}/${target_name}"
324
325  action(target_name) {
326    forward_variables_from(invoker, [ "testonly" ])
327    script = "//build/image/generate_initrd.py"
328
329    initrd_file = "${base_out_dir}/initrd.img"
330    initrd_staging = "${base_out_dir}/initrd"
331
332    deps = []
333    args = [
334      "--staging",
335      rebase_path(initrd_staging),
336      "--output",
337      rebase_path(initrd_file),
338    ]
339
340    # Add the files that need to be packaged into the RAM disk. The information
341    # about these files is encoded in lists with the following elements:
342    #
343    # - Target file name in the RAM disk
344    # - Build target for the file
345    # - File name to use as source (generated as output of the build)
346    foreach(file, invoker.files) {
347      deps += [ file[1] ]
348      args += [
349        "--file",
350        file[0],
351        rebase_path(get_label_info(file[1], "target_out_dir") + "/" + file[2]),
352      ]
353    }
354
355    outputs = [
356      initrd_file,
357    ]
358  }
359}
360
361# Generate Secure Partition package
362template("partition_package") {
363  base_out_dir = "${target_out_dir}/${target_name}"
364
365  action(target_name) {
366    forward_variables_from(invoker, [ "testonly" ])
367    script = "//build/image/sptool.py"
368
369    output_package = "${base_out_dir}/${invoker.output}"
370
371    deps = []
372    args = []
373
374    foreach(file, invoker.files) {
375      deps += [
376        file[2],
377        file[3],
378      ]
379      partition_dtb = rebase_path(get_label_info(file[0], "target_out_dir"))
380      partition_img = rebase_path(get_label_info(file[1], "target_out_dir"))
381      args += [
382        "-i",
383        "${partition_img}:${partition_dtb}",
384        "-o",
385        rebase_path(output_package),
386      ]
387    }
388
389    args += [
390      "--pm-offset",
391      "${invoker.pm_offset}",
392      "--img-offset",
393      "${invoker.img_offset}",
394    ]
395
396    outputs = [
397      output_package,
398    ]
399  }
400}
401
402# Generate a JSON file containing the path to the necessary artifacts to run
403# an FVP driver, and adequately populate its arguments.
404template("partitions_json") {
405  base_out_dir = "${target_out_dir}"
406  action(target_name) {
407    forward_variables_from(invoker,
408                           [
409                             "testonly",
410                             "sps",
411                             "vms",
412                           ])
413
414    script = "//build/image/generate_partitions_json.py"
415
416    json_file = "${base_out_dir}/" + invoker.json_file
417
418    args = []
419    deps = []
420
421    foreach(sp, sps) {
422      deps += [
423        sp[2],
424        sp[3],
425      ]
426
427      package_dir = get_label_info(sp[2], "name")
428      img = rebase_path(
429              get_label_info(package_dir + "/" + sp[0], "target_out_dir"))
430
431      dts = rebase_path(sp[1])
432
433      args += [
434        "--sp",
435        "${img},${dts}",
436      ]
437    }
438
439    foreach(vm, vms) {
440      deps += [
441        vm[2],
442        vm[3],
443      ]
444
445      package_dir = get_label_info(vm[2], "name")
446      img = rebase_path(
447              get_label_info(package_dir + "/" + vm[0], "target_out_dir"))
448
449      dts = rebase_path(vm[1])
450      args += [
451        "--vm",
452        "${img},${dts}",
453      ]
454    }
455
456    args += [
457      "--out",
458      rebase_path(json_file),
459    ]
460
461    outputs = [
462      json_file,
463    ]
464  }
465}
466