Syscalls

About basics

Sentry UAPI is implemented in Rust. In order to ensure compatibility with both Rust and C worlds, the UAPI specification is made using extern C external interface specification and types, and the crate defintion is automatically exported to C headers using cbindgen.

This allows a single root of trust for all UAPI definition, for both languages, in our case using a Rust definition that ensure struct typing usage.

The UAPI is design using the following:

  • a systypes package, that define all Sentry types shared with userspace

  • a uapi package, that hold the UAPI implementation in rust, depending on systypes

In the kernel counter part:

  • a sysgate package, that define all the kernel syscall implementation, and also depend on systypes

The UAPI description is made for C usage of the UAPI. For Rust definition, please refer to the Rust generated documentation.

Syscall definition

sys_alarm

API definition

C UAPI for alarm syscall
enum Status __sys_alarm(uint32_t timeout_ms);

Usage

Ask the kernel to emit a SIGNAL_ALARM signal in timeout_ms miliseconds to the current job.

This syscall is usefull when implementing userspace software-based timers. Although, there are some restrictions:

  • requested alarms can’t be removed before they expire

  • the system support a maximum number of concurrently configured alarms upto one alarm per task if all task requires an alarm in the same time

sample bare usage of sys_alarm
1if (__sys_alarm(200) != STATUS_OKAY) {
2   // [...]
3}
4res = __sys_wait_event([...]);
5/* alarm received 200ms later */

Note

as SIGNAL_ALARM can also be emitted by other tasks through the signal syscall, the alarm emitted by the kernel due to a alarm() request hold the current task identifier as signal source

Required capability

None.

Return values

  • STATUS_BUSY if the delay manager do not have any space for the requested alarm

  • STATUS_OK

sys_dma_assign_stream

API definition

C UAPI for dma_assign_stream syscall
enum Status __sys_dma_assign_stream(dmah_t handle);

Usage

A task can manipulate multiple DMA streams, and is allowed to support multiple stream configurations that may use the very same hardware DMA channel.

DMA streams lifecycle is designed so that DMA streams can be easily assigned, started, suspended, resumed, and unassigned during the job lifecycle, depending on its own need.

DMA stream lifecycle must respect the following state automaton:

  1. assign a DMA stream to the corresponding DMA channel

  2. start the previously assigned DMA stream

  3. if needed:

    1. suspend the DMA stream

    2. resume the DMA stream

  4. unassign the DMA stream

A DMA stream can be assigned as soon as the target DMA channel is currently unassigned. The DMA stream assignation consists in configuring the DMA channel with all the DMA streams attributes values. Assigning a stream do not start it.

A started DMA stream may stop by itself when configured as a single copy DMA. In that case, the owning job only needs to wait for the Transfer Complete event. The suspend action is also allowed while the DMA transfer is not terminated. In such a configuration, if the Transfer Complete event is received, the DMA stream is automatically set as assigned (i.e. not started).

Example

sample DMA stream assignation
1if (__sys_dma_assign_stream(stream_handle) != STATUS_OK) {
2   // the channel may be already assigned, unassign first
3}

Required capability

Like all other DMA-related syscalls, this syscall needs the calling task to hold the CAP_DEV_DMA capability.

Return values

  • STATUS_INVALID if the handle do not exist or the target DMA channel is busy (not unassigned)

  • STATUS_DENIED if the stream is not owned or the CAP_DEV_DMA is not hold by the task

  • STATUS_OK if the stream has been assigned

See also

sys_dma_assign_stream (2), sys_dma_unassign_stream (2), sys_dma_start_stream (2), sys_dma_suspend_stream (2), sys_get_dma_stream_handle (2)

sys_dma_resume_stream

API definition

C UAPI for dma_resume_stream syscall
enum Status __sys_dma_resume_stream(dmah_t handle);

Usage

DMA streams lifecycle is designed so that DMA streams can be easily assigned, started, suspended, resumed, and unassigned during the job lifecycle, depending on its own need.

DMA stream lifecycle must respect the following state automaton:

  1. assign a DMA stream to the corresponding DMA channel

  2. start the previously assigned DMA stream

  3. if needed:

    1. suspend the DMA stream

    2. resume the DMA stream

  4. unassign the DMA stream

A DMA stream may stop by itself when configured as a single copy DMA. In that case, the owning job only needs to wait for the Transfer Complete event. The suspend action is also allowed while the DMA transfer is not terminated. In such a configuration, if the Transfer Complete event is received, the DMA stream is automatically set as assigned (i.e. not started).

Once started, a DMA stream can be suspended so that the stream hardware configurations is kept but the DMA engine stops executing the data transfer. Such a usage is useful, for example, for backbuffer/frontbuffer transmission at software update time, in order to keep coherency of the data to be transmitted.

In such a case, restarting a suspended DMA stream is done using the sys_dma_resume_stream() syscall.

This syscall synchronously resume the DMA stream at DMA controller level. The stream can then consecutively be suspended again. The resume action is as simple as asking the DMA controller to continue its action with the DMA stream state values as defined at suspend time.

Example

sample DMA stream resume sequence
 1#include <uapi.h>
 2
 3if (__sys_dma_start_stream(stream_handle) != STATUS_OK) {
 4   // the channel may be already assigned, unassign first
 5}
 6// DMA behave in circular mode
 7
 8if (__sys_dma_suspend_stream(stream_handle) != STATUS_OK) {
 9   // [...]
10}
11// working on DMA source or destination....
12// resume stream no that SW update is done
13if (__sys_dma_resume_stream(stream_handle) != STATUS_OK) {
14   // [...]
15}

Required capability

Like all other DMA-related syscalls, this syscall needs the calling task to hold the CAP_DEV_DMA capability.

Return values

  • STATUS_INVALID if the handle do not exist or the DMA stream is not currently suspended

  • STATUS_DENIED if the stream is not owned or the CAP_DEV_DMA is not hold by the task

  • STATUS_OK if the stream has been suspended. The suspend flag check is checked synchronously

See also

