diff mbox series

[RFC,v4,11/30] target/loongarch: Add LoongArch interrupt and exception handle

Message ID 20220108091419.2027710-12-yangxiaojuan@loongson.cn (mailing list archive)
State New, archived
Headers show
Series Add LoongArch softmmu support. | expand

Commit Message

Xiaojuan Yang Jan. 8, 2022, 9:14 a.m. UTC
1.This patch Add loongarch interrupt and exception handle.
2.Rename the user excp to the exccode from the csr defintions.

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 linux-user/loongarch64/cpu_loop.c             |   8 +-
 target/loongarch/cpu.c                        | 252 +++++++++++++++++-
 target/loongarch/cpu.h                        |  11 -
 target/loongarch/fpu_helper.c                 |   2 +-
 target/loongarch/insn_trans/trans_extra.c.inc |   4 +-
 target/loongarch/translate.c                  |   2 +-
 6 files changed, 254 insertions(+), 25 deletions(-)

Comments

WANG Xuerui Jan. 9, 2022, 9:26 a.m. UTC | #1
On 1/8/22 17:14, Xiaojuan Yang wrote:
> 1.This patch Add loongarch interrupt and exception handle.
> 2.Rename the user excp to the exccode from the csr defintions.
>
> Signed-off-by: Xiaojuan Yang<yangxiaojuan@loongson.cn>
> Signed-off-by: Song Gao<gaosong@loongson.cn>
> ---
>   linux-user/loongarch64/cpu_loop.c             |   8 +-
>   target/loongarch/cpu.c                        | 252 +++++++++++++++++-
>   target/loongarch/cpu.h                        |  11 -
>   target/loongarch/fpu_helper.c                 |   2 +-
>   target/loongarch/insn_trans/trans_extra.c.inc |   4 +-
>   target/loongarch/translate.c                  |   2 +-
>   6 files changed, 254 insertions(+), 25 deletions(-)
>
> diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c
> index 6628d215ca..dd58eb048f 100644
> --- a/linux-user/loongarch64/cpu_loop.c
> +++ b/linux-user/loongarch64/cpu_loop.c
> @@ -28,7 +28,7 @@ void cpu_loop(CPULoongArchState *env)
>           case EXCP_INTERRUPT:
>               /* just indicate that signals should be handled asap */
>               break;
> -        case EXCP_SYSCALL:
> +        case EXCCODE_SYS:
>               env->pc += 4;
>               ret = do_syscall(env, env->gpr[11],
>                                env->gpr[4], env->gpr[5],
> @@ -48,10 +48,10 @@ void cpu_loop(CPULoongArchState *env)
>               }
>               env->gpr[4] = ret;
>               break;
> -        case EXCP_INE:
> +        case EXCCODE_INE:
>               force_sig_fault(TARGET_SIGILL, 0, env->pc);
>               break;
> -        case EXCP_FPE:
> +        case EXCCODE_FPE:
>               si_code = TARGET_FPE_FLTUNK;
>               if (GET_FP_CAUSE(env->fcsr0) & FP_INVALID) {
>                   si_code = TARGET_FPE_FLTINV;
> @@ -67,7 +67,7 @@ void cpu_loop(CPULoongArchState *env)
>               force_sig_fault(TARGET_SIGFPE, si_code, env->pc);
>               break;
>           case EXCP_DEBUG:
> -        case EXCP_BREAK:
> +        case EXCCODE_BRK:
>               force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc);
>               break;
>           case EXCP_ATOMIC:
> diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
> index 571092ce53..caab59b83a 100644
> --- a/target/loongarch/cpu.c
> +++ b/target/loongarch/cpu.c
> @@ -30,11 +30,23 @@ const char * const fregnames[32] = {
>       "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
>   };
>   
> -static const char * const excp_names[EXCP_LAST + 1] = {
> -    [EXCP_SYSCALL] = "Syscall",
> -    [EXCP_BREAK] = "Break",
> -    [EXCP_INE] = "Instruction Non-existent",
> -    [EXCP_FPE] = "Floating Point Exception",
> +static const char * const excp_names[] = {
> +    [EXCCODE_INT] = "Interrupt",
> +    [EXCCODE_PIL] = "TLB load page invalid",
> +    [EXCCODE_PIS] = "TLB store page invalid",
> +    [EXCCODE_PIF] = "TLB Fetch page invalid",
Where do the translations come from? According to the Chinese original, 
we should have "Page Invalid for {Load,Store,instruction Fetch}" here 
for correct acronyms.
> +    [EXCCODE_PME] = "TLB Page modify",
> +    [EXCCODE_PNR] = "TLB read-inhibit",
> +    [EXCCODE_PNX] = "TLB execute-inhibit",
> +    [EXCCODE_PPI] = "TLB priviledged error",
And "Page Modified Exception", "Page Not Readable", "Page Not 
Executable", and "Page Privilege Error" accordingly.
> +    [EXCCODE_ADEF] = "Fetch instruction error",
> +    [EXCCODE_ADEM] = "Memory access error",
And "Address Error for instruction Fetch", and "Address Error for Memory 
access".
> +    [EXCCODE_SYS] = "Syscall",
> +    [EXCCODE_BRK] = "Break",
> +    [EXCCODE_INE] = "Instruction Non-existent",
> +    [EXCCODE_IPE] = "Instruction priveiledged error",
"Instruction Privilege Error"
> +    [EXCCODE_FPE] = "Floating Point Exception",
> +    [EXCCODE_DBP] = "Debug breakpoint",
If you re-organize the series so that basic CPU emulation and system 
emulation come first, you may want to squash part of this commit into 
the former commit defining this array.
>   };
>   
>   const char *loongarch_exception_name(int32_t exception)
> @@ -66,6 +78,215 @@ static void loongarch_cpu_set_pc(CPUState *cs, vaddr value)
>       env->pc = value;
>   }
>   
> +#if !defined(CONFIG_USER_ONLY)
> +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 inline unsigned int get_vint_size(CPULoongArchState *env)
> +{
> +    uint64_t vs = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, VS);
> +    uint64_t size = 0;
> +
> +    if (vs == 0) {
> +        return 0;
> +    }
> +
> +    if (vs < 8) {
> +        size = 1 << (vs + 2);
> +    }
> +
> +    if (vs > 8) {
> +        qemu_log("%s: unexpected value", __func__);
> +        assert(0);
> +    }
> +
> +    return size;
> +}
> +
> +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);
> +
> +    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);
> +        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:
> +    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_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);
> +
> +    uint32_t vec_size = get_vint_size(env);
> +    env->pc = env->CSR_EENTRY;
> +    env->pc += cause * vec_size;
> +    if (tlbfill) {
> +        /* TLB Refill */
> +        env->pc = env->CSR_TLBRENTRY;
> +    }
> +    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. */
> +        while (pending >>= 1) {
> +            vector++;
> +        }
> +        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 %08lx ExS %08lx\n",
> +                      __func__, env->pc, env->CSR_ERA,
> +                      cause, env->CSR_BADV, env->CSR_DERA, vector,
> +                      env->CSR_ECFG, env->CSR_ESTAT);
> +    }
> +
> +    /* Excode */
"exccode"?
> +    env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, ECODE, cause);
> +
> +    if (cs->exception_index != EXCCODE_INT) {
> +        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 0x%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;
> +}
> +#endif
> +
>   #ifdef CONFIG_TCG
>   static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
>                                                 const TranslationBlock *tb)
> @@ -79,7 +300,20 @@ static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
>   
>   static bool loongarch_cpu_has_work(CPUState *cs)
>   {
> +#ifdef CONFIG_USER_ONLY
>       return true;
> +#else
> +    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;
> +#endif
>   }
>   
>   static void loongarch_3a5000_initfn(Object *obj)
> @@ -228,8 +462,11 @@ static void loongarch_cpu_reset(DeviceState *dev)
>           env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV3, 0);
>       }
>   
> +#ifndef CONFIG_USER_ONLY
> +    env->pc = 0x1c000000;
> +#endif
>       restore_fp_status(env);
> -    cs->exception_index = EXCP_NONE;
> +    cs->exception_index = -1;
>   }
>   
>   static void loongarch_cpu_disas_set_info(CPUState *s, disassemble_info *info)
> @@ -342,6 +579,9 @@ static struct TCGCPUOps loongarch_tcg_ops = {
>   
>   #if !defined(CONFIG_USER_ONLY)
>       .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_USER_ONLY */
>   };
>   #endif /* CONFIG_TCG */
> diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
> index 2d5bae1af4..e623e358ec 100644
> --- a/target/loongarch/cpu.h
> +++ b/target/loongarch/cpu.h
> @@ -431,17 +431,6 @@ typedef LoongArchCPU ArchCPU;
>   
>   #include "exec/cpu-all.h"
>   
> -/* Exceptions */
> -enum {
> -    EXCP_NONE          = -1,
> -    EXCP_SYSCALL       = 0,
> -    EXCP_BREAK,
> -    EXCP_INE,
> -    EXCP_FPE,
> -
> -    EXCP_LAST = EXCP_FPE,
> -};
> -
>   #define CPU_INTERRUPT_WAKE CPU_INTERRUPT_TGT_INT_0
>   
>   #define LOONGARCH_CPU_TYPE_SUFFIX "-" TYPE_LOONGARCH_CPU
> diff --git a/target/loongarch/fpu_helper.c b/target/loongarch/fpu_helper.c
> index 9f5235c4f8..1baf012ef7 100644
> --- a/target/loongarch/fpu_helper.c
> +++ b/target/loongarch/fpu_helper.c
> @@ -74,7 +74,7 @@ static void update_fcsr0_mask(CPULoongArchState *env, uintptr_t pc, int mask)
>       }
>   
>       if (GET_FP_ENABLES(env->fcsr0) & flags) {
> -        do_raise_exception(env, EXCP_FPE, pc);
> +        do_raise_exception(env, EXCCODE_FPE, pc);
>       } else {
>           UPDATE_FP_FLAGS(env->fcsr0, flags);
>       }
> diff --git a/target/loongarch/insn_trans/trans_extra.c.inc b/target/loongarch/insn_trans/trans_extra.c.inc
> index bc622ced23..2ce95d3382 100644
> --- a/target/loongarch/insn_trans/trans_extra.c.inc
> +++ b/target/loongarch/insn_trans/trans_extra.c.inc
> @@ -5,13 +5,13 @@
>   
>   static bool trans_break(DisasContext *ctx, arg_break *a)
>   {
> -    generate_exception(ctx, EXCP_BREAK);
> +    generate_exception(ctx, EXCCODE_BRK);
>       return true;
>   }
>   
>   static bool trans_syscall(DisasContext *ctx, arg_syscall *a)
>   {
> -    generate_exception(ctx, EXCP_SYSCALL);
> +    generate_exception(ctx, EXCCODE_SYS);
>       return true;
>   }
>   
> diff --git a/target/loongarch/translate.c b/target/loongarch/translate.c
> index 09771ee43f..ddb97661fa 100644
> --- a/target/loongarch/translate.c
> +++ b/target/loongarch/translate.c
> @@ -185,7 +185,7 @@ static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
>       if (!decode(ctx, ctx->opcode)) {
>           qemu_log_mask(LOG_UNIMP, "Error: unkown opcode. 0x%lx: 0x%x\n",
>                         ctx->base.pc_next, ctx->opcode);
> -        generate_exception(ctx, EXCP_INE);
> +        generate_exception(ctx, EXCCODE_INE);
>       }
>   
>       for (int i = ctx->ntemp - 1; i >= 0; --i) {
diff mbox series

Patch

diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c
index 6628d215ca..dd58eb048f 100644
--- a/linux-user/loongarch64/cpu_loop.c
+++ b/linux-user/loongarch64/cpu_loop.c
@@ -28,7 +28,7 @@  void cpu_loop(CPULoongArchState *env)
         case EXCP_INTERRUPT:
             /* just indicate that signals should be handled asap */
             break;
-        case EXCP_SYSCALL:
+        case EXCCODE_SYS:
             env->pc += 4;
             ret = do_syscall(env, env->gpr[11],
                              env->gpr[4], env->gpr[5],
@@ -48,10 +48,10 @@  void cpu_loop(CPULoongArchState *env)
             }
             env->gpr[4] = ret;
             break;
-        case EXCP_INE:
+        case EXCCODE_INE:
             force_sig_fault(TARGET_SIGILL, 0, env->pc);
             break;
-        case EXCP_FPE:
+        case EXCCODE_FPE:
             si_code = TARGET_FPE_FLTUNK;
             if (GET_FP_CAUSE(env->fcsr0) & FP_INVALID) {
                 si_code = TARGET_FPE_FLTINV;
@@ -67,7 +67,7 @@  void cpu_loop(CPULoongArchState *env)
             force_sig_fault(TARGET_SIGFPE, si_code, env->pc);
             break;
         case EXCP_DEBUG:
-        case EXCP_BREAK:
+        case EXCCODE_BRK:
             force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc);
             break;
         case EXCP_ATOMIC:
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index 571092ce53..caab59b83a 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -30,11 +30,23 @@  const char * const fregnames[32] = {
     "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
 };
 
-static const char * const excp_names[EXCP_LAST + 1] = {
-    [EXCP_SYSCALL] = "Syscall",
-    [EXCP_BREAK] = "Break",
-    [EXCP_INE] = "Instruction Non-existent",
-    [EXCP_FPE] = "Floating Point Exception",
+static const char * const excp_names[] = {
+    [EXCCODE_INT] = "Interrupt",
+    [EXCCODE_PIL] = "TLB load page invalid",
+    [EXCCODE_PIS] = "TLB store page invalid",
+    [EXCCODE_PIF] = "TLB Fetch page invalid",
+    [EXCCODE_PME] = "TLB Page modify",
+    [EXCCODE_PNR] = "TLB read-inhibit",
+    [EXCCODE_PNX] = "TLB execute-inhibit",
+    [EXCCODE_PPI] = "TLB priviledged error",
+    [EXCCODE_ADEF] = "Fetch instruction error",
+    [EXCCODE_ADEM] = "Memory access error",
+    [EXCCODE_SYS] = "Syscall",
+    [EXCCODE_BRK] = "Break",
+    [EXCCODE_INE] = "Instruction Non-existent",
+    [EXCCODE_IPE] = "Instruction priveiledged error",
+    [EXCCODE_FPE] = "Floating Point Exception",
+    [EXCCODE_DBP] = "Debug breakpoint",
 };
 
 const char *loongarch_exception_name(int32_t exception)
@@ -66,6 +78,215 @@  static void loongarch_cpu_set_pc(CPUState *cs, vaddr value)
     env->pc = value;
 }
 
+#if !defined(CONFIG_USER_ONLY)
+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 inline unsigned int get_vint_size(CPULoongArchState *env)
+{
+    uint64_t vs = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, VS);
+    uint64_t size = 0;
+
+    if (vs == 0) {
+        return 0;
+    }
+
+    if (vs < 8) {
+        size = 1 << (vs + 2);
+    }
+
+    if (vs > 8) {
+        qemu_log("%s: unexpected value", __func__);
+        assert(0);
+    }
+
+    return size;
+}
+
+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);
+
+    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);
+        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:
+    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_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);
+
+    uint32_t vec_size = get_vint_size(env);
+    env->pc = env->CSR_EENTRY;
+    env->pc += cause * vec_size;
+    if (tlbfill) {
+        /* TLB Refill */
+        env->pc = env->CSR_TLBRENTRY;
+    }
+    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. */
+        while (pending >>= 1) {
+            vector++;
+        }
+        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 %08lx ExS %08lx\n",
+                      __func__, env->pc, env->CSR_ERA,
+                      cause, env->CSR_BADV, env->CSR_DERA, vector,
+                      env->CSR_ECFG, env->CSR_ESTAT);
+    }
+
+    /* Excode */
+    env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, ECODE, cause);
+
+    if (cs->exception_index != EXCCODE_INT) {
+        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 0x%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;
+}
+#endif
+
 #ifdef CONFIG_TCG
 static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
                                               const TranslationBlock *tb)
