diff --git a/Cargo.lock b/Cargo.lock index 73de6bfcae3..23d5929b014 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -377,6 +377,27 @@ dependencies = [ "serde", ] +[[package]] +name = "bzip2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "cast" version = "0.2.7" @@ -1418,6 +1439,7 @@ dependencies = [ "termios", "thiserror", "tokio", + "zip", ] [[package]] @@ -6562,6 +6584,19 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zip" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf225bcf73bb52cbb496e70475c7bd7a3f769df699c0020f6c7bd9a96dcf0b8d" +dependencies = [ + "byteorder", + "bzip2", + "crc32fast", + "crossbeam-utils", + "flate2", +] + [[package]] name = "zone" version = "0.1.8" diff --git a/gateway/faux-mgs/Cargo.toml b/gateway/faux-mgs/Cargo.toml index 110224a8344..fe468f6c138 100644 --- a/gateway/faux-mgs/Cargo.toml +++ b/gateway/faux-mgs/Cargo.toml @@ -13,6 +13,7 @@ slog-term = "2.9" termios = "0.3" thiserror = "1.0" tokio = { version = "1.20", features = ["full"] } +zip = { version = "0.6.2", default-features = false, features = ["deflate","bzip2"] } gateway-messages = { path = "../../gateway-messages", features = ["std"] } gateway-sp-comms = { path = "../../gateway-sp-comms" } diff --git a/gateway/faux-mgs/src/hubris_archive.rs b/gateway/faux-mgs/src/hubris_archive.rs new file mode 100644 index 00000000000..127927c78d6 --- /dev/null +++ b/gateway/faux-mgs/src/hubris_archive.rs @@ -0,0 +1,59 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +// Copyright 2022 Oxide Computer Company + +//! Minimal parsing of Hubris archives. + +use anyhow::Context; +use anyhow::Result; +use std::fs; +use std::io::Cursor; +use std::io::Read; +use std::path::Path; +use std::path::PathBuf; +use zip::ZipArchive; + +pub struct HubrisArchive { + archive: ZipArchive>>, + path: PathBuf, +} + +impl HubrisArchive { + pub fn open(path: &Path) -> Result { + let data = fs::read(path) + .with_context(|| format!("failed to read {}", path.display()))?; + let cursor = Cursor::new(data); + let archive = ZipArchive::new(cursor).with_context(|| { + format!( + "failed to open {} as a zip file - is it a hubris archive?", + path.display() + ) + })?; + Ok(Self { archive, path: path.to_owned() }) + } + + pub fn final_bin(&mut self) -> Result> { + self.extract_by_name("img/final.bin") + } + + fn extract_by_name(&mut self, name: &str) -> Result> { + let mut f = self.archive.by_name(name).with_context(|| { + format!( + "failed to find `{}` within {} - is it a hubris archive?", + name, + self.path.display() + ) + })?; + let mut data = Vec::new(); + f.read_to_end(&mut data).with_context(|| { + format!( + "failed to extract `{}` within {}", + name, + self.path.display() + ) + })?; + Ok(data) + } +} diff --git a/gateway/faux-mgs/src/main.rs b/gateway/faux-mgs/src/main.rs index 2e955922f98..568e3aeff27 100644 --- a/gateway/faux-mgs/src/main.rs +++ b/gateway/faux-mgs/src/main.rs @@ -16,14 +16,16 @@ use slog::o; use slog::Drain; use slog::Level; use slog::Logger; -use std::fs; use std::net::SocketAddrV6; use std::path::PathBuf; use std::time::Duration; use tokio::net::UdpSocket; +mod hubris_archive; mod usart; +use self::hubris_archive::HubrisArchive; + /// Command line program that can send MGS messages to a single SP. #[derive(Parser, Debug)] struct Args { @@ -103,7 +105,7 @@ enum SpCommand { }, /// Upload a new image to the SP and have it swap banks (requires reset) - Update { image: PathBuf }, + Update { hubris_archive: PathBuf }, /// Instruct the SP to reset. Reset, @@ -187,12 +189,11 @@ async fn main() -> Result<()> { ) .await?; } - Some(SpCommand::Update { image }) => { - let data = fs::read(&image).with_context(|| { - format!("failed to read image {}", image.display()) - })?; + Some(SpCommand::Update { hubris_archive }) => { + let mut archive = HubrisArchive::open(&hubris_archive)?; + let data = archive.final_bin()?; sp.update(data).await.with_context(|| { - format!("updating to {} failed", image.display()) + format!("updating to {} failed", hubris_archive.display()) })?; } Some(SpCommand::Reset) => {