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.
45//! Provides low-level debugging functionality to userspace. The system call
6//! interface is documented in doc/syscalls/00008_low_level_debug.md.
78mod fmt;
910use core::cell::Cell;
1112use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
13use kernel::hil::uart::{Transmit, TransmitClient};
14use kernel::syscall::CommandReturn;
15use kernel::{ErrorCode, ProcessId};
1617// LowLevelDebug requires a &mut [u8] buffer of length at least BUF_LEN.
18pub use fmt::BUF_LEN;
1920pub const DRIVER_NUM: usize = crate::driver::NUM::LowLevelDebug as usize;
2122pub 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.
31grant_failed: Cell<bool>,
32 uart: &'u U,
33}
3435impl<'u, U: Transmit<'u>> LowLevelDebug<'u, U> {
36pub 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}
4950impl<'u, U: Transmit<'u>> kernel::syscall::SyscallDriver for LowLevelDebug<'u, U> {
51fn command(
52&self,
53 minor_num: usize,
54 r2: usize,
55 r3: usize,
56 caller_id: ProcessId,
57 ) -> CommandReturn {
58match minor_num {
590 => return CommandReturn::success(),
601 => self.push_entry(DebugEntry::AlertCode(r2), caller_id),
612 => self.push_entry(DebugEntry::Print1(r2), caller_id),
623 => self.push_entry(DebugEntry::Print2(r2, r3), caller_id),
63_ => return CommandReturn::failure(ErrorCode::NOSUPPORT),
64 }
65 CommandReturn::success()
66 }
6768fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
69self.grant.enter(processid, |_, _| {})
70 }
71}
7273impl<'u, U: Transmit<'u>> TransmitClient for LowLevelDebug<'u, U> {
74fn 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.
8283 // Prioritize printing the "grant init failed" message over per-app
84 // debug entries.
85if self.grant_failed.take() {
86const MESSAGE: &[u8] = b"LowLevelDebug: grant init failed\n";
87 tx_buffer[..MESSAGE.len()].copy_from_slice(MESSAGE);
8889let _ = self.uart.transmit_buffer(tx_buffer, MESSAGE.len()).map_err(
90 |(_, returned_buffer)| {
91self.buffer.set(Some(returned_buffer));
92 },
93 );
94return;
95 }
9697for process_grant in self.grant.iter() {
98let processid = process_grant.processid();
99let (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 });
103let to_print = match first_entry {
104None => continue,
105Some(to_print) => to_print,
106 };
107self.transmit_entry(tx_buffer, app_num, to_print);
108return;
109 }
110self.buffer.set(Some(tx_buffer));
111 }
112}
113114// -----------------------------------------------------------------------------
115// Implementation details below
116// -----------------------------------------------------------------------------
117118impl<'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.
121fn push_entry(&self, entry: DebugEntry, processid: ProcessId) {
122use DebugEntry::Dropped;
123124if let Some(buffer) = self.buffer.take() {
125self.transmit_entry(buffer, processid.id(), entry);
126return;
127 }
128129let result = self.grant.enter(processid, |borrow, _| {
130for queue_entry in &mut borrow.queue {
131if queue_entry.is_none() {
132*queue_entry = Some(entry);
133return;
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.
140borrow.queue[QUEUE_SIZE - 1] = match borrow.queue[QUEUE_SIZE - 1] {
141Some(Dropped(count)) => Some(Dropped(count + 1)),
142_ => Some(Dropped(2)),
143 };
144 });
145146// 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.
149if result.is_err() {
150self.grant_failed.set(true);
151 }
152 }
153154// Immediately prints the provided entry to the UART.
155fn transmit_entry(&self, buffer: &'static mut [u8], app_num: usize, entry: DebugEntry) {
156let 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.
159let _ = self
160.uart
161 .transmit_buffer(buffer, msg_len)
162 .map_err(|(_, returned_buffer)| {
163self.buffer.set(Some(returned_buffer));
164 });
165 }
166}
167168// 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;
172173#[derive(Default)]
174pub struct AppData {
175 queue: [Option<DebugEntry>; QUEUE_SIZE],
176}
177178#[derive(Clone, Copy)]
179pub(crate) enum DebugEntry {
180 Dropped(usize), // Some debug messages were dropped
181AlertCode(usize), // Display a predefined alert code
182Print1(usize), // Print a single number
183Print2(usize, usize), // Print two numbers
184}