linux-headers/arch/e2k/include/asm/fast_syscalls.h

488 lines
14 KiB
C

#ifndef _ASM_E2K_FAST_SYSCALLS_H
#define _ASM_E2K_FAST_SYSCALLS_H
#include <linux/time.h>
#include <linux/time.h>
#include <linux/timekeeper_internal.h>
#include <asm/sections.h>
#include <asm/signal.h>
#include <asm/sclkr.h>
#include <asm/clkr.h>
#include <asm/trap_table.h>
#include <asm/gregs.h>
#include <asm/hw_stacks.h>
#include <uapi/asm/ucontext.h>
struct fast_syscalls_data {
struct timekeeper *tk;
u32 mult;
u32 shift;
struct clocksource *clock;
struct timespec wall_time_coarse;
};
extern struct fast_syscalls_data fsys_data;
extern seqcount_t timekeeper_seq;
typedef void (*fast_system_call_func)(u64 arg1, u64 arg2);
extern const fast_system_call_func fast_sys_calls_table[NR_fast_syscalls];
extern const fast_system_call_func fast_sys_calls_table_32[NR_fast_syscalls];
int fast_sys_ni_syscall(void);
#define FAST_SYSTEM_CALL_TBL_ENTRY(sysname) \
(fast_system_call_func) sysname
#define COMPAT_FAST_SYSTEM_CALL_TBL_ENTRY(sysname) \
(fast_system_call_func) compat_##sysname
#define PROTECTED_FAST_SYSTEM_CALL_TBL_ENTRY(sysname) \
(fast_system_call_func) protected_##sysname
int native_do_fast_clock_gettime(const clockid_t which_clock,
struct timespec *tp);
int native_fast_sys_clock_gettime(const clockid_t which_clock,
struct timespec __user *tp);
int native_do_fast_gettimeofday(struct timeval *tv);
int native_do_fast_sys_set_return(u64 ip, int flags);
int native_fast_sys_siggetmask(u64 __user *oset, size_t sigsetsize);
#ifdef CONFIG_KVM_GUEST_KERNEL
/* it is pure guest kernel (not paravirtualized basec on pv_ops) */
#include <asm/kvm/guest/fast_syscalls.h>
#elif defined(CONFIG_PARAVIRT_GUEST)
/* it is paravirtualized host and guest kernel */
#include <asm/paravirt/fast_syscalls.h>
#else /* ! CONFIG_KVM_GUEST_KERNEL && ! CONFIG_PARAVIRT_GUEST */
/* it is native host kernel withounr virtualization */
/* or host kernel with virtualization support */
static inline int
do_fast_clock_gettime(const clockid_t which_clock, struct timespec *tp)
{
return native_do_fast_clock_gettime(which_clock, tp);
}
static inline int
do_fast_gettimeofday(struct timeval *tv)
{
return native_do_fast_gettimeofday(tv);
}
static inline int
do_fast_sys_set_return(u64 ip, int flags)
{
return native_do_fast_sys_set_return(ip, flags);
}
#ifdef CONFIG_KVM_HOST_MODE
extern long ret_from_fast_sys_call(void);
static inline long kvm_return_from_fast_syscall(thread_info_t *ti, long arg1)
{
/* Restore vcpu state reg old value (guest user) */
HOST_VCPU_STATE_REG_RESTORE(ti);
/* TODO: Cleanup guest kernel's pgds in shadow page table */
/* Get current parameters of top chain stack frame */
e2k_cr0_lo_t cr0_lo = READ_CR0_LO_REG();
e2k_cr0_hi_t cr0_hi = READ_CR0_HI_REG();
e2k_cr1_lo_t cr1_lo = READ_CR1_LO_REG();
e2k_cr1_hi_t cr1_hi = READ_CR1_HI_REG();
/*
* Correct ip in current chain stack frame to return to guest user
* through special trap function ret_from_fast_syscall_trampoline
*/
AS(cr0_lo).pf = -1ULL;
AS(cr0_hi).ip = ((u64)ret_from_fast_sys_call) >> 3;
AS(cr1_lo).psr = AW(E2K_KERNEL_PSR_DISABLED);
AS(cr1_lo).cui = KERNEL_CODES_INDEX;
WRITE_CR0_LO_REG(cr0_lo);
WRITE_CR0_HI_REG(cr0_hi);
WRITE_CR1_LO_REG(cr1_lo);
WRITE_CR1_HI_REG(cr1_hi);
return arg1;
}
static inline long kvm_set_return_user_ip(thread_info_t *gti, u64 ip, int flags)
{
e2k_pcsp_lo_t pcsp_lo;
e2k_pcsp_hi_t pcsp_hi;
e2k_cr0_hi_t cr0_hi;
e2k_mem_crs_t *frame, *base;
u64 prev_ip;
E2K_FLUSHC;
if (unlikely(flags))
return -EINVAL;
if (unlikely(ip >= USER_DS.seg))
return -EFAULT;
pcsp_hi = READ_PCSP_HI_REG(); /* We don't use %pcsp_hi.size */
pcsp_lo = READ_PCSP_LO_REG();
base = (e2k_mem_crs_t *) GET_PCS_BASE(&gti->u_hw_stack);
frame = (e2k_mem_crs_t *) (AS(pcsp_lo).base + AS(pcsp_hi).ind);
do {
--frame;
cr0_hi = frame->cr0_hi;
prev_ip = AS(cr0_hi).ip << 3;
} while (unlikely(prev_ip >= GUEST_TASK_SIZE && frame > base));
/* No user frames above? */
if (unlikely(prev_ip >= GUEST_TASK_SIZE))
return -EPERM;
/* Modify stack */
AS(cr0_hi).ip = ip >> 3;
frame->cr0_hi = cr0_hi;
return 0;
}
#endif /* CONFIG_KVM_HOST_MODE */
/* trap table entry started by direct branch (it is closer to fast system */
/* call wirthout switch and use user local data stack */
#define goto_ttable_entry1_args3(sys_num, arg1, arg2, ret) \
E2K_GOTO_ARG7(native_ttable_entry1, sys_num, arg1, arg2, 0, 0, 0, 0)
#define goto_ttable_entry1_args4(sys_num, arg1, arg2, arg3, ret) \
E2K_GOTO_ARG7(native_ttable_entry1, sys_num, arg1, arg2, arg3, 0, 0, 0)
#define goto_ttable_entry3_args3(sys_num, arg1, arg2, ret) \
E2K_GOTO_ARG7(native_ttable_entry3, sys_num, arg1, arg2, 0, 0, 0, 0)
#define goto_ttable_entry3_args4(sys_num, arg1, arg2, arg3, ret) \
E2K_GOTO_ARG7(native_ttable_entry3, sys_num, arg1, arg2, arg3, 0, 0, 0)
#endif /* ! CONFIG_KVM_GUEST_KERNEL */
/*
* These have to be macros since there is no way to return two
* values (seconds and nanoseconds) to an __interrupt function
* without assembler magic.
*/
enum {
FAST_SYS_OK,
FAST_SYS_ERROR
};
#define fast_get_time(secs, nsecs, monotonic) \
({ \
struct clocksource *__clock; \
struct timekeeper *__tk; \
u64 __cycles = 0, __cycle_last = 0, __mask = 0; \
u32 __mult, __shift; \
unsigned __seq; \
int __ret = FAST_SYS_ERROR; \
long wall2mon_sec, wall2mon_nsec; \
\
do { \
__seq = raw_read_seqcount_begin(&timekeeper_seq); \
\
__tk = fsys_data.tk; \
__clock = fsys_data.clock; \
__mult = fsys_data.mult; \
__shift = fsys_data.shift; \
\
secs = __tk->xtime_sec; \
nsecs = __tk->tkr_mono.xtime_nsec; \
\
if (monotonic) { \
wall2mon_sec = __tk->wall_to_monotonic.tv_sec; \
wall2mon_nsec = __tk->wall_to_monotonic.tv_nsec;\
} \
\
if (likely(__clock == &clocksource_sclkr)) { \
__cycle_last = __tk->tkr_mono.cycle_last; \
__mask = __clock->mask; \
__cycles = fast_syscall_read_sclkr(); \
if (__cycles) \
__ret = FAST_SYS_OK; \
} else if (likely(__clock == &clocksource_clkr)) { \
__cycle_last = __tk->tkr_mono.cycle_last; \
__mask = __clock->mask; \
__cycles = fast_syscall_read_clkr(); \
__ret = FAST_SYS_OK; \
} \
} while (unlikely(read_seqcount_retry(&timekeeper_seq, __seq))); \
\
if (__ret == FAST_SYS_OK) { \
nsecs = (((__cycles - __cycle_last) & __mask) \
* __mult + nsecs) >> __shift; \
\
if (monotonic) { \
secs += wall2mon_sec; \
nsecs += wall2mon_nsec; \
} \
\
while (nsecs >= NSEC_PER_SEC) { \
++secs; \
nsecs -= NSEC_PER_SEC; \
} \
} \
\
__ret; \
})
#define fast_get_time_coarse(secs, nsecs, monotonic) \
({ \
struct timekeeper *__tk; \
unsigned __seq; \
\
do { \
__seq = raw_read_seqcount_begin(&timekeeper_seq); \
\
secs = fsys_data.wall_time_coarse.tv_sec; \
nsecs = fsys_data.wall_time_coarse.tv_nsec; \
\
if (monotonic) { \
__tk = fsys_data.tk; \
secs += __tk->wall_to_monotonic.tv_sec; \
nsecs += __tk->wall_to_monotonic.tv_nsec; \
} \
} while (unlikely(read_seqcount_retry(&timekeeper_seq, __seq))); \
\
while (nsecs >= NSEC_PER_SEC) { \
++secs; \
nsecs -= NSEC_PER_SEC; \
} \
\
FAST_SYS_OK; \
})
static inline int
DO_FAST_CLOCK_GETTIME(const clockid_t which_clock, struct timespec *tp)
{
u64 secs = 0, nsecs = 0;
int ret;
switch (which_clock) {
case CLOCK_REALTIME:
case CLOCK_MONOTONIC:
ret = fast_get_time(secs, nsecs,
which_clock == CLOCK_MONOTONIC);
break;
case CLOCK_REALTIME_COARSE:
case CLOCK_MONOTONIC_COARSE:
ret = fast_get_time_coarse(secs, nsecs,
which_clock == CLOCK_MONOTONIC_COARSE);
break;
default:
ret = FAST_SYS_ERROR;
break;
}
if (likely(!ret)) {
tp->tv_sec = secs;
tp->tv_nsec = nsecs;
}
return ret;
}
/* trap table entry is called as function (it is closer to hardware start) */
typedef long (*ttable_entry_args3)(int sys_num, u64 arg1, u64 arg2);
typedef long (*ttable_entry_args4)(int sys_num, u64 arg1, u64 arg2, u64 arg3);
#define ttable_entry3_args3(sys_num, arg1, arg2) \
((ttable_entry_args3)(get_ttable_entry3))(sys_num, arg1, arg2)
#define ttable_entry3_args4(sys_num, arg1, arg2) \
((ttable_entry_args4)(get_ttable_entry3))(sys_num, arg1, arg2, arg3)
#define ttable_entry_clock_gettime(which, time, ret) \
/* ibranch */ goto_ttable_entry3_args3(__NR_clock_gettime, which, time, ret)
/* call ttable_entry3_args3(__NR_clock_gettime, which, time) */
#define ttable_entry_gettimeofday(tv, tz, ret) \
/* ibranch */ goto_ttable_entry3_args3(__NR_gettimeofday, tv, tz, ret)
/* ttable_entry3_args3(__NR_gettimeofday, tv, tz) */
#define ttable_entry_sigprocmask(how, nset, oset, ret) \
/* ibranch */ goto_ttable_entry3_args4(__NR_sigprocmask, how, \
nset, oset, ret)
/* ttable_entry3_args4(__NR_sigprocmask, how, nset, oset) */
#define ttable_entry_getcpu(cpup, nodep, unused, ret) \
/* ibranch */ goto_ttable_entry3_args4(__NR_getcpu, cpup, nodep, unused, ret)
/* ttable_entry3_args4(__NR_getcpu, cpup, nodep, unused) */
static inline int
FAST_SYS_CLOCK_GETTIME(const clockid_t which_clock, struct timespec __user *tp)
{
struct thread_info *const ti = READ_CURRENT_REG();
int r;
prefetch_nospec(&fsys_data);
tp = (typeof(tp)) ((u64) tp & E2K_VA_MASK);
if (unlikely((u64) tp + sizeof(struct timespec) > ti->addr_limit.seg))
return -EFAULT;
r = do_fast_clock_gettime(which_clock, tp);
if (unlikely(r))
ttable_entry_clock_gettime(which_clock, tp, r);
return r;
}
static inline int
DO_FAST_GETTIMEOFDAY(struct timeval *tv)
{
u64 secs = 0, nsecs = 0;
int ret;
ret = fast_get_time(secs, nsecs, false);
if (likely(!ret)) {
tv->tv_sec = secs;
tv->tv_usec = nsecs / 1000;
}
return ret;
}
static inline int
FAST_SYS_SIGGETMASK(u64 __user *oset, size_t sigsetsize)
{
struct thread_info *const ti = READ_CURRENT_REG();
struct task_struct *task = thread_info_task(ti);
u64 set;
set = task->blocked.sig[0];
if (unlikely(sigsetsize != 8))
return -EINVAL;
oset = (typeof(oset)) ((u64) oset & E2K_VA_MASK);
if (unlikely((u64) oset + sizeof(sigset_t) > ti->addr_limit.seg))
return -EFAULT;
*oset = set;
return 0;
}
int fast_sys_gettimeofday(struct timeval __user *tv,
struct timezone __user *tz);
int fast_sys_clock_gettime(const clockid_t which_clock,
struct timespec __user *tp);
struct getcpu_cache;
int fast_sys_getcpu(unsigned __user *cpup, unsigned __user *nodep,
struct getcpu_cache __user *unused);
int fast_sys_siggetmask(u64 __user *oset, size_t sigsetsize);
int fast_sys_getcontext(struct ucontext __user *ucp, size_t sigsetsize);
struct compat_timespec;
int compat_fast_sys_clock_gettime(const clockid_t which_clock,
struct compat_timespec __user *tp);
struct compat_timeval;
int compat_fast_sys_gettimeofday(struct compat_timeval __user *tv,
struct timezone __user *tz);
int compat_fast_sys_siggetmask(u32 __user *oset, size_t sigsetsize);
int compat_fast_sys_getcontext(struct ucontext_32 __user *ucp,
size_t sigsetsize);
int compat_fast_sys_set_return(u32 ip, int flags);
int protected_fast_sys_clock_gettime(u32 tags, clockid_t which_clock,
u64 arg3, u64 arg4, u64 arg5);
int protected_fast_sys_gettimeofday(u32 tags,
u64 arg2, u64 arg3, u64 arg4, u64 arg5);
int protected_fast_sys_getcpu(u32 tags, u64 arg2, u64 arg3, u64 arg4, u64 arg5);
int protected_fast_sys_siggetmask(u32 tags, u64 arg2, u64 arg3, size_t sigsetsize);
int protected_fast_sys_getcontext(u32 tags, u64 arg2, u64 arg3, size_t sigsetsize);
/* Inlined handlers for fast syscalls */
notrace __interrupt __section(.entry_handlers)
static inline int _fast_sys_gettimeofday(struct timeval __user *__restrict tv,
struct timezone __user *__restrict tz)
{
struct thread_info *const ti = READ_CURRENT_REG();
int ret;
prefetch_nospec(&fsys_data);
tv = (typeof(tv)) ((u64) tv & E2K_VA_MASK);
tz = (typeof(tz)) ((u64) tz & E2K_VA_MASK);
if (unlikely((u64) tv + sizeof(struct timeval) > ti->addr_limit.seg
|| (u64) tz + sizeof(struct timezone)
> ti->addr_limit.seg))
return -EFAULT;
if (likely(tv)) {
ret = do_fast_gettimeofday(tv);
if (unlikely(ret))
ttable_entry_gettimeofday((u64) tv, (u64) tz, ret);
} else {
ret = 0;
}
if (tz) {
tz->tz_minuteswest = sys_tz.tz_minuteswest;
tz->tz_dsttime = sys_tz.tz_dsttime;
}
return ret;
}
notrace __interrupt __section(.entry_handlers)
static inline int _fast_sys_clock_gettime(const clockid_t which_clock,
struct timespec __user *tp)
{
return FAST_SYS_CLOCK_GETTIME(which_clock, tp);
}
notrace __interrupt __section(.entry_handlers)
static inline int _fast_sys_siggetmask(u64 __user *oset, size_t sigsetsize)
{
return FAST_SYS_SIGGETMASK(oset, sigsetsize);
}
notrace __interrupt __section(.entry_handlers)
static inline int _fast_sys_getcontext(struct ucontext __user *ucp,
size_t sigsetsize)
{
struct thread_info *const ti = READ_CURRENT_REG();
struct task_struct *task = thread_info_task(ti);
register u64 pcsp_lo, pcsp_hi;
register u32 fpcr, fpsr, pfpfr;
u64 set, key;
BUILD_BUG_ON(sizeof(task->blocked.sig[0]) != 8);
set = task->blocked.sig[0];
if (unlikely(sigsetsize != 8))
return -EINVAL;
ucp = (typeof(ucp)) ((u64) ucp & E2K_VA_MASK);
if (unlikely((u64) ucp + sizeof(struct ucontext) > ti->addr_limit.seg))
return -EFAULT;
key = context_ti_key_fast_syscall(ti);
E2K_GETCONTEXT(fpcr, fpsr, pfpfr, pcsp_lo, pcsp_hi);
/* We want stack to point to user frame that called us */
pcsp_hi -= SZ_OF_CR;
*((u64 *) &ucp->uc_sigmask) = set;
ucp->uc_mcontext.sbr = key;
ucp->uc_mcontext.pcsp_lo = pcsp_lo;
ucp->uc_mcontext.pcsp_hi = pcsp_hi;
ucp->uc_extra.fpcr = fpcr;
ucp->uc_extra.fpsr = fpsr;
ucp->uc_extra.pfpfr = pfpfr;
return 0;
}
notrace __interrupt __section(.entry_handlers)
static inline int fast_sys_set_return(u64 ip, int flags)
{
return do_fast_sys_set_return(ip, flags);
}
#endif /* _ASM_E2K_FAST_SYSCALLS_H */