Skip to main content

capsules_extra/
syscall_return_test.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2026.
4
5//! Capsule for testing syscall return variants from userspace.
6//!
7//! Each command returns a specific [`CommandReturn`] variant with distinct,
8//! known values so userspace tests can verify correct encoding and decoding
9//! of every return type.
10//!
11//! The capsule also supports subscribe, allow read-only, allow read-write,
12//! and allow userspace-readable syscalls with subscribe_num/allow_num 0. This
13//! lets userspace tests verify that success and failure returns carry back the
14//! expected pointer and length values.
15//!
16//! For allow calls, pass a valid buffer (within app memory) for a success
17//! return and an invalid pointer (e.g. 0x90) for a failure return. For
18//! subscribe, pass a valid function pointer for success and an invalid one
19//! (e.g. 0x90) for failure.
20//!
21//! ## Command numbers
22//!
23//!  - 0:  Success (driver presence check)
24//!  - 1:  Failure(ErrorCode::FAIL)
25//!  - 2:  FailureU32(ErrorCode::BUSY, 0x20000001)
26//!  - 3:  FailureU32U32(ErrorCode::NOMEM, 0x30000001, 0x30000002)
27//!  - 4:  FailureU64(ErrorCode::INVAL, 0x4000000000000001)
28//!  - 5:  Success
29//!  - 6:  SuccessU32(0x60000001)
30//!  - 7:  SuccessU32U32(0x70000001, 0x70000002)
31//!  - 8:  SuccessU64(0x8000000000000001)
32//!  - 9:  SuccessU32U32U32(0x90000001, 0x90000002, 0x90000003)
33//!  - 10: SuccessU32U64(0xA0000001, 0xA000000000000002)
34
35use kernel::grant::{AllowRoCount, AllowRwCount, Grant, GrantKernelData, UpcallCount};
36use kernel::process;
37use kernel::processbuffer::UserspaceReadableProcessBuffer;
38use kernel::syscall::{CommandReturn, SyscallDriver};
39use kernel::{ErrorCode, ProcessId};
40
41pub const DRIVER_NUM: usize = capsules_core::driver::NUM::SyscallReturnTest as usize;
42
43/// Per-process grant data. Holds the userspace-readable buffer swapped in via
44/// `allow_userspace_readable`.
45#[derive(Default)]
46pub struct App {
47    userspace_readable_buf: UserspaceReadableProcessBuffer,
48}
49
50pub struct SyscallReturnTest {
51    apps: Grant<App, UpcallCount<1>, AllowRoCount<1>, AllowRwCount<1>>,
52}
53
54impl SyscallReturnTest {
55    pub fn new(grant: Grant<App, UpcallCount<1>, AllowRoCount<1>, AllowRwCount<1>>) -> Self {
56        SyscallReturnTest { apps: grant }
57    }
58}
59
60impl SyscallDriver for SyscallReturnTest {
61    fn command(
62        &self,
63        command_num: usize,
64        _r2: usize,
65        _r3: usize,
66        _process_id: ProcessId,
67    ) -> CommandReturn {
68        match command_num {
69            0 => CommandReturn::success(),
70            1 => CommandReturn::failure(ErrorCode::FAIL),
71            2 => CommandReturn::failure_u32(ErrorCode::BUSY, 0x2000_0001),
72            3 => CommandReturn::failure_u32_u32(ErrorCode::NOMEM, 0x3000_0001, 0x3000_0002),
73            4 => CommandReturn::failure_u64(ErrorCode::INVAL, 0x4000_0000_0000_0001),
74            5 => CommandReturn::success(),
75            6 => CommandReturn::success_u32(0x6000_0001),
76            7 => CommandReturn::success_u32_u32(0x7000_0001, 0x7000_0002),
77            8 => CommandReturn::success_u64(0x8000_0000_0000_0001),
78            9 => CommandReturn::success_u32_u32_u32(0x9000_0001, 0x9000_0002, 0x9000_0003),
79            10 => CommandReturn::success_u32_u64(0xA000_0001, 0xA000_0000_0000_0002),
80            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
81        }
82    }
83
84    /// Accept or reject an allow-userspace-readable buffer.
85    ///
86    /// `which` 0: accept — swap the incoming buffer with whatever was stored
87    /// previously (initially empty) and return the old one to userspace.
88    /// Any other `which` value: reject with `NOSUPPORT`.
89    fn allow_userspace_readable(
90        &self,
91        processid: ProcessId,
92        which: usize,
93        mut slice: UserspaceReadableProcessBuffer,
94    ) -> Result<UserspaceReadableProcessBuffer, (UserspaceReadableProcessBuffer, ErrorCode)> {
95        if which == 0 {
96            let res = self.apps.enter(processid, |data, _: &GrantKernelData| {
97                core::mem::swap(&mut data.userspace_readable_buf, &mut slice);
98            });
99            match res {
100                Ok(()) => Ok(slice),
101                Err(e) => Err((slice, e.into())),
102            }
103        } else {
104            Err((slice, ErrorCode::NOSUPPORT))
105        }
106    }
107
108    fn allocate_grant(&self, process_id: ProcessId) -> Result<(), process::Error> {
109        self.apps.enter(process_id, |_, _| {})
110    }
111}