From 72685e2c1c778fac48c02cf022229ed2d147554f Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 01:42:06 +0800 Subject: [PATCH 01/42] improve i2c error recovery --- libs/cramium-hal/src/udma/i2c.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/cramium-hal/src/udma/i2c.rs b/libs/cramium-hal/src/udma/i2c.rs index 41281f27a..607ac0553 100644 --- a/libs/cramium-hal/src/udma/i2c.rs +++ b/libs/cramium-hal/src/udma/i2c.rs @@ -218,8 +218,14 @@ impl I2c { timeout += 1; if timeout > TIMEOUT_ITERS { // reset the block + self.udma_reset(Bank::Custom); + self.udma_reset(Bank::Tx); + self.udma_reset(Bank::Rx); self.csr.wfo(utra::udma_i2c_0::REG_SETUP_R_DO_RST, 1); self.csr.wo(utra::udma_i2c_0::REG_SETUP, 0); + + self.send_cmd_list(&[I2cCmd::Config(self.divider)]); + self.pending.take(); return Err(xous::Error::Timeout); } #[cfg(feature = "std")] From 6829eae73cdc4b0108b233f71dae6373b7ca14d6 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 01:42:27 +0800 Subject: [PATCH 02/42] add a reset method to the UDMA abstraction --- libs/cramium-hal/src/udma/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/cramium-hal/src/udma/mod.rs b/libs/cramium-hal/src/udma/mod.rs index 7aaf94665..d0013e68c 100644 --- a/libs/cramium-hal/src/udma/mod.rs +++ b/libs/cramium-hal/src/udma/mod.rs @@ -413,6 +413,12 @@ pub trait Udma { bank_addr.add(DmaReg::Size.into()).write_volatile((buf.len() * size_of::()) as u32); bank_addr.add(DmaReg::Cfg.into()).write_volatile(config | CFG_EN) } + fn udma_reset(&self, bank: Bank) { + unsafe { + let bank_addr = self.csr().base().add(bank as usize); + bank_addr.add(DmaReg::Cfg.into()).write_volatile(CFG_CLEAR); + } + } fn udma_can_enqueue(&self, bank: Bank) -> bool { // Safety: only safe when used in the context of UDMA registers. unsafe { From 5ec5bc29c9139825a72eceaf8511f0008c03840f Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 01:42:47 +0800 Subject: [PATCH 03/42] add keyboard pins to board description --- libs/cramium-hal/src/board/baosec.rs | 45 ++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/libs/cramium-hal/src/board/baosec.rs b/libs/cramium-hal/src/board/baosec.rs index 4dceb4fa8..ad2787043 100644 --- a/libs/cramium-hal/src/board/baosec.rs +++ b/libs/cramium-hal/src/board/baosec.rs @@ -207,10 +207,11 @@ pub fn setup_ov2640_pins(iox: &T) -> (IoxPort, u8) { } /// returns the USB SE0 port and pin number +const SE0_PIN: u8 = 14; pub fn setup_usb_pins(iox: &T) -> (IoxPort, u8) { iox.setup_pin( IoxPort::PB, - 1, + SE0_PIN, Some(IoxDir::Output), Some(IoxFunction::Gpio), None, @@ -218,6 +219,44 @@ pub fn setup_usb_pins(iox: &T) -> (IoxPort, u8) { Some(IoxEnable::Enable), Some(IoxDriveStrength::Drive2mA), ); - iox.set_gpio_pin_value(IoxPort::PB, 1, IoxValue::Low); - (IoxPort::PB, 1) + iox.set_gpio_pin_value(IoxPort::PB, SE0_PIN, IoxValue::Low); + (IoxPort::PB, SE0_PIN) +} + +// These constants definitely change for NTO. These will only work on NTO. +const KB_PORT: IoxPort = IoxPort::PD; +const R_PINS: [u8; 3] = [0, 1, 4]; +const C_PINS: [u8; 3] = [5, 6, 7]; +pub fn setup_kb_pins(iox: &T) -> ([(IoxPort, u8); 3], [(IoxPort, u8); 3]) { + for r in R_PINS { + iox.setup_pin( + KB_PORT, + r, + Some(IoxDir::Output), + Some(IoxFunction::Gpio), + None, + None, + Some(IoxEnable::Enable), + Some(IoxDriveStrength::Drive2mA), + ); + iox.set_gpio_pin_value(KB_PORT, r, IoxValue::High); + } + + for c in C_PINS { + iox.setup_pin( + KB_PORT, + c, + Some(IoxDir::Input), + Some(IoxFunction::Gpio), + Some(IoxEnable::Enable), + None, + Some(IoxEnable::Enable), + Some(IoxDriveStrength::Drive2mA), + ); + } + ([(KB_PORT, R_PINS[0]), (KB_PORT, R_PINS[1]), (KB_PORT, R_PINS[2])], [ + (KB_PORT, C_PINS[0]), + (KB_PORT, C_PINS[1]), + (KB_PORT, C_PINS[2]), + ]) } From de12d0f73adf8d59f1d699459d400a6f9113417a Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 01:43:05 +0800 Subject: [PATCH 04/42] add usb feature to loader --- xtask/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 198ddfd92..2b6c8c7a8 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -582,6 +582,7 @@ fn main() -> Result<(), Box> { builder.add_feature("quantum-timer"); builder.add_kernel_feature("v2p"); builder.add_loader_feature("sram-margin"); + builder.add_loader_feature("usb"); match task.as_deref() { Some("baosec") => builder.target_cramium_soc(), _ => panic!("should be unreachable"), From 391fb84f00905d349d785b99953e7d9eb3a47678 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 01:43:25 +0800 Subject: [PATCH 05/42] update api to inclued perclk_freq --- loader/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loader/src/main.rs b/loader/src/main.rs index 8230b7db8..27fbbd9ba 100644 --- a/loader/src/main.rs +++ b/loader/src/main.rs @@ -96,7 +96,7 @@ pub unsafe extern "C" fn rust_entry(signed_buffer: *const usize, signature: u32) let perclk_freq = 0; #[cfg(feature = "cramium-soc")] - crate::platform::process_update(); + crate::platform::process_update(perclk_freq); // initially validate the whole image on disk (including kernel args) // kernel args must be validated because tampering with them can change critical assumptions about From d4f209c0b20e6f670d59aaff8dfb0d3d431131ec Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 01:43:46 +0800 Subject: [PATCH 06/42] add ghostfat to dependencies in anticipation of starting to emulate the FS --- loader/Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/loader/Cargo.toml b/loader/Cargo.toml index ff529c4be..e26f62433 100644 --- a/loader/Cargo.toml +++ b/loader/Cargo.toml @@ -42,6 +42,7 @@ libm = { version = "0.2.8", optional = true } nalgebra = { version = "0.33", default-features = false, features = [ "libm", ], optional = true } +ghostfat = { version = "0.5.0", optional = true, default-features = false } [dependencies.com_rs] git = "https://github.com/betrusted-io/com_rs" @@ -76,6 +77,7 @@ cramium-soc = [ "rand_chacha", "sram-margin", "libm", + "ghostfat", # "boot-delay", ] board-baosec = ["cramium-hal/board-baosec"] @@ -120,4 +122,4 @@ debug-print = [] earlyprintk = [] resume = [] # suspend/resume pathway code -default = ["board-bringup", "dump-trng", "usb", "qr"] +default = ["board-bringup", "dump-trng", "usb"] From f14cf1948c84524bb310b86a6cd2f2034d74be3f Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 01:44:28 +0800 Subject: [PATCH 07/42] gfx is now used as part of the update routine --- loader/src/platform/cramium.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/loader/src/platform/cramium.rs b/loader/src/platform/cramium.rs index 5193cf922..3f8ac5ae2 100644 --- a/loader/src/platform/cramium.rs +++ b/loader/src/platform/cramium.rs @@ -11,7 +11,6 @@ mod update; pub use update::*; mod verifier; -#[cfg(feature = "board-bringup")] mod gfx; #[cfg(feature = "qr")] mod homography; From bb594a5c40c15b855305579120f0c240512234d0 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 01:44:58 +0800 Subject: [PATCH 08/42] try to improve the pmic detection code we can now recover from an I2C timeout and get the PMIC data out, but mysteriously, when we leave the routine, the CPU hangs. I think fundamentally the I2C instability is due to something very wonky inside the chip itself, so the recovery only papers over an issue that is only fixed with a reboot... --- loader/src/platform/cramium/cramium.rs | 100 ++++++++++++++++++------- 1 file changed, 75 insertions(+), 25 deletions(-) diff --git a/loader/src/platform/cramium/cramium.rs b/loader/src/platform/cramium/cramium.rs index 6fb4ba564..b792b1b15 100644 --- a/loader/src/platform/cramium/cramium.rs +++ b/loader/src/platform/cramium/cramium.rs @@ -1,3 +1,4 @@ +use cramium_hal::axp2101::Axp2101; use cramium_hal::iox::{Iox, IoxDir, IoxEnable, IoxFunction, IoxPort}; use cramium_hal::udma; use cramium_hal::udma::PeriphId; @@ -74,6 +75,22 @@ pub const FLASH_BASE: usize = utralib::generated::HW_RERAM_MEM; // exclusive of the signature block offset pub const KERNEL_OFFSET: usize = 0x5_0000; +fn delay(quantum: usize) { + use utralib::{CSR, utra}; + // abuse the d11ctime timer to create some time-out like thing + let mut d11c = CSR::new(utra::d11ctime::HW_D11CTIME_BASE as *mut u32); + d11c.wfo(utra::d11ctime::CONTROL_COUNT, 333_333); // 1.0ms per interval + let mut polarity = d11c.rf(utra::d11ctime::HEARTBEAT_BEAT); + for _ in 0..quantum { + while polarity == d11c.rf(utra::d11ctime::HEARTBEAT_BEAT) {} + polarity = d11c.rf(utra::d11ctime::HEARTBEAT_BEAT); + } + // we have to split this because we don't know where we caught the previous interval + if quantum == 1 { + while polarity == d11c.rf(utra::d11ctime::HEARTBEAT_BEAT) {} + } +} + #[cfg(feature = "cramium-soc")] pub fn early_init() -> u32 { // Set up the initial clocks. This is done as a "poke array" into a table of addresses. @@ -271,26 +288,67 @@ pub fn early_init() -> u32 { }; let mut i2c = unsafe { cramium_hal::udma::I2c::new_with_ifram(i2c_channel, 400_000, perclk, i2c_ifram) }; // setup PMIC - let mut pmic = match cramium_hal::axp2101::Axp2101::new(&mut i2c) { - Ok(p) => p, - Err(_e) => { - crate::println!("Couldn't init AXP2101, rebooting"); - let mut rcurst = CSR::new(utra::sysctrl::HW_SYSCTRL_BASE as *mut u32); - rcurst.wo(utra::sysctrl::SFR_RCURST0, 0x55AA); - panic!("System should have reset"); + let mut pmic: Option = None; + + for _ in 0..3 { + match cramium_hal::axp2101::Axp2101::new(&mut i2c) { + Ok(p) => { + pmic = Some(p); + break; + } + Err(e) => { + crate::println!("Error initializing pmic: {:?}, retrying", e); + + // we have to reboot it appears if the I2C is unstable - a "soft recovery" + // just leads to CPU lock-up on exit from the init routine? what is going on?? + // maybe some IFRAM instability? Maybe the I2C unit is locking up? + let mut rcurst = CSR::new(utra::sysctrl::HW_SYSCTRL_BASE as *mut u32); + rcurst.wo(utra::sysctrl::SFR_RCURST0, 0x55AA); + rcurst.wo(utra::sysctrl::SFR_RCURST1, 0x55AA); + + /* + unsafe { + let ifram0 = HW_IFRAM0_MEM as *mut u32; + for i in 0..HW_IFRAM0_MEM_LEN / size_of::() { + ifram0.add(i).write_volatile(0); + } + let ifram1 = HW_IFRAM1_MEM as *mut u32; + for i in 0..HW_IFRAM1_MEM_LEN / size_of::() { + ifram1.add(i).write_volatile(0); + } + }*/ + + delay(500); + } + }; + } + if let Some(mut pmic) = pmic { + pmic.set_ldo(&mut i2c, Some(2.5), cramium_hal::axp2101::WhichLdo::Aldo2).unwrap(); + pmic.set_dcdc(&mut i2c, Some((1.2, false)), cramium_hal::axp2101::WhichDcDc::Dcdc4).unwrap(); + crate::println!("AXP2101 configure: {:?}", pmic); + + // Make this true to have the system shut down by disconnecting its own battery while on battery power + // Note this does nothing if you have USB power plugged in. + if false { + crate::println!("shutting down..."); + pmic.set_ldo(&mut i2c, Some(0.9), cramium_hal::axp2101::WhichLdo::Aldo3).ok(); + crate::println!("system should be off"); } - }; - pmic.set_ldo(&mut i2c, Some(2.5), cramium_hal::axp2101::WhichLdo::Aldo2).unwrap(); - pmic.set_dcdc(&mut i2c, Some((1.2, false)), cramium_hal::axp2101::WhichDcDc::Dcdc4).unwrap(); - crate::println!("AXP2101 configure: {:?}", pmic); + } else { + crate::println!("Couldn't init AXP2101, rebooting"); + let mut rcurst = CSR::new(utra::sysctrl::HW_SYSCTRL_BASE as *mut u32); + rcurst.wo(utra::sysctrl::SFR_RCURST0, 0x55AA); + rcurst.wo(utra::sysctrl::SFR_RCURST1, 0x55AA); + panic!("System should have reset"); + } - // TEMPORARY: turn off SE0 on USB + // Turn off SE0 on USB let _se0 = cramium_hal::board::baosec::setup_usb_pins(&iox); #[cfg(feature = "board-bringup")] let iox_loop = iox.clone(); // show the boot logo - use cramium_hal::minigfx::FrameBuffer; + use cramium_hal::{ifram, minigfx::FrameBuffer}; let mut sh1107 = cramium_hal::sh1107::Oled128x128::new(perclk, &mut iox, &mut udma_global); sh1107.init(); @@ -304,7 +362,7 @@ pub fn early_init() -> u32 { #[cfg(feature = "board-bringup")] { use cramium_hal::minigfx::{Line, Point}; - use cramium_hal::{axp2101::WhichLdo, iox::IoxValue, minigfx::ColorNative, sh1107::Mono, udma::Udma}; + use cramium_hal::{iox::IoxValue, minigfx::ColorNative, sh1107::Mono, udma::Udma}; use crate::platform::cramium::gfx; //------------- test I2C ------------ @@ -345,7 +403,7 @@ pub fn early_init() -> u32 { crate::println!("LDO result - last value should be 0xe: {:x?}", ldo); //------------- test USB --------------- - #[cfg(feature = "usb")] + #[cfg(feature = "usb-test")] { crate::platform::cramium::usb::init_usb(); // this does not return if USB is initialized correctly... @@ -674,14 +732,6 @@ pub fn early_init() -> u32 { frames += 1; } - // Make this true to have the system shut down by disconnecting its own battery while on battery power - // Note this does nothing if you have USB power plugged in. - if false { - crate::println!("shutting down..."); - pmic.set_ldo(&mut i2c, Some(0.9), WhichLdo::Aldo3).ok(); - crate::println!("system should be off"); - } - // ---------------- test TRNG ------------------- // configure the SCE clocks to enable the TRNG let mut sce = CSR::new(HW_SCE_GLBSFR_BASE as *mut u32); @@ -1160,8 +1210,8 @@ pub fn early_init() -> u32 { crate::println!("pmu_cr upd: {:x}", aoc_base.base().add(4).read_volatile()); } */ - udma_uart.write("Press any key to continue...".as_bytes()); - getc(); + // udma_uart.write("Press any key to continue...".as_bytes()); + // getc(); udma_uart.write(b"\n\rBooting!\n\r"); perclk From 6c79441ee134ec4a8c4c0890b86a392500100505 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 01:45:56 +0800 Subject: [PATCH 09/42] add boot option to loader --- loader/src/platform/cramium/update.rs | 87 ++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 3 deletions(-) diff --git a/loader/src/platform/cramium/update.rs b/loader/src/platform/cramium/update.rs index 5aabce976..1787da2d4 100644 --- a/loader/src/platform/cramium/update.rs +++ b/loader/src/platform/cramium/update.rs @@ -1,9 +1,90 @@ +use cramium_hal::iox::{IoGpio, IoSetup, Iox, IoxPort, IoxValue}; +use cramium_hal::minigfx::{FrameBuffer, Point}; +use cramium_hal::sh1107::Mono; +use cramium_hal::udma; +use utralib::generated::*; + +use crate::platform::cramium::gfx; + +pub fn scan_keyboard( + iox: &T, + rows: &[(IoxPort, u8)], + cols: &[(IoxPort, u8)], +) -> [Option<(u8, u8)>; 4] { + let mut key_presses: [Option<(u8, u8)>; 4] = [None; 4]; + let mut key_press_index = 0; // no Vec in no_std, so we have to manually track it + + for (row, (port, pin)) in rows.iter().enumerate() { + iox.set_gpio_pin_value(*port, *pin, IoxValue::Low); + for (col, (col_port, col_pin)) in cols.iter().enumerate() { + if iox.get_gpio_pin_value(*col_port, *col_pin) == IoxValue::Low { + if key_press_index < key_presses.len() { + key_presses[key_press_index] = Some((row as u8, col as u8)); + key_press_index += 1; + } + } + } + iox.set_gpio_pin_value(*port, *pin, IoxValue::High); + } + key_presses +} + /// Checks to see if the necessary conditions for an update are met -pub fn process_update() { +pub fn process_update(perclk: u32) { + crate::println!("entering process_update"); // Placeholder: // Remember to lock the root keys before processing any updates crate::platform::cramium::verifier::lifecycle_lock_root(); - { - // update code here + + crate::println!("waiting for button press"); + let mut iox = Iox::new(utra::iox::HW_IOX_BASE as *mut u32); + let mut udma_global = udma::GlobalConfig::new(utra::udma_ctrl::HW_UDMA_CTRL_BASE as *mut u32); + + let mut sh1107 = cramium_hal::sh1107::Oled128x128::new(perclk, &mut iox, &mut udma_global); + + gfx::msg(&mut sh1107, " START to boot", Point::new(0, 16), Mono::White.into(), Mono::Black.into()); + gfx::msg(&mut sh1107, " SELECT to update", Point::new(0, 0), Mono::White.into(), Mono::Black.into()); + + sh1107.buffer_swap(); + sh1107.draw(); + + // setup IO pins to check for update viability + let (rows, cols) = cramium_hal::board::baosec::setup_kb_pins(&iox); + + let mut key_pressed = false; + let mut do_update = false; + while !key_pressed { + let kps = scan_keyboard(&iox, &rows, &cols); + for kp in kps { + match kp { + // SELECT + Some((0, 2)) => { + crate::println!("SELECT detected"); + do_update = true; + key_pressed = true; + } + // START + Some((2, 1)) => { + crate::println!("START detected"); + key_pressed = true; + } + Some((r, c)) => { + crate::println!("{},{} pressed", r, c); + // this causes the system to boot if *any* key is pressed. + key_pressed = true; + } + None => (), + } + } + } + if do_update { + update(); + } +} + +fn update() { + crate::platform::cramium::usb::init_usb(); + unsafe { + crate::platform::cramium::usb::test_usb(); } } From 5d0cc45c439562f1f41752ad7d44caa63ac91ffc Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 01:46:22 +0800 Subject: [PATCH 10/42] update lockfile to reflect new dependencies --- Cargo.lock | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 533ec1f0d..c1980c3fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2985,6 +2985,7 @@ dependencies = [ "crc 1.8.1", "curve25519-dalek-loader", "ed25519-dalek-loader", + "ghostfat", "lazy_static", "libm 0.2.8", "nalgebra", @@ -5750,16 +5751,18 @@ version = "0.1.0" dependencies = [ "cram-hal-service", "cramium-hal", - "ghostfat", - "locales", "log", "num-derive 0.3.3", "num-traits", + "packed_struct", + "rkyv 0.8.8", + "usb-device", "utralib 0.1.25", "xous 0.9.64 (registry+https://github.com/rust-lang/crates.io-index)", "xous-api-log", "xous-api-names", "xous-api-ticktimer", + "xous-ipc 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "xous-usb-hid", ] From dfe5d50f41026d516054bf157eb86da53f20b80e Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 01:46:34 +0800 Subject: [PATCH 11/42] start to lay in the APIs for the USB interface in userspace --- services/usb-cramium/Cargo.toml | 22 +- services/usb-cramium/src/api.rs | 212 ++++++++++++++++++ services/usb-cramium/src/lib.rs | 377 ++++++++++++++++++++++++++++++++ 3 files changed, 608 insertions(+), 3 deletions(-) create mode 100644 services/usb-cramium/src/api.rs create mode 100644 services/usb-cramium/src/lib.rs diff --git a/services/usb-cramium/Cargo.toml b/services/usb-cramium/Cargo.toml index ccbec2334..e2494565c 100644 --- a/services/usb-cramium/Cargo.toml +++ b/services/usb-cramium/Cargo.toml @@ -8,6 +8,7 @@ utralib = { version = "0.1.25", optional = true, default-features = false } xous-names = { package = "xous-api-names", version = "0.9.63" } ticktimer = { package = "xous-api-ticktimer", version = "0.9.61" } xous = "0.9.64" +xous-ipc = "0.10.2" log-server = { package = "xous-api-log", version = "0.1.61" } log = "0.4.14" num-derive = { version = "0.3.3", default-features = false } @@ -15,10 +16,25 @@ num-traits = { version = "0.2.14", default-features = false } cramium-hal = { path = "../../libs/cramium-hal", features = ["std"] } cram-hal-service = { path = "../cram-hal-service" } xous-usb-hid = { git = "https://github.com/betrusted-io/xous-usb-hid.git", branch = "main" } -ghostfat = "0.5.0" +rkyv = { version = "0.8.8", default-features = false, features = [ + "std", + "alloc", +] } +packed_struct = { version = "0.10", default-features = false } # used by the xous-usb-hid crate + +[dependencies.usb-device] +# see top level Cargo.toml for patch.crates-io directive to help with dev work +version = "0.2.8" +features = ["log"] locales = { path = "../../locales" } [features] -cramium-soc = ["utralib/cramium-soc"] -default = [] +cramium-soc = ["utralib/cramium-soc", "utralib/std"] +# none of these other modes are actually supported, but are listed here +# to allow direct re-use of API code +precursor = ["utralib/precursor", "utralib/std"] +hosted = ["utralib/hosted", "utralib/std"] +renode = ["utralib/renode", "utralib/std"] +mass-storage = [] +default = ["mass-storage"] diff --git a/services/usb-cramium/src/api.rs b/services/usb-cramium/src/api.rs new file mode 100644 index 000000000..10cbc4e0b --- /dev/null +++ b/services/usb-cramium/src/api.rs @@ -0,0 +1,212 @@ +// Note: the log server relies on this name not changing in order to hook the serial port for logging output. +// changing this name shouldn't lead to a crash, but it will lead to the USB driver being undiscoverable by +// the log crate. +pub(crate) const SERVER_NAME_USB_DEVICE: &'static str = "_Xous USB device driver_"; + +#[derive(num_derive::FromPrimitive, num_derive::ToPrimitive, Debug)] +pub(crate) enum Opcode { + /// Returns the link status + LinkStatus = 0, + /// Send a keyboard code + SendKeyCode = 1, + /// "Type" a string to the keyboard. This API is relied upon by the log crate. + SendString = 2, + /// Get the current LED state + GetLedState = 3, + /// Switch to a specified device core + SwitchCores = 4, + /// Makes sure a given core is selected + EnsureCore = 5, + /// Check which core is connected + WhichCore = 6, + /// Restrict the debug core + RestrictDebugAccess = 7, + /// Retrieve restriction state + IsRestricted = 8, + /// Set-and-check of USB debug restriction + DebugUsbOp = 9, + /// Set autotype rate + SetAutotypeRate = 10, + /// Register a USB event observer + RegisterUsbObserver = 11, + /// Modify log level + SetLogLevel = 12, + + /// Send a U2F message + U2fTx = 128, + /// Blocks the caller, waiting for a U2F message + U2fRxDeferred = 129, + /// A bump from the timeout process to check if U2fRx has timed out + U2fRxTimeout = 130, + + /// Query if the HID driver was able to start + IsSocCompatible = 256, + + /// Hook serial ASCII listener + SerialHookAscii = 512, + /// Hook serial binary listener + SerialHookBinary = 513, + /// Flush any serial buffers + SerialFlush = 514, + /// Hook eager serial sender for TRNG output. This will not succeed if hooked for console mode already. + SerialHookTrngSender = 515, + /// Hook serial to the console input + SerialHookConsole = 516, + /// Clear any hooks + SerialClearHooks = 517, + /// TRNG send poll + SerialTrngPoll = 518, + + #[cfg(feature = "mass-storage")] + SetBlockDevice = 1024, + #[cfg(feature = "mass-storage")] + SetBlockDeviceSID = 1025, + #[cfg(feature = "mass-storage")] + ResetBlockDevice = 1026, + + // HIDv2 + /// Read a HID report + HIDReadReport = 1027, + + /// Write a HID report + HIDWriteReport = 1028, + + /// Set the HID descriptor to be pushed to the USB host + HIDSetDescriptor = 1029, + + /// Unset HID descriptor and reset HIDv2 state + HIDUnsetDescriptor = 1030, + + /// Handle the USB interrupt + UsbIrqHandler = 2048, + /// Suspend/resume callback + #[cfg(any(feature = "renode", feature = "precursor", feature = "hosted"))] + SuspendResume = 2049, + /// Exits the server + Quit = 4096, + + /// API used by the logging crate. The number is hard-coded; don't change it. + LogString = 8192, +} + +// The log crate depends on this API not changing. +#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Clone)] +pub struct UsbString { + pub s: String, + pub sent: Option, +} + +#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone)] +pub struct U2fMsgIpc { + /// All U2F protocol messages are 64 bytes + pub data: [u8; 64], + /// Encodes the state of the message + pub code: U2fCode, + /// Specifies an optional timeout + pub timeout_ms: Option, +} + +#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone, Eq, PartialEq)] +pub enum U2fCode { + Tx, + TxAck, + RxWait, + RxAck, + RxTimeout, + Hangup, + Denied, +} + +#[derive(Eq, PartialEq, Copy, Clone)] +#[repr(usize)] +pub enum UsbDeviceType { + Debug = 0, + FidoKbd = 1, + Fido = 2, + MassStorage = 3, + Serial = 4, + HIDv2 = 5, +} +use std::convert::TryFrom; + +impl TryFrom for UsbDeviceType { + type Error = &'static str; + + fn try_from(value: usize) -> Result { + match value { + 0 => Ok(UsbDeviceType::Debug), + 1 => Ok(UsbDeviceType::FidoKbd), + 2 => Ok(UsbDeviceType::Fido), + 3 => Ok(UsbDeviceType::MassStorage), + 4 => Ok(UsbDeviceType::Serial), + 5 => Ok(UsbDeviceType::HIDv2), + _ => Err("Invalid UsbDeviceType specifier"), + } + } +} + +pub const SERIAL_BINARY_BUFLEN: usize = 128; +#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Clone)] +pub struct UsbSerialAscii { + pub s: String, + pub delimiter: Option, +} + +#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone)] +pub struct UsbSerialBinary { + pub d: [u8; SERIAL_BINARY_BUFLEN], + pub len: usize, +} + +pub const MAX_HID_REPORT_DESCRIPTOR_LEN: usize = 1024; + +#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone)] +pub struct HIDReportDescriptorMessage { + pub descriptor: [u8; MAX_HID_REPORT_DESCRIPTOR_LEN], + pub len: usize, +} + +#[derive(Copy, Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +#[repr(C, align(8))] +pub struct HIDReport(pub [u8; 64]); + +impl Default for HIDReport { + fn default() -> Self { return Self([0u8; 64]) } +} + +#[derive(Debug, Default, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone)] +pub struct HIDReportMessage { + pub data: Option, +} + +/// this structure is used to register a USB listener. +#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Clone)] +pub(crate) struct UsbListenerRegistration { + pub server_name: String, + pub listener_op_id: usize, +} + +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[repr(usize)] +pub enum LogLevel { + Trace = 0, + Debug = 1, + Info = 2, + Warn = 3, + Err = 4, +} + +impl TryFrom for LogLevel { + type Error = &'static str; + + fn try_from(value: usize) -> Result { + match value { + 0 => Ok(LogLevel::Trace), + 1 => Ok(LogLevel::Debug), + 2 => Ok(LogLevel::Info), + 3 => Ok(LogLevel::Warn), + 4 => Ok(LogLevel::Err), + _ => Err("Invalid LogLevel"), + } + } +} diff --git a/services/usb-cramium/src/lib.rs b/services/usb-cramium/src/lib.rs new file mode 100644 index 000000000..87a1bafe9 --- /dev/null +++ b/services/usb-cramium/src/lib.rs @@ -0,0 +1,377 @@ +#![cfg_attr(target_os = "none", no_std)] + +use cram_hal_service::trng; + +pub mod api; +pub use api::*; +use num_traits::*; +use packed_struct::PackedStruct; +use rkyv::option::ArchivedOption; +use trng::api::TrngTestMode; +pub use usb_device::device::UsbDeviceState; +use xous::{CID, Message, send_message}; +use xous_ipc::Buffer; +pub use xous_usb_hid::device::fido::RawFidoReport; +pub use xous_usb_hid::device::keyboard::KeyboardLedsReport; +pub use xous_usb_hid::page::Keyboard as UsbKeyCode; + +#[derive(Debug)] +pub struct UsbHid { + conn: CID, +} +impl UsbHid { + pub fn new() -> Self { + let xns = xous_names::XousNames::new().expect("couldn't connect to XousNames"); + REFCOUNT.fetch_add(1, Ordering::Relaxed); + let conn = xns + .request_connection_blocking(api::SERVER_NAME_USB_DEVICE) + .expect("Can't connect to USB device server"); + UsbHid { conn } + } + + pub fn set_block_device(&self, read_id: usize, write_id: usize, max_lba_id: usize) { + send_message( + self.conn, + Message::new_blocking_scalar( + Opcode::SetBlockDevice.to_usize().unwrap(), + read_id, + write_id, + max_lba_id, + 0, + ), + ) + .unwrap(); + } + + pub fn set_block_device_sid(&self, app_sid: xous::SID) { + let sid = app_sid.to_u32(); + send_message( + self.conn, + Message::new_blocking_scalar( + Opcode::SetBlockDeviceSID.to_usize().unwrap(), + sid.0 as usize, + sid.1 as usize, + sid.2 as usize, + sid.3 as usize, + ), + ) + .unwrap(); + } + + pub fn reset_block_device(&self) { + send_message( + self.conn, + Message::new_blocking_scalar(Opcode::ResetBlockDevice.to_usize().unwrap(), 0, 0, 0, 0), + ) + .unwrap(); + } + + /// used to query if the HID core was able to start. Mainly to handle edge cases between updates. + pub fn is_soc_compatible(&self) -> bool { true } + + /// this will always trigger a reset, even if it's the same core we're switching to + pub fn switch_to_core(&self, _core: UsbDeviceType) -> Result<(), xous::Error> { Ok(()) } + + /// this will not trigger a reset if we're already on the requested core + pub fn ensure_core(&self, _core: UsbDeviceType) -> Result<(), xous::Error> { Ok(()) } + + pub fn get_current_core(&self) -> Result { Ok(UsbDeviceType::FidoKbd) } + + pub fn restrict_debug_access(&self, _restrict: bool) -> Result<(), xous::Error> { Ok(()) } + + pub fn is_debug_restricted(&self) -> Result { Ok(true) } + + // if do_lock is Some(), set the debug USB lock status to locked if true, unlocked if false + // returns a tuple of (bool, bool) -> (is_locked, force_update) + // needs_update is so that the polling function knows to redraw the UX after a resume-from-suspend + pub fn debug_usb(&self, _do_lock: Option) -> Result<(bool, bool), xous::Error> { Ok((true, false)) } + + pub fn status(&self) -> UsbDeviceState { + match send_message( + self.conn, + Message::new_blocking_scalar(Opcode::LinkStatus.to_usize().unwrap(), 0, 0, 0, 0), + ) { + Ok(xous::Result::Scalar1(code)) => match code { + 0 => UsbDeviceState::Default, + 1 => UsbDeviceState::Addressed, + 2 => UsbDeviceState::Configured, + 3 => UsbDeviceState::Suspend, + _ => panic!("Internal error: illegal status code"), + }, + _ => panic!("Internal error: illegal return type"), + } + } + + /// Sends up to three keyboard codes at once as defined by USB HID usage tables; + /// see See [Universal Serial Bus (USB) HID Usage Tables Version 1.12](): + /// If the vector is empty, you get an all-key-up situation + pub fn send_keycode(&self, code: Vec, auto_keyup: bool) -> Result<(), xous::Error> { + if code.len() > 3 { + log::warn!("Excess keycodes ignored"); + } + match send_message( + self.conn, + Message::new_blocking_scalar( + Opcode::SendKeyCode.to_usize().unwrap(), + if code.len() >= 1 { code[0] as usize } else { 0 }, + if code.len() >= 2 { code[1] as usize } else { 0 }, + if code.len() >= 3 { code[2] as usize } else { 0 }, + if auto_keyup { 1 } else { 0 }, + ), + ) { + Ok(xous::Result::Scalar1(code)) => { + match code { + 0 => Ok(()), + // indicates that we aren't connected to a host to send characters + _ => Err(xous::Error::UseBeforeInit), + } + } + _ => Err(xous::Error::UseBeforeInit), + } + } + + /// This will attempt to send a string using an API based on the currently connected device + /// If it's a Keyboard, it will "type" it; if it's a UART, it will just blast it out the Tx. + pub fn send_str(&self, s: &str) -> Result { + let serializer = UsbString { s: String::from(s), sent: None }; + let mut buf = Buffer::into_buf(serializer).or(Err(xous::Error::InternalError))?; + buf.lend_mut(self.conn, Opcode::SendString.to_u32().unwrap()).or(Err(xous::Error::InternalError))?; + let returned = buf.to_original::().or(Err(xous::Error::InternalError))?; + match returned.sent { + Some(sent) => Ok(sent as usize), + // indicate that probably the USB was not connected + None => Err(xous::Error::UseBeforeInit), + } + } + + /// Sets the autotype delay. Defaults to 30ms on boot, must be reset every time on reboot. + pub fn set_autotype_delay_ms(&self, rate: usize) { + send_message( + self.conn, + Message::new_scalar(Opcode::SetAutotypeRate.to_usize().unwrap(), rate, 0, 0, 0), + ) + .unwrap(); // just unwrap it. If the send fails, we want to see the panic at this spot! + } + + pub fn get_led_state(&self) -> Result { + match send_message( + self.conn, + Message::new_blocking_scalar(Opcode::GetLedState.to_usize().unwrap(), 0, 0, 0, 0), + ) { + Ok(xous::Result::Scalar1(code)) => match KeyboardLedsReport::unpack(&[code as u8]) { + Ok(r) => Ok(r), + Err(_) => Err(xous::Error::InternalError), + }, + _ => panic!("Internal error: illegal return type"), + } + } + + pub fn u2f_wait_incoming(&self) -> Result { + let req = U2fMsgIpc { data: [0; 64], code: U2fCode::RxWait, timeout_ms: None }; + let mut buf = Buffer::into_buf(req).or(Err(xous::Error::InternalError))?; + buf.lend_mut(self.conn, Opcode::U2fRxDeferred.to_u32().unwrap()) + .or(Err(xous::Error::InternalError))?; + let ack = buf.to_original::().unwrap(); + match ack.code { + U2fCode::RxAck => { + let mut u2fmsg = RawFidoReport::default(); + u2fmsg.packet.copy_from_slice(&ack.data); + Ok(u2fmsg) + } + U2fCode::Hangup => Err(xous::Error::ProcessTerminated), + U2fCode::RxTimeout => Err(xous::Error::Timeout), + _ => Err(xous::Error::InternalError), + } + } + + /// Note: this variant is not tested. + pub fn u2f_wait_incoming_timeout(&self, timeout_ms: u64) -> Result { + let req = U2fMsgIpc { data: [0; 64], code: U2fCode::RxWait, timeout_ms: Some(timeout_ms) }; + let mut buf = Buffer::into_buf(req).or(Err(xous::Error::InternalError))?; + buf.lend_mut(self.conn, Opcode::U2fRxDeferred.to_u32().unwrap()) + .or(Err(xous::Error::InternalError))?; + let ack = buf.to_original::().unwrap(); + match ack.code { + U2fCode::RxAck => { + let mut u2fmsg = RawFidoReport::default(); + u2fmsg.packet.copy_from_slice(&ack.data); + Ok(u2fmsg) + } + U2fCode::Hangup => Err(xous::Error::ProcessTerminated), + U2fCode::RxTimeout => Err(xous::Error::Timeout), + _ => Err(xous::Error::InternalError), + } + } + + pub fn u2f_send(&self, msg: RawFidoReport) -> Result<(), xous::Error> { + let mut req = U2fMsgIpc { data: [0; 64], code: U2fCode::Tx, timeout_ms: None }; + req.data.copy_from_slice(&msg.packet); + let mut buf = Buffer::into_buf(req).or(Err(xous::Error::InternalError))?; + buf.lend_mut(self.conn, Opcode::U2fTx.to_u32().unwrap()).or(Err(xous::Error::InternalError))?; + let ack = buf.to_original::().unwrap(); + match ack.code { + U2fCode::TxAck => Ok(()), + U2fCode::Denied => Err(xous::Error::AccessDenied), + _ => Err(xous::Error::InternalError), + } + } + + /// Blocks until an ASCII string terminated by `delimiter` is received on serial; if `None`, it + /// will return as soon as a character (or series of characters) have been received (thus the return + /// `String` will be piecemeal) + pub fn serial_wait_ascii(&self, delimiter: Option) -> String { + let req = UsbSerialAscii { s: String::new(), delimiter }; + let mut buf = Buffer::into_buf(req).or(Err(xous::Error::InternalError)).expect("Internal error"); + buf.lend_mut(self.conn, Opcode::SerialHookAscii.to_u32().unwrap()) + .or(Err(xous::Error::InternalError)) + .expect("Internal error"); + let resp = buf.to_original::().unwrap(); + resp.s + } + + /// Blocks until enough binary data has been received to fill the buffer + /// Another thread can be used to call serial_flush() if we don't want to + /// block forever and we're receiving small amounts of binary data. + pub fn serial_wait_binary(&self) -> Vec { + let req = UsbSerialBinary { d: [0u8; SERIAL_BINARY_BUFLEN], len: 0 }; + let mut buf = Buffer::into_buf(req).or(Err(xous::Error::InternalError)).expect("Internal error"); + buf.lend_mut(self.conn, Opcode::SerialHookBinary.to_u32().unwrap()) + .or(Err(xous::Error::InternalError)) + .expect("Internal error"); + let resp = buf.to_original::().unwrap(); + resp.d[..resp.len].to_vec() + } + + /// Non-blocking call that issues a serial flush command to the USB stack + pub fn serial_flush(&self) -> Result<(), xous::Error> { + send_message(self.conn, Message::new_scalar(Opcode::SerialFlush.to_usize().unwrap(), 0, 0, 0, 0)) + .map(|_| ()) + } + + /// Inject serial input over USB to the debug console. Dangerous! + /// This will also override/discard any existing hooked listeners. + pub fn serial_console_input_injection(&self) { + send_message( + self.conn, + Message::new_scalar(Opcode::SerialHookConsole.to_usize().unwrap(), 0, 0, 0, 0), + ) + .unwrap(); + } + + pub fn serial_clear_input_hooks(&self) { + send_message( + self.conn, + Message::new_scalar(Opcode::SerialClearHooks.to_usize().unwrap(), 0, 0, 0, 0), + ) + .unwrap(); + } + + /// Tries to set the serial port in TRNG mode. Will silently fail if already in console mode. + pub fn serial_set_trng_mode(&self, mode: TrngTestMode) { + send_message( + self.conn, + Message::new_scalar( + Opcode::SerialHookTrngSender.to_usize().unwrap(), + mode.to_usize().unwrap(), + 0, + 0, + 0, + ), + ) + .unwrap(); + } + + pub fn register_u2f_observer(&self, server_name: &str, action_opcode: usize) { + let kr = + UsbListenerRegistration { server_name: String::from(server_name), listener_op_id: action_opcode }; + let buf = Buffer::into_buf(kr).unwrap(); + buf.lend(self.conn, Opcode::RegisterUsbObserver.to_u32().unwrap()) + .expect("couldn't register listener"); + } + + /// Sets the userland application HID device descriptor. + /// It cannot be longer than 1024 bytes. + pub fn connect_hid_app(&self, descriptor: Vec) -> Result<(), xous::Error> { + if descriptor.len() > MAX_HID_REPORT_DESCRIPTOR_LEN { + return Err(xous::Error::OutOfMemory); + } + + let mut container = HIDReportDescriptorMessage { + descriptor: [0u8; MAX_HID_REPORT_DESCRIPTOR_LEN], + len: descriptor.len(), + }; + + for (place, element) in container.descriptor.iter_mut().zip(descriptor.iter()) { + *place = *element; + } + + let mut buf = Buffer::into_buf(container).or(Err(xous::Error::InternalError))?; + buf.lend_mut(self.conn, Opcode::HIDSetDescriptor.to_u32().unwrap()).map(|_| ())?; + + Ok(()) + } + + /// Unset the userland application HID device descriptor and discards the cached + /// reports. + pub fn disconnect_hid_app(&self) -> Result<(), xous::Error> { + match send_message( + self.conn, + Message::new_blocking_scalar(Opcode::HIDUnsetDescriptor.to_usize().unwrap(), 0, 0, 0, 0), + ) { + Ok(_) => Ok(()), + Err(err) => Err(err), + } + } + + /// Reads a HID report off the USB bus. + pub fn read_report(&self) -> Result { + let report = HIDReportMessage::default(); + + let mut buf = Buffer::into_buf(report).or(Err(xous::Error::InternalError))?; + buf.lend_mut(self.conn, Opcode::HIDReadReport.to_u32().unwrap()).map(|_| ())?; + + let report = buf.as_flat::().unwrap(); + + match &report.data { + ArchivedOption::Some(data) => { + let mut ret = HIDReport::default(); + + for (&s, d) in data.0[..data.0.len() as usize].iter().zip(ret.0.iter_mut()) { + *d = s; + } + + Ok(ret) + } + ArchivedOption::None => Err(xous::Error::UnknownError), + } + } + + /// Writes a HID report on the USB bus. + pub fn write_report(&self, report: HIDReport) -> Result<(), xous::Error> { + let buf = Buffer::into_buf(report).or(Err(xous::Error::InternalError))?; + buf.lend(self.conn, Opcode::HIDWriteReport.to_u32().unwrap()).map(|_| ())?; + + Ok(()) + } + + /// Attempts to set the logging level of the USB server + pub fn set_log_level(&self, level: LogLevel) { + send_message( + self.conn, + Message::new_scalar(Opcode::SetLogLevel.to_usize().unwrap(), level as usize, 0, 0, 0), + ) + .unwrap(); + } +} + +use core::sync::atomic::{AtomicU32, Ordering}; +static REFCOUNT: AtomicU32 = AtomicU32::new(0); +impl Drop for UsbHid { + fn drop(&mut self) { + if REFCOUNT.fetch_sub(1, Ordering::Relaxed) == 1 { + unsafe { + xous::disconnect(self.conn).unwrap(); + } + } + } +} From 2d322d852c21f5246969c3418a8e4a953dcca16d Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 20:36:57 +0800 Subject: [PATCH 12/42] cleanup warnin --- services/usb-cramium/Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/usb-cramium/Cargo.toml b/services/usb-cramium/Cargo.toml index e2494565c..42ccc2dd8 100644 --- a/services/usb-cramium/Cargo.toml +++ b/services/usb-cramium/Cargo.toml @@ -21,14 +21,13 @@ rkyv = { version = "0.8.8", default-features = false, features = [ "alloc", ] } packed_struct = { version = "0.10", default-features = false } # used by the xous-usb-hid crate +locales = { path = "../../locales" } [dependencies.usb-device] # see top level Cargo.toml for patch.crates-io directive to help with dev work version = "0.2.8" features = ["log"] -locales = { path = "../../locales" } - [features] cramium-soc = ["utralib/cramium-soc", "utralib/std"] # none of these other modes are actually supported, but are listed here From 51717fd7d90de2e0c7d773f8d20aa167caa862e7 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 20:38:31 +0800 Subject: [PATCH 13/42] move to simple-fatfs --- loader/Cargo.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/loader/Cargo.toml b/loader/Cargo.toml index e26f62433..6a9985ec6 100644 --- a/loader/Cargo.toml +++ b/loader/Cargo.toml @@ -42,7 +42,8 @@ libm = { version = "0.2.8", optional = true } nalgebra = { version = "0.33", default-features = false, features = [ "libm", ], optional = true } -ghostfat = { version = "0.5.0", optional = true, default-features = false } +simple-fatfs = { version = "0.1.0-alpha.1", optional = true, default-features = false } +linked_list_allocator = { version = "0.10.5", optional = true } [dependencies.com_rs] git = "https://github.com/betrusted-io/com_rs" @@ -77,7 +78,8 @@ cramium-soc = [ "rand_chacha", "sram-margin", "libm", - "ghostfat", + "simple-fatfs", + "linked_list_allocator", # "boot-delay", ] board-baosec = ["cramium-hal/board-baosec"] From 8cc80741acdc9769d0c65a5f2576be7e7c1e7c1a Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 20:39:06 +0800 Subject: [PATCH 14/42] cleanup warning --- loader/src/platform/cramium/gfx.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/loader/src/platform/cramium/gfx.rs b/loader/src/platform/cramium/gfx.rs index 5c5d1f248..19c8cc7cc 100644 --- a/loader/src/platform/cramium/gfx.rs +++ b/loader/src/platform/cramium/gfx.rs @@ -92,6 +92,7 @@ pub fn msg<'a>(fb: &mut dyn FrameBuffer, text: &'a str, ll_pos: Point, fg: Color ll_pos.x += x_update; } +#[allow(dead_code)] pub fn line(fb: &mut dyn FrameBuffer, l: Line, clip: Option, xor: bool) { let color: ColorNative; if l.style.stroke_color.is_some() { From e181c0d460b0e26a015ce1a7503513cefe66fb8c Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 20:39:27 +0800 Subject: [PATCH 15/42] use global allocator for simple-fatfs also initialize the disk image with some default data --- loader/src/platform/cramium/usb/driver.rs | 30 +++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/loader/src/platform/cramium/usb/driver.rs b/loader/src/platform/cramium/usb/driver.rs index c83dfd2b6..3f94f26a6 100644 --- a/loader/src/platform/cramium/usb/driver.rs +++ b/loader/src/platform/cramium/usb/driver.rs @@ -5,12 +5,23 @@ use cramium_hal::usb::compat::AtomicCsr; use cramium_hal::usb::driver::CorigineUsb; use cramium_hal::usb::driver::*; use cramium_hal::usb::utra::*; +use linked_list_allocator::LockedHeap; use super::*; +#[global_allocator] +static ALLOCATOR: LockedHeap = LockedHeap::empty(); + pub(crate) static mut USB: Option = None; pub fn init_usb() { + let heap_start = HEAP_ADDRESS; + let heap_end = HEAP_ADDRESS + HEAP_LEN; + let heap_size = heap_end - heap_start; + unsafe { + ALLOCATOR.lock().init(heap_start as *mut u8, heap_size); + } + let mut usb = unsafe { cramium_hal::usb::driver::CorigineUsb::new( 0, @@ -25,11 +36,23 @@ pub fn init_usb() { // initialize the "disk" area let disk = mass_storage::conjure_disk(); disk.fill(0); + // setup MBR disk[..MBR_TEMPLATE.len()].copy_from_slice(&MBR_TEMPLATE); + /* //set block size disk[0xb..0xd].copy_from_slice(&SECTOR_SIZE.to_le_bytes()); //set storage size disk[0x20..0x24].copy_from_slice(&(RAMDISK_LEN as u32 / SECTOR_SIZE as u32).to_le_bytes()); + */ + // populate an initial "windows-friendly" FAT layout, generated by plugging the empty disk + // into windows and letting it...do its thing, then snapshotting the data. + disk[TABLE_OFFSET_FAT1..TABLE_OFFSET_FAT1 + FAT_TABLE.len()].copy_from_slice(&FAT_TABLE); + disk[TABLE_OFFSET_FAT2..TABLE_OFFSET_FAT2 + FAT_TABLE.len()].copy_from_slice(&FAT_TABLE); + + disk[ROOT_DIR_OFFSET..ROOT_DIR_OFFSET + ROOT_DIR.len()].copy_from_slice(&ROOT_DIR); + disk[FAT_DATA1_OFFSET..FAT_DATA1_OFFSET + FAT_DATA1.len()].copy_from_slice(&FAT_DATA1); + disk[FAT_DATA2_OFFSET..FAT_DATA2_OFFSET + FAT_DATA2.len()].copy_from_slice(&FAT_DATA2); + disk[FAT_DATA3_OFFSET..FAT_DATA3_OFFSET + FAT_DATA3.len()].copy_from_slice(&FAT_DATA3); // install the interrupt handler // setup the stack & controller @@ -45,6 +68,7 @@ pub fn init_usb() { } } +#[allow(dead_code)] pub unsafe fn test_usb() { if let Some(ref mut usb_ref) = USB { let usb = &mut *core::ptr::addr_of_mut!(*usb_ref); @@ -181,7 +205,7 @@ fn handle_event(this: &mut CorigineUsb, event_trb: &mut EventTrbS) -> CrgEvent { // this.print_status(portsc_val); let portsc = PortSc(portsc_val); - crate::println!("{:?}", portsc); + // crate::println!("{:?}", portsc); if portsc.prc() && !portsc.pr() { crate::println!("update_current_speed() - reset done"); @@ -207,7 +231,7 @@ fn handle_event(this: &mut CorigineUsb, event_trb: &mut EventTrbS) -> CrgEvent { } else { udc_ep.deq_pt = AtomicPtr::new(deq_pt as *mut TransferTrbS); } - crate::println!("EventTransfer: comp_code {:?}, PEI {}", comp_code, pei); + // crate::println!("EventTransfer: comp_code {:?}, PEI {}", comp_code, pei); let dir = (pei & 1) != 0; if pei == 0 { @@ -252,6 +276,7 @@ fn handle_event(this: &mut CorigineUsb, event_trb: &mut EventTrbS) -> CrgEvent { let w_index = setup_pkt.w_index; let w_length = setup_pkt.w_length; + /* crate::println!( " b_request={:x}, b_request_type={:x}, w_value={:04x}, w_index=0x{:x}, w_length={}", setup_pkt.b_request, @@ -260,6 +285,7 @@ fn handle_event(this: &mut CorigineUsb, event_trb: &mut EventTrbS) -> CrgEvent { w_index, w_length ); + */ if (setup_pkt.b_request_type & USB_TYPE_MASK) == USB_TYPE_STANDARD { match setup_pkt.b_request { From d9709997fb3d697f09457dcb76ddfec0e8616499 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 20:40:05 +0800 Subject: [PATCH 16/42] add template disk image this is a pre-initialized, FAT12 image that is compatible with Windows (which is the harder of the OSes to please) --- .../src/platform/cramium/usb/mass_storage.rs | 75 +++++++++++++++++-- 1 file changed, 67 insertions(+), 8 deletions(-) diff --git a/loader/src/platform/cramium/usb/mass_storage.rs b/loader/src/platform/cramium/usb/mass_storage.rs index a40ffe873..b6cd68c8a 100644 --- a/loader/src/platform/cramium/usb/mass_storage.rs +++ b/loader/src/platform/cramium/usb/mass_storage.rs @@ -16,17 +16,21 @@ use super::*; // 0x6101_X000 "heap" would go here, except we don't have one // 0x6100_0000 rw data for rust is at base of RAM pub(crate) const RAMDISK_ADDRESS: usize = utralib::HW_SRAM_MEM + 128 * 1024; -pub(crate) const RAMDISK_LEN: usize = 1024 * 1024; // 1MiB of RAM allocated to "disk" +pub(crate) const RAMDISK_LEN: usize = 1536 * 1024; // 1.5MiB of RAM allocated to "disk" pub(crate) const SECTOR_SIZE: u16 = 512; +pub(crate) const HEAP_ADDRESS: usize = RAMDISK_ADDRESS + RAMDISK_LEN; +pub(crate) const HEAP_LEN: usize = 128 * 1024; + // MBR template // 0x0b~0x0C 2 bytes means block size, default 0x200 bytes // 0x20~0x23 4 bytes means block number, default 0x400 block #[rustfmt::skip] // keep this in 16-byte width +// TODO: change volume label from BAOSOR to BAOSEC pub(crate) const MBR_TEMPLATE: [u8; SECTOR_SIZE as usize] = [ - 0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x20, 0x01, 0x00, - 0x02, 0x00, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x01, 0x3f, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x80, 0x00, 0x29, 0x72, 0x1a, 0x65, 0xA4, 0x4E, 0x4F, 0x20, 0x4E, 0x41, + 0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x20, 0x06, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x0C, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0xBB, 0xBF, 0xB7, 0xB0, 0x4E, 0x4F, 0x20, 0x4E, 0x41, 0x4D, 0x45, 0x20, 0x20, 0x20, 0x20, 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x33, 0xC9, 0x8E, 0xD1, 0xBC, 0xF0, 0x7B, 0x8E, 0xD9, 0xB8, 0x00, 0x20, 0x8E, 0xC0, 0xFC, 0xBD, 0x00, 0x7C, 0x38, 0x4E, 0x24, 0x7D, 0x24, 0x8B, 0xC1, 0x99, 0xE8, 0x3C, 0x01, 0x72, 0x1C, 0x83, 0xEB, 0x3A, @@ -55,14 +59,70 @@ pub(crate) const MBR_TEMPLATE: [u8; SECTOR_SIZE as usize] = [ 0x68, 0x65, 0x72, 0x20, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x2E, 0xFF, 0x0D, 0x0A, 0x44, 0x69, 0x73, 0x6B, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 0xFF, 0x0D, 0x0A, 0x50, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6E, 0x79, 0x20, 0x6B, 0x65, 0x79, 0x20, 0x74, 0x6F, 0x20, 0x72, 0x65, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAC, 0xCB, 0xD8, 0x55, 0xAA + 0x72, 0x74, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAC, 0xCB, 0xD8, 0x55, 0xAA, +]; + +pub(crate) const FAT_TABLE: [u8; 0x08] = [0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F]; +pub(crate) const TABLE_OFFSET_FAT1: usize = 0xC00; +pub(crate) const TABLE_OFFSET_FAT2: usize = 0xE00; + +#[rustfmt::skip] // keep this in 16-byte width +pub (crate) const ROOT_DIR: [u8; 0x80] = [ + 0x42, 0x41, 0x4F, 0x53, 0x4F, 0x52, 0x20, 0x20, 0x20, 0x20, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0x78, 0x74, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x20, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x0F, 0x00, 0x72, 0x72, 0x00, + 0x6D, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, + 0x01, 0x53, 0x00, 0x79, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x0F, 0x00, 0x72, 0x6D, 0x00, + 0x20, 0x00, 0x56, 0x00, 0x6F, 0x00, 0x6C, 0x00, 0x75, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x65, 0x00, + 0x53, 0x59, 0x53, 0x54, 0x45, 0x4D, 0x7E, 0x31, 0x20, 0x20, 0x20, 0x16, 0x00, 0x36, 0xCF, 0x78, + 0x74, 0x59, 0x74, 0x59, 0x00, 0x00, 0xD0, 0x78, 0x74, 0x59, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, +]; +pub(crate) const ROOT_DIR_OFFSET: usize = 0x1000; + +#[rustfmt::skip] // keep this in 16-byte width +pub (crate) const FAT_DATA1: [u8; 0x100] = [ + 0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x00, 0x36, 0xCF, 0x78, + 0x74, 0x59, 0x74, 0x59, 0x00, 0x00, 0xD0, 0x78, 0x74, 0x59, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2E, 0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x00, 0x36, 0xCF, 0x78, + 0x74, 0x59, 0x74, 0x59, 0x00, 0x00, 0xD0, 0x78, 0x74, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x47, 0x00, 0x75, 0x00, 0x69, 0x00, 0x64, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0x01, 0x49, 0x00, 0x6E, 0x00, 0x64, 0x00, 0x65, 0x00, 0x78, 0x00, 0x0F, 0x00, 0xFF, 0x65, 0x00, + 0x72, 0x00, 0x56, 0x00, 0x6F, 0x00, 0x6C, 0x00, 0x75, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x65, 0x00, + 0x49, 0x4E, 0x44, 0x45, 0x58, 0x45, 0x7E, 0x31, 0x20, 0x20, 0x20, 0x20, 0x00, 0x4E, 0xCF, 0x78, + 0x74, 0x59, 0x74, 0x59, 0x00, 0x00, 0xD0, 0x78, 0x74, 0x59, 0x03, 0x00, 0x4C, 0x00, 0x00, 0x00, + 0x42, 0x74, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0xCE, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0x01, 0x57, 0x00, 0x50, 0x00, 0x53, 0x00, 0x65, 0x00, 0x74, 0x00, 0x0F, 0x00, 0xCE, 0x74, 0x00, + 0x69, 0x00, 0x6E, 0x00, 0x67, 0x00, 0x73, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x61, 0x00, + 0x57, 0x50, 0x53, 0x45, 0x54, 0x54, 0x7E, 0x31, 0x44, 0x41, 0x54, 0x20, 0x00, 0xB5, 0xD8, 0x78, + 0x74, 0x59, 0x74, 0x59, 0x00, 0x00, 0xD9, 0x78, 0x74, 0x59, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, +]; +pub(crate) const FAT_DATA1_OFFSET: usize = 0x5000; + +#[rustfmt::skip] // keep this in 16-byte width +pub(crate) const FAT_DATA2: [u8; 0x50] = [ + 0x7B, 0x00, 0x34, 0x00, 0x33, 0x00, 0x39, 0x00, 0x44, 0x00, 0x41, 0x00, 0x31, 0x00, 0x32, 0x00, + 0x44, 0x00, 0x2D, 0x00, 0x37, 0x00, 0x33, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2D, 0x00, 0x34, 0x00, + 0x43, 0x00, 0x41, 0x00, 0x34, 0x00, 0x2D, 0x00, 0x38, 0x00, 0x38, 0x00, 0x46, 0x00, 0x39, 0x00, + 0x2D, 0x00, 0x44, 0x00, 0x41, 0x00, 0x32, 0x00, 0x43, 0x00, 0x46, 0x00, 0x33, 0x00, 0x31, 0x00, + 0x38, 0x00, 0x45, 0x00, 0x32, 0x00, 0x37, 0x00, 0x35, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, +]; +pub(crate) const FAT_DATA2_OFFSET: usize = 0x9000; + +#[rustfmt::skip] // keep this in 16-byte width +pub(crate) const FAT_DATA3: [u8; 0x10] = [ + 0x0C, 0x00, 0x00, 0x00, 0x28, 0x34, 0x07, 0x9C, 0x7C, 0xBD, 0x65, 0xF9, 0x00, 0x00, 0x00, 0x00, ]; +pub(crate) const FAT_DATA3_OFFSET: usize = 0xD000; pub(crate) const CSW_ADDR: usize = CRG_UDC_MEMBASE + CRG_UDC_APP_BUFOFFSET + CRG_UDC_APP_BUF_LEN; pub(crate) const CBW_ADDR: usize = CRG_UDC_MEMBASE + CRG_UDC_APP_BUFOFFSET; pub(crate) const EP1_IN_BUF: usize = CRG_UDC_MEMBASE + CRG_UDC_APP_BUFOFFSET + 1024; pub(crate) const EP1_IN_BUF_LEN: usize = 1024; +#[allow(dead_code)] pub(crate) const EP1_OUT_BUF: usize = CRG_UDC_MEMBASE + CRG_UDC_APP_BUFOFFSET + 2048; +#[allow(dead_code)] pub(crate) const EP1_OUT_BUF_LEN: usize = 1024; pub(crate) const MASS_STORAGE_EPADDR_IN: u8 = 0x81; @@ -225,7 +285,7 @@ pub fn usb_ep1_bulk_out_complete(this: &mut CorigineUsb, buf_addr: usize, info: } pub fn usb_ep1_bulk_in_complete(this: &mut CorigineUsb, _buf_addr: usize, info: u32, _error: u8) { - crate::println!("bulk IN handler"); + // crate::println!("bulk IN handler"); let length = info & 0xFFFF; if UmsState::DataPhase == this.ms_state { //DATA @@ -321,7 +381,7 @@ fn process_request_sense(this: &mut CorigineUsb, cbw: Cbw) { csw.residue = 0; csw.status = 0; csw.send(this); - crate::println!("UMS_STATE_STATUS_PHASE\r\n"); + // crate::println!("UMS_STATE_STATUS_PHASE\r\n"); } else if cbw.flags & 0x80 != 0 { if cbw.data_transfer_length < 18 { let ep1_in = unsafe { core::slice::from_raw_parts_mut(EP1_IN_BUF as *mut u8, EP1_IN_BUF_LEN) }; @@ -505,7 +565,6 @@ fn process_write_command(this: &mut CorigineUsb, cbw: Cbw) { length |= cbw.cdb[8] as u32; length *= SECTOR_SIZE as u32; - crate::println!("write of {} bytes", length); if cbw.flags & 0x80 != 0 { csw.residue = cbw.data_transfer_length; From 9a839e3fe694f57720bc30ceb13a92e6ffd9515c Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 20:40:48 +0800 Subject: [PATCH 17/42] add keypress handling and no_std Cursor implementation --- loader/src/platform/cramium/update.rs | 224 +++++++++++++++++++++++--- 1 file changed, 200 insertions(+), 24 deletions(-) diff --git a/loader/src/platform/cramium/update.rs b/loader/src/platform/cramium/update.rs index 1787da2d4..ddad83f16 100644 --- a/loader/src/platform/cramium/update.rs +++ b/loader/src/platform/cramium/update.rs @@ -1,17 +1,36 @@ +use core::fmt::Display; + use cramium_hal::iox::{IoGpio, IoSetup, Iox, IoxPort, IoxValue}; use cramium_hal::minigfx::{FrameBuffer, Point}; use cramium_hal::sh1107::Mono; use cramium_hal::udma; +use simple_fatfs::io::{IOBase, Read, Seek, SeekFrom, Write}; +use simple_fatfs::{IOError, IOErrorKind, PathBuf}; use utralib::generated::*; use crate::platform::cramium::gfx; +use crate::platform::cramium::usb; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum KeyPress { + Up, + Down, + Left, + Right, + Select, + Start, + A, + B, + Invalid, + None, +} pub fn scan_keyboard( iox: &T, rows: &[(IoxPort, u8)], cols: &[(IoxPort, u8)], -) -> [Option<(u8, u8)>; 4] { - let mut key_presses: [Option<(u8, u8)>; 4] = [None; 4]; +) -> [KeyPress; 4] { + let mut key_presses: [KeyPress; 4] = [KeyPress::None; 4]; let mut key_press_index = 0; // no Vec in no_std, so we have to manually track it for (row, (port, pin)) in rows.iter().enumerate() { @@ -19,7 +38,17 @@ pub fn scan_keyboard( for (col, (col_port, col_pin)) in cols.iter().enumerate() { if iox.get_gpio_pin_value(*col_port, *col_pin) == IoxValue::Low { if key_press_index < key_presses.len() { - key_presses[key_press_index] = Some((row as u8, col as u8)); + key_presses[key_press_index] = match (row, col) { + (0, 2) => KeyPress::Select, + (2, 1) => KeyPress::Start, + (1, 2) => KeyPress::Left, + (1, 1) => KeyPress::Up, + (0, 1) => KeyPress::Down, + (2, 0) => KeyPress::Right, + (0, 0) => KeyPress::A, + (1, 0) => KeyPress::B, + _ => KeyPress::Invalid, + }; key_press_index += 1; } } @@ -56,35 +85,182 @@ pub fn process_update(perclk: u32) { while !key_pressed { let kps = scan_keyboard(&iox, &rows, &cols); for kp in kps { - match kp { - // SELECT - Some((0, 2)) => { - crate::println!("SELECT detected"); - do_update = true; - key_pressed = true; + if kp != KeyPress::None { + crate::println!("Got key: {:?}", kp); + key_pressed = true; + } + if kp == KeyPress::Select { + do_update = true; + } + } + } + if do_update { + crate::platform::cramium::usb::init_usb(); + // it's all unsafe because USB is global mutable state + unsafe { + if let Some(ref mut usb_ref) = crate::platform::cramium::usb::USB { + let usb = &mut *core::ptr::addr_of_mut!(*usb_ref); + usb.reset(); + let mut poweron = 0; + loop { + usb.udc_handle_interrupt(); + if usb.pp() { + poweron += 1; // .pp() is a sham. MPW has no way to tell if power is applied. This needs to be fixed for NTO. + } + crate::platform::delay(100); + if poweron >= 4 { + break; + } } - // START - Some((2, 1)) => { - crate::println!("START detected"); - key_pressed = true; + usb.reset(); + usb.init(); + usb.start(); + usb.update_current_speed(); + + crate::println!("hw started..."); + + loop { + let kps = scan_keyboard(&iox, &rows, &cols); + // only consider the first key returned in case of multi-key hit, for simplicity + if kps[0] == KeyPress::Select { + break; + } else if kps[0] != KeyPress::None { + crate::println!("Got key {:?}; ignoring", kps[0]); + } } - Some((r, c)) => { - crate::println!("{},{} pressed", r, c); - // this causes the system to boot if *any* key is pressed. - key_pressed = true; + + let disk = usb::conjure_disk(); + let mut cursor = SliceCursor::new(disk); + + // We can either pass by value of by (mutable) reference + let mut fs = simple_fatfs::FileSystem::from_storage(&mut cursor).unwrap(); + match fs.read_dir(PathBuf::from("/")) { + Ok(dir) => { + for entry in dir { + crate::println!("file: {:?}", entry); + } + } + Err(e) => { + crate::println!("Couldn't list dir: {:?}", e); + } } - None => (), + } else { + crate::println!("USB core not allocated, can't do update!"); } } } - if do_update { - update(); +} + +pub struct SliceCursor<'a> { + slice: &'a mut [u8], + pos: u64, +} + +impl<'a> SliceCursor<'a> { + pub fn new(slice: &'a mut [u8]) -> Self { Self { slice, pos: 0 } } +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum IOErrorFatfs { + UnexpectedEof, + Interrupted, + InvalidData, + Description, + SeekUnderflow, + SeekOverflow, + SeekOutOfBounds, +} + +impl Display for IOErrorFatfs { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + IOErrorFatfs::UnexpectedEof => write!(f, "Unexpected EOF"), + IOErrorFatfs::Interrupted => write!(f, "Interrupted"), + IOErrorFatfs::InvalidData => write!(f, "Invalid Data"), + IOErrorFatfs::Description => write!(f, "Unsupported string description"), + IOErrorFatfs::SeekOutOfBounds => write!(f, "Seek out of bounds"), + IOErrorFatfs::SeekOverflow => write!(f, "Seek overflow"), + IOErrorFatfs::SeekUnderflow => write!(f, "Seek underflow"), + } } } -fn update() { - crate::platform::cramium::usb::init_usb(); - unsafe { - crate::platform::cramium::usb::test_usb(); +impl From<&str> for IOErrorFatfs { + fn from(_value: &str) -> Self { IOErrorFatfs::Description } +} +impl IOErrorKind for IOErrorFatfs { + fn new_unexpected_eof() -> Self { Self::UnexpectedEof } + + fn new_invalid_data() -> Self { Self::InvalidData } + + fn new_interrupted() -> Self { Self::Interrupted } +} +impl IOError for IOErrorFatfs { + type Kind = IOErrorFatfs; + + fn new(kind: Self::Kind, _msg: M) -> Self + where + M: core::fmt::Display, + { + kind + } + + fn kind(&self) -> Self::Kind { *self } +} + +impl simple_fatfs::Error for IOErrorFatfs {} + +impl IOBase for SliceCursor<'_> { + type Error = IOErrorFatfs; +} + +impl Seek for SliceCursor<'_> { + fn seek(&mut self, seek_from: simple_fatfs::io::SeekFrom) -> Result { + let new_pos = match seek_from { + SeekFrom::Start(offset) => offset, + SeekFrom::End(offset) => { + let result = if offset < 0 { + self.slice.len().checked_sub((-offset) as usize).ok_or(IOErrorFatfs::SeekUnderflow)? + } else { + self.slice.len().checked_add(offset as usize).ok_or(IOErrorFatfs::SeekOverflow)? + }; + result as u64 + } + SeekFrom::Current(offset) => { + if offset < 0 { + self.pos.checked_sub((-offset) as u64).ok_or(IOErrorFatfs::SeekUnderflow)? + } else { + self.pos.checked_add(offset as u64).ok_or(IOErrorFatfs::SeekUnderflow)? + } + } + }; + + if new_pos > self.slice.len() as u64 { + Err(IOErrorFatfs::SeekOutOfBounds) + } else { + self.pos = new_pos; + Ok(self.pos) + } } } + +impl Read for SliceCursor<'_> { + fn read(&mut self, buf: &mut [u8]) -> Result { + let available = &self.slice[self.pos as usize..]; + let to_read = buf.len().min(available.len()); + buf[..to_read].copy_from_slice(&available[..to_read]); + self.pos += to_read as u64; + Ok(to_read) + } +} +impl Write for SliceCursor<'_> { + fn write(&mut self, buf: &[u8]) -> Result { + let available = &mut self.slice[self.pos as usize..]; + let to_write = buf.len().min(available.len()); + available[..to_write].copy_from_slice(&buf[..to_write]); + self.pos += to_write as u64; + Ok(to_write) + } + + fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } +} From 6d3a58ccea2d9afdf4978a2f7aa9a5840bcf189b Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 20:41:10 +0800 Subject: [PATCH 18/42] cleanup warnings --- loader/src/platform/cramium/cramium.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/loader/src/platform/cramium/cramium.rs b/loader/src/platform/cramium/cramium.rs index b792b1b15..c33c199fa 100644 --- a/loader/src/platform/cramium/cramium.rs +++ b/loader/src/platform/cramium/cramium.rs @@ -75,7 +75,7 @@ pub const FLASH_BASE: usize = utralib::generated::HW_RERAM_MEM; // exclusive of the signature block offset pub const KERNEL_OFFSET: usize = 0x5_0000; -fn delay(quantum: usize) { +pub(crate) fn delay(quantum: usize) { use utralib::{CSR, utra}; // abuse the d11ctime timer to create some time-out like thing let mut d11c = CSR::new(utra::d11ctime::HW_D11CTIME_BASE as *mut u32); @@ -348,7 +348,7 @@ pub fn early_init() -> u32 { let iox_loop = iox.clone(); // show the boot logo - use cramium_hal::{ifram, minigfx::FrameBuffer}; + use cramium_hal::minigfx::FrameBuffer; let mut sh1107 = cramium_hal::sh1107::Oled128x128::new(perclk, &mut iox, &mut udma_global); sh1107.init(); From fded456115399b8259e44e22fcd529436cd57d46 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 20:41:26 +0800 Subject: [PATCH 19/42] commit dependency impact of simple-fatfs --- Cargo.lock | 61 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1980c3fd..7fd35e643 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,7 +271,7 @@ version = "0.1.0" source = "git+https://github.com/Foundation-Devices/atsama5d27.git?branch=master#9e83a502e68384754bb328a5717c56f34c8618f7" dependencies = [ "armv7", - "bitflags 2.4.2", + "bitflags 2.6.0", "embedded-graphics", "embedded-hal", "png-decoder", @@ -475,6 +475,17 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" +[[package]] +name = "bitfield-struct" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de05f8756f1c68937349406d4632ae96ae35901019b5e59c508d9c38c64715fb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -483,9 +494,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitmask" @@ -1528,9 +1539,9 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", @@ -2913,7 +2924,7 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "libc", "redox_syscall", ] @@ -2924,7 +2935,7 @@ version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "libc", "redox_syscall", ] @@ -2946,6 +2957,15 @@ dependencies = [ "xous-api-ticktimer", ] +[[package]] +name = "linked_list_allocator" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" +dependencies = [ + "spinning_top", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -2985,14 +3005,15 @@ dependencies = [ "crc 1.8.1", "curve25519-dalek-loader", "ed25519-dalek-loader", - "ghostfat", "lazy_static", "libm 0.2.8", + "linked_list_allocator", "nalgebra", "pio", "pio-proc", "rand_chacha 0.3.1", "sha2-loader", + "simple-fatfs", "utralib 0.1.25", "xous-pio", "xous-pl230", @@ -4608,7 +4629,7 @@ version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -4987,6 +5008,18 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simple-fatfs" +version = "0.1.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61006ceee80c793fc9d5d619cc6ea624c5d509916c21a1b10625655dbd037d2" +dependencies = [ + "bitfield-struct", + "bitflags 2.6.0", + "displaydoc", + "time", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -5051,6 +5084,15 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spinning_top" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0" +dependencies = [ + "lock_api", +] + [[package]] name = "spinor" version = "0.1.0" @@ -5751,6 +5793,7 @@ version = "0.1.0" dependencies = [ "cram-hal-service", "cramium-hal", + "locales", "log", "num-derive 0.3.3", "num-traits", From b68abf1d2bd2e2b286c356d30b2bcf5fb76ded28 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 20:43:39 +0800 Subject: [PATCH 20/42] change volume label --- loader/src/platform/cramium/usb/mass_storage.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/loader/src/platform/cramium/usb/mass_storage.rs b/loader/src/platform/cramium/usb/mass_storage.rs index b6cd68c8a..10e867344 100644 --- a/loader/src/platform/cramium/usb/mass_storage.rs +++ b/loader/src/platform/cramium/usb/mass_storage.rs @@ -26,7 +26,6 @@ pub(crate) const HEAP_LEN: usize = 128 * 1024; // 0x0b~0x0C 2 bytes means block size, default 0x200 bytes // 0x20~0x23 4 bytes means block number, default 0x400 block #[rustfmt::skip] // keep this in 16-byte width -// TODO: change volume label from BAOSOR to BAOSEC pub(crate) const MBR_TEMPLATE: [u8; SECTOR_SIZE as usize] = [ 0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x20, 0x06, 0x00, 0x02, 0x00, 0x02, 0x00, 0x0C, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -68,7 +67,7 @@ pub(crate) const TABLE_OFFSET_FAT2: usize = 0xE00; #[rustfmt::skip] // keep this in 16-byte width pub (crate) const ROOT_DIR: [u8; 0x80] = [ - 0x42, 0x41, 0x4F, 0x53, 0x4F, 0x52, 0x20, 0x20, 0x20, 0x20, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x41, 0x4F, 0x53, 0x45, 0x43, 0x20, 0x20, 0x20, 0x20, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0x78, 0x74, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x20, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x0F, 0x00, 0x72, 0x72, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, From 3b567fb3e2d2abbde73aa2e282040bc6d4abc0d7 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 21:40:25 +0800 Subject: [PATCH 21/42] modularize SliceCursor --- .../src/platform/cramium/usb/slice_cursor.rs | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 loader/src/platform/cramium/usb/slice_cursor.rs diff --git a/loader/src/platform/cramium/usb/slice_cursor.rs b/loader/src/platform/cramium/usb/slice_cursor.rs new file mode 100644 index 000000000..e01fd7b2f --- /dev/null +++ b/loader/src/platform/cramium/usb/slice_cursor.rs @@ -0,0 +1,117 @@ +use core::fmt::Display; + +use simple_fatfs::io::{IOBase, Read, Seek, SeekFrom, Write}; +use simple_fatfs::{IOError, IOErrorKind}; + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum IOErrorFatfs { + UnexpectedEof, + Interrupted, + InvalidData, + Description, + SeekUnderflow, + SeekOverflow, + SeekOutOfBounds, +} + +impl Display for IOErrorFatfs { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + IOErrorFatfs::UnexpectedEof => write!(f, "Unexpected EOF"), + IOErrorFatfs::Interrupted => write!(f, "Interrupted"), + IOErrorFatfs::InvalidData => write!(f, "Invalid Data"), + IOErrorFatfs::Description => write!(f, "Unsupported string description"), + IOErrorFatfs::SeekOutOfBounds => write!(f, "Seek out of bounds"), + IOErrorFatfs::SeekOverflow => write!(f, "Seek overflow"), + IOErrorFatfs::SeekUnderflow => write!(f, "Seek underflow"), + } + } +} + +impl From<&str> for IOErrorFatfs { + fn from(_value: &str) -> Self { IOErrorFatfs::Description } +} +impl IOErrorKind for IOErrorFatfs { + fn new_unexpected_eof() -> Self { Self::UnexpectedEof } + + fn new_invalid_data() -> Self { Self::InvalidData } + + fn new_interrupted() -> Self { Self::Interrupted } +} +impl IOError for IOErrorFatfs { + type Kind = IOErrorFatfs; + + fn new(kind: Self::Kind, _msg: M) -> Self + where + M: core::fmt::Display, + { + kind + } + + fn kind(&self) -> Self::Kind { *self } +} + +impl simple_fatfs::Error for IOErrorFatfs {} + +impl IOBase for SliceCursor<'_> { + type Error = IOErrorFatfs; +} +pub struct SliceCursor<'a> { + slice: &'a mut [u8], + pos: u64, +} + +impl<'a> SliceCursor<'a> { + pub fn new(slice: &'a mut [u8]) -> Self { Self { slice, pos: 0 } } +} + +impl Seek for SliceCursor<'_> { + fn seek(&mut self, seek_from: simple_fatfs::io::SeekFrom) -> Result { + let new_pos = match seek_from { + SeekFrom::Start(offset) => offset, + SeekFrom::End(offset) => { + let result = if offset < 0 { + self.slice.len().checked_sub((-offset) as usize).ok_or(IOErrorFatfs::SeekUnderflow)? + } else { + self.slice.len().checked_add(offset as usize).ok_or(IOErrorFatfs::SeekOverflow)? + }; + result as u64 + } + SeekFrom::Current(offset) => { + if offset < 0 { + self.pos.checked_sub((-offset) as u64).ok_or(IOErrorFatfs::SeekUnderflow)? + } else { + self.pos.checked_add(offset as u64).ok_or(IOErrorFatfs::SeekUnderflow)? + } + } + }; + + if new_pos > self.slice.len() as u64 { + Err(IOErrorFatfs::SeekOutOfBounds) + } else { + self.pos = new_pos; + Ok(self.pos) + } + } +} + +impl Read for SliceCursor<'_> { + fn read(&mut self, buf: &mut [u8]) -> Result { + let available = &self.slice[self.pos as usize..]; + let to_read = buf.len().min(available.len()); + buf[..to_read].copy_from_slice(&available[..to_read]); + self.pos += to_read as u64; + Ok(to_read) + } +} +impl Write for SliceCursor<'_> { + fn write(&mut self, buf: &[u8]) -> Result { + let available = &mut self.slice[self.pos as usize..]; + let to_write = buf.len().min(available.len()); + available[..to_write].copy_from_slice(&buf[..to_write]); + self.pos += to_write as u64; + Ok(to_write) + } + + fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } +} From 60dcd906ed7fe1ea41fed2f78b8a3520e01a3601 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 21:40:50 +0800 Subject: [PATCH 22/42] include slice_cursor in mod list --- loader/src/platform/cramium/usb/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/loader/src/platform/cramium/usb/mod.rs b/loader/src/platform/cramium/usb/mod.rs index 55055a32e..6d2bbf34e 100644 --- a/loader/src/platform/cramium/usb/mod.rs +++ b/loader/src/platform/cramium/usb/mod.rs @@ -1,10 +1,12 @@ mod driver; mod irq; mod mass_storage; +mod slice_cursor; pub use driver::*; pub use irq::*; pub use mass_storage::*; +pub use slice_cursor::*; // Note that the trap handler is just placed one page below this, and it // needs to be manually updated in the assembly because we can't refer to From 8b571561f8f2f6207a32b8c5c5fc9153668ef38a Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 21:41:08 +0800 Subject: [PATCH 23/42] cleanup warning --- loader/src/platform/cramium/usb/irq.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/loader/src/platform/cramium/usb/irq.rs b/loader/src/platform/cramium/usb/irq.rs index 70ddb3e4f..c95a78c3c 100644 --- a/loader/src/platform/cramium/usb/irq.rs +++ b/loader/src/platform/cramium/usb/irq.rs @@ -190,7 +190,7 @@ pub extern "C" fn trap_handler( let mut ret = cramium_hal::usb::driver::CrgEvent::None; let status = usb.csr.r(USBSTS); // self.print_status(status); - let result = if (status & usb.csr.ms(USBSTS_SYSTEM_ERR, 1)) != 0 { + let _result = if (status & usb.csr.ms(USBSTS_SYSTEM_ERR, 1)) != 0 { crate::println!("System error"); usb.csr.wfo(USBSTS_SYSTEM_ERR, 1); crate::println!("USBCMD: {:x}", usb.csr.r(USBCMD)); @@ -203,7 +203,7 @@ pub extern "C" fn trap_handler( } ret }; - crate::println!("Result: {:?}", result); + // crate::println!("Result: {:?}", _result); } if usb.csr.rf(IMAN_IE) != 0 { usb.csr.wo(IMAN, usb.csr.ms(IMAN_IE, 1) | usb.csr.ms(IMAN_IP, 1)); From 9c1186022f6b179619ceddba28c33e579c574548 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 21:41:23 +0800 Subject: [PATCH 24/42] quiet debug --- loader/src/platform/cramium/usb/driver.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loader/src/platform/cramium/usb/driver.rs b/loader/src/platform/cramium/usb/driver.rs index 3f94f26a6..fffac7254 100644 --- a/loader/src/platform/cramium/usb/driver.rs +++ b/loader/src/platform/cramium/usb/driver.rs @@ -248,7 +248,7 @@ fn handle_event(this: &mut CorigineUsb, event_trb: &mut EventTrbS) -> CrgEvent { } } else if pei >= 2 { if comp_code == CompletionCode::Success || comp_code == CompletionCode::ShortPacket { - crate::println!("EP{} xfer event, dir {}", ep_num, if dir { "OUT" } else { "IN" }); + // crate::println!("EP{} xfer event, dir {}", ep_num, if dir { "OUT" } else { "IN" }); // xfer_complete if let Some(f) = this.udc_ep[pei as usize].completion_handler { // so unsafe. so unsafe. We're counting on the hardware to hand us a raw pointer From ed085f2c8320b14fab87951929c6b0bb8d2eb798 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 21:41:41 +0800 Subject: [PATCH 25/42] UX improvements for loader update interface --- loader/src/platform/cramium/update.rs | 191 ++++++++++---------------- 1 file changed, 69 insertions(+), 122 deletions(-) diff --git a/loader/src/platform/cramium/update.rs b/loader/src/platform/cramium/update.rs index ddad83f16..33b96c123 100644 --- a/loader/src/platform/cramium/update.rs +++ b/loader/src/platform/cramium/update.rs @@ -1,15 +1,22 @@ -use core::fmt::Display; - use cramium_hal::iox::{IoGpio, IoSetup, Iox, IoxPort, IoxValue}; use cramium_hal::minigfx::{FrameBuffer, Point}; use cramium_hal::sh1107::Mono; use cramium_hal::udma; -use simple_fatfs::io::{IOBase, Read, Seek, SeekFrom, Write}; -use simple_fatfs::{IOError, IOErrorKind, PathBuf}; +use cramium_hal::usb::driver::UsbDeviceState; +use simple_fatfs::PathBuf; use utralib::generated::*; use crate::platform::cramium::gfx; use crate::platform::cramium::usb; +use crate::platform::cramium::usb::SliceCursor; + +// TODO: +// - Port unicode font drawing into loader +// - Support localization + +// Empirically measured PORTSC when the port is unplugged. This might be a brittle way +// to detect if the device is unplugged. +const DISCONNECT_STATE: u32 = 0x40b; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum KeyPress { @@ -69,6 +76,7 @@ pub fn process_update(perclk: u32) { let mut iox = Iox::new(utra::iox::HW_IOX_BASE as *mut u32); let mut udma_global = udma::GlobalConfig::new(utra::udma_ctrl::HW_UDMA_CTRL_BASE as *mut u32); + let iox_kbd = iox.clone(); let mut sh1107 = cramium_hal::sh1107::Oled128x128::new(perclk, &mut iox, &mut udma_global); gfx::msg(&mut sh1107, " START to boot", Point::new(0, 16), Mono::White.into(), Mono::Black.into()); @@ -78,12 +86,12 @@ pub fn process_update(perclk: u32) { sh1107.draw(); // setup IO pins to check for update viability - let (rows, cols) = cramium_hal::board::baosec::setup_kb_pins(&iox); + let (rows, cols) = cramium_hal::board::baosec::setup_kb_pins(&iox_kbd); let mut key_pressed = false; let mut do_update = false; while !key_pressed { - let kps = scan_keyboard(&iox, &rows, &cols); + let kps = scan_keyboard(&iox_kbd, &rows, &cols); for kp in kps { if kp != KeyPress::None { crate::println!("Got key: {:?}", kp); @@ -94,7 +102,14 @@ pub fn process_update(perclk: u32) { } } } + + sh1107.clear(); + if do_update { + gfx::msg(&mut sh1107, "Connect to USB", Point::new(16, 64), Mono::White.into(), Mono::Black.into()); + sh1107.buffer_swap(); + sh1107.draw(); + crate::platform::cramium::usb::init_usb(); // it's all unsafe because USB is global mutable state unsafe { @@ -117,16 +132,57 @@ pub fn process_update(perclk: u32) { usb.start(); usb.update_current_speed(); - crate::println!("hw started..."); - + let mut last_usb_state = usb.get_device_state(); + let mut portsc = usb.portsc_val(); + crate::println!("USB state: {:?}, {:x}", last_usb_state, portsc); loop { - let kps = scan_keyboard(&iox, &rows, &cols); + let kps = scan_keyboard(&iox_kbd, &rows, &cols); // only consider the first key returned in case of multi-key hit, for simplicity if kps[0] == KeyPress::Select { break; } else if kps[0] != KeyPress::None { crate::println!("Got key {:?}; ignoring", kps[0]); } + let new_usb_state = usb.get_device_state(); + let new_portsc = usb.portsc_val(); + // alternately, break out of the loop when USB is disconnected + if new_portsc != portsc { + crate::println!("PP: {:x}", portsc); + portsc = new_portsc; + if portsc == DISCONNECT_STATE && new_usb_state == UsbDeviceState::Configured { + break; + } + } + if new_usb_state != last_usb_state { + crate::println!("USB state: {:?}", new_usb_state); + if new_usb_state == UsbDeviceState::Configured { + sh1107.clear(); + gfx::msg( + &mut sh1107, + "Copy files to device", + Point::new(6, 64), + Mono::White.into(), + Mono::Black.into(), + ); + gfx::msg( + &mut sh1107, + "Press SELECT", + Point::new(22, 46), + Mono::Black.into(), + Mono::White.into(), + ); + gfx::msg( + &mut sh1107, + "when finished!", + Point::new(19, 32), + Mono::Black.into(), + Mono::White.into(), + ); + sh1107.buffer_swap(); + sh1107.draw(); + last_usb_state = new_usb_state; + } + } } let disk = usb::conjure_disk(); @@ -149,118 +205,9 @@ pub fn process_update(perclk: u32) { } } } -} - -pub struct SliceCursor<'a> { - slice: &'a mut [u8], - pos: u64, -} - -impl<'a> SliceCursor<'a> { - pub fn new(slice: &'a mut [u8]) -> Self { Self { slice, pos: 0 } } -} - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub enum IOErrorFatfs { - UnexpectedEof, - Interrupted, - InvalidData, - Description, - SeekUnderflow, - SeekOverflow, - SeekOutOfBounds, -} - -impl Display for IOErrorFatfs { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - IOErrorFatfs::UnexpectedEof => write!(f, "Unexpected EOF"), - IOErrorFatfs::Interrupted => write!(f, "Interrupted"), - IOErrorFatfs::InvalidData => write!(f, "Invalid Data"), - IOErrorFatfs::Description => write!(f, "Unsupported string description"), - IOErrorFatfs::SeekOutOfBounds => write!(f, "Seek out of bounds"), - IOErrorFatfs::SeekOverflow => write!(f, "Seek overflow"), - IOErrorFatfs::SeekUnderflow => write!(f, "Seek underflow"), - } - } -} - -impl From<&str> for IOErrorFatfs { - fn from(_value: &str) -> Self { IOErrorFatfs::Description } -} -impl IOErrorKind for IOErrorFatfs { - fn new_unexpected_eof() -> Self { Self::UnexpectedEof } - - fn new_invalid_data() -> Self { Self::InvalidData } - - fn new_interrupted() -> Self { Self::Interrupted } -} -impl IOError for IOErrorFatfs { - type Kind = IOErrorFatfs; - - fn new(kind: Self::Kind, _msg: M) -> Self - where - M: core::fmt::Display, - { - kind - } - - fn kind(&self) -> Self::Kind { *self } -} - -impl simple_fatfs::Error for IOErrorFatfs {} - -impl IOBase for SliceCursor<'_> { - type Error = IOErrorFatfs; -} - -impl Seek for SliceCursor<'_> { - fn seek(&mut self, seek_from: simple_fatfs::io::SeekFrom) -> Result { - let new_pos = match seek_from { - SeekFrom::Start(offset) => offset, - SeekFrom::End(offset) => { - let result = if offset < 0 { - self.slice.len().checked_sub((-offset) as usize).ok_or(IOErrorFatfs::SeekUnderflow)? - } else { - self.slice.len().checked_add(offset as usize).ok_or(IOErrorFatfs::SeekOverflow)? - }; - result as u64 - } - SeekFrom::Current(offset) => { - if offset < 0 { - self.pos.checked_sub((-offset) as u64).ok_or(IOErrorFatfs::SeekUnderflow)? - } else { - self.pos.checked_add(offset as u64).ok_or(IOErrorFatfs::SeekUnderflow)? - } - } - }; - - if new_pos > self.slice.len() as u64 { - Err(IOErrorFatfs::SeekOutOfBounds) - } else { - self.pos = new_pos; - Ok(self.pos) - } - } -} - -impl Read for SliceCursor<'_> { - fn read(&mut self, buf: &mut [u8]) -> Result { - let available = &self.slice[self.pos as usize..]; - let to_read = buf.len().min(available.len()); - buf[..to_read].copy_from_slice(&available[..to_read]); - self.pos += to_read as u64; - Ok(to_read) - } -} -impl Write for SliceCursor<'_> { - fn write(&mut self, buf: &[u8]) -> Result { - let available = &mut self.slice[self.pos as usize..]; - let to_write = buf.len().min(available.len()); - available[..to_write].copy_from_slice(&buf[..to_write]); - self.pos += to_write as u64; - Ok(to_write) - } - fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } + gfx::msg(&mut sh1107, " Booting Xous...", Point::new(0, 64), Mono::White.into(), Mono::Black.into()); + sh1107.buffer_swap(); + sh1107.draw(); + sh1107.clear(); } From 996b5744864253895337c8d70c1aba5f487ef532 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 20 Nov 2024 21:42:02 +0800 Subject: [PATCH 26/42] quiet down debug; add a method to expatriate PORTSC --- libs/cramium-hal/src/usb/driver.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/cramium-hal/src/usb/driver.rs b/libs/cramium-hal/src/usb/driver.rs index 7cd8aff8a..7512ae8c6 100644 --- a/libs/cramium-hal/src/usb/driver.rs +++ b/libs/cramium-hal/src/usb/driver.rs @@ -1553,6 +1553,8 @@ impl CorigineUsb { pub fn pp(&self) -> bool { self.csr.rf(PORTSC_PP) != 0 } + pub fn portsc_val(&self) -> u32 { self.csr.r(PORTSC) } + pub fn stop(&mut self) { self.csr.rmwf(USBCMD_INT_ENABLE, 0); self.csr.rmwf(USBCMD_RUN_STOP, 0); @@ -2040,6 +2042,7 @@ impl CorigineUsb { unsafe { udc_ep.enq_pt.load(Ordering::SeqCst).as_mut().expect("couldn't deref pointer") }; let mut pcs = udc_ep.pcs; + /* crate::println!( "PEI = {} bufaddr = 0x{:x} pcs = {} length = 0x{:x}, enq_pt = 0x{:x?}", pei, @@ -2048,6 +2051,7 @@ impl CorigineUsb { len, enq_pt, ); + */ for i in 0..num_trb { if num_trb == 1 { //only 1 trb From ec409fedc0ca77a4fafa271b04e6638de0295707 Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 21 Nov 2024 01:37:48 +0800 Subject: [PATCH 27/42] add formatting nit to allow for bodge to work around HW configs the curve 25519 accelerator hw is different on different targets so it can't be enabled for all of them all at once. This is going to be come a bigger problem as time goes on... --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c3aace679..d8fcdb766 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -153,7 +153,8 @@ path = "services/aes" [patch.crates-io.curve25519-dalek] git = "https://github.com/betrusted-io/curve25519-dalek.git" -branch = "main" + +branch = "main" # c25519 # path = "../curve25519-dalek/curve25519-dalek" # when doing local dev work # feature overrides are specified at the crate level From 25c30f6cdea3f24f0b2c92105ba15e832270f007 Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 21 Nov 2024 01:39:35 +0800 Subject: [PATCH 28/42] add bodge to work around HW configuration in patches right now our patch set assumes a single HW configuration for cryptography accelerators. This isn't going to be true going forward. A more scalable approach to patching accelerators is going to be needed. For now, we're just falling back to software acceleration for the cramium target. --- xtask/src/builder.rs | 20 ++++++++++++++++++++ xtask/src/main.rs | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/xtask/src/builder.rs b/xtask/src/builder.rs index 90bca2651..13f1bc2ad 100644 --- a/xtask/src/builder.rs +++ b/xtask/src/builder.rs @@ -332,6 +332,26 @@ impl Builder { self.kernel = CrateSpec::Local("xous-kernel".to_string(), LoaderRegion::Ram); search_and_replace_in_file("services/aes/Cargo.toml", "default = []", "default = [\"cramium-soc\"]") .expect("couldn't patch AES"); + + // this is needed because we don't have an ed25519 accelerator on cramium targets + search_and_replace_in_file( + "Cargo.toml", + "[patch.crates-io.curve25519-dalek]", + "# [patch.crates-io.curve25519-dalek]", + ) + .expect("couldn't patch curve25519"); + search_and_replace_in_file( + "Cargo.toml", + "git = \"https://github.com/betrusted-io/curve25519-dalek.git\"", + "# git = \"https://github.com/betrusted-io/curve25519-dalek.git\"", + ) + .expect("couldn't patch curve25519"); + search_and_replace_in_file( + "Cargo.toml", + "branch = \"main\" # c25519", + "# branch = \"main\" # c25519", + ) + .expect("couldn't patch curve25519"); self } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 2b6c8c7a8..e1ce85181 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -201,6 +201,26 @@ fn main() -> Result<(), Box> { ) .expect("couldn't patch AES"); + // revert these just in case - but don't throw an error if the strings aren't found + builder::search_and_replace_in_file( + "Cargo.toml", + "# [patch.crates-io.curve25519-dalek]", + "[patch.crates-io.curve25519-dalek]", + ) + .ok(); + builder::search_and_replace_in_file( + "Cargo.toml", + "# git = \"https://github.com/betrusted-io/curve25519-dalek.git\"", + "git = \"https://github.com/betrusted-io/curve25519-dalek.git\"", + ) + .ok(); + builder::search_and_replace_in_file( + "Cargo.toml", + "# branch = \"main\" # c25519", + "branch = \"main\" # c25519", + ) + .ok(); + match builder::search_in_file("services/aes/Cargo.toml", "default = []") { Ok(false) => { return Err( @@ -635,6 +655,24 @@ fn main() -> Result<(), Box> { "default = []", ) .expect("couldn't patch AES"); + builder::search_and_replace_in_file( + "Cargo.toml", + "# [patch.crates-io.curve25519-dalek]", + "[patch.crates-io.curve25519-dalek]", + ) + .expect("couldn't patch curve25519"); + builder::search_and_replace_in_file( + "Cargo.toml", + "# git = \"https://github.com/betrusted-io/curve25519-dalek.git\"", + "git = \"https://github.com/betrusted-io/curve25519-dalek.git\"", + ) + .expect("couldn't patch curve25519"); + builder::search_and_replace_in_file( + "Cargo.toml", + "# branch = \"main\" # c25519", + "branch = \"main\" # c25519", + ) + .expect("couldn't patch curve25519"); } match builder::search_in_file("services/aes/Cargo.toml", "default = []") { Ok(false) => { From 6c49e2a6ddd18bdb33cebe4d170f42dc06bb47b1 Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 21 Nov 2024 02:18:58 +0800 Subject: [PATCH 29/42] include patches for shellchat and rootkeys --- xtask/src/builder.rs | 13 +++++++++++++ xtask/src/main.rs | 24 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/xtask/src/builder.rs b/xtask/src/builder.rs index 13f1bc2ad..47daabc3d 100644 --- a/xtask/src/builder.rs +++ b/xtask/src/builder.rs @@ -352,6 +352,19 @@ impl Builder { "# branch = \"main\" # c25519", ) .expect("couldn't patch curve25519"); + search_and_replace_in_file( + "services/root-keys/Cargo.toml", + "features = [\"auto-release\", \"warn-fallback\"]", + "# features = [\"auto-release\", \"warn-fallback\"]", + ) + .expect("couldn't patch rootkeys"); + search_and_replace_in_file( + "services/shellchat/Cargo.toml", + "features = [\"auto-release\", \"warn-fallback\"]", + "# features = [\"auto-release\", \"warn-fallback\"]", + ) + .expect("couldn't patch shellchat"); + self } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index e1ce85181..113d483d4 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -220,6 +220,18 @@ fn main() -> Result<(), Box> { "branch = \"main\" # c25519", ) .ok(); + builder::search_and_replace_in_file( + "services/root-keys/Cargo.toml", + "# features = [\"auto-release\", \"warn-fallback\"]", + "features = [\"auto-release\", \"warn-fallback\"]", + ) + .ok(); + builder::search_and_replace_in_file( + "services/shellchat/Cargo.toml", + "# features = [\"auto-release\", \"warn-fallback\"]", + "features = [\"auto-release\", \"warn-fallback\"]", + ) + .ok(); match builder::search_in_file("services/aes/Cargo.toml", "default = []") { Ok(false) => { @@ -673,6 +685,18 @@ fn main() -> Result<(), Box> { "branch = \"main\" # c25519", ) .expect("couldn't patch curve25519"); + builder::search_and_replace_in_file( + "services/root-keys/Cargo.toml", + "# features = [\"auto-release\", \"warn-fallback\"]", + "features = [\"auto-release\", \"warn-fallback\"]", + ) + .expect("couldn't patch rootkeys"); + builder::search_and_replace_in_file( + "services/shellchat/Cargo.toml", + "# features = [\"auto-release\", \"warn-fallback\"]", + "features = [\"auto-release\", \"warn-fallback\"]", + ) + .expect("couldn't patch shellchat"); } match builder::search_in_file("services/aes/Cargo.toml", "default = []") { Ok(false) => { From fb0efbce3b9e4bec988ecdf5d9c1140e62354ab6 Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 21 Nov 2024 02:19:21 +0800 Subject: [PATCH 30/42] add helper type for pre-hash digests --- loader/src/platform/cramium/sha512_digest.rs | 85 ++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 loader/src/platform/cramium/sha512_digest.rs diff --git a/loader/src/platform/cramium/sha512_digest.rs b/loader/src/platform/cramium/sha512_digest.rs new file mode 100644 index 000000000..8a2730f5a --- /dev/null +++ b/loader/src/platform/cramium/sha512_digest.rs @@ -0,0 +1,85 @@ +use core::fmt; + +use digest::{ + FixedOutput, HashMarker, InvalidOutputSize, Output, Update, + block_buffer::Eager, + core_api::{ + AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, OutputSizeUser, TruncSide, UpdateCore, + VariableOutputCore, + }, + typenum::{U64, U128}, +}; + +/// Wrap a pre-hash value in a Digest trait +#[derive(Clone)] +pub struct Sha512Prehash { + /// track the length of the message processed so far + hash: Option<[u8; 64]>, +} +impl Sha512Prehash { + // use this function instead of default for more control over configuration of the hardware engine + pub fn new() -> Self { Sha512Prehash { hash: None } } + + #[allow(dead_code)] // not used in hosted mode + pub fn set_prehash(&mut self, hash: [u8; 64]) { self.hash = Some(hash); } +} +impl Default for Sha512Prehash { + fn default() -> Self { Sha512Prehash::new() } +} + +impl HashMarker for Sha512Prehash {} + +impl BlockSizeUser for Sha512Prehash { + type BlockSize = U128; +} + +impl BufferKindUser for Sha512Prehash { + type BufferKind = Eager; +} + +impl OutputSizeUser for Sha512Prehash { + type OutputSize = U64; +} + +impl AlgorithmName for Sha512Prehash { + #[inline] + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Sha512") } +} + +impl fmt::Debug for Sha512Prehash { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Sha512Prehash { ... }") } +} + +impl UpdateCore for Sha512Prehash { + fn update_blocks(&mut self, _blocks: &[Block]) { + panic!("Prehash implementation can't take block updates"); + } +} + +impl VariableOutputCore for Sha512Prehash { + const TRUNC_SIDE: TruncSide = TruncSide::Left; + + fn new(_output_size: usize) -> Result { Ok(Self { hash: None }) } + + fn finalize_variable_core(&mut self, _buffer: &mut Buffer, out: &mut Output) { + for (dest, &src) in out.chunks_exact_mut(1).zip(self.hash.unwrap().iter()) { + dest.copy_from_slice(&[src]) + } + } +} + +impl Update for Sha512Prehash { + /// Update state using the provided data. + fn update(&mut self, _data: &[u8]) { + panic!("Prehash implementation can't take block updates"); + } +} +impl FixedOutput for Sha512Prehash { + /// Consume value and write result into provided array. + fn finalize_into(self, out: &mut Output) { + for (dest, &src) in out.chunks_exact_mut(1).zip(self.hash.unwrap().iter()) { + dest.copy_from_slice(&[src]) + } + } +} From 3bf8598509df8214b53ff5bd1690b1d00670ce54 Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 21 Nov 2024 02:19:45 +0800 Subject: [PATCH 31/42] make slice cursor peekable --- loader/src/platform/cramium/usb/slice_cursor.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/loader/src/platform/cramium/usb/slice_cursor.rs b/loader/src/platform/cramium/usb/slice_cursor.rs index e01fd7b2f..ef2041bfa 100644 --- a/loader/src/platform/cramium/usb/slice_cursor.rs +++ b/loader/src/platform/cramium/usb/slice_cursor.rs @@ -57,8 +57,8 @@ impl IOBase for SliceCursor<'_> { type Error = IOErrorFatfs; } pub struct SliceCursor<'a> { - slice: &'a mut [u8], - pos: u64, + pub slice: &'a mut [u8], + pub pos: u64, } impl<'a> SliceCursor<'a> { From 8aa633641fd44c031123687b70e1ad0b875b5e32 Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 21 Nov 2024 02:20:10 +0800 Subject: [PATCH 32/42] add new dependencies for image checking in loader --- loader/Cargo.toml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/loader/Cargo.toml b/loader/Cargo.toml index 6a9985ec6..26a27b24e 100644 --- a/loader/Cargo.toml +++ b/loader/Cargo.toml @@ -42,8 +42,16 @@ libm = { version = "0.2.8", optional = true } nalgebra = { version = "0.33", default-features = false, features = [ "libm", ], optional = true } -simple-fatfs = { version = "0.1.0-alpha.1", optional = true, default-features = false } +simple-fatfs = { git = "https://github.com/betrusted-io/simple-fatfs.git", branch = "baosec", optional = true, default-features = false } linked_list_allocator = { version = "0.10.5", optional = true } +sha2 = { version = "0.10.8", optional = true, default-features = false } +digest = { version = "0.10.7", optional = true } + +[dependencies.ed25519-dalek] +version = "=2.1.0" +optional = true +default-features = false +features = ["rand_core", "digest"] [dependencies.com_rs] git = "https://github.com/betrusted-io/com_rs" @@ -80,6 +88,9 @@ cramium-soc = [ "libm", "simple-fatfs", "linked_list_allocator", + "ed25519-dalek", + "sha2", + "digest", # "boot-delay", ] board-baosec = ["cramium-hal/board-baosec"] From 2437712b7bb5dd08e8a57bd3ee088b8046bc2d6e Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 21 Nov 2024 02:20:40 +0800 Subject: [PATCH 33/42] add some basic image integrity checking code this doesn't protect against malicious tampering (because the public key is the developer key) but it will at least catch correctness errors in USB upload and filesystem behavior. --- loader/src/platform/cramium/update.rs | 77 ++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/loader/src/platform/cramium/update.rs b/loader/src/platform/cramium/update.rs index 33b96c123..75b55c604 100644 --- a/loader/src/platform/cramium/update.rs +++ b/loader/src/platform/cramium/update.rs @@ -1,12 +1,18 @@ +use core::convert::TryInto; + use cramium_hal::iox::{IoGpio, IoSetup, Iox, IoxPort, IoxValue}; use cramium_hal::minigfx::{FrameBuffer, Point}; use cramium_hal::sh1107::Mono; use cramium_hal::udma; use cramium_hal::usb::driver::UsbDeviceState; +use ed25519_dalek::{Digest, Signature, VerifyingKey}; +use sha2::Sha512; use simple_fatfs::PathBuf; use utralib::generated::*; +use crate::SIGBLOCK_SIZE; use crate::platform::cramium::gfx; +use crate::platform::cramium::sha512_digest::Sha512Prehash; use crate::platform::cramium::usb; use crate::platform::cramium::usb::SliceCursor; @@ -18,6 +24,20 @@ use crate::platform::cramium::usb::SliceCursor; // to detect if the device is unplugged. const DISCONNECT_STATE: u32 = 0x40b; +// loader is not updateable here because we're XIP. But we can update these other images: +const SWAP_NAME: &'static str = "SWAP.IMG"; +const KERNEL_NAME: &'static str = "XOUS.IMG"; +const DEV_PUBKEY: [u8; 32] = [ + 0x1c, 0x9b, 0xea, 0xe3, 0x2a, 0xea, 0xc8, 0x75, 0x07, 0xc1, 0x80, 0x94, 0x38, 0x7e, 0xff, 0x1c, 0x74, + 0x61, 0x42, 0x82, 0xaf, 0xfd, 0x81, 0x52, 0xd8, 0x71, 0x35, 0x2e, 0xdf, 0x3f, 0x58, 0xbb, +]; +#[repr(C)] +struct SignatureInFlash { + pub version: u32, + pub signed_len: u32, + pub signature: [u8; 64], +} + #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum KeyPress { Up, @@ -193,7 +213,62 @@ pub fn process_update(perclk: u32) { match fs.read_dir(PathBuf::from("/")) { Ok(dir) => { for entry in dir { - crate::println!("file: {:?}", entry); + crate::println!("{:?}", entry); + if let Some(file_name) = entry.path().file_name() { + if file_name.to_ascii_uppercase() == KERNEL_NAME { + match fs.get_file(entry.path().clone()) { + Ok(f) => { + let sector_offset = f.sector_offset(); + crate::println!("sector offset: {}", sector_offset); + let disk_access = usb::conjure_disk(); + crate::println!( + "{:x?}", + &disk_access[sector_offset as usize * 512 + ..sector_offset as usize * 512 + 32] + ); + let pubkey = VerifyingKey::from_bytes(&DEV_PUBKEY) + .expect("public key was not valid"); + crate::println!("pubkey as reconstituted: {:x?}", pubkey); + + let k_start = sector_offset as usize * 512; + let sig_region = &disk_access + [k_start..k_start + core::mem::size_of::()]; + let sig_rec: &SignatureInFlash = (sig_region.as_ptr() + as *const SignatureInFlash) + .as_ref() + .unwrap(); // this pointer better not be null, we just created it! + let sig = Signature::from_bytes(&sig_rec.signature); + + let kern_len = sig_rec.signed_len as usize; + crate::println!("recorded kernel len: {} bytes", kern_len); + crate::println!( + "verifying with signature {:x?}", + sig_rec.signature + ); + crate::println!("verifying with pubkey {:x?}", pubkey.to_bytes()); + + let mut h: Sha512 = Sha512::new(); + let image = &disk_access[k_start + SIGBLOCK_SIZE + ..k_start + SIGBLOCK_SIZE + sig_rec.signed_len as usize]; + h.update(&image); + let hash = h.finalize(); + let mut ph = Sha512Prehash::new(); + ph.set_prehash(hash.as_slice().try_into().unwrap()); + match pubkey.verify_prehashed(ph, None, &sig) { + Ok(()) => crate::println!("Kernel image is good!"), + Err(e) => { + crate::println!("error verifying signature: {:?}", e); + } + } + } + Err(e) => { + crate::println!("Couldn't access image: {:?}", e); + } + } + } else if file_name.to_ascii_lowercase() == SWAP_NAME { + crate::println!("Found swap image"); + } + } } } Err(e) => { From 30cb5f84f74001fbaf5328d72c222f3ce7b7f7e4 Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 21 Nov 2024 02:22:08 +0800 Subject: [PATCH 34/42] make more space in the cramium loader need code space to handle image verification. getting kind of heavy, but I think we're almost done. --- loader/src/platform/cramium/cramium.rs | 2 +- loader/src/platform/cramium/link-soc.x | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/loader/src/platform/cramium/cramium.rs b/loader/src/platform/cramium/cramium.rs index c33c199fa..c1e915202 100644 --- a/loader/src/platform/cramium/cramium.rs +++ b/loader/src/platform/cramium/cramium.rs @@ -73,7 +73,7 @@ pub const FLASH_BASE: usize = utralib::generated::HW_RERAM_MEM; // location of kernel, as offset from the base of ReRAM. This needs to match up with what is in link.x. // exclusive of the signature block offset -pub const KERNEL_OFFSET: usize = 0x5_0000; +pub const KERNEL_OFFSET: usize = 0x6_0000; pub(crate) fn delay(quantum: usize) { use utralib::{CSR, utra}; diff --git a/loader/src/platform/cramium/link-soc.x b/loader/src/platform/cramium/link-soc.x index 5850567d1..3a56049f9 100644 --- a/loader/src/platform/cramium/link-soc.x +++ b/loader/src/platform/cramium/link-soc.x @@ -1,8 +1,8 @@ MEMORY { /* we are using an unsigned loader, so the offset is not 1k offset */ - FONTS : ORIGIN = 0x60030000, LENGTH = 128k - FLASH : ORIGIN = 0x60001000, LENGTH = 188k + FONTS : ORIGIN = 0x60040000, LENGTH = 128k + FLASH : ORIGIN = 0x60001000, LENGTH = 252k RAM : ORIGIN = 0x61000000, LENGTH = 2M } From 89fb9b4df60a3a84d7c541a52fba2c107a0ac146 Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 21 Nov 2024 02:22:39 +0800 Subject: [PATCH 35/42] add reference to the presign module --- loader/src/platform/cramium.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/loader/src/platform/cramium.rs b/loader/src/platform/cramium.rs index 3f8ac5ae2..b779cce32 100644 --- a/loader/src/platform/cramium.rs +++ b/loader/src/platform/cramium.rs @@ -18,3 +18,5 @@ mod homography; mod qr; #[cfg(feature = "usb")] mod usb; + +mod sha512_digest; From 607d784eae2e90525c296a1f2a2232303e448325 Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 21 Nov 2024 02:22:58 +0800 Subject: [PATCH 36/42] cleanup lockfile after loader dependency fixes --- Cargo.lock | 78 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7fd35e643..28be5d6b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -175,7 +175,7 @@ dependencies = [ "argh_shared", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -483,7 +483,7 @@ checksum = "de05f8756f1c68937349406d4632ae96ae35901019b5e59c508d9c38c64715fb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1275,7 +1275,7 @@ source = "git+https://github.com/betrusted-io/curve25519-dalek.git?branch=main#c dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1345,7 +1345,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1367,7 +1367,7 @@ checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77" dependencies = [ "darling_core 0.20.5", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1402,7 +1402,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1467,7 +1467,7 @@ checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1487,7 +1487,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1545,7 +1545,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1799,7 +1799,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1820,7 +1820,7 @@ dependencies = [ "darling 0.20.5", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1978,7 +1978,7 @@ version = "0.1.2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -2035,7 +2035,7 @@ checksum = "b0fa992f1656e1707946bbba340ad244f0814009ef8c0118eb7b658395f19a2e" dependencies = [ "frunk_proc_macro_helpers", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -2047,7 +2047,7 @@ dependencies = [ "frunk_core", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -2121,7 +2121,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -2163,7 +2163,7 @@ dependencies = [ "g2poly", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -3004,6 +3004,8 @@ dependencies = [ "cramium-hal", "crc 1.8.1", "curve25519-dalek-loader", + "digest 0.10.7", + "ed25519-dalek", "ed25519-dalek-loader", "lazy_static", "libm 0.2.8", @@ -3012,6 +3014,7 @@ dependencies = [ "pio", "pio-proc", "rand_chacha 0.3.1", + "sha2", "sha2-loader", "simple-fatfs", "utralib 0.1.25", @@ -3275,7 +3278,7 @@ checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -3445,7 +3448,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -3546,7 +3549,7 @@ checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -4157,7 +4160,7 @@ checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -4313,7 +4316,7 @@ checksum = "8b86292cf41ccfc96c5de7165c1c53d5b4ac540c5bab9d1857acbe9eba5f1a0b" dependencies = [ "proc-macro-hack", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -4526,7 +4529,7 @@ checksum = "09cb82b74b4810f07e460852c32f522e979787691b0b7b7439fe473e49d49b2f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -4770,9 +4773,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.196" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -4798,13 +4801,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -4826,7 +4829,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -5011,8 +5014,7 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "simple-fatfs" version = "0.1.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61006ceee80c793fc9d5d619cc6ea624c5d509916c21a1b10625655dbd037d2" +source = "git+https://github.com/betrusted-io/simple-fatfs.git?branch=baosec#83b886a5a849ad03d07350cb9f9af6b32da53e7c" dependencies = [ "bitfield-struct", "bitflags 2.6.0", @@ -5266,9 +5268,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -5380,7 +5382,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -5562,7 +5564,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -6185,7 +6187,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -6219,7 +6221,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6252,7 +6254,7 @@ checksum = "a5211b7550606857312bba1d978a8ec75692eae187becc5e680444fffc5e6f89" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -6956,7 +6958,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] From ba874229039e55ee1979e59225615352dbe3b873 Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 21 Nov 2024 18:23:20 +0800 Subject: [PATCH 37/42] add platform code for mailbox to CM7 --- libs/cramium-hal/src/mbox.rs | 244 +++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 libs/cramium-hal/src/mbox.rs diff --git a/libs/cramium-hal/src/mbox.rs b/libs/cramium-hal/src/mbox.rs new file mode 100644 index 000000000..8e95cef35 --- /dev/null +++ b/libs/cramium-hal/src/mbox.rs @@ -0,0 +1,244 @@ +use utra::mailbox; +use utralib::generated::*; + +/// This constraint is limited by the size of the memory on the CM7 side +pub const MAX_PKT_LEN: usize = 128; +pub const MBOX_PROTOCOL_REV: u32 = 0; +pub const TX_FIFO_DEPTH: u32 = 128; +pub const PAYLOAD_LEN_WORDS: usize = MAX_PKT_LEN - 2; +pub const RERAM_PAGE_SIZE_BYTES: usize = 32; + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[allow(dead_code)] +pub enum MboxError { + None, + NotReady, + TxOverflow, + TxUnderflow, + RxOverflow, + RxUnderflow, + InvalidOpcode, + ProtocolErr, +} + +#[repr(u16)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum ToRvOp { + Invalid = 0, + + RetKnock = 128, + RetDct8x8 = 129, + RetClifford = 130, + RetFlashWrite = 131, +} +impl TryFrom for ToRvOp { + type Error = MboxError; + + fn try_from(value: u16) -> Result { + match value { + 0 => Ok(ToRvOp::Invalid), + 128 => Ok(ToRvOp::RetKnock), + 129 => Ok(ToRvOp::RetDct8x8), + 130 => Ok(ToRvOp::RetClifford), + 131 => Ok(ToRvOp::RetFlashWrite), + _ => Err(MboxError::InvalidOpcode), + } + } +} + +#[repr(u16)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[allow(dead_code)] +pub enum ToCm7Op { + Invalid = 0, + + Knock = 1, + Dct8x8 = 2, + Clifford = 3, + FlashWrite = 4, +} + +pub struct MboxToCm7Pkt<'a> { + pub version: u32, + pub opcode: ToCm7Op, + pub data: &'a [u32], +} + +pub struct MboxToRvPkt { + pub version: u32, + pub opcode: ToRvOp, + #[cfg(target_os = "xous")] + pub data: Vec, + #[cfg(not(target_os = "xous"))] + pub len: usize, +} + +pub struct Mbox { + csr: CSR, +} +impl Mbox { + pub fn new() -> Mbox { + #[cfg(target_os = "xous")] + let csr_mem = xous::syscall::map_memory( + xous::MemoryAddress::new(mailbox::HW_MAILBOX_BASE), + None, + 4096, + xous::MemoryFlags::R | xous::MemoryFlags::W, + ) + .expect("couldn't map mailbox CSR range"); + #[cfg(target_os = "xous")] + let mut csr = CSR::new(csr_mem.as_mut_ptr() as *mut u32); + #[cfg(not(target_os = "xous"))] + let mut csr = CSR::new(mailbox::HW_MAILBOX_BASE as *mut u32); + csr.wfo(mailbox::LOOPBACK_LOOPBACK, 0); // ensure we're not in loopback mode + // generate available events - not hooked up to IRQ, but we'll poll for now + csr.wfo(mailbox::EV_ENABLE_AVAILABLE, 1); + + Self { csr } + } + + fn expect_tx(&mut self, val: u32) -> Result<(), MboxError> { + if (TX_FIFO_DEPTH - self.csr.rf(mailbox::STATUS_TX_WORDS)) == 0 { + return Err(MboxError::TxOverflow); + } else { + self.csr.wo(mailbox::WDATA, val); + Ok(()) + } + } + + pub fn try_send(&mut self, to_cm7: MboxToCm7Pkt) -> Result<(), MboxError> { + if to_cm7.data.len() > PAYLOAD_LEN_WORDS { + Err(MboxError::TxOverflow) + } else { + self.expect_tx(to_cm7.version)?; + self.expect_tx(to_cm7.opcode as u32 | (to_cm7.data.len() as u32) << 16)?; + for &d in to_cm7.data.iter() { + self.expect_tx(d)?; + } + // trigger the send + self.csr.wfo(mailbox::DONE_DONE, 1); + Ok(()) + } + } + + fn expect_rx(&mut self) -> Result { + if self.csr.rf(mailbox::STATUS_RX_WORDS) == 0 { + Err(MboxError::RxUnderflow) + } else { + Ok(self.csr.r(mailbox::RDATA)) + } + } + + #[cfg(target_os = "xous")] + pub fn try_rx(&mut self) -> Result { + let version = self.expect_rx()?; + let op_and_len = self.expect_rx()?; + let opcode = ToRvOp::try_from((op_and_len & 0xFFFF) as u16)?; + let len = (op_and_len >> 16) as usize; + let mut data = Vec::new(); + for _ in 0..len { + data.push(self.expect_rx()?); + } + Ok(MboxToRvPkt { version, opcode, data }) + } + + #[cfg(not(target_os = "xous"))] + pub fn try_rx(&mut self, data: &mut [u32]) -> Result { + let version = self.expect_rx()?; + let op_and_len = self.expect_rx()?; + let opcode = ToRvOp::try_from((op_and_len & 0xFFFF) as u16)?; + let len = (op_and_len >> 16) as usize; + for i in 0..len { + if i < data.len() { + data[i] = self.expect_rx()? + } + } + Ok(MboxToRvPkt { version, opcode, len }) + } + + pub fn poll_not_ready(&self) -> bool { self.csr.rf(mailbox::EV_PENDING_AVAILABLE) == 0 } + + pub fn poll_rx_available(&self) -> bool { self.csr.rf(mailbox::STATUS_RX_WORDS) != 0 } +} + +// TODO: make these protocol-level interactions more modular. Right now this is just a stand-alone +// test routine suitable for use in a no_std bootloader environment. +// +// TODO: resolve the timeout timer requirement in this code - there isn't a standard timer in +// the bootloader. +/* +#[cfg(not(target_os = "xous"))] +#[allow(dead_code)] +pub fn knock(mbox: &mut Mbox) -> Result<(), MboxError> { + let test_data = [0xC0DE_0000u32, 0x0000_600Du32]; + let mut expected_result = 0; + for &d in test_data.iter() { + expected_result ^= d; + } + let test_pkt = MboxToCm7Pkt { version: MBOX_PROTOCOL_REV, opcode: ToCm7Op::Knock, data: &test_data }; + crate::println!("sending knock..."); + match mbox.try_send(test_pkt) { + Ok(_) => { + crate::println!("Packet send Ok\n"); + let mut timeout = 0; + while mbox.poll_not_ready() { + timeout += 1; + if timeout > 1000 { + crate::println!("flash write timeout"); + return Err(MboxError::NotReady); + } + crate::platform::delay(2); + } // now receive the packet + let mut rx_data = [0u32; 16]; + timeout = 0; + while !mbox.poll_rx_available() { + timeout += 1; + if timeout > 1000 { + crate::println!("flash handshake timeout"); + return Err(MboxError::NotReady); + } + crate::platform::delay(2); + } + match mbox.try_rx(&mut rx_data) { + Ok(rx_pkt) => { + crate::println!("Knock result: {:x}", rx_data[0]); + if rx_pkt.version != MBOX_PROTOCOL_REV { + crate::println!("Version mismatch {} != {}", rx_pkt.version, MBOX_PROTOCOL_REV); + } + if rx_pkt.opcode != ToRvOp::RetKnock { + crate::println!( + "Opcode mismatch {} != {}", + rx_pkt.opcode as u16, + ToRvOp::RetKnock as u16 + ); + } + if rx_pkt.len != 1 { + crate::println!("Expected length mismatch {} != {}", rx_pkt.len, 1); + Err(MboxError::ProtocolErr) + } else { + if rx_data[0] != expected_result { + crate::println!( + "Expected data mismatch {:x} != {:x}", + rx_data[0], + expected_result + ); + Err(MboxError::ProtocolErr) + } else { + crate::println!("Knock test PASS: {:x}", rx_data[0]); + Ok(()) + } + } + } + Err(e) => { + crate::println!("Error while deserializing: {:?}\n", e); + Err(e) + } + } + } + Err(e) => { + crate::println!("Packet send error: {:?}\n", e); + Err(e) + } + } +} +*/ From faa8a6084331f493a617ad86e75ac53fc5b96894 Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 21 Nov 2024 18:23:36 +0800 Subject: [PATCH 38/42] add ref to mbox module --- libs/cramium-hal/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/cramium-hal/src/lib.rs b/libs/cramium-hal/src/lib.rs index 827bfff23..50b2d1252 100644 --- a/libs/cramium-hal/src/lib.rs +++ b/libs/cramium-hal/src/lib.rs @@ -13,4 +13,5 @@ pub mod shared_csr; pub mod udma; pub mod usb; pub use shared_csr::*; +pub mod mbox; pub mod minigfx; From b944c51474b704cfbdf8bb62c3cd160200df5b6a Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 21 Nov 2024 18:23:55 +0800 Subject: [PATCH 39/42] a simple update with verified image now working --- loader/src/platform/cramium/update.rs | 230 +++++++++++++++++++++----- 1 file changed, 186 insertions(+), 44 deletions(-) diff --git a/loader/src/platform/cramium/update.rs b/loader/src/platform/cramium/update.rs index 75b55c604..e7df5c225 100644 --- a/loader/src/platform/cramium/update.rs +++ b/loader/src/platform/cramium/update.rs @@ -1,6 +1,10 @@ use core::convert::TryInto; use cramium_hal::iox::{IoGpio, IoSetup, Iox, IoxPort, IoxValue}; +use cramium_hal::mbox::{ + MBOX_PROTOCOL_REV, Mbox, MboxError, MboxToCm7Pkt, PAYLOAD_LEN_WORDS, RERAM_PAGE_SIZE_BYTES, ToCm7Op, + ToRvOp, +}; use cramium_hal::minigfx::{FrameBuffer, Point}; use cramium_hal::sh1107::Mono; use cramium_hal::udma; @@ -15,6 +19,7 @@ use crate::platform::cramium::gfx; use crate::platform::cramium::sha512_digest::Sha512Prehash; use crate::platform::cramium::usb; use crate::platform::cramium::usb::SliceCursor; +use crate::platform::delay; // TODO: // - Port unicode font drawing into loader @@ -26,7 +31,10 @@ const DISCONNECT_STATE: u32 = 0x40b; // loader is not updateable here because we're XIP. But we can update these other images: const SWAP_NAME: &'static str = "SWAP.IMG"; -const KERNEL_NAME: &'static str = "XOUS.IMG"; +const KERNEL_NAME: &'static str = "XOUS.BIN"; +#[allow(dead_code)] +const SWAP_BASE_ADDR: usize = 0; +const KERNEL_BASE_ADDR: usize = crate::platform::KERNEL_OFFSET; const DEV_PUBKEY: [u8; 32] = [ 0x1c, 0x9b, 0xea, 0xe3, 0x2a, 0xea, 0xc8, 0x75, 0x07, 0xc1, 0x80, 0x94, 0x38, 0x7e, 0xff, 0x1c, 0x74, 0x61, 0x42, 0x82, 0xaf, 0xfd, 0x81, 0x52, 0xd8, 0x71, 0x35, 0x2e, 0xdf, 0x3f, 0x58, 0xbb, @@ -216,55 +224,113 @@ pub fn process_update(perclk: u32) { crate::println!("{:?}", entry); if let Some(file_name) = entry.path().file_name() { if file_name.to_ascii_uppercase() == KERNEL_NAME { - match fs.get_file(entry.path().clone()) { - Ok(f) => { - let sector_offset = f.sector_offset(); - crate::println!("sector offset: {}", sector_offset); - let disk_access = usb::conjure_disk(); - crate::println!( - "{:x?}", - &disk_access[sector_offset as usize * 512 - ..sector_offset as usize * 512 + 32] - ); - let pubkey = VerifyingKey::from_bytes(&DEV_PUBKEY) - .expect("public key was not valid"); - crate::println!("pubkey as reconstituted: {:x?}", pubkey); + let f = fs.get_file(entry.path().clone()).expect("file error"); + let sector_offset = f.sector_offset(); + crate::println!("sector offset: {}", sector_offset); + let disk_access = usb::conjure_disk(); + crate::println!( + "{:x?}", + &disk_access + [sector_offset as usize * 512..sector_offset as usize * 512 + 32] + ); + let pubkey = VerifyingKey::from_bytes(&DEV_PUBKEY) + .expect("public key was not valid"); + crate::println!("pubkey as reconstituted: {:x?}", pubkey); - let k_start = sector_offset as usize * 512; - let sig_region = &disk_access - [k_start..k_start + core::mem::size_of::()]; - let sig_rec: &SignatureInFlash = (sig_region.as_ptr() - as *const SignatureInFlash) - .as_ref() - .unwrap(); // this pointer better not be null, we just created it! - let sig = Signature::from_bytes(&sig_rec.signature); + let k_start = sector_offset as usize * 512; + let sig_region = &disk_access + [k_start..k_start + core::mem::size_of::()]; + let sig_rec: &SignatureInFlash = + (sig_region.as_ptr() as *const SignatureInFlash).as_ref().unwrap(); // this pointer better not be null, we just created it! + let sig = Signature::from_bytes(&sig_rec.signature); - let kern_len = sig_rec.signed_len as usize; - crate::println!("recorded kernel len: {} bytes", kern_len); - crate::println!( - "verifying with signature {:x?}", - sig_rec.signature - ); - crate::println!("verifying with pubkey {:x?}", pubkey.to_bytes()); + let kern_len = sig_rec.signed_len as usize; + crate::println!("recorded kernel len: {} bytes", kern_len); + crate::println!("verifying with signature {:x?}", sig_rec.signature); + crate::println!("verifying with pubkey {:x?}", pubkey.to_bytes()); - let mut h: Sha512 = Sha512::new(); - let image = &disk_access[k_start + SIGBLOCK_SIZE - ..k_start + SIGBLOCK_SIZE + sig_rec.signed_len as usize]; - h.update(&image); - let hash = h.finalize(); - let mut ph = Sha512Prehash::new(); - ph.set_prehash(hash.as_slice().try_into().unwrap()); - match pubkey.verify_prehashed(ph, None, &sig) { - Ok(()) => crate::println!("Kernel image is good!"), - Err(e) => { - crate::println!("error verifying signature: {:?}", e); - } - } + let mut h: Sha512 = Sha512::new(); + let image = &disk_access[k_start + SIGBLOCK_SIZE + ..k_start + SIGBLOCK_SIZE + sig_rec.signed_len as usize]; + crate::println!("image bytes: {:x?}", &image[..16]); + h.update(&image); + let hash = h.finalize(); + let mut ph = Sha512Prehash::new(); + ph.set_prehash(hash.as_slice().try_into().unwrap()); + let v_result = pubkey.verify_prehashed(ph, None, &sig); + if let Err(e) = v_result { + crate::println!("error verifying signature: {:?}", e); + gfx::msg( + &mut sh1107, + "Image corrupt!", + Point::new(10, 64), + Mono::White.into(), + Mono::Black.into(), + ); + sh1107.buffer_swap(); + sh1107.draw(); + sh1107.clear(); + return; + } + crate::println!("Kernel image is good!"); + let mut mbox = cramium_hal::mbox::Mbox::new(); + // concatenate this with e.g. .min(0x2000) to shorten the run, if needed + let test_len = sig_rec.signed_len as usize; + let check_slice = core::slice::from_raw_parts( + (KERNEL_BASE_ADDR + utralib::HW_RERAM_MEM) as *const u8, + 0x3000, + ); + crate::println!( + "data before: {:x?} .. {:x?}", + &check_slice[..32], + &check_slice[0x1000..0x1000 + 32] + ); + // nearest event multiple of RERAM_PAGE_SIZE_BYTES that fits into + // our available payload length + const BLOCKLEN_BYTES: usize = (PAYLOAD_LEN_WORDS * size_of::() + / RERAM_PAGE_SIZE_BYTES) + * RERAM_PAGE_SIZE_BYTES; + for (i, byte_chunk) in disk_access + [k_start..k_start + SIGBLOCK_SIZE + test_len as usize] + .chunks(BLOCKLEN_BYTES) + .enumerate() + { + // +2 words are for the address / length fields required by the + // protocol + let mut buffer = [0u32; BLOCKLEN_BYTES / size_of::() + 2]; + for (src, dst) in byte_chunk.chunks(4).zip(buffer[2..].iter_mut()) { + *dst = u32::from_le_bytes(src.try_into().unwrap()); } - Err(e) => { - crate::println!("Couldn't access image: {:?}", e); + // now fill in the metadata for the protocol + buffer[0] = + (KERNEL_BASE_ADDR + i * BLOCKLEN_BYTES + utralib::HW_RERAM_MEM) + as u32; + buffer[1] = BLOCKLEN_BYTES as u32; + if i % 8 == 0 { + crate::println!("{:x}: {:x?}", buffer[0], &buffer[2..6]); } + match write_rram(&mut mbox, &buffer) { + Ok(len) => { + if len != BLOCKLEN_BYTES { + crate::println!( + "write length mismatch, got {} expected {} words", + len, + BLOCKLEN_BYTES + ); + } + } + Err(e) => { + crate::println!("Write failed with {:?}", e); + break; + } + }; } + cache_flush(); + crate::println!( + "data after: {:x?} .. {:x?}", + &check_slice[..32], + &check_slice[0x1000..0x1000 + 32] + ); } else if file_name.to_ascii_lowercase() == SWAP_NAME { crate::println!("Found swap image"); } @@ -286,3 +352,79 @@ pub fn process_update(perclk: u32) { sh1107.draw(); sh1107.clear(); } + +pub fn write_rram(mbox: &mut Mbox, data: &[u32]) -> Result { + let write_pkt = MboxToCm7Pkt { version: MBOX_PROTOCOL_REV, opcode: ToCm7Op::FlashWrite, data }; + match mbox.try_send(write_pkt) { + Ok(_) => { + crate::platform::delay(10); + // crate::println!("flash write sent OK"); + let mut timeout = 0; + while mbox.poll_not_ready() { + timeout += 1; + if timeout > 1000 { + crate::println!("flash write timeout"); + return Err(MboxError::NotReady); + } + crate::platform::delay(2); + } + // now receive the packet + let mut rx_data = [0u32; 16]; + timeout = 0; + while !mbox.poll_rx_available() { + timeout += 1; + if timeout > 1000 { + crate::println!("flash handshake timeout"); + return Err(MboxError::NotReady); + } + crate::platform::delay(2); + } + match mbox.try_rx(&mut rx_data) { + Ok(rx_pkt) => { + crate::platform::delay(10); + if rx_pkt.version != MBOX_PROTOCOL_REV { + crate::println!("Version mismatch {} != {}", rx_pkt.version, MBOX_PROTOCOL_REV); + } + if rx_pkt.opcode != ToRvOp::RetFlashWrite { + crate::println!( + "Opcode mismatch {} != {}", + rx_pkt.opcode as u16, + ToRvOp::RetFlashWrite as u16 + ); + } + if rx_pkt.len != 1 { + crate::println!("Expected length mismatch {} != {}", rx_pkt.len, 1); + } else { + // crate::println!("Wrote {} bytes", rx_data[0]); + } + Ok(rx_data[0] as usize) + } + Err(e) => { + crate::platform::delay(10); + crate::println!("Error while deserializing: {:?}\n", e); + Err(e) + } + } + } + Err(e) => { + delay(10); + crate::println!("Flash write send error: {:?}", e); + Err(e) + } + } +} + +#[inline(always)] +fn cache_flush() { + unsafe { + #[rustfmt::skip] + core::arch::asm!( + ".word 0x500F", + "nop", + "nop", + "nop", + "nop", + "nop", + ); + } +} From a7619e55a81fec51989a2a3e0ebce4bc793317d5 Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 21 Nov 2024 22:03:06 +0800 Subject: [PATCH 40/42] add progress bar to burn --- loader/src/platform/cramium/update.rs | 45 ++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/loader/src/platform/cramium/update.rs b/loader/src/platform/cramium/update.rs index e7df5c225..61eee1dfb 100644 --- a/loader/src/platform/cramium/update.rs +++ b/loader/src/platform/cramium/update.rs @@ -1,4 +1,5 @@ use core::convert::TryInto; +use core::fmt::Write; use cramium_hal::iox::{IoGpio, IoSetup, Iox, IoxPort, IoxValue}; use cramium_hal::mbox::{ @@ -6,7 +7,7 @@ use cramium_hal::mbox::{ ToRvOp, }; use cramium_hal::minigfx::{FrameBuffer, Point}; -use cramium_hal::sh1107::Mono; +use cramium_hal::sh1107::{Mono, Oled128x128}; use cramium_hal::udma; use cramium_hal::usb::driver::UsbDeviceState; use ed25519_dalek::{Digest, Signature, VerifyingKey}; @@ -285,11 +286,15 @@ pub fn process_update(perclk: u32) { &check_slice[..32], &check_slice[0x1000..0x1000 + 32] ); + + progress_bar(&mut sh1107, 0); + // nearest event multiple of RERAM_PAGE_SIZE_BYTES that fits into // our available payload length const BLOCKLEN_BYTES: usize = (PAYLOAD_LEN_WORDS * size_of::() / RERAM_PAGE_SIZE_BYTES) * RERAM_PAGE_SIZE_BYTES; + let total_len = SIGBLOCK_SIZE + test_len; for (i, byte_chunk) in disk_access [k_start..k_start + SIGBLOCK_SIZE + test_len as usize] .chunks(BLOCKLEN_BYTES) @@ -308,6 +313,7 @@ pub fn process_update(perclk: u32) { buffer[1] = BLOCKLEN_BYTES as u32; if i % 8 == 0 { crate::println!("{:x}: {:x?}", buffer[0], &buffer[2..6]); + progress_bar(&mut sh1107, i * BLOCKLEN_BYTES * 100 / total_len); } match write_rram(&mut mbox, &buffer) { Ok(len) => { @@ -325,6 +331,7 @@ pub fn process_update(perclk: u32) { } }; } + progress_bar(&mut sh1107, 100); cache_flush(); crate::println!( "data after: {:x?} .. {:x?}", @@ -333,6 +340,7 @@ pub fn process_update(perclk: u32) { ); } else if file_name.to_ascii_lowercase() == SWAP_NAME { crate::println!("Found swap image"); + // This has a totally different method, as it's writing to SPI FLASH } } } @@ -353,6 +361,41 @@ pub fn process_update(perclk: u32) { sh1107.clear(); } +struct UsizeToString { + buf: [u8; 16], // Enough space for a u32 decimal string + pos: usize, +} + +impl UsizeToString { + pub const fn new() -> Self { Self { buf: [0; 16], pos: 0 } } + + pub fn as_str(&self) -> &str { core::str::from_utf8(&self.buf[..self.pos]).unwrap_or("") } +} + +impl Write for UsizeToString { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + let bytes = s.as_bytes(); + if self.pos + bytes.len() > self.buf.len() { + return Err(core::fmt::Error); + } + self.buf[self.pos..self.pos + bytes.len()].copy_from_slice(bytes); + self.pos += bytes.len(); + Ok(()) + } +} + +fn progress_bar(sh1107: &mut Oled128x128<'_>, percentage: usize) { + gfx::msg(sh1107, "Writing, do not", Point::new(8, 64), Mono::White.into(), Mono::Black.into()); + gfx::msg(sh1107, "reset or turn off!", Point::new(4, 50), Mono::White.into(), Mono::Black.into()); + + let mut usizestr = UsizeToString::new(); + write!(usizestr, "{}%", percentage).ok(); + gfx::msg(sh1107, usizestr.as_str(), Point::new(55, 32), Mono::Black.into(), Mono::White.into()); + sh1107.buffer_swap(); + sh1107.draw(); + sh1107.clear(); +} + pub fn write_rram(mbox: &mut Mbox, data: &[u32]) -> Result { let write_pkt = MboxToCm7Pkt { version: MBOX_PROTOCOL_REV, opcode: ToCm7Op::FlashWrite, data }; match mbox.try_send(write_pkt) { From 83dbb825005d425e64869333917542bd20169590 Mon Sep 17 00:00:00 2001 From: bunnie Date: Fri, 22 Nov 2024 00:55:15 +0800 Subject: [PATCH 41/42] add routines for programming SPI flash --- libs/cramium-hal/src/udma/spim.rs | 178 ++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/libs/cramium-hal/src/udma/spim.rs b/libs/cramium-hal/src/udma/spim.rs index 2d7917a08..f3dec412e 100644 --- a/libs/cramium-hal/src/udma/spim.rs +++ b/libs/cramium-hal/src/udma/spim.rs @@ -3,6 +3,9 @@ use utralib::*; use crate::ifram::{IframRange, UdmaWidths}; use crate::udma::*; +pub const FLASH_PAGE_LEN: usize = 256; +pub const FLASH_SECTOR_LEN: usize = 4096; + // ----------------------------------- SPIM ------------------------------------ /// The SPIM implementation for UDMA does reg-ception, in that they bury @@ -1115,6 +1118,181 @@ impl Spim { offset += chunk.len(); } } + + fn flash_wren(&mut self) { + self.mem_cs(true); + self.mem_send_cmd(0x06); + self.mem_cs(false); + } + + fn flash_wrdi(&mut self) { + self.mem_cs(true); + self.mem_send_cmd(0x04); + self.mem_cs(false); + } + + fn flash_rdsr(&mut self) -> u8 { + self.mem_cs(true); + self.mem_send_cmd(0x05); + let cmd_list = [SpimCmd::RxData(self.mode, SpimWordsPerXfer::Words1, 8, SpimEndian::MsbFirst, 1)]; + // safety: this is safe because rx_buf_phys() slice is only used as a base/bounds reference + unsafe { + self.udma_enqueue( + Bank::Rx, + &self.rx_buf_phys::()[..1], + CFG_EN | CFG_SIZE_8 | CFG_BACKPRESSURE, + ) + }; + self.send_cmd_list(&cmd_list); + while self.udma_busy(Bank::Rx) { + #[cfg(feature = "std")] + xous::yield_slice(); + } + let ret = self.rx_buf()[0]; + + self.mem_cs(false); + ret + } + + fn flash_rdscur(&mut self) -> u8 { + self.mem_cs(true); + self.mem_send_cmd(0x2b); + let cmd_list = [SpimCmd::RxData(self.mode, SpimWordsPerXfer::Words1, 8, SpimEndian::MsbFirst, 1)]; + // safety: this is safe because rx_buf_phys() slice is only used as a base/bounds reference + unsafe { + self.udma_enqueue( + Bank::Rx, + &self.rx_buf_phys::()[0..1], + CFG_EN | CFG_SIZE_8 | CFG_BACKPRESSURE, + ) + }; + self.send_cmd_list(&cmd_list); + while self.udma_busy(Bank::Rx) { + #[cfg(feature = "std")] + xous::yield_slice(); + } + let ret = self.rx_buf()[0]; + + self.mem_cs(false); + ret + } + + fn flash_se(&mut self, sector_address: u32) { + self.mem_cs(true); + self.mem_send_cmd(0x20); + let cmd_list = + [SpimCmd::TxData(self.mode, SpimWordsPerXfer::Words1, 8 as u8, SpimEndian::MsbFirst, 3 as u32)]; + self.tx_buf_mut()[..3].copy_from_slice(§or_address.to_be_bytes()[1..]); + // safety: this is safe because tx_buf_phys() slice is only used as a base/bounds reference + unsafe { self.udma_enqueue(Bank::Tx, &self.tx_buf_phys::()[..3], CFG_EN | CFG_SIZE_8) } + self.send_cmd_list(&cmd_list); + + while self.udma_busy(Bank::Tx) { + #[cfg(feature = "std")] + xous::yield_slice(); + } + self.mem_cs(false); + } + + pub fn flash_erase_sector(&mut self, sector_address: u32) -> u8 { + // enable writes: set wren mode + // crate::println!("flash_erase_sector: {:x}", sector_address); + loop { + self.flash_wren(); + let status = self.flash_rdsr(); + // crate::println!("wren status: {:x}", status); + if status & 0x02 != 0 { + break; + } + } + // issue erase command + self.flash_se(sector_address); + // wait for WIP bit to drop + loop { + let status = self.flash_rdsr(); + // crate::println!("WIP status: {:x}", status); + if status & 0x01 == 0 { + break; + } + } + // get the success code for return + let result = self.flash_rdscur(); + // disable writes: send wrdi + if self.flash_rdsr() & 0x02 != 0 { + loop { + self.flash_wrdi(); + let status = self.flash_rdsr(); + // crate::println!("WRDI status: {:x}", status); + if status & 0x02 == 0 { + break; + } + } + } + result + } + + /// This routine can data that is strictly a multiple of a page length (256 bytes) + pub fn mem_flash_write_page(&mut self, addr: u32, buf: &[u8; FLASH_PAGE_LEN]) -> bool { + // crate::println!("write_page: {:x}, {:x?}", addr, &buf[..8]); + // enable writes: set wren mode + loop { + self.flash_wren(); + let status = self.flash_rdsr(); + // crate::println!("wren status: {:x}", status); + if status & 0x02 != 0 { + break; + } + } + + self.mem_cs(true); + self.mem_send_cmd(0x02); // PP + let cmd_list = [SpimCmd::TxData( + self.mode, + SpimWordsPerXfer::Words1, + 8 as u8, + SpimEndian::MsbFirst, + 3 + FLASH_PAGE_LEN as u32, + )]; + self.tx_buf_mut()[..3].copy_from_slice(&addr.to_be_bytes()[1..]); + self.tx_buf_mut()[3..3 + FLASH_PAGE_LEN].copy_from_slice(buf); + // safety: this is safe because tx_buf_phys() slice is only used as a base/bounds reference + unsafe { + self.udma_enqueue(Bank::Tx, &self.tx_buf_phys::()[..3 + FLASH_PAGE_LEN], CFG_EN | CFG_SIZE_8) + } + self.send_cmd_list(&cmd_list); + + while self.udma_busy(Bank::Tx) { + #[cfg(feature = "std")] + xous::yield_slice(); + } + self.mem_cs(false); + + loop { + // wait while WIP is set + let status = self.flash_rdsr(); + // crate::println!("WIP status: {:x}", status); + if (status & 0x01) == 0 { + break; + } + } + // get the success code for return + let result = self.flash_rdscur(); + if result & 0x20 != 0 { + return false; + } + // disable writes: send wrdi + if self.flash_rdsr() & 0x02 != 0 { + loop { + self.flash_wrdi(); + let status = self.flash_rdsr(); + // crate::println!("WRDI status: {:x}", status); + if status & 0x02 == 0 { + break; + } + } + } + true + } } // Stub retained because it is helpful for debugging some bus contention issues. From ec3d8b5042d1ea376c02c5c45fe10b0245396326 Mon Sep 17 00:00:00 2001 From: bunnie Date: Fri, 22 Nov 2024 00:55:29 +0800 Subject: [PATCH 42/42] handle swap image burning --- loader/src/platform/cramium/update.rs | 131 +++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 4 deletions(-) diff --git a/loader/src/platform/cramium/update.rs b/loader/src/platform/cramium/update.rs index 61eee1dfb..6543cd6fa 100644 --- a/loader/src/platform/cramium/update.rs +++ b/loader/src/platform/cramium/update.rs @@ -1,6 +1,8 @@ use core::convert::TryInto; use core::fmt::Write; +use cramium_hal::board::SPIM_FLASH_IFRAM_ADDR; +use cramium_hal::ifram::IframRange; use cramium_hal::iox::{IoGpio, IoSetup, Iox, IoxPort, IoxValue}; use cramium_hal::mbox::{ MBOX_PROTOCOL_REV, Mbox, MboxError, MboxToCm7Pkt, PAYLOAD_LEN_WORDS, RERAM_PAGE_SIZE_BYTES, ToCm7Op, @@ -9,6 +11,7 @@ use cramium_hal::mbox::{ use cramium_hal::minigfx::{FrameBuffer, Point}; use cramium_hal::sh1107::{Mono, Oled128x128}; use cramium_hal::udma; +use cramium_hal::udma::*; use cramium_hal::usb::driver::UsbDeviceState; use ed25519_dalek::{Digest, Signature, VerifyingKey}; use sha2::Sha512; @@ -263,7 +266,7 @@ pub fn process_update(perclk: u32) { crate::println!("error verifying signature: {:?}", e); gfx::msg( &mut sh1107, - "Image corrupt!", + "Kernel invalid!", Point::new(10, 64), Mono::White.into(), Mono::Black.into(), @@ -338,9 +341,129 @@ pub fn process_update(perclk: u32) { &check_slice[..32], &check_slice[0x1000..0x1000 + 32] ); - } else if file_name.to_ascii_lowercase() == SWAP_NAME { - crate::println!("Found swap image"); + } else if file_name.to_ascii_uppercase() == SWAP_NAME { // This has a totally different method, as it's writing to SPI FLASH + crate::println!("Found swap image"); + let f = fs.get_file(entry.path().clone()).expect("file error"); + let flen = f.file_size(); + let swap_offset = f.sector_offset() as usize * 512; + crate::println!("swap offset: {:x}", swap_offset); + let swap_image = + &usb::conjure_disk()[swap_offset..swap_offset + flen as usize]; + + let ssh: &crate::swap::SwapSourceHeader = (swap_image.as_ptr() + as *const crate::swap::SwapSourceHeader) + .as_ref() + .unwrap(); + // minimal image validation - just check that the version number and magic + // number is correct. + if ssh.version == 0x01_01_0000 + && u32::from_be_bytes(swap_image[0x14..0x18].try_into().unwrap()) + == 0x73776170 + { + crate::println!( + "Burning swap image starting at 0x{:x} of len {} bytes", + swap_offset, + flen + ); + progress_bar(&mut sh1107, 0); + + let udma_global = GlobalConfig::new( + utralib::generated::HW_UDMA_CTRL_BASE as *mut u32, + ); + + // setup the I/O pins + let iox = Iox::new(utralib::generated::HW_IOX_BASE as *mut u32); + let channel = cramium_hal::board::setup_memory_pins(&iox); + udma_global.clock_on(PeriphId::from(channel)); + // safety: this is safe because clocks have been set up + let mut flash_spim = Spim::new_with_ifram( + channel, + // has to be half the clock frequency reaching the block, but + // run it as fast + // as we can run perclk + perclk / 4, + perclk / 2, + SpimClkPol::LeadingEdgeRise, + SpimClkPha::CaptureOnLeading, + SpimCs::Cs0, + 0, + 0, + None, + 256 + 16, /* just enough space to send commands + programming + * page */ + 4096, + Some(6), + None, + IframRange::from_raw_parts( + SPIM_FLASH_IFRAM_ADDR, + SPIM_FLASH_IFRAM_ADDR, + 4096 * 2, + ), + ); + + flash_spim.mem_qpi_mode(true); + + // NOTE: this programming routine only works for write destinations + // that start aligned to an erase page. In this case, `page_no`` + // starts at 0 and increments monatomically, so, this condition is + // satisfied. + for (page_no, chunk) in swap_image.chunks(FLASH_PAGE_LEN).enumerate() + { + // copy chunk to a full-page sized buffer: the last chunk may not + // be exactly a page in length, so this pads it out! + let mut buf = [0u8; 256]; + buf[..chunk.len()].copy_from_slice(chunk); + + // Erase the page if we're touching it the first time + if ((page_no * FLASH_PAGE_LEN) % 0x1000) == 0 { + flash_spim + .flash_erase_sector((page_no * FLASH_PAGE_LEN) as u32); + } + + flash_spim.mem_flash_write_page( + (page_no * FLASH_PAGE_LEN) as u32, + &buf, + ); + + if page_no % 32 == 0 { + progress_bar( + &mut sh1107, + page_no * FLASH_PAGE_LEN * 100 / flen as usize, + ); + crate::println!( + "{:x}: write {:x?}", + page_no * FLASH_PAGE_LEN, + &buf[..16] + ); + let mut rbk = [0u8; 16]; + flash_spim.mem_read( + (page_no * FLASH_PAGE_LEN) as u32, + &mut rbk, + false, + ); + crate::println!("rbk: {:x?}", &rbk); + } + } + progress_bar(&mut sh1107, 100); + } else { + crate::println!( + "Invalid swap image: ver {:x} magic: {:x}", + ssh.version, + u32::from_be_bytes(swap_image[0x14..0x18].try_into().unwrap()) + ); + gfx::msg( + &mut sh1107, + "Swap invalid!", + Point::new(6, 64), + Mono::White.into(), + Mono::Black.into(), + ); + sh1107.buffer_swap(); + sh1107.draw(); + sh1107.clear(); + return; + } } } } @@ -390,7 +513,7 @@ fn progress_bar(sh1107: &mut Oled128x128<'_>, percentage: usize) { let mut usizestr = UsizeToString::new(); write!(usizestr, "{}%", percentage).ok(); - gfx::msg(sh1107, usizestr.as_str(), Point::new(55, 32), Mono::Black.into(), Mono::White.into()); + gfx::msg(sh1107, usizestr.as_str(), Point::new(55, 26), Mono::Black.into(), Mono::White.into()); sh1107.buffer_swap(); sh1107.draw(); sh1107.clear();