Skip to main content

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::ErrorCode;
13use crate::process::FunctionCall;
14use crate::syscall::SyscallReturn;
15use crate::syscall::YieldVariant;
16use crate::syscall::{Syscall, SyscallClass};
17use crate::utilities::capability_ptr::CapabilityPtr;
18use crate::utilities::machine_register::MachineRegister;
19
20/// Helper function to split a [`u64`] into a higher and lower [`u32`].
21///
22/// Used in encoding 64-bit wide system call return values on 32-bit
23/// platforms.
24#[inline]
25fn u64_to_be_u32s(src: u64) -> (u32, u32) {
26    let src_bytes = src.to_be_bytes();
27    let src_msb = u32::from_be_bytes([src_bytes[0], src_bytes[1], src_bytes[2], src_bytes[3]]);
28    let src_lsb = u32::from_be_bytes([src_bytes[4], src_bytes[5], src_bytes[6], src_bytes[7]]);
29
30    (src_msb, src_lsb)
31}
32
33/// Helper function for converting raw values passed back from an application
34/// into a `Syscall` type.
35///
36/// The `Syscall` represents a typed version of a system call invocation. The
37/// method returns None if the values do not specify a valid system call.
38///
39/// Different architectures have different ABIs for a process and the kernel to
40/// exchange data. The 32-bit ABI for CortexM and RISCV microcontrollers is
41/// specified in TRD104.
42pub fn syscall_from_register_arguments_trd104(
43    syscall_number: u8,
44    r0: usize,
45    r1: MachineRegister,
46    r2: MachineRegister,
47    r3: MachineRegister,
48) -> Option<Syscall> {
49    match SyscallClass::try_from(syscall_number) {
50        Ok(SyscallClass::Yield) => match r0 {
51            0 => Some(Syscall::Yield {
52                yield_type: YieldVariant::NoWait {
53                    ptr: r1.as_capability_ptr().as_ptr::<u8>().cast_mut(),
54                },
55            }),
56            1 => Some(Syscall::Yield {
57                yield_type: YieldVariant::Wait,
58            }),
59            2 => Some(Syscall::Yield {
60                yield_type: YieldVariant::WaitFor {
61                    driver_number: r1.as_usize(),
62                    subdriver_number: r2.as_usize(),
63                },
64            }),
65            _ => None,
66        },
67        Ok(SyscallClass::Subscribe) => Some(Syscall::Subscribe {
68            driver_number: r0,
69            subdriver_number: r1.as_usize(),
70            upcall_ptr: r2.as_capability_ptr(),
71            appdata: r3,
72        }),
73        Ok(SyscallClass::Command) => Some(Syscall::Command {
74            driver_number: r0,
75            subdriver_number: r1.as_usize(),
76            arg0: r2.as_usize(),
77            arg1: r3.as_usize(),
78        }),
79        Ok(SyscallClass::ReadWriteAllow) => Some(Syscall::ReadWriteAllow {
80            driver_number: r0,
81            subdriver_number: r1.as_usize(),
82            allow_address: r2.as_capability_ptr().as_ptr::<u8>().cast_mut(),
83            allow_size: r3.as_usize(),
84        }),
85        Ok(SyscallClass::UserspaceReadableAllow) => Some(Syscall::UserspaceReadableAllow {
86            driver_number: r0,
87            subdriver_number: r1.as_usize(),
88            allow_address: r2.as_capability_ptr().as_ptr::<u8>().cast_mut(),
89            allow_size: r3.as_usize(),
90        }),
91        Ok(SyscallClass::ReadOnlyAllow) => Some(Syscall::ReadOnlyAllow {
92            driver_number: r0,
93            subdriver_number: r1.as_usize(),
94            allow_address: r2.as_capability_ptr().as_ptr(),
95            allow_size: r3.as_usize(),
96        }),
97        Ok(SyscallClass::Memop) => Some(Syscall::Memop {
98            operand: r0,
99            arg0: r1.as_usize(),
100        }),
101        Ok(SyscallClass::Exit) => Some(Syscall::Exit {
102            which: r0,
103            completion_code: r1.as_usize(),
104        }),
105        Err(_) => None,
106    }
107}
108
109/// Helper function for converting raw values passed back from an application
110/// into a `Syscall` type.
111///
112/// This implements a 64-bit ABI.
113pub fn syscall_from_register_arguments_trd64bit(
114    syscall_number: u8,
115    r0: usize,
116    r1: MachineRegister,
117    r2: MachineRegister,
118    r3: MachineRegister,
119) -> Option<Syscall> {
120    match SyscallClass::try_from(syscall_number) {
121        Ok(SyscallClass::Yield) => match r0 {
122            0 => Some(Syscall::Yield {
123                yield_type: YieldVariant::NoWait {
124                    ptr: r1.as_capability_ptr().as_ptr::<u8>().cast_mut(),
125                },
126            }),
127            1 => Some(Syscall::Yield {
128                yield_type: YieldVariant::Wait,
129            }),
130            2 => Some(Syscall::Yield {
131                yield_type: YieldVariant::WaitFor {
132                    driver_number: r1.as_usize_32bit(),
133                    subdriver_number: r2.as_usize_32bit(),
134                },
135            }),
136            _ => None,
137        },
138        Ok(SyscallClass::Subscribe) => Some(Syscall::Subscribe {
139            driver_number: r0 as u32 as usize,
140            subdriver_number: r1.as_usize_32bit(),
141            upcall_ptr: r2.as_capability_ptr(),
142            appdata: r3,
143        }),
144        Ok(SyscallClass::Command) => Some(Syscall::Command {
145            driver_number: r0 as u32 as usize,
146            subdriver_number: r1.as_usize_32bit(),
147            arg0: r2.as_usize_32bit(),
148            arg1: r3.as_usize_32bit(),
149        }),
150        Ok(SyscallClass::ReadWriteAllow) => Some(Syscall::ReadWriteAllow {
151            driver_number: r0 as u32 as usize,
152            subdriver_number: r1.as_usize_32bit(),
153            allow_address: r2.as_capability_ptr().as_ptr::<u8>().cast_mut(),
154            allow_size: r3.as_usize(),
155        }),
156        Ok(SyscallClass::UserspaceReadableAllow) => Some(Syscall::UserspaceReadableAllow {
157            driver_number: r0 as u32 as usize,
158            subdriver_number: r1.as_usize_32bit(),
159            allow_address: r2.as_capability_ptr().as_ptr::<u8>().cast_mut(),
160            allow_size: r3.as_usize(),
161        }),
162        Ok(SyscallClass::ReadOnlyAllow) => Some(Syscall::ReadOnlyAllow {
163            driver_number: r0 as u32 as usize,
164            subdriver_number: r1.as_usize_32bit(),
165            allow_address: r2.as_capability_ptr().as_ptr(),
166            allow_size: r3.as_usize(),
167        }),
168        Ok(SyscallClass::Memop) => Some(Syscall::Memop {
169            operand: r0 as u32 as usize,
170            arg0: r1.as_usize(),
171        }),
172        Ok(SyscallClass::Exit) => Some(Syscall::Exit {
173            which: r0 as u32 as usize,
174            completion_code: r1.as_usize_32bit(),
175        }),
176        Err(_) => None,
177    }
178}
179
180/// Enumeration of the system call return type variant identifiers described in
181/// TRD104.
182///
183/// Each variant is associated with the respective variant identifier that would
184/// be passed along with the return value to userspace.
185#[repr(u32)]
186#[derive(Copy, Clone, Debug)]
187pub enum TRD104SyscallReturnVariant {
188    Failure = 0,
189    FailureU32 = 1,
190    FailureU32U32 = 2,
191    FailureU64 = 3,
192    Success = 128,
193    SuccessU32 = 129,
194    SuccessU32U32 = 130,
195    SuccessU64 = 131,
196    SuccessU32U32U32 = 132,
197    SuccessU32U64 = 133,
198}
199
200/// System call return variants defined as defined in TRD104.
201///
202/// These are a strict subset of the variants defined in the core
203/// kernel's [`SyscallReturn`] enum. For documentation on the
204/// individual variants, refer to this type instead.
205#[derive(Copy, Clone, Debug)]
206pub enum TRD104SyscallReturn {
207    Failure(ErrorCode),
208    FailureU32(ErrorCode, u32),
209    FailureU32U32(ErrorCode, u32, u32),
210    FailureU64(ErrorCode, u64),
211    Success,
212    SuccessU32(u32),
213    SuccessU32U32(u32, u32),
214    SuccessU32U32U32(u32, u32, u32),
215    SuccessU64(u64),
216    SuccessU32U64(u32, u64),
217    AllowReadWriteSuccess(*mut u8, usize),
218    AllowReadWriteFailure(ErrorCode, *mut u8, usize),
219    UserspaceReadableAllowSuccess(*mut u8, usize),
220    UserspaceReadableAllowFailure(ErrorCode, *mut u8, usize),
221    AllowReadOnlySuccess(*const u8, usize),
222    AllowReadOnlyFailure(ErrorCode, *const u8, usize),
223    SubscribeSuccess(*const (), usize),
224    SubscribeFailure(ErrorCode, *const (), usize),
225    YieldWaitFor(u32, u32, u32),
226}
227
228impl From<SyscallReturn> for TRD104SyscallReturn {
229    /// Map from the kernel's [`SyscallReturn`] enum to the subset of return
230    /// values specified in TRD104. This ensures backwards compatibility with
231    /// architectures implementing the ABI as specified in TRD104.
232    fn from(syscall_return: SyscallReturn) -> Self {
233        match syscall_return {
234            // Identical variants:
235            SyscallReturn::Failure(a) => TRD104SyscallReturn::Failure(a),
236            SyscallReturn::FailureU32(a, b) => TRD104SyscallReturn::FailureU32(a, b),
237            SyscallReturn::FailureU32U32(a, b, c) => TRD104SyscallReturn::FailureU32U32(a, b, c),
238            SyscallReturn::FailureU64(a, b) => TRD104SyscallReturn::FailureU64(a, b),
239            SyscallReturn::Success => TRD104SyscallReturn::Success,
240            SyscallReturn::SuccessU32(a) => TRD104SyscallReturn::SuccessU32(a),
241            SyscallReturn::SuccessU32U32(a, b) => TRD104SyscallReturn::SuccessU32U32(a, b),
242            SyscallReturn::SuccessU32U32U32(a, b, c) => {
243                TRD104SyscallReturn::SuccessU32U32U32(a, b, c)
244            }
245            SyscallReturn::SuccessU64(a) => TRD104SyscallReturn::SuccessU64(a),
246            SyscallReturn::SuccessU32U64(a, b) => TRD104SyscallReturn::SuccessU32U64(a, b),
247            SyscallReturn::AllowReadWriteSuccess(a, b) => {
248                TRD104SyscallReturn::AllowReadWriteSuccess(a, b)
249            }
250            SyscallReturn::AllowReadWriteFailure(a, b, c) => {
251                TRD104SyscallReturn::AllowReadWriteFailure(a, b, c)
252            }
253            SyscallReturn::UserspaceReadableAllowSuccess(a, b) => {
254                TRD104SyscallReturn::UserspaceReadableAllowSuccess(a, b)
255            }
256            SyscallReturn::UserspaceReadableAllowFailure(a, b, c) => {
257                TRD104SyscallReturn::UserspaceReadableAllowFailure(a, b, c)
258            }
259            SyscallReturn::AllowReadOnlySuccess(a, b) => {
260                TRD104SyscallReturn::AllowReadOnlySuccess(a, b)
261            }
262            SyscallReturn::AllowReadOnlyFailure(a, b, c) => {
263                TRD104SyscallReturn::AllowReadOnlyFailure(a, b, c)
264            }
265            SyscallReturn::SubscribeSuccess(a, b) => TRD104SyscallReturn::SubscribeSuccess(a, b),
266            SyscallReturn::SubscribeFailure(a, b, c) => {
267                TRD104SyscallReturn::SubscribeFailure(a, b, c)
268            }
269            SyscallReturn::YieldWaitFor(a, b, c) => {
270                TRD104SyscallReturn::YieldWaitFor(a as u32, b as u32, c as u32)
271            }
272
273            // Compatibility mapping:
274            SyscallReturn::SuccessAddr(a) => TRD104SyscallReturn::SuccessU32(a as u32),
275            SyscallReturn::SuccessPtr(a) => {
276                TRD104SyscallReturn::SuccessU32(a.as_ptr::<()>() as u32)
277            }
278        }
279    }
280}
281
282/// Encode the system call return value into 4 registers, following the encoding
283/// specified in TRD104. Architectures which do not follow TRD104 are free to
284/// define their own encoding.
285pub fn encode_syscall_return_trd104(
286    syscall_return: &TRD104SyscallReturn,
287    a0: &mut u32,
288    a1: &mut u32,
289    a2: &mut u32,
290    a3: &mut u32,
291) {
292    match *syscall_return {
293        TRD104SyscallReturn::Failure(e) => {
294            *a0 = TRD104SyscallReturnVariant::Failure as u32;
295            *a1 = usize::from(e) as u32;
296        }
297        TRD104SyscallReturn::FailureU32(e, data0) => {
298            *a0 = TRD104SyscallReturnVariant::FailureU32 as u32;
299            *a1 = usize::from(e) as u32;
300            *a2 = data0;
301        }
302        TRD104SyscallReturn::FailureU32U32(e, data0, data1) => {
303            *a0 = TRD104SyscallReturnVariant::FailureU32U32 as u32;
304            *a1 = usize::from(e) as u32;
305            *a2 = data0;
306            *a3 = data1;
307        }
308        TRD104SyscallReturn::FailureU64(e, data0) => {
309            let (data0_msb, data0_lsb) = u64_to_be_u32s(data0);
310            *a0 = TRD104SyscallReturnVariant::FailureU64 as u32;
311            *a1 = usize::from(e) as u32;
312            *a2 = data0_lsb;
313            *a3 = data0_msb;
314        }
315        TRD104SyscallReturn::Success => {
316            *a0 = TRD104SyscallReturnVariant::Success as u32;
317        }
318        TRD104SyscallReturn::SuccessU32(data0) => {
319            *a0 = TRD104SyscallReturnVariant::SuccessU32 as u32;
320            *a1 = data0;
321        }
322        TRD104SyscallReturn::SuccessU32U32(data0, data1) => {
323            *a0 = TRD104SyscallReturnVariant::SuccessU32U32 as u32;
324            *a1 = data0;
325            *a2 = data1;
326        }
327        TRD104SyscallReturn::SuccessU32U32U32(data0, data1, data2) => {
328            *a0 = TRD104SyscallReturnVariant::SuccessU32U32U32 as u32;
329            *a1 = data0;
330            *a2 = data1;
331            *a3 = data2;
332        }
333        TRD104SyscallReturn::SuccessU64(data0) => {
334            let (data0_msb, data0_lsb) = u64_to_be_u32s(data0);
335
336            *a0 = TRD104SyscallReturnVariant::SuccessU64 as u32;
337            *a1 = data0_lsb;
338            *a2 = data0_msb;
339        }
340        TRD104SyscallReturn::SuccessU32U64(data0, data1) => {
341            let (data1_msb, data1_lsb) = u64_to_be_u32s(data1);
342
343            *a0 = TRD104SyscallReturnVariant::SuccessU32U64 as u32;
344            *a1 = data0;
345            *a2 = data1_lsb;
346            *a3 = data1_msb;
347        }
348        TRD104SyscallReturn::AllowReadWriteSuccess(ptr, len) => {
349            *a0 = TRD104SyscallReturnVariant::SuccessU32U32 as u32;
350            *a1 = ptr as u32;
351            *a2 = len as u32;
352        }
353        TRD104SyscallReturn::UserspaceReadableAllowSuccess(ptr, len) => {
354            *a0 = TRD104SyscallReturnVariant::SuccessU32U32 as u32;
355            *a1 = ptr as u32;
356            *a2 = len as u32;
357        }
358        TRD104SyscallReturn::AllowReadWriteFailure(err, ptr, len) => {
359            *a0 = TRD104SyscallReturnVariant::FailureU32U32 as u32;
360            *a1 = usize::from(err) as u32;
361            *a2 = ptr as u32;
362            *a3 = len as u32;
363        }
364        TRD104SyscallReturn::UserspaceReadableAllowFailure(err, ptr, len) => {
365            *a0 = TRD104SyscallReturnVariant::FailureU32U32 as u32;
366            *a1 = usize::from(err) as u32;
367            *a2 = ptr as u32;
368            *a3 = len as u32;
369        }
370        TRD104SyscallReturn::AllowReadOnlySuccess(ptr, len) => {
371            *a0 = TRD104SyscallReturnVariant::SuccessU32U32 as u32;
372            *a1 = ptr as u32;
373            *a2 = len as u32;
374        }
375        TRD104SyscallReturn::AllowReadOnlyFailure(err, ptr, len) => {
376            *a0 = TRD104SyscallReturnVariant::FailureU32U32 as u32;
377            *a1 = usize::from(err) as u32;
378            *a2 = ptr as u32;
379            *a3 = len as u32;
380        }
381        TRD104SyscallReturn::SubscribeSuccess(ptr, data) => {
382            *a0 = TRD104SyscallReturnVariant::SuccessU32U32 as u32;
383            *a1 = ptr as u32;
384            *a2 = data as u32;
385        }
386        TRD104SyscallReturn::SubscribeFailure(err, ptr, data) => {
387            *a0 = TRD104SyscallReturnVariant::FailureU32U32 as u32;
388            *a1 = usize::from(err) as u32;
389            *a2 = ptr as u32;
390            *a3 = data as u32;
391        }
392        TRD104SyscallReturn::YieldWaitFor(data0, data1, data2) => {
393            *a0 = data0;
394            *a1 = data1;
395            *a2 = data2;
396        }
397    }
398}
399
400/// Enumeration of the system call return type variant identifiers described in
401/// TRD-RISCV64bit.
402///
403/// Each variant is associated with the respective variant identifier that would
404/// be passed along with the return value to userspace.
405#[repr(u64)]
406#[derive(Copy, Clone, Debug)]
407pub enum TRDRiscv64bitSyscallReturnVariant {
408    Failure = 0,
409    FailureU32 = 1,
410    FailureU32U32 = 2,
411    FailureU64 = 3,
412    FailureFnptrOpaque = 4,
413    FailurePtrLen = 5,
414    Success = 128,
415    SuccessU32 = 129,
416    SuccessU32U32 = 130,
417    SuccessU64 = 131,
418    SuccessU32U32U32 = 132,
419    SuccessU32U64 = 133,
420    SuccessFnptrOpaque = 134,
421    SuccessPtrLen = 135,
422    SuccessPtr = 136,
423    SuccessAddr = 137,
424}
425
426/// System call return variants defined as defined in TRD-Riscv64bit.
427///
428/// These variants correspond to those defined in the core kernel's
429/// [`SyscallReturn`] enum. For documentation on the individual
430/// variants, refer to that type instead.
431#[derive(Copy, Clone, Debug)]
432pub enum TRDRiscv64bitSyscallReturn {
433    Failure(ErrorCode),
434    FailureU32(ErrorCode, u32),
435    FailureU32U32(ErrorCode, u32, u32),
436    FailureU64(ErrorCode, u64),
437    Success,
438    SuccessU32(u32),
439    SuccessU32U32(u32, u32),
440    SuccessU32U32U32(u32, u32, u32),
441    SuccessU64(u64),
442    SuccessU32U64(u32, u64),
443    AllowReadWriteSuccess(*mut u8, usize),
444    AllowReadWriteFailure(ErrorCode, *mut u8, usize),
445    UserspaceReadableAllowSuccess(*mut u8, usize),
446    UserspaceReadableAllowFailure(ErrorCode, *mut u8, usize),
447    AllowReadOnlySuccess(*const u8, usize),
448    AllowReadOnlyFailure(ErrorCode, *const u8, usize),
449    SubscribeSuccess(*const (), usize),
450    SubscribeFailure(ErrorCode, *const (), usize),
451    YieldWaitFor(u32, u32, u32),
452    SuccessAddr(usize),
453    SuccessPtr(CapabilityPtr),
454}
455
456impl From<SyscallReturn> for TRDRiscv64bitSyscallReturn {
457    /// Map from the kernel's [`SyscallReturn`] enum to the subset of return
458    /// values specified in TRD-Riscv64bit. This ensures backwards compatibility with
459    /// architectures implementing the ABI as specified in the TRD.
460    fn from(syscall_return: SyscallReturn) -> Self {
461        match syscall_return {
462            SyscallReturn::Failure(a) => TRDRiscv64bitSyscallReturn::Failure(a),
463            SyscallReturn::FailureU32(a, b) => TRDRiscv64bitSyscallReturn::FailureU32(a, b),
464            SyscallReturn::FailureU32U32(a, b, c) => {
465                TRDRiscv64bitSyscallReturn::FailureU32U32(a, b, c)
466            }
467            SyscallReturn::FailureU64(a, b) => TRDRiscv64bitSyscallReturn::FailureU64(a, b),
468            SyscallReturn::Success => TRDRiscv64bitSyscallReturn::Success,
469            SyscallReturn::SuccessU32(a) => TRDRiscv64bitSyscallReturn::SuccessU32(a),
470            SyscallReturn::SuccessU32U32(a, b) => TRDRiscv64bitSyscallReturn::SuccessU32U32(a, b),
471            SyscallReturn::SuccessU32U32U32(a, b, c) => {
472                TRDRiscv64bitSyscallReturn::SuccessU32U32U32(a, b, c)
473            }
474            SyscallReturn::SuccessU64(a) => TRDRiscv64bitSyscallReturn::SuccessU64(a),
475            SyscallReturn::SuccessU32U64(a, b) => TRDRiscv64bitSyscallReturn::SuccessU32U64(a, b),
476            SyscallReturn::AllowReadWriteSuccess(a, b) => {
477                TRDRiscv64bitSyscallReturn::AllowReadWriteSuccess(a, b)
478            }
479            SyscallReturn::AllowReadWriteFailure(a, b, c) => {
480                TRDRiscv64bitSyscallReturn::AllowReadWriteFailure(a, b, c)
481            }
482            SyscallReturn::UserspaceReadableAllowSuccess(a, b) => {
483                TRDRiscv64bitSyscallReturn::UserspaceReadableAllowSuccess(a, b)
484            }
485            SyscallReturn::UserspaceReadableAllowFailure(a, b, c) => {
486                TRDRiscv64bitSyscallReturn::UserspaceReadableAllowFailure(a, b, c)
487            }
488            SyscallReturn::AllowReadOnlySuccess(a, b) => {
489                TRDRiscv64bitSyscallReturn::AllowReadOnlySuccess(a, b)
490            }
491            SyscallReturn::AllowReadOnlyFailure(a, b, c) => {
492                TRDRiscv64bitSyscallReturn::AllowReadOnlyFailure(a, b, c)
493            }
494            SyscallReturn::SubscribeSuccess(a, b) => {
495                TRDRiscv64bitSyscallReturn::SubscribeSuccess(a, b)
496            }
497            SyscallReturn::SubscribeFailure(a, b, c) => {
498                TRDRiscv64bitSyscallReturn::SubscribeFailure(a, b, c)
499            }
500            SyscallReturn::YieldWaitFor(a, b, c) => {
501                TRDRiscv64bitSyscallReturn::YieldWaitFor(a as u32, b as u32, c as u32)
502            }
503            SyscallReturn::SuccessAddr(a) => TRDRiscv64bitSyscallReturn::SuccessAddr(a),
504            SyscallReturn::SuccessPtr(a) => TRDRiscv64bitSyscallReturn::SuccessPtr(a),
505        }
506    }
507}
508
509/// Encode the system call return value into 4 registers.
510///
511/// This follows the encoding specified in TRD-Riscv64bit.
512pub fn encode_syscall_return_trd64bit(
513    syscall_return: &TRDRiscv64bitSyscallReturn,
514    a0: &mut u64,
515    a1: &mut u64,
516    a2: &mut u64,
517    a3: &mut u64,
518) {
519    match *syscall_return {
520        TRDRiscv64bitSyscallReturn::Failure(e) => {
521            *a0 = TRDRiscv64bitSyscallReturnVariant::Failure as u64;
522            *a1 = e as u64;
523        }
524        TRDRiscv64bitSyscallReturn::FailureU32(e, data0) => {
525            *a0 = TRDRiscv64bitSyscallReturnVariant::FailureU32 as u64;
526            *a1 = e as u64;
527            *a2 = data0 as u64;
528        }
529        TRDRiscv64bitSyscallReturn::FailureU32U32(e, data0, data1) => {
530            *a0 = TRDRiscv64bitSyscallReturnVariant::FailureU32U32 as u64;
531            *a1 = e as u64;
532            *a2 = data0 as u64;
533            *a3 = data1 as u64;
534        }
535        TRDRiscv64bitSyscallReturn::FailureU64(e, data0) => {
536            *a0 = TRDRiscv64bitSyscallReturnVariant::FailureU64 as u64;
537            *a1 = e as u64;
538            *a2 = data0;
539        }
540        TRDRiscv64bitSyscallReturn::Success => {
541            *a0 = TRDRiscv64bitSyscallReturnVariant::Success as u64;
542        }
543        TRDRiscv64bitSyscallReturn::SuccessU32(data0) => {
544            *a0 = TRDRiscv64bitSyscallReturnVariant::SuccessU32 as u64;
545            *a1 = data0 as u64;
546        }
547        TRDRiscv64bitSyscallReturn::SuccessU32U32(data0, data1) => {
548            *a0 = TRDRiscv64bitSyscallReturnVariant::SuccessU32U32 as u64;
549            *a1 = data0 as u64;
550            *a2 = data1 as u64;
551        }
552        TRDRiscv64bitSyscallReturn::SuccessU32U32U32(data0, data1, data2) => {
553            *a0 = TRDRiscv64bitSyscallReturnVariant::SuccessU32U32U32 as u64;
554            *a1 = data0 as u64;
555            *a2 = data1 as u64;
556            *a3 = data2 as u64;
557        }
558        TRDRiscv64bitSyscallReturn::SuccessU64(data0) => {
559            *a0 = TRDRiscv64bitSyscallReturnVariant::SuccessU64 as u64;
560            *a1 = data0;
561        }
562        TRDRiscv64bitSyscallReturn::SuccessU32U64(data0, data1) => {
563            *a0 = TRDRiscv64bitSyscallReturnVariant::SuccessU32U64 as u64;
564            *a1 = data0 as u64;
565            *a2 = data1;
566        }
567        TRDRiscv64bitSyscallReturn::AllowReadWriteSuccess(ptr, len) => {
568            *a0 = TRDRiscv64bitSyscallReturnVariant::SuccessPtrLen as u64;
569            *a1 = ptr as u64;
570            *a2 = len as u64;
571        }
572        TRDRiscv64bitSyscallReturn::UserspaceReadableAllowSuccess(ptr, len) => {
573            *a0 = TRDRiscv64bitSyscallReturnVariant::SuccessPtrLen as u64;
574            *a1 = ptr as u64;
575            *a2 = len as u64;
576        }
577        TRDRiscv64bitSyscallReturn::AllowReadWriteFailure(err, ptr, len) => {
578            *a0 = TRDRiscv64bitSyscallReturnVariant::FailurePtrLen as u64;
579            *a1 = err as u64;
580            *a2 = ptr as u64;
581            *a3 = len as u64;
582        }
583        TRDRiscv64bitSyscallReturn::UserspaceReadableAllowFailure(err, ptr, len) => {
584            *a0 = TRDRiscv64bitSyscallReturnVariant::FailurePtrLen as u64;
585            *a1 = err as u64;
586            *a2 = ptr as u64;
587            *a3 = len as u64;
588        }
589        TRDRiscv64bitSyscallReturn::AllowReadOnlySuccess(ptr, len) => {
590            *a0 = TRDRiscv64bitSyscallReturnVariant::SuccessPtrLen as u64;
591            *a1 = ptr as u64;
592            *a2 = len as u64;
593        }
594        TRDRiscv64bitSyscallReturn::AllowReadOnlyFailure(err, ptr, len) => {
595            *a0 = TRDRiscv64bitSyscallReturnVariant::FailurePtrLen as u64;
596            *a1 = err as u64;
597            *a2 = ptr as u64;
598            *a3 = len as u64;
599        }
600        TRDRiscv64bitSyscallReturn::SubscribeSuccess(ptr, data) => {
601            *a0 = TRDRiscv64bitSyscallReturnVariant::SuccessFnptrOpaque as u64;
602            *a1 = ptr as u64;
603            *a2 = data as u64;
604        }
605        TRDRiscv64bitSyscallReturn::SubscribeFailure(err, ptr, data) => {
606            *a0 = TRDRiscv64bitSyscallReturnVariant::FailureFnptrOpaque as u64;
607            *a1 = err as u64;
608            *a2 = ptr as u64;
609            *a3 = data as u64;
610        }
611        TRDRiscv64bitSyscallReturn::YieldWaitFor(data0, data1, data2) => {
612            *a0 = data0 as u64;
613            *a1 = data1 as u64;
614            *a2 = data2 as u64;
615        }
616        TRDRiscv64bitSyscallReturn::SuccessAddr(addr) => {
617            *a0 = TRDRiscv64bitSyscallReturnVariant::SuccessAddr as u64;
618            *a1 = addr as u64;
619        }
620        TRDRiscv64bitSyscallReturn::SuccessPtr(ptr) => {
621            *a0 = TRDRiscv64bitSyscallReturnVariant::SuccessPtr as u64;
622            *a1 = ptr.as_ptr::<()>() as u64;
623        }
624    }
625}
626
627/// Encode the upcall arguments into 4 registers compatible with TRD104.
628pub fn encode_upcall_trd104(
629    upcall: &FunctionCall,
630    a0: &mut u32,
631    a1: &mut u32,
632    a2: &mut u32,
633    a3: &mut u32,
634) {
635    *a0 = upcall.argument0 as u32;
636    *a1 = upcall.argument1 as u32;
637    *a2 = upcall.argument2 as u32;
638    *a3 = upcall.argument3.as_usize() as u32;
639}
640
641/// Encode the upcall arguments into 4 registers compatible with TRD104.
642///
643/// Use pointer writes.
644///
645/// # Safety
646///
647/// All pointers `a0`, `a1`, `a2`, `a3` must point to valid, aligned, and
648/// writable memory locations.
649pub unsafe fn encode_upcall_trd104_ptr(
650    upcall: &FunctionCall,
651    a0: *mut u32,
652    a1: *mut u32,
653    a2: *mut u32,
654    a3: *mut u32,
655) {
656    // SAFETY: All safety invariants must be upheld by the function caller.
657    unsafe {
658        core::ptr::write(a0, upcall.argument0 as u32);
659        core::ptr::write(a1, upcall.argument1 as u32);
660        core::ptr::write(a2, upcall.argument2 as u32);
661        core::ptr::write(a3, upcall.argument3.as_usize() as u32);
662    }
663}
664
665/// Encode the upcall arguments into 4 registers compatible with TRD-RISCV64.
666pub fn encode_upcall_trd64bit(
667    upcall: &FunctionCall,
668    a0: &mut u64,
669    a1: &mut u64,
670    a2: &mut u64,
671    a3: &mut u64,
672) {
673    // The TRD specifies that arguments 0, 1, and 2 are u32s. So, we cast to u32
674    // to ensure we are not using the upper bits, and then cast to u64 to match
675    // the register size.
676    *a0 = upcall.argument0 as u32 as u64;
677    *a1 = upcall.argument1 as u32 as u64;
678    *a2 = upcall.argument2 as u32 as u64;
679    *a3 = upcall.argument3.as_usize() as u64;
680}
681
682/// Encode the upcall arguments into 4 registers compatible with TRD-RISCV64.
683///
684/// Use pointer writes.
685///
686/// # Safety
687///
688/// All pointers `a0`, `a1`, `a2`, `a3` must point to valid, aligned, and
689/// writable memory locations.
690pub unsafe fn encode_upcall_trd64bit_ptr(
691    upcall: &FunctionCall,
692    a0: *mut u64,
693    a1: *mut u64,
694    a2: *mut u64,
695    a3: *mut u64,
696) {
697    // SAFETY: All safety invariants must be upheld by the function caller.
698    unsafe {
699        // The TRD specifies that arguments 0, 1, and 2 are u32s. So, we cast to
700        // u32 to ensure we are not using the upper bits, and then cast to u64
701        // to match the register size.
702        core::ptr::write(a0, upcall.argument0 as u32 as u64);
703        core::ptr::write(a1, upcall.argument1 as u32 as u64);
704        core::ptr::write(a2, upcall.argument2 as u32 as u64);
705        core::ptr::write(a3, upcall.argument3.as_usize() as u64);
706    }
707}