twizzler_abi/syscall/thread_control.rs
1use num_enum::{FromPrimitive, IntoPrimitive};
2use twizzler_rt_abi::error::TwzError;
3
4use super::{convert_codes_to_result, twzerr, Syscall};
5use crate::{
6 arch::syscall::raw_syscall,
7 object::ObjID,
8 upcall::{UpcallFrame, UpcallTarget},
9};
10
11#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, FromPrimitive, IntoPrimitive)]
12#[repr(u64)]
13/// Possible Thread Control operations
14pub enum ThreadControl {
15 #[default]
16 /// Exit the thread. arg1 and arg2 should be code and location respectively, where code
17 /// contains a 64-bit value to write into *location, followed by the kernel performing a
18 /// thread-wake event on the memory word at location. If location is null, the write and
19 /// thread-wake do not occur.
20 Exit = 0,
21 /// Yield the thread's CPU time now. The actual effect of this is unspecified, but it acts as a
22 /// hint to the kernel that this thread does not need to run right now. The kernel, of course,
23 /// is free to ignore this hint.
24 Yield = 1,
25 /// Set thread's TLS pointer
26 SetTls = 2,
27 /// Get the thread's TLS pointer.
28 GetTls = 3,
29 /// Set the thread's upcall pointer (child threads in the same virtual address space will
30 /// inherit).
31 SetUpcall = 4,
32 /// Get the upcall pointer.
33 GetUpcall = 5,
34 /// Read a register from the thread's CPU state. The thread must be suspended.
35 ReadRegister = 6,
36 /// Write a value to a register in the thread's CPU state. The thread must be suspended.
37 WriteRegister = 7,
38 /// Send a user-defined async or sync event to the thread.
39 SendMessage = 8,
40 /// Change the thread's state. Allowed transitions are:
41 /// running -> suspended
42 /// suspended -> running
43 /// running -> exited
44 ChangeState = 9,
45 /// Set the Trap State for the thread.
46 SetTrapState = 10,
47 /// Get the Trap State for the thread.
48 GetTrapState = 11,
49 /// Set a thread's priority. Threads require special permission to increase their priority.
50 SetPriority = 12,
51 /// Get a thread's priority.
52 GetPriority = 13,
53 /// Set a thread's affinity.
54 SetAffinity = 14,
55 /// Get a thread's affinity.
56 GetAffinity = 15,
57 /// Resume from an upcall.
58 ResumeFromUpcall = 16,
59 /// Get the repr ID of the calling thread.
60 GetSelfId = 17,
61 /// Get the ID of the active security context.
62 GetActiveSctxId = 18,
63 /// Set the ID of the active security context.
64 SetActiveSctxId = 19,
65}
66
67/// Exit the thread. The code will be written to the [crate::thread::ThreadRepr] for the current
68/// thread as part of updating the status and code to indicate thread has exited.
69pub fn sys_thread_exit(code: u64) -> ! {
70 unsafe {
71 raw_syscall(Syscall::ThreadCtrl, &[ThreadControl::Exit as u64, code]);
72 }
73 unreachable!()
74}
75
76/// Yield the thread's CPU time now. The actual effect of this is unspecified, but it acts as a
77/// hint to the kernel that this thread does not need to run right now. The kernel, of course,
78/// is free to ignore this hint.
79pub fn sys_thread_yield() {
80 unsafe {
81 raw_syscall(Syscall::ThreadCtrl, &[ThreadControl::Yield as u64]);
82 }
83}
84
85/// Set the current kernel thread's TLS pointer. On x86_64, for example, this changes user's FS
86/// segment base to the supplies TLS value.
87pub fn sys_thread_settls(tls: u64) {
88 unsafe {
89 raw_syscall(Syscall::ThreadCtrl, &[ThreadControl::SetTls as u64, tls]);
90 }
91}
92
93/// Get the repr ID of the calling thread.
94pub fn sys_thread_self_id() -> ObjID {
95 let (hi, lo) = unsafe { raw_syscall(Syscall::ThreadCtrl, &[ThreadControl::GetSelfId as u64]) };
96 ObjID::from_parts([hi, lo])
97}
98
99/// Get the active security context ID for the calling thread.
100pub fn sys_thread_active_sctx_id() -> ObjID {
101 let (hi, lo) = unsafe {
102 raw_syscall(
103 Syscall::ThreadCtrl,
104 &[ThreadControl::GetActiveSctxId as u64],
105 )
106 };
107 ObjID::from_parts([hi, lo])
108}
109
110/// Get the active security context ID for the calling thread.
111pub fn sys_thread_set_active_sctx_id(id: ObjID) -> Result<(), TwzError> {
112 let (code, val) = unsafe {
113 raw_syscall(
114 Syscall::ThreadCtrl,
115 &[
116 ThreadControl::SetActiveSctxId as u64,
117 id.parts()[0],
118 id.parts()[1],
119 ],
120 )
121 };
122 convert_codes_to_result(code, val, |c, _| c != 0, |_, _| (), twzerr)
123}
124
125/// Set the upcall location for this thread.
126pub fn sys_thread_set_upcall(target: UpcallTarget) {
127 unsafe {
128 raw_syscall(
129 Syscall::ThreadCtrl,
130 &[
131 ThreadControl::SetUpcall as u64,
132 (&target as *const _) as usize as u64,
133 ],
134 );
135 }
136}
137
138/// Resume from an upcall, restoring registers. If you can
139/// resume yourself in userspace, this call is not necessary.
140///
141/// # Safety
142/// The frame argument must point to a valid upcall frame with
143/// a valid register state.
144pub unsafe fn sys_thread_resume_from_upcall(frame: &UpcallFrame) -> ! {
145 unsafe {
146 raw_syscall(
147 Syscall::ThreadCtrl,
148 &[
149 ThreadControl::ResumeFromUpcall as u64,
150 frame as *const _ as usize as u64,
151 ],
152 );
153 unreachable!()
154 }
155}
156
157pub fn sys_thread_ctrl(
158 target: Option<ObjID>,
159 cmd: ThreadControl,
160 arg0: usize,
161 arg1: usize,
162 arg2: usize,
163) -> (u64, u64) {
164 let target = target.unwrap_or(ObjID::new(0));
165 let ids = target.parts();
166 unsafe {
167 raw_syscall(
168 Syscall::ThreadCtrl,
169 &[
170 ids[0],
171 ids[1],
172 cmd as u64,
173 arg0 as u64,
174 arg1 as u64,
175 arg2 as u64,
176 ],
177 )
178 };
179 todo!("not ready yet!")
180}