sentry_uapi/
syscall.rs

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