linux-user/e2k: impl some fast syscalls

This commit is contained in:
Denis Drakhnia 2024-02-28 20:48:34 +02:00
parent b9c2c736c2
commit 65c773536f
5 changed files with 106 additions and 54 deletions

View File

@ -60,38 +60,55 @@ static void stack_expand(CPUE2KState *env, E2KPsp *s)
s->size = new_size; s->size = new_size;
} }
static void e2k_clear_probe_page_cache(uint32_t syscall) static void e2k_clear_probe_page_cache(uint32_t num)
{ {
CPUState *other_cpu; CPUState *other_cpu;
switch (syscall) { switch (num) {
case TARGET_NR_execve: case TARGET_NR_execve:
case TARGET_NR_brk: case TARGET_NR_brk:
case TARGET_NR_mmap: case TARGET_NR_mmap:
case TARGET_NR_munmap: case TARGET_NR_munmap:
case TARGET_NR_clone: case TARGET_NR_clone:
case TARGET_NR_mmap2: case TARGET_NR_mmap2:
case TARGET_NR_mremap: case TARGET_NR_mremap:
case TARGET_NR_remap_file_pages: case TARGET_NR_remap_file_pages:
case TARGET_NR_move_pages: case TARGET_NR_move_pages:
case TARGET_NR_migrate_pages: case TARGET_NR_migrate_pages:
start_exclusive(); start_exclusive();
CPU_FOREACH(other_cpu) { CPU_FOREACH(other_cpu) {
E2KCPU *cpu = E2K_CPU(other_cpu); E2KCPU *cpu = E2K_CPU(other_cpu);
CPUE2KState *env = &cpu->env; CPUE2KState *env = &cpu->env;
memset(env->probe_cache_page, 0, sizeof(env->probe_cache_page)); memset(env->probe_cache_page, 0, sizeof(env->probe_cache_page));
memset(env->probe_cache_flags, 0, sizeof(env->probe_cache_flags)); memset(env->probe_cache_flags, 0, sizeof(env->probe_cache_flags));
} }
end_exclusive(); end_exclusive();
break; break;
} }
}
static int e2k_map_fast_syscall(uint32_t num)
{
switch (num) {
case TARGET_NR_fast_sys_gettimeofday:
return TARGET_NR_gettimeofday;
case TARGET_NR_fast_sys_clock_gettime:
return TARGET_NR_clock_gettime;
case TARGET_NR_fast_sys_getcpu:
return TARGET_NR_getcpu;
case TARGET_NR_fast_sys_siggetmask:
case TARGET_NR_fast_sys_getcontext:
case TARGET_NR_fast_sys_set_return:
default:
return -TARGET_ENOSYS;
}
} }
void cpu_loop(CPUE2KState *env) void cpu_loop(CPUE2KState *env)
{ {
CPUState *cs = env_cpu(env); CPUState *cs = env_cpu(env);
int trapnr; int trapnr, psize;
while (1) { while (1) {
if (env->is_bp) { if (env->is_bp) {
@ -105,29 +122,38 @@ void cpu_loop(CPUE2KState *env)
process_queued_cpu_work(cs); process_queued_cpu_work(cs);
switch (trapnr) { switch (trapnr) {
case E2K_EXCP_SYSCALL: { case E2K_EXCP_SYSCALL:
int psize = MIN(E2K_SYSCALL_MAX_ARGS, env->wd.size); case E2K_EXCP_SYSCALL_FAST:
if ((psize = MIN(E2K_SYSCALL_MAX_ARGS, env->wd.size))) {
if (psize) { abi_ullong ret = 0, args[E2K_SYSCALL_MAX_ARGS] = { 0 };
abi_ullong ret, args[E2K_SYSCALL_MAX_ARGS] = { 0 }; int i, num;
int i;
for (i = 0; i < psize; i++) { for (i = 0; i < psize; i++) {
args[i] = env->wreg[i].lo; args[i] = env->wreg[i].lo;
} }
if (!env->enable_tags || (env->wtag[0] & E2K_TAG_MASK_32) == E2K_TAG_NUMBER32) { if (!env->enable_tags || (env->wtag[0] & E2K_TAG_MASK_32) == E2K_TAG_NUMBER32) {
args[0] = (uint32_t) args[0]; num = args[0];
e2k_clear_probe_page_cache(args[0]);
ret = do_syscall(env, args[0], args[1], args[2], args[3], if (trapnr == E2K_EXCP_SYSCALL_FAST) {
args[4], args[5], args[6], args[7], args[8]); num = e2k_map_fast_syscall(num);
if (num < 0) {
ret = -num;
}
}
if (ret == 0) {
e2k_clear_probe_page_cache(num);
ret = do_syscall(env, num, args[1], args[2], args[3],
args[4], args[5], args[6], args[7], args[8]);
}
} else { } else {
ret = TARGET_ENOSYS; ret = TARGET_ENOSYS;
} }
if (ret == -QEMU_ERESTARTSYS) { if (ret == -QEMU_ERESTARTSYS) {
/* do not set sysret address and syscall will be restarted */ /* do not set sysret address and syscall will be restarted */
} else if (ret != -QEMU_ESIGRETURN && env->wd.psize > 0) { } else if (ret != -QEMU_ESIGRETURN) {
env->ip = E2K_SYSRET_ADDR; env->ip = E2K_SYSRET_ADDR;
env->wreg[0].lo = ret; env->wreg[0].lo = ret;
@ -143,7 +169,6 @@ void cpu_loop(CPUE2KState *env)
env->ip = E2K_SYSRET_ADDR; env->ip = E2K_SYSRET_ADDR;
} }
break; break;
}
case E2K_EXCP_ILLEGAL_OPCODE: case E2K_EXCP_ILLEGAL_OPCODE:
case E2K_EXCP_PRIV_ACTION: case E2K_EXCP_PRIV_ACTION:
gen_signal(env, TARGET_SIGILL, TARGET_ILL_ILLOPC, env->ip); gen_signal(env, TARGET_SIGILL, TARGET_ILL_ILLOPC, env->ip);

View File

@ -454,4 +454,12 @@
#define TARGET_syscalls 431 #define TARGET_syscalls 431
/* Fast system calls */
#define TARGET_NR_fast_sys_gettimeofday 0
#define TARGET_NR_fast_sys_clock_gettime 1
#define TARGET_NR_fast_sys_getcpu 2
#define TARGET_NR_fast_sys_siggetmask 3
#define TARGET_NR_fast_sys_getcontext 4
#define TARGET_NR_fast_sys_set_return 5
#endif /* E2K_SYSCALL_NR_H */ #endif /* E2K_SYSCALL_NR_H */

View File

@ -91,22 +91,36 @@ typedef enum {
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
# define E2K_SYSCALL_MAX_ARGS 10 # define E2K_SYSCALL_MAX_ARGS 10
/* fake kernel addresses */ /*
# if TARGET_LONG_BITS == 64 * E2K syscall cannot be restarted because it can be bundled with other
# define E2K_FAKE_KERN_START 0xe20000000000 * operations. To work around the issue we have fake syscall entry
# define E2K_FAKE_KERN_END 0xe30000000000 * addresses. Each syscall jumps to a fake syscall entry address and
# else /* TARGET_LONG_BITS == 32 */ * can be restarted without issues.
# define E2K_FAKE_KERN_START 0xe0000000 */
# define E2K_FAKE_KERN_END 0xe3000000 # ifdef TARGET_E2K32
# define E2K_FAKE_KERN_START 0xe0000000
# define E2K_FAKE_KERN_END 0xe3000000
# else
# define E2K_FAKE_KERN_START 0xe20000000000
# define E2K_FAKE_KERN_END 0xe30000000000
# endif # endif
# define E2K_SYSCALL_ADDR1 (E2K_FAKE_KERN_START + 0x800 * 1)
# define E2K_SYSCALL_ADDR3 (E2K_FAKE_KERN_START + 0x800 * 3) # define E2K_SYSCALL_ADDR(TRAPNUM) (E2K_FAKE_KERN_START + 0x800 * (TRAPNUM))
# define E2K_SYSCALL_ADDR4 (E2K_FAKE_KERN_START + 0x800 * 4) # define E2K_SYSCALL_ENTRY_OLD E2K_SYSCALL_ADDR(4) /* Deprecated */
# define E2K_SYSCALL_ADDR6 (E2K_FAKE_KERN_START + 0x800 * 6) # if defined(TARGET_E2K128)
# define E2K_SYSCALL_ENTRY E2K_SYSCALL_ADDR(7)
# elif defined(TARGET_E2K32)
# define E2K_SYSCALL_ENTRY E2K_SYSCALL_ADDR(1)
# define E2K_SYSCALL_FAST_ENTRY E2K_SYSCALL_ADDR(5)
# else
# define E2K_SYSCALL_ENTRY E2K_SYSCALL_ADDR(3)
# define E2K_SYSCALL_FAST_ENTRY E2K_SYSCALL_ADDR(6)
# endif
# define E2K_SYSRET_ADDR (E2K_FAKE_KERN_START + 0x15700) # define E2K_SYSRET_ADDR (E2K_FAKE_KERN_START + 0x15700)
# define E2K_SIGRET_ADDR (E2K_FAKE_KERN_START + 0x15800) # define E2K_SIGRET_ADDR (E2K_FAKE_KERN_START + 0x15800)
# define E2K_SYSRET_BACKTRACE_ADDR (E2K_FAKE_KERN_START + 0x15900) # define E2K_SYSRET_BACKTRACE_ADDR (E2K_FAKE_KERN_START + 0x15900)
#endif #endif /* CONFIG_USER_ONLY */
#define WD_BASE_OFF 0 #define WD_BASE_OFF 0
#define WD_BASE_END 10 #define WD_BASE_END 10
@ -306,7 +320,9 @@ typedef enum {
E2K_EXCP_MAX = 43, E2K_EXCP_MAX = 43,
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
/* keep in sync with helper.c:raise_exception_ra */
E2K_EXCP_SYSCALL = 100, E2K_EXCP_SYSCALL = 100,
E2K_EXCP_SYSCALL_FAST = 101,
#endif #endif
} E2KException; } E2KException;

View File

@ -299,6 +299,7 @@ void G_NORETURN raise_exception_ra(CPUE2KState *env, int exception_index,
switch (exception_index) { switch (exception_index) {
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
case E2K_EXCP_SYSCALL: case E2K_EXCP_SYSCALL:
case E2K_EXCP_SYSCALL_FAST:
#endif #endif
case E2K_EXCP_PROC_STACK_BOUNDS: case E2K_EXCP_PROC_STACK_BOUNDS:
case E2K_EXCP_CHAIN_STACK_BOUNDS: case E2K_EXCP_CHAIN_STACK_BOUNDS:

View File

@ -7919,18 +7919,20 @@ static void e2k_tr_translate_insn(DisasContextBase *db, CPUState *cs)
switch (ctx->base.pc_next) { switch (ctx->base.pc_next) {
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
# ifdef TARGET_E2K32 case E2K_SYSCALL_ENTRY_OLD:
case E2K_SYSCALL_ADDR1: case E2K_SYSCALL_ENTRY:
case E2K_SYSCALL_ADDR4: # if defined(E2K_SYSCALL_FAST_ENTRY)
# else /* !TARGET_E2K32 */ case E2K_SYSCALL_FAST_ENTRY:
case E2K_SYSCALL_ADDR3:
case E2K_SYSCALL_ADDR6:
# endif # endif
/* fake enter into syscall handler */ /* fake enter into syscall handler */
ctx->base.is_jmp = DISAS_NORETURN; ctx->base.is_jmp = DISAS_NORETURN;
/* force non-zero tb size */ /* force non-zero tb size */
pc_next = ctx->base.pc_next + 8; pc_next = ctx->base.pc_next + 8;
gen_tr_exception(ctx, E2K_EXCP_SYSCALL); gen_tr_exception(ctx,
# if defined(E2K_SYSCALL_FAST_ENTRY)
ctx->base.pc_next == E2K_SYSCALL_FAST_ENTRY ? E2K_EXCP_SYSCALL_FAST :
# endif
E2K_EXCP_SYSCALL);
tcg_gen_exit_tb(NULL, TB_EXIT_IDX0); tcg_gen_exit_tb(NULL, TB_EXIT_IDX0);
break; break;
case E2K_SYSRET_BACKTRACE_ADDR: case E2K_SYSRET_BACKTRACE_ADDR: