moto_sys/
syscalls.rs

1/*
2 * Three syscalls:
3 * - SYS_CTL: object management/configuration
4 * - SYS_MEM: memory management (memory allocation and sharing)
5 * - SYS_CPU: CPU management (scheduling)
6 */
7
8// Syscall numbers.
9pub const SYS_CTL: u8 = 1;
10pub const SYS_MEM: u8 = 2;
11pub const SYS_CPU: u8 = 3;
12
13use crate::ErrorCode;
14
15// SyscallResult is passed on registers; x64, arm64, and risc-v all have
16// enough argument/scratch registers to pass data back this way.
17#[derive(Debug)]
18pub struct SyscallResult {
19    pub result: u64,    // rax
20    pub data: [u64; 6], // rdi, rsi, rdx, r10, r8, r9
21}
22
23impl SyscallResult {
24    // Flags.
25    pub const F_TIMED_OUT: u64 = 0x01_00_00;
26    pub const F_HANDLE_ARRAY: u64 = 0x_02_00_00;
27
28    pub fn is_ok(&self) -> bool {
29        ((self.result & 0xFF_FF) as u16) == ErrorCode::Ok as u16
30    }
31
32    pub fn timed_out(&self) -> bool {
33        (self.result & Self::F_TIMED_OUT) != 0
34    }
35
36    pub fn error_code(&self) -> ErrorCode {
37        ErrorCode::from_u16((self.result & 0xFF_FF) as u16)
38    }
39}
40
41/// SysHandle represents a kernel object to the userspace.
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
43#[repr(C, align(8))]
44pub struct SysHandle(u64);
45
46const _: () = assert!(core::mem::size_of::<SysHandle>() == 8);
47
48impl SysHandle {
49    // Pre-defined object handles.
50    pub const NONE: SysHandle = SysHandle(0);
51    pub const KERNEL: SysHandle = SysHandle(1);
52    pub const SELF: SysHandle = SysHandle(2); // This process.
53    pub const CURR: SysHandle = SysHandle(3); // This thread.
54    pub const PARENT: SysHandle = SysHandle(4); // The parent process.
55    pub const IO_MAN: SysHandle = SysHandle(5); // IO Manager process (the parent by default).
56
57    pub const fn from_u64(val: u64) -> Self {
58        SysHandle(val)
59    }
60    pub const fn as_u64(&self) -> u64 {
61        self.0
62    }
63    pub const fn is_none(&self) -> bool {
64        self.as_u64() == Self::NONE.as_u64()
65    }
66}
67
68/// Same as SysHandle, but calls SysCtl::put on drop.
69#[cfg(feature = "userspace")]
70pub struct RaiiHandle(u64);
71
72#[cfg(feature = "userspace")]
73impl Drop for RaiiHandle {
74    fn drop(&mut self) {
75        if self.0 != 0 {
76            SysCtl::put(self.syshandle()).unwrap()
77        }
78    }
79}
80
81#[cfg(feature = "userspace")]
82impl RaiiHandle {
83    pub const fn syshandle(&self) -> SysHandle {
84        SysHandle(self.0)
85    }
86
87    pub const fn from(handle: SysHandle) -> Self {
88        Self(handle.0)
89    }
90
91    pub fn take(mut self) -> SysHandle {
92        let result = SysHandle(self.0);
93        self.0 = 0;
94        result
95    }
96}
97
98pub struct SysCtl;
99
100#[cfg(feature = "userspace")]
101fn pack_nr_ver(syscall_number: u8, operation: u8, flags: u32, version: u16) -> u64 {
102    ((syscall_number as u64) << 56)
103        | ((operation as u64) << 48)
104        | ((flags as u64) << 16)
105        | (version as u64)
106}
107
108impl SysCtl {
109    pub const OP_GET: u8 = 1;
110    pub const OP_PUT: u8 = 2;
111    pub const OP_CREATE: u8 = 3;
112    pub const OP_QUERY_PROCESS: u8 = 4;
113    pub const OP_SET_LOG_LEVEL: u8 = 5;
114
115    pub const F_QUERY_STATUS: u32 = 1;
116    pub const F_QUERY_LIST: u32 = 2;
117    pub const F_QUERY_LIST_CHILDREN: u32 = 3;
118
119    // When connecting to ("getting") a shared URL, wake the counterpart.
120    pub const F_WAKE_PEER: u32 = 1;
121
122    // URLS:
123    //     - "address_space:$URL"
124    //                  Creates a new address space that can be identified by the $URL;
125    //     - "capabilities"
126    //     - "irq_wait:$NUM"
127    //     - "process:entry_point=$NUM;capabilities=$NUM"
128    //     - "serial_console"
129    //     - "shared:url=$URL;address=$addr;page_type=[small|mid];page_num=$num"
130    //            - A "server" calls CREATE for a custom URL (can be duplicates). Then waits.
131    //              may provide an unmapped page.
132    //            - A "client" calls GET for the URL; page must be mapped; if there is a matching
133    //              endpoint, the IPC channel is created and returned. Who then waits and who wakes
134    //              is up to the userspace.
135    //            - For now, only 1:1 connections are supported.
136    //            - Later "multicast" connections will be added (server writes), multiple clients read.
137    #[cfg(feature = "userspace")]
138    pub fn create(parent: SysHandle, flags: u32, url: &str) -> Result<SysHandle, ErrorCode> {
139        let bytes = url.as_bytes();
140
141        let result = do_syscall(
142            pack_nr_ver(SYS_CTL, Self::OP_CREATE, flags, 0),
143            parent.as_u64(),
144            bytes.as_ptr() as usize as u64,
145            bytes.len() as u64,
146            0,
147            0,
148            0,
149        );
150        if result.is_ok() {
151            Ok(SysHandle(result.data[0]))
152        } else {
153            Err(result.error_code())
154        }
155    }
156
157    // Create a pair of wait handles so that the userspace can wake/wait/swap on them.
158    // Similar to shared, but while shared is more of a server/client setup,
159    // the IPC pair is more of a direct connection (e.g. stdio).
160    //
161    // Returned handles are valid inside processes passed as parameters, not
162    // the caller; the caller can pass SysHandle::SELF to have one (or both) of
163    // the wait handles to belong to it.
164    #[cfg(feature = "userspace")]
165    pub fn create_ipc_pair(
166        process1: SysHandle,
167        process2: SysHandle,
168        flags: u32,
169    ) -> Result<(SysHandle, SysHandle), ErrorCode> {
170        let bytes = "ipc_pair".as_bytes();
171        let result = do_syscall(
172            pack_nr_ver(SYS_CTL, Self::OP_CREATE, flags, 0),
173            process1.as_u64(),
174            bytes.as_ptr() as usize as u64,
175            bytes.len() as u64,
176            process2.as_u64(),
177            0,
178            0,
179        );
180        if result.is_ok() {
181            Ok((SysHandle(result.data[0]), SysHandle(result.data[1])))
182        } else {
183            Err(result.error_code())
184        }
185    }
186
187    #[cfg(feature = "userspace")]
188    pub fn get(parent: SysHandle, flags: u32, url: &str) -> Result<SysHandle, ErrorCode> {
189        let res = Self::get_res1(parent, flags, url)?;
190        Ok(res.0)
191    }
192
193    #[cfg(feature = "userspace")]
194    pub fn get_res1(
195        parent: SysHandle,
196        flags: u32,
197        url: &str,
198    ) -> Result<(SysHandle, u64), ErrorCode> {
199        let bytes = url.as_bytes();
200
201        let result = do_syscall(
202            pack_nr_ver(SYS_CTL, Self::OP_GET, flags, 0),
203            parent.as_u64(),
204            bytes.as_ptr() as usize as u64,
205            bytes.len() as u64,
206            0,
207            0,
208            0,
209        );
210        if result.is_ok() {
211            Ok((SysHandle(result.data[0]), result.data[1]))
212        } else {
213            Err(result.error_code())
214        }
215    }
216
217    #[cfg(feature = "userspace")]
218    pub fn put(handle: SysHandle) -> Result<(), ErrorCode> {
219        Self::put_1(handle, 0)
220    }
221
222    #[cfg(feature = "userspace")]
223    pub fn put_1(handle: SysHandle, arg: u64) -> Result<(), ErrorCode> {
224        Self::put_remote_1(SysHandle::SELF, handle, arg)
225    }
226
227    #[cfg(feature = "userspace")]
228    pub fn put_remote(owner_process: SysHandle, handle: SysHandle) -> Result<(), ErrorCode> {
229        Self::put_remote_1(owner_process, handle, 0)
230    }
231
232    #[cfg(feature = "userspace")]
233    pub fn put_remote_1(
234        owner_process: SysHandle,
235        handle: SysHandle,
236        arg: u64,
237    ) -> Result<(), ErrorCode> {
238        let result = do_syscall(
239            pack_nr_ver(SYS_CTL, Self::OP_PUT, 0, 0),
240            owner_process.as_u64(),
241            handle.as_u64(),
242            arg,
243            0,
244            0,
245            0,
246        );
247        if result.is_ok() {
248            Ok(())
249        } else {
250            Err(result.error_code())
251        }
252    }
253
254    // Level: log::LevelFilter; 3 => Info, 4 => Debug, etc.
255    #[cfg(feature = "userspace")]
256    pub fn set_log_level(level: u8) -> Result<u8, ErrorCode> {
257        let result = do_syscall(
258            pack_nr_ver(SYS_CTL, Self::OP_SET_LOG_LEVEL, 0, 0),
259            level as u64,
260            0,
261            0,
262            0,
263            0,
264            0,
265        );
266        if result.is_ok() {
267            Ok(result.data[0] as u8)
268        } else {
269            Err(result.error_code())
270        }
271    }
272
273    #[cfg(feature = "userspace")]
274    pub fn process_status(handle: SysHandle) -> Result<Option<u64>, ErrorCode> {
275        let result = do_syscall(
276            pack_nr_ver(SYS_CTL, Self::OP_QUERY_PROCESS, Self::F_QUERY_STATUS, 0),
277            handle.as_u64(),
278            0,
279            0,
280            0,
281            0,
282            0,
283        );
284
285        if result.is_ok() {
286            Ok(Some(result.data[0]))
287        } else {
288            if result.error_code() == ErrorCode::AlreadyInUse {
289                Ok(None)
290            } else {
291                Err(ErrorCode::NotFound)
292            }
293        }
294    }
295
296    #[cfg(feature = "userspace")]
297    pub fn list_processes_v1(
298        pid: u64,
299        flat_list: bool,
300        buf: &mut [super::stats::ProcessStatsV1],
301    ) -> Result<usize, ErrorCode> {
302        if buf.len() < 1 {
303            return Err(ErrorCode::InvalidArgument);
304        }
305
306        let flags = if flat_list {
307            Self::F_QUERY_LIST
308        } else {
309            Self::F_QUERY_LIST_CHILDREN
310        };
311        let result = do_syscall(
312            pack_nr_ver(SYS_CTL, Self::OP_QUERY_PROCESS, flags, 1),
313            pid,
314            buf.as_mut_ptr() as usize as u64,
315            buf.len() as u64,
316            0,
317            0,
318            0,
319        );
320
321        if result.is_ok() {
322            Ok(result.data[0] as usize)
323        } else {
324            Err(result.error_code())
325        }
326    }
327}
328
329// standard arguments: rdi, rsi, rdx, rcx, r8, r9
330// our syscall arguments: rdi, rsi, rdx, r10, r8, r9
331#[cfg(feature = "userspace")]
332pub fn do_syscall(
333    nr_ver: u64,
334    arg0: u64,
335    arg1: u64,
336    arg2: u64,
337    arg3: u64,
338    arg4: u64,
339    arg5: u64,
340) -> SyscallResult {
341    use core::arch::asm;
342
343    let mut val0 = arg0;
344    let mut val1 = arg1;
345    let mut val2 = arg2;
346    let mut val3 = arg3;
347    let mut val4 = arg4;
348    let mut val5 = arg5;
349    let mut rax = nr_ver;
350
351    unsafe {
352        asm!(
353            "syscall",
354            inout("rax") rax,
355            inout("rdi") val0,
356            inout("rsi") val1,
357            inout("rdx") val2,
358            inout("r10") val3,
359            inout("r8" ) val4,
360            inout("r9" ) val5,
361            lateout("rcx") _,
362            lateout("r11") _,
363        )
364    };
365
366    let mut data: [u64; 6] = [0; 6];
367    data[0] = val0;
368    data[1] = val1;
369    data[2] = val2;
370    data[3] = val3;
371    data[4] = val4;
372    data[5] = val5;
373
374    SyscallResult { result: rax, data }
375}
376
377pub struct SysMem;
378
379impl SysMem {
380    // Operations: just constants, not bit flags.
381    pub const OP_CREATE: u8 = 1;
382    pub const OP_GET: u8 = 2;
383    pub const OP_PUT: u8 = 3;
384    pub const OP_MAP: u8 = 4;
385    pub const OP_UNMAP: u8 = 5;
386    pub const OP_REMAP: u8 = 6;
387    pub const OP_QUERY: u8 = 7;
388    pub const OP_DEBUG: u8 = 8;
389    pub const OP_RECLAIM: u8 = 9;
390
391    // Bit flags for create/map operations.
392    pub const F_READABLE: u32 = 1;
393    pub const F_WRITABLE: u32 = 2;
394    pub const F_MMIO: u32 = 4;
395    pub const F_CONTIGUOUS: u32 = 8;
396    pub const F_SHARE_SELF: u32 = 0x10;
397
398    // The kernel may or may not do actual mapping on
399    // memory allocations; F_LAZY is a *hint* that the userspace
400    // is OK with lazy mapping.
401    pub const F_LAZY: u32 = 0x20;
402
403    pub const F_LOG_UTF8: u32 = 1; // OP_DEBUG.
404
405    // Bit flags for query.
406    pub const F_QUERY_STATS: u32 = 1;
407
408    // Various constants.
409    pub const PAGE_SIZE_SMALL: u64 = 4096;
410    pub const PAGE_SIZE_MID: u64 = 4096 * 512;
411    pub const PAGE_SIZE_LARGE: u64 = 4096 * 512 * 512;
412
413    pub const PAGE_SIZE_SMALL_LOG2: u64 = 12;
414    pub const PAGE_SIZE_MID_LOG2: u64 = 21;
415    pub const PAGE_SIZE_LARGE_LOG2: u64 = 30;
416
417    pub const PAGE_TYPE_SMALL: u64 = 1;
418    pub const PAGE_TYPE_MID: u64 = 2;
419    pub const PAGE_TYPE_LARGE: u64 = 3;
420
421    pub const MAX_ADDRESS_SPACE_SIZE_LOG2: u64 = 46;
422
423    #[cfg(feature = "userspace")]
424    pub fn map(
425        address_space: SysHandle,
426        flags: u32,
427        phys_addr: u64,
428        virt_addr: u64,
429        page_size: u64,
430        num_pages: u64,
431    ) -> Result<u64, ErrorCode> {
432        debug_assert_ne!(num_pages, 0);
433        let result = do_syscall(
434            pack_nr_ver(SYS_MEM, Self::OP_MAP, flags, 0),
435            address_space.as_u64(),
436            phys_addr,
437            virt_addr,
438            page_size,
439            num_pages,
440            0,
441        );
442        if result.is_ok() {
443            Ok(result.data[0])
444        } else {
445            Err(result.error_code())
446        }
447    }
448
449    #[cfg(feature = "userspace")]
450    pub fn map2(
451        address_space: SysHandle,
452        flags: u32,
453        phys_addr: u64,
454        virt_addr: u64,
455        page_size: u64,
456        num_pages: u64,
457    ) -> Result<(u64, u64), ErrorCode> {
458        debug_assert_ne!(num_pages, 0);
459        let result = do_syscall(
460            pack_nr_ver(SYS_MEM, Self::OP_MAP, flags, 0),
461            address_space.as_u64(),
462            phys_addr,
463            virt_addr,
464            page_size,
465            num_pages,
466            0,
467        );
468        if result.is_ok() {
469            Ok((result.data[0], result.data[1]))
470        } else {
471            Err(result.error_code())
472        }
473    }
474    #[cfg(feature = "userspace")]
475    pub fn unmap(
476        address_space: SysHandle,
477        flags: u32,
478        phys_addr: u64,
479        virt_addr: u64,
480    ) -> Result<(), ErrorCode> {
481        let result = do_syscall(
482            pack_nr_ver(SYS_MEM, Self::OP_UNMAP, flags, 0),
483            address_space.as_u64(),
484            phys_addr,
485            virt_addr,
486            0,
487            0,
488            0,
489        );
490
491        if result.is_ok() {
492            Ok(())
493        } else {
494            Err(result.error_code())
495        }
496    }
497
498    #[cfg(feature = "userspace")]
499    pub fn virt_to_phys(virt_addr: u64) -> Result<u64, ErrorCode> {
500        let result = do_syscall(
501            pack_nr_ver(SYS_MEM, Self::OP_QUERY, 0, 0),
502            SysHandle::SELF.as_u64(),
503            u64::MAX,
504            virt_addr,
505            0,
506            0,
507            0,
508        );
509
510        if result.is_ok() {
511            Ok(result.data[0])
512        } else {
513            Err(result.error_code())
514        }
515    }
516
517    #[cfg(feature = "userspace")]
518    pub fn alloc(page_size: u64, num_pages: u64) -> Result<u64, ErrorCode> {
519        assert!(
520            page_size == Self::PAGE_SIZE_SMALL
521                || page_size == Self::PAGE_SIZE_MID
522                || page_size == Self::PAGE_SIZE_LARGE
523        );
524        assert_ne!(num_pages, 0);
525        Self::map(
526            SysHandle::SELF,
527            Self::F_READABLE | Self::F_WRITABLE,
528            u64::MAX,
529            u64::MAX,
530            page_size,
531            num_pages,
532        )
533    }
534
535    // Note: the calling process must have CAP_IO_MANAGER.
536    #[cfg(feature = "userspace")]
537    pub fn alloc_contiguous_pages(size: u64) -> Result<u64, ErrorCode> {
538        assert_ne!(size, 0);
539        if size > 20 * Self::PAGE_SIZE_MID {
540            return Err(ErrorCode::InvalidArgument);
541        }
542        let (page_size, page_size_log_2) = if size > (Self::PAGE_SIZE_MID >> 1) {
543            (Self::PAGE_SIZE_MID, Self::PAGE_SIZE_MID_LOG2)
544        } else {
545            (Self::PAGE_SIZE_SMALL, Self::PAGE_SIZE_SMALL_LOG2)
546        };
547
548        let size = super::align_up(size, page_size);
549        Self::map(
550            SysHandle::SELF,
551            Self::F_READABLE | Self::F_WRITABLE | Self::F_CONTIGUOUS,
552            u64::MAX,
553            u64::MAX,
554            page_size,
555            size >> page_size_log_2,
556        )
557    }
558
559    #[cfg(feature = "userspace")]
560    pub fn free(virt_addr: u64) -> Result<(), ErrorCode> {
561        Self::unmap(SysHandle::SELF, 0, u64::MAX, virt_addr)
562    }
563
564    #[cfg(feature = "userspace")]
565    pub fn mmio_map(phys_addr: u64, size: u64) -> Result<u64, ErrorCode> {
566        assert_eq!(0, size & (Self::PAGE_SIZE_SMALL - 1));
567        Self::map(
568            SysHandle::SELF,
569            Self::F_READABLE | Self::F_WRITABLE | Self::F_MMIO,
570            phys_addr,
571            u64::MAX,
572            Self::PAGE_SIZE_SMALL,
573            size >> Self::PAGE_SIZE_SMALL_LOG2,
574        )
575    }
576
577    #[cfg(feature = "userspace")]
578    pub fn log(msg: &str) -> Result<(), ErrorCode> {
579        let bytes = msg.as_bytes();
580        if bytes.len() == 0 {
581            return Err(ErrorCode::InvalidArgument);
582        }
583
584        let res = do_syscall(
585            pack_nr_ver(SYS_MEM, Self::OP_DEBUG, Self::F_LOG_UTF8, 0),
586            SysHandle::SELF.as_u64(),
587            0,
588            msg.as_bytes().as_ptr() as usize as u64,
589            0,
590            0,
591            bytes.len() as u64,
592        );
593
594        if res.is_ok() {
595            Ok(())
596        } else {
597            Err(res.error_code())
598        }
599    }
600
601    #[cfg(feature = "userspace")]
602    pub fn query_stats() -> Result<super::stats::MemoryStats, ErrorCode> {
603        use crate::stats::MemoryStats;
604
605        let mut stats = MemoryStats::default();
606
607        let res = do_syscall(
608            pack_nr_ver(SYS_MEM, Self::OP_QUERY, Self::F_QUERY_STATS, 0),
609            SysHandle::NONE.as_u64(),
610            &mut stats as *mut _ as usize as u64,
611            0,
612            0,
613            0,
614            0,
615        );
616
617        if res.is_ok() {
618            Ok(stats)
619        } else {
620            Err(res.error_code())
621        }
622    }
623
624    #[cfg(feature = "userspace")]
625    pub fn reclaim(handle: SysHandle) -> Result<(), ErrorCode> {
626        let res = do_syscall(
627            pack_nr_ver(SYS_MEM, Self::OP_RECLAIM, 0, 0),
628            handle.as_u64(),
629            0,
630            0,
631            0,
632            0,
633            0,
634        );
635
636        if res.is_ok() {
637            Ok(())
638        } else {
639            Err(res.error_code())
640        }
641    }
642}
643
644pub struct SysCpu;
645
646impl SysCpu {
647    pub const OP_EXIT: u8 = 1;
648    pub const OP_WAIT: u8 = 2;
649    pub const OP_WAKE: u8 = 3;
650    pub const OP_KILL: u8 = 4;
651    pub const OP_SPAWN: u8 = 5;
652    pub const OP_USAGE: u8 = 6;
653    pub const OP_AFFINE_CPU: u8 = 7;
654
655    // Controls whether hanles to wait for are passed via registers or as an array in memory.
656    pub const F_HANDLE_ARRAY: u32 = 1;
657    pub const F_TIMEOUT: u32 = 2; // If present, args[0] contains Instant abs timeout.
658
659    // If present, poll wakers and return, don't block. Can't be combined with F_TIMEOUT.
660    pub const F_DONTBLOCK: u32 = 4;
661
662    // If present, args[0] (if no timeout) or args[1] (if timeout) contains swap target.
663    pub const F_SWAP_TARGET: u32 = 8;
664
665    // If present, args[0] (if no timeout) or args[1] (if timeout) contains wake target.
666    pub const F_WAKE_TARGET: u32 = 16;
667
668    #[cfg(feature = "userspace")]
669    pub fn exit(code: u64) -> ! {
670        do_syscall(
671            pack_nr_ver(SYS_CPU, Self::OP_EXIT, 0, 0),
672            code,
673            0,
674            0,
675            0,
676            0,
677            0,
678        );
679        unreachable!();
680    }
681
682    #[cfg(feature = "userspace")]
683    pub fn kill(target: SysHandle) -> Result<(), ErrorCode> {
684        let result = do_syscall(
685            pack_nr_ver(SYS_CPU, Self::OP_KILL, 0, 0),
686            target.as_u64(),
687            0,
688            0,
689            0,
690            0,
691            0,
692        );
693
694        if result.is_ok() {
695            Ok(())
696        } else {
697            Err(result.error_code())
698        }
699    }
700
701    #[cfg(feature = "userspace")]
702    pub fn wake(target: SysHandle) -> Result<(), ErrorCode> {
703        let result = do_syscall(
704            pack_nr_ver(SYS_CPU, Self::OP_WAKE, 0, 0),
705            target.as_u64(),
706            0,
707            0,
708            0,
709            0,
710            0,
711        );
712
713        if result.is_ok() {
714            Ok(())
715        } else {
716            Err(result.error_code())
717        }
718    }
719
720    // THE wait function:
721    // - if timed out, will return Err(ErrorCode::TimedOut);
722    // - if Instant is_nan(), then won't block (and won't return TimedOut);
723    // - if Err(BadHandle), @handles will contain bad handles;
724    // - if Ok(()), @handles will contain wakers;
725    // - if [swap|wake]_target is not NONE, will swap into the target;
726    // - the [swap|wake]_target, if present, will be woken even if one of the wait handles are bad;
727    // - at the moment, specifying both the swap and the wake targets is not allowed.
728    #[cfg(feature = "userspace")]
729    pub fn wait(
730        wait_handles: &mut [SysHandle],
731        swap_target: SysHandle,
732        wake_target: SysHandle,
733        timeout: Option<crate::time::Instant>,
734    ) -> Result<(), ErrorCode> {
735        // Note: we consciously drop all wait objects on wakeup and
736        // require a full list of wait objects on each new wait. While it
737        // may seem that requiring a full list of wait objects on each wait
738        // is wasteful and does not scale, this is done consciously so that
739        // we avoid synchronous designs (where many wait objects are needed).
740        let mut flags: u32 = 0;
741        let mut next_arg: usize = 0;
742        let mut args = [0_u64; 6];
743
744        if let Some(timeout) = timeout {
745            if timeout.is_nan() {
746                flags |= Self::F_DONTBLOCK;
747            } else {
748                flags |= Self::F_TIMEOUT;
749                args[next_arg] = timeout.as_u64();
750                next_arg += 1;
751            }
752        }
753
754        if swap_target != SysHandle::NONE {
755            if wake_target != SysHandle::NONE {
756                return Err(ErrorCode::InvalidArgument);
757            }
758            flags |= Self::F_SWAP_TARGET;
759            args[next_arg] = swap_target.as_u64();
760            next_arg += 1;
761        } else if wake_target != SysHandle::NONE {
762            flags |= Self::F_WAKE_TARGET;
763            args[next_arg] = wake_target.as_u64();
764            next_arg += 1;
765        }
766
767        if wait_handles.len() > (args.len() - next_arg) {
768            flags |= Self::F_HANDLE_ARRAY;
769            args[next_arg] = wait_handles.as_ptr() as usize as u64;
770            args[next_arg + 1] = wait_handles.len() as u64;
771        } else {
772            for idx in 0..wait_handles.len() {
773                args[next_arg] = wait_handles[idx].as_u64();
774                next_arg += 1;
775            }
776        }
777
778        let result = do_syscall(
779            pack_nr_ver(SYS_CPU, Self::OP_WAIT, flags, 1),
780            args[0],
781            args[1],
782            args[2],
783            args[3],
784            args[4],
785            args[5],
786        );
787
788        Self::process_result(&result, wait_handles)
789    }
790
791    #[cfg(feature = "userspace")]
792    fn process_result(result: &SyscallResult, handles: &mut [SysHandle]) -> Result<(), ErrorCode> {
793        // If the condition below is false, the kernel has properly put data in @handles.
794        if result.result & SyscallResult::F_HANDLE_ARRAY == 0 {
795            for idx in 0..handles.len() {
796                if idx < 6 {
797                    handles[idx] = SysHandle::from_u64(result.data[idx]);
798                } else {
799                    handles[idx] = SysHandle::NONE;
800                }
801            }
802        }
803
804        if result.is_ok() {
805            Ok(())
806        } else {
807            Err(result.error_code())
808        }
809    }
810
811    #[cfg(feature = "userspace")]
812    pub fn spawn(
813        process: SysHandle,
814        stack_start: u64,
815        thread_fn: u64,
816        thread_arg: u64,
817    ) -> Result<SysHandle, ErrorCode> {
818        let result = do_syscall(
819            pack_nr_ver(SYS_CPU, Self::OP_SPAWN, 0, 0),
820            process.as_u64(),
821            stack_start,
822            thread_fn,
823            thread_arg,
824            0,
825            0,
826        );
827
828        if result.is_ok() {
829            Ok(SysHandle::from_u64(result.data[0]))
830        } else {
831            Err(result.error_code())
832        }
833    }
834
835    #[cfg(feature = "userspace")]
836    pub fn sched_yield() {
837        // Self::wait_timeout(Self::__F_TIMEOUT_RELATIVE, 0, SysHandle::NONE).unwrap();
838        Self::wait(
839            &mut [],
840            SysHandle::NONE,
841            SysHandle::NONE,
842            Some(crate::time::Instant::nan()),
843        )
844        .unwrap();
845    }
846
847    #[cfg(feature = "userspace")]
848    pub fn query_stats(buf: &mut [f32]) -> Result<(), ErrorCode> {
849        let result = do_syscall(
850            pack_nr_ver(SYS_CPU, Self::OP_USAGE, 0, 0),
851            buf.as_ptr() as usize as u64,
852            buf.len() as u64,
853            0,
854            0,
855            0,
856            0,
857        );
858
859        if result.is_ok() {
860            Ok(())
861        } else {
862            Err(result.error_code())
863        }
864    }
865
866    /// Affine the current thread to the specified CPU (or remove the affinity).
867    /// Only the IO_MANAGER can affine to CPU 0.
868    #[cfg(feature = "userspace")]
869    pub fn affine_to_cpu(cpu: Option<u32>) -> Result<(), ErrorCode> {
870        let result = do_syscall(
871            pack_nr_ver(SYS_CPU, Self::OP_AFFINE_CPU, 0, 0),
872            match cpu {
873                Some(cpu) => cpu as u64,
874                None => u64::MAX,
875            },
876            0,
877            0,
878            0,
879            0,
880            0,
881        );
882
883        if result.is_ok() {
884            Ok(())
885        } else {
886            Err(result.error_code())
887        }
888    }
889}