probe_rs/rtt/
channel.rs

1use crate::rtt::Error;
2use crate::{Core, MemoryInterface};
3use probe_rs_target::RegionMergeIterator;
4use std::cmp::min;
5use std::ffi::CStr;
6use std::num::NonZeroU64;
7use zerocopy::{FromBytes, Immutable, KnownLayout};
8
9/// Trait for channel information shared between up and down channels.
10pub trait RttChannel {
11    /// Returns the number of the channel.
12    fn number(&self) -> usize;
13
14    /// Returns the name of the channel or `None` if there is none.
15    fn name(&self) -> Option<&str>;
16
17    /// Returns the buffer size in bytes. Note that the usable size is one byte less due to how the
18    /// ring buffer is implemented.
19    fn buffer_size(&self) -> usize;
20}
21
22#[repr(C)]
23#[derive(Debug, FromBytes, Immutable, KnownLayout, Clone)]
24pub(crate) struct RttChannelBufferInner<T> {
25    standard_name_pointer: T,
26    buffer_start_pointer: T,
27    size_of_buffer: T,
28    write_offset: T,
29    read_offset: T,
30    flags: T,
31}
32
33impl<T> RttChannelBufferInner<T> {
34    pub fn write_buffer_ptr_offset(&self) -> usize {
35        std::mem::offset_of!(RttChannelBufferInner<T>, write_offset)
36    }
37
38    pub fn read_buffer_ptr_offset(&self) -> usize {
39        std::mem::offset_of!(RttChannelBufferInner<T>, read_offset)
40    }
41
42    pub fn flags_offset(&self) -> usize {
43        std::mem::offset_of!(RttChannelBufferInner<T>, flags)
44    }
45
46    pub fn size() -> usize {
47        std::mem::size_of::<RttChannelBufferInner<T>>()
48    }
49}
50
51#[derive(Debug, Clone)]
52pub(crate) enum RttChannelBuffer {
53    Buffer32(RttChannelBufferInner<u32>),
54    Buffer64(RttChannelBufferInner<u64>),
55}
56
57impl RttChannelBuffer {
58    pub fn size(&self) -> usize {
59        match self {
60            RttChannelBuffer::Buffer32(_) => RttChannelBufferInner::<u32>::size(),
61            RttChannelBuffer::Buffer64(_) => RttChannelBufferInner::<u64>::size(),
62        }
63    }
64}
65
66impl From<RttChannelBufferInner<u32>> for RttChannelBuffer {
67    fn from(value: RttChannelBufferInner<u32>) -> Self {
68        RttChannelBuffer::Buffer32(value)
69    }
70}
71
72impl From<RttChannelBufferInner<u64>> for RttChannelBuffer {
73    fn from(value: RttChannelBufferInner<u64>) -> Self {
74        RttChannelBuffer::Buffer64(value)
75    }
76}
77
78impl RttChannelBuffer {
79    pub fn buffer_start_pointer(&self) -> u64 {
80        match self {
81            RttChannelBuffer::Buffer32(x) => u64::from(x.buffer_start_pointer),
82            RttChannelBuffer::Buffer64(x) => x.buffer_start_pointer,
83        }
84    }
85
86    pub fn standard_name_pointer(&self) -> Option<NonZeroU64> {
87        match self {
88            RttChannelBuffer::Buffer32(x) => NonZeroU64::new(u64::from(x.standard_name_pointer)),
89            RttChannelBuffer::Buffer64(x) => NonZeroU64::new(x.standard_name_pointer),
90        }
91    }
92
93    pub fn size_of_buffer(&self) -> u64 {
94        match self {
95            RttChannelBuffer::Buffer32(x) => u64::from(x.size_of_buffer),
96            RttChannelBuffer::Buffer64(x) => x.size_of_buffer,
97        }
98    }
99
100    /// return (write_buffer_ptr, read_buffer_ptr)
101    pub fn read_buffer_offsets(&self, core: &mut Core, ptr: u64) -> Result<(u64, u64), Error> {
102        Ok(match self {
103            RttChannelBuffer::Buffer32(h32) => {
104                let mut block = [0u32; 2];
105                core.read_32(ptr + h32.write_buffer_ptr_offset() as u64, block.as_mut())?;
106                (u64::from(block[0]), u64::from(block[1]))
107            }
108            RttChannelBuffer::Buffer64(h64) => {
109                let mut block = [0u64; 2];
110                core.read_64(ptr + h64.write_buffer_ptr_offset() as u64, block.as_mut())?;
111                (block[0], block[1])
112            }
113        })
114    }
115
116    pub fn write_write_buffer_ptr(
117        &self,
118        core: &mut Core,
119        ptr: u64,
120        buffer_ptr: u64,
121    ) -> Result<(), Error> {
122        match self {
123            RttChannelBuffer::Buffer32(h32) => {
124                core.write_word_32(
125                    ptr + h32.write_buffer_ptr_offset() as u64,
126                    buffer_ptr.try_into().unwrap(),
127                )?;
128            }
129            RttChannelBuffer::Buffer64(h64) => {
130                core.write_word_64(ptr + h64.write_buffer_ptr_offset() as u64, buffer_ptr)?;
131            }
132        };
133        Ok(())
134    }
135
136    pub fn write_read_buffer_ptr(
137        &self,
138        core: &mut Core,
139        ptr: u64,
140        buffer_ptr: u64,
141    ) -> Result<(), Error> {
142        match self {
143            RttChannelBuffer::Buffer32(h32) => {
144                core.write_word_32(
145                    ptr + h32.read_buffer_ptr_offset() as u64,
146                    buffer_ptr.try_into().unwrap(),
147                )?;
148            }
149            RttChannelBuffer::Buffer64(h64) => {
150                core.write_word_64(ptr + h64.read_buffer_ptr_offset() as u64, buffer_ptr)?;
151            }
152        };
153        Ok(())
154    }
155
156    pub fn read_flags(&self, core: &mut Core, ptr: u64) -> Result<u64, Error> {
157        Ok(match self {
158            RttChannelBuffer::Buffer32(h32) => {
159                u64::from(core.read_word_32(ptr + h32.flags_offset() as u64)?)
160            }
161            RttChannelBuffer::Buffer64(h64) => {
162                core.read_word_64(ptr + h64.flags_offset() as u64)?
163            }
164        })
165    }
166
167    pub fn write_flags(&self, core: &mut Core, ptr: u64, flags: u64) -> Result<(), Error> {
168        match self {
169            RttChannelBuffer::Buffer32(h32) => {
170                core.write_word_32(ptr + h32.flags_offset() as u64, flags.try_into().unwrap())?;
171            }
172            RttChannelBuffer::Buffer64(h64) => {
173                core.write_word_64(ptr + h64.flags_offset() as u64, flags)?;
174            }
175        };
176        Ok(())
177    }
178}
179
180#[derive(Debug)]
181pub(crate) struct Channel {
182    number: usize,
183    core_id: usize,
184    name: Option<String>,
185    metadata_ptr: u64,
186    info: RttChannelBuffer,
187    last_read_ptr: Option<u64>,
188}
189
190// Chanels must follow this data layout when reading/writing memory in order to be compatible with
191// the official RTT implementation.
192//
193// struct Channel {
194//     const char *name; // Name of channel, pointer to null-terminated string. Optional.
195//     char *buffer; // Pointer to buffer data
196//     unsigned int size; // Size of data buffer. The actual capacity is one byte less.
197//     unsigned int write; // Offset in data buffer of next byte to write.
198//     unsigned int read; // Offset in data buffer of next byte to read.
199//     // The low 2 bits of flags are used for blocking/non blocking modes, the rest are ignored.
200//     unsigned int flags;
201// }
202
203impl Channel {
204    pub(crate) fn from(
205        core: &mut Core,
206        number: usize,
207        metadata_ptr: u64,
208        info: RttChannelBuffer,
209    ) -> Result<Option<Channel>, Error> {
210        let buffer_ptr = info.buffer_start_pointer();
211        if buffer_ptr == 0 {
212            // This buffer isn't in use
213            return Ok(None);
214        };
215
216        let mut this = Channel {
217            number,
218            core_id: core.id(),
219            metadata_ptr,
220            name: None,
221            info,
222            last_read_ptr: None,
223        };
224
225        // It's possible that the channel is not initialized with the magic string written last.
226        // We call read_pointers to validate that the channel pointers are in an expected range.
227        // This should at least catch most cases where the control block is partially initialized.
228        this.read_pointers(core, "")?;
229        // Read channel name just after the pointer was validated to be within an expected range.
230        this.name = if let Some(ptr) = this.info.standard_name_pointer() {
231            read_c_string(core, ptr)?
232        } else {
233            None
234        };
235        this.mode(core)?;
236
237        Ok(Some(this))
238    }
239
240    /// Validate that the Core id of a request is the same as the Core id against which the Channel was created.
241    pub(crate) fn validate_core_id(&self, core: &mut Core) -> Result<(), Error> {
242        if core.id() != self.core_id {
243            return Err(Error::IncorrectCoreSpecified(self.core_id, core.id()));
244        }
245
246        Ok(())
247    }
248
249    pub fn name(&self) -> Option<&str> {
250        self.name.as_deref()
251    }
252
253    pub fn buffer_size(&self) -> usize {
254        self.info.size_of_buffer() as usize
255    }
256
257    /// Reads the current channel mode from the target and returns its.
258    ///
259    /// See [`ChannelMode`] for more information on what the modes mean.
260    pub fn mode(&self, core: &mut Core) -> Result<ChannelMode, Error> {
261        self.validate_core_id(core)?;
262        let flags = self.info.read_flags(core, self.metadata_ptr)?;
263
264        ChannelMode::try_from(flags & ChannelMode::MASK)
265    }
266
267    /// Changes the channel mode on the target to the specified mode.
268    ///
269    /// See [`ChannelMode`] for more information on what the modes mean.
270    pub fn set_mode(&self, core: &mut Core, mode: ChannelMode) -> Result<(), Error> {
271        tracing::debug!("Setting RTT channel {} mode to {:?}", self.number, mode);
272        self.validate_core_id(core)?;
273        let flags = self.info.read_flags(core, self.metadata_ptr)?;
274
275        let new_flags = ChannelMode::set(mode, flags);
276        self.info.write_flags(core, self.metadata_ptr, new_flags)?;
277
278        Ok(())
279    }
280
281    fn read_pointers(&self, core: &mut Core, channel_kind: &str) -> Result<(u64, u64), Error> {
282        self.validate_core_id(core)?;
283
284        let (write, read) = self.info.read_buffer_offsets(core, self.metadata_ptr)?;
285
286        // Validate whether the buffers are sensible
287        let validate = |which, value| {
288            let buffer_offset_larger_than_size_of_buffer = value >= self.info.size_of_buffer();
289
290            if buffer_offset_larger_than_size_of_buffer {
291                return Err(Error::ControlBlockCorrupted(format!(
292                    "{which} pointer is {value:#010x} while buffer size is {:#010x} for {channel_kind}channel {} ({})",
293                    self.info.size_of_buffer(),
294                    self.number,
295                    self.name().unwrap_or("no name"),
296                )));
297            }
298
299            let buffer_fully_in_memory_region = core
300                .target()
301                .memory_map
302                .iter()
303                .filter_map(|mr| mr.as_ram_region())
304                .merge_consecutive()
305                .any(|rr| {
306                    let start = self.info.buffer_start_pointer();
307                    let end = self.info.buffer_start_pointer() + self.info.size_of_buffer();
308
309                    // `end` points at one beyond the last byte, and so does `rr.range.end`. Since
310                    // `rr.range` is exclusive, `contains` will return `false` if `end` is equal to
311                    // `rr.range.end`.
312                    rr.range.contains(&start) && end <= rr.range.end
313                });
314
315            if !buffer_fully_in_memory_region {
316                return Err(Error::ControlBlockCorrupted(format!(
317                    "the {which} buffer doesn't fully fit in any known (consecutive) ram region according to its own pointers: (start_pointer: {:#X}, size: {})",
318                    self.info.buffer_start_pointer(),
319                    self.info.size_of_buffer(),
320                )));
321            }
322
323            Ok(())
324        };
325
326        validate("write", write)?;
327        validate("read", read)?;
328
329        Ok((write, read))
330    }
331}
332
333/// RTT up (target to host) channel.
334#[derive(Debug)]
335pub struct UpChannel(pub(crate) Channel);
336
337impl UpChannel {
338    /// Returns the number of the channel.
339    pub fn number(&self) -> usize {
340        self.0.number
341    }
342
343    /// Returns the name of the channel or `None` if there is none.
344    pub fn name(&self) -> Option<&str> {
345        self.0.name()
346    }
347
348    /// Returns the buffer size in bytes. Note that the usable size is one byte less due to how the
349    /// ring buffer is implemented.
350    pub fn buffer_size(&self) -> usize {
351        self.0.buffer_size()
352    }
353
354    /// Reads the current channel mode from the target and returns its.
355    ///
356    /// See [`ChannelMode`] for more information on what the modes mean.
357    pub fn mode(&self, core: &mut Core) -> Result<ChannelMode, Error> {
358        self.0.mode(core)
359    }
360
361    /// Changes the channel mode on the target to the specified mode.
362    ///
363    /// See [`ChannelMode`] for more information on what the modes mean.
364    pub fn set_mode(&self, core: &mut Core, mode: ChannelMode) -> Result<(), Error> {
365        self.0.set_mode(core, mode)
366    }
367
368    fn read_core(&mut self, core: &mut Core, mut buf: &mut [u8]) -> Result<(u64, usize), Error> {
369        let (write, mut read) = self.0.read_pointers(core, "up ")?;
370
371        let mut total = 0;
372
373        if let Some(ptr) = self.0.last_read_ptr {
374            // Check if the read pointer has changed since we last wrote it.
375            if read != ptr {
376                return Err(Error::ReadPointerChanged);
377            }
378        }
379
380        // Read while buffer contains data and output buffer has space (maximum of two iterations)
381        while !buf.is_empty() {
382            let count = min(self.readable_contiguous(write, read), buf.len());
383            if count == 0 {
384                break;
385            }
386
387            core.read(self.0.info.buffer_start_pointer() + read, &mut buf[..count])?;
388
389            total += count;
390            read += count as u64;
391
392            if read >= self.0.info.size_of_buffer() {
393                // Wrap around to start
394                read = 0;
395            }
396
397            buf = &mut buf[count..];
398        }
399        self.0.last_read_ptr = Some(read);
400
401        Ok((read, total))
402    }
403
404    /// Reads some bytes from the channel to the specified buffer and returns how many bytes were
405    /// read.
406    ///
407    /// This method will not block waiting for data in the target buffer, and may read less bytes
408    /// than would fit in `buf`.
409    pub fn read(&mut self, core: &mut Core, buf: &mut [u8]) -> Result<usize, Error> {
410        let (read, total) = self.read_core(core, buf)?;
411
412        if total > 0 {
413            // Write read pointer back to target if something was read
414            self.0
415                .info
416                .write_read_buffer_ptr(core, self.0.metadata_ptr, read)?;
417        }
418
419        Ok(total)
420    }
421
422    /// Peeks at the current data in the channel buffer, copies data into the specified buffer and
423    /// returns how many bytes were read.
424    ///
425    /// The difference from [`read`](UpChannel::read) is that this does not discard the data in the
426    /// buffer.
427    pub fn peek(&mut self, core: &mut Core, buf: &mut [u8]) -> Result<usize, Error> {
428        Ok(self.read_core(core, buf)?.1)
429    }
430
431    /// Calculates amount of contiguous data available for reading
432    fn readable_contiguous(&self, write: u64, read: u64) -> usize {
433        let end = if read > write {
434            self.0.info.size_of_buffer()
435        } else {
436            write
437        };
438
439        (end - read) as usize
440    }
441}
442
443impl RttChannel for UpChannel {
444    /// Returns the number of the channel.
445    fn number(&self) -> usize {
446        self.0.number
447    }
448
449    fn name(&self) -> Option<&str> {
450        self.0.name()
451    }
452
453    fn buffer_size(&self) -> usize {
454        self.0.buffer_size()
455    }
456}
457
458/// RTT down (host to target) channel.
459#[derive(Debug)]
460pub struct DownChannel(pub(crate) Channel);
461
462impl DownChannel {
463    /// Returns the number of the channel.
464    pub fn number(&self) -> usize {
465        self.0.number
466    }
467
468    /// Returns the name of the channel or `None` if there is none.
469    pub fn name(&self) -> Option<&str> {
470        self.0.name()
471    }
472
473    /// Returns the buffer size in bytes. Note that the usable size is one byte less due to how the
474    /// ring buffer is implemented.
475    pub fn buffer_size(&self) -> usize {
476        self.0.buffer_size()
477    }
478
479    /// Writes some bytes into the channel buffer and returns the number of bytes written.
480    ///
481    /// This method will not block waiting for space to become available in the channel buffer, and
482    /// may not write all of `buf`.
483    pub fn write(&mut self, core: &mut Core, mut buf: &[u8]) -> Result<usize, Error> {
484        let (mut write, read) = self.0.read_pointers(core, "down ")?;
485
486        let mut total = 0;
487
488        // Write while buffer has space for data and output contains data (maximum of two iterations)
489        while !buf.is_empty() {
490            let count = min(self.writable_contiguous(write, read), buf.len());
491            if count == 0 {
492                break;
493            }
494
495            core.write(self.0.info.buffer_start_pointer() + write, &buf[..count])?;
496
497            total += count;
498            write += count as u64;
499
500            if write >= self.0.info.size_of_buffer() {
501                // Wrap around to start
502                write = 0;
503            }
504
505            buf = &buf[count..];
506        }
507
508        // Write write pointer back to target
509        self.0
510            .info
511            .write_write_buffer_ptr(core, self.0.metadata_ptr, write)?;
512
513        Ok(total)
514    }
515
516    /// Calculates amount of contiguous space available for writing
517    fn writable_contiguous(&self, write: u64, read: u64) -> usize {
518        (if read > write {
519            read - write - 1
520        } else if read == 0 {
521            self.0.info.size_of_buffer() - write - 1
522        } else {
523            self.0.info.size_of_buffer() - write
524        }) as usize
525    }
526}
527
528impl RttChannel for DownChannel {
529    /// Returns the number of the channel.
530    fn number(&self) -> usize {
531        self.0.number
532    }
533
534    fn name(&self) -> Option<&str> {
535        self.0.name()
536    }
537
538    fn buffer_size(&self) -> usize {
539        self.0.buffer_size()
540    }
541}
542
543/// Reads a null-terminated string from target memory. Lossy UTF-8 decoding is used.
544fn read_c_string(core: &mut Core, ptr: NonZeroU64) -> Result<Option<String>, Error> {
545    let ptr = ptr.get();
546    let Some(range) = core
547        .memory_regions()
548        .filter(|r| r.is_ram() || r.is_nvm())
549        .find_map(|r| r.contains(ptr).then_some(r.address_range()))
550    else {
551        return Err(Error::ControlBlockCorrupted(format!(
552            "The channel name pointer is not in a valid memory region: {ptr:#X}"
553        )));
554    };
555
556    // Read up to 128 bytes not going past the end of the region
557    let mut bytes = vec![0u8; min(128, (range.end - ptr) as usize)];
558    core.read(ptr, bytes.as_mut())?;
559
560    // If the bytes read contain a null, return the preceding part as a string, otherwise None.
561    let return_value = CStr::from_bytes_until_nul(&bytes)
562        .map(|s| s.to_string_lossy().into_owned())
563        .ok();
564
565    tracing::trace!("read_c_string() result = {:?}", return_value);
566    Ok(return_value)
567}
568
569/// Specifies what to do when a channel doesn't have enough buffer space for a complete write on the
570/// target side.
571#[derive(Clone, Copy, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
572#[repr(u32)]
573pub enum ChannelMode {
574    /// Skip writing the data completely if it doesn't fit in its entirety.
575    NoBlockSkip = 0,
576
577    /// Write as much as possible of the data and ignore the rest.
578    NoBlockTrim = 1,
579
580    /// Block (spin) if the buffer is full. Note that if the application writes within a critical
581    /// section, using this mode can cause the application to freeze if the buffer becomes full and
582    /// is not read by the host.
583    BlockIfFull = 2,
584}
585
586impl ChannelMode {
587    const MASK: u64 = 0x3;
588
589    fn set(self, flags: u64) -> u64 {
590        (flags & !Self::MASK) | (self as u64)
591    }
592}
593
594impl TryFrom<u64> for ChannelMode {
595    type Error = Error;
596
597    fn try_from(value: u64) -> Result<Self, Self::Error> {
598        match value {
599            0 => Ok(ChannelMode::NoBlockSkip),
600            1 => Ok(ChannelMode::NoBlockTrim),
601            2 => Ok(ChannelMode::BlockIfFull),
602            _ => Err(Error::ControlBlockCorrupted(format!(
603                "The channel mode flags are invalid: {value}"
604            ))),
605        }
606    }
607}