diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h index cae663706b..7e662f5402 100644 --- a/target-xtensa/cpu.h +++ b/target-xtensa/cpu.h @@ -108,6 +108,8 @@ enum { enum { SAR = 3, SCOMPARE1 = 12, + WINDOW_BASE = 72, + WINDOW_START = 73, EPC1 = 177, DEPC = 192, EXCSAVE1 = 209, @@ -134,6 +136,8 @@ enum { #define PS_WOE 0x40000 +#define MAX_NAREG 64 + enum { /* Static vectors */ EXC_RESET, @@ -185,6 +189,7 @@ enum { typedef struct XtensaConfig { const char *name; uint64_t options; + unsigned nareg; int excm_level; int ndepc; uint32_t exception_vector[EXC_MAX]; @@ -196,6 +201,7 @@ typedef struct CPUXtensaState { uint32_t pc; uint32_t sregs[256]; uint32_t uregs[256]; + uint32_t phys_regs[MAX_NAREG]; int exception_taken; @@ -214,6 +220,8 @@ int cpu_xtensa_exec(CPUXtensaState *s); void do_interrupt(CPUXtensaState *s); int cpu_xtensa_signal_handler(int host_signum, void *pinfo, void *puc); void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf); +void xtensa_sync_window_from_phys(CPUState *env); +void xtensa_sync_phys_from_window(CPUState *env); #define XTENSA_OPTION_BIT(opt) (((uint64_t)1) << (opt)) diff --git a/target-xtensa/helper.c b/target-xtensa/helper.c index 44ebb9f29d..4f8693448e 100644 --- a/target-xtensa/helper.c +++ b/target-xtensa/helper.c @@ -45,6 +45,7 @@ static const XtensaConfig core_config[] = { { .name = "sample-xtensa-core", .options = -1, + .nareg = 64, .ndepc = 1, .excm_level = 16, .exception_vector = { diff --git a/target-xtensa/helpers.h b/target-xtensa/helpers.h index 6329c43ff6..0971fde8da 100644 --- a/target-xtensa/helpers.h +++ b/target-xtensa/helpers.h @@ -5,5 +5,13 @@ DEF_HELPER_2(exception_cause, void, i32, i32) DEF_HELPER_3(exception_cause_vaddr, void, i32, i32, i32) DEF_HELPER_1(nsa, i32, i32) DEF_HELPER_1(nsau, i32, i32) +DEF_HELPER_1(wsr_windowbase, void, i32) +DEF_HELPER_3(entry, void, i32, i32, i32) +DEF_HELPER_1(retw, i32, i32) +DEF_HELPER_1(rotw, void, i32) +DEF_HELPER_2(window_check, void, i32, i32) +DEF_HELPER_0(restore_owb, void) +DEF_HELPER_1(movsp, void, i32) +DEF_HELPER_0(dump_state, void) #include "def-helper.h" diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c index 794a8340ca..7c3fb88a96 100644 --- a/target-xtensa/op_helper.c +++ b/target-xtensa/op_helper.c @@ -100,3 +100,195 @@ uint32_t HELPER(nsau)(uint32_t v) { return v ? clz32(v) : 32; } + +static void copy_window_from_phys(CPUState *env, + uint32_t window, uint32_t phys, uint32_t n) +{ + assert(phys < env->config->nareg); + if (phys + n <= env->config->nareg) { + memcpy(env->regs + window, env->phys_regs + phys, + n * sizeof(uint32_t)); + } else { + uint32_t n1 = env->config->nareg - phys; + memcpy(env->regs + window, env->phys_regs + phys, + n1 * sizeof(uint32_t)); + memcpy(env->regs + window + n1, env->phys_regs, + (n - n1) * sizeof(uint32_t)); + } +} + +static void copy_phys_from_window(CPUState *env, + uint32_t phys, uint32_t window, uint32_t n) +{ + assert(phys < env->config->nareg); + if (phys + n <= env->config->nareg) { + memcpy(env->phys_regs + phys, env->regs + window, + n * sizeof(uint32_t)); + } else { + uint32_t n1 = env->config->nareg - phys; + memcpy(env->phys_regs + phys, env->regs + window, + n1 * sizeof(uint32_t)); + memcpy(env->phys_regs, env->regs + window + n1, + (n - n1) * sizeof(uint32_t)); + } +} + + +static inline unsigned windowbase_bound(unsigned a, const CPUState *env) +{ + return a & (env->config->nareg / 4 - 1); +} + +static inline unsigned windowstart_bit(unsigned a, const CPUState *env) +{ + return 1 << windowbase_bound(a, env); +} + +void xtensa_sync_window_from_phys(CPUState *env) +{ + copy_window_from_phys(env, 0, env->sregs[WINDOW_BASE] * 4, 16); +} + +void xtensa_sync_phys_from_window(CPUState *env) +{ + copy_phys_from_window(env, env->sregs[WINDOW_BASE] * 4, 0, 16); +} + +static void rotate_window_abs(uint32_t position) +{ + xtensa_sync_phys_from_window(env); + env->sregs[WINDOW_BASE] = windowbase_bound(position, env); + xtensa_sync_window_from_phys(env); +} + +static void rotate_window(uint32_t delta) +{ + rotate_window_abs(env->sregs[WINDOW_BASE] + delta); +} + +void HELPER(wsr_windowbase)(uint32_t v) +{ + rotate_window_abs(v); +} + +void HELPER(entry)(uint32_t pc, uint32_t s, uint32_t imm) +{ + int callinc = (env->sregs[PS] & PS_CALLINC) >> PS_CALLINC_SHIFT; + if (s > 3 || ((env->sregs[PS] & (PS_WOE | PS_EXCM)) ^ PS_WOE) != 0) { + qemu_log("Illegal entry instruction(pc = %08x), PS = %08x\n", + pc, env->sregs[PS]); + HELPER(exception_cause)(pc, ILLEGAL_INSTRUCTION_CAUSE); + } else { + env->regs[(callinc << 2) | (s & 3)] = env->regs[s] - (imm << 3); + rotate_window(callinc); + env->sregs[WINDOW_START] |= + windowstart_bit(env->sregs[WINDOW_BASE], env); + } +} + +void HELPER(window_check)(uint32_t pc, uint32_t w) +{ + uint32_t windowbase = windowbase_bound(env->sregs[WINDOW_BASE], env); + uint32_t windowstart = env->sregs[WINDOW_START]; + uint32_t m, n; + + if ((env->sregs[PS] & (PS_WOE | PS_EXCM)) ^ PS_WOE) { + return; + } + + for (n = 1; ; ++n) { + if (n > w) { + return; + } + if (windowstart & windowstart_bit(windowbase + n, env)) { + break; + } + } + + m = windowbase_bound(windowbase + n, env); + rotate_window(n); + env->sregs[PS] = (env->sregs[PS] & ~PS_OWB) | + (windowbase << PS_OWB_SHIFT) | PS_EXCM; + env->sregs[EPC1] = env->pc = pc; + + if (windowstart & windowstart_bit(m + 1, env)) { + HELPER(exception)(EXC_WINDOW_OVERFLOW4); + } else if (windowstart & windowstart_bit(m + 2, env)) { + HELPER(exception)(EXC_WINDOW_OVERFLOW8); + } else { + HELPER(exception)(EXC_WINDOW_OVERFLOW12); + } +} + +uint32_t HELPER(retw)(uint32_t pc) +{ + int n = (env->regs[0] >> 30) & 0x3; + int m = 0; + uint32_t windowbase = windowbase_bound(env->sregs[WINDOW_BASE], env); + uint32_t windowstart = env->sregs[WINDOW_START]; + uint32_t ret_pc = 0; + + if (windowstart & windowstart_bit(windowbase - 1, env)) { + m = 1; + } else if (windowstart & windowstart_bit(windowbase - 2, env)) { + m = 2; + } else if (windowstart & windowstart_bit(windowbase - 3, env)) { + m = 3; + } + + if (n == 0 || (m != 0 && m != n) || + ((env->sregs[PS] & (PS_WOE | PS_EXCM)) ^ PS_WOE) != 0) { + qemu_log("Illegal retw instruction(pc = %08x), " + "PS = %08x, m = %d, n = %d\n", + pc, env->sregs[PS], m, n); + HELPER(exception_cause)(pc, ILLEGAL_INSTRUCTION_CAUSE); + } else { + int owb = windowbase; + + ret_pc = (pc & 0xc0000000) | (env->regs[0] & 0x3fffffff); + + rotate_window(-n); + if (windowstart & windowstart_bit(env->sregs[WINDOW_BASE], env)) { + env->sregs[WINDOW_START] &= ~windowstart_bit(owb, env); + } else { + /* window underflow */ + env->sregs[PS] = (env->sregs[PS] & ~PS_OWB) | + (windowbase << PS_OWB_SHIFT) | PS_EXCM; + env->sregs[EPC1] = env->pc = pc; + + if (n == 1) { + HELPER(exception)(EXC_WINDOW_UNDERFLOW4); + } else if (n == 2) { + HELPER(exception)(EXC_WINDOW_UNDERFLOW8); + } else if (n == 3) { + HELPER(exception)(EXC_WINDOW_UNDERFLOW12); + } + } + } + return ret_pc; +} + +void HELPER(rotw)(uint32_t imm4) +{ + rotate_window(imm4); +} + +void HELPER(restore_owb)(void) +{ + rotate_window_abs((env->sregs[PS] & PS_OWB) >> PS_OWB_SHIFT); +} + +void HELPER(movsp)(uint32_t pc) +{ + if ((env->sregs[WINDOW_START] & + (windowstart_bit(env->sregs[WINDOW_BASE] - 3, env) | + windowstart_bit(env->sregs[WINDOW_BASE] - 2, env) | + windowstart_bit(env->sregs[WINDOW_BASE] - 1, env))) == 0) { + HELPER(exception_cause)(pc, ALLOCA_CAUSE); + } +} + +void HELPER(dump_state)(void) +{ + cpu_dump_state(env, stderr, fprintf, 0); +} diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c index bc04a108b9..7a2e07ff60 100644 --- a/target-xtensa/translate.c +++ b/target-xtensa/translate.c @@ -67,6 +67,8 @@ static TCGv_i32 cpu_UR[256]; static const char * const sregnames[256] = { [SAR] = "SAR", [SCOMPARE1] = "SCOMPARE1", + [WINDOW_BASE] = "WINDOW_BASE", + [WINDOW_START] = "WINDOW_START", [EPC1] = "EPC1", [DEPC] = "DEPC", [EXCSAVE1] = "EXCSAVE1", @@ -217,6 +219,34 @@ static void gen_jumpi(DisasContext *dc, uint32_t dest, int slot) tcg_temp_free(tmp); } +static void gen_callw_slot(DisasContext *dc, int callinc, TCGv_i32 dest, + int slot) +{ + TCGv_i32 tcallinc = tcg_const_i32(callinc); + + tcg_gen_deposit_i32(cpu_SR[PS], cpu_SR[PS], + tcallinc, PS_CALLINC_SHIFT, PS_CALLINC_LEN); + tcg_temp_free(tcallinc); + tcg_gen_movi_i32(cpu_R[callinc << 2], + (callinc << 30) | (dc->next_pc & 0x3fffffff)); + gen_jump_slot(dc, dest, slot); +} + +static void gen_callw(DisasContext *dc, int callinc, TCGv_i32 dest) +{ + gen_callw_slot(dc, callinc, dest, -1); +} + +static void gen_callwi(DisasContext *dc, int callinc, uint32_t dest, int slot) +{ + TCGv_i32 tmp = tcg_const_i32(dest); + if (((dc->pc ^ dest) & TARGET_PAGE_MASK) != 0) { + slot = -1; + } + gen_callw_slot(dc, callinc, tmp, slot); + tcg_temp_free(tmp); +} + static void gen_brcond(DisasContext *dc, TCGCond cond, TCGv_i32 t0, TCGv_i32 t1, uint32_t offset) { @@ -263,6 +293,11 @@ static void gen_wsr_sar(DisasContext *dc, uint32_t sr, TCGv_i32 s) dc->sar_m32_5bit = false; } +static void gen_wsr_windowbase(DisasContext *dc, uint32_t sr, TCGv_i32 v) +{ + gen_helper_wsr_windowbase(v); +} + static void gen_wsr_ps(DisasContext *dc, uint32_t sr, TCGv_i32 v) { uint32_t mask = PS_WOE | PS_CALLINC | PS_OWB | @@ -281,6 +316,7 @@ static void gen_wsr(DisasContext *dc, uint32_t sr, TCGv_i32 s) static void (* const wsr_handler[256])(DisasContext *dc, uint32_t sr, TCGv_i32 v) = { [SAR] = gen_wsr_sar, + [WINDOW_BASE] = gen_wsr_windowbase, [PS] = gen_wsr_ps, }; @@ -429,7 +465,12 @@ static void disas_xtensa_insn(DisasContext *dc) case 1: /*RETWw*/ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); - TBD(); + { + TCGv_i32 tmp = tcg_const_i32(dc->pc); + gen_helper_retw(tmp, tmp); + gen_jump(dc, tmp); + tcg_temp_free(tmp); + } break; case 3: /*reserved*/ @@ -454,7 +495,13 @@ static void disas_xtensa_insn(DisasContext *dc) case 2: /*CALLX8w*/ case 3: /*CALLX12w*/ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); - TBD(); + { + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_mov_i32(tmp, cpu_R[CALLX_S]); + gen_callw(dc, CALLX_N, tmp); + tcg_temp_free(tmp); + } break; } break; @@ -463,7 +510,12 @@ static void disas_xtensa_insn(DisasContext *dc) case 1: /*MOVSPw*/ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); - TBD(); + { + TCGv_i32 pc = tcg_const_i32(dc->pc); + gen_helper_movsp(pc); + tcg_gen_mov_i32(cpu_R[RRR_T], cpu_R[RRR_S]); + tcg_temp_free(pc); + } break; case 2: /*SYNC*/ @@ -523,7 +575,27 @@ static void disas_xtensa_insn(DisasContext *dc) case 4: /*RFWOw*/ case 5: /*RFWUw*/ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); - TBD(); + gen_check_privilege(dc); + { + TCGv_i32 tmp = tcg_const_i32(1); + + tcg_gen_andi_i32( + cpu_SR[PS], cpu_SR[PS], ~PS_EXCM); + tcg_gen_shl_i32(tmp, tmp, cpu_SR[WINDOW_BASE]); + + if (RRR_S == 4) { + tcg_gen_andc_i32(cpu_SR[WINDOW_START], + cpu_SR[WINDOW_START], tmp); + } else { + tcg_gen_or_i32(cpu_SR[WINDOW_START], + cpu_SR[WINDOW_START], tmp); + } + + gen_helper_restore_owb(); + gen_jump(dc, cpu_SR[EPC1]); + + tcg_temp_free(tmp); + } break; default: /*reserved*/ @@ -670,7 +742,13 @@ static void disas_xtensa_insn(DisasContext *dc) case 8: /*ROTWw*/ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); - TBD(); + gen_check_privilege(dc); + { + TCGv_i32 tmp = tcg_const_i32( + RRR_T | ((RRR_T & 8) ? 0xfffffff0 : 0)); + gen_helper_rotw(tmp); + tcg_temp_free(tmp); + } break; case 14: /*NSAu*/ @@ -1129,7 +1207,35 @@ static void disas_xtensa_insn(DisasContext *dc) break; case 9: /*LSC4*/ - TBD(); + switch (OP2) { + case 0: /*L32E*/ + HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); + gen_check_privilege(dc); + { + TCGv_i32 addr = tcg_temp_new_i32(); + tcg_gen_addi_i32(addr, cpu_R[RRR_S], + (0xffffffc0 | (RRR_R << 2))); + tcg_gen_qemu_ld32u(cpu_R[RRR_T], addr, dc->ring); + tcg_temp_free(addr); + } + break; + + case 4: /*S32E*/ + HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); + gen_check_privilege(dc); + { + TCGv_i32 addr = tcg_temp_new_i32(); + tcg_gen_addi_i32(addr, cpu_R[RRR_S], + (0xffffffc0 | (RRR_R << 2))); + tcg_gen_qemu_st32(cpu_R[RRR_T], addr, dc->ring); + tcg_temp_free(addr); + } + break; + + default: + RESERVED(); + break; + } break; case 10: /*FP0*/ @@ -1368,7 +1474,8 @@ static void disas_xtensa_insn(DisasContext *dc) case 2: /*CALL8w*/ case 3: /*CALL12w*/ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); - TBD(); + gen_callwi(dc, CALL_N, + (dc->pc & ~3) + (CALL_OFFSET_SE << 2) + 4, 0); break; } break; @@ -1411,7 +1518,15 @@ static void disas_xtensa_insn(DisasContext *dc) switch (BRI8_M) { case 0: /*ENTRYw*/ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); - TBD(); + { + TCGv_i32 pc = tcg_const_i32(dc->pc); + TCGv_i32 s = tcg_const_i32(BRI12_S); + TCGv_i32 imm = tcg_const_i32(BRI12_IMM12); + gen_helper_entry(pc, s, imm); + tcg_temp_free(imm); + tcg_temp_free(s); + tcg_temp_free(pc); + } break; case 1: /*B1*/ @@ -1576,7 +1691,12 @@ static void disas_xtensa_insn(DisasContext *dc) case 1: /*RETW.Nn*/ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); - TBD(); + { + TCGv_i32 tmp = tcg_const_i32(dc->pc); + gen_helper_retw(tmp, tmp); + gen_jump(dc, tmp); + tcg_temp_free(tmp); + } break; case 2: /*BREAK.Nn*/ @@ -1750,6 +1870,13 @@ void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf, cpu_fprintf(f, "A%02d=%08x%c", i, env->regs[i], (i % 4) == 3 ? '\n' : ' '); } + + cpu_fprintf(f, "\n"); + + for (i = 0; i < env->config->nareg; ++i) { + cpu_fprintf(f, "AR%02d=%08x%c", i, env->phys_regs[i], + (i % 4) == 3 ? '\n' : ' '); + } } void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos)