Skip to content

Commit 8266990

Browse files
committed
Add an "EPOC" tag to the caboose
Bump hubtools version for EPOC tag in caboose Default epoch value is zero if not otherwise specified. Allow command line --epoch for Bootleby which may have no proper app.toml(epoch) Make sure that ImageHeader::epoch keeps in sync with the Caboose 'EPOC' tag. Add read-version command to fetch version and epoch.
1 parent 943c4bb commit 8266990

File tree

6 files changed

+295
-45
lines changed

6 files changed

+295
-45
lines changed

Cargo.lock

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

hubedit/src/main.rs

+50-3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ pub enum Command {
3333
#[clap(short, long)]
3434
version: String,
3535

36+
#[clap(short, long)]
37+
epoch: Option<String>,
38+
3639
#[clap(short, long)]
3740
force: bool,
3841

@@ -44,6 +47,8 @@ pub enum Command {
4447
#[clap(short, long)]
4548
force: bool,
4649
},
50+
/// Read version and epoch values from an image.
51+
ReadVersion,
4752
/// Replaces the binary image within an archive with a different binary
4853
/// image, supplied on the command line as a raw (BIN) file.
4954
///
@@ -93,6 +98,8 @@ pub enum Command {
9398
board: String,
9499
/// Git has for the hubris archive
95100
gitc: String,
101+
/// Epoch for rollback protection
102+
epoch: Option<u32>,
96103
},
97104
}
98105

