2020-11-10 13:06:44 +01:00
|
|
|
#include "qemu/osdep.h"
|
2020-11-13 15:49:28 +01:00
|
|
|
#include "qemu/log.h"
|
2020-11-10 13:06:44 +01:00
|
|
|
#include "cpu.h"
|
|
|
|
#include "exec/exec-all.h"
|
|
|
|
#include "qemu/host-utils.h"
|
|
|
|
#include "exec/helper-proto.h"
|
2020-11-12 14:52:51 +01:00
|
|
|
#include "translate.h"
|
2020-11-10 13:06:44 +01:00
|
|
|
|
2020-11-17 19:58:22 +01:00
|
|
|
static inline void reset_ctprs(CPUE2KState *env)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
2020-11-22 08:37:45 +01:00
|
|
|
for (i = 0; i < 3; i++) {
|
2020-11-22 10:44:28 +01:00
|
|
|
env->ctprs[i] = SET_FIELD(env->ctprs[i], CTPR_TAG_NONE,
|
|
|
|
CTPR_TAG_OFF, CTPR_TAG_LEN);
|
2020-11-17 19:58:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-26 21:02:34 +01:00
|
|
|
static inline void save_proc_chain_info(CPUE2KState *env, uint64_t buf[4],
|
|
|
|
int wbs)
|
2020-11-26 20:14:40 +01:00
|
|
|
{
|
|
|
|
env->cr1.br = e2k_state_br(env);
|
|
|
|
env->cr1.wpsz = env->wd.psize / 2;
|
|
|
|
|
|
|
|
buf[0] = env->cr0_lo;
|
|
|
|
buf[1] = env->cr0_hi;
|
|
|
|
buf[2] = e2k_state_cr1_lo(env);
|
|
|
|
buf[3] = e2k_state_cr1_hi(env);
|
2020-11-26 21:02:34 +01:00
|
|
|
|
|
|
|
env->cr1.wfx = env->wd.fx;
|
|
|
|
env->cr1.wbs = wbs;
|
|
|
|
env->wd.base = (env->wd.base + wbs * 2) % WREGS_SIZE;
|
|
|
|
env->wd.psize = env->wd.size -= wbs * 2;
|
2020-11-26 20:14:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void restore_proc_chain_info(CPUE2KState *env, uint64_t buf[4])
|
|
|
|
{
|
2020-11-26 21:02:34 +01:00
|
|
|
env->wd.fx = env->cr1.wfx;
|
|
|
|
|
2020-11-26 20:14:40 +01:00
|
|
|
e2k_state_cr1_hi_set(env, buf[3]);
|
|
|
|
e2k_state_cr1_lo_set(env, buf[2]);
|
|
|
|
env->cr0_hi = buf[1];
|
|
|
|
env->cr0_lo = buf[0];
|
|
|
|
|
|
|
|
env->wd.psize = env->cr1.wpsz * 2;
|
|
|
|
e2k_state_br_set(env, env->cr1.br);
|
|
|
|
}
|
|
|
|
|
2020-11-23 07:14:26 +01:00
|
|
|
static void pcs_push(CPUE2KState *env, int wbs)
|
2020-11-15 17:39:30 +01:00
|
|
|
{
|
2020-11-26 20:14:40 +01:00
|
|
|
uint64_t buf[4];
|
2020-11-15 17:39:30 +01:00
|
|
|
|
2020-11-26 20:14:40 +01:00
|
|
|
if (env->pcsp.size < (env->pcsp.index + 32)) {
|
2020-11-26 17:31:25 +01:00
|
|
|
helper_raise_exception(env, E2K_EXCP_MAPERR);
|
|
|
|
return;
|
2020-11-15 17:39:30 +01:00
|
|
|
}
|
|
|
|
|
2020-11-26 21:02:34 +01:00
|
|
|
save_proc_chain_info(env, buf, wbs);
|
2020-11-26 20:14:40 +01:00
|
|
|
memcpy(env->pcsp.base + env->pcsp.index, buf, 32);
|
|
|
|
env->pcsp.index += 32;
|
2020-11-15 17:39:30 +01:00
|
|
|
}
|
|
|
|
|
2020-11-23 07:14:26 +01:00
|
|
|
static void pcs_pop(CPUE2KState *env)
|
2020-11-15 17:39:30 +01:00
|
|
|
{
|
2020-11-26 20:14:40 +01:00
|
|
|
uint64_t buf[4];
|
2020-11-15 17:39:30 +01:00
|
|
|
|
2020-11-26 20:14:40 +01:00
|
|
|
if (env->pcsp.index < 32) {
|
2020-11-26 17:31:25 +01:00
|
|
|
helper_raise_exception(env, E2K_EXCP_MAPERR);
|
|
|
|
return;
|
2020-11-15 17:39:30 +01:00
|
|
|
}
|
|
|
|
|
2020-11-26 20:14:40 +01:00
|
|
|
env->pcsp.index -= 32;
|
|
|
|
memcpy(buf, env->pcsp.base + env->pcsp.index, 32);
|
|
|
|
restore_proc_chain_info(env, buf);
|
2020-11-16 15:26:39 +01:00
|
|
|
}
|
|
|
|
|
2020-11-26 16:02:49 +01:00
|
|
|
static void ps_push_nfx(CPUE2KState *env, unsigned int base, size_t len)
|
2020-11-16 15:26:39 +01:00
|
|
|
{
|
|
|
|
unsigned int i;
|
2020-11-26 16:02:49 +01:00
|
|
|
size_t size = len * sizeof(uint64_t);
|
|
|
|
uint64_t *p;
|
2020-11-16 15:26:39 +01:00
|
|
|
|
2020-11-26 16:02:49 +01:00
|
|
|
if (env->psp.size < (env->psp.index + size)) {
|
|
|
|
helper_raise_exception(env, E2K_EXCP_MAPERR);
|
|
|
|
return;
|
|
|
|
}
|
2020-11-16 15:26:39 +01:00
|
|
|
|
2020-11-26 16:02:49 +01:00
|
|
|
p = (uint64_t *) (env->psp.base + env->psp.index);
|
2020-11-16 15:26:39 +01:00
|
|
|
for (i = 0; i < len; i++) {
|
2020-11-26 16:02:49 +01:00
|
|
|
int idx = (base + i) % WREGS_SIZE;
|
|
|
|
memcpy(p + i, &env->wregs[idx], sizeof(uint64_t));
|
2020-11-16 15:26:39 +01:00
|
|
|
}
|
|
|
|
|
2020-11-26 16:02:49 +01:00
|
|
|
env->psp.index += size;
|
2020-11-16 15:26:39 +01:00
|
|
|
}
|
|
|
|
|
2020-11-26 16:02:49 +01:00
|
|
|
static void ps_pop_nfx(CPUE2KState *env, unsigned int base, size_t len)
|
2020-11-16 15:26:39 +01:00
|
|
|
{
|
|
|
|
unsigned int i;
|
2020-11-26 16:02:49 +01:00
|
|
|
size_t size = len * sizeof(uint64_t);
|
|
|
|
uint64_t *p;
|
2020-11-16 15:26:39 +01:00
|
|
|
|
2020-11-26 16:02:49 +01:00
|
|
|
if (env->psp.index < size) {
|
|
|
|
// TODO: check where to raise exception
|
|
|
|
helper_raise_exception(env, E2K_EXCP_MAPERR);
|
|
|
|
return;
|
|
|
|
}
|
2020-11-16 15:26:39 +01:00
|
|
|
|
2020-11-26 16:02:49 +01:00
|
|
|
env->psp.index -= size;
|
|
|
|
p = (uint64_t *) (env->psp.base + env->psp.index);
|
2020-11-16 15:26:39 +01:00
|
|
|
for (i = 0; i < len; i++) {
|
2020-11-26 16:02:49 +01:00
|
|
|
int idx = (base + i) % WREGS_SIZE;
|
|
|
|
memcpy(&env->wregs[idx], p + i, sizeof(uint64_t));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ps_push_fx(CPUE2KState *env, unsigned int base, size_t len)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
size_t size = len * 2 * sizeof(uint64_t);
|
|
|
|
uint64_t *p, zeros[2] = { 0 };
|
|
|
|
|
|
|
|
if (env->psp.size < (env->psp.index + size)) {
|
|
|
|
helper_raise_exception(env, E2K_EXCP_MAPERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = (uint64_t *) (env->psp.base + env->psp.index);
|
|
|
|
for (i = 0; i < len; i += 2) {
|
|
|
|
int idx = (base + i) % WREGS_SIZE;
|
|
|
|
memcpy(p + i * 2, &env->wregs[idx], 2 * sizeof(uint64_t));
|
|
|
|
// TODO: save fx part
|
|
|
|
memcpy(p + i * 2 + 2, zeros, 2 * sizeof(uint64_t));
|
|
|
|
}
|
|
|
|
|
|
|
|
env->psp.index += size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ps_pop_fx(CPUE2KState *env, unsigned int base, size_t len)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
size_t size = len * 2 * sizeof(uint64_t);
|
|
|
|
uint64_t *p;
|
|
|
|
|
|
|
|
if (env->psp.index < size) {
|
|
|
|
// TODO: check where to raise exception
|
|
|
|
helper_raise_exception(env, E2K_EXCP_MAPERR);
|
|
|
|
return;
|
2020-11-16 15:26:39 +01:00
|
|
|
}
|
|
|
|
|
2020-11-26 16:02:49 +01:00
|
|
|
env->psp.index -= size;
|
|
|
|
p = (uint64_t *) (env->psp.base + env->psp.index);
|
|
|
|
for (i = 0; i < len; i += 2) {
|
|
|
|
int idx = (base + i) % WREGS_SIZE;
|
|
|
|
memcpy(&env->wregs[idx], p + i * 2, sizeof(uint64_t));
|
|
|
|
// TODO: restore fx part
|
|
|
|
}
|
2020-11-15 17:39:30 +01:00
|
|
|
}
|
|
|
|
|
2020-11-26 21:02:34 +01:00
|
|
|
static inline void do_call(CPUE2KState *env, int wbs)
|
2020-11-12 14:52:51 +01:00
|
|
|
{
|
2020-11-23 07:14:26 +01:00
|
|
|
env->ip = env->nip;
|
2020-11-26 21:02:34 +01:00
|
|
|
ps_push_nfx(env, env->wd.base, wbs * 2);
|
|
|
|
pcs_push(env, wbs);
|
2020-11-17 19:58:22 +01:00
|
|
|
reset_ctprs(env);
|
2020-11-15 16:05:27 +01:00
|
|
|
}
|
|
|
|
|
2020-11-23 07:14:26 +01:00
|
|
|
void helper_return(CPUE2KState *env)
|
2020-11-15 16:05:27 +01:00
|
|
|
{
|
2020-11-23 07:14:26 +01:00
|
|
|
uint32_t new_wd_size, new_wd_base, wbs;
|
2020-11-16 15:26:39 +01:00
|
|
|
|
2020-11-26 20:14:40 +01:00
|
|
|
wbs = env->cr1.wbs;
|
2020-11-26 19:35:07 +01:00
|
|
|
new_wd_size = env->wd.psize + wbs * 2;
|
|
|
|
new_wd_base = (env->wd.base - wbs * 2) % WREGS_SIZE;
|
2020-11-15 16:05:27 +01:00
|
|
|
|
2020-11-26 19:35:07 +01:00
|
|
|
if (env->wd.base < new_wd_base) {
|
|
|
|
env->wd.base += WREGS_SIZE;
|
2020-11-22 10:44:28 +01:00
|
|
|
}
|
2020-11-15 16:05:27 +01:00
|
|
|
|
2020-11-26 19:35:07 +01:00
|
|
|
ps_pop_nfx(env, new_wd_base, env->wd.base - new_wd_base);
|
2020-11-23 07:14:26 +01:00
|
|
|
pcs_pop(env);
|
2020-11-16 15:26:39 +01:00
|
|
|
|
2020-11-26 19:35:07 +01:00
|
|
|
env->wd.base = new_wd_base;
|
|
|
|
env->wd.size = new_wd_size;
|
2020-11-16 15:26:39 +01:00
|
|
|
|
2020-11-17 19:58:22 +01:00
|
|
|
reset_ctprs(env);
|
2020-11-15 16:05:27 +01:00
|
|
|
}
|
|
|
|
|
2020-11-17 19:58:22 +01:00
|
|
|
static inline void do_syscall(CPUE2KState *env, int call_wbs)
|
2020-11-16 15:26:39 +01:00
|
|
|
{
|
2020-11-23 12:35:04 +01:00
|
|
|
env->syscall_wbs = call_wbs;
|
2020-11-17 19:58:22 +01:00
|
|
|
reset_ctprs(env);
|
2020-11-23 12:35:04 +01:00
|
|
|
helper_raise_exception(env, E2K_EXCP_SYSCALL);
|
2020-11-15 16:05:27 +01:00
|
|
|
}
|
|
|
|
|
2020-11-19 21:59:16 +01:00
|
|
|
target_ulong helper_call(CPUE2KState *env, uint64_t ctpr,
|
2020-11-17 19:58:22 +01:00
|
|
|
int call_wbs)
|
|
|
|
{
|
2020-11-23 21:26:47 +01:00
|
|
|
int ctpr_tag = GET_FIELD(ctpr, CTPR_TAG_OFF, CTPR_TAG_LEN);
|
2020-11-17 19:58:22 +01:00
|
|
|
|
|
|
|
switch (ctpr_tag) {
|
|
|
|
case CTPR_TAG_DISP:
|
|
|
|
do_call(env, call_wbs);
|
2020-11-23 21:26:47 +01:00
|
|
|
return GET_FIELD(ctpr, CTPR_BASE_OFF, CTPR_BASE_LEN);
|
2020-11-17 19:58:22 +01:00
|
|
|
case CTPR_TAG_SDISP:
|
|
|
|
do_syscall(env, call_wbs);
|
2020-11-23 07:14:26 +01:00
|
|
|
return env->ip;
|
2020-11-17 19:58:22 +01:00
|
|
|
default:
|
2020-11-24 21:50:33 +01:00
|
|
|
abort();
|
|
|
|
break;
|
2020-11-23 07:14:26 +01:00
|
|
|
}
|
2020-11-24 21:50:33 +01:00
|
|
|
|
|
|
|
return 0;
|
2020-11-23 07:14:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void helper_raise_exception(CPUE2KState *env, int tt)
|
|
|
|
{
|
|
|
|
CPUState *cs = env_cpu(env);
|
|
|
|
cs->exception_index = tt;
|
|
|
|
cpu_loop_exit(cs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void break_save_state(CPUE2KState *env)
|
|
|
|
{
|
2020-11-26 16:02:49 +01:00
|
|
|
int wbs;
|
|
|
|
|
2020-11-26 19:35:07 +01:00
|
|
|
wbs = env->wd.size / 2;
|
|
|
|
ps_push_fx(env, env->wd.base, env->wd.size);
|
2020-11-26 16:02:49 +01:00
|
|
|
pcs_push(env, wbs);
|
2020-11-23 07:14:26 +01:00
|
|
|
|
2020-11-26 19:35:07 +01:00
|
|
|
env->wd.base = (env->wd.base + env->wd.size) % WREGS_SIZE;
|
|
|
|
env->wd.size = 0;
|
|
|
|
env->wd.psize = 0;
|
2020-11-23 07:14:26 +01:00
|
|
|
|
|
|
|
env->is_bp = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void helper_break_restore_state(CPUE2KState *env)
|
|
|
|
{
|
2020-11-26 20:14:40 +01:00
|
|
|
int wbs = env->cr1.wbs;
|
2020-11-23 07:14:26 +01:00
|
|
|
|
|
|
|
pcs_pop(env);
|
2020-11-26 19:35:07 +01:00
|
|
|
env->wd.size = wbs * 2;
|
|
|
|
env->wd.base = (env->wd.base - env->wd.size) % WREGS_SIZE;
|
|
|
|
ps_pop_fx(env, env->wd.base, env->wd.size);
|
2020-11-23 07:14:26 +01:00
|
|
|
|
|
|
|
env->is_bp = false;
|
2020-11-15 16:05:27 +01:00
|
|
|
}
|
|
|
|
|
2020-11-12 22:46:57 +01:00
|
|
|
uint64_t helper_sxt(uint64_t x, uint64_t y)
|
|
|
|
{
|
|
|
|
int size;
|
|
|
|
|
|
|
|
switch (x & 3) {
|
|
|
|
case 0:
|
|
|
|
size = 8;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
size = 16;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
size = 32;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (x & 4) {
|
2020-11-13 10:53:21 +01:00
|
|
|
return y & GEN_MASK(0, size);
|
2020-11-12 22:46:57 +01:00
|
|
|
} else {
|
|
|
|
return (((int64_t) y) << (64 - size) >> (64 - size));
|
|
|
|
}
|
|
|
|
}
|
2020-11-13 15:49:28 +01:00
|
|
|
|
|
|
|
void helper_debug_i32(uint32_t x)
|
|
|
|
{
|
|
|
|
qemu_log_mask(LOG_UNIMP, "log %#x\n", x);
|
|
|
|
}
|
|
|
|
|
|
|
|
void helper_debug_i64(uint64_t x)
|
|
|
|
{
|
|
|
|
qemu_log_mask(LOG_UNIMP, "log %#lx\n", x);
|
|
|
|
}
|
2020-11-15 16:05:27 +01:00
|
|
|
|
|
|
|
uint64_t helper_state_reg_get(CPUE2KState *env, int reg)
|
|
|
|
{
|
|
|
|
switch (reg) {
|
|
|
|
case 0x2c: /* %usd.hi */
|
|
|
|
return env->usd_hi;
|
|
|
|
case 0x2d: /* %usd.lo */
|
|
|
|
return env->usd_lo;
|
2020-11-23 21:48:58 +01:00
|
|
|
case 0x80: /* %upsr */
|
|
|
|
return env->upsr;
|
2020-11-23 13:59:33 +01:00
|
|
|
case 0x81: /* %ip */
|
|
|
|
return env->ip;
|
2020-11-15 16:05:27 +01:00
|
|
|
case 0x83: /* %lsr */
|
|
|
|
return env->lsr;
|
2020-11-23 23:02:11 +01:00
|
|
|
case 0x8a: /* %idr */
|
|
|
|
return env->idr;
|
2020-11-24 10:19:33 +01:00
|
|
|
case 0x90: /* %clkr */
|
|
|
|
return cpu_get_host_ticks();
|
2020-11-15 16:05:27 +01:00
|
|
|
default:
|
|
|
|
/* TODO: exception */
|
2020-11-23 13:59:33 +01:00
|
|
|
qemu_log_mask(LOG_UNIMP, "unknown register 0x%x\n", reg);
|
2020-11-15 16:05:27 +01:00
|
|
|
abort();
|
2020-11-23 13:59:33 +01:00
|
|
|
return 0; /* unreachable */
|
2020-11-15 16:05:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void helper_state_reg_set(CPUE2KState *env, int reg, uint64_t val)
|
|
|
|
{
|
|
|
|
switch (reg) {
|
|
|
|
case 0x2c: /* %usd.hi */
|
|
|
|
/* FIXME: user cannot write */
|
|
|
|
env->usd_hi = val;
|
|
|
|
break;
|
|
|
|
case 0x2d: /* %usd.lo */
|
|
|
|
/* FIXME: user cannot write */
|
|
|
|
env->usd_lo = val;
|
|
|
|
break;
|
2020-11-23 21:48:58 +01:00
|
|
|
case 0x80: /* %upsr */
|
|
|
|
env->upsr = val;
|
|
|
|
break;
|
2020-11-15 16:05:27 +01:00
|
|
|
case 0x83: /* %lsr */
|
|
|
|
env->lsr = val;
|
|
|
|
break;
|
|
|
|
default:
|
2020-11-23 13:59:33 +01:00
|
|
|
qemu_log_mask(LOG_UNIMP, "unknown register 0x%x\n", reg);
|
2020-11-23 07:14:26 +01:00
|
|
|
helper_raise_exception(env, E2K_EXCP_ILLOPC);
|
2020-11-15 16:05:27 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-15 19:25:40 +01:00
|
|
|
uint64_t helper_getsp(CPUE2KState *env, uint64_t src2) {
|
2020-11-23 21:26:47 +01:00
|
|
|
uint64_t base = GET_FIELD(env->usd_lo, USD_LO_BASE_OFF, USD_LO_BASE_LEN);
|
2020-11-15 19:25:40 +01:00
|
|
|
|
|
|
|
base += src2;
|
|
|
|
|
|
|
|
/* TODO: stack overflow */
|
2020-11-16 15:26:39 +01:00
|
|
|
env->usd_lo = SET_FIELD(env->usd_lo, base, USD_LO_BASE_OFF,
|
|
|
|
USD_LO_BASE_LEN);
|
2020-11-15 19:25:40 +01:00
|
|
|
|
|
|
|
return base;
|
|
|
|
}
|
2020-11-26 21:02:34 +01:00
|
|
|
|
|
|
|
void helper_setwd(CPUE2KState *env, uint32_t lts)
|
|
|
|
{
|
|
|
|
int wsz = extract32(lts, 5, 7);
|
|
|
|
bool nfx = extract32(lts, 4, 1);
|
|
|
|
|
|
|
|
env->wd.size = wsz * 2;
|
|
|
|
env->wd.fx = nfx == 0;
|
|
|
|
|
|
|
|
if (env->version >= 3) {
|
|
|
|
bool dbl = extract32(lts, 3, 1);
|
|
|
|
// TODO: set dbl
|
|
|
|
if (dbl != false) {
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|