1 /*
2 * Copyright (c) 2024 Google Inc. All rights reserved
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 use alloc::boxed::Box;
25 use core::ffi::c_int;
26 use core::ffi::c_void;
27 use core::ffi::CStr;
28 use core::fmt;
29 use core::fmt::Display;
30 use core::fmt::Formatter;
31 use core::ops::Add;
32 use core::ops::Sub;
33 use core::ptr::NonNull;
34 use core::time::Duration;
35
36 use crate::Error;
37
38 use crate::sys::lk_time_ns_t;
39 use crate::sys::thread_create;
40 use crate::sys::thread_resume;
41 use crate::sys::thread_sleep_ns;
42 use crate::sys::thread_t;
43 use crate::sys::DEFAULT_PRIORITY;
44 use crate::sys::DPC_PRIORITY;
45 use crate::sys::HIGHEST_PRIORITY;
46 use crate::sys::HIGH_PRIORITY;
47 use crate::sys::IDLE_PRIORITY;
48 use crate::sys::LOWEST_PRIORITY;
49 use crate::sys::LOW_PRIORITY;
50 use crate::sys::NUM_PRIORITIES;
51
52 use crate::sys::DEFAULT_STACK_SIZE;
53
sleep(dur: Duration)54 pub fn sleep(dur: Duration) {
55 let dur_ns: lk_time_ns_t = dur.as_nanos().try_into().expect("could not convert duration to ns");
56 // Safety: trivially safe
57 unsafe { thread_sleep_ns(dur_ns) };
58 }
59
60 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
61 pub struct Priority(c_int);
62
63 impl Priority {
64 pub const NUM: usize = NUM_PRIORITIES as _;
65
66 pub const LOWEST: Self = Self(LOWEST_PRIORITY as _);
67 pub const HIGHEST: Self = Self(HIGHEST_PRIORITY as _);
68 pub const DPC: Self = Self(DPC_PRIORITY as _);
69 pub const IDLE: Self = Self(IDLE_PRIORITY as _);
70 pub const LOW: Self = Self(LOW_PRIORITY as _);
71 pub const DEFAULT: Self = Self(DEFAULT_PRIORITY as _);
72 pub const HIGH: Self = Self(HIGH_PRIORITY as _);
73 }
74
75 impl Default for Priority {
default() -> Self76 fn default() -> Self {
77 Self::DEFAULT
78 }
79 }
80
81 #[derive(Debug)]
82 pub enum PriorityError {
83 TooLow(c_int),
84 TooHigh(c_int),
85 }
86
87 impl Display for PriorityError {
fmt(&self, f: &mut Formatter) -> fmt::Result88 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
89 match self {
90 PriorityError::TooLow(val) => write!(f, "{val} < {}", Priority::LOWEST.0),
91 PriorityError::TooHigh(val) => write!(f, "{val} > {}", Priority::HIGHEST.0),
92 }
93 }
94 }
95
96 impl TryFrom<c_int> for Priority {
97 type Error = PriorityError;
98
try_from(value: c_int) -> Result<Self, Self::Error>99 fn try_from(value: c_int) -> Result<Self, Self::Error> {
100 if value < Priority::LOWEST.0 {
101 Err(PriorityError::TooLow(value))
102 } else if value > Priority::HIGHEST.0 {
103 Err(PriorityError::TooHigh(value))
104 } else {
105 Ok(Priority(value))
106 }
107 }
108 }
109
110 impl Add<c_int> for Priority {
111 type Output = Self;
112
add(self, other: c_int) -> Self113 fn add(self, other: c_int) -> Self {
114 match self.0.checked_add(other).map(Self::try_from) {
115 None => panic!("priority overflow"),
116 Some(Err(reason)) => panic!("priority out of range: {reason}"),
117 Some(Ok(priority)) => priority,
118 }
119 }
120 }
121
122 impl Sub<c_int> for Priority {
123 type Output = Self;
124
sub(self, other: c_int) -> Self125 fn sub(self, other: c_int) -> Self {
126 match self.0.checked_sub(other).map(Self::try_from) {
127 None => panic!("priority overflow"),
128 Some(Err(reason)) => panic!("priority out of range: {reason}"),
129 Some(Ok(priority)) => priority,
130 }
131 }
132 }
133
134 pub struct JoinHandle {
135 _thread: NonNull<thread_t>,
136 }
137
138 #[derive(Debug)]
139 pub struct Builder<'a> {
140 pub name: Option<&'a CStr>,
141 pub priority: Priority,
142 pub stack_size: usize,
143 }
144
145 impl<'a> Default for Builder<'a> {
default() -> Self146 fn default() -> Self {
147 Self::new()
148 }
149 }
150
151 impl<'a> Builder<'a> {
new() -> Self152 pub const fn new() -> Self {
153 Self { name: None, priority: Priority::DEFAULT, stack_size: DEFAULT_STACK_SIZE as _ }
154 }
155
name(mut self, name: &'a CStr) -> Self156 pub fn name(mut self, name: &'a CStr) -> Self {
157 self.name = Some(name);
158 self
159 }
160
priority(mut self, priority: Priority) -> Self161 pub fn priority(mut self, priority: Priority) -> Self {
162 self.priority = priority;
163 self
164 }
165
stack_size(mut self, stack_size: usize) -> Self166 pub fn stack_size(mut self, stack_size: usize) -> Self {
167 self.stack_size = stack_size;
168 self
169 }
170
spawn<F>(self, f: F) -> Result<JoinHandle, i32> where F: FnOnce() -> c_int + Send + 'static,171 pub fn spawn<F>(self, f: F) -> Result<JoinHandle, i32>
172 where
173 F: FnOnce() -> c_int + Send + 'static,
174 {
175 let name = self.name.unwrap_or(c"thread");
176 // We need a pointer to f that lasts until `thread_entry_wrapper`
177 // gets called. `thread_resume` does not wait for the new
178 // thread to run. Thus, passing the address of a local
179 // wouldn't live long enough so we heap allocate instead.
180 let f = Box::new(f);
181
182 extern "C" fn thread_entry_wrapper<F>(arg: *mut c_void) -> c_int
183 where
184 F: FnOnce() -> c_int + Send + 'static,
185 {
186 // SAFETY:
187 // We passed in a `Box<F>`.
188 // `thread_entry_wrapper` is called exactly once per thread.
189 let f = unsafe { Box::<F>::from_raw(arg as _) };
190 f()
191 }
192
193 // SAFETY:
194 // `name` outlives the call to `thread_create` during which the
195 // string is copied into the newly created thread structure.
196 //
197 // `arg`: The lifetime of `Box<F>` lasts until the end of the
198 // call to `thread_entry_wrapper`. The trusty kernel will pass `arg`
199 // to `thread_entry_wrapper` exactly once per thread.
200 let thread = unsafe {
201 thread_create(
202 name.as_ptr(),
203 Some(thread_entry_wrapper::<F>),
204 Box::<F>::into_raw(f) as *mut c_void,
205 self.priority.0,
206 self.stack_size,
207 )
208 };
209 let thread = NonNull::new(thread).ok_or::<i32>(Error::ERR_GENERIC.into())?;
210 // SAFETY: `thread` is non-null, so `thread_create` initialized it properly.
211 let status = unsafe { thread_resume(thread.as_ptr()) };
212 if status == Error::NO_ERROR.into() {
213 Ok(JoinHandle { _thread: thread })
214 } else {
215 Err(status)
216 }
217 }
218 }
219
spawn<F>(f: F) -> Result<JoinHandle, i32> where F: FnOnce() -> c_int + Send + 'static,220 pub fn spawn<F>(f: F) -> Result<JoinHandle, i32>
221 where
222 F: FnOnce() -> c_int + Send + 'static,
223 {
224 Builder::new().spawn(f)
225 }
226