capsules_core/low_level_debug/
mod.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Provides low-level debugging functionality to userspace. The system call
6//! interface is documented in doc/syscalls/00008_low_level_debug.md.
7
8mod fmt;
9
10use core::cell::Cell;
11
12use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
13use kernel::hil::uart::{Transmit, TransmitClient};
14use kernel::syscall::CommandReturn;
15use kernel::{ErrorCode, ProcessId};
16
17// LowLevelDebug requires a &mut [u8] buffer of length at least BUF_LEN.
18pub use fmt::BUF_LEN;
19
20pub const DRIVER_NUM: usize = crate::driver::NUM::LowLevelDebug as usize;
21
22pub struct LowLevelDebug<'u, U: Transmit<'u>> {
23    buffer: Cell<Option<&'static mut [u8]>>,
24    grant: Grant<AppData, UpcallCount<0>, AllowRoCount<0>, AllowRwCount<0>>,
25    // grant_failed is set to true when LowLevelDebug fails to allocate an app's
26    // grant region. When it has a chance, LowLevelDebug will print a message
27    // indicating a grant initialization has failed, then set this back to
28    // false. Although LowLevelDebug cannot print an application ID without
29    // using grant storage, it will at least output an error indicating some
30    // application's message was dropped.
31    grant_failed: Cell<bool>,
32    uart: &'u U,
33}
34
35impl<'u, U: Transmit<'u>> LowLevelDebug<'u, U> {
36    pub fn new(
37        buffer: &'static mut [u8],
38        uart: &'u U,
39        grant: Grant<AppData, UpcallCount<0>, AllowRoCount<0>, AllowRwCount<0>>,
40    ) -> LowLevelDebug<'u, U> {
41        LowLevelDebug {
42            buffer: Cell::new(Some(buffer)),
43            grant,
44            grant_failed: Cell::new(false),
45            uart,
46        }
47    }
48}
49
50impl<'u, U: Transmit<'u>> kernel::syscall::SyscallDriver for LowLevelDebug<'u, U> {
51    fn command(
52        &self,
53        minor_num: usize,
54        r2: usize,
55        r3: usize,
56        caller_id: ProcessId,
57    ) -> CommandReturn {
58        match minor_num {
59            0 => return CommandReturn::success(),
60            1 => self.push_entry(DebugEntry::AlertCode(r2), caller_id),
61            2 => self.push_entry(DebugEntry::Print1(r2), caller_id),
62            3 => self.push_entry(DebugEntry::Print2(r2, r3), caller_id),
63            _ => return CommandReturn::failure(ErrorCode::NOSUPPORT),
64        }
65        CommandReturn::success()
66    }
67
68    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
69        self.grant.enter(processid, |_, _| {})
70    }
71}
72
73impl<'u, U: Transmit<'u>> TransmitClient for LowLevelDebug<'u, U> {
74    fn transmitted_buffer(
75        &self,
76        tx_buffer: &'static mut [u8],
77        _tx_len: usize,
78        _rval: Result<(), ErrorCode>,
79    ) {
80        // Identify and transmit the next queued entry. If there are no queued
81        // entries remaining, store buffer.
82
83        // Prioritize printing the "grant init failed" message over per-app
84        // debug entries.
85        if self.grant_failed.take() {
86            const MESSAGE: &[u8] = b"LowLevelDebug: grant init failed\n";
87            tx_buffer[..MESSAGE.len()].copy_from_slice(MESSAGE);
88
89            let _ = self.uart.transmit_buffer(tx_buffer, MESSAGE.len()).map_err(
90                |(_, returned_buffer)| {
91                    self.buffer.set(Some(returned_buffer));
92                },
93            );
94            return;
95        }
96
97        for process_grant in self.grant.iter() {
98            let processid = process_grant.processid();
99            let (app_num, first_entry) = process_grant.enter(|owned_app_data, _| {
100                owned_app_data.queue.rotate_left(1);
101                (processid.id(), owned_app_data.queue[QUEUE_SIZE - 1].take())
102            });
103            let to_print = match first_entry {
104                None => continue,
105                Some(to_print) => to_print,
106            };
107            self.transmit_entry(tx_buffer, app_num, to_print);
108            return;
109        }
110        self.buffer.set(Some(tx_buffer));
111    }
112}
113
114// -----------------------------------------------------------------------------
115// Implementation details below
116// -----------------------------------------------------------------------------
117
118impl<'u, U: Transmit<'u>> LowLevelDebug<'u, U> {
119    // If the UART is not busy (the buffer is available), transmits the entry.
120    // Otherwise, adds it to the app's queue.
121    fn push_entry(&self, entry: DebugEntry, processid: ProcessId) {
122        use DebugEntry::Dropped;
123
124        if let Some(buffer) = self.buffer.take() {
125            self.transmit_entry(buffer, processid.id(), entry);
126            return;
127        }
128
129        let result = self.grant.enter(processid, |borrow, _| {
130            for queue_entry in &mut borrow.queue {
131                if queue_entry.is_none() {
132                    *queue_entry = Some(entry);
133                    return;
134                }
135            }
136            // The queue is full, so indicate some entries were dropped. If
137            // there is not a drop entry, replace the last entry with a drop
138            // counter. A new drop counter is initialized to two, as the
139            // overwrite drops an entry plus we're dropping this entry.
140            borrow.queue[QUEUE_SIZE - 1] = match borrow.queue[QUEUE_SIZE - 1] {
141                Some(Dropped(count)) => Some(Dropped(count + 1)),
142                _ => Some(Dropped(2)),
143            };
144        });
145
146        // If we were unable to enter the grant region, schedule a diagnostic
147        // message. This gives the user a chance of figuring out what happened
148        // when LowLevelDebug fails.
149        if result.is_err() {
150            self.grant_failed.set(true);
151        }
152    }
153
154    // Immediately prints the provided entry to the UART.
155    fn transmit_entry(&self, buffer: &'static mut [u8], app_num: usize, entry: DebugEntry) {
156        let msg_len = fmt::format_entry(app_num, entry, buffer);
157        // The uart's error message is ignored because we cannot do anything if
158        // it fails anyway.
159        let _ = self
160            .uart
161            .transmit_buffer(buffer, msg_len)
162            .map_err(|(_, returned_buffer)| {
163                self.buffer.set(Some(returned_buffer));
164            });
165    }
166}
167
168// Length of the debug queue for each app. Each queue entry takes 3 words (tag
169// and 2 usizes to print). The queue will be allocated in an app's grant region
170// when that app first uses the debug driver.
171const QUEUE_SIZE: usize = 4;
172
173#[derive(Default)]
174pub struct AppData {
175    queue: [Option<DebugEntry>; QUEUE_SIZE],
176}
177
178#[derive(Clone, Copy)]
179pub(crate) enum DebugEntry {
180    Dropped(usize),       // Some debug messages were dropped
181    AlertCode(usize),     // Display a predefined alert code
182    Print1(usize),        // Print a single number
183    Print2(usize, usize), // Print two numbers
184}