Skip to content

Commit 28ebb70

Browse files
bors[bot]Johnathan Van Why
and
Johnathan Van Why
authored
Merge #365
365: Add a runner for libtock2 examples. r=hudson-ayers a=jrvanwhy This replaces `tools/flash.sh`. In the future, it will have functionality to process test output, and will therefore replace `test_runner` as well. It can currently deploy to HiFive1 on QEMU, and has untested logic to deploy via `tockloader`. It is no longer possible to deploy Tock 1.0 libtock-rs apps using `cargo run`, as `cargo run` will now invoke the new tool. This prevents us from running the Tock 1.0 libtock-rs integration test, so I have removed it from `make test`. I also make the following changes to the submodule setup: 1. I added a `setup-qemu-2` target to build QEMU in the tock2/ submodule. When I remove the `tock/` submodule, I'll also remove the `setup-qemu` target and rename `setup-qemu-2` to `setup-qemu` to replace it. 2. I updated the `tock2/` submodule to a newer kernel. Because `libtock-rs` depends on the new Allow API implementation that is not in a released Tock version, I used the latest master branch commit. Co-authored-by: Johnathan Van Why <[email protected]>
2 parents a0440ba + 46916e1 commit 28ebb70

File tree

11 files changed

+515
-7
lines changed

11 files changed

+515
-7
lines changed

.cargo/config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ rustflags = [
1212
"-C", "relocation-model=static",
1313
"-C", "link-arg=-Tlayout.ld",
1414
]
15-
runner = "./tools/flash.sh"
15+
runner = ["cargo", "run", "-p", "runner", "--release"]

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
- name: Build and Test
4444
run: |
4545
sudo apt-get install binutils-arm-none-eabi \
46-
binutils-riscv64-unknown-elf
46+
binutils-riscv64-unknown-elf ninja-build
4747
cd "${GITHUB_WORKSPACE}"
4848
echo "[target.'cfg(all())']" >> .cargo/config
4949
echo 'rustflags = ["-D", "warnings"]' >> .cargo/config

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ lto = true
8282
debug = true
8383

