probe_rs_rtt/
channel.rs

1use crate::Error;
2use probe_rs::{config::MemoryRegion, Core, MemoryInterface};
3use scroll::{Pread, LE};
4use std::cmp::min;
5
6/// Trait for channel information shared between up and down channels.
7pub trait RttChannel {
8    /// Returns the number of the channel.
9    fn number(&self) -> usize;
10
11    /// Returns the name of the channel or `None` if there is none.
12    fn name(&self) -> Option<&str>;
13
14    /// Returns the buffer size in bytes. Note that the usable size is one byte less due to how the
15    /// ring buffer is implemented.
16    fn buffer_size(&self) -> usize;
17}
18
19#[derive(Debug)]
20pub(crate) struct Channel {
21    number: usize,
22    core_id: usize,
23    ptr: u32,
24    name: Option<String>,
25    buffer_ptr: u32,
26    size: u32,
27}
28
29// Chanels must follow this data layout when reading/writing memory in order to be compatible with
30// the official RTT implementation.
31//
32// struct Channel {
33//     const char *name; // Name of channel, pointer to null-terminated string. Optional.
34//     char *buffer; // Pointer to buffer data
35//     unsigned int size; // Size of data buffer. The actual capacity is one byte less.
36//     unsigned int write; // Offset in data buffer of next byte to write.
37//     unsigned int read; // Offset in data buffer of next byte to read.
38//     // The low 2 bits of flags are used for blocking/non blocking modes, the rest are ignored.
39//     unsigned int flags;
40// }
41
42impl Channel {
43    // Size of the Channel struct in target memory in bytes
44    pub(crate) const SIZE: usize = 24;
45
46    // Offsets of fields in target memory in bytes
47    const O_NAME: usize = 0;
48    const O_BUFFER_PTR: usize = 4;
49    const O_SIZE: usize = 8;
50    const O_WRITE: usize = 12;
51    const O_READ: usize = 16;
52    const O_FLAGS: usize = 20;
53
54    pub(crate) fn from(
55        core: &mut Core,
56        number: usize,
57        memory_map: &[MemoryRegion],
58        ptr: u32,
59        mem: &[u8],
60    ) -> Result<Option<Channel>, Error> {
61        let buffer_ptr: u32 = match mem.pread_with(Self::O_BUFFER_PTR, LE) {
62            Ok(buffer_ptr) => buffer_ptr,
63            Err(_error) => return Err(Error::MemoryRead("RTT channel address".to_string())),
64        };
65
66        if buffer_ptr == 0 {
67            // This buffer isn't in use
68            return Ok(None);
69        }
70
71        let name_ptr: u32 = match mem.pread_with(Self::O_NAME, LE) {
72            Ok(name_ptr) => name_ptr,
73            Err(_error) => return Err(Error::MemoryRead("RTT channel name".to_string())),
74        };
75
76        let name = if name_ptr == 0 {
77            None
78        } else {
79            read_c_string(core, memory_map, name_ptr)?
80        };
81
82        Ok(Some(Channel {
83            number,
84            core_id: core.id(),
85            ptr,
86            name,
87            buffer_ptr,
88            size: mem.pread_with(Self::O_SIZE, LE).unwrap(),
89        }))
90    }
91
92    /// Validate that the Core id of a request is the same as the Core id against which the Channel was created.
93    pub(crate) fn validate_core_id(&self, core: &mut Core) -> Result<(), Error> {
94        if core.id() == self.core_id {
95            Ok(())
96        } else {
97            Err(Error::IncorrectCoreSpecified(self.core_id, core.id()))
98        }
99    }
100
101    pub fn name(&self) -> Option<&str> {
102        self.name.as_ref().map(|s| s.as_ref())
103    }
104
105    pub fn buffer_size(&self) -> usize {
106        self.size as usize
107    }
108
109    fn read_pointers(&self, core: &mut Core, dir: &'static str) -> Result<(u32, u32), Error> {
110        self.validate_core_id(core)?;
111        let mut block = [0u32; 2];
112        core.read_32((self.ptr + Self::O_WRITE as u32).into(), block.as_mut())?;
113
114        let write: u32 = block[0];
115        let read: u32 = block[1];
116
117        let validate = |which, value| {
118            if value >= self.size {
119                Err(Error::ControlBlockCorrupted(format!(
120                    "{} pointer is {} while buffer size is {} for {:?} channel {} ({})",
121                    which,
122                    value,
123                    self.size,
124                    dir,
125                    self.number,
126                    self.name().unwrap_or("no name"),
127                )))
128            } else {
129                Ok(())
130            }
131        };
132
133        validate("write", write)?;
134        validate("read", read)?;
135
136        Ok((write, read))
137    }
138}
139
140/// RTT up (target to host) channel.
141#[derive(Debug)]
142pub struct UpChannel(pub(crate) Channel);
143
144impl UpChannel {
145    /// Returns the number of the channel.
146    pub fn number(&self) -> usize {
147        self.0.number
148    }
149
150    /// Returns the name of the channel or `None` if there is none.
151    pub fn name(&self) -> Option<&str> {
152        self.0.name()
153    }
154
155    /// Returns the buffer size in bytes. Note that the usable size is one byte less due to how the
156    /// ring buffer is implemented.
157    pub fn buffer_size(&self) -> usize {
158        self.0.buffer_size()
159    }
160
161    /// Reads the current channel mode from the target and returns its.
162    ///
163    /// See [`ChannelMode`] for more information on what the modes mean.
164    pub fn mode(&self, core: &mut Core) -> Result<ChannelMode, Error> {
165        self.0.validate_core_id(core)?;
166
167        let flags = core.read_word_32((self.0.ptr + Channel::O_FLAGS as u32).into())?;
168
169        match flags & 0x3 {
170            0 => Ok(ChannelMode::NoBlockSkip),
171            1 => Ok(ChannelMode::NoBlockTrim),
172            2 => Ok(ChannelMode::BlockIfFull),
173            _ => Err(Error::ControlBlockCorrupted(String::from(
174                "The channel mode flags are invalid",
175            ))),
176        }
177    }
178
179    /// Changes the channel mode on the target to the specified mode.
180    ///
181    /// See [`ChannelMode`] for more information on what the modes mean.
182    pub fn set_mode(&self, core: &mut Core, mode: ChannelMode) -> Result<(), Error> {
183        self.0.validate_core_id(core)?;
184        let flags = core.read_word_32((self.0.ptr + Channel::O_FLAGS as u32).into())?;
185
186        let new_flags = (flags & !3) | (mode as u32);
187        core.write_word_32((self.0.ptr + Channel::O_FLAGS as u32).into(), new_flags)?;
188
189        Ok(())
190    }
191
192    fn read_core(&self, core: &mut Core, mut buf: &mut [u8]) -> Result<(u32, usize), Error> {
193        self.0.validate_core_id(core)?;
194        let (write, mut read) = self.0.read_pointers(core, "up")?;
195
196        let mut total = 0;
197
198        // Read while buffer contains data and output buffer has space (maximum of two iterations)
199        while !buf.is_empty() {
200            let count = min(self.readable_contiguous(write, read), buf.len());
201            if count == 0 {
202                break;
203            }
204
205            core.read((self.0.buffer_ptr + read).into(), &mut buf[..count])?;
206
207            total += count;
208            read += count as u32;
209
210            if read >= self.0.size {
211                // Wrap around to start
212                read = 0;
213            }
214
215            buf = &mut buf[count..];
216        }
217
218        Ok((read, total))
219    }
220
221    /// Reads some bytes from the channel to the specified buffer and returns how many bytes were
222    /// read.
223    ///
224    /// This method will not block waiting for data in the target buffer, and may read less bytes
225    /// than would fit in `buf`.
226    pub fn read(&self, core: &mut Core, buf: &mut [u8]) -> Result<usize, Error> {
227        self.0.validate_core_id(core)?;
228        let (read, total) = self.read_core(core, buf)?;
229
230        if total > 0 {
231            // Write read pointer back to target if something was read
232            core.write_word_32((self.0.ptr + Channel::O_READ as u32).into(), read)?;
233        }
234
235        Ok(total)
236    }
237
238    /// Peeks at the current data in the channel buffer, copies data into the specified buffer and
239    /// returns how many bytes were read.
240    ///
241    /// The difference from [`read`](UpChannel::read) is that this does not discard the data in the
242    /// buffer.
243    pub fn peek(&self, core: &mut Core, buf: &mut [u8]) -> Result<usize, Error> {
244        self.0.validate_core_id(core)?;
245        Ok(self.read_core(core, buf)?.1)
246    }
247
248    /// Calculates amount of contiguous data available for reading
249    fn readable_contiguous(&self, write: u32, read: u32) -> usize {
250        (if read > write {
251            self.0.size - read
252        } else {
253            write - read
254        }) as usize
255    }
256}
257
258impl RttChannel for UpChannel {
259    /// Returns the number of the channel.
260    fn number(&self) -> usize {
261        self.0.number
262    }
263
264    fn name(&self) -> Option<&str> {
265        self.0.name()
266    }
267    fn buffer_size(&self) -> usize {
268        self.0.buffer_size()
269    }
270}
271
272/// RTT down (host to target) channel.
273#[derive(Debug)]
274pub struct DownChannel(pub(crate) Channel);
275
276impl DownChannel {
277    /// Returns the number of the channel.
278    pub fn number(&self) -> usize {
279        self.0.number
280    }
281
282    /// Returns the name of the channel or `None` if there is none.
283    pub fn name(&self) -> Option<&str> {
284        self.0.name()
285    }
286
287    /// Returns the buffer size in bytes. Note that the usable size is one byte less due to how the
288    /// ring buffer is implemented.
289    pub fn buffer_size(&self) -> usize {
290        self.0.buffer_size()
291    }
292
293    /// Writes some bytes into the channel buffer and returns the number of bytes written.
294    ///
295    /// This method will not block waiting for space to become available in the channel buffer, and
296    /// may not write all of `buf`.
297    pub fn write(&self, core: &mut Core, mut buf: &[u8]) -> Result<usize, Error> {
298        self.0.validate_core_id(core)?;
299        let (mut write, read) = self.0.read_pointers(core, "down")?;
300
301        if self.writable_contiguous(write, read) == 0 {
302            // Buffer is full - do nothing.
303            return Ok(0);
304        }
305
306        let mut total = 0;
307
308        // Write while buffer has space for data and output contains data (maximum of two iterations)
309        while !buf.is_empty() {
310            let count = min(self.writable_contiguous(write, read), buf.len());
311            if count == 0 {
312                break;
313            }
314
315            core.write_8((self.0.buffer_ptr + write).into(), &buf[..count])?;
316
317            total += count;
318            write += count as u32;
319
320            if write >= self.0.size {
321                // Wrap around to start
322                write = 0;
323            }
324
325            buf = &buf[count..];
326        }
327
328        // Write write pointer back to target
329        core.write_word_32((self.0.ptr + Channel::O_WRITE as u32).into(), write)?;
330
331        Ok(total)
332    }
333
334    /// Calculates amount of contiguous space available for writing
335    fn writable_contiguous(&self, write: u32, read: u32) -> usize {
336        (if read > write {
337            read - write - 1
338        } else if read == 0 {
339            self.0.size - write - 1
340        } else {
341            self.0.size - write
342        }) as usize
343    }
344}
345
346impl RttChannel for DownChannel {
347    /// Returns the number of the channel.
348    fn number(&self) -> usize {
349        self.0.number
350    }
351
352    fn name(&self) -> Option<&str> {
353        self.0.name()
354    }
355    fn buffer_size(&self) -> usize {
356        self.0.buffer_size()
357    }
358}
359
360/// Reads a null-terminated string from target memory. Lossy UTF-8 decoding is used.
361fn read_c_string(
362    core: &mut Core,
363    memory_map: &[MemoryRegion],
364    ptr: u32,
365) -> Result<Option<String>, Error> {
366    // Find out which memory range contains the pointer
367    let range = memory_map
368        .iter()
369        .filter_map(|r| match r {
370            MemoryRegion::Nvm(r) => Some(&r.range),
371            MemoryRegion::Ram(r) => Some(&r.range),
372            _ => None,
373        })
374        .find(|r| r.contains(&(ptr as u64)));
375
376    // If the pointer is not within any valid range, return None.
377    let range = match range {
378        Some(r) => r,
379        None => return Ok(None),
380    };
381
382    // Read up to 128 bytes not going past the end of the region
383    let mut bytes = vec![0u8; min(128, (range.end - ptr as u64) as usize)];
384    core.read(ptr.into(), bytes.as_mut())?;
385
386    let return_value = bytes
387        .iter()
388        .position(|&b| b == 0)
389        .map(|p| String::from_utf8_lossy(&bytes[..p]).into_owned());
390    tracing::debug!(
391        "probe-rs-rtt::Channel::read_c_string() result = {:?}",
392        return_value
393    );
394    // If the bytes read contain a null, return the preceding part as a string, otherwise None.
395    Ok(return_value)
396}
397
398/// Specifies what to do when a channel doesn't have enough buffer space for a complete write on the
399/// target side.
400#[derive(Clone, Copy, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
401#[repr(u32)]
402pub enum ChannelMode {
403    /// Skip writing the data completely if it doesn't fit in its entirety.
404    NoBlockSkip = 0,
405
406    /// Write as much as possible of the data and ignore the rest.
407    NoBlockTrim = 1,
408
409    /// Block (spin) if the buffer is full. Note that if the application writes within a critical
410    /// section, using this mode can cause the application to freeze if the buffer becomes full and
411    /// is not read by the host.
412    BlockIfFull = 2,
413}