Skip to content

Commit 94af09f

Browse files
committed
std: xous: add thread support
Add initial support for threads on Xous. This includes thread creation and joining. Signed-off-by: Sean Cross <[email protected]>
1 parent 303099c commit 94af09f

File tree

2 files changed

+150
-1
lines changed

2 files changed

+150
-1
lines changed

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ pub mod pipe;
2525
#[path = "../unsupported/process.rs"]
2626
pub mod process;
2727
pub mod stdio;
28-
#[path = "../unsupported/thread.rs"]
2928
pub mod thread;
3029
pub mod thread_local_key;
3130
#[path = "../unsupported/time.rs"]

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

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
use crate::ffi::CStr;
2+
use crate::io;
3+
use crate::num::NonZeroUsize;
4+
use crate::os::xous::ffi::MemoryFlags;
5+
use crate::time::Duration;
6+
use core::arch::asm;
7+
8+
pub struct Thread {
9+
tid: crate::os::xous::ffi::ThreadId,
10+
}
11+
12+
pub const DEFAULT_MIN_STACK_SIZE: usize = 131072;
13+
const MIN_STACK_SIZE: usize = 4096;
14+
pub const GUARD_PAGE_SIZE: usize = 4096;
15+
16+
impl Thread {
17+
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
18+
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
19+
let p = Box::into_raw(box p);
20+
let stack_size = crate::cmp::max(stack, MIN_STACK_SIZE);
21+
22+
// Allocate the whole thing, then divide it up after the fact. This ensures that
23+
// even if there's a context switch during this function, the whole stack plus
24+
// guard pages will remain contiguous.
25+
let stack_plus_guard_pages: core::ops::Range<*mut u8> = crate::os::xous::ffi::map_memory(
26+
None,
27+
None,
28+
stack_size + GUARD_PAGE_SIZE + GUARD_PAGE_SIZE,
29+
MemoryFlags::R | MemoryFlags::W | MemoryFlags::X,
30+
)
31+
.map_err(|code| io::Error::from_raw_os_error(code as i32))?;
32+
33+
// No access to this page. Note: Write-only pages are illegal, and will
34+
// cause an access violation.
35+
let guard_page_pre = unsafe {
36+
stack_plus_guard_pages.start..stack_plus_guard_pages.start.add(GUARD_PAGE_SIZE)
37+
};
38+
crate::os::xous::ffi::update_memory_flags(&guard_page_pre, MemoryFlags::W)
39+
.map_err(|code| io::Error::from_raw_os_error(code as i32))?;
40+
41+
// Stack sandwiched between guard pages
42+
let stack = unsafe { guard_page_pre.end..guard_page_pre.end.add(stack_size) };
43+
44+
// No access to this page. Note: Write-only pages are illegal, and will
45+
// cause an access violation.
46+
let guard_page_post = unsafe { stack.end..stack.end.add(GUARD_PAGE_SIZE) };
47+
crate::os::xous::ffi::update_memory_flags(&guard_page_post, MemoryFlags::W)
48+
.map_err(|code| io::Error::from_raw_os_error(code as i32))?;
49+
50+
// Ensure that the pages are laid out like we expect them.
51+
let pre_addr = guard_page_pre.start as usize;
52+
let stack_addr = stack.start as usize;
53+
let post_addr = guard_page_post.start as usize;
54+
55+
assert_eq!(pre_addr + GUARD_PAGE_SIZE, stack_addr);
56+
assert_eq!(pre_addr + GUARD_PAGE_SIZE + stack_size, post_addr);
57+
58+
let tid = crate::os::xous::ffi::create_thread(
59+
thread_start as *mut usize,
60+
stack,
61+
p as usize,
62+
pre_addr,
63+
stack_size,
64+
0,
65+
)
66+
.map_err(|code| io::Error::from_raw_os_error(code as i32))?;
67+
68+
extern "C" fn thread_start(main: *mut usize, guard_page_pre: usize, stack_size: usize) {
69+
unsafe {
70+
// // Next, set up our stack overflow handler which may get triggered if we run
71+
// // out of stack.
72+
// let _handler = stack_overflow::Handler::new();
73+
// Finally, let's run some code.
74+
Box::from_raw(main as *mut Box<dyn FnOnce()>)();
75+
}
76+
77+
// Destroy TLS, which will free the TLS page
78+
unsafe {
79+
crate::sys::thread_local_key::destroy_tls();
80+
}
81+
82+
// Deallocate the stack memory, along with the guard pages.
83+
let mapped_memory_base = guard_page_pre;
84+
let mapped_memory_length = GUARD_PAGE_SIZE + stack_size + GUARD_PAGE_SIZE;
85+
unsafe {
86+
asm!(
87+
"ecall",
88+
in("a0") crate::os::xous::ffi::Syscall::UnmapMemory as usize,
89+
in("a1") mapped_memory_base,
90+
in("a2") mapped_memory_length,
91+
options(nomem, nostack)
92+
);
93+
}
94+
95+
// Exit the thread by returning to the magic address 0xff80_3000usize,
96+
// which tells the kernel to deallcate this thread.
97+
unsafe {
98+
asm!("ret", in("a0") 0, in("ra") 0xff80_3000usize,
99+
options(nomem, nostack, noreturn)
100+
);
101+
}
102+
}
103+
104+
Ok(Thread { tid })
105+
}
106+
107+
pub fn yield_now() {
108+
crate::os::xous::ffi::do_yield();
109+
}
110+
111+
pub fn set_name(_name: &CStr) {
112+
// nope
113+
}
114+
115+
pub fn sleep(dur: Duration) {
116+
// Because the sleep server works on units of `usized milliseconds`, split
117+
// the messages up into these chunks. This means we may run into issues
118+
// if you try to sleep a thread for more than 49 days on a 32-bit system.
119+
let mut millis = dur.as_millis();
120+
while millis > 0 {
121+
let sleep_duration =
122+
if millis > (usize::MAX as _) { usize::MAX } else { millis as usize };
123+
crate::os::xous::ffi::blocking_scalar(
124+
crate::os::xous::services::ticktimer_server(),
125+
[1 /* SleepMs */, sleep_duration, 0, 0, 0],
126+
)
127+
.expect("failed to send message to ticktimer server");
128+
millis -= sleep_duration as u128;
129+
}
130+
}
131+
132+
pub fn join(self) {
133+
crate::os::xous::ffi::join_thread(self.tid).unwrap();
134+
}
135+
}
136+
137+
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
138+
// We're unicore right now.
139+
Ok(unsafe { NonZeroUsize::new_unchecked(1) })
140+
}
141+
142+
pub mod guard {
143+
pub type Guard = !;
144+
pub unsafe fn current() -> Option<Guard> {
145+
None
146+
}
147+
pub unsafe fn init() -> Option<Guard> {
148+
None
149+
}
150+
}

0 commit comments

Comments
 (0)