sys_dma_assign_stream (2), sys_dma_unassign_stream (2), sys_dma_start_stream (2), sys_dma_suspend_stream (2), sys_get_dma_stream_handle (2)

sys_dma_start_stream

API definition

C UAPI for dma_start_stream syscall
enum Status __sys_dma_start_stream(dmah_t handle);

Usage

A task can manipulate multiple DMA streams, and is allowed to support multiple stream configurations that may use the very same hardware DMA channel.

DMA streams lifecycle is designed so that DMA streams can be easily assigned, started, suspended, resumed, and unassigned during the job lifecycle, depending on its own need.

DMA stream lifecycle must respect the following state automaton:

  1. assign a DMA stream to the corresponding DMA channel

  2. start the previously assigned DMA stream

  3. if needed:

    1. suspend the DMA stream

    2. resume the DMA stream

  4. unassign the DMA stream

Once assigned, a DMA stream needs to be voluntary started by the owning job. Starting a DMA is a synchronous syscall. The DMA streams start immediately. The calling job can then directly wait for DMA event as soon as the start syscall has returned without error.

A DMA stream may stop by itself when configured as a single copy DMA. In that case, the owning job only needs to wait for the Transfer Complete event. The suspend action is also allowed while the DMA transfer is not terminated. In such a configuration, if the Transfer Complete event is received, the DMA stream is automatically set as assigned (i.e. not started).

Note

There is no such stop_stream action. For a given started DMA stream, stopping a DMA stream is done using the sys_dma_suspend_stream()+sys_dma_unassign_stream() couple

Example

sample DMA stream start sequence
 1#include <uapi.h>
 2
 3if (__sys_dma_start_stream(stream_handle) != STATUS_OK) {
 4   // the channel may be already assigned, unassign first
 5}
 6if (__sys_wait_for_event(EVENT_TYPE_DMA, WFE_WAIT_FOREVER) != STATUS_OK) {
 7   // error while waiting for DMA event
 8}
 9// handle DMA event corresponding to previously started DMA stream
10// [...]

Required capability

Like all other DMA-related syscalls, this syscall needs the calling task to hold the CAP_DEV_DMA capability.

Return values

  • STATUS_INVALID if the handle do not exist or the DMA stream is not currently assigned

  • STATUS_DENIED if the stream is not owned or the CAP_DEV_DMA is not hold by the task

  • STATUS_OK if the stream has been started

See also

sys_dma_assign_stream (2), sys_dma_unassign_stream (2), sys_dma_start_stream (2), sys_dma_suspend_stream (2), sys_get_dma_stream_handle (2)

sys_dma_suspend_stream

API definition

C UAPI for dma_suspend_stream syscall
enum Status __sys_dma_suspend_stream(dmah_t handle);

Usage

A task can manipulate multiple DMA streams, and is allowed to support multiple stream configurations that may use the very same hardware DMA channel.

DMA streams lifecycle is designed so that DMA streams can be easily assigned, started, suspended, resumed, and unassigned during the job lifecycle, depending on its own need.

DMA stream lifecycle must respect the following state automaton:

  1. assign a DMA stream to the corresponding DMA channel

  2. start the previously assigned DMA stream

  3. if needed:

    1. suspend the DMA stream

    2. resume the DMA stream

  4. unassign the DMA stream

A DMA stream may stop by itself when configured as a single copy DMA. In that case, the owning job only needs to wait for the Transfer Complete event. The suspend action is also allowed while the DMA transfer is not terminated. In such a configuration, if the Transfer Complete event is received, the DMA stream is automatically set as assigned (i.e. not started).

Once started, a DMA stream can be suspended so that the stream hardware configurations is kept but the DMA engine stops executing the data transfer. Such a usage is useful, for example, for backbuffer/frontbuffer transmission at software update time, in order to keep coherency of the data to be transmitted.

A suspended DMA stream can be:

  • resumed: the DMA stream starts again from its previously suspended state

  • unassigned: the DMA stream is stopped and cleared, and can’t be used again while not re-assigned

At return time, the DMA stream is properly suspended, meaning that the syscall waits for the DMA engine to confirm the suspend state before returning.

Example

sample DMA stream suspend sequence
 1#include <uapi.h>
 2
 3if (__sys_dma_start_stream(stream_handle) != STATUS_OK) {
 4   // the channel may be already assigned, unassign first
 5}
 6// DMA behave in circular mode
 7
 8if (__sys_dma_suspend_stream(stream_handle) != STATUS_OK) {
 9   // [...]
10}
11// working on DMA source or destination....
12// resume stream no that SW update is done
13if (__sys_dma_resume_stream(stream_handle) != STATUS_OK) {
14   // [...]
15}

Required capability

Like all other DMA-related syscalls, this syscall needs the calling task to hold the CAP_DEV_DMA capability.

Return values

  • STATUS_INVALID if the handle do not exist or the DMA stream is not currently started

  • STATUS_DENIED if the stream is not owned or the CAP_DEV_DMA is not hold by the task

  • STATUS_OK if the stream has been suspended. The suspend flag check is checked synchronously

See also

sys_dma_assign_stream (2), sys_dma_unassign_stream (2), sys_dma_start_stream (2), sys_dma_suspend_stream (2), sys_get_dma_stream_handle (2)

sys_dma_unassign_stream

API definition

C UAPI for dma_unassign_stream syscall
enum Status __sys_dma_unassign_stream(dmah_t handle);

Usage

A task can manipulate multiple DMA streams, and is allowed to support multiple stream configurations that may use the very same hardware DMA channel.

DMA streams lifecycle is designed so that DMA streams can be easily assigned, started, suspended, resumed, and unassigned during the job lifecycle, depending on its own need.

DMA stream lifecycle must respect the following state automaton:

  1. assign a DMA stream to the corresponding DMA channel

  2. start the previously assigned DMA stream

  3. if needed:

    1. suspend the DMA stream

    2. resume the DMA stream

  4. unassign the DMA stream

A DMA stream may stop by itself when configured as a single copy DMA. In that case, the owning job only needs to wait for the Transfer Complete event. The suspend action is also allowed while the DMA transfer is not terminated. In such a configuration, if the Transfer Complete event is received, the DMA stream is automatically set as assigned (i.e. not started).

