Skip to main content

safa_abi/
process.rs

1//! Process & Thread related ABI structures
2use core::num::NonZero;
3use core::ops::BitOr;
4
5use crate::ffi::num::ShouldNotBeZero;
6use crate::ffi::option::{COption, OptZero};
7use crate::ffi::ptr::FFINonNull;
8use crate::ffi::slice::Slice;
9use crate::ffi::str::Str;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12#[repr(C)]
13/// ABI structures are structures that are passed to processes by the parent process
14/// for now only stdio file descriptors are passed
15/// you get a pointer to them in the `r8` register at _start (the 5th argument)
16pub struct AbiStructures {
17    pub stdio: ProcessStdio,
18    /// The PID of the parent process of this thread
19    pub parent_process_pid: u32,
20    /// The number of available CPUs for this process (currently the number of available CPUs in the system)
21    pub available_cpus: u32,
22}
23
24impl AbiStructures {
25    pub fn new(stdio: ProcessStdio, parent_pid: u32, available_cpus: u32) -> Self {
26        Self {
27            available_cpus,
28            stdio,
29            parent_process_pid: parent_pid,
30        }
31    }
32}
33
34// Resources are actually 32-bit now but if i change this everything will break
35#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
36#[repr(C)]
37pub struct ProcessStdio {
38    pub stdout: COption<usize>,
39    pub stdin: COption<usize>,
40    pub stderr: COption<usize>,
41}
42
43impl ProcessStdio {
44    pub fn new(stdout: Option<u32>, stdin: Option<u32>, stderr: Option<u32>) -> Self {
45        Self {
46            stdout: stdout.map(|u32| u32 as usize).into(),
47            stdin: stdin.map(|u32| u32 as usize).into(),
48            stderr: stderr.map(|u32| u32 as usize).into(),
49        }
50    }
51
52    /// Convert the ProcessStdio into a tuple of Option<u32>, (stdout, stdin, stderr)
53    pub fn into_rust(self) -> (Option<u32>, Option<u32>, Option<u32>) {
54        (
55            match self.stdout {
56                COption::Some(stdout) => Some(stdout as u32),
57                COption::None => None,
58            },
59            match self.stdin {
60                COption::Some(stdin) => Some(stdin as u32),
61                COption::None => None,
62            },
63            match self.stderr {
64                COption::Some(stderr) => Some(stderr as u32),
65                COption::None => None,
66            },
67        )
68    }
69}
70
71#[derive(Debug, Clone, Copy)]
72#[repr(C)]
73/// Flags for the [crate::syscalls::SyscallTable::SysPSpawn] syscall
74pub struct SpawnFlags(u8);
75impl SpawnFlags {
76    pub const CLONE_RESOURCES: Self = Self(1 << 0);
77    pub const CLONE_CWD: Self = Self(1 << 1);
78    pub const EMPTY: Self = Self(0);
79}
80
81impl BitOr for SpawnFlags {
82    type Output = Self;
83    fn bitor(self, rhs: Self) -> Self::Output {
84        Self(self.0 | rhs.0)
85    }
86}
87
88#[repr(u8)]
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub enum RawContextPriority {
91    /// Default priority, used when no custom priority is specified, the behaviour is not clearly defined if you choose this priority, but it should be the default,
92    /// currently the kernel will use the Medium priority if this was passed to the SysPSpawn syscall, later on it could inherit the priority of the parent process
93    ///
94    /// Incase of the SysTSpawn syscall, the kernel will inherit the priority of the parent process
95    Default = 0,
96    Low = 1,
97    Medium = 2,
98    High = 3,
99}
100
101/// configuration for the spawn syscall
102#[repr(C)]
103pub struct RawPSpawnConfig {
104    /// config version for compatibility
105    /// added in kernel version 0.2.1 and therefore breaking compatibility with any program compiled for version below 0.2.1
106    /// revision 1: added env
107    /// revision 2: added priority (v0.4.0)
108    /// revision 3: added custom stack size (v0.4.0)
109    pub revision: u8,
110    _reserved: [u8; 7],
111    pub name: OptZero<Str>,
112    pub argv: OptZero<Slice<Str>>,
113    pub flags: SpawnFlags,
114    _reserved1: [u8; 7],
115    pub stdio: OptZero<FFINonNull<ProcessStdio>>,
116    /// revision 1 and above
117    pub env: OptZero<Slice<Slice<u8>>>,
118    /// revision 2 and above
119    pub priority: RawContextPriority,
120    _reserved2: [u8; 7],
121    /// revision 3 and above
122    pub custom_stack_size: OptZero<ShouldNotBeZero<usize>>,
123}
124
125impl RawPSpawnConfig {
126    /// Creates a new process spawn configuration with the latest revision from raw FFI values
127    #[inline(always)]
128    pub const fn new_from_raw(
129        name: OptZero<Str>,
130        argv: OptZero<Slice<Str>>,
131        env: OptZero<Slice<Slice<u8>>>,
132        flags: SpawnFlags,
133        stdio: OptZero<FFINonNull<ProcessStdio>>,
134        priority: RawContextPriority,
135        custom_stack_size: OptZero<ShouldNotBeZero<usize>>,
136    ) -> Self {
137        Self {
138            revision: 3,
139            name,
140            argv,
141            env,
142            flags,
143            stdio,
144            priority,
145            custom_stack_size,
146            _reserved2: [0; 7],
147            _reserved1: [0; 7],
148            _reserved: [0; 7],
149        }
150    }
151
152    #[inline]
153    /// Construct a new process spawn configuration from the given rust parameters.
154    ///
155    /// # Safety
156    /// The given parameters must live as long as the returned value is alive, because currently this doesn't have any lifetime bounds.
157    ///
158    /// `argv` and `envv` may be reused to store their FFI representations,
159    /// even though nothing should change because the layout matches the rust representation, it is still not exactly guaranteed
160    /// but this should be resolved through this [RFC](https://github.com/rust-lang/rfcs/pull/3775)
161    pub unsafe fn new<'a>(
162        name: Option<&'a str>,
163        argv: Option<&'a mut [*mut str]>,
164        envv: Option<&'a mut [*mut [u8]]>,
165        flags: SpawnFlags,
166        stdio: Option<&'a ProcessStdio>,
167        priority: RawContextPriority,
168        custom_stack_size: Option<NonZero<usize>>,
169    ) -> Self {
170        Self::new_from_raw(
171            OptZero::from_option(name.map(|s| Str::from_str(s))),
172            OptZero::from_option(argv.map(|v| unsafe { Slice::from_str_slices_mut(v) })),
173            OptZero::from_option(envv.map(|v| unsafe { Slice::from_slices_ptr_mut(v) })),
174            flags,
175            OptZero::from_option(
176                stdio.map(|v| unsafe { FFINonNull::new_unchecked(v as *const _ as *mut _) }),
177            ),
178            priority,
179            OptZero::from_option(custom_stack_size.map(|v| v.into())),
180        )
181    }
182}
183
184/// configuration for the thread spawn syscall
185/// for now it takes only a single argument pointer which is a pointer to an optional argument, that pointer is going to be passed to the thread as the second argument
186#[repr(C)]
187pub struct RawTSpawnConfig {
188    /// revision 1: added custom stack size
189    pub revision: u32,
190    _reserved: u32,
191    pub argument_ptr: *const (),
192    pub priority: RawContextPriority,
193    /// The index of the CPU to append to, if it is None the kernel will choose one, use `0` for the boot CPU
194    pub cpu: COption<u8>,
195    _reserved1: [u8; 5],
196    /// revision 1 and above
197    pub custom_stack_size: OptZero<ShouldNotBeZero<usize>>,
198}
199
200impl RawTSpawnConfig {
201    #[inline(always)]
202    /// Creates a new thread spawn configuration with the latest revision from raw FFI values
203    pub const fn new_from_raw(
204        argument_ptr: *const (),
205        priority: RawContextPriority,
206        cpu: COption<u8>,
207        custom_stack_size: OptZero<ShouldNotBeZero<usize>>,
208    ) -> Self {
209        Self {
210            revision: 1,
211            _reserved1: [0; 5],
212            _reserved: 0,
213            argument_ptr,
214            priority,
215            cpu,
216            custom_stack_size,
217        }
218    }
219
220    #[inline]
221    /// Create a new thread spawn configuration with the latest revision
222    pub fn new(
223        argument_ptr: *const (),
224        priority: RawContextPriority,
225        cpu: Option<u8>,
226        custom_stack_size: Option<NonZero<usize>>,
227    ) -> Self {
228        Self::new_from_raw(
229            argument_ptr,
230            priority.into(),
231            cpu.into(),
232            match custom_stack_size {
233                Some(size) => OptZero::some(unsafe { ShouldNotBeZero::new_unchecked(size.get()) }),
234                None => OptZero::none(),
235            },
236        )
237    }
238}