diff --git a/Cargo.lock b/Cargo.lock index 42b4d99bd..46d15c6f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,7 +6,7 @@ version = 3 name = "abi" version = "0.1.0" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "byteorder", "phash", "serde", @@ -193,9 +193,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bitvec" @@ -251,7 +251,7 @@ dependencies = [ name = "build-kconfig" version = "0.1.0" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "serde", ] @@ -1255,6 +1255,7 @@ dependencies = [ "drv-lpc55-spi", "drv-lpc55-syscon-api", "drv-lpc55-update-api", + "drv-sp-ctrl-api", "drv-sprot-api", "drv-update-api", "dumper-api", @@ -2015,7 +2016,7 @@ dependencies = [ name = "drv-stm32xx-sys" version = "0.1.0" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "build-stm32xx-sys", "build-util", "cfg-if", @@ -2346,9 +2347,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "gateway-messages" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/management-gateway-service#e727e960d31f62afe6077fdfc405e0ac2379f6b2" +source = "git+https://github.com/oxidecomputer/management-gateway-service#c6e6eb667b0ee061f77a0e40ec10e5e15f2f54cf" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "hubpack", "serde", "serde_repr", @@ -2547,7 +2548,7 @@ dependencies = [ name = "host-sp-messages" version = "0.1.0" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "counters", "drv-i2c-types", "fletcher", @@ -2773,7 +2774,7 @@ dependencies = [ "abi", "anyhow", "armv8-m-mpu", - "bitflags 2.4.2", + "bitflags 2.5.0", "build-kconfig", "build-util", "byteorder", @@ -4856,7 +4857,7 @@ name = "task-thermal" version = "0.1.0" dependencies = [ "anyhow", - "bitflags 2.4.2", + "bitflags 2.5.0", "build-i2c", "build-util", "cortex-m", @@ -5352,7 +5353,7 @@ name = "transceiver-messages" version = "0.1.1" source = "git+https://github.com/oxidecomputer/transceiver-control/#0a18f231372069b74b72f1756e2337186e888dc0" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "hubpack", "serde", ] @@ -5415,9 +5416,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" [[package]] name = "vcell" diff --git a/Cargo.toml b/Cargo.toml index f8b52c3a0..eba43860f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ anyhow = { version = "1.0.31", default-features = false, features = ["std"] } arrayvec = { version = "0.7.4", default-features = false } atty = { version = "0.2", default-features = false } bitfield = { version = "0.13", default-features = false } -bitflags = { version = "2.4.1", default-features = false } +bitflags = { version = "2.5.0", default-features = false } bstringify = { version = "0.1.2", default-features = false } byteorder = { version = "1.3.4", default-features = false } cargo_metadata = { version = "0.12.0", default-features = false } diff --git a/app/oxide-rot-1/app-dev.toml b/app/oxide-rot-1/app-dev.toml index 563230c2a..83fd5630a 100644 --- a/app/oxide-rot-1/app-dev.toml +++ b/app/oxide-rot-1/app-dev.toml @@ -77,14 +77,14 @@ task-slots = ["syscon_driver"] [tasks.sprot] name = "drv-lpc55-sprot-server" priority = 6 -max-sizes = {flash = 48608, ram = 32768} +max-sizes = {flash = 51168, ram = 32768} uses = ["flexcomm8", "bootrom"] -features = ["spi0"] +features = ["spi0", "sp-ctrl"] start = true notifications = ["spi-irq"] interrupts = {"flexcomm8.hs_spi" = "spi-irq"} stacksize = 16384 -task-slots = ["gpio_driver", "syscon_driver", "update_server", "dumper", "attest"] +task-slots = ["gpio_driver", "syscon_driver", "update_server", "dumper", "attest", "swd"] [tasks.sprot.config] pins = [ @@ -110,7 +110,7 @@ uses = ["flexcomm5", "iocon"] start = true stacksize = 1000 task-slots = ["gpio_driver", "syscon_driver"] -notifications = ["spi-irq"] +notifications = ["spi-irq", "timer"] interrupts = {"flexcomm5.irq" = "spi-irq"} [tasks.swd.config] @@ -132,6 +132,7 @@ pins = [ # SCK { pin = { port = 0, pin = 7 }, alt = 3 }, { name = "SP_TO_ROT_JTAG_DETECT_L", pin = { port = 0, pin = 20 }, alt = 0, direction = "input" }, + { name = "ROT_TO_SP_RESET_L", pin = { port = 0, pin = 13 }, alt = 0, value = true, direction = "output", opendrain = "opendrain" }, ] spi_num = 5 diff --git a/app/oxide-rot-1/app.toml b/app/oxide-rot-1/app.toml index bb7db15e7..445e9c031 100644 --- a/app/oxide-rot-1/app.toml +++ b/app/oxide-rot-1/app.toml @@ -69,12 +69,12 @@ task-slots = ["syscon_driver"] name = "drv-lpc55-sprot-server" priority = 6 uses = ["flexcomm8", "bootrom"] -features = ["spi0"] +features = ["spi0", "sp-ctrl"] start = true notifications = ["spi-irq"] interrupts = {"flexcomm8.hs_spi" = "spi-irq"} stacksize = 16384 -task-slots = ["gpio_driver", "syscon_driver", "update_server", "dumper", "attest"] +task-slots = ["gpio_driver", "syscon_driver", "update_server", "dumper", "attest", "swd"] [tasks.sprot.config] pins = [ @@ -100,7 +100,7 @@ uses = ["flexcomm5", "iocon"] start = true stacksize = 1000 task-slots = ["gpio_driver", "syscon_driver"] -notifications = ["spi-irq"] +notifications = ["spi-irq", "timer"] interrupts = {"flexcomm5.irq" = "spi-irq"} [tasks.swd.config] @@ -122,6 +122,7 @@ pins = [ # SCK { pin = { port = 0, pin = 7 }, alt = 3 }, { name = "SP_TO_ROT_JTAG_DETECT_L", pin = { port = 0, pin = 20 }, alt = 0, direction = "input" }, + { name = "ROT_TO_SP_RESET_L", pin = { port = 0, pin = 13 }, alt = 0, value = true, direction = "output", opendrain = "opendrain" }, ] spi_num = 5 diff --git a/app/rot-carrier/app.toml b/app/rot-carrier/app.toml index a42dfac9d..7082b0c4c 100644 --- a/app/rot-carrier/app.toml +++ b/app/rot-carrier/app.toml @@ -108,14 +108,14 @@ task-slots = ["syscon_driver"] [tasks.sprot] name = "drv-lpc55-sprot-server" priority = 6 -max-sizes = {flash = 48608, ram = 32768} +max-sizes = {flash = 49728, ram = 32768} uses = ["flexcomm8", "bootrom"] features = ["spi0"] start = true notifications = ["spi-irq"] interrupts = {"flexcomm8.hs_spi" = "spi-irq"} stacksize = 16384 -task-slots = ["gpio_driver", "syscon_driver", "update_server", "dumper", "attest"] +task-slots = ["gpio_driver", "syscon_driver", "update_server", "attest"] [tasks.sprot.config] pins = [ @@ -133,47 +133,6 @@ pins = [ { name = "SP_RESET", pin = { port = 0, pin = 9}, alt = 0, direction = "input"}, ] -[tasks.swd] -name = "drv-lpc55-swd" -priority = 4 -max-sizes = {flash = 16384, ram = 4096} -uses = ["flexcomm3", "iocon"] -start = true -stacksize = 1000 -notifications = ["spi-irq"] -task-slots = ["gpio_driver", "syscon_driver"] -interrupts = {"flexcomm3.irq" = "spi-irq"} - -[tasks.swd.config] -# MOSI = PIO0_3 -# MISO = PIO0_2 - -# Out = MOSI on, MISO off -out_cfg = [ - { pin = { port = 0, pin = 3 }, alt = 1 }, - { pin = { port = 0, pin = 2 }, alt = 0, mode = "pulldown" }, -] -# In = MISO on, MOSI off -in_cfg = [ - { pin = { port = 0, pin = 2 }, alt = 1 }, - { pin = { port = 0, pin = 3 }, alt = 0, mode = "pulldown" }, -] -pins = [ - # SCK - { pin = { port = 0, pin = 6 }, alt = 1 }, - # CS, not strictly necessary but handy for debugging - # { pin = {port = 0, pin = 20}, alt = 1}, -] -spi_num = 3 - -[tasks.dumper] -name = "task-dumper" -priority = 5 -max-sizes = {flash = 16384, ram = 4096} -start = true -stacksize = 2600 -task-slots = ["swd"] - [tasks.pong] name = "task-pong" priority = 7 @@ -185,21 +144,11 @@ notifications = ["timer"] [tasks.hiffy] name = "task-hiffy" priority = 6 -features = ["lpc55", "gpio", "spctrl", "spi"] +features = ["lpc55", "gpio", "spi"] max-sizes = {flash = 32768, ram = 16384 } stacksize = 2048 start = true -task-slots = ["gpio_driver", "swd", "update_server"] - -[tasks.sp_measure] -name = "task-sp-measure" -priority = 6 -max-sizes = {flash = 131072, ram = 8192} -task-slots = ["swd"] -stacksize = 2048 - -[tasks.sp_measure.config] -binary_path = "../../target/gemini-bu/dist/final.bin" +task-slots = ["gpio_driver", "update_server"] [tasks.attest] name = "task-attest" diff --git a/build/lpc55pins/src/lib.rs b/build/lpc55pins/src/lib.rs index 8f7e1949b..ec63924e2 100644 --- a/build/lpc55pins/src/lib.rs +++ b/build/lpc55pins/src/lib.rs @@ -50,7 +50,9 @@ pub struct PinConfig { digimode: Digimode, #[serde(default)] opendrain: Opendrain, + direction: Option, + value: Option, name: Option, } @@ -155,6 +157,21 @@ pub fn codegen(pins: Vec) -> Result<()> { writeln!(&mut file, "{}", p.to_token_stream())?; writeln!(&mut file, ");")?; + // Output pins can specify their value, which is set before configuring + // their output mode (to avoid glitching). + if let Some(v) = p.value { + assert!( + matches!(p.direction, Some(Direction::Output)), + "can only set value for output pins" + ); + writeln!(&mut file, "iocon.set_val(")?; + writeln!(&mut file, "{}", p.pin.to_token_stream())?; + writeln!( + &mut file, + "{});", + if v { "Value::One" } else { "Value::Zero" } + )?; + } match p.direction { None => (), Some(d) => { diff --git a/drv/lpc55-sprot-server/Cargo.toml b/drv/lpc55-sprot-server/Cargo.toml index 55d6526db..e0ad2b9e4 100644 --- a/drv/lpc55-sprot-server/Cargo.toml +++ b/drv/lpc55-sprot-server/Cargo.toml @@ -22,7 +22,6 @@ drv-lpc55-syscon-api = { path = "../lpc55-syscon-api" } drv-lpc55-update-api = { path = "../lpc55-update-api" } drv-sprot-api = { path = "../sprot-api" } drv-update-api = { path = "../update-api" } -dumper-api = { path = "../../task/dumper-api" } lpc55_romapi = { path = "../../lib/lpc55-romapi" } ringbuf = { path = "../../lib/ringbuf" } static-cell = { path = "../../lib/static-cell" } @@ -30,6 +29,9 @@ task-jefe-api = { path = "../../task/jefe-api" } userlib = { path = "../../sys/userlib" } lpc55-rom-data = { path = "../../lib/lpc55-rom-data" } +drv-sp-ctrl-api = { path = "../sp-ctrl-api", optional = true } +dumper-api = { path = "../../task/dumper-api", optional = true } + [build-dependencies] build-lpc55pins = { path = "../../build/lpc55pins" } build-util = { path = "../../build/util" } @@ -40,6 +42,7 @@ idol = { workspace = true } [features] spi0 = [] no-ipc-counters = ["idol/no-counters"] +sp-ctrl = ["drv-sp-ctrl-api", "dumper-api"] # This section is here to discourage RLS/rust-analyzer from doing test builds, # since test builds don't work for cross compilation. diff --git a/drv/lpc55-sprot-server/src/handler.rs b/drv/lpc55-sprot-server/src/handler.rs index 0dff9ae44..c3963e2a5 100644 --- a/drv/lpc55-sprot-server/src/handler.rs +++ b/drv/lpc55-sprot-server/src/handler.rs @@ -7,12 +7,12 @@ use attest_api::Attest; use crc::{Crc, CRC_32_CKSUM}; use drv_lpc55_update_api::{RotPage, SlotId, Update}; use drv_sprot_api::{ - AttestReq, AttestRsp, CabooseReq, CabooseRsp, DumpReq, DumpRsp, ReqBody, - Request, Response, RotIoStats, RotPageRsp, RotState, RotStatus, RspBody, - SprocketsError, SprotError, SprotProtocolError, UpdateReq, UpdateRsp, - CURRENT_VERSION, MIN_VERSION, REQUEST_BUF_SIZE, RESPONSE_BUF_SIZE, + AttestReq, AttestRsp, CabooseReq, CabooseRsp, DumpReq, ReqBody, Request, + Response, RotIoStats, RotPageRsp, RotState, RotStatus, RspBody, + SprocketsError, SprotError, SprotProtocolError, SwdReq, UpdateReq, + UpdateRsp, CURRENT_VERSION, MIN_VERSION, REQUEST_BUF_SIZE, + RESPONSE_BUF_SIZE, }; -use dumper_api::Dumper; use lpc55_romapi::bootrom; use ringbuf::ringbuf_entry_root as ringbuf_entry; use sprockets_rot::RotSprocket; @@ -22,10 +22,14 @@ mod sprockets; task_slot!(UPDATE_SERVER, update_server); +#[cfg(feature = "sp-ctrl")] task_slot!(DUMPER, dumper); task_slot!(ATTEST, attest); +#[cfg(feature = "sp-ctrl")] +task_slot!(SP_CTRL, swd); + pub const CRC32: Crc = Crc::::new(&CRC_32_CKSUM); /// State that is set once at the start of the driver @@ -56,6 +60,9 @@ pub struct Handler { update: Update, startup_state: StartupState, attest: Attest, + + #[cfg(feature = "sp-ctrl")] + sp_ctrl: drv_sp_ctrl_api::SpCtrl, } impl<'a> Handler { @@ -69,6 +76,9 @@ impl<'a> Handler { max_response_size: RESPONSE_BUF_SIZE.try_into().unwrap_lite(), }, attest: Attest::from(ATTEST.get_task_id()), + + #[cfg(feature = "sp-ctrl")] + sp_ctrl: drv_sp_ctrl_api::SpCtrl::from(SP_CTRL.get_task_id()), } } @@ -273,10 +283,24 @@ impl<'a> Handler { None, )), ReqBody::Dump(DumpReq::V1 { addr }) => { - ringbuf_entry!(Trace::Dump(addr)); - let dumper = Dumper::from(DUMPER.get_task_id()); - let err = dumper.dump(addr).err(); - Ok((RspBody::Dump(DumpRsp::V1 { err }), None)) + #[cfg(feature = "sp-ctrl")] + { + use dumper_api::Dumper; + ringbuf_entry!(Trace::Dump(addr)); + let dumper = Dumper::from(DUMPER.get_task_id()); + let err = dumper.dump(addr).err(); + Ok(( + RspBody::Dump(drv_sprot_api::DumpRsp::V1 { err }), + None, + )) + } + #[cfg(not(feature = "sp-ctrl"))] + { + let _ = addr; + Err(SprotError::Protocol( + SprotProtocolError::BadMessageType, + )) + } } ReqBody::Update(UpdateReq::GetBlockSize) => { let size = self.update.block_size()?; @@ -415,6 +439,60 @@ impl<'a> Handler { }; Ok((RspBody::Attest(rsp), None)) } + ReqBody::Swd(SwdReq::EnableSpSlotWatchdog { time_ms }) => { + // Enabling the watchdog doesn't actually do any SWD work, but + // we'll call `setup()` now to make sure that the SWD system is + // working. + #[cfg(feature = "sp-ctrl")] + { + decode_watchdog_err(self.sp_ctrl.setup().and_then(|()| { + self.sp_ctrl.enable_sp_slot_watchdog(time_ms) + })) + } + + #[cfg(not(feature = "sp-ctrl"))] + { + let _ = time_ms; + Err(SprotError::Protocol( + SprotProtocolError::BadMessageType, + )) + } + } + ReqBody::Swd(SwdReq::DisableSpSlotWatchdog) => { + #[cfg(feature = "sp-ctrl")] + { + self.sp_ctrl.disable_sp_slot_watchdog(); + Ok((RspBody::Ok, None)) + } + + #[cfg(not(feature = "sp-ctrl"))] + Err(SprotError::Protocol(SprotProtocolError::BadMessageType)) + } + ReqBody::Swd(SwdReq::SpSlotWatchdogSupported) => { + #[cfg(feature = "sp-ctrl")] + { + decode_watchdog_err(self.sp_ctrl.setup()) + } + + #[cfg(not(feature = "sp-ctrl"))] + Err(SprotError::Protocol(SprotProtocolError::BadMessageType)) + } } } } + +/// Converts a `Result<(), SpCtrlError>` into a watchdog-flavored response +#[cfg(feature = "sp-ctrl")] +fn decode_watchdog_err<'a>( + s: Result<(), drv_sp_ctrl_api::SpCtrlError>, +) -> Result<(RspBody, Option>), SprotError> { + match s { + Ok(()) => Ok((RspBody::Ok, None)), + Err(drv_sp_ctrl_api::SpCtrlError::DongleDetected) => Err( + SprotError::Watchdog(drv_sprot_api::WatchdogError::DongleDetected), + ), + Err(i) => Err(SprotError::Watchdog( + drv_sprot_api::WatchdogError::Other(u32::from(i)), + )), + } +} diff --git a/drv/lpc55-sprot-server/src/main.rs b/drv/lpc55-sprot-server/src/main.rs index a5b32b795..1180b0d25 100644 --- a/drv/lpc55-sprot-server/src/main.rs +++ b/drv/lpc55-sprot-server/src/main.rs @@ -67,7 +67,6 @@ use handler::Handler; #[derive(Copy, Clone, PartialEq)] pub(crate) enum Trace { None, - Dump(u32), ReceivedBytes(usize), Flush, FlowError, @@ -76,6 +75,9 @@ pub(crate) enum Trace { Err(SprotProtocolError), Stats(RotIoStats), Desynchronized, + + #[cfg(feature = "sp-ctrl")] + Dump(u32), } ringbuf!(Trace, 32, Trace::None); diff --git a/drv/lpc55-swd/build.rs b/drv/lpc55-swd/build.rs index 8dbeaca4b..1b0339115 100644 --- a/drv/lpc55-swd/build.rs +++ b/drv/lpc55-swd/build.rs @@ -100,6 +100,7 @@ fn main() -> Result<(), Box> { )?; build_util::expose_target_board(); + build_util::build_notifications()?; let task_config = build_util::task_config::()?; diff --git a/drv/lpc55-swd/src/main.rs b/drv/lpc55-swd/src/main.rs index 3fc01f2bc..f65fac92f 100644 --- a/drv/lpc55-swd/src/main.rs +++ b/drv/lpc55-swd/src/main.rs @@ -86,6 +86,10 @@ enum Trace { DongleDetected, Dhcsr(u32), ParityFail { data: u32, received_parity: u16 }, + EnabledWatchdog, + DisabledWatchdog, + WatchdogFired, + WatchdogSwap(Result<(), Ack>), } ringbuf!(Trace, 128, Trace::None); @@ -484,16 +488,50 @@ impl idl::InOrderSpCtrlImpl for ServerImpl { Err(_) => Err(SpCtrlError::Fault.into()), } } + + fn enable_sp_slot_watchdog( + &mut self, + _msg: &userlib::RecvMessage, + time_ms: u32, + ) -> Result<(), RequestError> { + ringbuf_entry!(Trace::EnabledWatchdog); + if !self.init { + return Err(SpCtrlError::NeedInit.into()); + } + // This function is idempotent(ish), so we don't care if the timer was + // already running; set the new deadline based on current time. + let deadline = sys_get_timer().now + time_ms as u64; + sys_set_timer(Some(deadline), notifications::TIMER_MASK); + Ok(()) + } + + fn disable_sp_slot_watchdog( + &mut self, + _msg: &userlib::RecvMessage, + ) -> Result<(), RequestError> { + ringbuf_entry!(Trace::DisabledWatchdog); + sys_set_timer(None, notifications::TIMER_MASK); + Ok(()) + } } impl NotificationHandler for ServerImpl { fn current_notification_mask(&self) -> u32 { - // We don't use notifications, don't listen for any. - 0 + notifications::TIMER_MASK } fn handle_notification(&mut self, _bits: u32) { - unreachable!() + ringbuf_entry!(Trace::WatchdogFired); + + // Disable the watchdog timer + sys_set_timer(None, notifications::TIMER_MASK); + + // Attempt to do the swap + let r = self.swap_sp_slot(); + ringbuf_entry!(Trace::WatchdogSwap(r)); + + // Force reinitialization + self.init = false; } } @@ -737,18 +775,10 @@ impl ServerImpl { } fn swd_dongle_detected(&self) -> bool { - cfg_if::cfg_if! { - if #[cfg(any( - target_board = "oxide-rot-1", - ))] { - use drv_lpc55_gpio_api::*; - - let gpio = Pins::from(self.gpio); - gpio.read_val(SP_TO_ROT_JTAG_DETECT_L) == Value::Zero - } else { - false - } - } + use drv_lpc55_gpio_api::*; + + let gpio = Pins::from(self.gpio); + gpio.read_val(SP_TO_ROT_JTAG_DETECT_L) == Value::Zero } fn swd_setup(&mut self) -> Result<(), Ack> { @@ -972,6 +1002,68 @@ impl ServerImpl { fn pin_setup(&mut self) { setup_pins(self.gpio).unwrap_lite(); } + + /// Swaps the currently-active SP slot + fn swap_sp_slot(&mut self) -> Result<(), Ack> { + // All registers and constants are within the FLASH peripheral block, so + // I'm going to skip prefixing everything with `FLASH_`. + + // RM0433 Table 8 + const BASE: u32 = 0x52002000; + + // RM0433 Section 4 + const OPTCR: u32 = BASE + 0x018; + const OPTCR_OPTLOCK_BIT: u32 = 1 << 0; + const OPTCR_OPTSTART_BIT: u32 = 1 << 1; + + // Check whether we have to unlock the flash control register + let optcr = self.read_single_target_addr(OPTCR)?; + if optcr & OPTCR_OPTLOCK_BIT != 0 { + // Keys constants are defined in RM0433 Rev 7 + // Section 4.9.3 + const OPT_KEY1: u32 = 0x0819_2A3B; + const OPT_KEY2: u32 = 0x4C5D_6E7F; + const OPTKEYR: u32 = BASE + 0x008; + self.write_single_target_addr(OPTKEYR, OPT_KEY1)?; + self.write_single_target_addr(OPTKEYR, OPT_KEY2)?; + } + + // Read the current bank swap bit + const OPTSR_CUR: u32 = BASE + 0x01C; + const OPTSR_SWAP_BANK_OPT_BIT: u32 = 1 << 31; + const OPTSR_OPT_BUSY_BIT: u32 = 1 << 0; + let optsr_cur = self.read_single_target_addr(OPTSR_CUR)?; + + // Mask and toggle the bank swap bit + let new_swap = + (optsr_cur & OPTSR_SWAP_BANK_OPT_BIT) ^ OPTSR_SWAP_BANK_OPT_BIT; + + // Modify the bank swap bit in OPTSR_PRG + const OPTSR_PRG: u32 = BASE + 0x020; + let mut optsr_prg = self.read_single_target_addr(OPTSR_PRG)?; + optsr_prg = (optsr_prg & !OPTSR_SWAP_BANK_OPT_BIT) | new_swap; + self.write_single_target_addr(OPTSR_PRG, optsr_prg)?; + + // Start programming option bits + let mut optcr = self.read_single_target_addr(OPTCR)?; + optcr |= OPTCR_OPTSTART_BIT; + self.write_single_target_addr(OPTCR, optcr)?; + + // Wait for option bit programming to finish + while self.read_single_target_addr(OPTSR_CUR)? & OPTSR_OPT_BUSY_BIT != 0 + { + hl::sleep_for(5); + } + + // Reset the STM32, causing it to reboot into the newly-set slot + use drv_lpc55_gpio_api::{Pins, Value}; + let gpio = Pins::from(self.gpio); + gpio.set_val(ROT_TO_SP_RESET_L, Value::Zero); + hl::sleep_for(10); + gpio.set_val(ROT_TO_SP_RESET_L, Value::One); + + Ok(()) + } } #[export_name = "main"] @@ -1015,3 +1107,4 @@ mod idl { include!(concat!(env!("OUT_DIR"), "/pin_config.rs")); include!(concat!(env!("OUT_DIR"), "/swd.rs")); +include!(concat!(env!("OUT_DIR"), "/notifications.rs")); diff --git a/drv/sprot-api/src/error.rs b/drv/sprot-api/src/error.rs index a5c959de0..3336ceeb6 100644 --- a/drv/sprot-api/src/error.rs +++ b/drv/sprot-api/src/error.rs @@ -15,8 +15,10 @@ use hubpack::SerializedSize; use serde::{Deserialize, Serialize}; use gateway_messages::{ - RotError, SpError, SprocketsError as GwSprocketsErr, + RotError, RotWatchdogError as GwRotWatchdogError, SpError, + SprocketsError as GwSprocketsErr, SprotProtocolError as GwSprotProtocolError, + WatchdogError as GwWatchdogError, }; use idol_runtime::RequestError; @@ -37,6 +39,7 @@ pub enum SprotError { Spi(#[count(children)] SpiError), Update(#[count(children)] UpdateError), Sprockets(#[count(children)] SprocketsError), + Watchdog(#[count(children)] WatchdogError), } impl From for SpError { @@ -46,6 +49,7 @@ impl From for SpError { SprotError::Spi(e) => Self::Spi(e.into()), SprotError::Update(e) => Self::Update(e.into()), SprotError::Sprockets(e) => Self::Sprockets(e.into()), + SprotError::Watchdog(e) => Self::Watchdog(e.into()), } } } @@ -57,6 +61,7 @@ impl From for RotError { SprotError::Spi(e) => Self::Spi(e.into()), SprotError::Update(e) => Self::Update(e.into()), SprotError::Sprockets(e) => Self::Sprockets(e.into()), + SprotError::Watchdog(e) => Self::Watchdog(e.into()), } } } @@ -299,3 +304,33 @@ impl From for AttestOrSprotError { AttestOrSprotError::Attest(AttestError::TaskRestarted) } } + +// Added in sprot protocol version 5 +#[derive( + Copy, + Clone, + Debug, + Eq, + PartialEq, + Serialize, + Deserialize, + SerializedSize, + counters::Count, +)] +pub enum WatchdogError { + /// Could not control the SP over SWD + DongleDetected, + /// Raw `SpCtrlError` value + Other(u32), +} + +impl From for GwWatchdogError { + fn from(s: WatchdogError) -> Self { + match s { + WatchdogError::DongleDetected => { + Self::Rot(GwRotWatchdogError::DongleDetected) + } + WatchdogError::Other(i) => Self::Rot(GwRotWatchdogError::Other(i)), + } + } +} diff --git a/drv/sprot-api/src/lib.rs b/drv/sprot-api/src/lib.rs index f9726eaed..e0cd98aae 100644 --- a/drv/sprot-api/src/lib.rs +++ b/drv/sprot-api/src/lib.rs @@ -15,6 +15,7 @@ use dumper_api::DumperError; pub use error::{ AttestOrSprotError, CabooseOrSprotError, DumpOrSprotError, RawCabooseOrSprotError, SprocketsError, SprotError, SprotProtocolError, + WatchdogError, }; use crc::{Crc, CRC_16_XMODEM}; @@ -47,7 +48,7 @@ pub const MIN_VERSION: Version = Version(2); /// Code between the `CURRENT_VERSION` and `MIN_VERSION` must remain /// compatible. Use the rules described in the comments for [`Msg`] to evolve /// the protocol such that this remains true. -pub const CURRENT_VERSION: Version = Version(4); +pub const CURRENT_VERSION: Version = Version(5); /// We allow room in the buffer for message evolution pub const REQUEST_BUF_SIZE: usize = 1024; @@ -344,6 +345,24 @@ pub enum ReqBody { Attest(AttestReq), // Added in sprot protocol version 4 RotPage { page: RotPage }, + // Added in sprot protocol version 5 + Swd(SwdReq), +} + +// Added in sprot protocol version 5 +#[derive(Clone, Serialize, Deserialize, SerializedSize)] +pub enum SwdReq { + EnableSpSlotWatchdog { + time_ms: u32, + }, + DisableSpSlotWatchdog, + + /// Checks whether the SP slot watchdog is supported + /// + /// In practice, this calls `drv_lpc55_swd::ServerImpl::setup` to make sure + /// that there's no debugger attached that would prevent us from talking to + /// the SP. + SpSlotWatchdogSupported, } /// Instruct the RoT to take a dump of the SP via SWD diff --git a/drv/stm32h7-sprot-server/src/main.rs b/drv/stm32h7-sprot-server/src/main.rs index bd77f8520..c3991b1e7 100644 --- a/drv/stm32h7-sprot-server/src/main.rs +++ b/drv/stm32h7-sprot-server/src/main.rs @@ -1162,6 +1162,40 @@ impl idl::InOrderSpRotImpl for ServerImpl { Err(e) => Err(AttestOrSprotError::Sprot(e).into()), } } + + fn enable_sp_slot_watchdog( + &mut self, + _msg: &userlib::RecvMessage, + time_ms: u32, + ) -> Result<(), idol_runtime::RequestError> { + let body = ReqBody::Swd(SwdReq::EnableSpSlotWatchdog { time_ms }); + let tx_size = Request::pack(&body, self.tx_buf); + let rsp = self.do_send_recv_retries(tx_size, TIMEOUT_QUICK, 1)?; + rsp.body?; + Ok(()) + } + + fn disable_sp_slot_watchdog( + &mut self, + _msg: &userlib::RecvMessage, + ) -> Result<(), idol_runtime::RequestError> { + let body = ReqBody::Swd(SwdReq::DisableSpSlotWatchdog); + let tx_size = Request::pack(&body, self.tx_buf); + let rsp = self.do_send_recv_retries(tx_size, TIMEOUT_QUICK, 1)?; + rsp.body?; + Ok(()) + } + + fn sp_slot_watchdog_supported( + &mut self, + _msg: &userlib::RecvMessage, + ) -> Result<(), idol_runtime::RequestError> { + let body = ReqBody::Swd(SwdReq::SpSlotWatchdogSupported); + let tx_size = Request::pack(&body, self.tx_buf); + let rsp = self.do_send_recv_retries(tx_size, TIMEOUT_QUICK, 1)?; + rsp.body?; + Ok(()) + } } impl NotificationHandler for ServerImpl { diff --git a/idl/sp-ctrl.idol b/idl/sp-ctrl.idol index 737f5db66..075be7e10 100644 --- a/idl/sp-ctrl.idol +++ b/idl/sp-ctrl.idol @@ -89,5 +89,20 @@ Interface( err: CLike("SpCtrlError"), ) ), + "enable_sp_slot_watchdog": ( + doc: "Enable a watchdog that will reset the SP into the inactive slot", + args: { + "time_ms" : "u32", + }, + reply: Result( + ok: "()", + err: CLike("SpCtrlError"), + ), + ), + "disable_sp_slot_watchdog": ( + doc: "Disable the SP slot watchdog", + reply: Simple("()"), + idempotent: true, + ), } ) diff --git a/idl/sprot.idol b/idl/sprot.idol index 822646b11..b6fc25ec4 100644 --- a/idl/sprot.idol +++ b/idl/sprot.idol @@ -278,5 +278,32 @@ Interface( encoding: Hubpack, idempotent: true, ), + "enable_sp_slot_watchdog": ( + doc: "Enable a watchdog that will reset the SP into the alternate slot", + args: { + "time_ms" : "u32", + }, + reply: Result( + ok: "()", + err: Complex("SprotError"), + ), + encoding: Hubpack, + ), + "disable_sp_slot_watchdog": ( + doc: "Disable the SP slot watchdog", + reply: Result( + ok: "()", + err: Complex("SprotError"), + ), + encoding: Hubpack, + ), + "sp_slot_watchdog_supported": ( + doc: "Checks if the SP slot watchdog is supported", + reply: Result( + ok: "()", + err: Complex("SprotError"), + ), + encoding: Hubpack, + ), } ) diff --git a/task/control-plane-agent/src/mgs_common.rs b/task/control-plane-agent/src/mgs_common.rs index bb45ab4da..f92e56816 100644 --- a/task/control-plane-agent/src/mgs_common.rs +++ b/task/control-plane-agent/src/mgs_common.rs @@ -3,8 +3,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use crate::{ - inventory::Inventory, update::rot::RotUpdate, update::sp::SpUpdate, Log, - MgsMessage, + inventory::Inventory, + update::{rot::RotUpdate, sp::SpUpdate, ComponentUpdater}, + Log, MgsMessage, }; use drv_caboose::{CabooseError, CabooseReader}; use drv_sprot_api::{ @@ -15,8 +16,8 @@ use drv_stm32h7_update_api::Update; use gateway_messages::{ CfpaPage, DiscoverResponse, PowerState, RotError, RotRequest, RotResponse, RotSlotId, RotStateV2, SensorReading, SensorRequest, SensorRequestKind, - SensorResponse, SpComponent, SpError, SpPort, SpStateV2, - VpdError as GatewayVpdError, + SensorResponse, SpComponent, SpError, SpPort, SpStateV2, UpdateStatus, + VpdError as GatewayVpdError, WatchdogError, }; use ringbuf::ringbuf_entry_root as ringbuf_entry; use static_assertions::const_assert; @@ -481,6 +482,52 @@ impl MgsCommon { Ok(cnt) } + + pub(crate) fn reset_component_trigger_with_watchdog( + &mut self, + component: SpComponent, + time_ms: u32, + ) -> Result<(), SpError> { + if self.reset_component_requested != Some(component) { + return Err(SpError::ResetComponentTriggerWithoutPrepare); + } + if !matches!(self.sp_update.status(), UpdateStatus::Complete(..)) { + return Err(SpError::Watchdog(WatchdogError::NoCompletedUpdate)); + } + + if component == SpComponent::SP_ITSELF { + self.sprot.enable_sp_slot_watchdog(time_ms)?; + task_jefe_api::Jefe::from(crate::JEFE.get_task_id()) + .request_reset(); + panic!(); // we really really shouldn't get here + } else { + Err(SpError::RequestUnsupportedForComponent) + } + } + + pub(crate) fn disable_component_watchdog( + &mut self, + component: SpComponent, + ) -> Result<(), SpError> { + if component == SpComponent::SP_ITSELF { + self.sprot.disable_sp_slot_watchdog()?; + } else { + return Err(SpError::RequestUnsupportedForComponent); + } + Ok(()) + } + + pub(crate) fn component_watchdog_supported( + &mut self, + component: SpComponent, + ) -> Result<(), SpError> { + if component == SpComponent::SP_ITSELF { + self.sprot.sp_slot_watchdog_supported()?; + } else { + return Err(SpError::RequestUnsupportedForComponent); + } + Ok(()) + } } fn translate_sensor_nodata( diff --git a/task/control-plane-agent/src/mgs_gimlet.rs b/task/control-plane-agent/src/mgs_gimlet.rs index dc222cb9d..5c4f871b1 100644 --- a/task/control-plane-agent/src/mgs_gimlet.rs +++ b/task/control-plane-agent/src/mgs_gimlet.rs @@ -1131,6 +1131,30 @@ impl SpHandler for MgsHandler { ) -> Result { self.common.vpd_lock_status_all(buf) } + + fn reset_component_trigger_with_watchdog( + &mut self, + component: SpComponent, + time_ms: u32, + ) -> Result<(), SpError> { + self.common + .reset_component_trigger_with_watchdog(component, time_ms) + .map(|_| ()) + } + + fn disable_component_watchdog( + &mut self, + component: SpComponent, + ) -> Result<(), SpError> { + self.common.disable_component_watchdog(component) + } + + fn component_watchdog_supported( + &mut self, + component: SpComponent, + ) -> Result<(), SpError> { + self.common.component_watchdog_supported(component) + } } struct UsartHandler { diff --git a/task/control-plane-agent/src/mgs_psc.rs b/task/control-plane-agent/src/mgs_psc.rs index 59e4d0914..4d58e4b87 100644 --- a/task/control-plane-agent/src/mgs_psc.rs +++ b/task/control-plane-agent/src/mgs_psc.rs @@ -648,4 +648,27 @@ impl SpHandler for MgsHandler { ) -> Result { self.common.vpd_lock_status_all(buf) } + + fn reset_component_trigger_with_watchdog( + &mut self, + component: SpComponent, + time_ms: u32, + ) -> Result<(), SpError> { + self.common + .reset_component_trigger_with_watchdog(component, time_ms) + } + + fn disable_component_watchdog( + &mut self, + component: SpComponent, + ) -> Result<(), SpError> { + self.common.disable_component_watchdog(component) + } + + fn component_watchdog_supported( + &mut self, + component: SpComponent, + ) -> Result<(), SpError> { + self.common.component_watchdog_supported(component) + } } diff --git a/task/control-plane-agent/src/mgs_sidecar.rs b/task/control-plane-agent/src/mgs_sidecar.rs index d9465be78..d6d6849ff 100644 --- a/task/control-plane-agent/src/mgs_sidecar.rs +++ b/task/control-plane-agent/src/mgs_sidecar.rs @@ -757,6 +757,29 @@ impl SpHandler for MgsHandler { ) -> Result { self.common.vpd_lock_status_all(buf) } + + fn reset_component_trigger_with_watchdog( + &mut self, + component: SpComponent, + time_ms: u32, + ) -> Result<(), SpError> { + self.common + .reset_component_trigger_with_watchdog(component, time_ms) + } + + fn disable_component_watchdog( + &mut self, + component: SpComponent, + ) -> Result<(), SpError> { + self.common.disable_component_watchdog(component) + } + + fn component_watchdog_supported( + &mut self, + component: SpComponent, + ) -> Result<(), SpError> { + self.common.component_watchdog_supported(component) + } } // Helper function for `.map_err()`; we can't use `?` because we can't implement