A DMA stream can be assigned as soon as the target DMA channel is currently unassigned. A DMA stream can be unassigned as soon as the target DMA channel is suspended or assigned. A started (or resumed) DMA stream can’t be unassigned.

Note

When a single chunk DMA stream terminates (TC event received), the kernel consider the stream as assigned, and can be directly unassigned

Example

sample DMA stream unassignation
 1if (__sys_dma_start_stream(stream_handle) != STATUS_OK) {
 2   // the channel may be already assigned, unassign first
 3}
 4if (__sys_wait_for_event(EVENT_TYPE_DMA, WFE_WAIT_FOREVER) != STATUS_OK) {
 5   // error while waiting for DMA event
 6}
 7// handle DMA event corresponding to previously started DMA stream
 8
 9// unasign stream if Transfer Complete event received
10if (__sys_dma_unassigned_stream(stream_handle) != STATUS_OK) {
11   // the channel may be already assigned, unassign first
12}

Required capability

Like all other DMA-related syscalls, this syscall needs the calling task to hold the CAP_DEV_DMA capability.

Return values

  • STATUS_INVALID if the handle do not exist or the DMA stream is not currently assigned

  • STATUS_DENIED if the stream is not owned or the CAP_DEV_DMA is not hold by the task

  • STATUS_OK if the stream has been assigned

See also

sys_dma_assign_stream (2), sys_dma_unassign_stream (2), sys_dma_start_stream (2), sys_dma_suspend_stream (2), sys_get_dma_stream_handle (2)

sys_exit

API definition

C UAPI for exit syscall
enum Status __sys_exit(uint32_t status);

Usage

Terminate the current job with status code given in argument. Any non-zero status code is considered as an abnormal status code and is passed to job termination mechanism.

Required capability

None.

Return values

End the current job, never returns.

The exit status is used by the exitpoint symbol as only argument. This symbol is typically the _exit symbol of the libc that can execute post-execution triggers.

If the libc supports it, the application developer can define such a trigger so that it can be called by the _exit function in a post exit context in order to properly clean the task data. See the libShield documentation for more information.

See job termination chapter for more informations.

Note

The goal here is to support runtime-based termination call with potential application developper hooks, so that a unified central handler can react to various status code values, wherever the exit() call is made in the job implementation

sys_get_device_handle

API definition

C UAPI for get_task_handle syscall
enum Status __sys_get_device_handle(uint32_t dev_label);

Usage

In Sentry, all devices are uniquely identified by ther handle. At bootup, the userspace task only hold the device label, which is generated using the dts file. This label is unique to the system and may vary depending on the project device tree file evolution.

In order to manipulate devices (for e.. to map or unmap it), a task must require the device handle from the kernel, using the device label as unique identifier.

As a consequence, before manipulating a device, a device driver (or the corresponding task holding it) must first get back the device handle. While got, the handle is valid for all the current OS lifecycle.

Note

the device identifier is stored in the devinfo_t const structure generated using the dts file

sample bare usage of sys_get_device_handle
1uint32_t my_dev_label = devinfo[MYDEVICE].id;
2devh_t my_dev_handle;
3if (__sys_get_device_handle(my_dev_label) != STATUS_OK) {
4   // [...]
5}
6copy_from_kernel(&my_dev_handle, sizeof(devh_t));

Required capability

None in this syscall while the device is owned by the current task.

Warning

if the application do not hold the corresponding device capability, other device-related syscalls, such as sys_map_dev() will failed with STATUS_DENIED.

Return values

  • STATUS_INVALID if the label do not exist or is owned by another task

  • STATUS_OK

sys_get_shm_handle

API definition

C UAPI for get_shm_handle syscall
enum Status __sys_get_shm_handle(uint32_t label);

Usage

In Sentry, as explained in SHM model definition chapter, a shared memory is unikely identified by its label, in the same way as tasks. the shared memory handle is forged at boot time so that its value can’t be predefined at compile time. As a consequence, shared memory owner needs to ask for the SHM handle that correspond to a known labbel.

When the shared memory is shared with another task by the SHM owner, both tasks (owner and user) can ask for the handle that correspond to the label. This allows two sharing model:

  • the owner task voluntary share the label with the user task (using IPC for example)

  • owner and user task share the label since compile time, using config-based or hard-coded label

This syscall returns the handle corresponding to the label declared in the device-tree. this handle until the next reboot.

sample bare usage of sys_get_shm_handle
1uint32_t my_peer_label=0xbabe;
2taskh_t my_peer_handle;
3if (__sys_get_shm_handle(my_shm_label) != STATUS_OK) {
4   // [...]
5}
6copy_from_kernel(&my_peer_handle, sizeof(shmh_t));

Required capability

None.

Return values

  • STATUS_INVALID if the SHM do not exist or is not owned or used by the calling task

  • STATUS_OK

sys_get_dma_handle

API definition

C UAPI for get_dma_stream_handle syscall
enum Status __sys_get_dma_stream_handle(uint32_t label);

Usage

In Sentry, all DMA streams are uniquely identified by ther handle. At bootup, the user-space task only holds the DMA stream label, as defined in the sentry,label attribute of the DMA stream in the project dts file. This label is unique to the system.

DMA streams can only be manipulated through their handle, which must be requested by the owner task. The DMA handle values are forged at bootup and valid until next reboot.

Note

the DMA stream value is stored in the device tree file. This value is a 8 bits unsigned identifier. There must not have collision between DMA identifiers, but other objects identifiers are considered separately

sample bare usage of sys_get_dma_handle
1uint32_t my_stream_label = 0x42;
2dmah_t my_stream_handle;
3if (__sys_get_dma_stream_handle(my_dma_label) != STATUS_OK) {
4   // [...]
5}
6copy_from_kernel(&my_dma_handle, sizeof(devh_t));

Required capability

Like all other DMA-related syscalls, this syscall needs the calling task to hold the CAP_DEV_DMA capability.

