diff --git a/sw/host/opentitantool/BUILD b/sw/host/opentitantool/BUILD index 94a8115b955aa..9274732e464df 100644 --- a/sw/host/opentitantool/BUILD +++ b/sw/host/opentitantool/BUILD @@ -10,6 +10,7 @@ package(default_visibility = ["//visibility:public"]) rust_binary( name = "opentitantool", srcs = [ + "src/command/bfv.rs", "src/command/bootstrap.rs", "src/command/certificate.rs", "src/command/clear_bitstream.rs", @@ -52,6 +53,7 @@ rust_binary( "//sw/host/opentitanlib", "//sw/host/ot_certs", "//sw/host/sphincsplus", + "//util:bfv_decoder", "@crate_index//:anyhow", "@crate_index//:clap", "@crate_index//:directories", diff --git a/sw/host/opentitantool/src/command/bfv.rs b/sw/host/opentitantool/src/command/bfv.rs new file mode 100644 index 0000000000000..2e10c0d7eeaeb --- /dev/null +++ b/sw/host/opentitantool/src/command/bfv.rs @@ -0,0 +1,55 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::{Context, Result}; +use clap::Args; +use serde_annotate::Annotate; +use std::any::Any; + +use opentitanlib::app::command::CommandDispatch; +use opentitanlib::app::TransportWrapper; + +#[derive(Debug, Args)] +/// Decode a raw status. Optionally accepts an ELF file to recover the filename. +pub struct BfvCommand { + /// Hex BFV value as reported by device on failures. + bfv: Vec, +} + +extern "C" { + fn bfv_decoder(bfv: u32, buf: *mut u8, buf_size: usize) -> usize; +} + +impl CommandDispatch for BfvCommand { + fn run( + &self, + _context: &dyn Any, + _transport: &TransportWrapper, + ) -> Result>> { + for value in &self.bfv { + // Decode status. + let string_bfv = if value.starts_with("0x") { + &value.as_str()[2..] + } else { + value.as_str() + }; + + let bfv = u32::from_str_radix(string_bfv, 16) + .context(format!("\"{}\" is not a valid hex value", string_bfv))?; + let mut text = [0u8; 80]; + // SAFETY: the decodr function is guaranteed to size of the string + // written into text. + let size = unsafe { bfv_decoder(bfv, text.as_mut_ptr(), text.len()) }; + + println!( + "{:08x}: {}", + bfv, + std::str::from_utf8(&text[..size]).unwrap() + ); + } + // Separate command output from the next shell prompt. + println!(); + Ok(None) + } +} diff --git a/sw/host/opentitantool/src/command/mod.rs b/sw/host/opentitantool/src/command/mod.rs index 8452c37d96c49..dca42b18d763c 100644 --- a/sw/host/opentitantool/src/command/mod.rs +++ b/sw/host/opentitantool/src/command/mod.rs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 +pub mod bfv; pub mod bootstrap; pub mod certificate; pub mod clear_bitstream; diff --git a/sw/host/opentitantool/src/main.rs b/sw/host/opentitantool/src/main.rs index 7588ec3256ceb..3aed7a0e1b1eb 100644 --- a/sw/host/opentitantool/src/main.rs +++ b/sw/host/opentitantool/src/main.rs @@ -25,6 +25,7 @@ use opentitanlib::backend; #[allow(clippy::large_enum_variant)] #[derive(Debug, Parser, CommandDispatch)] enum RootCommandHierarchy { + Bfv(command::bfv::BfvCommand), // Not flattened because `Bootstrap` is a leaf command. Bootstrap(command::bootstrap::BootstrapCommand), // Not flattened because `Console` is a leaf command. diff --git a/util/BUILD b/util/BUILD index 94b9e1672e307..82fedcef56d02 100644 --- a/util/BUILD +++ b/util/BUILD @@ -80,3 +80,11 @@ py_binary( srcs = ["fusesoc_build.py"], deps = all_requirements, ) + +cc_library( + name = "bfv_decoder", + srcs = ["bfv_decoder.c"], + deps = [ + "//sw/device/silicon_creator/lib:error", + ], +) diff --git a/util/bfv_decoder.c b/util/bfv_decoder.c new file mode 100644 index 0000000000000..6c6ffdba53143 --- /dev/null +++ b/util/bfv_decoder.c @@ -0,0 +1,59 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include + +#include "sw/device/silicon_creator/lib/error.h" + +typedef struct { + const char *text; + int value; +} error_descriptor_t; + +#define ERROR_TABLE_ENTRY(name, value) \ + { #name, value } + +// Add an empty element in the end to make it easier to stop iterating over the +// table. +const static error_descriptor_t error_table[] = { + DEFINE_ERRORS(ERROR_TABLE_ENTRY){}}; + +size_t bfv_decoder(uint32_t bfv, uint8_t *buf, size_t buf_size) { + char *str = (char *)buf; + + const error_descriptor_t *edesc = error_table; + + while (edesc->value) { + if (edesc->value == bfv) { + snprintf(str, buf_size, "%s", edesc->text); + break; + } + edesc++; + } + if (!edesc->value) { + // This was not an encoded BFV, must have been an exception. + unsigned char mod_high = (bfv >> 16) & 0xff; + unsigned char mod_low = (bfv >> 8) & 0xff; + + if (((mod_high == 'R') && (mod_low == 'I')) || + ((mod_high == 'I') && (mod_low == 'R'))) { + // This is an interrupt/exception error, retrieve the encoded mcause. + uint32_t mcause = (bfv & (1 << 31)) | ((bfv >> 24) & 0x7f); + const char *prefix = ""; + + if (mod_high == 'R') { + prefix = "ROM_EXT "; + } + + snprintf(str, buf_size, + "%sinterrupt/exception, mcause 0x%08x, status 0x%02x", prefix, + mcause, bfv & 0xff); + } else { + snprintf(str, buf_size, "unknown error code"); + } + } + return strlen(str); +}