twizzler_abi/syscall/
thread_control.rs

1use core::mem::MaybeUninit;
2
3use num_enum::{FromPrimitive, IntoPrimitive};
4use twizzler_rt_abi::error::TwzError;
5
6use super::{convert_codes_to_result, twzerr, Syscall};
7use crate::{
8    arch::{syscall::raw_syscall, ArchRegisters},
9    object::ObjID,
10    thread::ExecutionState,
11    upcall::{ResumeFlags, UpcallFrame, UpcallTarget},
12};
13
14#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, FromPrimitive, IntoPrimitive)]
15#[repr(u64)]
16/// Possible Thread Control operations
17pub enum ThreadControl {
18    #[default]
19    /// Exit the thread. arg1 and arg2 should be code and location respectively, where code
20    /// contains a 64-bit value to write into *location, followed by the kernel performing a
21    /// thread-wake event on the memory word at location. If location is null, the write and
22    /// thread-wake do not occur.
23    Exit = 0,
24    /// Yield the thread's CPU time now. The actual effect of this is unspecified, but it acts as a
25    /// hint to the kernel that this thread does not need to run right now. The kernel, of course,
26    /// is free to ignore this hint.
27    Yield = 1,
28    /// Set thread's TLS pointer
29    SetTls = 2,
30    /// Get the thread's TLS pointer.
31    GetTls = 3,
32    /// Set the thread's upcall pointer (child threads in the same virtual address space will
33    /// inherit).
34    SetUpcall = 4,
35    /// Get the upcall pointer.
36    GetUpcall = 5,
37    /// Read the thread's CPU state. The thread must be suspended.
38    ReadRegisters = 6,
39    /// Write the thread's CPU state. The thread must be suspended.
40    WriteRegisters = 7,
41    /// Send a user-defined async or sync event to the thread.
42    SendMessage = 8,
43    /// Change the thread's state. Allowed transitions are:
44    /// running -> suspended
45    /// suspended -> running
46    /// running -> exited
47    ChangeState = 9,
48    /// Set the Trap State for the thread.
49    SetTrapState = 10,
50    /// Get the Trap State for the thread.
51    GetTrapState = 11,
52    /// Set a thread's priority. Threads require special permission to increase their priority.
53    SetPriority = 12,
54    /// Get a thread's priority.
55    GetPriority = 13,
56    /// Set a thread's affinity.
57    SetAffinity = 14,
58    /// Get a thread's affinity.
59    GetAffinity = 15,
60    /// Resume from an upcall.
61    ResumeFromUpcall = 16,
62    /// Get the repr ID of the calling thread.
63    GetSelfId = 17,
64    /// Get the ID of the active security context.
65    GetActiveSctxId = 18,
66    /// Set the ID of the active security context.
67    SetActiveSctxId = 19,
68    /// Set trace events.
69    SetTraceEvents = 20,
70    /// Get trace events.
71    GetTraceEvents = 21,
72}
73
74/// Exit the thread. The code will be written to the [crate::thread::ThreadRepr] for the current
75/// thread as part of updating the status and code to indicate thread has exited.
76pub fn sys_thread_exit(code: u64) -> ! {
77    unsafe {
78        raw_syscall(
79            Syscall::ThreadCtrl,
80            &[0, 0, ThreadControl::Exit as u64, code],
81        );
82    }
83    unreachable!()
84}
85
86/// Yield the thread's CPU time now. The actual effect of this is unspecified, but it acts as a
87/// hint to the kernel that this thread does not need to run right now. The kernel, of course,
88/// is free to ignore this hint.
89pub fn sys_thread_yield() {
90    unsafe {
91        raw_syscall(Syscall::ThreadCtrl, &[0, 0, ThreadControl::Yield as u64]);
92    }
93}
94
95/// Set the current kernel thread's TLS pointer. On x86_64, for example, this changes user's FS
96/// segment base to the supplies TLS value.
97pub fn sys_thread_settls(tls: u64) {
98    unsafe {
99        raw_syscall(
100            Syscall::ThreadCtrl,
101            &[0, 0, ThreadControl::SetTls as u64, tls],
102        );
103    }
104}
105
106/// Get the repr ID of the calling thread.
107pub fn sys_thread_self_id() -> ObjID {
108    let (hi, lo) = unsafe {
109        raw_syscall(
110            Syscall::ThreadCtrl,
111            &[0, 0, ThreadControl::GetSelfId as u64],
112        )
113    };
114    ObjID::from_parts([hi, lo])
115}
116
117/// Get the active security context ID for the calling thread.
118pub fn sys_thread_active_sctx_id() -> ObjID {
119    let (hi, lo) = unsafe {
120        raw_syscall(
121            Syscall::ThreadCtrl,
122            &[0, 0, ThreadControl::GetActiveSctxId as u64],
123        )
124    };
125    ObjID::from_parts([hi, lo])
126}
127
128/// Get the active security context ID for the calling thread.
129pub fn sys_thread_set_active_sctx_id(id: ObjID) -> Result<(), TwzError> {
130    let (code, val) = unsafe {
131        raw_syscall(
132            Syscall::ThreadCtrl,
133            &[
134                0,
135                0,
136                ThreadControl::SetActiveSctxId as u64,
137                id.parts()[0],
138                id.parts()[1],
139            ],
140        )
141    };
142    convert_codes_to_result(code, val, |c, _| c != 0, |_, _| (), twzerr)
143}
144
145/// Set the upcall location for this thread.
146pub fn sys_thread_set_upcall(target: UpcallTarget) {
147    unsafe {
148        raw_syscall(
149            Syscall::ThreadCtrl,
150            &[
151                0,
152                0,
153                ThreadControl::SetUpcall as u64,
154                (&target as *const _) as usize as u64,
155            ],
156        );
157    }
158}
159
160/// Resume from an upcall, restoring registers. If you can
161/// resume yourself in userspace, this call is not necessary.
162///
163/// # Safety
164/// The frame argument must point to a valid upcall frame with
165/// a valid register state.
166pub unsafe fn sys_thread_resume_from_upcall(frame: &UpcallFrame, flags: ResumeFlags) -> ! {
167    unsafe {
168        raw_syscall(
169            Syscall::ThreadCtrl,
170            &[
171                0,
172                0,
173                ThreadControl::ResumeFromUpcall as u64,
174                frame as *const _ as usize as u64,
175                flags.bits(),
176            ],
177        );
178        unreachable!()
179    }
180}
181
182/// Get the current kernel thread's TLS pointer.
183pub fn sys_thread_gettls() -> u64 {
184    let (tls, _) =
185        unsafe { raw_syscall(Syscall::ThreadCtrl, &[0, 0, ThreadControl::GetTls as u64]) };
186    tls
187}
188
189/// Read the thread's CPU state. The thread must be suspended.
190pub fn sys_thread_read_registers(target: ObjID) -> Result<ArchRegisters, TwzError> {
191    let mut regs = MaybeUninit::zeroed();
192    let (code, val) = unsafe {
193        raw_syscall(
194            Syscall::ThreadCtrl,
195            &[
196                target.parts()[0],
197                target.parts()[1],
198                ThreadControl::ReadRegisters as u64,
199                regs.as_mut_ptr() as usize as u64,
200            ],
201        )
202    };
203    convert_codes_to_result(
204        code,
205        val,
206        |c, _| c != 0,
207        move |_, _| unsafe { regs.assume_init() },
208        twzerr,
209    )
210}
211
212/// Write the thread's CPU state. The thread must be suspended.
213pub fn sys_thread_write_registers(target: ObjID, regs: &ArchRegisters) -> Result<(), TwzError> {
214    let (code, val) = unsafe {
215        raw_syscall(
216            Syscall::ThreadCtrl,
217            &[
218                target.parts()[0],
219                target.parts()[1],
220                ThreadControl::WriteRegisters as u64,
221                regs as *const _ as usize as u64,
222            ],
223        )
224    };
225    convert_codes_to_result(code, val, |c, _| c != 0, |_, _| (), twzerr)
226}
227
228/// Send a user-defined async or sync event to the thread.
229pub fn sys_thread_send_message(target: ObjID, message: u64, flags: u64) -> Result<(), TwzError> {
230    let (code, val) = unsafe {
231        raw_syscall(
232            Syscall::ThreadCtrl,
233            &[
234                target.parts()[0],
235                target.parts()[1],
236                ThreadControl::SendMessage as u64,
237                message,
238                flags,
239            ],
240        )
241    };
242    convert_codes_to_result(code, val, |c, _| c != 0, |_, _| (), twzerr)
243}
244
245/// Change the thread's state. If successful, returns the previous state.
246pub fn sys_thread_change_state(
247    target: ObjID,
248    new_state: ExecutionState,
249) -> Result<ExecutionState, TwzError> {
250    let (code, val) = unsafe {
251        raw_syscall(
252            Syscall::ThreadCtrl,
253            &[
254                target.parts()[0],
255                target.parts()[1],
256                ThreadControl::ChangeState as u64,
257                new_state.to_status(),
258            ],
259        )
260    };
261    convert_codes_to_result(
262        code,
263        val,
264        |c, _| c != 0,
265        |_, v| ExecutionState::from_status(v),
266        twzerr,
267    )
268}
269
270/// Set the Trap State for the thread.
271pub fn sys_thread_set_trap_state(target: ObjID, trap_state: u64) -> Result<(), TwzError> {
272    let (code, val) = unsafe {
273        raw_syscall(
274            Syscall::ThreadCtrl,
275            &[
276                target.parts()[0],
277                target.parts()[1],
278                ThreadControl::SetTrapState as u64,
279                trap_state,
280            ],
281        )
282    };
283    convert_codes_to_result(code, val, |c, _| c != 0, |_, _| (), twzerr)
284}
285
286/// Get the Trap State for the thread.
287pub fn sys_thread_get_trap_state(target: ObjID) -> Result<u64, TwzError> {
288    let (code, val) = unsafe {
289        raw_syscall(
290            Syscall::ThreadCtrl,
291            &[
292                target.parts()[0],
293                target.parts()[1],
294                ThreadControl::GetTrapState as u64,
295            ],
296        )
297    };
298    convert_codes_to_result(code, val, |c, _| c != 0, |_, v| v, twzerr)
299}
300
301/// Set the Trap State for the thread.
302pub fn sys_thread_set_trace_events(target: ObjID, events: u64) -> Result<(), TwzError> {
303    let (code, val) = unsafe {
304        raw_syscall(
305            Syscall::ThreadCtrl,
306            &[
307                target.parts()[0],
308                target.parts()[1],
309                ThreadControl::SetTraceEvents as u64,
310                events,
311            ],
312        )
313    };
314    convert_codes_to_result(code, val, |c, _| c != 0, |_, _| (), twzerr)
315}
316
317/// Get the Trap State for the thread.
318pub fn sys_thread_get_trace_events(target: ObjID) -> Result<u64, TwzError> {
319    let (code, val) = unsafe {
320        raw_syscall(
321            Syscall::ThreadCtrl,
322            &[
323                target.parts()[0],
324                target.parts()[1],
325                ThreadControl::GetTraceEvents as u64,
326            ],
327        )
328    };
329    convert_codes_to_result(code, val, |c, _| c != 0, |_, v| v, twzerr)
330}
331
332pub const PERTHREAD_TRACE_GEN_SAMPLE: u64 = 1;
333
334pub fn sys_thread_ctrl(
335    target: Option<ObjID>,
336    cmd: ThreadControl,
337    arg0: usize,
338    arg1: usize,
339    arg2: usize,
340) -> (u64, u64) {
341    let target = target.unwrap_or(ObjID::new(0));
342    let ids = target.parts();
343    unsafe {
344        raw_syscall(
345            Syscall::ThreadCtrl,
346            &[
347                ids[0],
348                ids[1],
349                cmd as u64,
350                arg0 as u64,
351                arg1 as u64,
352                arg2 as u64,
353            ],
354        )
355    };
356    todo!("not ready yet!")
357}