sentry_uapi/
syscall.rs

1// SPDX-FileCopyrightText: 2023 Ledger SAS
2// SPDX-FileCopyrightText: 2025 H2Lab OSS Team
3// SPDX-License-Identifier: Apache-2.0
4
5use crate::exchange;
6use crate::systypes::*;
7#[cfg(not(target_arch = "x86_64"))]
8use core::arch::asm;
9
10/// Exits the current job
11///
12/// # Usage
13///
14/// This function immediately exit the current job instance, returning
15/// the given status as job termination status code.
16///
17/// The status code is any 32 bit integer.
18/// There is no Sentry-level semantic about the status code value. This
19/// value is only used in order to call the `_finish(status)` function of the
20/// current task so that it is possible to delivers differentiated
21/// termination behavior depending on the returned status code. See
22/// [Sentry concept](https://sentry-kernel.readthedocs.io/en/latest/concepts/initial.html#action-on-termination)
23/// for more informations about task terminations.
24///
25/// > **NOTE**: This function never returns.
26///
27/// # Example
28///
29/// ```ignore
30/// sentry_uapi::syscall::exit(42);
31/// // unreachable
32/// ```
33///
34#[inline(always)]
35pub fn exit(status: i32) -> Status {
36    syscall!(Syscall::Exit, status as u32).into()
37}
38
39/// Get global identifier for a given process label
40///
41/// # Usage
42///
43/// This syscall set the task handle that matches the given label in the
44/// SVC_EXCHANGE area if the label is matched and is in the same domain as
45/// the current task in the kernel task vector.
46///
47/// If no handle is found, the resulting status is Status::Invalid. If the
48/// requested application is not in the same domain as the current one,
49/// Status::Denied is returned. The SVC_EXCHANGE area is not set.
50///
51/// > **NOTE**: except without good reasons, do not use this syscall directly
52///
53/// # Example
54///
55/// ```ignore
56/// match sentry_uapi::syscall::get_process_handle(tasklabel) {
57///     Status::Ok => (),
58///     any_err => return (any_err),
59/// }
60/// let exch_area = unsafe { &mut SVC_EXCHANGE_AREA[..4] };
61/// let handle = u32::from_ne_bytes(exch_area.try_into().map_err(|_| Status::Invalid)?);
62/// ```
63///
64#[inline(always)]
65pub fn get_process_handle(process: TaskLabel) -> Status {
66    syscall!(Syscall::GetProcessHandle, process).into()
67}
68
69/// Get global identifier for a given SHM label
70///
71/// # Usage
72///
73/// This syscall set the SHM handle that matches the given label in the
74/// SVC_EXCHANGE area if the label matches a shared-memory that has been
75/// declared in the device tree, and is owned or shared with the current task.
76///
77/// If label is not found or no accessible shared memory is found, the
78/// resulting status is Status::Invalid. The SVC_EXCHANGE area is not set.
79///
80///  > **NOTE**: except without good reasons, do not use this syscall directly
81///
82/// # Example
83///
84/// ```ignore
85/// match sentry_uapi::syscall::get_shm_handle(shmlabel) {
86///     Status::Ok => (),
87///     any_err => return (any_err),
88/// }
89/// let exch_area = unsafe { &mut SVC_EXCHANGE_AREA[..4] };
90/// let handle = u32::from_ne_bytes(exch_area.try_into().map_err(|_| Status::Invalid)?);
91/// ```
92///
93#[inline(always)]
94pub fn get_shm_handle(shm: ShmLabel) -> Status {
95    syscall!(Syscall::GetShmHandle, shm).into()
96}
97
98/// Get global identifier for a given DMA stream label
99///
100/// # Usage
101///
102/// This syscall set the MA stream handle that matches the given label in the
103/// SVC_EXCHANGE area if the label matches a DMA stream that has been
104/// declared in the device tree and is owned by the current task.
105///
106/// If label is not found or no accessible shared memory is found, the
107/// resulting status is Status::Invalid. The SVC_EXCHANGE area is not set.
108///
109///  > **NOTE**: except without good reasons, do not use this syscall directly
110///
111/// # Example
112///
113/// ```ignore
114/// match get_dma_stream_handle(streamlabel) {
115///    Status::Ok => (),
116///     any_err => return (any_err),
117/// }
118/// let exch_area = unsafe { &mut SVC_EXCHANGE_AREA[..4] };
119/// let handle = u32::from_ne_bytes(exch_area.try_into().map_err(|_| Status::Invalid)?);
120/// ```
121///
122#[inline(always)]
123pub fn get_dma_stream_handle(stream: StreamLabel) -> Status {
124    syscall!(Syscall::GetDmaStreamHandle, stream).into()
125}
126
127/// Release the processor before the end of the current quantum
128///
129/// # Usage
130///
131/// Allows triggering a schedule() even if not in the process's central blocking point.
132/// This permits to preempt the current job even if the current quantum is reached.
133/// This permits to ensure that when coming back from a yield call, the current
134/// job quantum is full, giving a well-known time window in which the job will not
135/// be preempted by another job.
136///
137/// Calling this syscall do not make the task uneligible, but instead push the task
138/// bellow in the Sentry's scheduler queue. See [Sentry scheduler](https://sentry-kernel.readthedocs.io/en/latest/sw_architecture/schedulers.html#rrmq-scheduler)
139/// documentation for more information.
140///
141/// This syscall never fails. This syscall preempt the current job.
142///
143/// There is no useful check of the syscall return code as it is always Status::Ok.
144///
145/// # Example
146///
147/// ```ignore
148/// sentry_uapi::syscall::sched_yield();
149/// ```
150///
151#[inline(always)]
152pub fn sched_yield() -> Status {
153    syscall!(Syscall::Yield).into()
154}
155
156/// Sleep for a given amount of time
157///
158/// # Usage
159///
160/// Allows to preempt and release the current job from the scheduler queue for a
161/// given amount of time.
162///
163/// The requested sleeping method support multiple modes, based on the
164/// [`SleepMode`]
165///
166///  - **deep sleeping** — Ensure that the task is sleeping for a minimum amount of
167///    time, whatever happen.
168///
169///  - **shallow sleeping** — sleeps for a maximum duration, allowing job schedule
170///    if an external event targets the task.
171///
172/// The sleep duration is based on the duration_ms input parameter.
173/// See [`SleepDuration`] definition for various duration definitions.
174///
175/// This syscall never fails, but may return different Status code depending on the
176/// awakening event.
177/// If the job is awoken before the end of the required sleep duration, the
178/// returned status is Status::Intr. If the job reaches the end of its
179/// sleeping duration, the syscall returns Status::Timeout.
180///
181/// # Example
182///
183/// ```ignore
184/// match sentry_uapi::syscall::sleep(SleepMode::Deep, D5ms) {
185///    Status::Intr => (),
186///    Status::Timeout => (),
187/// }
188/// ```
189///
190#[inline(always)]
191pub fn sleep(duration_ms: SleepDuration, mode: SleepMode) -> Status {
192    syscall!(Syscall::Sleep, u32::from(duration_ms), u32::from(mode)).into()
193}
194
195/// Start another task identified by its label
196///
197/// # Usage
198///
199/// This syscall allows a task with the CAP_SYS_PROCSTART capability
200/// to start another task using its label.
201///
202/// Starting another task do not preempt the current job.
203/// The newly started task is eligible and added to the scheduler queue.
204///
205/// If the current job do not own the capability, this syscall returns
206/// Status::Denied. If the target task is already started, this syscall
207/// returns Status::Invalid
208///
209/// # Example
210///
211/// ```ignore
212/// match start(tsklabel) {
213///    Status::Ok => (),
214///    any_err => return(any_err),
215/// }
216/// ```
217///
218#[inline(always)]
219pub fn start(process: TaskLabel) -> Status {
220    syscall!(Syscall::Start, process).into()
221}
222
223///  Map a device in the caller's memory layout
224///
225/// # Usage
226///
227/// Maps a given device identified by its handle (see [`get_device_handle`]).
228///
229/// The memory mapping system is responsible for verifying that there is enough space
230/// in the caller's memory layout, and if yes, do map, synchronously, the device area
231/// in the caller's memory. The device is mapped read-write, so that the caller can
232/// directly configure it.
233///
234/// Any mapped device can be unmapped (see [`unmap_dev`]).
235///
236/// This syscall returns Status::Ok if the device is successfully mapped by the kernel.
237///
238/// If the device handle is not owned by the caller or the caller do not own the
239/// device's capability, the syscall returns Status::Denied
240/// If the device handle is not found, or the device is already mapped, the syscall
241/// returns Status::Invalid.
242/// If the device can't be mapped (e.g. if the task memory layout is already full)
243/// the syscall returns Status::Busy.
244///
245/// # Example
246///
247/// ```ignore
248/// match map_dev(devh) {
249///    Status::Ok => (),
250///    Status::Busy => (unmap_other()),
251///    any_err => return(any_err),
252/// }
253/// ```
254///
255#[inline(always)]
256pub fn map_dev(dev: DeviceHandle) -> Status {
257    syscall!(Syscall::MapDev, dev).into()
258}
259
260///  Map a shared memory in the caller's memory layout
261///
262/// # Usage
263///
264/// Maps a given shared identified by its handle (see [`get_shm_handle`]).
265///
266/// The memory mapping system is responsible for verifying that there is enough space
267/// in the caller's memory layout, and if yes, do map, synchronously, the shared memory
268/// in the caller's memory.
269/// The memory access policy is based on the SHM policy defined for the caller through
270/// the [`shm_set_credential`] syscall. The SHM mapping is synchronous.
271///
272/// Any shared memory can be unmapped (see [`unmap_shm`]).
273///
274/// This syscall returns Status::Ok if the device is successfully mapped by the kernel.
275///
276/// If the shared memory handle is not associated to the caller (neither own nor previously declared
277/// as user), the syscall returns Status::Denied
278/// If the shared memory handle is not found, or the shared memory is already mapped, the syscall
279/// returns Status::Invalid.
280/// If the shared memory can't be mapped (e.g. if the task memory layout is already full)
281/// the syscall returns Status::Busy.
282///
283/// # Example
284///
285/// ```ignore
286/// match map_shm(shmh) {
287///    Status::Ok => (),
288///    Status::Busy => (unmap_other()),
289///    any_err => return(any_err),
290/// }
291/// ```
292///
293#[inline(always)]
294pub fn map_shm(shm: ShmHandle) -> Status {
295    syscall!(Syscall::MapShm, shm).into()
296}
297
298///  Unùap a previously mapped device
299///
300/// # Usage
301///
302/// Unmap a device that has been previously mapped by the caller, once no more
303/// required. The device device is identified by its handle (see [`get_device_handle`]).
304///
305/// The memory mapping system is responsible for verifying that the device is already
306/// mapped in the caller's memory layout. There is neither capability nor ownership check
307/// as these checks are made at map time. Unmapping is synchronous.
308///
309/// An unmapped device can always be remapped later (see [`map_dev`]).
310///
311/// This syscall returns Status::Ok if the device is successfully unmapped by the kernel.
312///
313/// If the device is not already mapped by the caller, the syscall
314/// returns Status::Invalid.
315///
316/// # Example
317///
318/// ```ignore
319/// match unmap_dev(devh) {
320///    Status::Ok => (),
321///    any_err => return(any_err),
322/// }
323/// ```
324///
325#[inline(always)]
326pub fn unmap_dev(dev: DeviceHandle) -> Status {
327    syscall!(Syscall::UnmapDev, dev).into()
328}
329
330///  Unmap a previously mapped shared memory
331///
332/// # Usage
333///
334/// Unmap a shared memory that has been previously mapped by the caller, once no more
335/// required. The shared memory is identified by its handle (see [`get_shm_handle`]).
336///
337/// The memory mapping system is responsible for verifying that the shared memory is already
338/// mapped in the caller's memory layout. There is no ownership check
339/// as these checks are made at map time. Unmapping is synchronous.
340///
341/// An unmapped shared memory can always be remapped later while credentials are
342/// still valid for the caller (see [`map_shm`] and [`shm_set_credential`]).
343///
344/// This syscall returns Status::Ok if the shared memory is successfully unmapped by the kernel.
345/// If the shared memory is not already mapped by the caller, the syscall returns Status::Invalid.
346///
347/// # Example
348///
349/// ```ignore
350/// match unmap_shm(devh) {
351///    Status::Ok => (),
352///    any_err => return(any_err),
353/// }
354/// ```
355///
356#[inline(always)]
357pub fn unmap_shm(shm: ShmHandle) -> Status {
358    syscall!(Syscall::UnmapShm, shm).into()
359}
360
361///  Set the credentials associated to a shared memory
362///
363/// # Usage
364///
365/// Specify the credentials and declare usership for a given, owned, shared memory.
366/// a shared memory hold two instances of credentials. One for the shared memory
367/// owner and one for the samed memory user.
368/// This syscall provides a credentials configuration interface for both instances,
369/// only for the shared memory owner.
370///
371/// Credentials are based on a bitfield model, as defined in [`SHMPermission`].
372/// Setting credentials is synchronous. There is no specific capability associated
373/// to this interface as the shared memory ownership is considered instead.
374///
375/// Setting shared memory credential is **required** before any usage of the shared memory,
376/// meaning that even the owner can't manipulate a shared memory while the credentials have
377/// not been set at least one.
378///
379/// When setting credentials for another task, the shared memory is synchronously ready to
380/// be used by the other task without any other required action. The shared memory handle
381/// exchange with the other task is out of the current syscall and needs to be made through
382/// a task-specific, independent, communication channel.
383///
384/// a shared memory credentials can't be set for a given target (owner or user) while this
385/// very target has mapped the shared memory. This check is **not done** if the shared memory
386/// is used without mapping (e.g. if the target uses DMA stream(s) that target this shared
387/// memory without having it mapped). Such a behavior control is instead delegated to the integrator
388/// through the analysis of the device tree configuration, including the shared memory declared
389/// owner and the declared shared memories for which the `dma-pool` attribute is set.
390///
391/// This syscall returns Status::Invalid if the shared memory handle is out of the caller's scope
392/// (if the caller is neither an owner or a user of the shared memory).
393/// This syscall returns Status::Invalid if any of the parameter is malformated or if the
394/// target task handle is not found.
395/// This syscall returns Status::Denied if the caller is a user of the shared memory, but do not
396/// own it.
397/// This syscall return Status::Busy if the target of the credential setting as mapped the
398/// shared memory.
399/// This syscall returns Status::Ok if the credentials are properly set.
400///
401/// # Example
402///
403/// ```ignore
404/// match shm_set_credential(shmh, myhandle, SHMPermission::Map | SHMPermission::Write) {
405///    Status::Ok => (),
406///    any_err => return(any_err),
407/// }
408/// ```
409///
410/// # See also
411///
412/// Shared memory related syscalls:
413/// [`get_shm_handle`], [`map_shm`], [`unmap_shm`] and [`shm_get_infos`].
414///
415#[inline(always)]
416pub fn shm_set_credential(shm: ShmHandle, id: TaskHandle, shm_perm: u32) -> Status {
417    syscall!(Syscall::SHMSetCredential, shm, id, shm_perm).into()
418}
419
420/// Send events to another job identified by its handle
421///
422/// # description
423///
424/// This syscall emit a short message through the SVC Exchange memory that target
425/// another job identified by its handle.
426///
427/// The IPC message content must be stored 'as is' in the SVC exchange memory before
428/// calling this syscall. The message length must be shorter than the SVC Exchange
429/// area.
430///
431/// `send_ipc()` is a blocking syscall. The current job is preempted and will
432/// be eligible again only if:
433///
434///    * the target job reads the IPC content using [`wait_for_event`]
435///    * the target job terminates
436///
437/// This syscall synchronously returns `Status::Invalid` If the target job handle
438/// is not found.
439/// This syscall synchronously returns `Status::Invalid` if the specified message len
440/// is bigger than
441/// This syscall returns `Status::Intr` if the target terminates while the IPC
442/// message is not yet read.
443/// This syscall returns `Status::Ok` once the target as read the IPC message.
444///
445/// **Note**: This syscall do not clear the SVC Exchange area.
446///
447/// There is no timeout notion in IPC emission, meaning that a given job can stay
448/// in a blocking state forever while the target neither read the emitted IPC nor
449/// terminates.
450///
451/// # examples
452///
453/// ```ignore
454/// uapi::send_ipc(TargetTaskh, IpcLen)?continue_here;
455/// ```
456///
457#[inline(always)]
458pub fn send_ipc(target: TaskHandle, length: u8) -> Status {
459    syscall!(Syscall::SendIPC, target, length as u32).into()
460}
461
462/// Send signal to another job identified by its handle
463///
464/// # description
465///
466/// This syscall emit a signal that target another job identified by its handle.
467///
468/// A signal is not data, and as such there is no data copy between tasks.
469/// Signals are asynchronous events. This syscall is not a blocking syscall, the
470/// target has not received the signal when returning from this syscall.
471///
472/// The signal list is compile-time fixed and can't be increased at runtime.
473/// The signal definitions and proper associated usage is listed in [`Signal`].
474///
475/// As this syscall is fully asynchronous, there is no way to be informed that
476/// the target has read the signal.
477///
478/// This syscall returns `Status::Invalid` If the target job handle
479/// is not found.
480/// This syscall returns `Status::Invalid` if the signal identifier do not exist.
481/// If a task emit a signal while the target hasn't read the previous one from
482/// the same source, the syscall returns `Status::Busy`.
483///
484/// This syscall returns `Status::Ok` once the signal is delivered into the target
485/// input queue.
486///
487/// **Note**: The signal input queue is per-peer with a depth of 1, meaning that there
488/// is no impact between tasks if multiple tasks send signals to the same target.
489///
490///
491/// # examples
492///
493/// ```ignore
494/// send_signal(TargetTaskh, Signal::Pipe)?continue_here;
495/// ```
496///
497#[inline(always)]
498pub fn send_signal(resource: u32, signal_type: Signal) -> Status {
499    syscall!(Syscall::SendSignal, resource, signal_type as u32).into()
500}
501
502#[allow(clippy::tabs_in_doc_comments)]
503/// Get the value of a given GPIO for a device identified by its handle
504///
505/// # description
506///
507/// This syscall allows to get back the value of a GPIO identifier associated
508/// to the corresponding pinmux declared in the device tree.
509///
510/// Considering the following device tree content:
511///
512/// ```dts
513/// &i2c1 {
514///     status = "okay";
515///	    sentry,owner = <0xC001F001>;
516///     pinctrl-0 = <&i2c1_sda>;
517/// }
518///
519/// i2c1_sda: i2c_sda_pb9  {
520///	    label = "sda";
521///     pinmux = <&gpiob 9 STM32_DT_PIN_MODE_ALTFUNC(4)>;
522///     pincfg = <STM32_OTYPER_OPEN_DRAIN \
523///               STM32_OSPEEDR_VERY_HIGH_SPEED \
524///               STM32_PUPDR_PULL_UP>;
525///    };
526/// ```
527/// The task-level i2c1 table generated would then hold a one entry
528/// vector with the Sda index named identified explicitly, targeting
529/// the lonely vector cell.
530///
531/// This identifier can be used as-is for the io input value.
532///
533/// This syscall returns Status::Invalid if the io does not exist for the
534/// given device handle or if the device handle itself is invalid or not owned.
535/// This syscall returns Status::Ok if the GPIO has been read, and the GPIO
536/// value can be acceded in the SVC_Exchange area.
537///
538/// # Example
539///
540/// ```ignore
541/// gpio_get(devh, Sda);
542/// ```
543///
544#[inline(always)]
545pub fn gpio_get(resource: u32, io: u8) -> Status {
546    syscall!(Syscall::GpioGet, resource, io as u32).into()
547}
548
549/// Set the value of a given GPIO for a device identified by its handle
550///
551/// # description
552///
553/// This syscall allows to get back the value of a GPIO identifier associated
554/// to the corresponding pinmux declared in the device tree.
555///
556/// Its behavior is similar to [`gpio_get`]. the value set is a classical
557/// boolean value, used in order to set high or low the value of the GPIO pin.
558///
559/// If the given GPIO is in input mode, the syscall returns `Status::Badstate`.
560///
561/// Except this specific behavior, this syscall behave the same way as [`gpio_get`].
562///
563#[inline(always)]
564pub fn gpio_set(resource: u32, io: u8, val: bool) -> Status {
565    syscall!(Syscall::GpioSet, resource, io as u32, val as u32).into()
566}
567
568/// Reset the value of a given GPIO for a device identified by its handle
569///
570/// # description
571///
572/// This syscall allows to get back the value of a GPIO identifier associated
573/// to the corresponding pinmux declared in the device tree.
574///
575/// Its behavior is similar to [`gpio_set`]. the value set is reset to low
576/// level, behave n the same way as `gpio_set()` with `value` parameter set
577/// to `false`.
578///
579#[inline(always)]
580pub fn gpio_reset(resource: u32, io: u8) -> Status {
581    syscall!(Syscall::GpioReset, resource, io as u32).into()
582}
583
584/// Toggle the value of a given GPIO for a device identified by its handle
585///
586/// # description
587///
588/// This syscall invert the GPIO pin value of a GPIO identifier associated
589/// to the corresponding pinmux declared in the device tree.
590///
591/// Its behavior is similar to [`gpio_reset`], except that the new GPIO pin
592/// value is based on the inversion of its current value.
593///
594#[inline(always)]
595pub fn gpio_toggle(resource: u32, io: u8) -> Status {
596    syscall!(Syscall::GpioToggle, resource, io as u32).into()
597}
598
599/// Configure a given GPIO for a device identified by its handle
600///
601/// # description
602///
603/// This syscall configures the GPIO using the corresponding pinmux settings set
604/// in the device-tree. See [`gpio_get`] for device-tree related description.
605///
606/// The return values are similar to [`gpio_get`]. The GPIO is synchronously
607/// set according to the corresponding pinmux definition.
608///
609#[inline(always)]
610pub fn gpio_configure(resource: u32, io: u8) -> Status {
611    syscall!(Syscall::GpioConfigure, resource, io as u32).into()
612}
613
614/// Get global identifier for a given device label
615///
616/// # Usage
617///
618/// This syscall set the device handle that matches the given label in the
619/// SVC_EXCHANGE area if the label is found in the kernel declared device list.
620///
621/// If no handle is found, the resulting status is Status::Invalid. If the
622/// requested device not in own by the caller, Status::Denied is returned.
623/// In these cases, the SVC_EXCHANGE area is not set.
624///
625///
626#[inline(always)]
627pub fn get_device_handle(devlabel: u8) -> Status {
628    syscall!(Syscall::GetDeviceHandle, devlabel as u32).into()
629}
630
631/// acknowledge at interrupt controller level the given interrupt
632#[inline(always)]
633pub fn irq_acknowledge(irq: u16) -> Status {
634    syscall!(Syscall::IrqAcknowledge, irq as u32).into()
635}
636
637/// enable (unmask) at interrupt controller level the given interrupt
638#[inline(always)]
639pub fn irq_enable(irq: u16) -> Status {
640    syscall!(Syscall::IrqEnable, irq as u32).into()
641}
642
643/// disable (mask) at interrupt controller level the given interrupt
644#[inline(always)]
645pub fn irq_disable(irq: u16) -> Status {
646    syscall!(Syscall::IrqDisable, irq as u32).into()
647}
648
649/// Wait for input event. Single active blocking syscall.
650///
651/// This syscall holds the current thread as long as none of the event requested
652/// in the given event mask is received.
653///
654/// The event source (a device for an interrupt, a PID for an IPC or signal) can be set.
655/// Setting the source to 0 means that any source is allowed.
656///
657/// If received, event information is set in the task SVC data
658/// structure and the function returns `Status::Ok`.
659///
660/// This function must be the single blocking point of the function (excepting
661/// sleep() case)
662///
663/// NOTE: The timeout is kept i32 by now due to C FFI. Usage of enumerate is not
664/// easy as, at the end, the value is set to a HW register... which is a u32, to be
665/// transferred to the kernel corresponding gate.
666///
667#[inline(always)]
668pub fn wait_for_event(mask: u8, timeout: i32) -> Status {
669    match syscall!(Syscall::WaitForEvent, u32::from(mask), timeout as u32).into() {
670        Status::Intr => syscall!(Syscall::WaitForEvent, u32::from(mask), timeout as u32).into(),
671        any => any,
672    }
673}
674
675/// Configure the CPU's sleep behaviour.
676///
677/// The `mode` parameters toggles between the two traditional wake-up
678/// modes for ARM CPUs:
679/// - wait for interrupt (`wfi`)
680/// - wait for event (`wfe`)
681///
682/// it also accepts two other mode values that enable or prevent the
683/// CPU from sleeping.
684#[inline(always)]
685pub fn pm_manage(mode: CPUSleep) -> Status {
686    syscall!(Syscall::PmManage, u32::from(mode)).into()
687}
688
689/// Send a SIGALRM signal to the task after `timeout_ms` milliseconds.
690#[inline(always)]
691pub fn alarm(timeout_ms: u32, flag: AlarmFlag) -> Status {
692    syscall!(Syscall::Alarm, timeout_ms, u32::from(flag)).into()
693}
694
695/// Send a message from the current task's 'svc_exchange area' through
696/// the UART.
697#[inline(always)]
698pub fn log(length: usize) -> Status {
699    if length > exchange::length() {
700        Status::Invalid
701    } else {
702        syscall!(Syscall::Log, length as u32).into()
703    }
704}
705
706/// Get random seed from the Sentry kernel RNG backend
707///
708/// # Usage
709///
710/// The random seed received is 32 bits length. If the caller needs to
711/// forge a longer seeds without using an application hosted derivation, the
712/// syscall need to be called multiple-time.
713///
714/// This syscall requires the caller to hold the CAP_CRY_KRNG capability.
715/// Without this capability, Status::Denied is returned.
716///
717/// The syscall may fail if the kernel RNG source fails to properly delivers
718/// a FIPS compliant random value. In that case, the syscall returns
719/// Status::Critical.
720///
721/// The generated seed is delivered through the SVC_EXCHANGE zone.
722///
723/// # Example
724///
725/// ```ignore
726/// match (get_random()) {
727///     Status::Ok => (),
728///     any_err => return(any_err),
729/// };
730/// ```
731///
732#[inline(always)]
733pub fn get_random() -> Status {
734    syscall!(Syscall::GetRandom).into()
735}
736
737/// Get back the elapsed time since startup, in various units
738///
739/// # Usage
740///
741/// The kernel store the elapsed time since its startup so that it is possible
742/// to get basic time measurement, being from startup or between two call to
743/// this API, in various format.
744///
745/// The precision required is passed in the unit argument, as defined in the
746/// [`Precision`] type.
747///
748/// Whatever the precision is, the returned value is always encoded as `u64`.
749///
750/// While [`Precision::Milliseconds`] and [`Precision::Microseconds`] do not
751/// require specific capability, [`Precision::Nanoseconds`] and [`Precision::Cycle`]
752/// require the CAP_TIM_HP_CHRONO capability. This avoid any hability to
753/// get high precision measurements that may leed to side channel analysis on
754/// another task's behavior without having the corresponding capability.
755///
756/// Most of the time, this capability is not required.
757///
758/// This syscall returns the value in the SVC_EXCHANGE area, where it needs to
759/// be read afterward.
760///
761#[inline(always)]
762pub fn get_cycle(precision: Precision) -> Status {
763    syscall!(Syscall::GetCycle, precision as u32).into()
764}
765
766/// Set a given explicit clock config
767///
768/// Required for complex devices that need to make clock configuration vary.
769/// Requires the CAP_SYS_POWER capability
770///
771/// - TODO: using dts instead
772/// - TODO: update documentation when moving to PM-related work
773#[inline(always)]
774pub fn pm_set_clock(clk_reg: u32, clkmsk: u32, val: u32) -> Status {
775    syscall!(Syscall::PmSetClock, clk_reg, clkmsk, val).into()
776}
777
778/// start a DMA stream
779///
780/// TODO: with complete DMA support
781#[inline(always)]
782pub fn dma_start_stream(dmah: StreamHandle) -> Status {
783    syscall!(Syscall::DmaStartStream, dmah).into()
784}
785
786/// suspend a DMA stream
787///
788/// TODO: with complete DMA support
789#[inline(always)]
790pub fn dma_suspend_stream(dmah: StreamHandle) -> Status {
791    syscall!(Syscall::DmaSuspendStream, dmah).into()
792}
793
794/// get the status of a given DMA stream
795///
796/// TODO: with complete DMA support
797#[inline(always)]
798pub fn dma_get_stream_status(dmah: StreamHandle) -> Status {
799    syscall!(Syscall::DmaGetStreamStatus, dmah).into()
800}
801
802/// get the static information of a given DMA stream
803///
804/// TODO: with complete DMA support
805#[inline(always)]
806pub fn shm_get_infos(shm: ShmHandle) -> Status {
807    syscall!(Syscall::ShmGetInfos, shm).into()
808}
809
810/// assign a DMA stream to its corresponding hardware channel
811///
812/// TODO: with complete DMA support
813#[inline(always)]
814pub fn dma_assign_stream(dmah: StreamHandle) -> Status {
815    syscall!(Syscall::DmaAssignStream, dmah).into()
816}
817
818/// unassign a DMA stream from its corresponding hardware channel
819///
820/// TODO: with complete DMA support
821#[inline(always)]
822pub fn dma_unassign_stream(dmah: StreamHandle) -> Status {
823    syscall!(Syscall::DmaUnassignStream, dmah).into()
824}
825
826/// get a DMA stream static (dts-related) information from the kernel, as a
827/// structured data through SVC_Exchange
828///
829/// TODO: with complete DMA support
830#[inline(always)]
831pub fn dma_get_stream_info(dmah: StreamHandle) -> Status {
832    syscall!(Syscall::DmaGetStreamInfo, dmah).into()
833}
834
835/// resume a DMA stream
836///
837/// TODO: with complete DMA support
838#[inline(always)]
839pub fn dma_resume_stream(dmah: StreamHandle) -> Status {
840    syscall!(Syscall::DmaResumeStream, dmah).into()
841}
842
843/// Set (enable) given capability for autotest
844/// Only autotest has the userspace knowledge of the capabilities effective values
845#[cfg(CONFIG_BUILD_TARGET_AUTOTEST)]
846#[inline(always)]
847pub fn autotest_set_capa(capability: u32) -> Status {
848    syscall!(Syscall::AutotestSetCapa, capability).into()
849}
850
851/// Clear (disable) given capability for autotest
852/// Only autotest has the userspace knowledge of the capabilities effective values
853#[cfg(CONFIG_BUILD_TARGET_AUTOTEST)]
854#[inline(always)]
855pub fn autotest_clear_capa(capability: u32) -> Status {
856    syscall!(Syscall::AutotestClearCapa, capability).into()
857}
858
859#[cfg(test)]
860mod tests {
861    use super::*;
862
863    #[test]
864    fn syscall_enum() {
865        for syscall_num in 0..32 {
866            match Syscall::try_from(syscall_num) {
867                Ok(sysn) => {
868                    assert_eq!(sysn as u8, syscall_num); // ensure roundtrip loops
869                }
870                Err(_) => (),
871            }
872        }
873    }
874
875    #[test]
876    fn basic_sleep() {
877        assert_eq!(sleep(SleepDuration::D1ms, SleepMode::Shallow), Status::Ok);
878    }
879
880    #[test]
881    fn basic_start() {
882        assert_eq!(start(0), Status::Ok);
883    }
884
885    #[test]
886    fn basic_yield() {
887        assert_eq!(sched_yield(), Status::Ok);
888    }
889
890    #[test]
891    fn basic_exit() {
892        assert_eq!(exit(1), Status::Ok);
893    }
894
895    #[test]
896    fn invalid_status() {
897        assert_eq!(exit(0xaaaa), Status::Ok);
898    }
899}