442 lines
11 KiB
C++
442 lines
11 KiB
C++
/*
|
|
* \brief Linux system-call bindings
|
|
* \author Norman Feske
|
|
* \date 2008-10-22
|
|
*
|
|
* This file is meant to be internally used by the framework. It is not public
|
|
* interface.
|
|
*
|
|
* From within the framework libraries, we have to use the Linux syscall
|
|
* interface directly rather than relying on convenient libc functions to be
|
|
* able to link this part of the framework to a custom libc. Otherwise, we
|
|
* would end up with very nasty cyclic dependencies when using framework
|
|
* functions such as IPC from the libc back end.
|
|
*
|
|
* The Linux syscall interface looks different for 32bit and 64bit system, in
|
|
* particular regarding the socket interface. On 32bit systems, all socket
|
|
* operations are invoked via the 'socketcall' syscall. On 64bit systems, the
|
|
* different socket functions have distinct syscalls.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2008-2017 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU Affero General Public License version 3.
|
|
*/
|
|
|
|
#ifndef _LIB__SYSCALL__LINUX_SYSCALLS_H_
|
|
#define _LIB__SYSCALL__LINUX_SYSCALLS_H_
|
|
|
|
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE 1 /* needed to enable the definition of 'stat64' */
|
|
#endif
|
|
|
|
/* Genode includes */
|
|
#include <util/string.h>
|
|
#include <base/snprintf.h>
|
|
#include <base/log.h>
|
|
|
|
/*
|
|
* Resolve ambiguity between 'Genode::size_t' and the host's header's 'size_t'.
|
|
*/
|
|
#ifndef __SIZE_TYPE__
|
|
#define size_t __SIZE_TYPE__
|
|
#endif
|
|
|
|
/* Linux includes */
|
|
#include <sys/cdefs.h> /* include first to avoid double definition of '__always_inline' */
|
|
#include <linux/futex.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <linux/sched.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/un.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/mman.h>
|
|
|
|
|
|
#undef size_t
|
|
|
|
|
|
/***********************************
|
|
** Low-level debugging utilities **
|
|
***********************************/
|
|
|
|
extern "C" void wait_for_continue(void);
|
|
|
|
#define PRAW(fmt, ...) \
|
|
do { \
|
|
char str[128]; \
|
|
Genode::snprintf(str, sizeof(str), \
|
|
ESC_ERR fmt ESC_END "\n", ##__VA_ARGS__); \
|
|
Genode::raw(Genode::Cstring(str)); \
|
|
} while (0)
|
|
|
|
|
|
/*********************************************************
|
|
** System-call bindings implemented in syscall library **
|
|
*********************************************************/
|
|
|
|
extern "C" long lx_syscall(int number, ...);
|
|
extern "C" int lx_clone(int (*fn)(void *), void *child_stack,
|
|
int flags, void *arg);
|
|
|
|
|
|
/*****************************************
|
|
** General syscalls used by base-linux **
|
|
*****************************************/
|
|
|
|
inline int lx_write(int fd, const void *buf, Genode::size_t count)
|
|
{
|
|
return lx_syscall(SYS_write, fd, buf, count);
|
|
}
|
|
|
|
|
|
inline int lx_close(int fd)
|
|
{
|
|
return lx_syscall(SYS_close, fd);
|
|
}
|
|
|
|
|
|
inline int lx_dup2(int fd, int to)
|
|
{
|
|
return lx_syscall(SYS_dup2, fd, to);
|
|
}
|
|
|
|
|
|
/*****************************************
|
|
** Functions used by the IPC framework **
|
|
*****************************************/
|
|
|
|
#include <linux/net.h>
|
|
|
|
#ifdef SYS_socketcall
|
|
|
|
inline int lx_socketcall(int call, long *args)
|
|
{
|
|
int res = lx_syscall(SYS_socketcall, call, args);
|
|
return res;
|
|
}
|
|
|
|
inline int lx_socketpair(int domain, int type, int protocol, int sd[2])
|
|
{
|
|
long args[4] = { domain, type, protocol, (long)sd };
|
|
return lx_socketcall(SYS_SOCKETPAIR, args);
|
|
}
|
|
|
|
|
|
inline int lx_sendmsg(int sockfd, const struct msghdr *msg, int flags)
|
|
{
|
|
long args[3] = { sockfd, (long)msg, flags };
|
|
return lx_socketcall(SYS_SENDMSG, args);
|
|
}
|
|
|
|
|
|
inline int lx_recvmsg(int sockfd, struct msghdr *msg, int flags)
|
|
{
|
|
long args[3] = { sockfd, (long)msg, flags };
|
|
return lx_socketcall(SYS_RECVMSG, args);
|
|
}
|
|
|
|
|
|
inline int lx_getpeername(int sockfd, struct sockaddr *name, socklen_t *namelen)
|
|
{
|
|
long args[3] = { sockfd, (long)name, (long)namelen };
|
|
return lx_socketcall(SYS_GETPEERNAME, args);
|
|
}
|
|
|
|
#else
|
|
|
|
inline int lx_socketpair(int domain, int type, int protocol, int sd[2])
|
|
{
|
|
return lx_syscall(SYS_socketpair, domain, type, protocol, (unsigned long)sd);
|
|
}
|
|
|
|
|
|
inline int lx_sendmsg(int sockfd, const struct msghdr *msg, int flags)
|
|
{
|
|
return lx_syscall(SYS_sendmsg, sockfd, msg, flags);
|
|
}
|
|
|
|
|
|
inline int lx_recvmsg(int sockfd, struct msghdr *msg, int flags)
|
|
{
|
|
return lx_syscall(SYS_recvmsg, sockfd, msg, flags);
|
|
}
|
|
|
|
|
|
inline int lx_getpeername(int sockfd, struct sockaddr *name, socklen_t *namelen)
|
|
{
|
|
return lx_syscall(SYS_getpeername, sockfd, name, namelen);
|
|
}
|
|
|
|
/* TODO add missing socket system calls */
|
|
|
|
#endif /* SYS_socketcall */
|
|
|
|
|
|
/*******************************************
|
|
** Functions used by the process library **
|
|
*******************************************/
|
|
|
|
inline void lx_exit(int status)
|
|
{
|
|
lx_syscall(SYS_exit, status);
|
|
}
|
|
|
|
|
|
inline void lx_exit_group(int status)
|
|
{
|
|
lx_syscall(SYS_exit_group, status);
|
|
}
|
|
|
|
|
|
/************************************************************
|
|
** Functions used by the env library and local rm session **
|
|
************************************************************/
|
|
|
|
/* O_CLOEXEC is a GNU extension so we provide it here */
|
|
enum { LX_O_CLOEXEC = 02000000 };
|
|
|
|
inline void *lx_mmap(void *start, Genode::size_t length, int prot, int flags,
|
|
int fd, off_t offset)
|
|
{
|
|
#ifdef _LP64
|
|
return (void *)lx_syscall(SYS_mmap, start, length, prot, flags, fd, offset);
|
|
#else
|
|
return (void *)lx_syscall(SYS_mmap2, start, length, prot, flags, fd, offset/4096);
|
|
#endif /* _LP64 */
|
|
}
|
|
|
|
|
|
inline int lx_munmap(void *addr, Genode::size_t length)
|
|
{
|
|
return lx_syscall(SYS_munmap, addr, length);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
** Functions used by thread lib and core's cancel-blocking mechanism **
|
|
***********************************************************************/
|
|
|
|
enum {
|
|
LX_SIGINT = 2, /* used by core to catch Control-C */
|
|
LX_SIGILL = 4, /* exception: illegal instruction */
|
|
LX_SIGBUS = 7, /* exception: bus error, i.e., bad memory access */
|
|
LX_SIGFPE = 8, /* exception: floating point */
|
|
LX_SIGUSR1 = 10, /* used for cancel-blocking mechanism */
|
|
LX_SIGSEGV = 11, /* exception: segmentation violation */
|
|
LX_SIGCHLD = 17, /* child process changed state, i.e., terminated */
|
|
LX_SIGCANCEL = 32, /* accoring to glibc, this equals SIGRTMIN,
|
|
used for killing threads */
|
|
LX_NSIG = 64, /* number of different signals supported */
|
|
};
|
|
|
|
|
|
struct kernel_sigaction
|
|
{
|
|
void (*handler)(int);
|
|
unsigned long flags;
|
|
void (*restorer)(void);
|
|
sigset_t mask;
|
|
};
|
|
|
|
|
|
inline int lx_sigemptyset(sigset_t *set)
|
|
{
|
|
if (set == 0)
|
|
return -1;
|
|
Genode::memset(set, 0, sizeof(sigset_t));
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef _LP64
|
|
extern "C" void lx_restore_rt (void);
|
|
#endif
|
|
|
|
/**
|
|
* Simplified binding for sigaction system call
|
|
*/
|
|
inline int lx_sigaction(int signum, void (*handler)(int), bool altstack)
|
|
{
|
|
struct kernel_sigaction act;
|
|
act.handler = handler;
|
|
|
|
#ifdef _LP64
|
|
/*
|
|
* The SA_RESTORER flag is not officially documented, but used internally
|
|
* by the glibc implementation of sigaction(). Without specifying this flag
|
|
* tgkill() does not work on x86_64. The restorer function gets called
|
|
* when leaving the signal handler and it should call the rt_sigreturn syscall.
|
|
*/
|
|
enum { SA_RESTORER = 0x04000000 };
|
|
act.flags = SA_RESTORER;
|
|
act.restorer = lx_restore_rt;
|
|
#else
|
|
act.flags = 0;
|
|
act.restorer = 0;
|
|
#endif
|
|
|
|
/* use alternate signal stack if requested */
|
|
act.flags |= altstack ? SA_ONSTACK : 0;
|
|
|
|
lx_sigemptyset(&act.mask);
|
|
|
|
return lx_syscall(SYS_rt_sigaction, signum, &act, 0UL, LX_NSIG/8);
|
|
}
|
|
|
|
|
|
/**
|
|
* Send signal to thread
|
|
*
|
|
* This function is used by core to cancel blocking operations of
|
|
* threads, and by the thread library to kill threads.
|
|
*/
|
|
inline int lx_tgkill(int pid, int tid, int signal)
|
|
{
|
|
return lx_syscall(SYS_tgkill, pid, tid, signal);
|
|
}
|
|
|
|
|
|
/**
|
|
* Alternate signal stack (handles also SIGSEGV in a safe way)
|
|
*/
|
|
inline int lx_sigaltstack(void *signal_stack, Genode::size_t stack_size)
|
|
{
|
|
stack_t stack { signal_stack, 0, stack_size };
|
|
|
|
return lx_syscall(SYS_sigaltstack, &stack, nullptr);
|
|
}
|
|
|
|
|
|
inline int lx_create_thread(void (*entry)(), void *stack, void *arg)
|
|
{
|
|
int flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND
|
|
| CLONE_THREAD | CLONE_SYSVSEM;
|
|
|
|
/*
|
|
* The syscall binding for clone does not exist in the FreeBSD libc, which
|
|
* we are using as libc for Genode. In glibc, clone is implemented as a
|
|
* assembler binding without external libc references. Hence, we are safe
|
|
* to rely on the glibc version of 'clone' here.
|
|
*/
|
|
return lx_clone((int (*)(void *))entry, stack, flags, arg);
|
|
}
|
|
|
|
|
|
inline pid_t lx_getpid() { return lx_syscall(SYS_getpid); }
|
|
inline pid_t lx_gettid() { return lx_syscall(SYS_gettid); }
|
|
inline uid_t lx_getuid() { return lx_syscall(SYS_getuid); }
|
|
|
|
|
|
/************************************
|
|
** Functions used by lock library **
|
|
************************************/
|
|
|
|
struct timespec;
|
|
|
|
inline int lx_nanosleep(const struct timespec *req, struct timespec *rem)
|
|
{
|
|
return lx_syscall(SYS_nanosleep, req, rem);
|
|
}
|
|
|
|
enum {
|
|
LX_FUTEX_WAIT = FUTEX_WAIT,
|
|
LX_FUTEX_WAKE = FUTEX_WAKE,
|
|
};
|
|
|
|
inline int lx_futex(const int *uaddr, int op, int val)
|
|
{
|
|
return lx_syscall(SYS_futex, uaddr, op, val, 0, 0, 0);
|
|
}
|
|
|
|
|
|
/**
|
|
* Signal set corrsponding to glibc's 'sigset_t'
|
|
*/
|
|
class Lx_sigset
|
|
{
|
|
private:
|
|
|
|
enum {
|
|
BITS_PER_LONG = 8 * sizeof(unsigned long),
|
|
SIGSET_SIZE = LX_NSIG / BITS_PER_LONG
|
|
};
|
|
|
|
unsigned long _value[SIGSET_SIZE];
|
|
|
|
/*
|
|
* Both '__sigword' and '__sigmask' were macros defined in the glibc
|
|
* header file 'bits/sigset.h'. The macros were moved in later versions
|
|
* and, therefore, we implement them explicitly.
|
|
*/
|
|
|
|
/* return mask that includes the bit for 'signum' only (was __sigmask) */
|
|
unsigned long _mask(int signum)
|
|
{
|
|
return (1UL) << ((signum - 1) % BITS_PER_LONG);
|
|
}
|
|
|
|
/* return word index for 'signum' (was __sigword) */
|
|
unsigned _word(int signum)
|
|
{
|
|
return (signum - 1) / BITS_PER_LONG;
|
|
}
|
|
|
|
public:
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
Lx_sigset() { Genode::memset(_value, 0, sizeof(_value)); }
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* \param signum set specified entry of sigset
|
|
*/
|
|
Lx_sigset(int signum) : Lx_sigset()
|
|
{
|
|
_value[_word(signum)] = _mask(signum);
|
|
}
|
|
|
|
bool is_set(int signum)
|
|
{
|
|
return _value[_word(signum)] && _mask(signum);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Check if signal is pending
|
|
*
|
|
* \return true if signal is pending
|
|
*/
|
|
inline bool lx_sigpending(int signum)
|
|
{
|
|
Lx_sigset sigset;
|
|
lx_syscall(SYS_rt_sigpending, &sigset, sizeof(sigset));
|
|
return sigset.is_set(signum);
|
|
}
|
|
|
|
|
|
/**
|
|
* Set signal mask state
|
|
*
|
|
* \param signum signal to mask or unmask
|
|
* \param state mask state for the signal,
|
|
* true enables the signal,
|
|
* false blocks the signal
|
|
*/
|
|
inline bool lx_sigsetmask(int signum, bool state)
|
|
{
|
|
Lx_sigset old_sigmask, sigset(signum);
|
|
lx_syscall(SYS_rt_sigprocmask, state ? SIG_UNBLOCK : SIG_BLOCK, &sigset, &old_sigmask, sizeof(sigset));
|
|
return old_sigmask.is_set(signum);
|
|
}
|
|
|
|
|
|
#endif /* _LIB__SYSCALL__LINUX_SYSCALLS_H_ */
|