mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-12 07:44:18 +00:00
221 lines
11 KiB
Rust
221 lines
11 KiB
Rust
use crate::{ShellCmdApi, CommonEnv};
|
|
use root_keys::api::{AesRootkeyType, Block};
|
|
use xous_ipc::String;
|
|
|
|
#[derive(Debug)]
|
|
pub struct Keys {
|
|
testing_range: xous::MemoryRange,
|
|
spinor: spinor::Spinor,
|
|
rootkeys: root_keys::RootKeys,
|
|
}
|
|
const TEST_SIZE: usize = 0x4000;
|
|
const TEST_BASE: usize = 0x608_0000;
|
|
impl Keys {
|
|
pub fn new(xns: &xous_names::XousNames) -> Keys {
|
|
#[cfg(any(target_os = "none", target_os = "xous"))]
|
|
let testing_range = xous::syscall::map_memory(
|
|
Some(core::num::NonZeroUsize::new(TEST_BASE + xous::FLASH_PHYS_BASE as usize).unwrap()), // occupy the 44.1khz short sample area for testing
|
|
None,
|
|
TEST_SIZE,
|
|
xous::MemoryFlags::R,
|
|
).expect("couldn't map in testing range");
|
|
#[cfg(not(any(target_os = "none", target_os = "xous")))] // just make a dummy mapping to keep things from crashing in hosted mode
|
|
let testing_range = xous::syscall::map_memory(
|
|
None,
|
|
None,
|
|
TEST_SIZE,
|
|
xous::MemoryFlags::R,
|
|
).expect("couldn't map in a fake testing range for hosted mode");
|
|
|
|
let spinor = spinor::Spinor::new(&xns).unwrap();
|
|
// NOTE NOTE NOTE -- this should be removed once we have the SoC updater code written, but for testing
|
|
// we occupy this slot as it is a pre-requisite for the block to work
|
|
spinor.register_soc_token().unwrap();
|
|
Keys {
|
|
testing_range,
|
|
spinor,
|
|
rootkeys: root_keys::RootKeys::new(&xns, Some(AesRootkeyType::User0)).expect("couldn't allocate rootkeys API"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> ShellCmdApi<'a> for Keys {
|
|
cmd_api!(keys); // inserts boilerplate for command API
|
|
|
|
fn process(&mut self, args: String::<1024>, env: &mut CommonEnv) -> Result<Option<String::<1024>>, xous::Error> {
|
|
use core::fmt::Write;
|
|
let mut ret = String::<1024>::new();
|
|
let helpstring = "keys [usblock] [usbunlock] [spinortest]";
|
|
|
|
let mut tokens = args.as_str().unwrap().split(' ');
|
|
|
|
if let Some(sub_cmd) = tokens.next() {
|
|
match sub_cmd {
|
|
"ux" => {
|
|
self.rootkeys.test_ux(0);
|
|
//debug_here::debug_here!();
|
|
write!(ret, "show UX").unwrap();
|
|
}
|
|
"bbram" => {
|
|
self.rootkeys.bbram_provision();
|
|
write!(ret, "Provisioning BBRAM").unwrap();
|
|
}
|
|
"aes" => {
|
|
use aes_xous::{BlockEncrypt, BlockDecrypt};
|
|
let mut pass = true;
|
|
let mut block = Block::clone_from_slice(&[0; 16]);
|
|
self.rootkeys.encrypt_block(&mut block);
|
|
log::info!("encrypted block: {:?}", block);
|
|
let mut sum = 0;
|
|
for &b in block.as_slice().iter() {
|
|
sum += b as u32; // we don't use .sum() because we need more bits than a u8
|
|
}
|
|
if sum == 0 {
|
|
pass = false;
|
|
}
|
|
// this will cause a second password box to pop up if you told it to use-once.
|
|
self.rootkeys.decrypt_block(&mut block);
|
|
log::info!("decrypted block: {:?}", block);
|
|
for &b in block.as_slice().iter() {
|
|
if b != 0 {
|
|
pass = false;
|
|
}
|
|
}
|
|
if pass {
|
|
write!(ret, "aes test passed").unwrap();
|
|
} else {
|
|
write!(ret, "aes test failed").unwrap();
|
|
}
|
|
}
|
|
"usblock" => {
|
|
env.llio.debug_usb(Some(true)).unwrap();
|
|
write!(ret, "USB debug port locked out; one word at 0x80000000 is disclosable via USB.").unwrap();
|
|
}
|
|
"usbunlock" => {
|
|
env.llio.debug_usb(Some(false)).unwrap();
|
|
write!(ret, "USB debug port unlocked: all secrets are readable via USB!").unwrap();
|
|
}
|
|
"spinortest" => {
|
|
let region = self.testing_range.as_slice::<u8>();
|
|
let region_base = TEST_BASE as u32; // base offsets are 0-relative to start of FLASH
|
|
|
|
log::debug!("top of region: {:x?}", ®ion[0..8]);
|
|
// a region to stash a copy of the previous data in the testing area, so we can diff to confirm things worked!
|
|
let mut reference_region = xous::syscall::map_memory(
|
|
None,
|
|
None,
|
|
TEST_SIZE,
|
|
xous::MemoryFlags::R | xous::MemoryFlags::W,
|
|
).expect("couldn't allocate a reference region for testing flash data");
|
|
let reference = reference_region.as_slice_mut::<u8>();
|
|
|
|
for byte in reference.iter_mut() {
|
|
*byte = 0xFF;
|
|
}
|
|
log::debug!("erasing region");
|
|
self.spinor.patch(®ion, region_base, &reference, 0).expect("couldn't erase region for testing");
|
|
|
|
for (addr, &byte) in region.iter().enumerate() {
|
|
if byte != 0xFF {
|
|
log::error!("erase did not succeed at offset 0x{:x}", addr);
|
|
}
|
|
}
|
|
log::debug!("erase check finished");
|
|
|
|
// test a simple patch on erased data, at an unaligned address -- the smallest quantum is 2 bytes
|
|
log::debug!("simple patch test");
|
|
let patch: [u8; 2] = [0x33, 0xCC];
|
|
self.spinor.patch(®ion, region_base, &patch, 4).expect("couldn't do smallest patch");
|
|
for (addr, &byte) in region.iter().enumerate() {
|
|
match addr {
|
|
4 => if byte != 0x33 { log::error!("smallpatch failed e.33 o.{:02x}", byte) },
|
|
5 => if byte != 0xCC { log::error!("smallpatch failed e.cc o.{:02x}", byte) },
|
|
_ => if byte != 0xFF { log::error!("smallpatch failed, erase disturb at region offset 0x{:08x}", addr)}
|
|
}
|
|
}
|
|
log::debug!("simple patch test finished");
|
|
|
|
// fill the region with random data
|
|
log::debug!("fill random data");
|
|
use rand_core::RngCore;
|
|
env.trng.try_fill_bytes(reference).expect("couldn't fill reference region with random data");
|
|
self.spinor.patch(®ion, region_base, &reference, 0).expect("couldn't fill test region with random data");
|
|
|
|
let mut errs = 0;
|
|
for (addr, (&rom, &reference)) in region.iter().zip(reference.iter()).enumerate() {
|
|
if rom != reference {
|
|
if (errs % 256 < 2) || (errs % 256 >= 254) {
|
|
log::error!("fill random failed 0x{:08x}: e.{:02x} o.{:02x}", addr, reference, rom);
|
|
}
|
|
errs += 1;
|
|
}
|
|
}
|
|
log::debug!("fill random data finished, errs: {}", errs);
|
|
|
|
// test a patch on random data
|
|
log::debug!("programmed patch test");
|
|
let patch: [u8; 4] = [0xaa, 0xbb, 0xcc, 0xdd];
|
|
self.spinor.patch(®ion, region_base, &patch, 12).expect("couldn't do smallest patch");
|
|
for (addr, (&byte, &reference)) in region.iter().zip(reference.iter()).enumerate() {
|
|
match addr {
|
|
12 => if byte != 0xaa { log::error!("patch failed e.aa o.{:02x}", byte) },
|
|
13 => if byte != 0xbb { log::error!("patch failed e.bb o.{:02x}", byte) },
|
|
14 => if byte != 0xcc { log::error!("patch failed e.cc o.{:02x}", byte) },
|
|
15 => if byte != 0xdd { log::error!("patch failed e.dd o.{:02x}", byte) },
|
|
_ => if byte != reference {
|
|
if (errs % 256 < 4) || (errs % 256 >= 252) {
|
|
log::error!("patch failed, erase disturb at region offset 0x{:08x}: e.{:02x} o.{:02x}", addr, reference, byte)
|
|
}
|
|
errs += 1;
|
|
}
|
|
}
|
|
}
|
|
log::debug!("patch test finished, errs {}", errs);
|
|
// refresh the reference, otherwise this patch will throw errors
|
|
for (&src, dst) in region.iter().zip(reference.iter_mut()) {
|
|
*dst = src;
|
|
}
|
|
|
|
log::debug!("patch across a page boundary");
|
|
let bigger_patch: [u8; 16] = [0xf0, 0x0d, 0xbe, 0xef, 0xaa, 0x55, 0x99, 0x66, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff];
|
|
log::debug!("before patch: {:x?}", ®ion[0x0ff4..0x100c]);
|
|
self.spinor.patch(®ion, region_base, &bigger_patch, 0x0FF8).expect("can't patch across page boundary");
|
|
let mut i = 0;
|
|
errs = 0;
|
|
for (addr, (&rom, &reference)) in region.iter().zip(reference.iter()).enumerate() {
|
|
match addr {
|
|
0xff8..=0x1007 => {
|
|
if rom != bigger_patch[i] {
|
|
if (errs % 256 < 2) || (errs % 256 >= 254) {
|
|
log::error!("bigger patch failed 0x{:08x}: e.{:02x} o.{:02x}", addr, bigger_patch[i], rom);
|
|
}
|
|
errs += 1;
|
|
}
|
|
i += 1;
|
|
},
|
|
_ => {
|
|
if rom != reference {
|
|
if (errs % 256 < 2) || (errs % 256 >= 254) {
|
|
log::error!("data disturbed at 0x{:08x}: e.{:02x} o.{:02x}", addr, reference, rom);
|
|
}
|
|
errs += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
log::debug!("after patch: {:x?}", ®ion[0x0ff4..0x100c]);
|
|
log::debug!("patch across a page boundary finished, errs: {}", errs);
|
|
|
|
write!(ret, "Finished SPINOR primitives test (see serial log for pass/fail details).").unwrap();
|
|
}
|
|
_ => {
|
|
write!(ret, "{}", helpstring).unwrap();
|
|
}
|
|
}
|
|
|
|
} else {
|
|
write!(ret, "{}", helpstring).unwrap();
|
|
}
|
|
Ok(Some(ret))
|
|
}
|
|
}
|