/*
 * \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'.
 */
#define size_t __SIZE_TYPE__

/* 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 <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_ */
