diff --git a/Cargo.lock b/Cargo.lock index 1bc0afbcf4..0da6f50964 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3098,6 +3098,7 @@ dependencies = [ "cortex-m-rt", "drv-stm32h7-startup", "kern", + "ringbuf", "stm32h7", ] @@ -5192,6 +5193,25 @@ dependencies = [ "zerocopy-derive 0.8.25", ] +[[package]] +name = "task-bork" +version = "0.1.0" +dependencies = [ + "build-util", + "cortex-m", + "counters", + "drv-sprot-api", + "drv-stm32xx-sys-api", + "idol", + "idol-runtime", + "num-traits", + "ringbuf", + "stm32h7", + "userlib", + "zerocopy 0.8.25", + "zerocopy-derive 0.8.25", +] + [[package]] name = "task-config" version = "0.1.0" diff --git a/app/cosmo/base.toml b/app/cosmo/base.toml index 64221b4e75..117d267b08 100644 --- a/app/cosmo/base.toml +++ b/app/cosmo/base.toml @@ -6,8 +6,8 @@ fwid = true [kernel] name = "cosmo" -requires = {flash = 32768, ram = 8192} -features = ["dump"] +requires = {flash = 32768, ram = 16384} +features = ["dump", "traptrace"] [mmio] peripheral-region = "fmc_nor_psram_bank_1" @@ -357,6 +357,16 @@ notifications = ["jefe-state-change", "timer"] task-slots = ["jefe", "spartan7_loader", "packrat", "sensor"] uses = ["mmio_dimms"] +[tasks.bork] +name = "task-bork" +priority = 10 +max-sizes = {flash = 16384, ram = 16384 } +start = true +notifications = ["timer"] +task-slots = ["sprot", "sys"] +features = ["h753"] +uses = ["rtc"] + [tasks.auxflash] name = "drv-auxflash-server" priority = 3 diff --git a/app/cosmo/src/tracing.rs b/app/cosmo/src/tracing.rs index 224aa30f7c..d36b576bd2 100644 --- a/app/cosmo/src/tracing.rs +++ b/app/cosmo/src/tracing.rs @@ -70,6 +70,8 @@ fn timer_isr_exit() { } fn context_switch(addr: usize) { + let rtc = unsafe { &*stm32h7::stm32h753::RTC::ptr() }; + rtc.bkpr[0].modify(|_, w| w.bkp().bits(addr as u32)); trace(Event::ContextSwitch(addr)); } @@ -86,5 +88,26 @@ static TRACING: kern::profiling::EventsTable = kern::profiling::EventsTable { }; pub fn table() -> &'static kern::profiling::EventsTable { + let rtc = unsafe { &*stm32h7::stm32h753::RTC::ptr() }; + + // Unlock + rtc.wpr.write(|w| w.key().bits(0xca)); + rtc.wpr.write(|w| w.key().bits(0x53)); + + // don't erase! + rtc.tampcr.modify(|_, w| { + w.tamp3noerase() + .set_bit() + .tamp2noerase() + .set_bit() + .tamp1noerase() + .set_bit() + }); + + // Save our last state + let last = rtc.bkpr[0].read().bkp().bits(); + // And rotate + rtc.bkpr[1].modify(|_, w| w.bkp().bits(last)); + &TRACING } diff --git a/app/grapefruit/Cargo.toml b/app/grapefruit/Cargo.toml index d9b0a1cb92..f278bfcd9f 100644 --- a/app/grapefruit/Cargo.toml +++ b/app/grapefruit/Cargo.toml @@ -5,6 +5,7 @@ readme = "README.md" version = "0.1.0" [features] +traptrace = ["ringbuf"] dump = ["kern/dump"] [dependencies] @@ -12,6 +13,7 @@ cortex-m = { workspace = true } cortex-m-rt = { workspace = true } cfg-if = { workspace = true } stm32h7 = { workspace = true, features = ["rt", "stm32h753"] } +ringbuf = { path = "../../lib/ringbuf", optional = true } drv-stm32h7-startup = { path = "../../drv/stm32h7-startup", features = ["h753"] } kern = { path = "../../sys/kern" } diff --git a/app/grapefruit/base.toml b/app/grapefruit/base.toml index ed3a3eab14..508a960f81 100644 --- a/app/grapefruit/base.toml +++ b/app/grapefruit/base.toml @@ -14,7 +14,7 @@ register-map = "../../drv/spartan7-loader/grapefruit/gfruit_top.json" [kernel] name = "grapefruit" requires = {flash = 32768, ram = 8192} -features = ["dump"] +features = ["dump", "traptrace"] [caboose] tasks = ["control_plane_agent"] @@ -337,6 +337,16 @@ task-slots = ["hash_driver", "spartan7_loader"] stacksize = 4000 notifications = ["timer"] +[tasks.bork] +name = "task-bork" +priority = 8 +max-sizes = {flash = 16384, ram = 16384 } +start = true +notifications = ["timer"] +task-slots = ["sprot", "sys"] +features = ["h753"] +uses = ["rtc"] + [config.net] # VLAN configuration [config.net.vlans.sidecar1] diff --git a/app/grapefruit/build.rs b/app/grapefruit/build.rs index 2800895934..7e465bc6b9 100644 --- a/app/grapefruit/build.rs +++ b/app/grapefruit/build.rs @@ -4,4 +4,5 @@ fn main() { build_util::expose_target_board(); + build_util::expose_m_profile().unwrap(); } diff --git a/app/grapefruit/src/main.rs b/app/grapefruit/src/main.rs index 91439dd1c3..9305bf2010 100644 --- a/app/grapefruit/src/main.rs +++ b/app/grapefruit/src/main.rs @@ -17,12 +17,18 @@ use drv_stm32h7_startup::ClockConfig; use cortex_m_rt::entry; +#[cfg(feature = "traptrace")] +mod tracing; + #[entry] fn main() -> ! { system_init(); const CYCLES_PER_MS: u32 = 400_000; + #[cfg(feature = "traptrace")] + kern::profiling::configure_events_table(tracing::table()); + unsafe { kern::startup::start_kernel(CYCLES_PER_MS) } } diff --git a/app/grapefruit/src/tracing.rs b/app/grapefruit/src/tracing.rs new file mode 100644 index 0000000000..d36b576bd2 --- /dev/null +++ b/app/grapefruit/src/tracing.rs @@ -0,0 +1,113 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +// +// If you are cutting-and-pasting this code into another kernel (and that +// kernel is armv6m), it is hoped that you will cut-and-paste this compile +// error along with it and take heed of its admonition! +// +#[cfg(not(any(armv7m, armv8m)))] +compile_error!("ringbuf is unsound in the kernel on armv6m"); + +use ringbuf::*; + +#[derive(Copy, Clone, PartialEq)] +enum Event { + SyscallEnter(u32), + SyscallExit, + SecondarySyscallEnter, + SecondarySyscallExit, + IsrEnter, + IsrExit, + TimerIsrEnter, + TimerIsrExit, + ContextSwitch(usize), +} + +#[derive(Copy, Clone, PartialEq)] +enum Trace { + Event(Event), + None, +} + +ringbuf!(Trace, 128, Trace::None); + +fn trace(event: Event) { + ringbuf_entry!(Trace::Event(event)); +} + +fn syscall_enter(nr: u32) { + trace(Event::SyscallEnter(nr)); +} + +fn syscall_exit() { + trace(Event::SyscallExit); +} + +fn secondary_syscall_enter() { + trace(Event::SecondarySyscallEnter); +} + +fn secondary_syscall_exit() { + trace(Event::SecondarySyscallExit); +} + +fn isr_enter() { + trace(Event::IsrEnter); +} + +fn isr_exit() { + trace(Event::IsrExit); +} + +fn timer_isr_enter() { + trace(Event::TimerIsrEnter); +} + +fn timer_isr_exit() { + trace(Event::TimerIsrExit); +} + +fn context_switch(addr: usize) { + let rtc = unsafe { &*stm32h7::stm32h753::RTC::ptr() }; + rtc.bkpr[0].modify(|_, w| w.bkp().bits(addr as u32)); + trace(Event::ContextSwitch(addr)); +} + +static TRACING: kern::profiling::EventsTable = kern::profiling::EventsTable { + syscall_enter, + syscall_exit, + secondary_syscall_enter, + secondary_syscall_exit, + isr_enter, + isr_exit, + timer_isr_enter, + timer_isr_exit, + context_switch, +}; + +pub fn table() -> &'static kern::profiling::EventsTable { + let rtc = unsafe { &*stm32h7::stm32h753::RTC::ptr() }; + + // Unlock + rtc.wpr.write(|w| w.key().bits(0xca)); + rtc.wpr.write(|w| w.key().bits(0x53)); + + // don't erase! + rtc.tampcr.modify(|_, w| { + w.tamp3noerase() + .set_bit() + .tamp2noerase() + .set_bit() + .tamp1noerase() + .set_bit() + }); + + // Save our last state + let last = rtc.bkpr[0].read().bkp().bits(); + // And rotate + rtc.bkpr[1].modify(|_, w| w.bkp().bits(last)); + + &TRACING +} diff --git a/chips/stm32h7/chip.toml b/chips/stm32h7/chip.toml index 04091700e2..1c15469b76 100644 --- a/chips/stm32h7/chip.toml +++ b/chips/stm32h7/chip.toml @@ -110,6 +110,10 @@ address = 0x52005000 size = 4096 interrupts = { irq = 92 } +[rtc] +address = 0x58004000 +size = 1024 + [eth] address = 0x40028000 size = 0x2000 diff --git a/drv/stm32h7-startup/src/lib.rs b/drv/stm32h7-startup/src/lib.rs index e89549f9c0..cffb8fb268 100644 --- a/drv/stm32h7-startup/src/lib.rs +++ b/drv/stm32h7-startup/src/lib.rs @@ -31,6 +31,7 @@ unsafe fn system_pre_init() { // convert it to a reference. We can have a reference to PWR because it's // hardware, and is thus not uninitialized. let pwr = &*device::PWR::ptr(); + pwr.cr1.modify(|_, w| w.dbp().set_bit()); // Poke CR3 to enable the LDO and prevent further writes. pwr.cr3.modify(|_, w| w.ldoen().set_bit()); @@ -324,6 +325,10 @@ pub fn system_init_custom( // spin } + p.RCC.cfgr.modify(|_, w| w.rtcpre().bits(0b100)); + + p.RCC.bdcr.modify(|_, w| w.rtcen().set_bit().rtcsel().lse()); + // set RNG clock to PLL1 clock #[cfg(any(feature = "h743", feature = "h753"))] p.RCC.d2ccip2r.modify(|_, w| w.rngsel().pll1_q()); diff --git a/idl/bork.idol b/idl/bork.idol new file mode 100644 index 0000000000..b658c5e4b6 --- /dev/null +++ b/idl/bork.idol @@ -0,0 +1,23 @@ +// Cosmo SPD task API +// +// Right now, this doesn't actually do anything, but it lets us use the idol +// runtime for notifications / dispatch. +Interface( + name: "Bork", + ops: { + "ping": ( + doc: "blocks until the server is running", + args: {}, + reply: Simple("()"), + idempotent: true, + ), + "wave_bye_bye": ( + doc: "wave bye bye", + args: {}, + reply: Simple("()"), + idempotent: true, + ), + + }, +) + diff --git a/sys/kern/build.rs b/sys/kern/build.rs index c009b13095..3ef7c5c232 100644 --- a/sys/kern/build.rs +++ b/sys/kern/build.rs @@ -425,7 +425,7 @@ fn generate_statics(gen: &Generated) -> Result<()> { #[no_mangle] pub static HUBRIS_IMAGE_ID: u64 = #image_id; - static mut HUBRIS_TASK_TABLE_SPACE: + pub(crate) static mut HUBRIS_TASK_TABLE_SPACE: core::mem::MaybeUninit<[crate::task::Task; HUBRIS_TASK_COUNT]> = core::mem::MaybeUninit::uninit(); }, diff --git a/sys/kern/src/arch/arm_m.rs b/sys/kern/src/arch/arm_m.rs index b00a516ab7..efc9a5d0a9 100644 --- a/sys/kern/src/arch/arm_m.rs +++ b/sys/kern/src/arch/arm_m.rs @@ -1346,6 +1346,21 @@ enum FaultType { #[cfg(any(armv7m, armv8m))] global_asm! {" + .section .text.im_dead + .globl im_dead + .type im_dead,function + .cpu cortex-m4 @ least common denominator we support + im_dead: + @ lie down try not to cry cry a lot + movw r0, #0xed0c + movt r0, #0xe000 + movw r1, #0x0004 + movt r1, #0x05fa + str.w r1, [r0] + 1: + b 1b + + .section .text.configurable_fault .globl configurable_fault .type configurable_fault,function @@ -1416,6 +1431,12 @@ global_asm! {" .type UsageFault,function UsageFault: b configurable_fault + + .section .text.HardFault + .globl HardFault + .type HardFault,function + HardFault: + b im_dead ", } diff --git a/sys/kern/src/profiling.rs b/sys/kern/src/profiling.rs index c8c6b3aa18..c6fcd7d6ca 100644 --- a/sys/kern/src/profiling.rs +++ b/sys/kern/src/profiling.rs @@ -170,7 +170,11 @@ pub(crate) fn event_timer_isr_exit() { } pub(crate) fn event_context_switch(tcb: usize) { + // HACK too hard to figure out the task ID from the base of the TCB + let base = core::ptr::addr_of_mut!(crate::startup::HUBRIS_TASK_TABLE_SPACE) + as *mut u32 as u32 as usize; + let size = core::mem::size_of::(); if let Some(t) = table() { - (t.context_switch)(tcb) + (t.context_switch)((tcb - base) / size) } } diff --git a/task/bork/Cargo.toml b/task/bork/Cargo.toml new file mode 100644 index 0000000000..f815306bdc --- /dev/null +++ b/task/bork/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "task-bork" +version = "0.1.0" +edition = "2021" + +[dependencies] +# The idle task cannot panic, so we deliberately don't request panic-messages +# to keep the binary tiny. +userlib = { path = "../../sys/userlib" } +cortex-m = { workspace = true } +drv-sprot-api = { path = "../../drv/sprot-api" } +drv-stm32xx-sys-api = { path = "../../drv/stm32xx-sys-api" } +idol-runtime.workspace = true +zerocopy.workspace = true +zerocopy-derive.workspace = true +ringbuf = { path = "../../lib/ringbuf" } +counters = { path = "../../lib/counters" } +num-traits.workspace = true +stm32h7 = { workspace = true, features = ["stm32h753"] } + +[features] +h753 = ["drv-stm32xx-sys-api/h753"] + +[build-dependencies] +build-util = { path = "../../build/util" } +idol = { workspace = true } + +# This section is here to discourage RLS/rust-analyzer from doing test builds, +# since test builds don't work for cross compilation. +[[bin]] +name = "task-bork" +test = false +bench = false + +[lints] +workspace = true diff --git a/task/bork/build.rs b/task/bork/build.rs new file mode 100644 index 0000000000..af0c6aa8b8 --- /dev/null +++ b/task/bork/build.rs @@ -0,0 +1,16 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +fn main() -> Result<(), Box> { + build_util::expose_target_board(); + build_util::build_notifications()?; + + idol::Generator::new().build_server_support( + "../../idl/bork.idol", + "server_stub.rs", + idol::server::ServerStyle::InOrder, + )?; + + Ok(()) +} diff --git a/task/bork/src/main.rs b/task/bork/src/main.rs new file mode 100644 index 0000000000..4dd34356a1 --- /dev/null +++ b/task/bork/src/main.rs @@ -0,0 +1,97 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#![no_std] +#![no_main] + +// Make sure we actually link in userlib, despite not using any of it explicitly +// - we need it for our _start routine. + +use idol_runtime::{NotificationHandler, RequestError}; +use ringbuf::*; +use stm32h7::stm32h753 as device; +use userlib::{set_timer_relative, task_slot, RecvMessage}; + +//const WATCHDOG_INTERVAL: u32 = 5000; + +const TIMER_INTERVAL: u32 = 100; + +task_slot!(SPROT, sprot); +task_slot!(SYS, sys); + +#[derive(Copy, Clone, PartialEq, Count)] +enum Trace { + LastId(u32), + //Dogerr(drv_sprot_api::SprotError), + None, +} +counted_ringbuf!(Trace, 8, Trace::None); + +struct ServerImpl { + //sprot: drv_sprot_api::SpRot, +} + +impl NotificationHandler for ServerImpl { + fn current_notification_mask(&self) -> u32 { + notifications::TIMER_MASK + } + + fn handle_notification(&mut self, bits: u32) { + if (bits & notifications::TIMER_MASK) == 0 { + return; + } + } +} + +impl idl::InOrderBorkImpl for ServerImpl { + fn ping( + &mut self, + _mgs: &RecvMessage, + ) -> Result<(), RequestError> { + Ok(()) + } + + #[allow(unreachable_code)] + fn wave_bye_bye( + &mut self, + _mgs: &RecvMessage, + ) -> Result<(), RequestError> { + loop { + cortex_m::asm::nop(); + } + Ok(()) + } +} + +#[export_name = "main"] +fn main() -> ! { + //let sys = sys_api::Sys::from(SYS.get_task_id()); + //sys.enable_clock(sys_api::Peripheral::RtcApb); + //sys.leave_reset(sys_api::Peripheral::RtcApb); + + let rtc = unsafe { &*device::RTC::ptr() }; + + //rtc.wpr.write(|w| w.key().bits(0xca)); + //rtc.wpr.write(|w| w.key().bits(0x53)); + + //rtc.tampcr.modify(|_, w| w.tamp3noerase().set_bit().tamp2noerase().set_bit().tamp1noerase().set_bit()); + + let last = rtc.bkpr[1].read().bkp().bits(); + ringbuf_entry!(Trace::LastId(last)); + + let mut buffer = [0; idl::INCOMING_SIZE]; + let mut server = ServerImpl { + //sprot: drv_sprot_api::SpRot::from(SPROT.get_task_id()), + }; + set_timer_relative(TIMER_INTERVAL, notifications::TIMER_MASK); + loop { + idol_runtime::dispatch(&mut buffer, &mut server); + } +} + +mod idl { + include!(concat!(env!("OUT_DIR"), "/server_stub.rs")); +} + +include!(concat!(env!("OUT_DIR"), "/notifications.rs"));