-
Notifications
You must be signed in to change notification settings - Fork 115
Add a runner for libtock2 examples. #365
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
2c45730
Add a runner for libtock2 examples.
4f34598
Install Ninja in the CI workflow, as QEMU now requires it.
e2505b5
Remove the libtock-rs 1.0 integration tests, as they do not pass with…
dc96125
Address review feedback:
22b0cc6
runner: Add support for multiple platforms in the QEMU deployer.
a67900f
Add a `kernel-opentitan` make action that builds an OpenTitan kernel …
46916e1
Fix variable name in the QEMU deployer: tab_path -> tbf_path, and cla…
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[package] | ||
authors = ["Tock Project Developers <[email protected]>"] | ||
description = """Tool used to run libtock-rs process binaries.""" | ||
edition = "2018" | ||
license = "Apache-2.0 OR MIT" | ||
name = "runner" | ||
publish = false | ||
repository = "https://www.github.com/tock/libtock-rs" | ||
version = "0.1.0" | ||
|
||
[dependencies] | ||
clap = { features = ["derive"], version = "3.0.10" } | ||
elf = "0.0.10" | ||
signal-hook = "0.3.13" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
use super::Cli; | ||
use std::fs::{metadata, remove_file}; | ||
use std::io::ErrorKind; | ||
use std::path::PathBuf; | ||
use std::process::Command; | ||
|
||
// Converts the ELF file specified on the command line into TBF and TAB files, | ||
// and returns the paths to those files. | ||
pub fn convert_elf(cli: &Cli) -> OutFiles { | ||
let package_name = cli.elf.file_stem().expect("ELF must be a file"); | ||
let mut tab_path = cli.elf.clone(); | ||
tab_path.set_extension("tab"); | ||
let protected_size = TBF_HEADER_SIZE.to_string(); | ||
if cli.verbose { | ||
println!("Package name: {:?}", package_name); | ||
println!("TAB path: {}", tab_path.display()); | ||
println!("Protected region size: {}", protected_size); | ||
} | ||
let stack_size = read_stack_size(cli); | ||
let elf = cli.elf.as_os_str(); | ||
let mut tbf_path = cli.elf.clone(); | ||
tbf_path.set_extension("tbf"); | ||
if cli.verbose { | ||
println!("ELF file: {:?}", elf); | ||
println!("TBF path: {}", tbf_path.display()); | ||
} | ||
|
||
// If elf2tab returns a successful status but does not write to the TBF | ||
// file, then we run the risk of using an outdated TBF file, creating a | ||
// hard-to-debug situation. Therefore, we delete the TBF file, forcing | ||
// elf2tab to create it, and later verify that it exists. | ||
if let Err(io_error) = remove_file(&tbf_path) { | ||
// Ignore file-no-found errors, panic on any other error. | ||
assert_eq!( | ||
io_error.kind(), | ||
ErrorKind::NotFound, | ||
"Unable to remove the TBF file" | ||
jrvanwhy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
); | ||
} | ||
|
||
let mut command = Command::new("elf2tab"); | ||
#[rustfmt::skip] | ||
command.args([ | ||
// TODO: libtock-rs' crates are designed for Tock 2.1's Allow interface, | ||
// so we should increment this as soon as the Tock kernel will accept a | ||
// 2.1 app. | ||
"--kernel-major".as_ref(), "2".as_ref(), | ||
"--kernel-minor".as_ref(), "0".as_ref(), | ||
"-n".as_ref(), package_name, | ||
"-o".as_ref(), tab_path.as_os_str(), | ||
"--protected-region-size".as_ref(), protected_size.as_ref(), | ||
"--stack".as_ref(), stack_size.as_ref(), | ||
elf, | ||
]); | ||
if cli.verbose { | ||
command.arg("-v"); | ||
println!("elf2tab command: {:?}", command); | ||
println!("Spawning elf2tab"); | ||
} | ||
let mut child = command.spawn().expect("failed to spawn elf2tab"); | ||
let status = child.wait().expect("failed to wait for elf2tab"); | ||
if cli.verbose { | ||
println!("elf2tab finished. {}", status); | ||
} | ||
assert!(status.success(), "elf2tab returned an error. {}", status); | ||
|
||
// Verify that elf2tab created the TBF file, and that it is a file. | ||
match metadata(&tbf_path) { | ||
Err(io_error) => { | ||
if io_error.kind() == ErrorKind::NotFound { | ||
panic!("elf2tab did not create {}", tbf_path.display()); | ||
} | ||
panic!( | ||
"Unable to query metadata for {}: {}", | ||
tbf_path.display(), | ||
io_error | ||
); | ||
} | ||
Ok(metadata) => { | ||
assert!(metadata.is_file(), "{} is not a file", tbf_path.display()); | ||
} | ||
} | ||
|
||
OutFiles { tab_path, tbf_path } | ||
} | ||
|
||
// Paths to the files output by elf2tab. | ||
pub struct OutFiles { | ||
pub tab_path: PathBuf, | ||
pub tbf_path: PathBuf, | ||
} | ||
|
||
// The amount of space to reserve for the TBF header. | ||
const TBF_HEADER_SIZE: u32 = 0x48; | ||
alistair23 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Reads the stack size, and returns it as a String for use on elf2tab's command | ||
// line. | ||
fn read_stack_size(cli: &Cli) -> String { | ||
let file = elf::File::open_path(&cli.elf).expect("Unable to open ELF"); | ||
for section in file.sections { | ||
// This section name comes from runtime/libtock_layout.ld, and it | ||
// matches the size (and location) of the process binary's stack. | ||
if section.shdr.name == ".stack" { | ||
let stack_size = section.shdr.size.to_string(); | ||
if cli.verbose { | ||
println!("Found .stack section, size: {}", stack_size); | ||
} | ||
return stack_size; | ||
} | ||
} | ||
|
||
panic!("Unable to find the .stack section in {}", cli.elf.display()); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
mod elf2tab; | ||
mod output_processor; | ||
mod qemu; | ||
mod tockloader; | ||
|
||
use clap::{ArgEnum, Parser}; | ||
use std::path::PathBuf; | ||
|
||
/// Converts ELF binaries into Tock Binary Format binaries and runs them on a | ||
/// Tock system. | ||
#[derive(Debug, Parser)] | ||
pub struct Cli { | ||
/// Where to deploy the process binary. If not specified, runner will only | ||
/// make a TBF file and not attempt to run it. | ||
#[clap(arg_enum, long, short)] | ||
deploy: Option<Deploy>, | ||
|
||
/// The executable to convert into Tock Binary Format and run. | ||
elf: PathBuf, | ||
|
||
/// Whether to output verbose debugging information to the console. | ||
#[clap(long, short)] | ||
verbose: bool, | ||
} | ||
|
||
#[derive(ArgEnum, Clone, Debug)] | ||
pub enum Deploy { | ||
Qemu, | ||
Tockloader, | ||
} | ||
|
||
fn main() { | ||
let cli = Cli::parse(); | ||
let paths = elf2tab::convert_elf(&cli); | ||
let child = match cli.deploy { | ||
None => return, | ||
Some(Deploy::Qemu) => qemu::deploy(&cli, paths.tbf_path), | ||
Some(Deploy::Tockloader) => tockloader::deploy(&cli, paths.tab_path), | ||
}; | ||
output_processor::process(&cli, child); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
use super::Cli; | ||
use signal_hook::flag::{register, register_conditional_default}; | ||
use std::io::{stdout, BufReader, Read, Write}; | ||
use std::process::Child; | ||
use std::sync::atomic::AtomicBool; | ||
use std::sync::Arc; | ||
|
||
/// Reads the console messages from `child`'s standard output, sending SIGTERM | ||
/// to the child when the process is terminated. | ||
pub fn process(cli: &Cli, mut child: Child) { | ||
// When Ctrl+C is pressed, Bash sends SIGINT to both us and our child | ||
// process. If we finish before the child process does, then bash will print | ||
// the shell prompt before the child process prints its final messages, | ||
// which is annoying for the user. | ||
// | ||
// So instead, we ignore the first SIGINT we receive. Ctrl+C should | ||
// therefore result in a clean shutdown: the child process exits, then we | ||
// see that it finished and terminates. As a fail-safe in case we fail to | ||
// terminate, this combination will make us exit if SIGINT is received a | ||
// second time. | ||
let sigint_received = Arc::new(AtomicBool::new(false)); | ||
register_conditional_default(signal_hook::consts::SIGINT, sigint_received.clone()) | ||
.expect("Unable to register SIGINT conditional handler."); | ||
register(signal_hook::consts::SIGINT, sigint_received) | ||
.expect("Unable to register SIGINT handler."); | ||
|
||
let reader = BufReader::new(child.stdout.as_mut().expect("Child's stdout not piped.")); | ||
for byte in reader.bytes() { | ||
let byte = byte.expect("Unexpected IO error."); | ||
stdout() | ||
.write_all(&[byte]) | ||
.expect("Failed to write to stdout."); | ||
} | ||
if cli.verbose { | ||
println!("Waiting for child process to exit"); | ||
} | ||
let status = child.wait().expect("Unable to wait for child process"); | ||
assert!( | ||
status.success(), | ||
"Child process did not exit successfully. {}", | ||
status | ||
); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
use super::Cli; | ||
use std::path::PathBuf; | ||
use std::process::{Child, Command, Stdio}; | ||
|
||
// Spawns a QEMU VM with a simulated Tock system and the process binary. Returns | ||
// the handle for the spawned QEMU process. | ||
pub fn deploy(cli: &Cli, tab_path: PathBuf) -> Child { | ||
let device = format!( | ||
"loader,file={},addr=0x20040000", | ||
tab_path | ||
jrvanwhy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.into_os_string() | ||
.into_string() | ||
.expect("Non-UTF-8 path") | ||
); | ||
let mut qemu = Command::new("tock2/tools/qemu/build/qemu-system-riscv32"); | ||
#[rustfmt::skip] | ||
qemu.args([ | ||
"-device", &device, | ||
"-kernel", "tock2/target/riscv32imac-unknown-none-elf/release/hifive1", | ||
"-M", "sifive_e,revb=true", | ||
"-nographic", | ||
jrvanwhy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
]); | ||
// QEMU does something to its stdin that prevents Ctrl+C from generating | ||
// SIGINT. If we set QEMU's stdin to be our stdin, then Ctrl+C will not | ||
// close us. To prevent that, we set QEMU's stdin to null. | ||
qemu.stdin(Stdio::null()); | ||
alistair23 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
qemu.stdout(Stdio::piped()); | ||
if cli.verbose { | ||
println!("QEMU command: {:?}", qemu); | ||
println!("Spawning QEMU") | ||
} | ||
qemu.spawn().expect("failed to spawn QEMU") | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.