Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Measure the SP on SP_RESET signal interrupt #1946

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions drv/lpc55-swd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ lpc55-pac = { workspace = true }
num-traits = { workspace = true }
zerocopy = { workspace = true }
bitflags = { workspace = true }
static_assertions = { workspace = true }

attest-api = { path = "../../task/attest-api" }
drv-lpc55-gpio-api = { path = "../lpc55-gpio-api" }
Expand Down
148 changes: 98 additions & 50 deletions drv/lpc55-swd/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,11 @@ use idol_runtime::{
};
use lpc55_pac as device;
use ringbuf::*;
use static_assertions::const_assert;
use userlib::{
hl, set_timer_relative, sys_get_timer, sys_irq_control,
sys_irq_control_clear_pending, sys_set_timer, task_slot, RecvMessage,
TaskId, UnwrapLite,
TaskId,
};
use zerocopy::AsBytes;

Expand Down Expand Up @@ -654,13 +655,18 @@ impl NotificationHandler for ServerImpl {
if (bits & notifications::JTAG_DETECT_IRQ_MASK) != 0 {
ringbuf_entry!(Trace::SpJtagDetectFired);
const SLOT: PintSlot = SP_TO_ROT_JTAG_DETECT_L_PINT_SLOT;
if gpio
.pint_op(SLOT, PintOp::Detected, PintCondition::Falling)
.map_or(false, |v| v.unwrap_lite())
if let Ok(Some(detected)) =
gpio.pint_op(SLOT, PintOp::Detected, PintCondition::Falling)
{
ringbuf_entry!(Trace::InvalidateSpMeasurement);
invalidate = true;
self.init = false;
if detected {
ringbuf_entry!(Trace::InvalidateSpMeasurement);
invalidate = true;
self.init = false;
}
} else {
// The pint_op parameters are for a configured PINT slot for one of
// our configured GPIO pins. We're testing a status bit in a register.
unreachable!();
}
let _ = gpio.pint_op(SLOT, PintOp::Clear, PintCondition::Status);
sys_irq_control(notifications::JTAG_DETECT_IRQ_MASK, true);
Expand Down Expand Up @@ -1111,26 +1117,30 @@ impl ServerImpl {
return Err(Ack::Fault);
}
for slice in data.chunks_exact(U32_SIZE) {
let word = u32::from_le_bytes(slice.try_into().unwrap_lite());
let mut limit = 2;
loop {
self.write_single_target_addr(addr, word)?;
let readback = self.read_single_target_addr(addr)?;
if readback == word {
break;
}
ringbuf_entry!(Trace::Data {
addr,
data: readback,
src: word
});
if limit == 0 {
ringbuf_entry!(Trace::ReadbackFailure);
return Err(Ack::Fault);
if let Some(word) = slice_to_le_u32(slice) {
let mut limit = 2;
loop {
self.write_single_target_addr(addr, word)?;
let readback = self.read_single_target_addr(addr)?;
if readback == word {
break;
}
ringbuf_entry!(Trace::Data {
addr,
data: readback,
src: word
});
if limit == 0 {
ringbuf_entry!(Trace::ReadbackFailure);
return Err(Ack::Fault);
}
limit -= 1;
}
limit -= 1;
addr += U32_SIZE as u32;
} else {
// Using chunks_exact means that the conversion to u32 will succeed.
unreachable!();
}
addr += U32_SIZE as u32;
}
Ok(())
}
Expand Down Expand Up @@ -1239,7 +1249,9 @@ impl ServerImpl {
}

fn pin_setup(&mut self) {
setup_pins(self.gpio).unwrap_lite();
// setup_pins is generated at compile time
// and has configuration sanity checks.
_ = setup_pins(self.gpio);
}

/// Swaps the currently-active SP slot
Expand Down Expand Up @@ -1339,26 +1351,48 @@ impl ServerImpl {
// write DHCSR to RUN (MATIC + C_DEBUGEN)
// poll for S_HALT or timeout

// Search st.com for document "PM0253". Section 2.4.4 describes
// the STM32H753 vector table.
//
// The `endoscope` image has the vector table at offset 0. When loaded
// into the SP, that table will be at `endoscope::LOAD` and the SP's
// `VTOR` register will be set to that address prior to the SP being
// released from reset. Addresses in the vector table are absolute
// runtime values.
//
// The first u32 in the `endoscope` image is the initial stack pointer
// value. The second u32 is the initial program counter, a.k.a. the
// reset vector.

const_assert!(ENDOSCOPE_BYTES.len() > 2 * 1024);

// Set SP's Program Counter
let sp_reset_vector =
u32::from_le_bytes(ENDOSCOPE_BYTES[4..=7].try_into().unwrap_lite());
if self
.do_write_core_register(Reg::Dr.into(), sp_reset_vector | 1)
.is_err()
if let Some(sp_reset_vector) = slice_to_le_u32(&ENDOSCOPE_BYTES[4..=7])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this change? I found the previous version to be more clear:

let sp_reset_vector =
    u32::from_le_bytes(ENDOSCOPE_BYTES[4..=7].try_into().unwrap_lite());

versus the new code, which hides the unreachable! in the else of a conditional which is always taken

{
ringbuf_entry!(Trace::WrotePcRegisterFail);
return Err(());
if self
.do_write_core_register(Reg::Dr.into(), sp_reset_vector)
.is_err()
{
ringbuf_entry!(Trace::WrotePcRegisterFail);
return Err(());
}
} else {
// There are sufficient bytes to read this word out.
unreachable!();
}

// Set SP's Stack Pointer
let sp_initial_sp =
u32::from_le_bytes(ENDOSCOPE_BYTES[0..=3].try_into().unwrap_lite());
if self
.do_write_core_register(Reg::Sp.into(), sp_initial_sp)
.is_err()
{
ringbuf_entry!(Trace::WroteSpRegisterFail);
return Err(());
if let Some(sp_initial_sp) = slice_to_le_u32(&ENDOSCOPE_BYTES[0..=3]) {
if self
.do_write_core_register(Reg::Sp.into(), sp_initial_sp)
.is_err()
{
ringbuf_entry!(Trace::WroteSpRegisterFail);
return Err(());
}
} else {
// There are sufficient bytes to read this word out.
unreachable!();
}

// Set VTOR - Set vector table base address
Expand Down Expand Up @@ -1658,16 +1692,21 @@ impl ServerImpl {
let mut need_undo = Undo::from_bits_retain(0);

// Did SP_RESET transition to Zero?
if !gpio
.pint_op(SLOT, PintOp::Detected, PintCondition::Falling)
.map_or(false, |v| v.unwrap_lite())
if let Ok(Some(detected)) =
gpio.pint_op(SLOT, PintOp::Detected, PintCondition::Falling)
{
// Use of sys_irq_control_clear_pending(...) should avoid
// appearance of a "spurious" intrerrupt.
// Otherwise, cases where we assert SP_RESET then clean-up the PINT
// condition will have a pending notification.
ringbuf_entry!(Trace::SpResetNotAsserted);
return true; // no work required.
if !detected {
// Use of sys_irq_control_clear_pending(...) should avoid
// appearance of a "spurious" intrerrupt.
// Otherwise, cases where we assert SP_RESET then clean-up the PINT
// condition will have a pending notification.
ringbuf_entry!(Trace::SpResetNotAsserted);
return true; // no work required.
}
} else {
// The pint_op parameters are for a configured PINT slot for one of
// our configured GPIO pins. We're testing a status bit in a register.
unreachable!();
}

// Not ok yet: A reset happened. If we don't get a measurement then
Expand Down Expand Up @@ -1891,6 +1930,15 @@ impl ServerImpl {
}
}

fn slice_to_le_u32(slice: &[u8]) -> Option<u32> {
if slice.len() == core::mem::size_of::<u32>() {
if let Ok(data) = core::convert::TryInto::<[u8; 4]>::try_into(slice) {
return Some(u32::from_le_bytes(data));
}
}
None
}

#[export_name = "main"]
fn main() -> ! {
let syscon = SYSCON.get_task_id();
Expand Down
Loading