mod channel;
pub use channel::*;
pub mod channels;
pub use channels::Channels;
use crate::{config::MemoryRegion, Core, MemoryInterface};
use scroll::{Pread, LE};
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::ops::Range;
#[derive(Debug)]
pub struct Rtt {
ptr: u32,
up_channels: Channels<UpChannel>,
down_channels: Channels<DownChannel>,
}
impl Rtt {
const RTT_ID: [u8; 16] = *b"SEGGER RTT\0\0\0\0\0\0";
const MIN_SIZE: usize = Self::O_CHANNEL_ARRAYS;
const O_ID: usize = 0;
const O_MAX_UP_CHANNELS: usize = 16;
const O_MAX_DOWN_CHANNELS: usize = 20;
const O_CHANNEL_ARRAYS: usize = 24;
fn from(
core: &mut Core,
memory_map: &[MemoryRegion],
ptr: u32,
mem_in: Option<&[u8]>,
) -> Result<Option<Rtt>, Error> {
let mut mem = match mem_in {
Some(mem) => Cow::Borrowed(mem),
None => {
let mut mem = vec![0u8; Self::MIN_SIZE];
core.read(ptr.into(), &mut mem)?;
Cow::Owned(mem)
}
};
let rtt_id = &mem[Self::O_ID..(Self::O_ID + Self::RTT_ID.len())];
if rtt_id != Self::RTT_ID {
tracing::trace!(
"Expected control block to start with RTT ID: {:?}\n. Got instead: {:?}",
String::from_utf8_lossy(&Self::RTT_ID),
String::from_utf8_lossy(rtt_id)
);
return Err(Error::ControlBlockNotFound);
}
let max_up_channels = mem.pread_with::<u32>(Self::O_MAX_UP_CHANNELS, LE).unwrap() as usize;
let max_down_channels = mem
.pread_with::<u32>(Self::O_MAX_DOWN_CHANNELS, LE)
.unwrap() as usize;
if max_up_channels > 255 || max_down_channels > 255 {
return Err(Error::ControlBlockCorrupted(format!(
"Nonsensical array sizes at {ptr:08x}: max_up_channels={max_up_channels} max_down_channels={max_down_channels}"
)));
}
let cb_len = Self::O_CHANNEL_ARRAYS + (max_up_channels + max_down_channels) * Channel::SIZE;
if let Cow::Owned(mem) = &mut mem {
mem.resize(cb_len, 0);
core.read(
(ptr + Self::MIN_SIZE as u32).into(),
&mut mem[Self::MIN_SIZE..cb_len],
)?;
}
if mem.len() < cb_len {
tracing::debug!("Control block doesn't fit in scanned memory region.");
return Ok(None);
}
let mut up_channels = BTreeMap::new();
let mut down_channels = BTreeMap::new();
for i in 0..max_up_channels {
let offset = Self::O_CHANNEL_ARRAYS + i * Channel::SIZE;
if let Some(chan) =
Channel::from(core, i, memory_map, ptr + offset as u32, &mem[offset..])?
{
up_channels.insert(i, UpChannel(chan));
} else {
tracing::warn!("Buffer for up channel {} not initialized", i);
}
}
for i in 0..max_down_channels {
let offset =
Self::O_CHANNEL_ARRAYS + (max_up_channels * Channel::SIZE) + i * Channel::SIZE;
if let Some(chan) =
Channel::from(core, i, memory_map, ptr + offset as u32, &mem[offset..])?
{
down_channels.insert(i, DownChannel(chan));
} else {
tracing::warn!("Buffer for down channel {} not initialized", i);
}
}
Ok(Some(Rtt {
ptr,
up_channels: Channels(up_channels),
down_channels: Channels(down_channels),
}))
}
pub fn attach(core: &mut Core, memory_map: &[MemoryRegion]) -> Result<Rtt, Error> {
Self::attach_region(core, memory_map, &Default::default())
}
pub fn attach_region(
core: &mut Core,
memory_map: &[MemoryRegion],
region: &ScanRegion,
) -> Result<Rtt, Error> {
let ranges: Vec<Range<u32>> = match region {
ScanRegion::Exact(addr) => {
tracing::debug!("Scanning at exact address: 0x{:X}", addr);
return Rtt::from(core, memory_map, *addr, None)?
.ok_or(Error::ControlBlockNotFound);
}
ScanRegion::Ram => {
tracing::debug!("Scanning RAM");
memory_map
.iter()
.filter_map(|r| match r {
MemoryRegion::Ram(r) => Some(Range {
start: r.range.start as u32,
end: r.range.end as u32,
}),
_ => None,
})
.collect()
}
ScanRegion::Range(region) => {
tracing::debug!("Scanning region: {:?}", region);
vec![region.clone()]
}
};
let mut instances = ranges
.into_iter()
.filter_map(|range| {
if range.len() < Self::MIN_SIZE {
return None;
}
let mut mem = vec![0; range.len()];
{
core.read(range.start.into(), mem.as_mut()).ok()?;
}
match kmp::kmp_find(&Self::RTT_ID, mem.as_slice()) {
Some(offset) => Rtt::from(
core,
memory_map,
range.start + offset as u32,
Some(&mem[offset..]),
)
.transpose(),
None => None,
}
})
.collect::<Result<Vec<_>, _>>()?;
if instances.is_empty() {
return Err(Error::ControlBlockNotFound);
}
if instances.len() > 1 {
return Err(Error::MultipleControlBlocksFound(
instances.into_iter().map(|i| i.ptr).collect(),
));
}
Ok(instances.remove(0))
}
pub fn ptr(&self) -> u32 {
self.ptr
}
pub fn up_channels(&mut self) -> &mut Channels<UpChannel> {
&mut self.up_channels
}
pub fn down_channels(&mut self) -> &mut Channels<DownChannel> {
&mut self.down_channels
}
}
#[derive(Clone, Debug)]
pub enum ScanRegion {
Ram,
Range(Range<u32>),
Exact(u32),
}
impl Default for ScanRegion {
fn default() -> Self {
ScanRegion::Ram
}
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(
"RTT control block not found in target memory.\n\
- Make sure RTT is initialized on the target, AND that there are NO target breakpoints before RTT initalization.\n\
- For VSCode and probe-rs-debugger users, using `halt_after_reset:true` in your `launch.json` file will prevent RTT \n\
\tinitialization from happening on time.\n\
- Depending on the target, sleep modes can interfere with RTT."
)]
ControlBlockNotFound,
#[error("Multiple control blocks found in target memory.")]
MultipleControlBlocksFound(Vec<u32>),
#[error("Control block corrupted: {0}")]
ControlBlockCorrupted(String),
#[error("Incorrect Core number specified for this operation. Expected {0}, and found {1}")]
IncorrectCoreSpecified(usize, usize),
#[error("Error communicating with probe: {0}")]
Probe(#[from] crate::Error),
#[error("Unexpected error while reading {0} from target memory. Please report this as a bug.")]
MemoryRead(String),
}