mirror of
https://github.com/xemu-project/xemu.git
synced 2024-12-03 17:11:01 +00:00
target/loongarch: Add LoongArch interrupt and exception handle
Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn> Signed-off-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20220606124333.2060567-24-yangxiaojuan@loongson.cn> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
parent
7e1c521e2a
commit
f757a2cd69
@ -80,6 +80,215 @@ static void loongarch_cpu_set_pc(CPUState *cs, vaddr value)
|
||||
env->pc = value;
|
||||
}
|
||||
|
||||
void loongarch_cpu_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
LoongArchCPU *cpu = opaque;
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
CPUState *cs = CPU(cpu);
|
||||
|
||||
if (irq < 0 || irq >= N_IRQS) {
|
||||
return;
|
||||
}
|
||||
|
||||
env->CSR_ESTAT = deposit64(env->CSR_ESTAT, irq, 1, level != 0);
|
||||
|
||||
if (FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS)) {
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
} else {
|
||||
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool cpu_loongarch_hw_interrupts_enabled(CPULoongArchState *env)
|
||||
{
|
||||
bool ret = 0;
|
||||
|
||||
ret = (FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE) &&
|
||||
!(FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check if there is pending and not masked out interrupt */
|
||||
static inline bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env)
|
||||
{
|
||||
uint32_t pending;
|
||||
uint32_t status;
|
||||
bool r;
|
||||
|
||||
pending = FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS);
|
||||
status = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, LIE);
|
||||
|
||||
r = (pending & status) != 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
static void loongarch_cpu_do_interrupt(CPUState *cs)
|
||||
{
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
bool update_badinstr = 1;
|
||||
int cause = -1;
|
||||
const char *name;
|
||||
bool tlbfill = FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR);
|
||||
uint32_t vec_size = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, VS);
|
||||
|
||||
if (cs->exception_index != EXCCODE_INT) {
|
||||
if (cs->exception_index < 0 ||
|
||||
cs->exception_index > ARRAY_SIZE(excp_names)) {
|
||||
name = "unknown";
|
||||
} else {
|
||||
name = excp_names[cs->exception_index];
|
||||
}
|
||||
|
||||
qemu_log_mask(CPU_LOG_INT,
|
||||
"%s enter: pc " TARGET_FMT_lx " ERA " TARGET_FMT_lx
|
||||
" TLBRERA " TARGET_FMT_lx " %s exception\n", __func__,
|
||||
env->pc, env->CSR_ERA, env->CSR_TLBRERA, name);
|
||||
}
|
||||
|
||||
switch (cs->exception_index) {
|
||||
case EXCCODE_DBP:
|
||||
env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DCL, 1);
|
||||
env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, ECODE, 0xC);
|
||||
goto set_DERA;
|
||||
set_DERA:
|
||||
env->CSR_DERA = env->pc;
|
||||
env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DST, 1);
|
||||
env->pc = env->CSR_EENTRY + 0x480;
|
||||
break;
|
||||
case EXCCODE_INT:
|
||||
if (FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) {
|
||||
env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DEI, 1);
|
||||
goto set_DERA;
|
||||
}
|
||||
QEMU_FALLTHROUGH;
|
||||
case EXCCODE_PIF:
|
||||
cause = cs->exception_index;
|
||||
update_badinstr = 0;
|
||||
break;
|
||||
case EXCCODE_ADEM:
|
||||
case EXCCODE_SYS:
|
||||
case EXCCODE_BRK:
|
||||
case EXCCODE_PIL:
|
||||
case EXCCODE_PIS:
|
||||
case EXCCODE_PME:
|
||||
case EXCCODE_PNR:
|
||||
case EXCCODE_PNX:
|
||||
case EXCCODE_PPI:
|
||||
case EXCCODE_INE:
|
||||
case EXCCODE_IPE:
|
||||
case EXCCODE_FPE:
|
||||
cause = cs->exception_index;
|
||||
break;
|
||||
default:
|
||||
qemu_log("Error: exception(%d) '%s' has not been supported\n",
|
||||
cs->exception_index, excp_names[cs->exception_index]);
|
||||
abort();
|
||||
}
|
||||
|
||||
if (update_badinstr) {
|
||||
env->CSR_BADI = cpu_ldl_code(env, env->pc);
|
||||
}
|
||||
|
||||
/* Save PLV and IE */
|
||||
if (tlbfill) {
|
||||
env->CSR_TLBRPRMD = FIELD_DP64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV,
|
||||
FIELD_EX64(env->CSR_CRMD,
|
||||
CSR_CRMD, PLV));
|
||||
env->CSR_TLBRPRMD = FIELD_DP64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE,
|
||||
FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE));
|
||||
/* set the DA mode */
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 1);
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 0);
|
||||
env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA,
|
||||
PC, (env->pc >> 2));
|
||||
} else {
|
||||
env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, ECODE, cause);
|
||||
env->CSR_PRMD = FIELD_DP64(env->CSR_PRMD, CSR_PRMD, PPLV,
|
||||
FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV));
|
||||
env->CSR_PRMD = FIELD_DP64(env->CSR_PRMD, CSR_PRMD, PIE,
|
||||
FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE));
|
||||
env->CSR_ERA = env->pc;
|
||||
}
|
||||
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, 0);
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, 0);
|
||||
|
||||
if (cs->exception_index == EXCCODE_INT) {
|
||||
/* Interrupt */
|
||||
uint32_t vector = 0;
|
||||
uint32_t pending = FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS);
|
||||
pending &= FIELD_EX64(env->CSR_ECFG, CSR_ECFG, LIE);
|
||||
|
||||
/* Find the highest-priority interrupt. */
|
||||
vector = 31 - clz32(pending);
|
||||
env->pc = env->CSR_EENTRY + (EXCCODE_EXTERNAL_INT + vector) * vec_size;
|
||||
qemu_log_mask(CPU_LOG_INT,
|
||||
"%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx
|
||||
" cause %d\n" " A " TARGET_FMT_lx " D "
|
||||
TARGET_FMT_lx " vector = %d ExC " TARGET_FMT_lx "ExS"
|
||||
TARGET_FMT_lx "\n",
|
||||
__func__, env->pc, env->CSR_ERA,
|
||||
cause, env->CSR_BADV, env->CSR_DERA, vector,
|
||||
env->CSR_ECFG, env->CSR_ESTAT);
|
||||
} else {
|
||||
if (tlbfill) {
|
||||
env->pc = env->CSR_TLBRENTRY;
|
||||
} else {
|
||||
env->pc = env->CSR_EENTRY;
|
||||
env->pc += cause * vec_size;
|
||||
}
|
||||
qemu_log_mask(CPU_LOG_INT,
|
||||
"%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx
|
||||
" cause %d%s\n, ESTAT " TARGET_FMT_lx
|
||||
" EXCFG " TARGET_FMT_lx " BADVA " TARGET_FMT_lx
|
||||
"BADI " TARGET_FMT_lx " SYS_NUM " TARGET_FMT_lu
|
||||
" cpu %d asid " TARGET_FMT_lx "\n", __func__, env->pc,
|
||||
tlbfill ? env->CSR_TLBRERA : env->CSR_ERA,
|
||||
cause, tlbfill ? "(refill)" : "", env->CSR_ESTAT,
|
||||
env->CSR_ECFG,
|
||||
tlbfill ? env->CSR_TLBRBADV : env->CSR_BADV,
|
||||
env->CSR_BADI, env->gpr[11], cs->cpu_index,
|
||||
env->CSR_ASID);
|
||||
}
|
||||
cs->exception_index = -1;
|
||||
}
|
||||
|
||||
static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
|
||||
vaddr addr, unsigned size,
|
||||
MMUAccessType access_type,
|
||||
int mmu_idx, MemTxAttrs attrs,
|
||||
MemTxResult response,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
|
||||
if (access_type == MMU_INST_FETCH) {
|
||||
do_raise_exception(env, EXCCODE_ADEF, retaddr);
|
||||
} else {
|
||||
do_raise_exception(env, EXCCODE_ADEM, retaddr);
|
||||
}
|
||||
}
|
||||
|
||||
static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
||||
{
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
|
||||
if (cpu_loongarch_hw_interrupts_enabled(env) &&
|
||||
cpu_loongarch_hw_interrupts_pending(env)) {
|
||||
/* Raise it */
|
||||
cs->exception_index = EXCCODE_INT;
|
||||
loongarch_cpu_do_interrupt(cs);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TCG
|
||||
static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
|
||||
const TranslationBlock *tb)
|
||||
@ -91,6 +300,20 @@ static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
|
||||
}
|
||||
#endif /* CONFIG_TCG */
|
||||
|
||||
static bool loongarch_cpu_has_work(CPUState *cs)
|
||||
{
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
bool has_work = false;
|
||||
|
||||
if ((cs->interrupt_request & CPU_INTERRUPT_HARD) &&
|
||||
cpu_loongarch_hw_interrupts_pending(env)) {
|
||||
has_work = true;
|
||||
}
|
||||
|
||||
return has_work;
|
||||
}
|
||||
|
||||
static void loongarch_la464_initfn(Object *obj)
|
||||
{
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
|
||||
@ -237,6 +460,8 @@ static void loongarch_cpu_reset(DeviceState *dev)
|
||||
env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV3, 0);
|
||||
}
|
||||
|
||||
env->pc = 0x1c000000;
|
||||
|
||||
restore_fp_status(env);
|
||||
cs->exception_index = -1;
|
||||
}
|
||||
@ -269,6 +494,7 @@ static void loongarch_cpu_init(Object *obj)
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
|
||||
|
||||
cpu_set_cpustate_pointers(cpu);
|
||||
qdev_init_gpio_in(DEVICE(cpu), loongarch_cpu_set_irq, N_IRQS);
|
||||
}
|
||||
|
||||
static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model)
|
||||
@ -337,6 +563,9 @@ static struct TCGCPUOps loongarch_tcg_ops = {
|
||||
.synchronize_from_tb = loongarch_cpu_synchronize_from_tb,
|
||||
|
||||
.tlb_fill = loongarch_cpu_tlb_fill,
|
||||
.cpu_exec_interrupt = loongarch_cpu_exec_interrupt,
|
||||
.do_interrupt = loongarch_cpu_do_interrupt,
|
||||
.do_transaction_failed = loongarch_cpu_do_transaction_failed,
|
||||
};
|
||||
#endif /* CONFIG_TCG */
|
||||
|
||||
@ -357,6 +586,7 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data)
|
||||
device_class_set_parent_reset(dc, loongarch_cpu_reset, &lacc->parent_reset);
|
||||
|
||||
cc->class_by_name = loongarch_cpu_class_by_name;
|
||||
cc->has_work = loongarch_cpu_has_work;
|
||||
cc->dump_state = loongarch_cpu_dump_state;
|
||||
cc->set_pc = loongarch_cpu_set_pc;
|
||||
dc->vmsd = &vmstate_loongarch_cpu;
|
||||
|
@ -184,6 +184,8 @@ FIELD(CSR_CRMD, WE, 9, 1)
|
||||
extern const char * const regnames[32];
|
||||
extern const char * const fregnames[32];
|
||||
|
||||
#define N_IRQS 13
|
||||
|
||||
#define LOONGARCH_STLB 2048 /* 2048 STLB */
|
||||
#define LOONGARCH_MTLB 64 /* 64 MTLB */
|
||||
#define LOONGARCH_TLB_MAX (LOONGARCH_STLB + LOONGARCH_MTLB)
|
||||
|
@ -30,6 +30,8 @@ void restore_fp_status(CPULoongArchState *env);
|
||||
|
||||
extern const VMStateDescription vmstate_loongarch_cpu;
|
||||
|
||||
void loongarch_cpu_set_irq(void *opaque, int irq, int level);
|
||||
|
||||
bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
||||
MMUAccessType access_type, int mmu_idx,
|
||||
bool probe, uintptr_t retaddr);
|
||||
|
Loading…
Reference in New Issue
Block a user