e2k: Add access_hw_stacks and backtrace syscalls.

Special system calls for managing hardware stacks.
Required for C++ exceptions.

Signed-off-by: Denis Drakhnya <numas13@gmail.com>
This commit is contained in:
Denis Drakhnia 2021-02-20 14:17:37 +02:00 committed by Denis Drakhnia
parent f49a9d8df0
commit 374e658eb1
9 changed files with 440 additions and 36 deletions

View File

@ -223,7 +223,7 @@ static void target_setup_frame(int sig, struct target_sigaction *ka,
env->wd.psize = 0;
env->usd.size = env->sbr - frame_addr;
env->usd.base = frame_addr;
helper_signal_frame(env, 2, E2K_SYSRET_ADDR_CTPR);
helper_signal_frame(env, 2, E2K_SIGRET_ADDR);
env->ip = ka->_sa_handler;
env->regs[0] = sig;
@ -232,9 +232,9 @@ static void target_setup_frame(int sig, struct target_sigaction *ka,
if (info && (ka->sa_flags & TARGET_SA_SIGINFO)) {
tswap_siginfo(&frame->info, info);
env->regs[1] = (uint64_t) &frame->info;
env->regs[1] = frame_addr + offsetof(struct target_sigframe, info);
env->tags[1] = E2K_TAG_NUMBER64;
env->regs[2] = (uint64_t) &frame->uc;
env->regs[2] = frame_addr + offsetof(struct target_sigframe, uc);
env->tags[2] = E2K_TAG_NUMBER64;
}

View File

@ -70,7 +70,7 @@ struct target_pt_regs {
#define TARGET_PAGE_OFFSET 0x0000d00000000000UL
#define TARGET_TASK_SIZE TARGET_PAGE_OFFSET
#else
#define TARGET_TASK_SIZE 0xf0000000UL
#define TARGET_TASK_SIZE 0xe0000000UL
#endif
/* modes for sys_access_hw_stacks */

View File

@ -133,6 +133,12 @@
{ TARGET_NR_access_hw_stacks, "access_hw_stacks", NULL,
print_access_hw_stacks, NULL },
#endif
#ifdef TARGET_NR_set_backtrace
{ TARGET_NR_set_backtrace, "set_backtrace", "%s(%p,%lu,%lu,%lu)", NULL, NULL },
#endif
#ifdef TARGET_NR_get_backtrace
{ TARGET_NR_get_backtrace, "get_backtrace", "%s(%p,%lu,%lu,%lu)", NULL, NULL },
#endif
#ifdef TARGET_NR_epoll_create
{ TARGET_NR_epoll_create, "epoll_create", "%s(%d)", NULL, NULL },
#endif

View File

@ -7089,6 +7089,405 @@ static abi_long do_e2k_longjmp2(CPUE2KState *env, struct target_jmp_info *jmp_in
return 0;
}
static abi_long copy_current_chain_stack(abi_ulong dst, abi_ulong src,
abi_ulong size)
{
E2KCrs *from, *to;
abi_long ret = 0;
int i;
if (!QEMU_IS_ALIGNED(src, sizeof(*from)) ||
!QEMU_IS_ALIGNED(size, sizeof(*from)))
{
return -TARGET_EINVAL;
}
from = lock_user(VERIFY_READ, src, size, 1);
to = lock_user(VERIFY_WRITE, dst, size, 0);
if (!to || !from) {
ret = -TARGET_EFAULT;
goto exit;
}
for (i = 0; i < size; i += sizeof(E2KCrs), to++, from++) {
E2KCrs crs = *from;
target_ulong ip;
memset(to, 0, sizeof(*to));
to->cr0_lo = from->cr0_lo;
ip = crs.cr0_hi & ~7;
if (ip < TARGET_TASK_SIZE) {
to->cr0_hi = ip;
to->cr1.ussz = from->cr1.ussz;
}
// FIXME: check what exactly needs to be copied
to->cr1 = from->cr1;
}
exit:
unlock_user(from, src, size);
unlock_user(to, dst, size);
return ret;
}
static abi_long copy_procedure_stack(abi_ulong dst, abi_ulong dst_tag, abi_ulong src,
abi_ulong size)
{
abi_ullong *from, *to;
uint8_t *to_tag = NULL;
abi_long ret = 0;
int i;
from = lock_user(VERIFY_READ, src, size, 1);
to = lock_user(VERIFY_WRITE, dst, size, 0);
if (!to || !from) {
ret = -TARGET_EFAULT;
goto exit;
}
if (dst_tag) {
to_tag = lock_user(VERIFY_WRITE, dst_tag, size / 8, 0);
if (!to_tag) {
ret = -TARGET_EFAULT;
goto exit;
}
// TODO: what to do with tags?
memset(to_tag, 0, size / 8);
}
for (i = 0; i < size; i += sizeof(abi_ullong), to++, from++) {
*to = *from;
}
exit:
unlock_user(from, src, size);
unlock_user(to_tag, dst_tag, size / 8);
unlock_user(to, dst, size);
return ret;
}
static abi_long do_e2k_access_hw_stacks(CPUState *cpu, abi_ulong arg2,
abi_ulong arg3, abi_ulong arg4, abi_ulong arg5, abi_ulong arg6)
{
E2KCPU *e2k_cpu = E2K_CPU(cpu);
CPUE2KState *env = &e2k_cpu->env;
abi_ulong mode = arg2;
abi_ulong frame_addr = arg3; // __user (abi_ullong *)
abi_ulong buf_addr = arg4; // __user (char *)
abi_ulong buf_size = arg5;
abi_ulong size_addr = arg6; // __user (void *)
int ret = 0;
switch (mode) {
case GET_PROCEDURE_STACK_SIZE:
ret = put_user(env->psp.index, size_addr, target_ulong);
break;
case GET_CHAIN_STACK_SIZE:
ret = put_user(env->pcsp.index + sizeof(E2KCrs), size_addr, target_ulong);
break;
case GET_CHAIN_STACK_OFFSET:
{
abi_ullong frame, pcs_top;
ret = get_user(frame, frame_addr, abi_ullong);
if (ret) {
return ret;
}
pcs_top = env->pcsp.base + env->pcsp.size;
if (env->pcsp.base > frame || pcs_top <= frame) {
return -TARGET_ESRCH;
}
ret = put_user(frame - env->pcsp.base, size_addr, target_ulong);
break;
}
case READ_CHAIN_STACK:
{
abi_ullong frame, pcs_used_top;
abi_ulong used_size;
ret = get_user(frame, frame_addr, abi_ullong);
if (ret) {
return ret;
}
pcs_used_top = env->pcsp.base + env->pcsp.index;
if (frame < env->pcsp.base || frame > pcs_used_top) {
return -TARGET_EINVAL;
}
used_size = frame - env->pcsp.base;
if (size_addr) {
ret = put_user(used_size, size_addr, target_ulong);
if (ret) {
return ret;
}
}
if (used_size > buf_size) {
return -TARGET_ENOMEM;
}
ret = copy_current_chain_stack(buf_addr, env->pcsp.base,
used_size);
break;
}
case READ_CHAIN_STACK_EX:
case WRITE_CHAIN_STACK_EX:
{
abi_ullong frame;
abi_ulong dst, src;
ret = get_user(frame, frame_addr, abi_ullong);
if (ret) {
return ret;
}
if ((env->pcsp.index + sizeof(E2KCrs)) < (frame + buf_size)) {
return -TARGET_EFAULT;
}
if (mode == READ_CHAIN_STACK_EX) {
dst = buf_addr;
src = env->pcsp.base + frame;
} else {
dst = env->pcsp.base + frame;
src = buf_addr;
}
ret = copy_current_chain_stack(dst, src, buf_size);
break;
}
case READ_PROCEDURE_STACK:
case WRITE_PROCEDURE_STACK:
{
abi_ullong offset, ps_used_top;
abi_ulong used_size, dst, dst_tag, src;
ret = get_user(offset, frame_addr, abi_ullong);
if (ret) {
return ret;
}
ps_used_top = env->psp.base + env->psp.index;
if (offset < env->psp.base || offset > ps_used_top) {
return -TARGET_EINVAL;
}
used_size = offset - env->psp.base;
if (size_addr) {
ret = put_user(used_size, size_addr, target_ulong);
if (ret) {
return ret;
}
}
if (used_size > buf_size) {
return -TARGET_ENOMEM;
}
if (mode == READ_PROCEDURE_STACK) {
dst = buf_addr;
dst_tag = 0;
src = env->psp.base;
} else {
dst = env->psp.base;
dst_tag = env->psp.base_tag;
src = buf_addr;
}
ret = copy_procedure_stack(dst, dst_tag, src, used_size);
break;
}
case READ_PROCEDURE_STACK_EX:
case WRITE_PROCEDURE_STACK_EX:
{
abi_ullong offset;
abi_ulong dst, dst_tag, src;
ret = get_user(offset, frame_addr, abi_ullong);
if (ret) {
return ret;
}
if (env->psp.index < (offset + buf_size)) {
return -TARGET_EFAULT;
}
if (mode == READ_PROCEDURE_STACK_EX) {
dst = buf_addr;
dst_tag = 0;
src = env->psp.base + offset;
} else {
dst = env->psp.base + offset;
dst_tag = env->psp.base_tag + offset / 8;
src = buf_addr;
}
ret = copy_procedure_stack(dst, dst_tag, src, buf_size);
break;
}
default:
return -TARGET_ENOSYS;
}
return ret;
}
static bool is_privileged_return(target_ulong ip)
{
return ip == E2K_SYSRET_BACKTRACE_ADDR;
}
static abi_long do_e2k_set_backtrace(CPUState *cpu, abi_ulong buf_addr,
abi_ulong count, abi_ulong skip, abi_ulong flags)
{
E2KCPU *e2k_cpu = E2K_CPU(cpu);
CPUE2KState *env = &e2k_cpu->env;
E2KCrs *pcs_base, *frame;
target_ulong *buf;
abi_long ret = 0;
int nr_written = 0;
uint64_t cr0_hi;
E2KCr1 cr1;
if (flags) {
return -TARGET_EINVAL;
}
buf = lock_user(VERIFY_READ, buf_addr, count * sizeof(*buf), 1);
pcs_base = lock_user(VERIFY_WRITE, env->pcsp.base, env->pcsp.index, 1);
if (!buf || !pcs_base) {
ret = -TARGET_EFAULT;
goto exit;
}
frame = pcs_base + env->pcsp.index / sizeof(*frame);
while (nr_written < count) {
target_ulong prev_ip, ip;
frame -= 1;
if (frame < pcs_base) {
/* Not an error: we will just return the size */
break;
}
__get_user(ip, buf);
__get_user(cr0_hi, &frame->cr0_hi);
__get_user(cr1.lo, &frame->cr1.lo);
if (ip == -1) {
ip = E2K_SYSRET_BACKTRACE_ADDR;
}
prev_ip = cr0_hi & ~7;
/* skip fake kernel frames */
if (prev_ip >= TARGET_TASK_SIZE) {
continue;
}
/* Skip the requested number of frames */
if (skip) {
--skip;
continue;
}
if (!is_privileged_return(ip) && !access_ok(cpu, VERIFY_READ, ip, 8)) {
ret = -TARGET_EFAULT;
break;
}
/* Forbid changing of special return value into normal
* one - to avoid cases when user changes to special and
* back to normal function to avoid security checks. */
if (is_privileged_return(prev_ip) && !is_privileged_return(ip)) {
ret = -TARGET_EPERM;
break;
}
// TODO: ip/prev_ip page flags checks
cr0_hi = ip & ~7;
__put_user(cr0_hi, &frame->cr0_hi);
if (is_privileged_return(ip)) {
cr1.pm = 1;
cr1.ic = 0;
__put_user(cr1.lo, &frame->cr1.lo);
}
buf += 1;
nr_written += 1;
}
if (nr_written) {
ret = nr_written;
}
exit:
unlock_user(pcs_base, env->pcsp.base, env->pcsp.index);
unlock_user(buf, buf_addr, count * sizeof(buf));
return ret;
}
static abi_long do_e2k_get_backtrace(CPUState *cpu, abi_ulong buf_addr,
abi_ulong count, abi_ulong skip, abi_ulong flags)
{
E2KCPU *e2k_cpu = E2K_CPU(cpu);
CPUE2KState *env = &e2k_cpu->env;
E2KCrs *pcs_base, *frame;
target_ulong *buf;
abi_long ret = 0;
int nr_read = 0;
uint64_t cr0_hi;
if (flags) {
return -TARGET_EINVAL;
}
buf = lock_user(VERIFY_WRITE, buf_addr, count * sizeof(*buf), 0);
pcs_base = lock_user(VERIFY_READ, env->pcsp.base, env->pcsp.index, 1);
if (!buf || !pcs_base) {
ret = -TARGET_EFAULT;
goto exit;
}
frame = pcs_base + env->pcsp.index / sizeof(*frame);
while (nr_read < count) {
target_ulong ip;
frame -= 1;
if (frame < pcs_base) {
/* Not an error: we will just return the size */
break;
}
__get_user(cr0_hi, &frame->cr0_hi);
ip = cr0_hi & ~7;
/* skip fake kernel frames */
if (!is_privileged_return(ip) && ip >= TARGET_TASK_SIZE) {
continue;
}
/* Skip the requested number of frames */
if (skip) {
--skip;
continue;
}
if (!is_privileged_return(ip) && !access_ok(cpu, VERIFY_READ, ip, 8)) {
ret = -TARGET_EFAULT;
break;
}
/* Special case of "just return" function */
if (is_privileged_return(ip)) {
ip = -1;
}
__put_user(ip, buf);
buf += 1;
nr_read += 1;
}
if (nr_read) {
ret = nr_read;
}
exit:
unlock_user(pcs_base, env->pcsp.base, env->pcsp.index);
unlock_user(buf, buf_addr, count * sizeof(buf));
return ret;
}
#endif /* end of TARGET_E2K */
static abi_long do_fcntl(int fd, int cmd, abi_ulong arg)
@ -11999,24 +12398,24 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
if (ret) {
break;
}
do_e2k_longjmp2(env, &ji);
ret = do_e2k_longjmp2(env, &ji);
if (ret) {
break;
}
return arg2;
}
#endif
#ifdef TARGET_NR_access_hw_stacks
case TARGET_NR_access_hw_stacks:
{
#if 0
abi_ulong mode = arg2;
abi_ulong frame_ptr = arg3; // __user (abi_ullong *)
abi_ulong buf = arg4; // __user (char *)
abi_ulong buf_size = arg5;
abi_ulong real_size = arg6; // __user (void *)
return do_e2k_access_hw_stacks(cpu, arg1, arg2, arg3, arg4, arg5);
#endif
// TODO: e2k_sys_access_hw_stacks
return -TARGET_ENOSYS;
}
#ifdef TARGET_NR_set_backtrace
case TARGET_NR_set_backtrace:
return do_e2k_set_backtrace(cpu, arg1, arg2, arg3, arg4);
#endif
#ifdef TARGET_NR_get_backtrace
case TARGET_NR_get_backtrace:
return do_e2k_get_backtrace(cpu, arg1, arg2, arg3, arg4);
#endif
#ifdef CONFIG_ATTR
#ifdef TARGET_NR_setxattr

View File

@ -93,19 +93,17 @@ typedef enum {
# if TARGET_LONG_BITS == 64
# define E2K_FAKE_KERN_START 0xe20000000000
# define E2K_FAKE_KERN_END 0xe30000000000
# define E2K_SYSRET_ADDR (E2K_FAKE_KERN_START + 0x8000000000)
# define E2K_SYSRET_ADDR_CTPR (E2K_FAKE_KERN_START + 0xfffffffff8)
# define E2K_SYSCALL_ADDR3 (E2K_FAKE_KERN_START + 0x800 * 3)
# define E2K_SYSCALL_ADDR6 (E2K_FAKE_KERN_START + 0x800 * 6)
# else /* TARGET_LONG_BITS == 32 */
# define E2K_FAKE_KERN_START 0xe0000000
# define E2K_FAKE_KERN_END 0xe3000000
# define E2K_SYSRET_ADDR (E2K_FAKE_KERN_START + 0x800000)
# define E2K_SYSRET_ADDR_CTPR (E2K_FAKE_KERN_START + 0xfffff8)
# define E2K_SYSCALL_ADDR1 (E2K_FAKE_KERN_START + 0x800 * 1)
# define E2K_SYSCALL_ADDR4 (E2K_FAKE_KERN_START + 0x800 * 4)
# endif
# define E2K_SYSCALL_ADDR1 (E2K_FAKE_KERN_START + 0x800 * 1)
# define E2K_SYSCALL_ADDR3 (E2K_FAKE_KERN_START + 0x800 * 3)
# define E2K_SYSCALL_ADDR4 (E2K_FAKE_KERN_START + 0x800 * 4)
# define E2K_SYSCALL_ADDR6 (E2K_FAKE_KERN_START + 0x800 * 6)
# define E2K_SYSRET_ADDR (E2K_FAKE_KERN_START + 0x15700)
# define E2K_SIGRET_ADDR (E2K_FAKE_KERN_START + 0x15800)
# define E2K_SYSRET_BACKTRACE_ADDR (E2K_FAKE_KERN_START + 0x15900)
#endif
#define WD_BASE_OFF 0
@ -373,7 +371,7 @@ typedef struct {
uint64_t unused2: 40;
uint64_t cui: 16;
uint64_t ic: 1;
uint64_t pm: 1;
uint64_t pm: 1; /* privileged mode */
uint64_t ie: 1;
uint64_t sge: 1;
uint64_t lw: 1;

View File

@ -226,15 +226,10 @@ uint64_t HELPER(prep_return)(CPUE2KState *env, int ipd)
target_ulong addr = env->pcsp.base + env->pcsp.index + offsetof(E2KCrs, cr0_hi);
uint64_t cr0_hi = cpu_ldq_le_data(env, addr) & ~7;
if (cr0_hi == E2K_SYSRET_ADDR_CTPR) {
ret.base = E2K_SIGRET_ADDR;
ret.opc = CTPR_OPC_SIGRET;
} else {
ret.base = cr0_hi;
}
ret.tag = CTPR_TAG_RETURN;
ret.ipd = ipd;
ret.base = cr0_hi;
ret.tag = CTPR_TAG_RETURN;
ret.opc = cr0_hi == E2K_SIGRET_ADDR ? CTPR_OPC_SIGRET : 0;
return ret.raw;
}

View File

@ -40,8 +40,10 @@ uint64_t helper_state_reg_read_i64(CPUE2KState *env, int idx)
{
switch (idx) {
case 0x01: return e2k_state_wd(env); /* %wd */
case 0x0f: return e2k_state_pcsp_lo(env); /* %pcsp.lo */
case 0x07: return e2k_state_psp_hi(env); /* %psp.hi */
case 0x09: return e2k_state_psp_lo(env); /* %psp.lo */
case 0x0d: return e2k_state_pcsp_hi(env); /* %pcsp.hi */
case 0x0f: return e2k_state_pcsp_lo(env); /* %pcsp.lo */
case 0x13: return 0; /* %pcshtp */
case 0x2c: return env->usd.hi; /* %usd.hi */
case 0x2d: return env->usd.lo; /* %usd.lo */

View File

@ -1150,7 +1150,9 @@ static void e2k_tr_translate_insn(DisasContextBase *db, CPUState *cs)
gen_helper_syscall(cpu_env);
tcg_gen_exit_tb(NULL, TB_EXIT_IDX0);
break;
case E2K_SYSRET_ADDR: {
case E2K_SYSRET_BACKTRACE_ADDR:
case E2K_SYSRET_ADDR:
{
/* fake return from syscall handler */
TCGv_i32 t0 = tcg_const_i32(0);
@ -1170,7 +1172,8 @@ static void e2k_tr_translate_insn(DisasContextBase *db, CPUState *cs)
tcg_gen_exit_tb(NULL, TB_EXIT_IDX0);
break;
#endif /* CONFIG_USER_ONLY */
default: {
default:
{
pc_next = do_decode(ctx, cs);
#ifdef CONFIG_USER_ONLY
if (ctx->bundle2.cs1.type == CS1_CALL) {

View File

@ -1617,6 +1617,7 @@ static inline void gen_rr_i32(Instr *instr)
TCGv_i32 t0 = tcg_const_i32(instr->src1);
gen_save_cpu_state(instr->ctx);
// FIXME: output size should be affected by wdbl
gen_helper_state_reg_read_i32(dst, cpu_env, t0);
set_al_result_reg32(instr, dst);
tcg_temp_free_i32(t0);