Return values

  • STATUS_INVALID if the handle do not exist or can’t be assigned

  • STATUS_DENIED if the stream is not owned or the CAP_DEV_DMA is not hold by the task

  • STATUS_OK if the handle is properly retuned to the calling job

See also

sys_dma_assign_stream (2), sys_dma_unassign_stream (2), sys_dma_start_stream (2), sys_dma_suspend_stream (2), sys_get_dma_stream_handle (2)

sys_get_random

API definition

C UAPI for get_random syscall
enum Status __sys_get_random(void);

Usage

Receive a 32bit length random value from the kernel RNG source in the svc_exchange area.

In devices that support hardware-based random source, the random source is using the hardware source and respects the FIPS requirements on random generators. This value can be used as a random seed for userspace-based cryptographic implemention.

sample bare usage of sys_log
1uint32_t seed = 0;
2if (__sys_get_random() != STATUS_OK) {
3   // [...]
4}
5memcpy(&seed, _s_svc_exchange, sizeof(uint32_t));

Note

the libShield libc can typically delivers PRNG rand() API seeded by this syscall. other cryptographic libraries such as libecc can also use this syscall as random source

Required capability

CAP_CRY_KRNG

Return values

  • STATUS_DENIED if the task do not own the capability

  • STATUS_INVALID if the random source failed to delivers a FIPS-compliant random value

  • STATUS_OK

sys_get_task_handle

API definition

C UAPI for get_task_handle syscall
enum Status __sys_get_task_handle(uint32_t label);

Usage

In Sentry, as explained in Task terminology chapter, a task is unikely identified by its label, but can spawn sucessive jobs. Each of these jobs is being a dedicated instance of the same task, but at different moments of the system lifecycle.

In order to communicate with another task without any confusion and in order to be sure that the starting point of the communication, end to the finishing point of the communication stays with the very same remote job instance, communication API is using a per-job unique identifier, based on the task label, but with complementary fields.

As a consequence, before communicating with a remote task, knowing the remote task label, must ask the kernel for the currently remote job instance identifier of that task. This identifier is a task handle, and will be used for all communication.

If the remote job terminates (whatever the reason is), the task handle will automatically be invalid for next communication requests, even if a new job has been respawned for the very same task. This is an easy way to detect remote failure or termination.

This syscall returns the currently uptodate valid handle associated with the task uniquely identified by label on the system, and can be called multiple time if needed.

sample bare usage of sys_get_handle
1uint32_t my_peer_label=0xbabe;
2taskh_t my_peer_handle;
3if (__sys_get_handle(my_peer_label) != STATUS_OK) {
4   // [...]
5}
6copy_from_kernel(&my_peer_handle, sizeof(taskh_t));
7__sys_send_signal(my_peer_handle, SIGNAL_POLL);

Required capability

None.

Return values

  • STATUS_INVALID if the target task do not exist in the current task domain

  • STATUS_OK

sys_gpio_get

API definition

C UAPI for gpio_get syscall
enum Status __sys_gpio_get(devh_t device, uint8_t io_identifier);

Usage

Getting a given I/O of a given device.

Any I/O (including standalone I/Os such as buttons, leds, external interrupt lines…) are always declared as a device in the device tree, which always generate a dedicated device handle to which the I/O is associated. When the device is a SoC-device that requires I/O configuration, the very same mechanisms is used, through the standard definition and usage of pinctrl attribute.

1button0: button_0 {
2         compatible = "gpio-button";
3 sentry,owner = <0xbabe>;
4 pinctrl-0 = <&button_pa4>;
5 status = "okay";
6 };

If the I/O exists in the given device and if the device is owned by the application, this function get back the current GPIO value into the svc_exchange area. The GPIO value is written into the fist byte of the svc_echange.

getting I/O 0 from button
1if (__sys_gpio_get(myhandle, 0) != STATUS_OK) {
2   // [...]
3}
4copy_from_kernel(uint8_t *button_value, sizeof(uint8_t));

Required capability

CAPA_DEV_IO is required for autonomous GPIO-based devices. For other devices, each device hold its own capability. devices that hold pinmux are motly buses, that require the CAPA_DEV_BUSES.

Return values

  • STATUS_INVALID if the pin definition do not exist

  • STATUS_OK

sys_gpio_reset

API definition

C UAPI for gpio_reset syscall
enum Status __sys_gpio_reset(devh_t device, uint8_t io_identifier);

Usage

Resetting the value of a given I/O of a given device.

Any I/O (including standalone I/Os such as buttons, leds, external interrupt lines…) are always declared as a device in the device tree, which always generate a dedicated device handle to which the I/O is associated. When the device is a SoC-device that requires I/O configuration, the very same mechanisms is used, through the standard definition and usage of pinctrl attribute.

1led0: led_0 {
2         compatible = "gpio-leds";
3 sentry,owner = <0xbabe>;
4 pinctrl-0 = <&led_pc7>;
5 status = "okay";
6 };

If the I/O exists in the given device and if the device is owned by the application, this function reset the current GPIO value into the svc_exchange area if the I/O is in output mode.

getting I/O 0 from button
1if (__sys_gpio_reset(myhandle, 0) != STATUS_OK) {
2   // [...]
3}

Required capability

CAPA_DEV_IO is required for autonomous GPIO-based devices. For other devices, each device hold its own capability. devices that hold pinmux are motly buses, that require the CAPA_DEV_BUSES.

Return values

  • STATUS_INVALID if the pin definition do not exist

  • STATUS_OK

sys_gpio_set

API definition

C UAPI for gpio_set syscall
enum Status __sys_gpio_set(devh_t device, uint8_t io_identifier, bool value);

Usage

Setting a given I/O of a given device.

Any I/O (including standalone I/Os such as buttons, leds, external interrupt lines…) are always declared as a device in the device tree, which always generate a dedicated device handle to which the I/O is associated. When the device is a SoC-device that requires I/O configuration, the very same mechanisms is used, through the standard definition and usage of pinctrl attribute.

