PebbleOS
Loading...
Searching...
No Matches
Syscalls

Calls from unprivileged threads into privileged code. More...

Macros

#define PBL_SYSCALL_COPY_MAX   128
 Largest argument PBL_SYSCALL() copies onto the syscall stack.
 
#define PBL_SYSCALL(ret, name, ...)   PRV_SC_DEFINE(, return, ret, name, __VA_ARGS__)
 Define a syscall whose arguments are checked from their declared kinds.
 
#define PBL_SYSCALL_NORETURN(name, ...)   PRV_SC_DEFINE([[noreturn]], , void, name, __VA_ARGS__)
 As PBL_SYSCALL(), for a syscall that never returns.
 

Functions

void pbl_syscall_fail (void)
 Kill the thread whose syscall is in progress.
 
void pbl_syscall_verify_user (const void *buf, size_t num_bytes)
 Kill the caller unless a range is its memory.
 
bool pbl_user_access_ok (const void *buf, size_t num_bytes)
 Check that the caller of the syscall in progress may access a range.
 
int pbl_user_copy_from (void *dst, const void *src, size_t num_bytes)
 Copy from the caller's memory.
 
int pbl_user_copy_to (void *dst, const void *src, size_t num_bytes)
 Copy to the caller's memory.
 
int pbl_user_strnlen (const char *str, size_t max)
 Measure a string in the caller's memory.
 
static bool pbl_is_user_context (void)
 Check whether the CPU runs unprivileged code.
 
void pbl_user_mode_enter (void)
 Drop the calling thread to unprivileged mode for good.
 
void pbl_user_call (void(*fn)(void *), void *ctx)
 Call a function unprivileged from privileged thread mode.
 
void pbl_kernel_syscall_exit (void)
 Called on the way out of every syscall made from unprivileged code.
 
void pbl_kernel_syscall_fault (uintptr_t lr)
 Kill the thread whose syscall failed.
 

Detailed Description

Calls from unprivileged threads into privileged code.

A syscall is defined with PBL_SYSCALL(). Its stub raises privilege, checks the arguments against the kinds they are declared with, runs the body and drops privilege again on return. Only stubs linked between __syscall_text_start__ and __syscall_text_end__ may raise privilege. A thread created with a syscall stack runs the bodies there instead of on its own stack.

Pointers from the caller are only accepted within the thread's user memory (see pbl_thread_attr::user_start), above the stack pointer it made the syscall from.

PBL_SYSCALL(int, sys_foo, (VAL, int, a), (OUT, int *, b)) {
*b = a;
return 0;
}
#define PBL_SYSCALL(ret, name,...)
Define a syscall whose arguments are checked from their declared kinds.
Definition syscall.h:62

Macro Definition Documentation

◆ PBL_SYSCALL

#define PBL_SYSCALL (   ret,
  name,
  ... 
)    PRV_SC_DEFINE(, return, ret, name, __VA_ARGS__)

Define a syscall whose arguments are checked from their declared kinds.

Each argument is a tuple (KIND, type, name[, extra]):

  • (VAL, type, name): passed through.
  • (IN, const T *, name): copied in before the call; the body gets a kernel copy.
  • (OUT, T *, name) / (INOUT, T *, name): copied in, then copied back out after the body returns, so bytes it does not write are kept.
  • IN_OPT, OUT_OPT, INOUT_OPT: as above, but may be nullptr.
  • (IN_BUF, const void *, name, len) / (OUT_BUF, void *, name, len): [name, name + len) must be in the caller's memory; not copied.
  • (IN_ARRAY, const T *, name, count) / (OUT_ARRAY, T *, name, count): as IN_BUF/OUT_BUF for count elements; an overflowing size is rejected.
  • (STR, const char *, name): NUL-terminated within the caller's memory; not copied.
  • (STRN, const char *, name, max): as STR, at most max characters long.

An unprivileged caller passing an argument that fails its check is killed. Privileged callers skip all checks. The body follows the macro, like a function body, and also gets bool from_user, true for a checked call from unprivileged code.

Parameters
retReturn type.
nameSyscall name.

◆ PBL_SYSCALL_COPY_MAX

#define PBL_SYSCALL_COPY_MAX   128

Largest argument PBL_SYSCALL() copies onto the syscall stack.

◆ PBL_SYSCALL_NORETURN

#define PBL_SYSCALL_NORETURN (   name,
  ... 
)    PRV_SC_DEFINE([[noreturn]], , void, name, __VA_ARGS__)

As PBL_SYSCALL(), for a syscall that never returns.

Function Documentation

◆ pbl_is_user_context()

static bool pbl_is_user_context ( void  )
inlinestatic

Check whether the CPU runs unprivileged code.

Returns
true in an unprivileged thread, false in a privileged one or an exception handler, and always false without CONFIG_USERSPACE.

◆ pbl_kernel_syscall_exit()

void pbl_kernel_syscall_exit ( void  )

Called on the way out of every syscall made from unprivileged code.

Implemented by the application. Runs privileged, on the syscall's stack, just before privilege is dropped; it may block, e.g. to hold a thread that is being killed.

◆ pbl_kernel_syscall_fault()

void pbl_kernel_syscall_fault ( uintptr_t  lr)

Kill the thread whose syscall failed.

Implemented by the application; called by pbl_syscall_fail().

Parameters
lrReturn address of the failed check, for the crash report.

◆ pbl_syscall_fail()

void pbl_syscall_fail ( void  )

Kill the thread whose syscall is in progress.

For a syscall handed bad arguments. Calls pbl_kernel_syscall_fault().

◆ pbl_syscall_verify_user()

void pbl_syscall_verify_user ( const void *  buf,
size_t  num_bytes 
)

Kill the caller unless a range is its memory.

Parameters
bufStart of the range.
num_bytesLength of the range.

◆ pbl_user_access_ok()

bool pbl_user_access_ok ( const void *  buf,
size_t  num_bytes 
)

Check that the caller of the syscall in progress may access a range.

Parameters
bufStart of the range.
num_bytesLength of the range.
Returns
true if [buf, buf + num_bytes) is the caller's memory.

◆ pbl_user_call()

void pbl_user_call ( void(*)(void *)  fn,
void *  ctx 
)

Call a function unprivileged from privileged thread mode.

For untrusted callbacks: memory or peripheral access the thread is not allowed inside fn faults the MPU instead of silently succeeding. The caller is privileged again when this returns.

Parameters
fnFunction to call.
ctxArgument passed to fn.

◆ pbl_user_copy_from()

int pbl_user_copy_from ( void *  dst,
const void *  src,
size_t  num_bytes 
)

Copy from the caller's memory.

Parameters
dstKernel destination.
srcSource in the caller's memory.
num_bytesBytes to copy.
Returns
0, or -EFAULT if src is not the caller's memory.

◆ pbl_user_copy_to()

int pbl_user_copy_to ( void *  dst,
const void *  src,
size_t  num_bytes 
)

Copy to the caller's memory.

Parameters
dstDestination in the caller's memory.
srcKernel source.
num_bytesBytes to copy.
Returns
0, or -EFAULT if dst is not the caller's memory.

◆ pbl_user_mode_enter()

void pbl_user_mode_enter ( void  )

Drop the calling thread to unprivileged mode for good.

From then on it reaches privileged code only through syscalls.

◆ pbl_user_strnlen()

int pbl_user_strnlen ( const char *  str,
size_t  max 
)

Measure a string in the caller's memory.

Parameters
strString.
maxLongest length accepted.
Returns
The length of str, or -EFAULT if it is longer than max or does not end within the caller's memory.