Skip to content

Commit faf1f32

Browse files
committed
std: xous: add panic support
When a process panics, it will send the panic message as a series of scalar messages to the log server. This ensures that messages get out while making sure the thread can clean up after itself. Additionally, a panic message is sent to the graphics server if one exists. Signed-off-by: Sean Cross <[email protected]>
1 parent 94af09f commit faf1f32

File tree

1 file changed

+94
-10
lines changed

1 file changed

+94
-10
lines changed

library/std/src/sys/xous/stdio.rs

Lines changed: 94 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
use crate::cell::RefCell;
12
use crate::io;
3+
thread_local! { static PANIC_WRITER: RefCell<Option<PanicWriter>> = RefCell::new(None) }
24

35
pub struct Stdin;
46
pub struct Stdout {}
@@ -32,14 +34,7 @@ impl io::Write for Stdout {
3234
for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) {
3335
*dest = *src;
3436
}
35-
crate::os::xous::ffi::lend(
36-
connection,
37-
1,
38-
&lend_buffer.0,
39-
0,
40-
chunk.len(),
41-
)
42-
.unwrap();
37+
crate::os::xous::ffi::lend(connection, 1, &lend_buffer.0, 0, chunk.len()).unwrap();
4338
}
4439
Ok(buf.len())
4540
}
@@ -71,6 +66,95 @@ pub fn is_ebadf(_err: &io::Error) -> bool {
7166
true
7267
}
7368

74-
pub fn panic_output() -> Option<Vec<u8>> {
75-
None
69+
#[derive(Copy, Clone)]
70+
pub struct PanicWriter {
71+
conn: crate::os::xous::ffi::Connection,
72+
gfx_conn: Option<crate::os::xous::ffi::Connection>,
73+
}
74+
75+
impl PanicWriter {
76+
// Group `usize` bytes into a `usize` and return it, beginning
77+
// from `offset` * sizeof(usize) bytes from the start. For example,
78+
// `group_or_null([1,2,3,4,5,6,7,8], 1)` on a 32-bit system will
79+
// return a usize with 5678 packed into it.
80+
fn group_or_null(data: &[u8], offset: usize) -> usize {
81+
let start = offset * core::mem::size_of::<usize>();
82+
let mut out_array = [0u8; core::mem::size_of::<usize>()];
83+
if start < data.len() {
84+
for (dest, src) in out_array.iter_mut().zip(&data[start..]) {
85+
*dest = *src;
86+
}
87+
}
88+
usize::from_le_bytes(out_array)
89+
}
90+
}
91+
92+
impl io::Write for PanicWriter {
93+
fn write(&mut self, s: &[u8]) -> core::result::Result<usize, io::Error> {
94+
for c in s.chunks(core::mem::size_of::<usize>() * 4) {
95+
// Text is grouped into 4x `usize` words. The id is 1100 plus
96+
// the number of characters in this message.
97+
// Ignore errors since we're already panicking.
98+
crate::os::xous::ffi::try_scalar(
99+
self.conn,
100+
[
101+
1100 + c.len(),
102+
Self::group_or_null(&c, 0),
103+
Self::group_or_null(&c, 1),
104+
Self::group_or_null(&c, 2),
105+
Self::group_or_null(&c, 3),
106+
],
107+
)
108+
.ok();
109+
}
110+
111+
// Serialze the text to the graphics panic handler, only if we were able
112+
// to acquire a connection to it. Text length is encoded in the `valid` field,
113+
// the data itself in the buffer. Typically several messages are require to
114+
// fully transmit the entire panic message.
115+
if let Some(connection) = self.gfx_conn {
116+
#[repr(align(4096))]
117+
struct Request([u8; 4096]);
118+
let mut request = Request([0u8; 4096]);
119+
for (&s, d) in s.iter().zip(request.0.iter_mut()) {
120+
*d = s;
121+
}
122+
crate::os::xous::ffi::try_lend(
123+
connection,
124+
0, /* AppendPanicText */
125+
&request.0,
126+
0,
127+
s.len(),
128+
)
129+
.ok();
130+
}
131+
Ok(s.len())
132+
}
133+
134+
// Tests show that this does not seem to be reliably called at the end of a panic
135+
// print, so, we can't rely on this to e.g. trigger a graphics update.
136+
fn flush(&mut self) -> io::Result<()> {
137+
Ok(())
138+
}
139+
}
140+
141+
pub fn panic_output() -> Option<impl io::Write> {
142+
PANIC_WRITER.with(|pwr| {
143+
if pwr.borrow().is_none() {
144+
// Generally this won't fail because every server has already allocated this connection.
145+
let conn = crate::os::xous::services::log_server();
146+
147+
// This is possibly fallible in the case that the connection table is full,
148+
// and we can't make the connection to the graphics server. Most servers do not already
149+
// have this connection.
150+
let gfx_conn = crate::os::xous::services::try_connect("panic-to-screen!");
151+
152+
let pw = PanicWriter { conn, gfx_conn };
153+
154+
// Send the "We're panicking" message (1000).
155+
crate::os::xous::ffi::scalar(conn, [1000 /* BeginPanic */, 0, 0, 0, 0]).ok();
156+
*pwr.borrow_mut() = Some(pw);
157+
}
158+
*pwr.borrow()
159+
})
76160
}

0 commit comments

Comments
 (0)