1led0: led_0 {
2         compatible = "gpio-leds";
3 sentry,owner = <0xbabe>;
4 pinctrl-0 = <&led_pc7>;
5 status = "okay";
6 };

If the I/O exists in the given device and if the device is owned by the application, this function set the GPIO value to the value given, while the GPIO is configured in output mode.

setting I/O 0 (fist element of the pinctrl)
1if (__sys_gpio_set(myhandle, 0, 1) != STATUS_OK) {
2   // [...]
3}

Required capability

CAPA_DEV_IO is required for autonomous GPIO-based devices. For other devices, each device hold its own capability. devices that hold pinmux are motly buses, that require the CAPA_DEV_BUSES.

Return values

  • STATUS_INVALID if the pin definition do not exist or is in input mode

  • STATUS_OK

sys_gpio_toggle

API definition

C UAPI for gpio_set syscall
enum Status __sys_gpio_toggle(devh_t device, uint8_t io_identifier);

Usage

Toggling a given I/O of a given device.

Any I/O (including standalone I/Os such as buttons, leds, external interrupt lines…) are always declared as a device in the device tree, which always generate a dedicated device handle to which the I/O is associated. When the device is a SoC-device that requires I/O configuration, the very same mechanisms is used, through the standard definition and usage of pinctrl attribute.

1led0: led_0 {
2         compatible = "gpio-leds";
3 sentry,owner = <0xbabe>;
4 pinctrl-0 = <&led_pc7>;
5 status = "okay";
6 };

If the I/O exists in the given device and if the device is owned by the application, this function set the GPIO value to the value given, while the GPIO is configured in output mode.

toggling I/O 0 (fist element of the pinctrl)
1if (__sys_gpio_toggle(myhandle, 0) != STATUS_OK) {
2   // [...]
3}

Required capability

CAPA_DEV_IO is required for autonomous GPIO-based devices. For other devices, each device hold its own capability. devices that hold pinmux are motly buses, that require the CAPA_DEV_BUSES.

Return values

  • STATUS_INVALID if the pin definition do not exist or is in input mode

  • STATUS_OK

sys_irq_acknowledge

API definition

C UAPI for irq_acknowledge syscall
enum Status __sys_irq_acknowledge(uint16_t IRQn);

Usage

Acknowledge (clear pending) a given IRQ line.

This syscall is made in order to allow userspace driver to acknowledge a given IRQ when the IRQ handler is executed.

This requires the interrupt line to be owned by the given task.

acknowledge given IRQ of an owned device
1int my_handler(uint16_t IRQn) {
2   // executing the handler
3   // [...]
4   if (__sys_irq_acknowledge(myIRQn) != STATUS_OK) {
5      // [...]
6   }
7   // [...]
8}

Required capability

at least one CAP_DEV_xxx capa is required, as the IRQ acknowledgement is linked to a given device.

Return values

  • STATUS_INVALID if the IRQ is not owned or do not exists

  • STATUS_DENIED if the task do not hold any DEV capability

  • STATUS_OK

sys_irq_enable

API definition

C UAPI for irq_enable syscall
enum Status __sys_irq_enable(uint16_t IRQn);

Usage

Enable (unmask) given IRQ line at IRQ controller level.

Note

the controller assignation is not yet supported with this API, and would require a modification to support this

This syscall is made in order to allow userspace driver to enable a given IRQ once the device is properly configured.

This requires the interrupt line to be owned by a device of the given task.

The kernel do not modify the IRQ enable flag of any devices here, but only unmasks the IRQ line in the main interrupt controller. This means that an interrupt may rise as soon as the kernel unmask the interrupt, pushing an interrupt event in the task’s input queue just after the syscall execution. The userspace driver is responsible for (un)masking the interrupt-enable flag at device level.

enable given IRQ of an owned device
1// configure the device (and device-relative IRQ config)
2// [...]
3if (__sys_irq_enable(myIRQn) != STATUS_OK) {
4      // [...]
5}

Required capability

at least one CAP_DEV_xxx capa is required, as the IRQ enabling is linked to a given device.

Return values

  • STATUS_INVALID if the IRQ is not owned or do not exists

  • STATUS_DENIED if the task do not hold any DEV capability

  • STATUS_OK

sys_irq_disable

API definition

C UAPI for irq_disable syscall
enum Status __sys_irq_disable(uint16_t IRQn);

Usage

Disable (mask) given IRQ line at IRQ main controller level.

Note

the controller assignation is not yet supported with this API, and would require a modification to support this

This syscall is made in order to allow userspace driver to disable a given IRQ. This is useful when the IRQ was previously enabled, and need, for the driver own specific requirements, to be disabled. This syscall do not check that the IRQ was previously enabled.

This requires the interrupt line to be owned by a device of the given task.

The kernel do not modify the IRQ enable flag of any devices here, but only masks the IRQ line from the main interrupt controller. The userspace driver is responsible for manipulating device-specific configuration.

disable given IRQ of an owned device
1if (__sys_irq_disable(myIRQn) != STATUS_OK) {
2      // [...]
3}

Required capability

at least one CAP_DEV_xxx capa is required, as the IRQ disabling is linked to a given device.

Return values

  • STATUS_INVALID if the IRQ is not owned or do not exists

  • STATUS_DENIED if the task do not hold any DEV capability

  • STATUS_OK

sys_log

API definition

C UAPI for log syscall
enum Status __sys_log(size_t length);

Usage

Emit the given logs data length from the svc_exchange area toward the log output.

In release mode, the UAPI will just do nothing, avoiding any ifdef usage at application level. The kernel binary do not host any debug functionality and will simply ignore such requests

sample bare usage of sys_log
1enum Status res = STATUS_INVALID;
2if (len <=> CONFIG_SVC_EXCHANGE_AREA_LEN) {
3   memcpy(&_s_svc_exchange, data, len);
4   res = __sys_log(len);
5}

Note

the libShield libc typically delivers a printf() implementation, while the UAPI delivers the rust println! macro to simplify the usage of the sys_log() function

Warning

the log syscall do not execute any parsing of the logged data. This allows binary transmission to the debug output if needed and requires upper layers to implement the format string parser. The syscall do not need any trailing zero (c string format)

