---
title: "Syscalls"
canonical_url: https://developer.repebble.com/pebbleos/docs/kernel/syscalls/
last_updated: 2026-10-21
sdk_version: 4.33.1
---
# Syscalls

Source: https://developer.repebble.com/pebbleos/docs/kernel/syscalls/

Threads can run unprivileged, confined by the MPU, and enter privileged code only through syscalls. `include/pbl/kernel/syscall.h` is the interface; `CONFIG_USERSPACE` turns it on, on architectures that select `ARCH_HAS_USERSPACE` (Cortex-M).

## Defining a syscall

`PBL_SYSCALL()` defines the stub and its body in one go. Each argument is declared with a kind that says how the stub checks it before the body runs: passed through, copied in, copied in and back out, or a buffer, array or string that must lie in the caller’s memory. A caller that passes a bad argument is killed. The body gets `bool from_user`, true when the call came from unprivileged code, for checks no kind covers. [Exporting a function](https://developer.repebble.com/pebbleos/docs/sdk/exporting.md) lists the kinds.

Bodies that check by hand use `pbl_user_access_ok()`, `pbl_syscall_verify_user()`, `pbl_user_copy_from()`, `pbl_user_copy_to()`, `pbl_user_strnlen()` and `pbl_syscall_fail()`.

## User threads

A thread’s syscalls may only hand over memory between `pbl_thread_attr.user_start` and `user_end`, and only above the stack pointer the syscall was made from: below it lies the unused part of the caller’s stack and its guard. A thread created with a `syscall_stack` runs syscall bodies there, so a thread that exhausts its own stack faults unprivileged rather than in privileged code.

A thread created privileged drops to unprivileged mode for good with `pbl_user_mode_enter()`. `pbl_user_call()` runs a callback unprivileged from privileged code, for code that calls into untrusted callbacks. `pbl_is_user_context()` tells whether the CPU runs unprivileged code.

## Hooks

What happens to a thread is left to the application:

- `pbl_kernel_syscall_exit()` runs on the way out of every syscall made from unprivileged code, and may hold the thread there.

- `pbl_kernel_syscall_fault()` kills the caller of a failed syscall.

## Without `CONFIG_USERSPACE`

Every thread runs privileged, syscalls are plain calls with `from_user` always false, `pbl_is_user_context()` is always false and the user-memory helpers accept everything. Nothing then isolates one thread from another or from the kernel.

## Cortex-M

`kernel/arch/arm/syscall.c` implements the stub, the privilege drop and `pbl_user_call()`; [Kernel internals](https://developer.repebble.com/pebbleos/docs/kernel/internals.md) describes the supervisor call.

Generated from PebbleOS [3a7b71785](https://github.com/coredevices/pebbleos/commit/3a7b71785fd439666c4a8bc116f561140e8dec01). This page is maintained in the pebbleos repository: [docs/kernel/syscalls.md](https://github.com/coredevices/pebbleos/blob/3a7b71785fd439666c4a8bc116f561140e8dec01/docs/kernel/syscalls.md).