@@ -129,6 +136,7 @@ fn main() -> Result<()> {
129136
}
130137
Command::WriteCaboose {
131138
version,
139+
epoch,
132140
force,
133141
no_defaults,
134142
} => {
@@ -143,9 +151,10 @@ fn main() -> Result<()> {
143151
}
144152
}
145153
if no_defaults {
146-
archive.write_version_to_caboose(&version)?;
154+
archive.write_version_to_caboose(&version, epoch.as_ref())?;
147155
} else {
148-
archive.write_default_caboose(Some(&version))?;
156+
archive
157+
.write_default_caboose(Some(&version), epoch.as_ref())?;
149158
}
150159
archive.overwrite()?;
151160
}
@@ -159,6 +168,42 @@ fn main() -> Result<()> {
159168
archive.erase_caboose()?;
160169
archive.overwrite()?;
161170
}
171+
Command::ReadVersion => {
172+
// Some images are missing an ImageHeader (old bootleby images),
173+
// or have no caboose, or have no caboose with an `EPOC` value.
174+
// The default epoch value is zero.
175+
176+
let (version, caboose_epoch) =
177+
if let Ok(caboose) = archive.read_caboose() {
178+
let version = match caboose.version() {
179+
Ok(vers) => {
180+
std::str::from_utf8(vers).unwrap_or("").to_string()
181+
}
182+
Err(_) => "".to_string(),
183+
};
184+
let epoch =
185+
std::str::from_utf8(caboose.epoch().unwrap_or(&[b'0']))
186+
.unwrap_or("0")
187+
.parse::<u32>()
188+
.unwrap_or(0u32);
189+
(version.clone(), epoch)
190+
} else {
191+
// No caboose
192+
("".to_string(), 0u32)
193+
};
194+
let header_epoch = archive.image.image_header_epoch().unwrap_or(0);
195+
196+
println!("Version: \"{}\"", version);
197+
if header_epoch == caboose_epoch {
198+
println!("Epoch: {}", header_epoch);
199+
} else {
200+
println!("Epoch: {}", 0u32);
201+
bail!(format!(
202+
"Epoch mismatch: ImageHeader({}) != Caboose({})",
203+
header_epoch, caboose_epoch
204+
));
205+
}
206+
}
162207
Command::ReplaceImage { image } => {
163208
let contents = std::fs::read(&image).with_context(|| {
164209
format!("reading image file {}", image.display())
@@ -202,8 +247,10 @@ fn main() -> Result<()> {
202247
board,
203248
name,
204249
gitc,
250+
epoch,
205251
} => {
206-
let archive = bootleby_to_archive(elf_file, board, name, gitc)?;
252+
let archive =
253+
bootleby_to_archive(elf_file, board, name, gitc, epoch)?;
207254

208255
std::fs::write(&args.archive, archive)?;
209256

hubtools/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "hubtools"
3-
version = "0.4.6"
3+
version = "0.4.7"
44
edition = "2021"
55
rust-version = "1.66"
66

hubtools/src/bootleby.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ fn header_offset(elf: &object::read::File) -> Result<u64, Error> {
5757
Err(Error::MissingHeader)
5858
}
5959

60-
fn add_image_header(path: PathBuf) -> Result<Vec<u8>, Error> {
60+
fn add_image_header(path: PathBuf, epoch: u32) -> Result<Vec<u8>, Error> {
6161
// We can't construct an ELF object from a mutable reference so we
6262
// work on the file path and return the bytes
6363
let mut f = std::fs::read(path).map_err(Error::FileReadError)?;
@@ -73,8 +73,9 @@ fn add_image_header(path: PathBuf) -> Result<Vec<u8>, Error> {
7373
drop(elf);
7474

7575
let header = header::ImageHeader {
76-
magic: 0x64_CE_D6_CA,
7776
total_image_len: len as u32,
77+
epoch,
78+
..Default::default()
7879
};
7980

8081
header
@@ -89,8 +90,11 @@ pub fn bootleby_to_archive(
8990
board: String,
9091
name: String,
9192
gitc: String,
93+
epoc: Option<u32>,
9294
) -> Result<Vec<u8>, Error> {
93-
let f = add_image_header(path)?;
95+
let image_header_epoch: u32 = epoc.unwrap_or(0u32);
96+
let epoc = image_header_epoch.to_string();
97+
let f = add_image_header(path, image_header_epoch)?;
9498

9599
let img = RawHubrisImage::from_generic_elf(&f)?;
96100

@@ -103,8 +107,9 @@ pub fn bootleby_to_archive(
103107
name = "{}"
104108
board = "{}"
105109
chip = "lpc55"
110+
epoch = {}
106111
"#,
107-
name, board
112+
name, board, epoc
108113
);
109114

110115
archive.add_file("elf/kernel", &f)?;

hubtools/src/caboose.rs

+59
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ impl Caboose {
4848
self.get_tag(tags::SIGN)
4949
}
5050

51+
pub fn epoch(&self) -> Result<&[u8], CabooseError> {
52+
self.get_tag(tags::EPOC)
53+
}
54+
5155
fn get_tag(&self, tag: [u8; 4]) -> Result<&[u8], CabooseError> {
5256
use tlvc::TlvcReader;
5357
let mut reader = TlvcReader::begin(self.as_slice())
@@ -85,6 +89,7 @@ pub(crate) mod tags {
8589
pub(crate) const NAME: [u8; 4] = *b"NAME";
8690
pub(crate) const VERS: [u8; 4] = *b"VERS";
8791
pub(crate) const SIGN: [u8; 4] = *b"SIGN";
92+
pub(crate) const EPOC: [u8; 4] = *b"EPOC";
8893
}
8994

9095
#[derive(Debug, Default, Clone, PartialEq, Eq)]
@@ -94,6 +99,7 @@ pub struct CabooseBuilder {
9499
name: Option<String>,
95100
version: Option<String>,
96101
sign: Option<String>,
102+
epoch: Option<String>,
97103
}
98104

99105
impl CabooseBuilder {
@@ -122,6 +128,22 @@ impl CabooseBuilder {
122128
self
123129
}
124130

131+
pub fn epoch<S: Into<String>>(mut self, epoch: S) -> Self {
132+
let epoch: String = epoch.into();
133+
match epoch.len() {
134+
0 => self.epoch = Some("0".to_string()),
135+
_ => {
136+
if let Ok(epoch) = epoch.parse::<u32>() {
137+
self.epoch = Some(format!("{epoch}").to_owned());
138+
} else {
139+
// Any invalid value is treated as zero
140+
self.epoch = Some("0".to_string())
141+
}
142+
}
143+
}
144+
self
145+
}
146+
125147
pub fn build(self) -> Caboose {
126148
let mut pieces = Vec::new();
127149
for (tag, maybe_value) in [
@@ -130,6 +152,7 @@ impl CabooseBuilder {
130152
(tags::NAME, self.name),
131153
(tags::VERS, self.version),
132154
(tags::SIGN, self.sign),
155+
(tags::EPOC, self.epoch),
133156
] {
134157
let Some(value) = maybe_value else {
135158
continue;
@@ -166,6 +189,10 @@ mod tests {
166189
caboose.version(),
167190
Err(CabooseError::MissingTag { tag: tags::VERS })
168191
);
192+
assert_eq!(
193+
caboose.epoch(),
194+
Err(CabooseError::MissingTag { tag: tags::EPOC })
195+
);
169196
}
170197

171198
#[test]
@@ -184,6 +211,10 @@ mod tests {
184211
caboose.version(),
185212
Err(CabooseError::MissingTag { tag: tags::VERS })
186213
);
214+
assert_eq!(
215+
caboose.epoch(),
216+
Err(CabooseError::MissingTag { tag: tags::EPOC })
217+
);
187218
}
188219

189220
#[test]
@@ -193,10 +224,38 @@ mod tests {
193224
.board("bar")
194225
.name("fizz")
195226
.version("buzz")
227+
.epoch("")
196228
.build();
197229
assert_eq!(caboose.git_commit(), Ok("foo".as_bytes()));
198230
assert_eq!(caboose.board(), Ok("bar".as_bytes()));
199231
assert_eq!(caboose.name(), Ok("fizz".as_bytes()));
200232
assert_eq!(caboose.version(), Ok("buzz".as_bytes()));
233+
assert_eq!(caboose.epoch(), Ok("0".as_bytes()));
234+
}
235+
236+
#[test]
237+
fn builder_can_make_caboose_with_non_zero_epoch() {
238+
let caboose = CabooseBuilder::default().epoch("1234567890").build();
239+
assert_eq!(caboose.epoch(), Ok("1234567890".as_bytes()));
240+
}
241+
242+
#[test]
243+
fn builder_will_normalize_empty_epoch() {
244+
let caboose = CabooseBuilder::default().epoch("").build();
245+
assert_eq!(caboose.epoch(), Ok("0".as_bytes()));
246+
}
247+
248+
#[test]
249+
fn builder_will_normalize_short_epoch() {
250+
let caboose = CabooseBuilder::default().epoch("1234").build();
251+
assert_eq!(caboose.epoch(), Ok("1234".as_bytes()));
252+
}
253+
254+
#[test]
255+
fn builder_will_convert_u64_epoch_to_zero() {
256+
let caboose = CabooseBuilder::default()
257+
.epoch("4294967296") // u32::MAX_VALUE + 1
258+
.build();
259+
assert_eq!(caboose.epoch(), Ok("0".as_bytes()));
201260
}
202261
}

0 commit comments

Comments
 (0)