Required capability

None.

Return values

  • STATUS_INVALID if length is bigger than CONFIG_SVC_EXCHANGE_AREA_LEN

  • STATUS_OK

sys_map_dev

API definition

C UAPI for device mapping syscall
enum Status __sys_map_dev(devh_t dev);
enum Status __sys_unmap_dev(devh_t dev);

Usage

Map a given device into the task context. If the device has never been mapped before:

  • configure the device input clock(s).

  • enable interrupts line associated to the device if set in the device node

Once returning from this syscall, the device is mapped at its corresponding address (io-mapped address) as declared in its device tree node.

The devh_t device handle value is a property of the device driver library that has been forged at build time and must be used as an opaque field.

sample bare usage of sys_map
1enum Status res = STATUS_INVALID;
2devh_t mydriver_handle = mydriver_get_handle();
3res = sys_map_dev(mydriver_handle);
4// manipulating device registers.....
5res = __sys_unmap_dev(mydriver_handle);

Note

the libShield libc typically delivers a mmap() implementation with, for example, the following usage type:

addr = mmap(NULL, 0, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, devh, 0);

Required capability

The required capability depend on the concerned device, with the following capability mapping:

  • CAP_DEV_BUSES: All buses such as USART, SPI, CAN, I2C…

  • CAP_DEV_IO: bare general purpose I/O manipulation (e.g. LED or button)

  • CAP_DEV_DMA: DMA streams, and DMA masters such as GPU, DMA2D

  • CAP_DEV_ANALOG: analogic devices, such as DAC and ADC

  • CAP_DEV_TIMER: hardware timers

  • CAP_DEV_STORAGE: storage devices (MMIO, etc.)

  • CAP_DEV_CRYPTO: cryptographic accelerator (HMAC, CRYP, etc.)

  • CAP_DEV_CLOCK: real-time clock, GPS, when in-SoC

  • CAP_DEV_POWER: power-related devices

  • CAP_DEV_NEURAL: neural accelerators

Return values

  • STATUS_INVALID if length is bigger than CONFIG_SVC_EXCHANGE_AREA_LEN

  • STATUS_PERM if device or device capability capability is not owned

  • STATUS_DENIED if device or device capability capability is not owned

  • STATUS_ALREADY_MAPPED if device is already mapped

  • STATUS_OK if device was successfully mapped

sys_map_shm

API definition

C UAPI for device mapping syscall
enum Status __sys_map_shm(shmh_t shm);
enum Status __sys_unmap_shm(shmh_t shm);

Usage

Map a given shared memory into the task context.

Once returning from this syscall, the shared-memory is mapped at its corresponding address as declared in its device tree node.

sample bare usage of sys_map_shm
1enum Status res = STATUS_INVALID;
2shmh_t shm;
3uint32_t shm_label = 0xf00UL;
4
5if (__sys_get_shmhandle(shm_label) != STATUS_OK) {
6   // [...]
7}
8res = __sys_map_shm(shm_label);

Note

the SHM credential must be set through sys_shm_set_credential() in order to be allowed to map the SHM.

Unmapping a shared memory is made using sys_shm_unmap(), in the very same way SHM is mapped. Unmapping the shared memory free the associated memory ressource of the task immediately.

Required capability

None.

Return values

For sys_shm_map():

  • STATUS_INVALID if the SHM handle do not exist or is not associated at all to the calling task

  • STATUS_DENIED if the SHM credential do not allow mapping

  • STATUS_ALREADY_MAPPED if SHM is already mapped

  • STATUS_OK

For sys_shm_unmap():

  • STATUS_INVALID if the SHM handle do not exist or is not mapped

  • STATUS_OK

sys_unmap_shm

API definition

C UAPI for device mapping syscall
enum Status __sys_map_shm(shmh_t shm);
enum Status __sys_unmap_shm(shmh_t shm);

Usage

Map a given shared memory into the task context.

Once returning from this syscall, the shared-memory is mapped at its corresponding address as declared in its device tree node.

sample bare usage of sys_map_shm
1enum Status res = STATUS_INVALID;
2shmh_t shm;
3uint32_t shm_label = 0xf00UL;
4
5if (__sys_get_shmhandle(shm_label) != STATUS_OK) {
6   // [...]
7}
8res = __sys_map_shm(shm_label);

Note

the SHM credential must be set through sys_shm_set_credential() in order to be allowed to map the SHM.

Unmapping a shared memory is made using sys_shm_unmap(), in the very same way SHM is mapped. Unmapping the shared memory free the associated memory ressource of the task immediately.

Required capability

None.

Return values

For __sys_shm_map():

  • STATUS_INVALID if the SHM handle do not exist or is not associated at all to the calling task

  • STATUS_DENIED if the SHM credential do not allow mapping

  • STATUS_ALREADY_MAPPED if SHM is already mapped

  • STATUS_OK

For __sys_shm_unmap():

  • STATUS_INVALID if the SHM handle do not exist or is not mapped

  • STATUS_OK

sys_pm_clock_set

API definition

C UAPI for pm_clock_set syscall
enum Status __sys_pm_clock_set(uint32_t reg_offset, uint32_t reg_value);

Usage

Set a given RCC clock register to a given value. This syscall is required only in specific devices usage such as MIPI-DSI bridges, that require successive consecutive input PLL configuration.

Required capability

CAPA_SYS_POWER is required as this impact the overall system power configuration.

Return Values

  • STATUS_DENIED if the calling task do not hold the CAPA_SYS_POWER capability

  • STATUS_INVALID if the given register offset is not supported for reconfiguration

  • STATUS_OK if the power configuration update is made properly

sys_send_ipc

API definition

C UAPI for send_ipc syscall
enum Status __sys_send_ipc(taskh_t target, uint32_t len);

Usage

Sending an Inter-Process Communication message toward the target job identified by the handle taskh_t.

IPC are peace of data that are emitted between task’s jobs. The data content type is not considered at kernel level, allowing jobs to emit any type of content, while short enough to be transmitted between svc echange zones.

