Skip to content

Commit

Permalink
Merge pull request tock#4197 from tock/kernel-doc-utilities
Browse files Browse the repository at this point in the history
kernel: Update documentation to utilities and debug
  • Loading branch information
alevy authored Oct 25, 2024
2 parents 41df205 + 8262484 commit abf7669
Show file tree
Hide file tree
Showing 13 changed files with 299 additions and 161 deletions.
105 changes: 64 additions & 41 deletions kernel/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
//! If you are writing and the buffer fills up, you can make the size of
//! `output_buffer` larger.
//!
//! Before debug interfaces can be used, the board file must assign them hardware:
//! Before debug interfaces can be used, the board file must assign them
//! hardware:
//!
//! ```ignore
//! kernel::debug::assign_gpios(
Expand All @@ -17,11 +18,12 @@
//! None,
//! );
//!
//! components::debug_writer::DebugWriterComponent::new(uart_mux).finalize(components::debug_writer_component_static!());
//! components::debug_writer::DebugWriterComponent::new(uart_mux)
//! .finalize(components::debug_writer_component_static!());
//! ```
//!
//! The debug queue is optional, if not set in the board it is just ignored.
//! You can add one in the board file as follows:
//! The debug queue is optional, if not set in the board it is just ignored. You
//! can add one in the board file as follows:
//!
//! ```ignore
//! components::debug_queue::DebugQueueComponent::new()
Expand Down Expand Up @@ -70,14 +72,18 @@ use crate::utilities::cells::NumericCellExt;
use crate::utilities::cells::{MapCell, TakeCell};
use crate::ErrorCode;

