target-xtensa: implement exceptions
- mark privileged opcodes with ring check; - make debug exception on exception handler entry. Signed-off-by: Max Filippov <jcmvbkbc@gmail.com> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
This commit is contained in:
parent
f0a548b93d
commit
40643d7c0f
@ -489,6 +489,12 @@ int cpu_exec(CPUState *env)
|
|||||||
do_interrupt(env);
|
do_interrupt(env);
|
||||||
next_tb = 0;
|
next_tb = 0;
|
||||||
}
|
}
|
||||||
|
#elif defined(TARGET_XTENSA)
|
||||||
|
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
||||||
|
env->exception_index = EXC_IRQ;
|
||||||
|
do_interrupt(env);
|
||||||
|
next_tb = 0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
/* Don't use the cached interrupt_request value,
|
/* Don't use the cached interrupt_request value,
|
||||||
do_interrupt may have updated the EXITTB flag. */
|
do_interrupt may have updated the EXITTB flag. */
|
||||||
|
@ -108,7 +108,12 @@ enum {
|
|||||||
enum {
|
enum {
|
||||||
SAR = 3,
|
SAR = 3,
|
||||||
SCOMPARE1 = 12,
|
SCOMPARE1 = 12,
|
||||||
|
EPC1 = 177,
|
||||||
|
DEPC = 192,
|
||||||
|
EXCSAVE1 = 209,
|
||||||
PS = 230,
|
PS = 230,
|
||||||
|
EXCCAUSE = 232,
|
||||||
|
EXCVADDR = 238,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define PS_INTLEVEL 0xf
|
#define PS_INTLEVEL 0xf
|
||||||
@ -129,9 +134,60 @@ enum {
|
|||||||
|
|
||||||
#define PS_WOE 0x40000
|
#define PS_WOE 0x40000
|
||||||
|
|
||||||
|
enum {
|
||||||
|
/* Static vectors */
|
||||||
|
EXC_RESET,
|
||||||
|
EXC_MEMORY_ERROR,
|
||||||
|
|
||||||
|
/* Dynamic vectors */
|
||||||
|
EXC_WINDOW_OVERFLOW4,
|
||||||
|
EXC_WINDOW_UNDERFLOW4,
|
||||||
|
EXC_WINDOW_OVERFLOW8,
|
||||||
|
EXC_WINDOW_UNDERFLOW8,
|
||||||
|
EXC_WINDOW_OVERFLOW12,
|
||||||
|
EXC_WINDOW_UNDERFLOW12,
|
||||||
|
EXC_IRQ,
|
||||||
|
EXC_KERNEL,
|
||||||
|
EXC_USER,
|
||||||
|
EXC_DOUBLE,
|
||||||
|
EXC_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ILLEGAL_INSTRUCTION_CAUSE = 0,
|
||||||
|
SYSCALL_CAUSE,
|
||||||
|
INSTRUCTION_FETCH_ERROR_CAUSE,
|
||||||
|
LOAD_STORE_ERROR_CAUSE,
|
||||||
|
LEVEL1_INTERRUPT_CAUSE,
|
||||||
|
ALLOCA_CAUSE,
|
||||||
|
INTEGER_DIVIDE_BY_ZERO_CAUSE,
|
||||||
|
PRIVILEGED_CAUSE = 8,
|
||||||
|
LOAD_STORE_ALIGNMENT_CAUSE,
|
||||||
|
|
||||||
|
INSTR_PIF_DATA_ERROR_CAUSE = 12,
|
||||||
|
LOAD_STORE_PIF_DATA_ERROR_CAUSE,
|
||||||
|
INSTR_PIF_ADDR_ERROR_CAUSE,
|
||||||
|
LOAD_STORE_PIF_ADDR_ERROR_CAUSE,
|
||||||
|
|
||||||
|
INST_TLB_MISS_CAUSE,
|
||||||
|
INST_TLB_MULTI_HIT_CAUSE,
|
||||||
|
INST_FETCH_PRIVILEGE_CAUSE,
|
||||||
|
INST_FETCH_PROHIBITED_CAUSE = 20,
|
||||||
|
LOAD_STORE_TLB_MISS_CAUSE = 24,
|
||||||
|
LOAD_STORE_TLB_MULTI_HIT_CAUSE,
|
||||||
|
LOAD_STORE_PRIVILEGE_CAUSE,
|
||||||
|
LOAD_PROHIBITED_CAUSE = 28,
|
||||||
|
STORE_PROHIBITED_CAUSE,
|
||||||
|
|
||||||
|
COPROCESSOR0_DISABLED = 32,
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct XtensaConfig {
|
typedef struct XtensaConfig {
|
||||||
const char *name;
|
const char *name;
|
||||||
uint64_t options;
|
uint64_t options;
|
||||||
|
int excm_level;
|
||||||
|
int ndepc;
|
||||||
|
uint32_t exception_vector[EXC_MAX];
|
||||||
} XtensaConfig;
|
} XtensaConfig;
|
||||||
|
|
||||||
typedef struct CPUXtensaState {
|
typedef struct CPUXtensaState {
|
||||||
@ -141,6 +197,8 @@ typedef struct CPUXtensaState {
|
|||||||
uint32_t sregs[256];
|
uint32_t sregs[256];
|
||||||
uint32_t uregs[256];
|
uint32_t uregs[256];
|
||||||
|
|
||||||
|
int exception_taken;
|
||||||
|
|
||||||
CPU_COMMON
|
CPU_COMMON
|
||||||
} CPUXtensaState;
|
} CPUXtensaState;
|
||||||
|
|
||||||
@ -164,6 +222,15 @@ static inline bool xtensa_option_enabled(const XtensaConfig *config, int opt)
|
|||||||
return (config->options & XTENSA_OPTION_BIT(opt)) != 0;
|
return (config->options & XTENSA_OPTION_BIT(opt)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int xtensa_get_cintlevel(const CPUState *env)
|
||||||
|
{
|
||||||
|
int level = (env->sregs[PS] & PS_INTLEVEL) >> PS_INTLEVEL_SHIFT;
|
||||||
|
if ((env->sregs[PS] & PS_EXCM) && env->config->excm_level > level) {
|
||||||
|
level = env->config->excm_level;
|
||||||
|
}
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int xtensa_get_ring(const CPUState *env)
|
static inline int xtensa_get_ring(const CPUState *env)
|
||||||
{
|
{
|
||||||
if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
|
if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
|
||||||
|
@ -36,7 +36,8 @@
|
|||||||
|
|
||||||
void cpu_reset(CPUXtensaState *env)
|
void cpu_reset(CPUXtensaState *env)
|
||||||
{
|
{
|
||||||
env->pc = 0;
|
env->exception_taken = 0;
|
||||||
|
env->pc = env->config->exception_vector[EXC_RESET];
|
||||||
env->sregs[PS] = 0x1f;
|
env->sregs[PS] = 0x1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,6 +45,20 @@ static const XtensaConfig core_config[] = {
|
|||||||
{
|
{
|
||||||
.name = "sample-xtensa-core",
|
.name = "sample-xtensa-core",
|
||||||
.options = -1,
|
.options = -1,
|
||||||
|
.ndepc = 1,
|
||||||
|
.excm_level = 16,
|
||||||
|
.exception_vector = {
|
||||||
|
[EXC_RESET] = 0x5fff8000,
|
||||||
|
[EXC_WINDOW_OVERFLOW4] = 0x5fff8400,
|
||||||
|
[EXC_WINDOW_UNDERFLOW4] = 0x5fff8440,
|
||||||
|
[EXC_WINDOW_OVERFLOW8] = 0x5fff8480,
|
||||||
|
[EXC_WINDOW_UNDERFLOW8] = 0x5fff84c0,
|
||||||
|
[EXC_WINDOW_OVERFLOW12] = 0x5fff8500,
|
||||||
|
[EXC_WINDOW_UNDERFLOW12] = 0x5fff8540,
|
||||||
|
[EXC_KERNEL] = 0x5fff861c,
|
||||||
|
[EXC_USER] = 0x5fff863c,
|
||||||
|
[EXC_DOUBLE] = 0x5fff865c,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -94,4 +109,24 @@ target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
|
|||||||
|
|
||||||
void do_interrupt(CPUState *env)
|
void do_interrupt(CPUState *env)
|
||||||
{
|
{
|
||||||
|
switch (env->exception_index) {
|
||||||
|
case EXC_WINDOW_OVERFLOW4:
|
||||||
|
case EXC_WINDOW_UNDERFLOW4:
|
||||||
|
case EXC_WINDOW_OVERFLOW8:
|
||||||
|
case EXC_WINDOW_UNDERFLOW8:
|
||||||
|
case EXC_WINDOW_OVERFLOW12:
|
||||||
|
case EXC_WINDOW_UNDERFLOW12:
|
||||||
|
case EXC_KERNEL:
|
||||||
|
case EXC_USER:
|
||||||
|
case EXC_DOUBLE:
|
||||||
|
if (env->config->exception_vector[env->exception_index]) {
|
||||||
|
env->pc = env->config->exception_vector[env->exception_index];
|
||||||
|
env->exception_taken = 1;
|
||||||
|
} else {
|
||||||
|
qemu_log("%s(pc = %08x) bad exception_index: %d\n",
|
||||||
|
__func__, env->pc, env->exception_index);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "def-helper.h"
|
#include "def-helper.h"
|
||||||
|
|
||||||
DEF_HELPER_1(exception, void, i32)
|
DEF_HELPER_1(exception, void, i32)
|
||||||
|
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(nsa, i32, i32)
|
||||||
DEF_HELPER_1(nsau, i32, i32)
|
DEF_HELPER_1(nsau, i32, i32)
|
||||||
|
|
||||||
|
@ -59,6 +59,35 @@ void HELPER(exception)(uint32_t excp)
|
|||||||
cpu_loop_exit(env);
|
cpu_loop_exit(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HELPER(exception_cause)(uint32_t pc, uint32_t cause)
|
||||||
|
{
|
||||||
|
uint32_t vector;
|
||||||
|
|
||||||
|
env->pc = pc;
|
||||||
|
if (env->sregs[PS] & PS_EXCM) {
|
||||||
|
if (env->config->ndepc) {
|
||||||
|
env->sregs[DEPC] = pc;
|
||||||
|
} else {
|
||||||
|
env->sregs[EPC1] = pc;
|
||||||
|
}
|
||||||
|
vector = EXC_DOUBLE;
|
||||||
|
} else {
|
||||||
|
env->sregs[EPC1] = pc;
|
||||||
|
vector = (env->sregs[PS] & PS_UM) ? EXC_USER : EXC_KERNEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->sregs[EXCCAUSE] = cause;
|
||||||
|
env->sregs[PS] |= PS_EXCM;
|
||||||
|
|
||||||
|
HELPER(exception)(vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(exception_cause_vaddr)(uint32_t pc, uint32_t cause, uint32_t vaddr)
|
||||||
|
{
|
||||||
|
env->sregs[EXCVADDR] = vaddr;
|
||||||
|
HELPER(exception_cause)(pc, cause);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t HELPER(nsa)(uint32_t v)
|
uint32_t HELPER(nsa)(uint32_t v)
|
||||||
{
|
{
|
||||||
if (v & 0x80000000) {
|
if (v & 0x80000000) {
|
||||||
|
@ -67,7 +67,12 @@ static TCGv_i32 cpu_UR[256];
|
|||||||
static const char * const sregnames[256] = {
|
static const char * const sregnames[256] = {
|
||||||
[SAR] = "SAR",
|
[SAR] = "SAR",
|
||||||
[SCOMPARE1] = "SCOMPARE1",
|
[SCOMPARE1] = "SCOMPARE1",
|
||||||
|
[EPC1] = "EPC1",
|
||||||
|
[DEPC] = "DEPC",
|
||||||
|
[EXCSAVE1] = "EXCSAVE1",
|
||||||
[PS] = "PS",
|
[PS] = "PS",
|
||||||
|
[EXCCAUSE] = "EXCCAUSE",
|
||||||
|
[EXCVADDR] = "EXCVADDR",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char * const uregnames[256] = {
|
static const char * const uregnames[256] = {
|
||||||
@ -165,6 +170,22 @@ static void gen_exception(int excp)
|
|||||||
tcg_temp_free(tmp);
|
tcg_temp_free(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gen_exception_cause(DisasContext *dc, uint32_t cause)
|
||||||
|
{
|
||||||
|
TCGv_i32 tpc = tcg_const_i32(dc->pc);
|
||||||
|
TCGv_i32 tcause = tcg_const_i32(cause);
|
||||||
|
gen_helper_exception_cause(tpc, tcause);
|
||||||
|
tcg_temp_free(tpc);
|
||||||
|
tcg_temp_free(tcause);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_check_privilege(DisasContext *dc)
|
||||||
|
{
|
||||||
|
if (dc->cring) {
|
||||||
|
gen_exception_cause(dc, PRIVILEGED_CAUSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void gen_jump_slot(DisasContext *dc, TCGv dest, int slot)
|
static void gen_jump_slot(DisasContext *dc, TCGv dest, int slot)
|
||||||
{
|
{
|
||||||
tcg_gen_mov_i32(cpu_pc, dest);
|
tcg_gen_mov_i32(cpu_pc, dest);
|
||||||
@ -392,7 +413,7 @@ static void disas_xtensa_insn(DisasContext *dc)
|
|||||||
case 0: /*SNM0*/
|
case 0: /*SNM0*/
|
||||||
switch (CALLX_M) {
|
switch (CALLX_M) {
|
||||||
case 0: /*ILL*/
|
case 0: /*ILL*/
|
||||||
TBD();
|
gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1: /*reserved*/
|
case 1: /*reserved*/
|
||||||
@ -479,7 +500,52 @@ static void disas_xtensa_insn(DisasContext *dc)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 3: /*RFEIx*/
|
case 3: /*RFEIx*/
|
||||||
TBD();
|
switch (RRR_T) {
|
||||||
|
case 0: /*RFETx*/
|
||||||
|
HAS_OPTION(XTENSA_OPTION_EXCEPTION);
|
||||||
|
switch (RRR_S) {
|
||||||
|
case 0: /*RFEx*/
|
||||||
|
gen_check_privilege(dc);
|
||||||
|
tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_EXCM);
|
||||||
|
gen_jump(dc, cpu_SR[EPC1]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: /*RFUEx*/
|
||||||
|
RESERVED();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: /*RFDEx*/
|
||||||
|
gen_check_privilege(dc);
|
||||||
|
gen_jump(dc, cpu_SR[
|
||||||
|
dc->config->ndepc ? DEPC : EPC1]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4: /*RFWOw*/
|
||||||
|
case 5: /*RFWUw*/
|
||||||
|
HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
|
||||||
|
TBD();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: /*reserved*/
|
||||||
|
RESERVED();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: /*RFIx*/
|
||||||
|
HAS_OPTION(XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT);
|
||||||
|
TBD();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: /*RFME*/
|
||||||
|
TBD();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: /*reserved*/
|
||||||
|
RESERVED();
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4: /*BREAKx*/
|
case 4: /*BREAKx*/
|
||||||
@ -489,12 +555,28 @@ static void disas_xtensa_insn(DisasContext *dc)
|
|||||||
|
|
||||||
case 5: /*SYSCALLx*/
|
case 5: /*SYSCALLx*/
|
||||||
HAS_OPTION(XTENSA_OPTION_EXCEPTION);
|
HAS_OPTION(XTENSA_OPTION_EXCEPTION);
|
||||||
TBD();
|
switch (RRR_S) {
|
||||||
|
case 0: /*SYSCALLx*/
|
||||||
|
gen_exception_cause(dc, SYSCALL_CAUSE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: /*SIMCALL*/
|
||||||
|
TBD();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
RESERVED();
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6: /*RSILx*/
|
case 6: /*RSILx*/
|
||||||
HAS_OPTION(XTENSA_OPTION_INTERRUPT);
|
HAS_OPTION(XTENSA_OPTION_INTERRUPT);
|
||||||
TBD();
|
gen_check_privilege(dc);
|
||||||
|
tcg_gen_mov_i32(cpu_R[RRR_T], cpu_SR[PS]);
|
||||||
|
tcg_gen_ori_i32(cpu_SR[PS], cpu_SR[PS], RRR_S);
|
||||||
|
tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS],
|
||||||
|
RRR_S | ~PS_INTLEVEL);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 7: /*WAITIx*/
|
case 7: /*WAITIx*/
|
||||||
@ -691,6 +773,9 @@ static void disas_xtensa_insn(DisasContext *dc)
|
|||||||
case 6: /*XSR*/
|
case 6: /*XSR*/
|
||||||
{
|
{
|
||||||
TCGv_i32 tmp = tcg_temp_new_i32();
|
TCGv_i32 tmp = tcg_temp_new_i32();
|
||||||
|
if (RSR_SR >= 64) {
|
||||||
|
gen_check_privilege(dc);
|
||||||
|
}
|
||||||
tcg_gen_mov_i32(tmp, cpu_R[RRR_T]);
|
tcg_gen_mov_i32(tmp, cpu_R[RRR_T]);
|
||||||
gen_rsr(dc, cpu_R[RRR_T], RSR_SR);
|
gen_rsr(dc, cpu_R[RRR_T], RSR_SR);
|
||||||
gen_wsr(dc, RSR_SR, tmp);
|
gen_wsr(dc, RSR_SR, tmp);
|
||||||
@ -799,6 +884,9 @@ static void disas_xtensa_insn(DisasContext *dc)
|
|||||||
case 3: /*RST3*/
|
case 3: /*RST3*/
|
||||||
switch (OP2) {
|
switch (OP2) {
|
||||||
case 0: /*RSR*/
|
case 0: /*RSR*/
|
||||||
|
if (RSR_SR >= 64) {
|
||||||
|
gen_check_privilege(dc);
|
||||||
|
}
|
||||||
gen_rsr(dc, cpu_R[RRR_T], RSR_SR);
|
gen_rsr(dc, cpu_R[RRR_T], RSR_SR);
|
||||||
if (!sregnames[RSR_SR]) {
|
if (!sregnames[RSR_SR]) {
|
||||||
TBD();
|
TBD();
|
||||||
@ -806,6 +894,9 @@ static void disas_xtensa_insn(DisasContext *dc)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 1: /*WSR*/
|
case 1: /*WSR*/
|
||||||
|
if (RSR_SR >= 64) {
|
||||||
|
gen_check_privilege(dc);
|
||||||
|
}
|
||||||
gen_wsr(dc, RSR_SR, cpu_R[RRR_T]);
|
gen_wsr(dc, RSR_SR, cpu_R[RRR_T]);
|
||||||
if (!sregnames[RSR_SR]) {
|
if (!sregnames[RSR_SR]) {
|
||||||
TBD();
|
TBD();
|
||||||
@ -1421,7 +1512,7 @@ static void disas_xtensa_insn(DisasContext *dc)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 6: /*ILL.Nn*/
|
case 6: /*ILL.Nn*/
|
||||||
TBD();
|
gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: /*reserved*/
|
default: /*reserved*/
|
||||||
@ -1493,6 +1584,12 @@ static void gen_intermediate_code_internal(
|
|||||||
|
|
||||||
gen_icount_start();
|
gen_icount_start();
|
||||||
|
|
||||||
|
if (env->singlestep_enabled && env->exception_taken) {
|
||||||
|
env->exception_taken = 0;
|
||||||
|
tcg_gen_movi_i32(cpu_pc, dc.pc);
|
||||||
|
gen_exception(EXCP_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
check_breakpoint(env, &dc);
|
check_breakpoint(env, &dc);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user