Emitting an IPC is always a blocking event. The job is preempted and is awakened only when:

  • the IPC target job read the IPC content

  • the IPC target job exists without reading the IPC (whatever the cause)

IPC kernel implementation is a one-copy mechanism implementation. The effective IPC data copy is not done at send time but instead at receive time, by the target (see sys_recv_event for more information).

Sentry IPC support direct and indirect deadlock detection, and thus allows to avoid any potential cycles that may generate user-space communication automaton locks. This is done by checking IPC cycles between tasks each time an IPC send syscall is executed.

IPC do not require specific capability, but use task handles as target, requiring each task to know the target task label identifier before communicating.

Warning

IPC between tasks of different domains is forbidden

The way IPC are executed use the following pseudocode (Ada-based pseudo-code):

procedure send_ipc
    (length: in u32; target: in taskh_t)
is
begin
    -- check IPC syscall inputs
    if length not in 1 .. MAX_IPC_LEN then
        current.syscall_return_value := ERROR_INVAL;
        return;
    end if;
    if job_do_not_exist(target) = TRUE then
        current.syscall_return_value := ERROR_NOENT;
        return;
    end if;

    -- check for direct or indirect deadlocks
    if ensure_no_deadlock(target, current) = FALSE then
        current.syscall_return_value := ERROR_DEADLOCK;
        return;
    end if;

    -- flag target IPC input that current task has emit an IPC. Non-zero is a trigger
    target.ipcs(current).length := len;

    -- awake target, if possible
    if awakable_for_ipc(target) then
        awake(target);
    end if;

    -- set current task job as unschedulable
    current.state := STATE_WAITFORIPC;

    -- elect a new job
    sched_elect;
    -- preemption here, until asynchronous event rise:
    -- - target read the IPC (valid awakening, no error)
    -- - target exit before IPC is read (invalid awakening: ERROR_BROKENPIPE)
end send_ipc;

Note

If reaching the elect line, the syscall return value is asynchronously updated at handler level, before moving back to userspace, using the current.syscall_return_value

Note

IPCs are considered as a slow path. For high performance exchanges, use signals or shared memories

Return values

STATUS_OK: IPC has been emitted and received (read) by peer. STATUS_INVALID: The IPC arguments are not valid. STATUS_DEADLK: emitting this IPC would generate an inter-task deadlock. Please check your own input IPC before emitting one.

sys_send_signal

API definition

C UAPI for send_signal syscall
enum Status __sys_send_signal(taskh_t target, enum Signal signal);

Usage

Emit a signal to the target identified by the target opaque, as received by the sys_get_process_handle() syscall.

If the target exists and is running, the signal is added to its input signal queue. The syscall is a non-blocking, synchronous syscall and do not generate any scheduling impact. The signal management is an asynchronous communication mechanism, meaning that the syscall returns before that the target do actually receive the signal.

Warning

Only one signal at a time is supported by a peer for a given source. If a source send a new signal to a peer that did not already received the previous one, the send_signal syscall will return a STATUS_BUSY flag

The Sentry supported list of signals are defined in UAPI model definition.

sample bare usage of sys_send_signal
1uint32_t seed = 0;
2if (__sys_send_signal(target, SIGNAL_USR1) != STATUS_OK) {
3   // [...]
4}

Note

If a previously working signal request starts to fail with an invalid return, this is typically the consequence of a target respawn or termination

Note

See get_task_handle() UAPI specification to learn about how to forge the target variable value

Required capability

None.

Return values

  • STATUS_BUSY if the target has its input signal queue full

  • STATUS_INVALID if the target do not exist in the current job domain

  • STATUS_OK

sys_shm_set_credential

API definition

C UAPI for shm_set_crendential syscall
enum Status __sys_get_shm_handle(shmh_t shm, taskh_t target, uint32_t perms);

Usage

In Sentry, as explained in SHM model definition chapter, a shared memory hold credential for its owner and user task.

These credentials need to be set at bootup by the owner task. Only the owner task is allowed to set credentials of both itself and any other user task.

Setting credentials is made using the shm_set_credential() syscall, targetting a given task handle.

The task handle can be:

  • the owner itself, meaning that the configured credential is associated with the owner

  • another task handle, meaning that the configured credential is associated with another task

If the target task is declared for the first time, it become the user task with which the SHM is shared.

An owner can’t set credential for a target that currently maps the SHM. This means that credential must be set for a target that currently do not map it. This is required to keep coherency between the currently mapped ressources and the kernel configuration.

If not mapped and the target task hande changes, the previously set job associated with the previous task handle with which the shared memory is shared can’t map the SHM any more.

The following credential flags exist:

  • SHM_PERMISSION_MAP: the shared memory is mappable by the credential target task

  • SHM_PERMISSION_READ: the shared memory is readable when mapped. On MCU devices, it is always true

  • SHM_PERMISSION_WRITE: the shared memory is writeable when mapped

  • SHM_PERMISSION_TRANSFER: the shared memory user can be transferable to another task

These flags are ORed so that multiple flags can be set if needed.

It is to note that the SHM_PERMISSION_MAP is ignored if the sentry,no-map attribute in the device tree is set (see SHM general description of Sentry concept, for more information).

sample bare usage of sys_shm__set_credential
 1uint32_t my_shm_label=0xf00UL;
 2taskh_t myself;
 3if (__sys_get_task_handle(myself_label) != STATUS_OK) {
 4   // [...]
 5}
 6copy_from_kernel(&myself, sizeof(taskh_t));
 7if (__sys_get_shm_handle(my_shm_label) != STATUS_OK) {
 8   // [...]
 9}
10copy_from_kernel(&my_shm_handle, sizeof(shmh_t));
11
12__sys_shm_set_credential(my_shm_handle, myself, SHM_PERMISSION_MAP | SHM_PERMISSION_WRITE);

Required capability

None.

Return values

  • STATUS_INVALID if the SHM do not exist, target do not exist or is not owned or used by the calling task

  • STATUS_DENIED if the calling task is the user, not the owner

  • STATUS_BUSY if the target associated with the credential has the SHM mapped

  • STATUS_OK

