Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 39 additions & 18 deletions humility-core/src/hubris.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2080,34 +2080,27 @@ impl HubrisArchive {
// To validate that what we're running on the target matches what
// we have in the archive, we are going to check the image ID, an
// identifer created for this purpose.
let addr = self.imageid.0;
let nbytes = self.imageid.1.len();
assert!(nbytes > 0);

let mut id = vec![0; nbytes];
core.read_8(addr, &mut id[0..nbytes]).with_context(|| {
format!("failed to read image ID at 0x{:x}; board mismatch?", addr)
})?;

let deltas = id
.iter()
.zip(self.imageid.1.iter())
.filter(|&(lhs, rhs)| lhs != rhs)
.count();

if deltas > 0 || id.len() != self.imageid.1.len() {
let id = self.read_image_id_from_flash(core)?;
if id != self.imageid.1 {
bail!(
"image ID in archive ({:x?}) does not equal \
ID at 0x{:x} ({:x?})",
self.imageid.1, self.imageid.0, id,
);
}

if criteria == HubrisValidate::ArchiveMatch {
if self.task_dump().is_some() {
return Ok(());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@labbott What does it mean for an archive to be "a dump from a single task"? I'm wondering if it's intentional/correct that if you pass in HubrisValidate::Booted in that case, it returns early here and skips the boot check.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's possible to take a dump from only a single task's memory range (e.g. a "single task dump"), but not an "entire system" dump. Laura can confirm though :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

James is correct. We have the notion of single task dumps vs a wholes system dump. You can run many of the same commands like e.g. humility tasks and humility ringbuf and things should work as expected. I don't think we capture the boot information in a single task dump which is why it gets skipped.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I added a comment about it.

} else {
core.halt()?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we do core.run()? again after checking the PC?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely yes this got lost in some refactoring 😬

if let Ok((false, pc)) = self.is_pc_within_archive(core) {
bail!("image ID matches but PC at 0x{pc:x} is not part of any \
module. This is likely an incorrect A/B archive.");
}
core.run()?;
}

if self.task_dump().is_some() {
if criteria == HubrisValidate::ArchiveMatch {
return Ok(());
}

Expand Down Expand Up @@ -2170,6 +2163,34 @@ impl HubrisArchive {
);
}

pub fn read_image_id_from_flash(
&self,
core: &mut dyn crate::core::Core,
) -> Result<Vec<u8>> {
let addr = self.imageid.0;
let nbytes = self.imageid.1.len();
assert!(nbytes > 0);

let mut id = vec![0; nbytes];
core.read_8(addr, &mut id[0..nbytes]).with_context(|| {
format!("failed to read image ID at 0x{:x}; board mismatch?", addr)
})?;
Ok(id)
}

/// The core must be halted before this is called
pub fn is_pc_within_archive(
&self,
core: &mut dyn crate::core::Core
) -> Result<(bool, u32)> {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite follow this return type. Can you explain it with a comment?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Yeah it's kinda weird. I wanted a helper function for doing the check, but also didn't want to have to remove the PC value from your error message. It will be useful for debugging when this error gets triggered by some other strange situation someday.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to change it now but it's also worth considering something like

enum PcResult {
     InArchive,
     NotInArchive { pc: u32 },
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do like that, changed.

let pc = core.read_reg(ARMRegister::PC)?;
if self.instr_mod(pc).is_none() {
Ok((false, pc))
} else {
Ok((true, pc))
}
}

pub fn verify(
&self,
core: &mut dyn crate::core::Core,
Expand Down
32 changes: 26 additions & 6 deletions humility-flash/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
//! Functions to check flash state and reprogram a processor
#![warn(missing_docs)]

use anyhow::anyhow;
use humility::{
core::Core,
hubris::{HubrisArchive, HubrisValidate},
log::{Logger, info},
hubris::{HubrisArchive},
log::{Logger, info, warn},
};
use humility_auxflash::{AuxFlashHandler, AuxFlashWriter};
use humility_probes_core::ProbeCore;
Expand Down Expand Up @@ -92,11 +93,30 @@ pub fn get_image_state(
) -> Result<ImageStateResult, ImageStateError> {
core.halt().map_err(ImageStateError::HaltFailed)?;

if let Ok((false, pc)) = hubris.is_pc_within_archive(core) {
warn!(log, "PC at 0x{pc:x} is not part of any module. Maybe you're \
flashing an A image while a B image is running, or vice \
versa?");
}
Comment on lines +107 to +115

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we drop the Err case here. That should only happen if we can't read the PC and we're probably going to have other errors that would get caught below so I think it's fine.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah my thought was for it to just log nothing unless it's sure the PC location is bad. Would you prefer to have a "failed to check PC location" warning too?


// First pass: check only the image ID
if let Err(e) = hubris.validate(core, HubrisValidate::ArchiveMatch) {
return Ok(ImageStateResult::DoesNotMatch(
ImageStateMismatch::FlashArchiveMismatch(e),
));
match hubris.read_image_id_from_flash(core) {
Err(e) => {
return Ok(ImageStateResult::DoesNotMatch(
ImageStateMismatch::FlashArchiveMismatch(e),
));
}
Ok(id) if id != hubris.image_id() => {
return Ok(ImageStateResult::DoesNotMatch(
ImageStateMismatch::FlashArchiveMismatch(anyhow!(
"image ID in archive ({:x?}) does not equal \
image ID of target ({:x?})",
hubris.image_id(),
id,
)),
));
}
Comment on lines +119 to +131

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should be two separate error types: one to represent an error reading the image ID and a separate one to indicate an actual mismatch. This also means we can avoid having to create an extra error message with anyhow and have all the information in the ImageStateMismatch enum.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. That's a nice side effect of not calling validate anymore.

_ => (),
}

// More rigorous checks if requested
Expand Down
Loading