Skip to content

Commit

Permalink
hil: initial commit (#97)
Browse files Browse the repository at this point in the history
Adds an orb-hil binary, to make flashing easy and automated. Its little
more than a glorified bash script right now.

Currently it:
* Assumes that it is on the hil and relies on some cli tools provided
from the nix shell, like awscli and tar
* Supports flashing from a s3 url or a local tarball

It does not yet support:
* Automatically controlling CTS and RTS pins to reboot orb or trigger
recovery mode
* Connecting to serial
* Retrieving s3 urls from a git tag
* Automatically running in response to network events or github runners
  • Loading branch information
TheButlah authored May 14, 2024
1 parent 3f5d86f commit d854318
Show file tree
Hide file tree
Showing 8 changed files with 418 additions and 0 deletions.
127 changes: 127 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ members = [
"deps-tests",
"endpoints",
"header-parsing",
"hil",
"mcu-util",
"orb-attest",
"orb-backend-state",
Expand Down
27 changes: 27 additions & 0 deletions hil/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "orb-hil"
version = "0.0.0"
description = "Everything related to hardware-in-loop"
authors = ["Ryan Butler <[email protected]>"]
publish = false

edition.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true

[dependencies]
camino = "1.1.6"
clap = { version = "4", features = ["derive"] }
cmd_lib = "1.9.3"
color-eyre = "0.6"
orb-build-info.path = "../build-info"
orb-security-utils = { path = "../security-utils", features = ["reqwest"] }
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls"] }
tempfile = "3"
tokio = { version = "1", default-features = false, features = ["macros"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

[build-dependencies]
orb-build-info = { path = "../build-info", features = ["build-script"] }
1 change: 1 addition & 0 deletions hil/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# orb-hil
3 changes: 3 additions & 0 deletions hil/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
orb_build_info::initialize().expect("failed to initialize build info")
}
80 changes: 80 additions & 0 deletions hil/src/download_s3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use camino::Utf8Path;
use cmd_lib::run_cmd;
use color_eyre::{
eyre::{ensure, ContextCompat, WrapErr},
Result, Section,
};

pub async fn download_url(url: &str, out_path: &Utf8Path) -> Result<()> {
let parent_dir = out_path
.parent()
.expect("please provide the path to a file");
ensure!(
parent_dir.try_exists().unwrap_or(false),
"parent directory {parent_dir} doesn't exist"
);
let out_path_copy = out_path.to_owned();
let url_copy = url.to_owned();
tokio::task::spawn_blocking(move || {
download_using_awscli(&url_copy, &out_path_copy)
})
.await
.wrap_err("task panicked")?
}

fn download_using_awscli(url: &str, out_path: &Utf8Path) -> Result<()> {
let result = run_cmd! {
info downloading $url to $out_path;
aws s3 cp $url $out_path;
info finished download!;
};
result
.wrap_err("failed to call aws cli")
.with_note(|| format!("url was {url}"))
.with_note(|| format!("out_path was {out_path}"))
.with_suggestion(|| "Are the AWS url and your credentials valid?")
}

/// Calculates the filename based on the s3 url.
pub fn parse_filename(url: &str) -> Result<String> {
let expected_prefix = "s3://worldcoin-orb-update-packages-stage/worldcoin/orb-os/";
let path = url
.strip_prefix(expected_prefix)
.wrap_err_with(|| format!("missing url prefix of {expected_prefix}"))?;
let splits: Vec<_> = path.split('/').collect();
ensure!(
splits.len() == 3,
"invalid number of '/' delineated segments in the url"
);
ensure!(
splits[2].contains(".tar."),
"it doesn't look like this url ends in a tarball"
);
Ok(format!("{}-{}", splits[0], splits[2]))
}
#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_parse() -> color_eyre::Result<()> {
let examples = [
(
"s3://worldcoin-orb-update-packages-stage/worldcoin/orb-os/2024-05-07-heads-main-0-g4b8aae5/rts/rts-dev.tar.zst",
"2024-05-07-heads-main-0-g4b8aae5-rts-dev.tar.zst"
),
(
"s3://worldcoin-orb-update-packages-stage/worldcoin/orb-os/2024-05-08-remotes-pull-386-merge-0-geea20f1/rts/rts-prod.tar.zst",
"2024-05-08-remotes-pull-386-merge-0-geea20f1-rts-prod.tar.zst"
),
(
"s3://worldcoin-orb-update-packages-stage/worldcoin/orb-os/2024-05-08-tags-release-5.0.39-0-ga12b3d7/rts/rts-dev.tar.zst",
"2024-05-08-tags-release-5.0.39-0-ga12b3d7-rts-dev.tar.zst"
),
];
for (url, expected_filename) in examples {
assert_eq!(parse_filename(url)?, expected_filename);
}
Ok(())
}
}
77 changes: 77 additions & 0 deletions hil/src/flash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use std::path::Path;

use camino::Utf8Path;
use cmd_lib::run_cmd;
use color_eyre::{
eyre::{ensure, WrapErr},
Result, Section,
};
use tempfile::TempDir;

pub async fn flash(variant: FlashVariant, path_to_rts_tar: &Utf8Path) -> Result<()> {
let path_to_rts = path_to_rts_tar.to_owned();
tokio::task::spawn_blocking(move || {
let tmp_dir = extract(&path_to_rts)?;
println!("{tmp_dir:?}");
flash_cmd(variant, tmp_dir.path())?;
Ok(())
})
.await
.wrap_err("task panicked")?
}

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum FlashVariant {
Fast,
Regular,
}

impl FlashVariant {
fn file_name(&self) -> &'static str {
match self {
FlashVariant::Fast => "fastflashcmd.txt",
FlashVariant::Regular => "flashcmd.txt",
}
}
}

fn extract(path_to_rts: &Utf8Path) -> Result<TempDir> {
ensure!(
path_to_rts.try_exists().unwrap_or(false),
"{path_to_rts} doesn't exist"
);
ensure!(path_to_rts.is_file(), "{path_to_rts} should be a file!");
let temp_dir = TempDir::new_in(path_to_rts.parent().unwrap())
.wrap_err("failed to create temporary extract dir")?;
let extract_dir = temp_dir.path();
let result = run_cmd! {
cd $extract_dir;
info extracting rts $path_to_rts;
tar xvf $path_to_rts;
info finished extract!;
};
result
.wrap_err("failed to extract rts")
.with_note(|| format!("path_to_rts was {path_to_rts}"))?;
Ok(temp_dir)
}

fn flash_cmd(variant: FlashVariant, extracted_dir: &Path) -> Result<()> {
let bootloader_dir = extracted_dir.join("ready-to-sign").join("bootloader");
ensure!(
bootloader_dir.try_exists().unwrap_or(false),
"{bootloader_dir:?} doesn't exist"
);

let cmd_file_name = variant.file_name();
let result = run_cmd! {
cd $bootloader_dir;
info running $cmd_file_name;
bash $cmd_file_name;
info finished flashing!;
};
result
.wrap_err("failed to flash rts")
.with_note(|| format!("bootloader_dir was {bootloader_dir:?}"))?;
Ok(())
}
Loading

0 comments on commit d854318

Please sign in to comment.