sys_shm_get_infos

API definition

C UAPI for shm_get_infos syscall
enum Status __sys_shm_get_infos(shmh_t shm);

Usage

In Sentry, as explained in SHM model definition chapter, a shared memory holds credential for its owner and user task. It also hold some memory-related shm_get_infos such as base address and size.

These information set is useful to get back in user-space without requiring any DTS forge at application level.

These informations can be received through this syscall, by fulfilling the shm_infos_t structure declared by the UAPI in the SVC Exchange area. The shm_infos_t is defined in the UAPI <types.h> header in C or through the uapi.systypes Rust mod.

shm_infos_t structure definition
1/* SHM informations data structure */
2typedef struct shm_infos {
3  shmh_t   handle;  /*< SHM handle */
4  uint32_t label;   /*< SHM label */
5  size_t   base;    /*< SHM base address */
6  size_t   len;     /*< SHM length in bytes */
7  uint32_t perms;   /*< SHM permissions (mask of SHMPermission) */
8} shm_infos_t;

The only input required for this syscall is the SHM handle, as given by the sys_get_shmhandle() syscall.

When a given SHM credentials set for the current job is updated, the sys_shm_get_infos() returns an up-to-date content synchronously.

sample bare usage of sys_shm_get_infos
 1uint32_t my_shm_label=0xf00UL;
 2taskh_t myself;
 3shm_infos_t infos;
 4if (__sys_get_task_handle(myself_label) != STATUS_OK) {
 5   // [...]
 6}
 7copy_from_kernel(&myself, sizeof(taskh_t));
 8if (__sys_get_shm_handle(my_shm_label) != STATUS_OK) {
 9   // [...]
10}
11copy_from_kernel(&my_shm_handle, sizeof(shmh_t));
12
13if (__sys_shm_get_infos(my_shm_handle)) {
14  // [...]
15}
16copy_from_kernel(&infos, sizeof(shm_infos_t));
17printf("SHM base address is %lx\n", infos.base);

Required capability

None.

Return values

  • STATUS_INVALID if the SHM do not exist, target do not exist or is not owned or used by the calling task

  • STATUS_OK

sys_wait_for_event

API definition

C UAPI for wait_for_event syscall
typedef enum EventType {
  EVENT_TYPE_NONE = 0,
  EVENT_TYPE_IPC = 1,
  EVENT_TYPE_SIGNAL = 2,
  EVENT_TYPE_IRQ = 4,
  EVENT_TYPE_DMA = 8,
  EVENT_TYPE_ALL = 0xf,
} EventType;

enum Status __sys_wait_for_event(uint8_t mask, int32_t timeout);

Usage

This syscall is designed in order to be a blocking point of the calling thread. Yet, it also allows non-blocking mode if needed.

The goal of this syscall is to wait for various external events such as:

  • IPC

  • signals

  • IRQ

  • DMA

Returning from this syscall in blocking mode means that an event has been received or the blocking timeout has been reached. In non-blocking mode, this syscall returns immediately, even if no event has been received.

The mask argument is used in order to filter which event type is waited.

Warning

Waiting for no event type at all in blocking mode will always reach the timeout or in full blocking mode will leave the job de-scheduled for ever.

When using multiple call to wait_for_event, the non-blocking mode should be preferred to avoid multiple blocking points in the same thread.

The timeout argument is used to define the temporal behavior.

the timeout argument the following, canonically defined, values:

wait_for_event timeout helpers
#define WFE_WAIT_NO      (-1)
#define WFE_WAIT_FOREVER (0)
#define WFE_WAIT_UPTO(x) (x)
  • if timeout is WFE_WAIT_NO, the syscall synchronously return to the job

  • if timeout is 0, the job is preempted until an event is received

  • if timeout is positive, the job waits up-to timeout milliseconds.

the wait_for_event API only returns a single event type, even if multiple heterogeneous events are set in the job input queue. As a consequence, there is an event types receiving order that has been defined in the kernel implementation. Events type receiving order respects the following:

  1. Signals events

  2. IRQ events

  3. DMA events

  4. IPC events

Return code

  • If an event is received, the event data are written in the SVC_EXCHANGE area and the syscall returns STATUS_OKAY. In blocking mode, if the job was preempted during the syscall execution, the quantum is reset to its declared value.

  • If the declared timeout is reached without receiving any event, the syscall returns STATUS_TIMEOUT. The job has its quantum reset to its configured value at the time of the syscall returns.

  • If the syscall is in non-blocking mode and no event exists in the job input queue at the time of the syscall execution, STATUS_AGAIN is returned. The job can continue its execution up-to reaching its quantum.

  • If any of the argument is invalid, the syscall synchronously returns STATUS_INVALID. The job can continue its execution up-to reaching its quantum.

Returned data

If the syscall returns STATUS_OKAY the kernel always push event in the SVC_EXCHANGE data.

The data returned in the SVC_EXCHANGE area respects the data encoding defined in events definition. The returned event type is always one of the events that have been required in the mask argument.

The magic field is used in order to detect invalid exchange content easily, to prevent invalid data values access from userspace upper layers.

The data field length depend on the received event type. The events type length and content are defined in the About events chapter of Sentry concepts.

Example

Typicall wait_for_event usage
exchange_event_t * event = NULL;
status = __sys_wait_for_event(EVENT_TYPE_IPC | EVENT_TYPE_SIGNAL, WFE_WAIT_NO);
switch (status) {
   case STATUS_OKAY:
      /* an IPC or signal is received */
      event = &_s_svcexchange;
      switch (event->type) {
         case EVENT_TYPE_IPC:
            /* handle IPC */
            break;
         case EVENT_TYPE_SIGNAL:
            /* handle signal */
            break;
         default:
            break;
      }
      break;
   case STATUS_AGAIN:
      break;
   default:
      /* others are errors that should be handled */
      break;
}

Warning

Note that svc_exhchange area content is ephemeral up-to the next syscall. The developer should copy its content to a safe area or manipulate it without any syscall in the between