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/// Get the upcall location for this thread.
146pub fn sys_thread_get_upcall() -> Result<UpcallTarget, TwzError> {
147    let mut target = MaybeUninit::<UpcallTarget>::uninit();
148    let (code, val) = unsafe {
149        raw_syscall(
150            Syscall::ThreadCtrl,
151            &[
152                0,
153                0,
154                ThreadControl::GetUpcall as u64,
155                target.as_mut_ptr() as usize as u64,
156            ],
157        )
158    };
159    convert_codes_to_result(
160        code,
161        val,
162        |c, _| c != 0,
163        |_, _| unsafe { target.assume_init() },
164        twzerr,
165    )
166}
167
168/// Set the upcall location for this thread.
169pub fn sys_thread_set_upcall(target: UpcallTarget) -> Result<(), TwzError> {
170    let (code, val) = unsafe {
171        raw_syscall(
172            Syscall::ThreadCtrl,
173            &[
174                0,
175                0,
176                ThreadControl::SetUpcall as u64,
177                (&target as *const _) as usize as u64,
178            ],
179        )
180    };
181    convert_codes_to_result(code, val, |c, _| c != 0, |_, _| (), twzerr)
182}
183
184/// Resume from an upcall, restoring registers. If you can
185/// resume yourself in userspace, this call is not necessary.
186///
187/// # Safety
188/// The frame argument must point to a valid upcall frame with
189/// a valid register state.
190pub unsafe fn sys_thread_resume_from_upcall(frame: &UpcallFrame, flags: ResumeFlags) -> ! {
191    unsafe {
192        raw_syscall(
193            Syscall::ThreadCtrl,
194            &[
195                0,
196                0,
197                ThreadControl::ResumeFromUpcall as u64,
198                frame as *const _ as usize as u64,
199                flags.bits(),
200            ],
201        );
202        unreachable!()
203    }
204}
205
206/// Get the current kernel thread's TLS pointer.
207pub fn sys_thread_gettls() -> u64 {
208    let (tls, _) =
209        unsafe { raw_syscall(Syscall::ThreadCtrl, &[0, 0, ThreadControl::GetTls as u64]) };
210    tls
211}
212
213/// Read the thread's CPU state. The thread must be suspended.
214pub fn sys_thread_read_registers(target: ObjID) -> Result<ArchRegisters, TwzError> {
215    let mut regs = MaybeUninit::zeroed();
216    let (code, val) = unsafe {
217        raw_syscall(
218            Syscall::ThreadCtrl,
219            &[
220                target.parts()[0],
221                target.parts()[1],
222                ThreadControl::ReadRegisters as u64,
223                regs.as_mut_ptr() as usize as u64,
224            ],
225        )
226    };
227    convert_codes_to_result(
228        code,
229        val,
230        |c, _| c != 0,
231        move |_, _| unsafe { regs.assume_init() },
232        twzerr,
233    )
234}
235
236/// Write the thread's CPU state. The thread must be suspended.
237pub fn sys_thread_write_registers(target: ObjID, regs: &ArchRegisters) -> Result<(), TwzError> {
238    let (code, val) = unsafe {
239        raw_syscall(
240            Syscall::ThreadCtrl,
241            &[
242                target.parts()[0],
243                target.parts()[1],
244                ThreadControl::WriteRegisters as u64,
245                regs as *const _ as usize as u64,
246            ],
247        )
248    };
249    convert_codes_to_result(code, val, |c, _| c != 0, |_, _| (), twzerr)
250}
251
252/// Send a user-defined async or sync event to the thread.
253pub fn sys_thread_send_message(target: ObjID, message: u64, flags: u64) -> Result<(), TwzError> {
254    let (code, val) = unsafe {
255        raw_syscall(
256            Syscall::ThreadCtrl,
257            &[
258                target.parts()[0],
259                target.parts()[1],
260                ThreadControl::SendMessage as u64,
261                message,
262                flags,
263            ],
264        )
265    };
266    convert_codes_to_result(code, val, |c, _| c != 0, |_, _| (), twzerr)
267}
268
269/// Change the thread's state. If successful, returns the previous state.
270pub fn sys_thread_change_state(
271    target: ObjID,
272    new_state: ExecutionState,
273) -> Result<ExecutionState, TwzError> {
274    let (code, val) = unsafe {
275        raw_syscall(
276            Syscall::ThreadCtrl,
277            &[
278                target.parts()[0],
279                target.parts()[1],
280                ThreadControl::ChangeState as u64,
281                new_state.to_status(),
282            ],
283        )
284    };
285    convert_codes_to_result(
286        code,
287        val,
288        |c, _| c != 0,
289        |_, v| ExecutionState::from_status(v),
290        twzerr,
291    )
292}
293
294/// Set the Trap State for the thread.
295pub fn sys_thread_set_trap_state(target: ObjID, trap_state: u64) -> Result<(), TwzError> {
296    let (code, val) = unsafe {
297        raw_syscall(
298            Syscall::ThreadCtrl,
299            &[
300                target.parts()[0],
301                target.parts()[1],
302                ThreadControl::SetTrapState as u64,
303                trap_state,
304            ],
305        )
306    };
307    convert_codes_to_result(code, val, |c, _| c != 0, |_, _| (), twzerr)
308}
309
310/// Get the Trap State for the thread.
311pub fn sys_thread_get_trap_state(target: ObjID) -> Result<u64, TwzError> {
312    let (code, val) = unsafe {
313        raw_syscall(
314            Syscall::ThreadCtrl,
315            &[
316                target.parts()[0],
317                target.parts()[1],
318                ThreadControl::GetTrapState as u64,
319            ],
320        )
321    };
322    convert_codes_to_result(code, val, |c, _| c != 0, |_, v| v, twzerr)
323}
324
325/// Set the Trap State for the thread.
326pub fn sys_thread_set_trace_events(target: ObjID, events: u64) -> Result<(), TwzError> {
327    let (code, val) = unsafe {
328        raw_syscall(
329            Syscall::ThreadCtrl,
330            &[
331                target.parts()[0],
332                target.parts()[1],
333                ThreadControl::SetTraceEvents as u64,
334                events,
335            ],
336        )
337    };
338    convert_codes_to_result(code, val, |c, _| c != 0, |_, _| (), twzerr)
339}
340
341/// Get the Trap State for the thread.
342pub fn sys_thread_get_trace_events(target: ObjID) -> Result<u64, TwzError> {
343    let (code, val) = unsafe {
344        raw_syscall(
345            Syscall::ThreadCtrl,
346            &[
347                target.parts()[0],
348                target.parts()[1],
349                ThreadControl::GetTraceEvents as u64,
350            ],
351        )
352    };
353    convert_codes_to_result(code, val, |c, _| c != 0, |_, v| v, twzerr)
354}
355
356pub const PERTHREAD_TRACE_GEN_SAMPLE: u64 = 1;
357
358pub fn sys_thread_ctrl(
359    target: Option<ObjID>,
360    cmd: ThreadControl,
361    arg0: usize,
362    arg1: usize,
363    arg2: usize,
364) -> (u64, u64) {
365    let target = target.unwrap_or(ObjID::new(0));
366    let ids = target.parts();
367    unsafe {
368        raw_syscall(
369            Syscall::ThreadCtrl,
370            &[
371                ids[0],
372                ids[1],
373                cmd as u64,
374                arg0 as u64,
375                arg1 as u64,
376                arg2 as u64,
377            ],
378        )
379    };
380    todo!("not ready yet!")
381}