qemu-e2k/target/e2k/helper.c

335 lines
8.1 KiB
C
Raw Normal View History

#include "qemu/osdep.h"
2020-11-13 15:49:28 +01:00
#include "qemu/log.h"
#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"
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-23 07:14:26 +01:00
static inline void save_br_state(CPUE2KState *env)
2020-11-16 15:26:39 +01:00
{
2020-11-23 07:14:26 +01:00
int rbs, rsz, rcur;
2020-11-16 15:26:39 +01:00
2020-11-23 07:14:26 +01:00
rbs = env->boff / 2;
rsz = (env->bsize - 2) / 2;
rcur = env->bcur / 2;
2020-11-16 15:26:39 +01:00
2020-11-23 07:14:26 +01:00
env->br = SET_FIELD(env->br, rbs, BR_RBS_OFF, BR_RBS_LEN);
env->br = SET_FIELD(env->br, rsz, BR_RSZ_OFF, BR_RSZ_LEN);
env->br = SET_FIELD(env->br, rcur, BR_RCUR_OFF, BR_RCUR_LEN);
env->br = SET_FIELD(env->br, env->psize, BR_PSZ_OFF, BR_PSZ_LEN);
env->br = SET_FIELD(env->br, env->pcur, BR_PCUR_OFF, BR_PCUR_LEN);
2020-11-16 15:26:39 +01:00
2020-11-23 07:14:26 +01:00
e2k_state_cr1_br_set(env, env->br);
2020-11-16 15:26:39 +01:00
}
2020-11-22 10:44:28 +01:00
static inline void restore_br_state(CPUE2KState *env)
2020-11-16 15:26:39 +01:00
{
2020-11-23 07:14:26 +01:00
int rbs, rsz, rcur;
2020-11-16 15:26:39 +01:00
2020-11-23 07:14:26 +01:00
env->br = e2k_state_cr1_br_get(env);
rbs = GET_FIELD(env->br, BR_RBS_OFF, BR_RBS_LEN);
rsz = GET_FIELD(env->br, BR_RSZ_OFF, BR_RSZ_LEN);
rcur = GET_FIELD(env->br, BR_RCUR_OFF, BR_RCUR_LEN);
2020-11-16 15:26:39 +01:00
env->boff = rbs * 2;
env->bsize = rsz * 2 + 2;
env->bcur = rcur * 2;
env->psize = GET_FIELD(env->br, BR_PSZ_OFF, BR_PSZ_LEN);
env->pcur = GET_FIELD(env->br, BR_PCUR_OFF, BR_PCUR_LEN);
2020-11-16 15:26:39 +01:00
}
2020-11-23 07:14:26 +01:00
static void pcs_push(CPUE2KState *env, int wbs)
2020-11-15 17:39:30 +01:00
{
size_t size = e2k_state_pcs_size_get(env);
size_t offset = e2k_state_pcs_index_get(env);
2020-11-23 07:14:26 +01:00
void *pcsp = (void*) e2k_state_pcs_base_get(env) + offset;
2020-11-15 17:39:30 +01:00
if (offset + 32 > size) {
/* TODO: allocate more memory */
abort();
}
2020-11-23 07:14:26 +01:00
save_br_state(env);
e2k_state_cr1_wpsz_set(env, env->wd_psize / 2);
2020-11-23 12:35:04 +01:00
memcpy(pcsp, &env->proc_chain[0], 32);
2020-11-23 07:14:26 +01:00
e2k_state_cr1_wbs_set(env, wbs);
2020-11-15 17:39:30 +01:00
e2k_state_pcs_index_set(env, offset + 32);
}
2020-11-23 07:14:26 +01:00
static void pcs_pop(CPUE2KState *env)
2020-11-15 17:39:30 +01:00
{
size_t offset = e2k_state_pcs_index_get(env);
2020-11-23 07:14:26 +01:00
void *pcsp = (void*) e2k_state_pcs_base_get(env) + offset - 32;
2020-11-15 17:39:30 +01:00
if (offset < 32) {
/* TODO: SIGKILL */
abort();
}
2020-11-23 12:35:04 +01:00
memcpy(&env->proc_chain[0], pcsp, 32);
2020-11-15 17:39:30 +01:00
2020-11-23 07:14:26 +01:00
// TODO: restore wbs (after pshtp implemented)
env->wd_psize = e2k_state_cr1_wpsz_get(env) * 2;
restore_br_state(env);
2020-11-16 15:26:39 +01:00
2020-11-23 07:14:26 +01:00
e2k_state_pcs_index_set(env, offset - 32);
2020-11-16 15:26:39 +01:00
}
2020-11-23 07:14:26 +01:00
static void ps_push(CPUE2KState *env, unsigned int wd_base, size_t len)
2020-11-16 15:26:39 +01:00
{
unsigned int i;
size_t index = e2k_state_ps_ind_get(env);
uint64_t *p = (uint64_t*) (e2k_state_ps_base_get(env) + index);
/* TODO: push FX registers */
/* TODO: stack overflow */
for (i = 0; i < len; i++) {
2020-11-23 07:14:26 +01:00
uint64_t reg = env->wregs[(wd_base + i) % WREGS_SIZE];
2020-11-16 15:26:39 +01:00
memcpy(p + i, &reg, sizeof(uint64_t));
}
e2k_state_ps_ind_set(env, index + len * sizeof(uint64_t));
}
2020-11-23 07:14:26 +01:00
static void ps_pop(CPUE2KState *env, unsigned int wd_base, size_t len)
2020-11-16 15:26:39 +01:00
{
unsigned int i;
size_t index = e2k_state_ps_ind_get(env) - len * sizeof(uint64_t);
uint64_t *p = (uint64_t*) (e2k_state_ps_base_get(env) + index);
/* TODO: pop FX registers */
/* TODO: stack overflow */
for (i = 0; i < len; i++) {
2020-11-23 07:14:26 +01:00
uint64_t *reg = &env->wregs[(wd_base + i) % WREGS_SIZE];
2020-11-16 15:26:39 +01:00
memcpy(reg, p + i, sizeof(uint64_t));
}
e2k_state_ps_ind_set(env, index);
2020-11-15 17:39:30 +01:00
}
static inline void do_call(CPUE2KState *env, int call_wbs)
2020-11-12 14:52:51 +01:00
{
2020-11-22 10:44:28 +01:00
int call_wpsz = env->wd_size / 2 - call_wbs;
2020-11-15 17:39:30 +01:00
2020-11-23 07:14:26 +01:00
env->ip = env->nip;
pcs_push(env, call_wbs);
2020-11-22 10:44:28 +01:00
ps_push(env, env->wd_base, call_wbs * 2);
2020-11-22 10:44:28 +01:00
env->wd_base = (env->wd_base + call_wbs * 2) % WREGS_SIZE;
env->wd_size = env->wd_psize = call_wpsz * 2;
2020-11-15 17:39:30 +01:00
reset_ctprs(env);
}
2020-11-23 07:14:26 +01:00
void helper_return(CPUE2KState *env)
{
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-23 07:14:26 +01:00
wbs = e2k_state_cr1_wbs_get(env);
2020-11-22 10:44:28 +01:00
new_wd_size = env->wd_psize + wbs * 2;
new_wd_base = (env->wd_base - wbs * 2) % WREGS_SIZE;
2020-11-22 10:44:28 +01:00
if (env->wd_base < new_wd_base) {
env->wd_base += WREGS_SIZE;
}
2020-11-22 10:44:28 +01:00
ps_pop(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-22 10:44:28 +01:00
env->wd_base = new_wd_base;
env->wd_size = new_wd_size;
2020-11-16 15:26:39 +01:00
reset_ctprs(env);
}
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;
reset_ctprs(env);
2020-11-23 12:35:04 +01:00
helper_raise_exception(env, E2K_EXCP_SYSCALL);
}
target_ulong helper_call(CPUE2KState *env, uint64_t ctpr,
int call_wbs)
{
int ctpr_tag = GET_FIELD(ctpr, CTPR_TAG_OFF, CTPR_TAG_LEN);
switch (ctpr_tag) {
case CTPR_TAG_DISP:
do_call(env, call_wbs);
return GET_FIELD(ctpr, CTPR_BASE_OFF, CTPR_BASE_LEN);
case CTPR_TAG_SDISP:
do_syscall(env, call_wbs);
2020-11-23 07:14:26 +01:00
return env->ip;
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);
save_br_state(env);
cs->exception_index = tt;
cpu_loop_exit(cs);
}
static void break_save_ps(CPUE2KState *env)
{
unsigned int i;
unsigned int wd_base = env->wd_base;
unsigned int len = env->wd_size;
size_t index = e2k_state_ps_ind_get(env);
uint64_t *p = (uint64_t*) (e2k_state_ps_base_get(env) + index);
/* TODO: stack overflow */
for (i = 0; i < len; i += 2) {
uint64_t t[2] = { 0 };
uint64_t *reg = &env->wregs[(wd_base + i) % WREGS_SIZE];
memcpy(p + i * 2, reg, 2 * sizeof(uint64_t));
memcpy(p + i * 2 + 2, t, 2 * sizeof(uint64_t)); // TODO: fx part
}
2020-11-23 07:14:26 +01:00
e2k_state_ps_ind_set(env, index + len * 2 * sizeof(uint64_t));
}
static void break_save_state(CPUE2KState *env)
{
break_save_ps(env);
pcs_push(env, env->wd_size / 2);
env->wd_base = (env->wd_base + env->wd_size) % WREGS_SIZE;
env->wd_size = 0;
env->wd_psize = 0;
env->is_bp = true;
}
void helper_break_restore_state(CPUE2KState *env)
{
size_t index;
int wbs;
wbs = e2k_state_cr1_wbs_get(env);
pcs_pop(env);
env->wd_size = wbs * 2;
env->wd_base = (env->wd_base - env->wd_size) % WREGS_SIZE;
index = e2k_state_ps_ind_get(env);
e2k_state_ps_ind_set(env, index - env->wd_size * 2 * sizeof(uint64_t));
env->is_bp = false;
}
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) {
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);
}
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;
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();
default:
/* TODO: exception */
2020-11-23 13:59:33 +01:00
qemu_log_mask(LOG_UNIMP, "unknown register 0x%x\n", reg);
abort();
2020-11-23 13:59:33 +01:00
return 0; /* unreachable */
}
}
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;
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);
break;
}
}
2020-11-15 19:25:40 +01:00
uint64_t helper_getsp(CPUE2KState *env, uint64_t src2) {
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;
}