Skip to content

Commit

Permalink
host: fix last-panic/boot-fail commands
Browse files Browse the repository at this point in the history
Memory layout changed in Hubris.
  • Loading branch information
cbiffle committed Jun 4, 2024
1 parent 3f249da commit 943d8c7
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 23 deletions.
3 changes: 2 additions & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ name = "humility"
#
# Be sure to check in and push all of the files that change. Happy versioning!
#
version = "0.11.6"
version = "0.11.7"
authors = ["Bryan Cantrill <bryan@oxide.computer>"]
edition = "2018"
license = "MPL-2.0"
Expand Down
1 change: 1 addition & 0 deletions cmd/host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ zerocopy.workspace = true
humility.workspace = true
humility-cmd.workspace = true
humility-cli.workspace = true
humility-doppel.workspace = true
humility-log.workspace = true
136 changes: 119 additions & 17 deletions cmd/host/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,14 @@
//! [0; 4096]
//! ```

use anyhow::{bail, Context, Result};
use anyhow::{anyhow, bail, Context, Result};
use clap::{CommandFactory, Parser};
use zerocopy::FromBytes;

use humility::{core::Core, hubris::HubrisArchive};
use humility::{core::Core, hubris::HubrisArchive, reflect, reflect::Load};
use humility_cli::{ExecutionContext, Subcommand};
use humility_cmd::{Archive, Attach, Command, CommandKind, Validate};
use humility_doppel as doppel;
use humility_log::{msg, warn};

#[derive(Parser, Debug)]
Expand All @@ -77,35 +78,114 @@ struct HostArgs {
cmd: HostCommand,
}

fn read_var(
static SEPARATE_HOST_BOOT_FAIL_NAME: &str = "LAST_HOST_BOOT_FAIL";

static SEPARATE_LAST_HOST_PANIC_NAME: &str = "LAST_HOST_PANIC";

static HOST_STATE_BUF_NAME: &str =
"task_host_sp_comms::ServerImpl::claim_static_resources::BUFS";

fn read_uqvar(
hubris: &HubrisArchive,
core: &mut dyn Core,
name: &str,
) -> Result<Vec<u8>> {
msg!("reading {name}");
let var = hubris
.lookup_variable(name)
.context(format!("could not find {name}; is this a Gimlet image?"))?;
) -> Result<Option<Vec<u8>>> {
let Some(var) = hubris.lookup_variable(name).ok() else {
return Ok(None);
};

let mut buf: Vec<u8> = vec![0u8; var.size];

core.halt()?;
core.read_8(var.addr, buf.as_mut_slice())?;
core.run()?;

Ok(buf)
Ok(Some(buf))
}

fn host_boot_fail(hubris: &HubrisArchive, core: &mut dyn Core) -> Result<()> {
let d = read_var(hubris, core, "LAST_HOST_BOOT_FAIL")?;
if d.iter().all(|&c| c == 0) {
println!("[0; {}]", d.len());
fn read_qualified_state_buf(
hubris: &HubrisArchive,
core: &mut dyn Core,
name: &str,
) -> Result<Option<reflect::Struct>> {
let Some(var) = hubris.lookup_qualified_variable(name).ok() else {
return Ok(None);
};

let var_ty = hubris.lookup_type(var.goff)?;

let mut buf: Vec<u8> = vec![0u8; var.size];

core.halt()?;
core.read_8(var.addr, &mut buf)?;
core.run()?;

let v = reflect::load_value(hubris, &buf, var_ty, 0)?;
let as_static_cell = doppel::ClaimOnceCell::from_value(&v)?;
match as_static_cell.cell.value {
reflect::Value::Struct(s) => Ok(Some(s)),
v => bail!("unexpected structure for host_sp_comms BUFS: {v:?}"),
}
}

fn print_escaped_ascii(mut bytes: &[u8]) {
// Drop trailing NULs to avoid escaping them and cluttering up our output.
while let Some((&b'\0', prefix)) = bytes.split_last() {
bytes = prefix;
}

if bytes.is_empty() {
println!("Message contains no non-NUL bytes, not printing.");
return;
} else {
match std::str::from_utf8(&d) {
Ok(s) => println!("{}", s.trim_matches('\0')),
Err(e) => println!("{:?}\n (could not decode: {e})", d),
println!("Message is {} bytes long:", bytes.len());
}

let mut buf = String::new();
for &b in bytes {
match b {
b'\\' => {
// Escape any backslashes in the original, so that any escapes
// _we_ emit are unambiguous.
buf.push_str("\\\\");
}
b'\n' | b'\r' | b'\t' | 0x20..=0x7E => {
// Pass through basic text characters that we expect we might
// see in a message.
buf.push(b as char);
}
_ => {
// Escape any other non-printable characters.
buf.push_str("\\x");
buf.push_str(&format!("{b:02X}"));
}
}
}
println!("{buf}");
}

fn host_boot_fail(hubris: &HubrisArchive, core: &mut dyn Core) -> Result<()> {
// Try old name:
let d = read_uqvar(hubris, core, SEPARATE_HOST_BOOT_FAIL_NAME)?;
if let Some(d) = d {
print_escaped_ascii(&d);
return Ok(());
}
// Try new name
let d = read_qualified_state_buf(hubris, core, HOST_STATE_BUF_NAME)?;

let Some(d) = d else {
bail!(
"Could not find host boot variables under any known name; \
is this a Gimlet image?"
);
};
let bytes = d
.get("last_boot_fail")
.ok_or_else(|| anyhow!("BUFS.last_boot_fail field missing"))?;
let raw = Vec::from_value(bytes)?;

print_escaped_ascii(&raw[..]);

Ok(())
}
Expand Down Expand Up @@ -149,8 +229,30 @@ struct IpccPanicStack {
}

fn host_last_panic(hubris: &HubrisArchive, core: &mut dyn Core) -> Result<()> {
let d = read_var(hubris, core, "LAST_HOST_PANIC")?;
// Try original name:
let d = read_uqvar(hubris, core, SEPARATE_LAST_HOST_PANIC_NAME)?;
if let Some(d) = d {
return print_panic(d);
}

// Try new name:
let d = read_qualified_state_buf(hubris, core, HOST_STATE_BUF_NAME)?;

let Some(d) = d else {
bail!(
"Could not find host boot variables under any known name; \
is this a Gimlet image?"
);
};
let bytes = d
.get("last_panic")
.ok_or_else(|| anyhow!("BUFS.last_panic field missing"))?;
let raw = Vec::from_value(bytes)?;

print_panic(raw)
}

fn print_panic(d: Vec<u8>) -> Result<()> {
// Fix for https://github.com/oxidecomputer/hubris/issues/1554
//
// In some cases, `ipd_cause` is unambiguous based on the first byte;
Expand Down
10 changes: 10 additions & 0 deletions humility-core/src/hubris.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1899,6 +1899,16 @@ impl HubrisArchive {
}
}

pub fn lookup_qualified_variable(
&self,
name: &str,
) -> Result<&HubrisVariable> {
match self.qualified_variables.get(name) {
Some(variable) => Ok(variable),
None => Err(anyhow!("variable {} not found", name)),
}
}

pub fn lookup_variables(&self, name: &str) -> Result<&Vec<HubrisVariable>> {
match self.variables.get_vec(name) {
None => Err(anyhow!("variable {} not found", name)),
Expand Down
5 changes: 5 additions & 0 deletions humility-doppel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,11 @@ pub enum CounterVariant {
Nested(Counters),
}

#[derive(Clone, Debug, Load)]
pub struct ClaimOnceCell {
pub cell: UnsafeCell,
}

#[derive(Clone, Debug, Load)]
pub struct StaticCell {
pub cell: UnsafeCell,
Expand Down
4 changes: 2 additions & 2 deletions tests/cmd/chip.trycmd
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ For more information try --help

```
$ humility --chip this-can-be-anything -V
humility 0.11.6
humility 0.11.7

```

Expand All @@ -28,7 +28,7 @@ For more information try --help

```
$ humility -c apx432 -V
humility 0.11.6
humility 0.11.7

```

4 changes: 2 additions & 2 deletions tests/cmd/version.trycmd
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ Long version flag:

```
$ humility --version
humility 0.11.6
humility 0.11.7

```

Short version flag:

```
$ humility -V
humility 0.11.6
humility 0.11.7

```

0 comments on commit 943d8c7

Please sign in to comment.