8484
[workspace]
85-
exclude = [ "tock" ]
85+
exclude = ["tock", "tock2"]
8686
members = [
8787
"apis/leds",
8888
"apis/low_level_debug",
@@ -91,6 +91,7 @@ members = [
9191
"libtock2",
9292
"panic_handlers/small_panic",
9393
"platform",
94+
"runner",
9495
"runtime",
9596
"syscalls_tests",
9697
"test_runner",

Makefile

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ usage:
2626
@echo " Set the DEBUG flag to enable the debug build"
2727
@echo " Set the FEATURES flag to enable features"
2828
@echo "Run 'make flash-<board> EXAMPLE=<>' to flash EXAMPLE to that board"
29+
@echo "Run 'make qemu-example EXAMPLE=<>' to run EXAMPLE in QEMU"
2930
@echo "Run 'make test' to test any local changes you have made"
3031
@echo "Run 'make print-sizes' to print size data for the example binaries"
3132

@@ -38,7 +39,7 @@ release=--release
3839
endif
3940

4041
.PHONY: setup
41-
setup: setup-qemu
42+
setup: setup-qemu setup-qemu-2
4243
cargo install elf2tab
4344
cargo install stack-sizes
4445
cargo miri setup
@@ -49,6 +50,12 @@ setup: setup-qemu
4950
setup-qemu:
5051
CI=true $(MAKE) -C tock ci-setup-qemu
5152

53+
# Sets up QEMU in the tock2/ directory. We use Tock's QEMU which may contain
54+
# patches to better support boards that Tock supports.
55+
.PHONY: setup-qemu-2
56+
setup-qemu-2:
57+
CI=true $(MAKE) -C tock2 ci-setup-qemu
58+
5259
# Builds a Tock kernel for the HiFive board for use by QEMU tests.
5360
.PHONY: kernel-hifive
5461
kernel-hifive:
@@ -63,11 +70,25 @@ kernel-hifive-2:
6370
$(MAKE) -C tock2/boards/hifive1 \
6471
$(CURDIR)/tock2/target/riscv32imac-unknown-none-elf/release/hifive1.elf
6572

73+
# Builds a Tock kernel for the OpenTitan board on the cw310 FPGA for use by QEMU
74+
# tests.
75+
.PHONY: kernel-opentitan
76+
kernel-opentitan:
77+
CARGO_TARGET_RISCV32IMC_UNKNOWN_NONE_ELF_RUNNER="[]" \
78+
$(MAKE) -C tock2/boards/opentitan/earlgrey-cw310 \
79+
$(CURDIR)/tock2/target/riscv32imc-unknown-none-elf/release/earlgrey-cw310.elf
80+
6681
# Prints out the sizes of the example binaries.
6782
.PHONY: print-sizes
6883
print-sizes: examples
6984
cargo run --release -p print_sizes
7085

86+
# Runs a libtock2 example in QEMU on a simulated HiFive board.
87+
.PHONY: qemu-example
88+
qemu-example: kernel-hifive-2
89+
LIBTOCK_PLATFORM="hifive1" cargo run --example "$(EXAMPLE)" -p libtock2 \
90+
--release --target=riscv32imac-unknown-none-elf -- --deploy qemu
91+
7192
# Runs the libtock_test tests in QEMU on a simulated HiFive board.
7293
.PHONY: test-qemu-hifive
7394
test-qemu-hifive: kernel-hifive
@@ -100,7 +121,7 @@ EXCLUDE_MIRI := $(EXCLUDE_RUNTIME) --exclude libtock_codegen \
100121
# Arguments to pass to cargo to exclude `std` and crates that depend on it. Used
101122
# when we build a crate for an embedded target, as those targets lack `std`.
102123
EXCLUDE_STD := --exclude libtock_unittest --exclude print_sizes \
103-
--exclude syscalls_tests --exclude test_runner
124+
--exclude runner --exclude syscalls_tests --exclude test_runner
104125

105126
# Some of our crates should build with a stable toolchain. This verifies those
106127
# crates don't depend on unstable features by using cargo check. We specify a
@@ -112,7 +133,7 @@ test-stable:
112133
$(EXCLUDE_RUNTIME) --exclude libtock --exclude libtock_core
113134

114135
.PHONY: test
115-
test: examples test-qemu-hifive test-stable
136+
test: examples test-stable
116137
PLATFORM=nrf52 cargo test $(EXCLUDE_RUNTIME) --workspace
117138
# TODO: When we have a working embedded test harness, change the libtock2
118139
# builds to --all-targets rather than --examples.
@@ -238,3 +259,4 @@ flash-msp432:
238259
clean:
239260
cargo clean
240261
$(MAKE) -C tock clean
262+
$(MAKE) -C tock2 clean

runner/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
authors = ["Tock Project Developers <[email protected]>"]
3+
description = """Tool used to run libtock-rs process binaries."""
4+
edition = "2018"
5+
license = "Apache-2.0 OR MIT"
6+
name = "runner"
7+
publish = false
8+
repository = "https://www.github.com/tock/libtock-rs"
9+
version = "0.1.0"
10+
11+
[dependencies]
12+
clap = { features = ["derive"], version = "3.0.10" }
13+
elf = "0.0.10"
14+
libc = "0.2.113"
15+
termion = "1.5.6"

runner/src/elf2tab.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use super::Cli;
2+
use std::fs::{metadata, remove_file};
3+
use std::io::ErrorKind;
4+
use std::path::PathBuf;
5+
use std::process::Command;
6+
7+
// Converts the ELF file specified on the command line into TBF and TAB files,
8+
// and returns the paths to those files.
9+
pub fn convert_elf(cli: &Cli) -> OutFiles {
10+
let package_name = cli.elf.file_stem().expect("ELF must be a file");
11+
let mut tab_path = cli.elf.clone();
12+
tab_path.set_extension("tab");
13+
let protected_size = TBF_HEADER_SIZE.to_string();
14+
if cli.verbose {
15+
println!("Package name: {:?}", package_name);
16+
println!("TAB path: {}", tab_path.display());
17+
println!("Protected region size: {}", protected_size);
18+
}
19+
let stack_size = read_stack_size(cli);
20+
let elf = cli.elf.as_os_str();
21+
let mut tbf_path = cli.elf.clone();
22+
tbf_path.set_extension("tbf");
23+
if cli.verbose {
24+
println!("ELF file: {:?}", elf);
25+
println!("TBF path: {}", tbf_path.display());
26+
}
27+
28+
// If elf2tab returns a successful status but does not write to the TBF
29+
// file, then we run the risk of using an outdated TBF file, creating a
30+
// hard-to-debug situation. Therefore, we delete the TBF file, forcing
31+
// elf2tab to create it, and later verify that it exists.
32+
if let Err(io_error) = remove_file(&tbf_path) {
33+
// Ignore file-no-found errors, panic on any other error.
34+
if io_error.kind() != ErrorKind::NotFound {
35+
panic!("Unable to remove the TBF file. Error: {}", io_error);
36+
}
37+
}
38+
39+
let mut command = Command::new("elf2tab");
40+
#[rustfmt::skip]
41+
command.args([
42+
// TODO: libtock-rs' crates are designed for Tock 2.1's Allow interface,
43+
// so we should increment this as soon as the Tock kernel will accept a
44+
// 2.1 app.
45+
"--kernel-major".as_ref(), "2".as_ref(),
46+
"--kernel-minor".as_ref(), "0".as_ref(),
47+
"-n".as_ref(), package_name,
48+
"-o".as_ref(), tab_path.as_os_str(),
49+
"--protected-region-size".as_ref(), protected_size.as_ref(),
50+
"--stack".as_ref(), stack_size.as_ref(),
51+
elf,
52+
]);
53+
if cli.verbose {
54+
command.arg("-v");
55+
println!("elf2tab command: {:?}", command);
56+
println!("Spawning elf2tab");
57+
}
58+
let mut child = command.spawn().expect("failed to spawn elf2tab");
59+
let status = child.wait().expect("failed to wait for elf2tab");
60+
if cli.verbose {
61+
println!("elf2tab finished. {}", status);
62+
}
63+
assert!(status.success(), "elf2tab returned an error. {}", status);
64+
65+
// Verify that elf2tab created the TBF file, and that it is a file.
66+
match metadata(&tbf_path) {
67+
Err(io_error) => {
68+
if io_error.kind() == ErrorKind::NotFound {
69+
panic!("elf2tab did not create {}", tbf_path.display());
70+
}
71+
panic!(
72+
"Unable to query metadata for {}: {}",
73+
tbf_path.display(),
74+
io_error
75+
);
76+
}
77+
Ok(metadata) => {
78+
assert!(metadata.is_file(), "{} is not a file", tbf_path.display());
79+
}
80+
}
81+
82+
OutFiles { tab_path, tbf_path }
83+
}
84+
85+
// Paths to the files output by elf2tab.
86+
pub struct OutFiles {
87+
pub tab_path: PathBuf,
88+
pub tbf_path: PathBuf,
89+
}
90+
91+
// The amount of space to reserve for the TBF header. This must match the
92+
// TBF_HEADER_SIZE value in the layout file for the platform, which is currently
93+
// 0x48 for all platforms.
94+
const TBF_HEADER_SIZE: u32 = 0x48;
95+
96+
// Reads the stack size, and returns it as a String for use on elf2tab's command
97+
// line.
98+
fn read_stack_size(cli: &Cli) -> String {
99+
let file = elf::File::open_path(&cli.elf).expect("Unable to open ELF");
100+
for section in file.sections {
101+
// This section name comes from runtime/libtock_layout.ld, and it
102+
// matches the size (and location) of the process binary's stack.
103+
if section.shdr.name == ".stack" {
104+
let stack_size = section.shdr.size.to_string();
105+
if cli.verbose {
106+
println!("Found .stack section, size: {}", stack_size);
107+
}
108+
return stack_size;
109+
}
110+
}
111+
112+
panic!("Unable to find the .stack section in {}", cli.elf.display());
113+
}

runner/src/main.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
mod elf2tab;
2+
mod output_processor;
3+
mod qemu;
4+
mod tockloader;
5+
6+
use clap::{ArgEnum, Parser};
7+
use std::env::{var, VarError};
8+
use std::path::PathBuf;
9+
10+
/// Converts ELF binaries into Tock Binary Format binaries and runs them on a
11+
/// Tock system.
12+
#[derive(Debug, Parser)]
13+
pub struct Cli {
14+
/// Where to deploy the process binary. If not specified, runner will only
15+
/// make a TBF file and not attempt to run it.
16+
#[clap(arg_enum, long, short)]
17+
deploy: Option<Deploy>,
18+
19+
/// The executable to convert into Tock Binary Format and run.
20+
elf: PathBuf,
21+
22+
/// Whether to output verbose debugging information to the console.
23+
#[clap(long, short)]
24+
verbose: bool,
25+
}
26+
27+
#[derive(ArgEnum, Clone, Copy, Debug)]
28+
pub enum Deploy {
29+
Qemu,
30+
Tockloader,
31+
}
32+
33+
fn main() {
34+
let cli = Cli::parse();
35+
let paths = elf2tab::convert_elf(&cli);
36+
let deploy = match cli.deploy {
37+
None => return,
38+
Some(deploy) => deploy,
39+
};
40+
let platform = match var("LIBTOCK_PLATFORM") {
41+
Err(VarError::NotPresent) => {
42+
panic!("LIBTOCK_PLATFORM must be specified to deploy")
43+
}
44+
Err(VarError::NotUnicode(platform)) => {
45+
panic!("Non-UTF-8 LIBTOCK_PLATFORM value: {:?}", platform)
46+
}
47+
Ok(platform) => platform,
48+
};
49+
if cli.verbose {
50+
println!("Detected platform {}", platform);
51+
}
52+
let child = match deploy {
53+
Deploy::Qemu => qemu::deploy(&cli, platform, paths.tbf_path),
54+
Deploy::Tockloader => tockloader::deploy(&cli, platform, paths.tab_path),
55+
};
56+
output_processor::process(&cli, child);
57+
}

0 commit comments

Comments
 (0)