e2k: Impl basic unix signals support.

This commit is contained in:
Denis Drakhnia 2021-02-02 13:47:14 +02:00 committed by Denis Drakhnia
parent 9c1321402e
commit 26b9e9a1e4
7 changed files with 371 additions and 98 deletions

View File

@ -26,19 +26,12 @@
#include "cpu_loop-common.h"
#include "target_elf.h"
void helper_return(CPUE2KState *env);
void cpu_loop(CPUE2KState *env)
{
CPUState *cs = env_cpu(env);
int trapnr;
target_siginfo_t info;
if (env->restore_procedure) {
env->restore_procedure = false;
helper_return(env);
}
while (1) {
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
@ -47,30 +40,26 @@ void cpu_loop(CPUE2KState *env)
switch (trapnr) {
case E2K_EXCP_SYSCALL: {
abi_long args[10] = { 0 };
int psize = env->wd.psize <= 10 ? env->wd.psize : 10;
int i;
abi_ullong args[E2K_SYSCALL_MAX_ARGS] = { 0 };
int psize = MIN(E2K_SYSCALL_MAX_ARGS, env->wd.size);
abi_ulong ret;
for (i = 0; i < psize; i++) {
args[i] = env->regs[i];
}
// TODO: check what happens if env->wd.size is zero
memcpy(args, env->regs, psize * sizeof(args[0]));
abi_ulong ret = do_syscall(env, args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7], args[8]);
do {
ret = do_syscall(env, args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7], args[8]);
// FIXME: I don't know how to properly handle syscall restart
} while (ret == -TARGET_ERESTARTSYS);
if (ret != -TARGET_QEMU_ESIGRETURN && env->wd.psize > 0) {
memset(env->tags, E2K_TAG_NON_NUMBER64,
psize * sizeof(env->tags[0]));
if (ret == -TARGET_ERESTARTSYS) {
/* TODO: restart syscall */
abort();
} else if (env->wd.psize > 0 && ret != -TARGET_QEMU_ESIGRETURN) {
env->regs[0] = ret;
env->tags[0] = E2K_TAG_NUMBER64;
for (i = 1; i < env->wd.psize; i++) {
env->tags[i] = E2K_TAG_NON_NUMBER64;
}
}
// FIXME: Can call helpers from here?
helper_return(env);
break;
}
case E2K_EXCP_ILLOPC:

View File

@ -33,75 +33,270 @@
#define MLT_NUM (16 * 3) /* common for E3M and E3S */
struct target_sigcontext {
abi_ulong cr0_lo;
abi_ulong cr0_hi;
abi_ulong cr1_lo;
abi_ulong cr1_hi;
abi_ulong sbr; /* 21 Stack base register: top of */
abi_ullong cr0_lo;
abi_ullong cr0_hi;
abi_ullong cr1_lo;
abi_ullong cr1_hi;
abi_ullong sbr; /* 21 Stack base register: top of */
/* local data (user) stack */
abi_ulong usd_lo; /* 22 Local data (user) stack */
abi_ulong usd_hi; /* 23 descriptor: base & size */
abi_ulong psp_lo; /* 24 Procedure stack pointer: */
abi_ulong psp_hi; /* 25 base & index & size */
abi_ulong pcsp_lo; /* 26 Procedure chain stack */
abi_ulong pcsp_hi; /* 27 pointer: base & index & size */
abi_ullong usd_lo; /* 22 Local data (user) stack */
abi_ullong usd_hi; /* 23 descriptor: base & size */
abi_ullong psp_lo; /* 24 Procedure stack pointer: */
abi_ullong psp_hi; /* 25 base & index & size */
abi_ullong pcsp_lo; /* 26 Procedure chain stack */
abi_ullong pcsp_hi; /* 27 pointer: base & index & size */
/* additional part (for binary compiler) */
abi_ullong rpr_hi;
abi_ullong rpr_lo;
abi_ullong nr_TIRs;
abi_ullong tir_lo[TIR_NUM];
abi_ullong tir_hi[TIR_NUM];
abi_ullong trap_cell_addr[MAX_TC_SIZE];
abi_ullong trap_cell_val[MAX_TC_SIZE];
uint8_t trap_cell_tag[MAX_TC_SIZE];
abi_ullong trap_cell_info[MAX_TC_SIZE];
abi_ullong dam[DAM_ENTRIES_NUM];
abi_ullong sbbp[SBBP_ENTRIES_NUM];
abi_ullong mlt[MLT_NUM];
abi_ullong upsr;
};
/*
* additional part (for binary compiler)
* This structure is used for compatibility
* All new fields must be added in this structure
*/
uint8_t bincomp_padding[sizeof(abi_ulong)*184 + 10];
struct target_extra_ucontext {
abi_int sizeof_extra_uc; /* size of used fields(in bytes) */
abi_int curr_cnt; /* current index into trap_celler */
abi_int tc_count; /* trap_celler records count */
#if 0
abi_ulong rpr_hi;
abi_ulong rpr_lo;
/*
* For getcontext()
*/
abi_int fpcr;
abi_int fpsr;
abi_int pfpfr;
abi_ulong nr_TIRs;
abi_ulong tir_lo[TIR_NUM];
abi_ulong tir_hi[TIR_NUM];
abi_ulong trap_cell_addr[MAX_TC_SIZE];
abi_ulong trap_cell_val[MAX_TC_SIZE];
uint8_t trap_cell_tag[MAX_TC_SIZE];
abi_ulong trap_cell_info[MAX_TC_SIZE];
abi_ullong ctpr1;
abi_ullong ctpr2;
abi_ullong ctpr3;
abi_ulong dam[DAM_ENTRIES_NUM];
abi_int sc_need_rstrt;
};
abi_ulong sbbp[SBBP_ENTRIES_NUM];
struct target_ucontext {
abi_ulong uc_flags;
struct target_ucontext *uc_link;
target_stack_t uc_stack;
struct target_sigcontext uc_mcontext;
union {
target_sigset_t uc_sigmask; /* mask last for extensibility */
abi_ullong pad[16];
};
struct target_extra_ucontext uc_extra; /* for compatibility */
};
abi_ulong mlt[MLT_NUM];
struct target_sigframe {
target_siginfo_t info;
union {
struct target_ucontext uc;
// TODO: ucontext_prot
};
target_sigset_t saved_set;
abi_ulong upsr;
#endif
// FIXME: find where AAU state is saved
E2KAauState aau;
};
#define NF_ALIGNEDSZ (((sizeof(struct target_signal_frame) + 7) & (~7)))
void setup_frame(int sig, struct target_sigaction *ka,
target_sigset_t *set, CPUE2KState *env)
void helper_signal_frame(CPUE2KState *env, int wbs, target_ulong ret_ip);
void helper_signal_return(CPUE2KState *env);
static void setup_sigcontext(CPUE2KState *env,
struct target_sigcontext *sc, struct target_extra_ucontext *extra)
{
// TODO: setup_frame
qemu_log_mask(LOG_UNIMP, "setup_frame: not implemented\n");
int i;
// TODO: save binary compiler state (uspr, rpr, MLT)
__put_user(env->crs.cr0_lo, &sc->cr0_lo);
__put_user(env->crs.cr0_hi, &sc->cr0_hi);
__put_user(env->crs.cr1.lo, &sc->cr1_lo);
__put_user(env->crs.cr1.hi, &sc->cr1_hi);
__put_user(env->sbr, &sc->sbr);
__put_user(env->usd.lo, &sc->usd_lo);
__put_user(env->usd.hi, &sc->usd_hi);
__put_user(env->psp.lo, &sc->psp_lo);
__put_user(env->psp.hi, &sc->psp_hi);
__put_user(env->pcsp.lo, &sc->pcsp_lo);
__put_user(env->pcsp.hi, &sc->pcsp_hi);
// TODO: save trap state
__put_user(0, &sc->nr_TIRs);
__put_user(0, &sc->tir_lo[0]);
__put_user(0, &sc->tir_hi[0]);
__put_user(-1, &extra->curr_cnt);
__put_user(0, &extra->tc_count);
__put_user(sizeof(struct target_extra_ucontext) - sizeof(abi_int),
&extra->sizeof_extra_uc);
__put_user(env->fpcr.raw, &extra->fpcr);
__put_user(env->fpsr.raw, &extra->fpsr);
__put_user(env->pfpfr, &extra->pfpfr);
__put_user(env->ctprs[0].raw, &extra->ctpr1);
__put_user(env->ctprs[1].raw, &extra->ctpr2);
__put_user(env->ctprs[2].raw, &extra->ctpr3);
for (i = 0; i < DAM_ENTRIES_NUM; i++) {
__put_user(env->dam[i].raw, &sc->dam[i]);
}
}
static void setup_ucontext(struct target_ucontext *uc, CPUE2KState *env)
{
__put_user(0, &uc->uc_flags);
__put_user(0, &uc->uc_link);
target_save_altstack(&uc->uc_stack, env);
setup_sigcontext(env, &uc->uc_mcontext, &uc->uc_extra);
}
static abi_ulong get_sigframe(struct target_sigaction *ka, CPUE2KState *env,
size_t frame_size)
{
abi_ulong sp;
sp = target_sigsp(env->usd.base, ka);
sp = (sp - frame_size) & ~15;
return sp;
}
static void target_setup_frame(int sig, struct target_sigaction *ka,
target_siginfo_t *info, target_sigset_t *set, CPUE2KState *env)
{
abi_ulong frame_addr;
struct target_sigframe *frame;
/* save current frame */
helper_signal_frame(env, env->wd.size, env->ip);
frame_addr = get_sigframe(ka, env, sizeof(*frame));
trace_user_setup_rt_frame(env, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
force_sigsegv(sig);
}
setup_ucontext(&frame->uc, env);
copy_to_user((abi_ulong) &frame->uc.uc_sigmask, set, sizeof(*set));
copy_to_user((abi_ulong) &frame->aau, &env->aau, sizeof(env->aau));
if (ka->sa_flags & TARGET_SA_RESTORER) {
// TODO: sa_restorer?
qemu_log_mask(LOG_UNIMP, "target_setup_frame sa_restorer +\n");
} else {
// TODO: ignore?
}
/* fake kernel frame */
env->regs[0] = frame_addr;
env->tags[0] = E2K_TAG_NUMBER64;
env->wd.size = 2;
env->wd.psize = 0;
env->usd.size = env->sbr - frame_addr;
env->usd.base = frame_addr;
helper_signal_frame(env, 2, 0xe2ffffffffff);
env->ip = ka->_sa_handler;
env->regs[0] = sig;
env->tags[0] = E2K_TAG_NUMBER64;
env->wd.size = 8;
if (info && (ka->sa_flags & TARGET_SA_SIGINFO)) {
tswap_siginfo(&frame->info, info);
env->regs[1] = (uint64_t) &frame->info;
env->tags[1] = E2K_TAG_NUMBER64;
env->regs[2] = (uint64_t) &frame->uc;
env->tags[2] = E2K_TAG_NUMBER64;
}
unlock_user_struct(frame, frame_addr, 1);
}
static int target_restore_sigframe(CPUE2KState *env,
struct target_sigframe *frame)
{
__get_user(env->crs.cr0_hi, &frame->uc.uc_mcontext.cr0_hi);
__get_user(env->ctprs[0].raw, &frame->uc.uc_extra.ctpr1);
__get_user(env->ctprs[1].raw, &frame->uc.uc_extra.ctpr2);
__get_user(env->ctprs[2].raw, &frame->uc.uc_extra.ctpr3);
copy_from_user(&env->aau, (abi_ulong) &frame->aau, sizeof(env->aau));
return 0;
}
void setup_frame(int sig, struct target_sigaction *ka,
target_sigset_t *set, CPUE2KState *env)
{
target_setup_frame(sig, ka, 0, set, env);
}
void setup_rt_frame(int sig, struct target_sigaction *ka,
target_siginfo_t *info,
target_sigset_t *set, CPUE2KState *env)
target_siginfo_t *info, target_sigset_t *set, CPUE2KState *env)
{
// TODO: setup_rt_frame
qemu_log_mask(LOG_UNIMP, "setup_rt_frame: not implemented\n");
target_setup_frame(sig, ka, info, set, env);
}
long do_sigreturn(CPUE2KState *env)
{
// TODO: do_sigreturn
qemu_log_mask(LOG_UNIMP, "do_sigreturn: not implemented\n");
return 0;
return do_rt_sigreturn(env);
}
long do_rt_sigreturn(CPUE2KState *env)
{
trace_user_do_rt_sigreturn(env, 0);
// TODO: do_rt_sigreturn
qemu_log_mask(LOG_UNIMP, "do_rt_sigreturn: not implemented\n");
return -TARGET_ENOSYS;
abi_ulong frame_addr;
struct target_sigframe *frame;
sigset_t set;
/* restore fake kernel frame */
helper_signal_return(env);
frame_addr = env->regs[0];
trace_user_do_rt_sigreturn(env, frame_addr);
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
goto badframe;
}
target_to_host_sigset(&set, &frame->uc.uc_sigmask);
set_sigmask(&set);
if (target_restore_sigframe(env, frame)) {
goto badframe;
}
if (do_sigaltstack(frame_addr +
offsetof(struct target_sigframe, uc.uc_stack),
0, get_sp_from_cpustate(env)) == -EFAULT)
{
goto badframe;
}
env->ip = E2K_SIGRET_ADDR;
unlock_user_struct(frame, frame_addr, 0);
return -QEMU_ESIGRETURN;
badframe:
unlock_user_struct(frame, frame_addr, 0);
force_sig(TARGET_SIGSEGV);
return -QEMU_ESIGRETURN;
}
abi_long do_swapcontext(CPUArchState *env, abi_ulong uold_ctx,

View File

@ -10,6 +10,14 @@ static inline void cpu_clone_regs_child(CPUE2KState *env, target_ulong newsp,
unsigned flags)
{
if (newsp) {
// FIXME: what size must be?
env->usd.size = 0x20000;
env->usd.base = env->sbr = newsp & ~0xf;
env->usd.read = 1;
env->usd.write = 1;
}
if (flags & CLONE_VM) {
E2KPcsState pcs;
E2KPsState ps;
E2KCrs crs = { 0 };
@ -19,12 +27,6 @@ static inline void cpu_clone_regs_child(CPUE2KState *env, target_ulong newsp,
target_ulong ps_base = env->psp.base + env->psp.index;
int i;
// FIXME: what size must be?
env->usd.size = 0x20000;
env->usd.base = env->sbr = newsp & ~0xf;
env->usd.read = 1;
env->usd.write = 1;
e2k_pcs_new(&pcs);
e2k_ps_new(&ps);
@ -49,12 +51,12 @@ static inline void cpu_clone_regs_child(CPUE2KState *env, target_ulong newsp,
unlock_user_struct(ps_new, ps.base, 1);
unlock_user_struct(ps_old, ps_base, 0);
env->ip = E2K_SYSRET_ADDR;
env->pcsp = pcs;
env->psp = ps;
env->regs[0] = 0;
env->tags[0] = 0;
}
env->restore_procedure = true;
env->regs[0] = 0;
env->tags[0] = 0;
}
static inline void cpu_clone_regs_parent(CPUE2KState *env, unsigned flags)

View File

@ -76,8 +76,19 @@ typedef enum {
typedef enum {
CTPR_OPC_DISP = 0x0,
CTPR_OPC_LDISP = 0x1,
CTPR_OPC_SIGRET = 0x3,
} CtprOpc;
#ifdef CONFIG_USER_ONLY
#define E2K_SYSCALL_MAX_ARGS 10
/* fake kernel addresses */
#define E2K_SYSCALL_ADDR3 0xe20000001800
#define E2K_SYSCALL_ADDR6 0xe20000003000
#define E2K_SYSRET_ADDR 0xe28000000000
#define E2K_SYSRET_ADDR_CTPR 0xe2fffffffff8
#define E2K_SIGRET_ADDR 0xe20000015800
#endif
#define WD_BASE_OFF 0
#define WD_BASE_END 10
#define WD_BASE_LEN (WD_BASE_END - WD_BASE_OFF + 1)
@ -669,16 +680,13 @@ typedef struct CPUArchState {
/* internal use */
uint32_t is_bp; /* breakpoint flag */
int syscall_wbs; // FIXME: temp for syscall
/* zeroing upper register half for 32-bit instructions */
uint32_t wdbl;
target_ulong pcs_base;
target_ulong ps_base;
/* New thread needs to restore its state from hw stacks. */
bool restore_procedure;
/* Fields up to this point are cleared by a CPU reset */
struct {} end_reset_fields;

View File

@ -8,6 +8,8 @@
#define PS_FORCE_FX true
void helper_signal_frame(CPUE2KState *env, int wbs, target_ulong ret_ip);
static inline void reset_ctprs(CPUE2KState *env)
{
unsigned int i;
@ -65,7 +67,7 @@ static void proc_chain_save(CPUE2KState *env, int wd_base, target_ulong ret_ip)
pcs_push(env, env->crs.cr1.hi);
env->crs.cr0_lo = env->pregs;
env->crs.cr0_hi = ret_ip;
env->crs.cr0_hi = ret_ip & ~7;
env->crs.cr1.wbs = wd_base / 2;
env->crs.cr1.wpsz = env->wd.psize / 2;
env->crs.cr1.wfx = env->wd.fx;
@ -83,7 +85,7 @@ static void proc_chain_restore(CPUE2KState *env)
int wd_base;
env->pregs = env->crs.cr0_lo;
env->ip = env->crs.cr0_hi;
env->ip = env->crs.cr0_hi & ~7;
wd_base = env->crs.cr1.wbs * 2;
e2k_state_br_set(env, env->crs.cr1.br);
env->wd.size = env->wd.psize + wd_base;
@ -156,6 +158,18 @@ static void caller_window(CPUE2KState *env)
ps_fill(env, s, env->crs.cr1.wfx);
}
void HELPER(signal_frame)(CPUE2KState *env, int wd_size, target_ulong ret_ip)
{
callee_window(env, wd_size / 2);
proc_chain_save(env, wd_size, ret_ip);
}
void HELPER(signal_return)(CPUE2KState *env)
{
caller_window(env);
proc_chain_restore(env);
}
static inline void do_call(CPUE2KState *env, int wbs, target_ulong ret_ip)
{
callee_window(env, wbs);
@ -171,6 +185,13 @@ static inline void do_syscall(CPUE2KState *env, int wbs, target_ulong ret_ip)
cpu_loop_exit(cs);
}
void HELPER(syscall)(CPUE2KState *env)
{
CPUState *cs = env_cpu(env);
cs->exception_index = E2K_EXCP_SYSCALL;
cpu_loop_exit(cs);
}
void HELPER(call)(CPUE2KState *env, uint64_t ctpr_raw, int call_wbs,
target_ulong pc_next)
{
@ -178,11 +199,8 @@ void HELPER(call)(CPUE2KState *env, uint64_t ctpr_raw, int call_wbs,
switch (ctpr.tag) {
case CTPR_TAG_DISP:
do_call(env, call_wbs, pc_next);
env->ip = ctpr.base;
break;
case CTPR_TAG_SDISP:
do_syscall(env, call_wbs, pc_next);
do_call(env, call_wbs, pc_next);
env->ip = ctpr.base;
break;
default:
@ -200,7 +218,13 @@ uint64_t HELPER(prep_return)(CPUE2KState *env, int ipd)
return 0;
}
ret.base = env->crs.cr0_hi;
if (env->crs.cr0_hi == E2K_SYSRET_ADDR_CTPR) {
ret.base = E2K_SIGRET_ADDR;
ret.opc = CTPR_OPC_SIGRET;
} else {
ret.base = env->crs.cr0_hi;
}
ret.tag = CTPR_TAG_RETURN;
ret.ipd = ipd;
@ -209,9 +233,24 @@ uint64_t HELPER(prep_return)(CPUE2KState *env, int ipd)
void HELPER(return)(CPUE2KState *env)
{
caller_window(env);
proc_chain_restore(env);
reset_ctprs(env);
CtprOpc opc = env->ctprs[2].opc;
if (opc == CTPR_OPC_SIGRET) {
CPUState *cs = env_cpu(env);
env->wd.psize = 2;
env->regs[0] = 119; /* TARGET_NR_sigreturn */
env->tags[0] = E2K_TAG_NUMBER64;
cs->exception_index = E2K_EXCP_SYSCALL;
cpu_loop_exit(cs);
} else {
if (opc != 0) {
qemu_log("%#lx: unknown return ctpr opc %d\n", env->ip, opc);
}
caller_window(env);
proc_chain_restore(env);
reset_ctprs(env);
}
}
void HELPER(raise_exception)(CPUE2KState *env, int tt)

View File

@ -11,7 +11,9 @@ DEF_HELPER_2(raise_exception, noreturn, env, int)
DEF_HELPER_2(raise_exception_no_spill, noreturn, env, int)
DEF_HELPER_2(prep_return, i64, env, int)
DEF_HELPER_1(return, void, env)
DEF_HELPER_1(signal_return, void, env)
DEF_HELPER_4(call, void, env, i64, int, tl)
DEF_HELPER_1(syscall, void, env)
DEF_HELPER_2(sxt, i64, i64, i32)
DEF_HELPER_1(debug_i32, void, i32)
DEF_HELPER_1(debug_i64, void, i64)

View File

@ -619,7 +619,8 @@ static inline void gen_cs0(DisasContext *ctx)
}
case CS0_SDISP: {
// TODO: real sdisp target address
target_ulong target = (0xe2UL << 40) | cs0->sdisp.disp;
target_ulong target = 0xe2UL << 40;
target = deposit64(target, 11, 17, cs0->sdisp.disp);
uint64_t ctpr = ctpr_new(CTPR_TAG_SDISP, 0, cs0->sdisp.ipd, target);
gen_set_ctpr(cs0->sdisp.ctpr, ctpr);
break;
@ -1149,13 +1150,50 @@ static void e2k_tr_translate_insn(DisasContextBase *db, CPUState *cs)
DisasContext *ctx = container_of(db, DisasContext, base);
target_ulong pc_next;
pc_next = do_decode(ctx, cs);
do_execute(ctx);
do_checks(ctx);
do_commit(ctx);
do_branch(ctx, pc_next);
switch (ctx->base.pc_next) {
#ifdef CONFIG_USER_ONLY
case E2K_SYSCALL_ADDR3:
case E2K_SYSCALL_ADDR6:
/* fake enter into syscall handler */
ctx->base.is_jmp = DISAS_NORETURN;
/* force non-zero tb size */
pc_next = ctx->base.pc_next + 8;
e2k_gen_save_pc(E2K_SYSRET_ADDR);
gen_helper_syscall(cpu_env);
tcg_gen_exit_tb(NULL, TB_EXIT_IDX0);
break;
case E2K_SYSRET_ADDR: {
/* fake return from syscall handler */
TCGv_i32 t0 = tcg_const_i32(0);
ctx->base.is_jmp = DISAS_NORETURN;
/* force non-zero tb size */
pc_next = ctx->base.pc_next + 8;
gen_helper_prep_return(e2k_cs.ctprs[2], cpu_env, t0);
gen_helper_return(cpu_env);
tcg_gen_exit_tb(NULL, TB_EXIT_IDX0);
tcg_temp_free_i32(t0);
break;
}
case E2K_SIGRET_ADDR:
/* fake return from signal handler */
gen_helper_signal_return(cpu_env);
tcg_gen_exit_tb(NULL, TB_EXIT_IDX0);
break;
#endif
default: {
pc_next = do_decode(ctx, cs);
do_execute(ctx);
do_checks(ctx);
do_commit(ctx);
do_branch(ctx, pc_next);
ctx->mlock = NULL;
break;
}
}
ctx->mlock = NULL;
ctx->base.pc_next = pc_next;
/* Free temporary values */