@@ -79,7 +300,20 @@  static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
 
 static bool loongarch_cpu_has_work(CPUState *cs)
 {
+#ifdef CONFIG_USER_ONLY
     return true;
+#else
+    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;
+#endif
 }
 
 static void loongarch_3a5000_initfn(Object *obj)
@@ -228,8 +462,11 @@  static void loongarch_cpu_reset(DeviceState *dev)
         env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV3, 0);
     }
 
+#ifndef CONFIG_USER_ONLY
+    env->pc = 0x1c000000;
+#endif
     restore_fp_status(env);
-    cs->exception_index = EXCP_NONE;
+    cs->exception_index = -1;
 }
 
 static void loongarch_cpu_disas_set_info(CPUState *s, disassemble_info *info)
@@ -342,6 +579,9 @@  static struct TCGCPUOps loongarch_tcg_ops = {
 
 #if !defined(CONFIG_USER_ONLY)
     .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_USER_ONLY */
 };
 #endif /* CONFIG_TCG */
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 2d5bae1af4..e623e358ec 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -431,17 +431,6 @@  typedef LoongArchCPU ArchCPU;
 
 #include "exec/cpu-all.h"
 
-/* Exceptions */
-enum {
-    EXCP_NONE          = -1,
-    EXCP_SYSCALL       = 0,
-    EXCP_BREAK,
-    EXCP_INE,
-    EXCP_FPE,
-
-    EXCP_LAST = EXCP_FPE,
-};
-
 #define CPU_INTERRUPT_WAKE CPU_INTERRUPT_TGT_INT_0
 
 #define LOONGARCH_CPU_TYPE_SUFFIX "-" TYPE_LOONGARCH_CPU
diff --git a/target/loongarch/fpu_helper.c b/target/loongarch/fpu_helper.c
index 9f5235c4f8..1baf012ef7 100644
--- a/target/loongarch/fpu_helper.c
+++ b/target/loongarch/fpu_helper.c
@@ -74,7 +74,7 @@  static void update_fcsr0_mask(CPULoongArchState *env, uintptr_t pc, int mask)
     }
 
     if (GET_FP_ENABLES(env->fcsr0) & flags) {
-        do_raise_exception(env, EXCP_FPE, pc);
+        do_raise_exception(env, EXCCODE_FPE, pc);
     } else {
         UPDATE_FP_FLAGS(env->fcsr0, flags);
     }
diff --git a/target/loongarch/insn_trans/trans_extra.c.inc b/target/loongarch/insn_trans/trans_extra.c.inc
index bc622ced23..2ce95d3382 100644
--- a/target/loongarch/insn_trans/trans_extra.c.inc
+++ b/target/loongarch/insn_trans/trans_extra.c.inc
@@ -5,13 +5,13 @@ 
 
 static bool trans_break(DisasContext *ctx, arg_break *a)
 {
-    generate_exception(ctx, EXCP_BREAK);
+    generate_exception(ctx, EXCCODE_BRK);
     return true;
 }
 
 static bool trans_syscall(DisasContext *ctx, arg_syscall *a)
 {
-    generate_exception(ctx, EXCP_SYSCALL);
+    generate_exception(ctx, EXCCODE_SYS);
     return true;
 }
 
diff --git a/target/loongarch/translate.c b/target/loongarch/translate.c
index 09771ee43f..ddb97661fa 100644
--- a/target/loongarch/translate.c
+++ b/target/loongarch/translate.c
@@ -185,7 +185,7 @@  static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
     if (!decode(ctx, ctx->opcode)) {
         qemu_log_mask(LOG_UNIMP, "Error: unkown opcode. 0x%lx: 0x%x\n",
                       ctx->base.pc_next, ctx->opcode);
-        generate_exception(ctx, EXCP_INE);
+        generate_exception(ctx, EXCCODE_INE);
     }
 
     for (int i = ctx->ntemp - 1; i >= 0; --i) {