kernel/utilities/
arch_helpers.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 2024.
4
5//! Helper functions and types shared between multiple `arch` crates.
6//!
7//! This function contains functions and types that do not have to be in the
8//! core kernel and are architecture-specific, but are shared by two or more
9//! `arch` crates. While these could also live in a dedicated crate, we use the
10//! `kernel` crate as all `arch` crates already depend on it.
11
12use crate::syscall::SyscallReturn;
13use crate::ErrorCode;
14
15/// Helper function to split a [`u64`] into a higher and lower [`u32`].
16///
17/// Used in encoding 64-bit wide system call return values on 32-bit
18/// platforms.
19#[inline]
20fn u64_to_be_u32s(src: u64) -> (u32, u32) {
21    let src_bytes = src.to_be_bytes();
22    let src_msb = u32::from_be_bytes([src_bytes[0], src_bytes[1], src_bytes[2], src_bytes[3]]);
23    let src_lsb = u32::from_be_bytes([src_bytes[4], src_bytes[5], src_bytes[6], src_bytes[7]]);
24
25    (src_msb, src_lsb)
26}
27
28/// Enumeration of the system call return type variant identifiers described in
29/// TRD104.
30///
31/// Each variant is associated with the respective variant identifier that would
32/// be passed along with the return value to userspace.
33#[repr(u32)]
34#[derive(Copy, Clone, Debug)]
35pub enum TRD104SyscallReturnVariant {
36    Failure = 0,
37    FailureU32 = 1,
38    FailureU32U32 = 2,
39    FailureU64 = 3,
40    Success = 128,
41    SuccessU32 = 129,
42    SuccessU32U32 = 130,
43    SuccessU64 = 131,
44    SuccessU32U32U32 = 132,
45    SuccessU32U64 = 133,
46}
47
48/// System call return variants defined as defined in TRD104.
49///
50/// These are a strict subset of the variants defined in the core
51/// kernel's [`SyscallReturn`] enum. For documentation on the
52/// individual variants, refer to this type instead.
53#[derive(Copy, Clone, Debug)]
54pub enum TRD104SyscallReturn {
55    Failure(ErrorCode),
56    FailureU32(ErrorCode, u32),
57    FailureU32U32(ErrorCode, u32, u32),
58    FailureU64(ErrorCode, u64),
59    Success,
60    SuccessU32(u32),
61    SuccessU32U32(u32, u32),
62    SuccessU32U32U32(u32, u32, u32),
63    SuccessU64(u64),
64    SuccessU32U64(u32, u64),
65    AllowReadWriteSuccess(*mut u8, usize),
66    AllowReadWriteFailure(ErrorCode, *mut u8, usize),
67    UserspaceReadableAllowSuccess(*mut u8, usize),
68    UserspaceReadableAllowFailure(ErrorCode, *mut u8, usize),
69    AllowReadOnlySuccess(*const u8, usize),
70    AllowReadOnlyFailure(ErrorCode, *const u8, usize),
71    SubscribeSuccess(*const (), usize),
72    SubscribeFailure(ErrorCode, *const (), usize),
73    YieldWaitFor(usize, usize, usize),
74}
75
76impl TRD104SyscallReturn {
77    /// Map from the kernel's [`SyscallReturn`] enum to the subset of return
78    /// values specified in TRD104. This ensures backwards compatibility with
79    /// architectures implementing the ABI as specified in TRD104.
80    pub fn from_syscall_return(syscall_return: SyscallReturn) -> Self {
81        match syscall_return {
82            // Identical variants:
83            SyscallReturn::Failure(a) => TRD104SyscallReturn::Failure(a),
84            SyscallReturn::FailureU32(a, b) => TRD104SyscallReturn::FailureU32(a, b),
85            SyscallReturn::FailureU32U32(a, b, c) => TRD104SyscallReturn::FailureU32U32(a, b, c),
86            SyscallReturn::FailureU64(a, b) => TRD104SyscallReturn::FailureU64(a, b),
87            SyscallReturn::Success => TRD104SyscallReturn::Success,
88            SyscallReturn::SuccessU32(a) => TRD104SyscallReturn::SuccessU32(a),
89            SyscallReturn::SuccessU32U32(a, b) => TRD104SyscallReturn::SuccessU32U32(a, b),
90            SyscallReturn::SuccessU32U32U32(a, b, c) => {
91                TRD104SyscallReturn::SuccessU32U32U32(a, b, c)
92            }
93            SyscallReturn::SuccessU64(a) => TRD104SyscallReturn::SuccessU64(a),
94            SyscallReturn::SuccessU32U64(a, b) => TRD104SyscallReturn::SuccessU32U64(a, b),
95            SyscallReturn::AllowReadWriteSuccess(a, b) => {
96                TRD104SyscallReturn::AllowReadWriteSuccess(a, b)
97            }
98            SyscallReturn::AllowReadWriteFailure(a, b, c) => {
99                TRD104SyscallReturn::AllowReadWriteFailure(a, b, c)
100            }
101            SyscallReturn::UserspaceReadableAllowSuccess(a, b) => {
102                TRD104SyscallReturn::UserspaceReadableAllowSuccess(a, b)
103            }
104            SyscallReturn::UserspaceReadableAllowFailure(a, b, c) => {
105                TRD104SyscallReturn::UserspaceReadableAllowFailure(a, b, c)
106            }
107            SyscallReturn::AllowReadOnlySuccess(a, b) => {
108                TRD104SyscallReturn::AllowReadOnlySuccess(a, b)
109            }
110            SyscallReturn::AllowReadOnlyFailure(a, b, c) => {
111                TRD104SyscallReturn::AllowReadOnlyFailure(a, b, c)
112            }
113            SyscallReturn::SubscribeSuccess(a, b) => TRD104SyscallReturn::SubscribeSuccess(a, b),
114            SyscallReturn::SubscribeFailure(a, b, c) => {
115                TRD104SyscallReturn::SubscribeFailure(a, b, c)
116            }
117            SyscallReturn::YieldWaitFor(a, b, c) => TRD104SyscallReturn::YieldWaitFor(a, b, c),
118
119            // Compatibility mapping:
120            SyscallReturn::SuccessAddr(a) => TRD104SyscallReturn::SuccessU32(a as u32),
121            SyscallReturn::SuccessPtr(a) => {
122                TRD104SyscallReturn::SuccessU32(a.as_ptr::<()>() as u32)
123            }
124        }
125    }
126}
127
128/// Encode the system call return value into 4 registers, following the encoding
129/// specified in TRD104. Architectures which do not follow TRD104 are free to
130/// define their own encoding.
131pub fn encode_syscall_return_trd104(
132    syscall_return: &TRD104SyscallReturn,
133    a0: &mut u32,
134    a1: &mut u32,
135    a2: &mut u32,
136    a3: &mut u32,
137) {
138    match *syscall_return {
139        TRD104SyscallReturn::Failure(e) => {
140            *a0 = TRD104SyscallReturnVariant::Failure as u32;
141            *a1 = usize::from(e) as u32;
142        }
143        TRD104SyscallReturn::FailureU32(e, data0) => {
144            *a0 = TRD104SyscallReturnVariant::FailureU32 as u32;
145            *a1 = usize::from(e) as u32;
146            *a2 = data0;
147        }
148        TRD104SyscallReturn::FailureU32U32(e, data0, data1) => {
149            *a0 = TRD104SyscallReturnVariant::FailureU32U32 as u32;
150            *a1 = usize::from(e) as u32;
151            *a2 = data0;
152            *a3 = data1;
153        }
154        TRD104SyscallReturn::FailureU64(e, data0) => {
155            let (data0_msb, data0_lsb) = u64_to_be_u32s(data0);
156            *a0 = TRD104SyscallReturnVariant::FailureU64 as u32;
157            *a1 = usize::from(e) as u32;
158            *a2 = data0_lsb;
159            *a3 = data0_msb;
160        }
161        TRD104SyscallReturn::Success => {
162            *a0 = TRD104SyscallReturnVariant::Success as u32;
163        }
164        TRD104SyscallReturn::SuccessU32(data0) => {
165            *a0 = TRD104SyscallReturnVariant::SuccessU32 as u32;
166            *a1 = data0;
167        }
168        TRD104SyscallReturn::SuccessU32U32(data0, data1) => {
169            *a0 = TRD104SyscallReturnVariant::SuccessU32U32 as u32;
170            *a1 = data0;
171            *a2 = data1;
172        }
173        TRD104SyscallReturn::SuccessU32U32U32(data0, data1, data2) => {
174            *a0 = TRD104SyscallReturnVariant::SuccessU32U32U32 as u32;
175            *a1 = data0;
176            *a2 = data1;
177            *a3 = data2;
178        }
179        TRD104SyscallReturn::SuccessU64(data0) => {
180            let (data0_msb, data0_lsb) = u64_to_be_u32s(data0);
181
182            *a0 = TRD104SyscallReturnVariant::SuccessU64 as u32;
183            *a1 = data0_lsb;
184            *a2 = data0_msb;
185        }
186        TRD104SyscallReturn::SuccessU32U64(data0, data1) => {
187            let (data1_msb, data1_lsb) = u64_to_be_u32s(data1);
188
189            *a0 = TRD104SyscallReturnVariant::SuccessU32U64 as u32;
190            *a1 = data0;
191            *a2 = data1_lsb;
192            *a3 = data1_msb;
193        }
194        TRD104SyscallReturn::AllowReadWriteSuccess(ptr, len) => {
195            *a0 = TRD104SyscallReturnVariant::SuccessU32U32 as u32;
196            *a1 = ptr as u32;
197            *a2 = len as u32;
198        }
199        TRD104SyscallReturn::UserspaceReadableAllowSuccess(ptr, len) => {
200            *a0 = TRD104SyscallReturnVariant::SuccessU32U32 as u32;
201            *a1 = ptr as u32;
202            *a2 = len as u32;
203        }
204        TRD104SyscallReturn::AllowReadWriteFailure(err, ptr, len) => {
205            *a0 = TRD104SyscallReturnVariant::FailureU32U32 as u32;
206            *a1 = usize::from(err) as u32;
207            *a2 = ptr as u32;
208            *a3 = len as u32;
209        }
210        TRD104SyscallReturn::UserspaceReadableAllowFailure(err, ptr, len) => {
211            *a0 = TRD104SyscallReturnVariant::FailureU32U32 as u32;
212            *a1 = usize::from(err) as u32;
213            *a2 = ptr as u32;
214            *a3 = len as u32;
215        }
216        TRD104SyscallReturn::AllowReadOnlySuccess(ptr, len) => {
217            *a0 = TRD104SyscallReturnVariant::SuccessU32U32 as u32;
218            *a1 = ptr as u32;
219            *a2 = len as u32;
220        }
221        TRD104SyscallReturn::AllowReadOnlyFailure(err, ptr, len) => {
222            *a0 = TRD104SyscallReturnVariant::FailureU32U32 as u32;
223            *a1 = usize::from(err) as u32;
224            *a2 = ptr as u32;
225            *a3 = len as u32;
226        }
227        TRD104SyscallReturn::SubscribeSuccess(ptr, data) => {
228            *a0 = TRD104SyscallReturnVariant::SuccessU32U32 as u32;
229            *a1 = ptr as u32;
230            *a2 = data as u32;
231        }
232        TRD104SyscallReturn::SubscribeFailure(err, ptr, data) => {
233            *a0 = TRD104SyscallReturnVariant::FailureU32U32 as u32;
234            *a1 = usize::from(err) as u32;
235            *a2 = ptr as u32;
236            *a3 = data as u32;
237        }
238        TRD104SyscallReturn::YieldWaitFor(data0, data1, data2) => {
239            *a0 = data0 as u32;
240            *a1 = data1 as u32;
241            *a2 = data2 as u32;
242        }
243    }
244}