blob: 84c07b8b29056bda6d02cd507e3ae23e82997a5b [file] [log] [blame]
Chris Frantz9b34e4a2021-11-24 17:03:12 -08001# Copyright lowRISC contributors.
2# Licensed under the Apache License, Version 2.0, see LICENSE for details.
3# SPDX-License-Identifier: Apache-2.0
4
Miguel Young de la Sota3b5a9f52022-03-24 16:11:42 -04005load("//rules:rv.bzl", "rv_rule")
Miguel Young de la Sota4b01c7a2022-04-21 17:25:52 -04006load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain")
Chris Frantz9b34e4a2021-11-24 17:03:12 -08007
Jade Philipoom39c9b322022-03-08 12:27:10 +00008def _get_assembler(cc_toolchain):
9 """Find the path to riscv-unknown-elf-as."""
10
Miguel Young de la Sota8fce79d2022-03-23 15:37:39 -040011 # Note: the toolchain config doesn"t appear to have a good way to get
12 # access to the assembler. We should be able to access it via the
Timothy Trippelefe736b2022-04-20 12:50:40 -070013 # the compiler, but I had trouble with //hw/ip/otbn/util/otbn_as.py invoking
Miguel Young de la Sota8fce79d2022-03-23 15:37:39 -040014 # the compiler as assembler.
Jade Philipoom39c9b322022-03-08 12:27:10 +000015 return [f for f in cc_toolchain.all_files.to_list() if f.basename.endswith("as")][0]
16
17def _otbn_assemble_sources(ctx):
18 """Helper function that, for each source file in the provided context, adds
Timothy Trippelefe736b2022-04-20 12:50:40 -070019 an action to the context that invokes the otbn assember (otbn_as.py),
Jade Philipoom39c9b322022-03-08 12:27:10 +000020 producing a corresponding object file. Returns a list of all object files
21 that will be generated by these actions.
22 """
Miguel Young de la Sota4b01c7a2022-04-21 17:25:52 -040023 cc_toolchain = find_cc_toolchain(ctx).cc
Jade Philipoom39c9b322022-03-08 12:27:10 +000024 assembler = _get_assembler(cc_toolchain)
25
26 objs = []
27 for src in ctx.files.srcs:
28 obj = ctx.actions.declare_file(src.basename.replace("." + src.extension, ".o"))
29 objs.append(obj)
30 ctx.actions.run(
31 outputs = [obj],
32 inputs = ([src] +
33 cc_toolchain.all_files.to_list() +
Timothy Trippelfc197342022-04-20 14:38:11 -070034 [ctx.executable._otbn_as]),
Jade Philipoom39c9b322022-03-08 12:27:10 +000035 env = {
36 "RV32_TOOL_AS": assembler.path,
37 },
38 arguments = ["-o", obj.path, src.path],
Timothy Trippelfc197342022-04-20 14:38:11 -070039 executable = ctx.executable._otbn_as,
Jade Philipoom39c9b322022-03-08 12:27:10 +000040 )
41
42 return objs
43
44def _otbn_library(ctx):
45 """Produces a collection of object files, one per source file, that can be
46 used as a dependency for otbn binaries."""
47 objs = _otbn_assemble_sources(ctx)
48
49 return [
Timothy Trippelfc197342022-04-20 14:38:11 -070050 DefaultInfo(
51 files = depset(objs),
52 data_runfiles = ctx.runfiles(files = objs),
53 ),
Jade Philipoom39c9b322022-03-08 12:27:10 +000054 ]
55
Chris Frantz9b34e4a2021-11-24 17:03:12 -080056def _otbn_binary(ctx):
57 """The build process for otbn resources currently invokes
Timothy Trippelfc197342022-04-20 14:38:11 -070058 `//hw/ip/otbn/util/otbn_{as,ld,...}.py` to build the otbn resource.
Chris Frantz9b34e4a2021-11-24 17:03:12 -080059 These programs are python scripts which translate otbn special
60 instructions into the proper opcode sequences and _then_ invoke the normal
61 `rv32-{as,ld,...}` programs to produce the resource. These "native"
62 otbn resources are the `otbn_objs` and `elf` output groups.
63
64 In order to make the otbn resource useful to the the main CPU, the
65 otbn resource needs to be included as a blob of data that the main
66 CPU can dump into the otbn `imem` area and ask otbn to execute it.
67 `util/otbn-build.py` does this with some objcopy-fu, emitting
68 `foo.rv32embed.o`. Bazel's `cc_*` rules really want dependency objects
69 expressed as archives rather than raw object files, so I've modified
70 `otbn-build` to also emit an archive file.
71
72 _Morally_, the otbn resource is a data dependency. However the
73 practical meaning of a `data` dependency in bazel is a file made
74 available at runtime, which is not how we're using the otbn resource.
75 The closest analog is something like `cc_embed_data`, which is like
76 a data dependency that needs to be linked into the main program.
Timothy Trippelfc197342022-04-20 14:38:11 -070077 We achieve by having `otbn_build.py` emit a conventional RV32I library
Chris Frantz9b34e4a2021-11-24 17:03:12 -080078 that other rules can depend on in their `deps`.
79 """
Miguel Young de la Sota4b01c7a2022-04-21 17:25:52 -040080 cc_toolchain = find_cc_toolchain(ctx).cc
Jade Philipoom39c9b322022-03-08 12:27:10 +000081 assembler = _get_assembler(cc_toolchain)
82
83 # Run the otbn assembler on source files to produce object (.o) files.
84 objs = _otbn_assemble_sources(ctx)
85
86 # Declare output files.
Chris Frantz9b34e4a2021-11-24 17:03:12 -080087 elf = ctx.actions.declare_file(ctx.attr.name + ".elf")
88 rv32embed = ctx.actions.declare_file(ctx.attr.name + ".rv32embed.o")
89 archive = ctx.actions.declare_file(ctx.attr.name + ".rv32embed.a")
Chris Frantz9b34e4a2021-11-24 17:03:12 -080090
Jade Philipoom39c9b322022-03-08 12:27:10 +000091 deps = [f for dep in ctx.attr.deps for f in dep.files.to_list()]
Chris Frantz9b34e4a2021-11-24 17:03:12 -080092
Jade Philipoom39c9b322022-03-08 12:27:10 +000093 # Run the otbn_build.py script to link object files from the sources and
94 # dependencies.
Chris Frantz9b34e4a2021-11-24 17:03:12 -080095 ctx.actions.run(
Jade Philipoom39c9b322022-03-08 12:27:10 +000096 outputs = [elf, rv32embed, archive],
97 inputs = (objs +
98 deps +
Chris Frantz9b34e4a2021-11-24 17:03:12 -080099 cc_toolchain.all_files.to_list() +
Chris Frantz9b34e4a2021-11-24 17:03:12 -0800100 ctx.files._otbn_data +
Timothy Trippel024e3932022-04-20 15:40:55 -0700101 [ctx.executable._wrapper]),
Chris Frantz9b34e4a2021-11-24 17:03:12 -0800102 env = {
Chris Frantz9b34e4a2021-11-24 17:03:12 -0800103 "RV32_TOOL_AS": assembler.path,
104 "RV32_TOOL_AR": cc_toolchain.ar_executable,
105 "RV32_TOOL_LD": cc_toolchain.ld_executable,
106 "RV32_TOOL_OBJCOPY": cc_toolchain.objcopy_executable,
107 },
108 arguments = [
109 "--app-name={}".format(ctx.attr.name),
110 "--archive",
Jade Philipoom39c9b322022-03-08 12:27:10 +0000111 "--no-assembler",
Chris Frantz9b34e4a2021-11-24 17:03:12 -0800112 "--out-dir={}".format(elf.dirname),
Jade Philipoom39c9b322022-03-08 12:27:10 +0000113 ] + [obj.path for obj in (objs + deps)],
Timothy Trippel024e3932022-04-20 15:40:55 -0700114 executable = ctx.executable._wrapper,
Chris Frantz9b34e4a2021-11-24 17:03:12 -0800115 )
116
117 feature_configuration = cc_common.configure_features(
118 ctx = ctx,
119 cc_toolchain = cc_toolchain,
120 requested_features = ctx.features,
121 unsupported_features = ctx.disabled_features,
122 )
123
Jade Philipoom39c9b322022-03-08 12:27:10 +0000124 outputs = objs + [elf, rv32embed, archive]
Chris Frantz9b34e4a2021-11-24 17:03:12 -0800125 return [
126 DefaultInfo(files = depset(outputs), data_runfiles = ctx.runfiles(files = outputs)),
127 OutputGroupInfo(
Jade Philipoom39c9b322022-03-08 12:27:10 +0000128 otbn_objs = depset(objs + deps),
Chris Frantz9b34e4a2021-11-24 17:03:12 -0800129 elf = depset([elf]),
130 rv32embed = depset([rv32embed]),
131 archive = depset([archive]),
132 ),
133 # Emit a CcInfo provider so that this rule can be a dependency in other
134 # cc_* rules.
135 CcInfo(
136 linking_context = cc_common.create_linking_context(
137 linker_inputs = depset([cc_common.create_linker_input(
138 owner = ctx.label,
139 libraries = depset([cc_common.create_library_to_link(
140 actions = ctx.actions,
141 feature_configuration = feature_configuration,
142 cc_toolchain = cc_toolchain,
143 static_library = archive,
144 )]),
145 )]),
146 ),
147 ),
148 ]
149
Jade Philipoom6195eef2022-04-19 10:32:36 +0100150def _otbn_sim_test(ctx):
151 """This rule is for standalone OTBN unit tests, which are run on the host
152 via the OTBN simulator.
153
154 It first generates binaries using the same method as otbn_binary, then runs
155 them on the simulator. Tests are expected to count failures in the w0
156 register; the test checks that w0=0 to determine if the test passed.
157 """
158 providers = _otbn_binary(ctx)
159
160 # Extract the output .elf file from the output group.
161 elf = providers[1].elf.to_list()[0]
162
Jade Philipoom6f32da62022-10-21 13:32:44 +0200163 exp_file = ctx.file.exp
Jade Philipoom6195eef2022-04-19 10:32:36 +0100164
Jade Philipoom955d57a2022-09-16 14:27:24 +0200165 # Create a simple script that runs the OTBN test wrapper on the .elf file
166 # using the provided simulator path.
167 sim_test_wrapper = ctx.executable._sim_test_wrapper
168 simulator = ctx.executable._simulator
169 ctx.actions.write(
170 output = ctx.outputs.executable,
171 content = "{} {} {} {}".format(sim_test_wrapper.short_path, simulator.short_path, exp_file.short_path, elf.short_path),
172 )
173
174 # Runfiles include sources, the .elf file, the simulator and test wrapper
175 # themselves, and all the simulator and test wrapper runfiles.
176 runfiles = ctx.runfiles(files = (ctx.files.srcs + [elf, exp_file, ctx.executable._simulator, ctx.executable._sim_test_wrapper]))
Jade Philipoom1b4bb0e2022-08-30 13:46:07 +0200177 runfiles = runfiles.merge(ctx.attr._simulator[DefaultInfo].default_runfiles)
Jade Philipoom955d57a2022-09-16 14:27:24 +0200178 runfiles = runfiles.merge(ctx.attr._sim_test_wrapper[DefaultInfo].default_runfiles)
Jade Philipoom6b746f72022-08-01 09:52:15 +0100179 return [
Jade Philipoom955d57a2022-09-16 14:27:24 +0200180 DefaultInfo(runfiles = runfiles),
Jade Philipoom6b746f72022-08-01 09:52:15 +0100181 providers[1],
182 ]
183
184def _otbn_consttime_test_impl(ctx):
185 """This rule checks if a program or subroutine is constant-time.
186
187 There are some limitations to this check; see the Python script's
188 documentation for details. In particular, the check may not be able to
189 determine that a program runs in constant-time when in fact it does.
190 However, if the check passes, the program should always run in constant
191 time; that is, the check can produce false negatives but never false
192 positives.
193
194 This rule expects one dependency of an otbn_binary or otbn_sim_test type,
195 which should provide exactly one `.elf` file.
196 """
197
198 # Extract the output .elf file from the output group.
199 elf = [f for t in ctx.attr.deps for f in t[OutputGroupInfo].elf.to_list()]
200 if len(elf) != 1:
201 fail("Expected only one .elf file in dependencies, got: " + str(elf))
202 elf = elf[0]
203
204 # Write a very simple script that runs the checker.
205 script_content = "{} {} --verbose".format(ctx.executable._checker.short_path, elf.short_path)
206 if ctx.attr.subroutine:
207 script_content += " --subroutine {}".format(ctx.attr.subroutine)
208 if ctx.attr.secrets:
209 script_content += " --secrets {}".format(" ".join(ctx.attr.secrets))
Jade Philipoom18a74792022-08-08 17:15:19 +0100210 if ctx.attr.initial_constants:
211 script_content += " --constants {}".format(" ".join(ctx.attr.initial_constants))
Jade Philipoom6b746f72022-08-01 09:52:15 +0100212 ctx.actions.write(
213 output = ctx.outputs.executable,
214 content = script_content,
215 )
216
217 # The .elf file must be added to runfiles in order to be visible to the
218 # test at runtime. In addition, we need to add all the runfiles from the
219 # checker script itself (e.g. the Python runtime and dependencies).
220 runfiles = ctx.runfiles(files = [elf])
221 runfiles = runfiles.merge(ctx.attr._checker[DefaultInfo].default_runfiles)
222 return [DefaultInfo(runfiles = runfiles)]
Jade Philipoom6195eef2022-04-19 10:32:36 +0100223
Jade Philipoomf05ed4d2022-07-28 17:27:21 +0100224def _otbn_insn_count_range(ctx):
225 """This rule gets min/max possible instruction counts for an OTBN program.
226 """
227
228 # Extract the .elf file to check from the dependency list.
229 elf = [f for t in ctx.attr.deps for f in t[OutputGroupInfo].elf.to_list()]
230 if len(elf) != 1:
231 fail("Expected only one .elf file in dependencies, got: " + str(elf))
232 elf = elf[0]
233
234 # Command to run the counter script and extract the min/max values.
235 out = ctx.actions.declare_file(ctx.attr.name + ".txt")
236 ctx.actions.run_shell(
237 outputs = [out],
238 inputs = [ctx.file._counter, elf],
239 command = "{} {} > {}".format(ctx.file._counter.path, elf.path, out.path),
240 )
241
242 runfiles = ctx.runfiles(files = ([out]))
243 return [DefaultInfo(files = depset([out]), runfiles = runfiles)]
244
Miguel Young de la Sota3b5a9f52022-03-24 16:11:42 -0400245otbn_library = rv_rule(
Jade Philipoom39c9b322022-03-08 12:27:10 +0000246 implementation = _otbn_library,
Jade Philipoom39c9b322022-03-08 12:27:10 +0000247 attrs = {
248 "srcs": attr.label_list(allow_files = True),
Timothy Trippelfc197342022-04-20 14:38:11 -0700249 "_cc_toolchain": attr.label(
250 default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
251 ),
252 "_otbn_as": attr.label(
253 default = "//hw/ip/otbn/util:otbn_as",
254 executable = True,
255 cfg = "exec",
256 ),
Jade Philipoom39c9b322022-03-08 12:27:10 +0000257 },
258 fragments = ["cpp"],
259 toolchains = ["@rules_cc//cc:toolchain_type"],
260 incompatible_use_toolchain_transition = True,
261)
262
Miguel Young de la Sota3b5a9f52022-03-24 16:11:42 -0400263otbn_binary = rv_rule(
Chris Frantz9b34e4a2021-11-24 17:03:12 -0800264 implementation = _otbn_binary,
Chris Frantz9b34e4a2021-11-24 17:03:12 -0800265 attrs = {
266 "srcs": attr.label_list(allow_files = True),
Jade Philipoom39c9b322022-03-08 12:27:10 +0000267 "deps": attr.label_list(providers = [DefaultInfo]),
Chris Frantz9b34e4a2021-11-24 17:03:12 -0800268 "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")),
Timothy Trippelfc197342022-04-20 14:38:11 -0700269 "_otbn_as": attr.label(
270 default = "//hw/ip/otbn/util:otbn_as",
271 executable = True,
272 cfg = "exec",
273 ),
Timothy Trippelfc197342022-04-20 14:38:11 -0700274 "_otbn_data": attr.label(
275 default = "//hw/ip/otbn/data:all_files",
276 allow_files = True,
277 ),
278 "_wrapper": attr.label(
Timothy Trippel024e3932022-04-20 15:40:55 -0700279 default = "//util:otbn_build",
280 executable = True,
281 cfg = "exec",
Timothy Trippelfc197342022-04-20 14:38:11 -0700282 ),
Chris Frantz9b34e4a2021-11-24 17:03:12 -0800283 },
284 fragments = ["cpp"],
285 toolchains = ["@rules_cc//cc:toolchain_type"],
286 incompatible_use_toolchain_transition = True,
287)
Jade Philipoom6195eef2022-04-19 10:32:36 +0100288
289otbn_sim_test = rv_rule(
290 implementation = _otbn_sim_test,
291 test = True,
292 attrs = {
293 "srcs": attr.label_list(allow_files = True),
294 "deps": attr.label_list(providers = [DefaultInfo]),
Jade Philipoom6f32da62022-10-21 13:32:44 +0200295 "exp": attr.label(allow_single_file = True),
Jade Philipoom6195eef2022-04-19 10:32:36 +0100296 "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")),
297 "_otbn_as": attr.label(
298 default = "//hw/ip/otbn/util:otbn_as",
299 executable = True,
300 cfg = "exec",
301 ),
302 "_otbn_data": attr.label(
303 default = "//hw/ip/otbn/data:all_files",
304 allow_files = True,
305 ),
Jade Philipoom1b4bb0e2022-08-30 13:46:07 +0200306 "_simulator": attr.label(
307 default = "//hw/ip/otbn/dv/otbnsim:standalone",
308 executable = True,
309 cfg = "exec",
310 ),
Jade Philipoom955d57a2022-09-16 14:27:24 +0200311 "_sim_test_wrapper": attr.label(
312 default = "//hw/ip/otbn/util:otbn_sim_test",
313 executable = True,
314 cfg = "exec",
315 ),
Jade Philipoom6195eef2022-04-19 10:32:36 +0100316 "_wrapper": attr.label(
317 default = "//util:otbn_build",
318 executable = True,
319 cfg = "exec",
320 ),
321 },
322 fragments = ["cpp"],
323 toolchains = ["@rules_cc//cc:toolchain_type"],
324 incompatible_use_toolchain_transition = True,
325)
Jade Philipoom6b746f72022-08-01 09:52:15 +0100326
327otbn_consttime_test = rule(
328 implementation = _otbn_consttime_test_impl,
329 test = True,
330 attrs = {
331 "srcs": attr.label_list(allow_files = True),
332 "deps": attr.label_list(providers = [OutputGroupInfo]),
333 "subroutine": attr.string(),
334 "secrets": attr.string_list(),
Jade Philipoom18a74792022-08-08 17:15:19 +0100335 "initial_constants": attr.string_list(),
Jade Philipoom6b746f72022-08-01 09:52:15 +0100336 "_checker": attr.label(
337 default = "//hw/ip/otbn/util:check_const_time",
338 executable = True,
339 cfg = "exec",
340 ),
341 },
342)
Jade Philipoomf05ed4d2022-07-28 17:27:21 +0100343
344otbn_insn_count_range = rule(
345 implementation = _otbn_insn_count_range,
346 attrs = {
347 "deps": attr.label_list(providers = [OutputGroupInfo]),
348 "_counter": attr.label(
349 default = "//hw/ip/otbn/util:get_instruction_count_range.py",
350 allow_single_file = True,
351 ),
352 },
353)