/// This trait is similar to std::io::Write in that it takes bytes instead of a string (contrary to
/// core::fmt::Write), but io::Write isn't available in no_std (due to std::io::Error not being
/// available).
/// Implementation of `std::io::Write` for `no_std`.
///
/// Also, in our use cases, writes are infaillible, so the write function cannot return an Error,
/// however it might not be able to write everything, so it returns the number of bytes written.
/// This takes bytes instead of a string (contrary to [`core::fmt::Write`]), but
/// we cannot use `std::io::Write' as it isn't available in `no_std` (due to
/// `std::io::Error` not being available).
///
/// See also the tracking issue: <https://github.com/rust-lang/rfcs/issues/2262>
/// Also, in our use cases, writes are infallible, so the write function cannot
/// return an `Err`, however it might not be able to write everything, so it
/// returns the number of bytes written.
///
/// See also the tracking issue:
/// <https://github.com/rust-lang/rfcs/issues/2262>.
pub trait IoWrite {
fn write(&mut self, buf: &[u8]) -> usize;

Expand All @@ -99,13 +105,13 @@ pub trait IoWrite {

/// Tock panic routine, without the infinite LED-blinking loop.
///
/// This is useful for boards which do not feature LEDs to blink or
/// want to implement their own behaviour. This method returns after
/// performing the panic dump.
/// This is useful for boards which do not feature LEDs to blink or want to
/// implement their own behavior. This method returns after performing the panic
/// dump.
///
/// After this method returns, the system is no longer in a
/// well-defined state. Care must be taken on how one interacts with
/// the system once this function returns.
/// After this method returns, the system is no longer in a well-defined state.
/// Care must be taken on how one interacts with the system once this function
/// returns.
///
/// **NOTE:** The supplied `writer` must be synchronous.
pub unsafe fn panic_print<W: Write + IoWrite, C: Chip, PP: ProcessPrinter>(
Expand All @@ -122,11 +128,10 @@ pub unsafe fn panic_print<W: Write + IoWrite, C: Chip, PP: ProcessPrinter>(
panic_banner(writer, panic_info);
panic_cpu_state(chip, writer);

// Some systems may enforce memory protection regions for the
// kernel, making application memory inaccessible. However,
// printing process information will attempt to access memory. If
// we are provided a chip reference, attempt to disable userspace
// memory protection first:
// Some systems may enforce memory protection regions for the kernel, making
// application memory inaccessible. However, printing process information
// will attempt to access memory. If we are provided a chip reference,
// attempt to disable userspace memory protection first:
chip.map(|c| {
use crate::platform::mpu::MPU;
c.mpu().disable_app_mpu()
Expand All @@ -149,8 +154,8 @@ pub unsafe fn panic<L: hil::led::Led, W: Write + IoWrite, C: Chip, PP: ProcessPr
chip: &'static Option<&'static C>,
process_printer: &'static Option<&'static PP>,
) -> ! {
// Call `panic_print` first which will print out the panic
// information and return
// Call `panic_print` first which will print out the panic information and
// return
panic_print(writer, panic_info, nop, processes, chip, process_printer);

// The system is no longer in a well-defined state, we cannot
Expand Down Expand Up @@ -260,12 +265,14 @@ pub fn panic_blink_forever<L: hil::led::Led>(leds: &mut [&L]) -> ! {
///////////////////////////////////////////////////////////////////
// debug_gpio! support

/// Object to hold the assigned debugging GPIOs.
pub static mut DEBUG_GPIOS: (
Option<&'static dyn hil::gpio::Pin>,
Option<&'static dyn hil::gpio::Pin>,
Option<&'static dyn hil::gpio::Pin>,
) = (None, None, None);

/// Map up to three GPIO pins to use for debugging.
pub unsafe fn assign_gpios(
gpio0: Option<&'static dyn hil::gpio::Pin>,
gpio1: Option<&'static dyn hil::gpio::Pin>,
Expand All @@ -276,7 +283,7 @@ pub unsafe fn assign_gpios(
DEBUG_GPIOS.2 = gpio2;
}

/// In-kernel gpio debugging, accepts any GPIO HIL method
/// In-kernel gpio debugging that accepts any GPIO HIL method.
#[macro_export]
macro_rules! debug_gpio {
($i:tt, $method:ident $(,)?) => {{
Expand All @@ -290,8 +297,8 @@ macro_rules! debug_gpio {
///////////////////////////////////////////////////////////////////
// debug_enqueue! support

/// Wrapper type that we need a mutable reference to for the core::fmt::Write
/// interface.
/// Wrapper type that we need a mutable reference to for the
/// [`core::fmt::Write`] interface.
pub struct DebugQueueWrapper {
dw: MapCell<&'static DebugQueue>,
}
Expand All @@ -304,6 +311,7 @@ impl DebugQueueWrapper {
}
}

/// Queue to hold debug strings.
pub struct DebugQueue {
ring_buffer: TakeCell<'static, RingBuffer<'static, u8>>,
}
Expand All @@ -316,6 +324,7 @@ impl DebugQueue {
}
}

/// Global reference used by debug macros.
static mut DEBUG_QUEUE: Option<&'static mut DebugQueueWrapper> = None;

/// Function used by board main.rs to set a reference to the debug queue.
Expand All @@ -338,13 +347,15 @@ impl Write for DebugQueueWrapper {
}
}

/// Add a format string to the debug queue.
pub fn debug_enqueue_fmt(args: Arguments) {
unsafe { DEBUG_QUEUE.as_deref_mut() }.map(|buffer| {
let _ = write(buffer, args);
let _ = buffer.write_str("\r\n");
});
}

/// Flush the debug queue by writing to the underlying writer implementation.
pub fn debug_flush_queue_() {
let writer = unsafe { get_debug_writer() };

Expand All @@ -358,8 +369,11 @@ pub fn debug_flush_queue_() {
}
}

/// This macro prints a new line to an internal ring buffer, the contents of
/// which are only flushed with `debug_flush_queue!` and in the panic handler.
/// Add a new line to an internal ring buffer.
///
/// The internal queue is only flushed with
/// [`debug_flush_queue!()`](crate::debug_flush_queue) or within the panic
/// handler.
#[macro_export]
macro_rules! debug_enqueue {
() => ({
Expand All @@ -373,8 +387,7 @@ macro_rules! debug_enqueue {
});
}

/// This macro flushes the contents of the debug queue into the regular
/// debug output.
/// Flushes the contents of the debug queue into the regular debug output.
#[macro_export]
macro_rules! debug_flush_queue {
() => {{
Expand All @@ -385,14 +398,13 @@ macro_rules! debug_flush_queue {
///////////////////////////////////////////////////////////////////
// debug! and debug_verbose! support

/// Wrapper type that we need a mutable reference to for the core::fmt::Write
/// interface.
/// Wrapper type that we need a mutable reference to for the
/// [`core::fmt::Write`] interface.
pub struct DebugWriterWrapper {
dw: MapCell<&'static DebugWriter>,
}

/// Main type that we need an immutable reference to so we can share it with
/// the UART provider and this debug module.
/// Main type that we share with the UART provider and this debug module.
pub struct DebugWriter {
// What provides the actual writing mechanism.
uart: &'static dyn hil::uart::Transmit<'static>,
Expand All @@ -404,8 +416,10 @@ pub struct DebugWriter {
count: Cell<usize>,
}

/// Static variable that holds the kernel's reference to the debug tool. This is
/// needed so the debug!() macros have a reference to the object to use.
/// Static variable that holds the kernel's reference to the debug tool.
///
/// This is needed so the `debug!()` macros have a reference to the object to
/// use.
static mut DEBUG_WRITER: Option<&'static mut DebugWriterWrapper> = None;

unsafe fn try_get_debug_writer() -> Option<&'static mut DebugWriterWrapper> {
Expand Down Expand Up @@ -577,13 +591,15 @@ impl Write for DebugWriterWrapper {
}
}

/// Write a debug message without a trailing newline.
pub fn debug_print(args: Arguments) {
let writer = unsafe { get_debug_writer() };

let _ = write(writer, args);
writer.publish_bytes();
}

/// Write a debug message with a trailing newline.
pub fn debug_println(args: Arguments) {
let writer = unsafe { get_debug_writer() };

Expand All @@ -592,6 +608,7 @@ pub fn debug_println(args: Arguments) {
writer.publish_bytes();
}

/// Write a [`ReadableProcessSlice`] to the debug output.
pub fn debug_slice(slice: &ReadableProcessSlice) -> usize {
let writer = unsafe { get_debug_writer() };
let mut total = 0;
Expand All @@ -608,6 +625,7 @@ pub fn debug_slice(slice: &ReadableProcessSlice) -> usize {
total
}

/// Return how many bytes are remaining in the internal debug buffer.
pub fn debug_available_len() -> usize {
let writer = unsafe { get_debug_writer() };
writer.available_len()
Expand All @@ -619,6 +637,8 @@ fn write_header(writer: &mut DebugWriterWrapper, (file, line): &(&'static str, u
writer.write_fmt(format_args!("TOCK_DEBUG({}): {}:{}: ", count, file, line))
}

/// Write a debug message with file and line information without a trailing
/// newline.
pub fn debug_verbose_print(args: Arguments, file_line: &(&'static str, u32)) {
let writer = unsafe { get_debug_writer() };

Expand All @@ -627,6 +647,8 @@ pub fn debug_verbose_print(args: Arguments, file_line: &(&'static str, u32)) {
writer.publish_bytes();
}

/// Write a debug message with file and line information with a trailing
/// newline.
pub fn debug_verbose_println(args: Arguments, file_line: &(&'static str, u32)) {
let writer = unsafe { get_debug_writer() };

Expand Down Expand Up @@ -682,18 +704,18 @@ macro_rules! debug_verbose {
});
}

#[macro_export]
/// Prints out the expression and its location, then returns it.
///
/// ```rust,ignore
/// let foo: u8 = debug_expr!(0xff);
/// // Prints [main.rs:2] 0xff = 255
/// ```
/// Taken straight from Rust std::dbg.
/// Taken straight from Rust `std::dbg`.
#[macro_export]
macro_rules! debug_expr {
// NOTE: We cannot use `concat!` to make a static string as a format argument
// of `eprintln!` because `file!` could contain a `{` or
// `$val` expression could be a block (`{ .. }`), in which case the `eprintln!`
// NOTE: We cannot use `concat!` to make a static string as a format
// argument of `eprintln!` because `file!` could contain a `{` or `$val`
// expression could be a block (`{ .. }`), in which case the `eprintln!`
// will be malformed.
() => {
$crate::debug!("[{}:{}]", file!(), line!())
Expand All @@ -714,6 +736,7 @@ macro_rules! debug_expr {
};
}

/// Flush any stored messages to the output writer.
pub unsafe fn flush<W: Write + IoWrite>(writer: &mut W) {
if let Some(debug_writer) = try_get_debug_writer() {
if let Some(ring_buffer) = debug_writer.extract() {
Expand Down
51 changes: 34 additions & 17 deletions kernel/src/utilities/copy_slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,38 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Helper functions for copying buffers.
//!
//! This utility provides an implementation of the standard Rust
//! [`slice::copy_from_slice()`] method that cannot panic. This method is
//! provided through the [`CopyOrErr`] trait.
//!
//! This functionality is currently provided for the following types:
//! - `u8`
//! - `u16`
//! - `u32`
//! - `u64`
//! - `usize`
use crate::ErrorCode;
use core::ptr;

/// Interface for copying buffers that cannot panic.
pub trait CopyOrErr {
/// Copies a nonoverlapping slice from src to self. Returns Err(ErrorCode) if source and self
/// are not the same length. This is a non-panicing version of slice::copy_from_slice.
/// Copy a non-overlapping slice from `src` to `self`.
///
/// This is a non-panicking version of [`slice::copy_from_slice`].
///
/// Returns `Err(ErrorCode)` if `src` and `self` are not the same length.
fn copy_from_slice_or_err(&mut self, src: &Self) -> Result<(), ErrorCode>;
}

impl CopyOrErr for [u8] {
fn copy_from_slice_or_err(&mut self, src: &Self) -> Result<(), ErrorCode> {
if self.len() == src.len() {
// SAFETY: `self` is valid for `self.len()` elements by definition, and `src` was
// checked to have the same length. The slices cannot overlap because
// mutable references are exclusive.
// SAFETY: `self` is valid for `self.len()` elements by definition,
// and `src` was checked to have the same length. The slices cannot
// overlap because mutable references are exclusive.
unsafe {
ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), self.len());
}
Expand All @@ -30,9 +47,9 @@ impl CopyOrErr for [u8] {
impl CopyOrErr for [u16] {
fn copy_from_slice_or_err(&mut self, src: &Self) -> Result<(), ErrorCode> {
if self.len() == src.len() {
// SAFETY: `self` is valid for `self.len()` elements by definition, and `src` was
// checked to have the same length. The slices cannot overlap because
// mutable references are exclusive.
// SAFETY: `self` is valid for `self.len()` elements by definition,
// and `src` was checked to have the same length. The slices cannot
// overlap because mutable references are exclusive.
unsafe {
ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), self.len());
}
Expand All @@ -46,9 +63,9 @@ impl CopyOrErr for [u16] {
impl CopyOrErr for [u32] {
fn copy_from_slice_or_err(&mut self, src: &Self) -> Result<(), ErrorCode> {
if self.len() == src.len() {
// SAFETY: `self` is valid for `self.len()` elements by definition, and `src` was
// checked to have the same length. The slices cannot overlap because
// mutable references are exclusive.
// SAFETY: `self` is valid for `self.len()` elements by definition,
// and `src` was checked to have the same length. The slices cannot
// overlap because mutable references are exclusive.
unsafe {
ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), self.len());
}
Expand All @@ -62,9 +79,9 @@ impl CopyOrErr for [u32] {
impl CopyOrErr for [u64] {
fn copy_from_slice_or_err(&mut self, src: &Self) -> Result<(), ErrorCode> {
if self.len() == src.len() {
// SAFETY: `self` is valid for `self.len()` elements by definition, and `src` was
// checked to have the same length. The slices cannot overlap because
// mutable references are exclusive.
// SAFETY: `self` is valid for `self.len()` elements by definition,
// and `src` was checked to have the same length. The slices cannot
// overlap because mutable references are exclusive.
unsafe {
ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), self.len());
}
Expand All @@ -78,9 +95,9 @@ impl CopyOrErr for [u64] {
impl CopyOrErr for [usize] {
fn copy_from_slice_or_err(&mut self, src: &Self) -> Result<(), ErrorCode> {
if self.len() == src.len() {
// SAFETY: `self` is valid for `self.len()` elements by definition, and `src` was
// checked to have the same length. The slices cannot overlap because
// mutable references are exclusive.
// SAFETY: `self` is valid for `self.len()` elements by definition,
// and `src` was checked to have the same length. The slices cannot
// overlap because mutable references are exclusive.
unsafe {
ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), self.len());
}
Expand Down
Loading

0 comments on commit abf7669

Please sign in to comment.