diff mbox series

[RFC,v4,10/30] target/loongarch: Add other core instructions support

Message ID 20220108091419.2027710-11-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:13 a.m. UTC
This includes:
-CACOP
-LDDIR
-LDPTE
-ERTN
-DBCL
-IDLE

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 target/loongarch/cpu.h                       |  2 +
 target/loongarch/disas.c                     | 17 ++++
 target/loongarch/helper.h                    |  4 +
 target/loongarch/insn_trans/trans_core.c.inc | 74 +++++++++++++++++
 target/loongarch/insns.decode                | 11 +++
 target/loongarch/internals.h                 |  5 ++
 target/loongarch/op_helper.c                 | 43 ++++++++++
 target/loongarch/tlb_helper.c                | 87 ++++++++++++++++++++
 8 files changed, 243 insertions(+)

Comments

WANG Xuerui Jan. 9, 2022, 9:26 a.m. UTC | #1
On 1/8/22 17:13, Xiaojuan Yang wrote:
> This includes:
> -CACOP
> -LDDIR
> -LDPTE
> -ERTN
> -DBCL
> -IDLE
Okay, now I got that the word "core" actually meant "privileged"... so 
please adjust all occurrences of that word.
> Signed-off-by: Xiaojuan Yang<yangxiaojuan@loongson.cn>
> Signed-off-by: Song Gao<gaosong@loongson.cn>
> ---
>   target/loongarch/cpu.h                       |  2 +
>   target/loongarch/disas.c                     | 17 ++++
>   target/loongarch/helper.h                    |  4 +
>   target/loongarch/insn_trans/trans_core.c.inc | 74 +++++++++++++++++
>   target/loongarch/insns.decode                | 11 +++
>   target/loongarch/internals.h                 |  5 ++
>   target/loongarch/op_helper.c                 | 43 ++++++++++
>   target/loongarch/tlb_helper.c                | 87 ++++++++++++++++++++
>   8 files changed, 243 insertions(+)
>
> diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
> index ddb69ffecf..2d5bae1af4 100644
> --- a/target/loongarch/cpu.h
> +++ b/target/loongarch/cpu.h
> @@ -442,6 +442,8 @@ enum {
>       EXCP_LAST = EXCP_FPE,
>   };
>   
> +#define CPU_INTERRUPT_WAKE CPU_INTERRUPT_TGT_INT_0
> +
>   #define LOONGARCH_CPU_TYPE_SUFFIX "-" TYPE_LOONGARCH_CPU
>   #define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX
>   #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU
> diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c
> index 483270f331..516866c2d3 100644
> --- a/target/loongarch/disas.c
> +++ b/target/loongarch/disas.c
> @@ -226,6 +226,17 @@ static void output_i_rr(DisasContext *ctx, arg_i_rr *a, const char *mnemonic)
>       output(ctx, mnemonic, "%d, r%d, r%d", a->imm, a->rj, a->rk);
>   }
>   
> +static void output_cop_r_i(DisasContext *ctx, arg_cop_r_i *a,
> +                           const char *mnemonic)
> +{
> +    output(ctx, mnemonic, "%d, r%d, %d", a->cop, a->rj, a->imm);
> +}
> +
> +static void output_j_i(DisasContext *ctx, arg_j_i *a, const char *mnemonic)
> +{
> +    output(ctx, mnemonic, "r%d, %d", a->rj, a->imm);
> +}
> +
>   #define INSN(insn, type)                                    \
>   static bool trans_##insn(DisasContext *ctx, arg_##type * a) \
>   {                                                           \
> @@ -556,6 +567,12 @@ INSN(tlbfill,      empty)
>   INSN(tlbclr,       empty)
>   INSN(tlbflush,     empty)
>   INSN(invtlb,       i_rr)
> +INSN(cacop,        cop_r_i)
"cop" reads like "co-processor" while you may just mean "cache op"... as 
the format is for this particular instruction only, you may as well just 
name the format "cacop" and be done with it. (AFAIK it's called "cache" 
before being renamed, allegedly for avoiding the MIPS name, but the new 
name is miserable and unpronounceable, coming up with names is hard but 
people should really try harder...)
> +INSN(lddir,        rr_i)
> +INSN(ldpte,        j_i)
> +INSN(ertn,         empty)
> +INSN(idle,         i)
> +INSN(dbcl,         i)
>   
>   #define output_fcmp(C, PREFIX, SUFFIX)                                         \
>   {                                                                              \
> diff --git a/target/loongarch/helper.h b/target/loongarch/helper.h
> index 97af7ac8aa..c916f2650b 100644
> --- a/target/loongarch/helper.h
> +++ b/target/loongarch/helper.h
> @@ -112,4 +112,8 @@ DEF_HELPER_2(invtlb_all_g, void, env, i32)
>   DEF_HELPER_2(invtlb_all_asid, void, env, tl)
>   DEF_HELPER_3(invtlb_page_asid, void, env, tl, tl)
>   DEF_HELPER_3(invtlb_page_asid_or_g, void, env, tl, tl)
> +DEF_HELPER_4(lddir, tl, env, tl, tl, i32)
> +DEF_HELPER_4(ldpte, void, env, tl, tl, i32)
> +DEF_HELPER_1(ertn, void, env)
> +DEF_HELPER_1(idle, void, env)
>   #endif /* !CONFIG_USER_ONLY */
> diff --git a/target/loongarch/insn_trans/trans_core.c.inc b/target/loongarch/insn_trans/trans_core.c.inc
> index 5a8e9e0643..834ffc03d5 100644
> --- a/target/loongarch/insn_trans/trans_core.c.inc
> +++ b/target/loongarch/insn_trans/trans_core.c.inc
> @@ -35,6 +35,12 @@ GEN_FALSE_TRANS(tlbfill)
>   GEN_FALSE_TRANS(tlbclr)
>   GEN_FALSE_TRANS(tlbflush)
>   GEN_FALSE_TRANS(invtlb)
> +GEN_FALSE_TRANS(cacop)
> +GEN_FALSE_TRANS(ldpte)
> +GEN_FALSE_TRANS(lddir)
> +GEN_FALSE_TRANS(ertn)
> +GEN_FALSE_TRANS(dbcl)
> +GEN_FALSE_TRANS(idle)
>   
>   #else
>   
> @@ -335,4 +341,72 @@ static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
>       return true;
>   }
>   
> +static bool trans_cacop(DisasContext *ctx, arg_cacop *a)
> +{
> +    /* Treat the cacop as a nop */
> +    if (check_plv(ctx)) {
> +        return false;
> +    }
> +    return true;
> +}
> +
> +static bool trans_ldpte(DisasContext *ctx, arg_ldpte *a)
> +{
> +    TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx);
> +    TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
> +
> +    if (check_plv(ctx)) {
> +        return false;
> +    }
> +    gen_helper_ldpte(cpu_env, src1, tcg_constant_tl(a->imm), mem_idx);
> +    return true;
> +}
> +
> +static bool trans_lddir(DisasContext *ctx, arg_lddir *a)
> +{
> +    TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx);
> +    TCGv src = gpr_src(ctx, a->rj, EXT_NONE);
> +    TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
> +
> +    if (check_plv(ctx)) {
> +        return false;
> +    }
> +    gen_helper_lddir(dest, cpu_env, src, tcg_constant_tl(a->imm), mem_idx);
> +    return true;
> +}
> +
> +static bool trans_ertn(DisasContext *ctx, arg_ertn *a)
> +{
> +    if (check_plv(ctx)) {
> +        return false;
> +    }
> +    gen_helper_ertn(cpu_env);
> +    ctx->base.is_jmp = DISAS_EXIT;
> +    return true;
> +}
> +
> +static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a)
> +{
> +    /*
> +     * XXX: not clear which exception should be raised
> +     *      when in debug mode...
> +     */
Then confirm this with other people?
> +    if (check_plv(ctx)) {
> +        return false;
> +    }
> +    generate_exception(ctx, EXCCODE_DBP);
> +    return true;
> +}
> +
> +static bool trans_idle(DisasContext *ctx, arg_idle *a)
> +{
> +    if (check_plv(ctx)) {
> +        return false;
> +    }
> +
> +    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
> +    gen_helper_idle(cpu_env);
> +    ctx->base.is_jmp = DISAS_NORETURN;
> +    return true;
> +}
>   #endif
> diff --git a/target/loongarch/insns.decode b/target/loongarch/insns.decode
> index 6f2a814195..3fdc6e148c 100644
> --- a/target/loongarch/insns.decode
> +++ b/target/loongarch/insns.decode
> @@ -49,6 +49,8 @@
>   &rr_csr       rd rj csr
>   &empty
>   &i_rr         imm rj rk
> +&cop_r_i      cop rj imm
> +&j_i          rj imm
>   
>   #
>   # Formats
> @@ -60,6 +62,7 @@
>   @r_i20                          .... ... imm:s20 rd:5    &r_i
>   @rr_ui5           .... ........ ..... imm:5 rj:5 rd:5    &rr_i
>   @rr_ui6            .... ........ .... imm:6 rj:5 rd:5    &rr_i
> +@rr_ui8              .. ........ .... imm:8 rj:5 rd:5    &rr_i
>   @rr_i12                 .... ...... imm:s12 rj:5 rd:5    &rr_i
>   @rr_ui12                 .... ...... imm:12 rj:5 rd:5    &rr_i
>   @rr_i14s2         .... ....  .............. rj:5 rd:5    &rr_i imm=%i14s2
> @@ -93,6 +96,8 @@
>   @rr_csr                    .... .... csr:14 rj:5 rd:5    &rr_csr
>   @empty          .... ........ ..... ..... ..... .....    &empty
>   @i_rr             ...... ...... ..... rk:5 rj:5 imm:5    &i_rr
> +@cop_r_i              .... ......  imm:s12 rj:5 cop:5    &cop_r_i
> +@j_i               .... ........ .. imm:8 rj:5 .....    &j_i
>   
>   #
>   # Fixed point arithmetic operation instruction
> @@ -473,3 +478,9 @@ tlbfill          0000 01100100 10000 01101 00000 00000    @empty
>   tlbclr           0000 01100100 10000 01000 00000 00000    @empty
>   tlbflush         0000 01100100 10000 01001 00000 00000    @empty
>   invtlb           0000 01100100 10011 ..... ..... .....    @i_rr
> +cacop            0000 011000 ............ ..... .....     @cop_r_i
> +lddir            0000 01100100 00 ........ ..... .....    @rr_ui8
> +ldpte            0000 01100100 01 ........ ..... 00000    @j_i
> +ertn             0000 01100100 10000 01110 00000 00000    @empty
> +idle             0000 01100100 10001 ...............      @i15
> +dbcl             0000 00000010 10101 ...............      @i15
> diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h
> index a5b81bdca3..7035cbd7d5 100644
> --- a/target/loongarch/internals.h
> +++ b/target/loongarch/internals.h
> @@ -16,6 +16,11 @@
>   #define TARGET_PHYS_MASK MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS)
>   #define TARGET_VIRT_MASK MAKE_64BIT_MASK(0, TARGET_VIRT_ADDR_SPACE_BITS)
>   
> +/* Global bit used for lddir/ldpte */
> +#define LOONGARCH_PAGE_HUGE_SHIFT   6
> +/* Global bit for huge page */
> +#define LOONGARCH_HGLOBAL_SHIFT     12
> +
>   void loongarch_translate_init(void);
>   
>   void loongarch_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
> diff --git a/target/loongarch/op_helper.c b/target/loongarch/op_helper.c
> index 48c25e5a9b..6f9742054a 100644
> --- a/target/loongarch/op_helper.c
> +++ b/target/loongarch/op_helper.c
> @@ -91,3 +91,46 @@ target_ulong helper_cpucfg(CPULoongArchState *env, target_ulong rj)
>   {
>       return rj > 21 ? 0 : env->cpucfg[rj];
>   }
> +
> +#ifndef CONFIG_USER_ONLY
> +void helper_ertn(CPULoongArchState *env)
> +{
> +    uint64_t csr_pplv, csr_pie;
> +    if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
> +        csr_pplv = FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV);
> +        csr_pie = FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE);
> +
> +        /* Clear Refill flag DA flag and set pc */

"clear the IsTLBR flag and the DA flag, and set PC", but as the code is 
obvious enough it's better to just drop this comment...

> +        env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0);
> +        env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 0);
> +        env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 1);
> +        env->pc = env->CSR_TLBRERA;
> +        qemu_log_mask(CPU_LOG_INT, "%s: TLBRERA 0x%lx\n",
> +                      __func__, env->CSR_TLBRERA);
> +    } else {
> +        csr_pplv = FIELD_EX64(env->CSR_PRMD, CSR_PRMD, PPLV);
> +        csr_pie = FIELD_EX64(env->CSR_PRMD, CSR_PRMD, PIE);
> +
> +        /* set pc*/
This comment serves no purpose, remove it.
> +        env->pc = env->CSR_ERA;
> +        qemu_log_mask(CPU_LOG_INT, "%s: ERA 0x%lx\n", __func__, env->CSR_ERA);
> +    }
> +    env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, csr_pplv);
> +    env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, csr_pie);
> +
> +    env->lladdr = 1;
> +}
> +
> +void helper_idle(CPULoongArchState *env)
> +{
> +    CPUState *cs = env_cpu(env);
> +
> +    cs->halted = 1;
> +    cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE);
> +    /*
> +     * Last instruction in the block, PC was updated before
> +     * - no need to recover PC and icount
> +     */
It seems no other targets need this explanation, as the behavior should 
be the same across targets -- this block of comment could be removed as 
well.
> +    do_raise_exception(env, EXCP_HLT, 0);
> +}
> +#endif /* !CONFIG_USER_ONLY */
> diff --git a/target/loongarch/tlb_helper.c b/target/loongarch/tlb_helper.c
> index 53dd70de17..a778ae8a66 100644
> --- a/target/loongarch/tlb_helper.c
> +++ b/target/loongarch/tlb_helper.c
> @@ -688,3 +688,90 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>           do_raise_exception(env, cs->exception_index, retaddr);
>       }
>   }
> +
> +target_ulong helper_lddir(CPULoongArchState *env, target_ulong base,
> +                          target_ulong level, uint32_t mem_idx)
> +{
> +    CPUState *cs = env_cpu(env);
> +    target_ulong badvaddr, index, phys, ret;
> +    int shift;
> +    uint64_t dir1_base, dir1_width;
> +    uint64_t dir3_base, dir3_width;
> +    bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1;
> +
> +    badvaddr = env->CSR_TLBRBADV;
> +    base = base & TARGET_PHYS_MASK;
> +
> +    /* 0:8B, 1:16B, 2:32B, 3:64B */
> +    shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH);
> +    shift = (shift + 1) * 3;

This seems wrong, according to the manual on CSR.PWCL.PTEWidth:

- 0 for 64-bit,
- 1 for 128-bit,
- 2 for 192-bit,
- 3 for 256-bit;

so the comment is incorrect, and the PTEWidth=2 case cannot be 
represented with shifts.

> +
> +    if (huge) {
> +        return base;
> +    }
> +    switch (level) {
> +    case 1:
> +        dir1_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE);
> +        dir1_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH);
> +        index = (badvaddr >> dir1_base) & ((1 << dir1_width) - 1);
> +        break;
> +    case 3:
> +        dir3_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE);
> +        dir3_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH);
> +        index = (badvaddr >> dir3_base) & ((1 << dir3_width) - 1);
> +        break;
> +    default:
> +        do_raise_exception(env, EXCCODE_INE, GETPC());
> +        return 0;
> +    }
> +
> +    phys = base | index << shift;
> +    ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK;
> +    return ret;
> +}
> +
> +void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd,
> +                  uint32_t mem_idx)
> +{
> +    CPUState *cs = env_cpu(env);
> +    target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv;
> +    int shift;
> +    bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1;
> +    uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE);
> +    uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH);
> +
> +    base = base & TARGET_PHYS_MASK;
> +
> +    if (huge) {
> +        /* Huge Page. base is paddr */
> +        tmp0 = base ^ LOONGARCH_PAGE_HUGE_SHIFT;
> +        /* Move Global bit */
> +        tmp0 = (tmp0 >> LOONGARCH_HGLOBAL_SHIFT) << R_TLBENTRY_G_SHIFT |
> +               (tmp0 & (~(1 << R_TLBENTRY_G_SHIFT)));
> +        ps = ptbase + ptwidth - 1;
> +        if (odd) {
> +            tmp0 += (1 << ps);
> +        }
> +    } else {
> +        /* 0:8B, 1:16B, 2:32B, 3:64B */
> +        shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH);
> +        shift = (shift + 1) * 3;
Same problem here.
> +        badv = env->CSR_TLBRBADV;
> +
> +        ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1);
> +        ptindex = ptindex & ~0x1;   /* clear bit 0 */
> +        ptoffset0 = ptindex << shift;
> +        ptoffset1 = (ptindex + 1) << shift;
> +
> +        phys = base | (odd ? ptoffset1 : ptoffset0);
> +        tmp0 = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK;
> +        ps = ptbase;
> +    }
> +
> +    if (odd) {
> +        env->CSR_TLBRELO1 = tmp0;
> +    } else {
> +        env->CSR_TLBRELO0 = tmp0;
> +    }
> +    env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps);
> +}
diff mbox series

Patch

diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index ddb69ffecf..2d5bae1af4 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -442,6 +442,8 @@  enum {
     EXCP_LAST = EXCP_FPE,
 };
 
+#define CPU_INTERRUPT_WAKE CPU_INTERRUPT_TGT_INT_0
+
 #define LOONGARCH_CPU_TYPE_SUFFIX "-" TYPE_LOONGARCH_CPU
 #define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX
 #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU
diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c
index 483270f331..516866c2d3 100644
--- a/target/loongarch/disas.c
+++ b/target/loongarch/disas.c
@@ -226,6 +226,17 @@  static void output_i_rr(DisasContext *ctx, arg_i_rr *a, const char *mnemonic)
     output(ctx, mnemonic, "%d, r%d, r%d", a->imm, a->rj, a->rk);
 }
 
+static void output_cop_r_i(DisasContext *ctx, arg_cop_r_i *a,
+                           const char *mnemonic)
+{
+    output(ctx, mnemonic, "%d, r%d, %d", a->cop, a->rj, a->imm);
+}
+
+static void output_j_i(DisasContext *ctx, arg_j_i *a, const char *mnemonic)
+{
+    output(ctx, mnemonic, "r%d, %d", a->rj, a->imm);
+}
+
 #define INSN(insn, type)                                    \
 static bool trans_##insn(DisasContext *ctx, arg_##type * a) \
 {                                                           \
@@ -556,6 +567,12 @@  INSN(tlbfill,      empty)
 INSN(tlbclr,       empty)
 INSN(tlbflush,     empty)
 INSN(invtlb,       i_rr)
+INSN(cacop,        cop_r_i)
+INSN(lddir,        rr_i)
+INSN(ldpte,        j_i)
+INSN(ertn,         empty)
+INSN(idle,         i)
+INSN(dbcl,         i)
 
 #define output_fcmp(C, PREFIX, SUFFIX)                                         \
 {                                                                              \
diff --git a/target/loongarch/helper.h b/target/loongarch/helper.h
index 97af7ac8aa..c916f2650b 100644
--- a/target/loongarch/helper.h
+++ b/target/loongarch/helper.h
@@ -112,4 +112,8 @@  DEF_HELPER_2(invtlb_all_g, void, env, i32)
 DEF_HELPER_2(invtlb_all_asid, void, env, tl)
 DEF_HELPER_3(invtlb_page_asid, void, env, tl, tl)
 DEF_HELPER_3(invtlb_page_asid_or_g, void, env, tl, tl)
+DEF_HELPER_4(lddir, tl, env, tl, tl, i32)
+DEF_HELPER_4(ldpte, void, env, tl, tl, i32)
+DEF_HELPER_1(ertn, void, env)
+DEF_HELPER_1(idle, void, env)
 #endif /* !CONFIG_USER_ONLY */
diff --git a/target/loongarch/insn_trans/trans_core.c.inc b/target/loongarch/insn_trans/trans_core.c.inc
index 5a8e9e0643..834ffc03d5 100644
--- a/target/loongarch/insn_trans/trans_core.c.inc
+++ b/target/loongarch/insn_trans/trans_core.c.inc
@@ -35,6 +35,12 @@  GEN_FALSE_TRANS(tlbfill)
 GEN_FALSE_TRANS(tlbclr)
 GEN_FALSE_TRANS(tlbflush)
 GEN_FALSE_TRANS(invtlb)
+GEN_FALSE_TRANS(cacop)
+GEN_FALSE_TRANS(ldpte)
+GEN_FALSE_TRANS(lddir)
+GEN_FALSE_TRANS(ertn)
+GEN_FALSE_TRANS(dbcl)
+GEN_FALSE_TRANS(idle)
 
 #else
 
@@ -335,4 +341,72 @@  static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
     return true;
 }
 
+static bool trans_cacop(DisasContext *ctx, arg_cacop *a)
+{
+    /* Treat the cacop as a nop */
+    if (check_plv(ctx)) {
+        return false;
+    }
+    return true;
+}
+
+static bool trans_ldpte(DisasContext *ctx, arg_ldpte *a)
+{
+    TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx);
+    TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
+
+    if (check_plv(ctx)) {
+        return false;
+    }
+    gen_helper_ldpte(cpu_env, src1, tcg_constant_tl(a->imm), mem_idx);
+    return true;
+}
+
+static bool trans_lddir(DisasContext *ctx, arg_lddir *a)
+{
+    TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx);
+    TCGv src = gpr_src(ctx, a->rj, EXT_NONE);
+    TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+
+    if (check_plv(ctx)) {
+        return false;
+    }
+    gen_helper_lddir(dest, cpu_env, src, tcg_constant_tl(a->imm), mem_idx);
+    return true;
+}
+
+static bool trans_ertn(DisasContext *ctx, arg_ertn *a)
+{
+    if (check_plv(ctx)) {
+        return false;
+    }
+    gen_helper_ertn(cpu_env);
+    ctx->base.is_jmp = DISAS_EXIT;
+    return true;
+}
+
+static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a)
+{
+    /*
+     * XXX: not clear which exception should be raised
+     *      when in debug mode...
+     */
+    if (check_plv(ctx)) {
+        return false;
+    }
+    generate_exception(ctx, EXCCODE_DBP);
+    return true;
+}
+
+static bool trans_idle(DisasContext *ctx, arg_idle *a)
+{
+    if (check_plv(ctx)) {
+        return false;
+    }
+
+    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
+    gen_helper_idle(cpu_env);
+    ctx->base.is_jmp = DISAS_NORETURN;
+    return true;
+}
 #endif
diff --git a/target/loongarch/insns.decode b/target/loongarch/insns.decode
index 6f2a814195..3fdc6e148c 100644
--- a/target/loongarch/insns.decode
+++ b/target/loongarch/insns.decode
@@ -49,6 +49,8 @@ 
 &rr_csr       rd rj csr
 &empty
 &i_rr         imm rj rk
+&cop_r_i      cop rj imm
+&j_i          rj imm
 
 #
 # Formats
@@ -60,6 +62,7 @@ 
 @r_i20                          .... ... imm:s20 rd:5    &r_i
 @rr_ui5           .... ........ ..... imm:5 rj:5 rd:5    &rr_i
 @rr_ui6            .... ........ .... imm:6 rj:5 rd:5    &rr_i
+@rr_ui8              .. ........ .... imm:8 rj:5 rd:5    &rr_i
 @rr_i12                 .... ...... imm:s12 rj:5 rd:5    &rr_i
 @rr_ui12                 .... ...... imm:12 rj:5 rd:5    &rr_i
 @rr_i14s2         .... ....  .............. rj:5 rd:5    &rr_i imm=%i14s2
@@ -93,6 +96,8 @@ 
 @rr_csr                    .... .... csr:14 rj:5 rd:5    &rr_csr
 @empty          .... ........ ..... ..... ..... .....    &empty
 @i_rr             ...... ...... ..... rk:5 rj:5 imm:5    &i_rr
+@cop_r_i              .... ......  imm:s12 rj:5 cop:5    &cop_r_i
+@j_i               .... ........ .. imm:8 rj:5 .....    &j_i
 
 #
 # Fixed point arithmetic operation instruction
@@ -473,3 +478,9 @@  tlbfill          0000 01100100 10000 01101 00000 00000    @empty
 tlbclr           0000 01100100 10000 01000 00000 00000    @empty
 tlbflush         0000 01100100 10000 01001 00000 00000    @empty
 invtlb           0000 01100100 10011 ..... ..... .....    @i_rr
+cacop            0000 011000 ............ ..... .....     @cop_r_i
+lddir            0000 01100100 00 ........ ..... .....    @rr_ui8
+ldpte            0000 01100100 01 ........ ..... 00000    @j_i
+ertn             0000 01100100 10000 01110 00000 00000    @empty
+idle             0000 01100100 10001 ...............      @i15
+dbcl             0000 00000010 10101 ...............      @i15
diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h
index a5b81bdca3..7035cbd7d5 100644
--- a/target/loongarch/internals.h
+++ b/target/loongarch/internals.h
@@ -16,6 +16,11 @@ 
 #define TARGET_PHYS_MASK MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS)
 #define TARGET_VIRT_MASK MAKE_64BIT_MASK(0, TARGET_VIRT_ADDR_SPACE_BITS)
 
+/* Global bit used for lddir/ldpte */
+#define LOONGARCH_PAGE_HUGE_SHIFT   6
+/* Global bit for huge page */
+#define LOONGARCH_HGLOBAL_SHIFT     12
+
 void loongarch_translate_init(void);
 
 void loongarch_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
diff --git a/target/loongarch/op_helper.c b/target/loongarch/op_helper.c
index 48c25e5a9b..6f9742054a 100644
--- a/target/loongarch/op_helper.c
+++ b/target/loongarch/op_helper.c
@@ -91,3 +91,46 @@  target_ulong helper_cpucfg(CPULoongArchState *env, target_ulong rj)
 {
     return rj > 21 ? 0 : env->cpucfg[rj];
 }
+
+#ifndef CONFIG_USER_ONLY
+void helper_ertn(CPULoongArchState *env)
+{
+    uint64_t csr_pplv, csr_pie;
+    if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
+        csr_pplv = FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV);
+        csr_pie = FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE);
+
+        /* Clear Refill flag DA flag and set pc */
+        env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0);
+        env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 0);
+        env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 1);
+        env->pc = env->CSR_TLBRERA;
+        qemu_log_mask(CPU_LOG_INT, "%s: TLBRERA 0x%lx\n",
+                      __func__, env->CSR_TLBRERA);
+    } else {
+        csr_pplv = FIELD_EX64(env->CSR_PRMD, CSR_PRMD, PPLV);
+        csr_pie = FIELD_EX64(env->CSR_PRMD, CSR_PRMD, PIE);
+
+        /* set pc*/
+        env->pc = env->CSR_ERA;
+        qemu_log_mask(CPU_LOG_INT, "%s: ERA 0x%lx\n", __func__, env->CSR_ERA);
+    }
+    env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, csr_pplv);
+    env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, csr_pie);
+
+    env->lladdr = 1;
+}
+
+void helper_idle(CPULoongArchState *env)
+{
+    CPUState *cs = env_cpu(env);
+
+    cs->halted = 1;
+    cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE);
+    /*
+     * Last instruction in the block, PC was updated before
+     * - no need to recover PC and icount
+     */
+    do_raise_exception(env, EXCP_HLT, 0);
+}
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target/loongarch/tlb_helper.c b/target/loongarch/tlb_helper.c
index 53dd70de17..a778ae8a66 100644
--- a/target/loongarch/tlb_helper.c
+++ b/target/loongarch/tlb_helper.c
@@ -688,3 +688,90 @@  bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
         do_raise_exception(env, cs->exception_index, retaddr);
     }
 }
+
+target_ulong helper_lddir(CPULoongArchState *env, target_ulong base,
+                          target_ulong level, uint32_t mem_idx)
+{
+    CPUState *cs = env_cpu(env);
+    target_ulong badvaddr, index, phys, ret;
+    int shift;
+    uint64_t dir1_base, dir1_width;
+    uint64_t dir3_base, dir3_width;
+    bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1;
+
+    badvaddr = env->CSR_TLBRBADV;
+    base = base & TARGET_PHYS_MASK;
+
+    /* 0:8B, 1:16B, 2:32B, 3:64B */
+    shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH);
+    shift = (shift + 1) * 3;
+
+    if (huge) {
+        return base;
+    }
+    switch (level) {
+    case 1:
+        dir1_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE);
+        dir1_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH);
+        index = (badvaddr >> dir1_base) & ((1 << dir1_width) - 1);
+        break;
+    case 3:
+        dir3_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE);
+        dir3_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH);
+        index = (badvaddr >> dir3_base) & ((1 << dir3_width) - 1);
+        break;
+    default:
+        do_raise_exception(env, EXCCODE_INE, GETPC());
+        return 0;
+    }
+
+    phys = base | index << shift;
+    ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK;
+    return ret;
+}
+
+void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd,
+                  uint32_t mem_idx)
+{
+    CPUState *cs = env_cpu(env);
+    target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv;
+    int shift;
+    bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1;
+    uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE);
+    uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH);
+
+    base = base & TARGET_PHYS_MASK;
+
+    if (huge) {
+        /* Huge Page. base is paddr */
+        tmp0 = base ^ LOONGARCH_PAGE_HUGE_SHIFT;
+        /* Move Global bit */
+        tmp0 = (tmp0 >> LOONGARCH_HGLOBAL_SHIFT) << R_TLBENTRY_G_SHIFT |
+               (tmp0 & (~(1 << R_TLBENTRY_G_SHIFT)));
+        ps = ptbase + ptwidth - 1;
+        if (odd) {
+            tmp0 += (1 << ps);
+        }
+    } else {
+        /* 0:8B, 1:16B, 2:32B, 3:64B */
+        shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH);
+        shift = (shift + 1) * 3;
+        badv = env->CSR_TLBRBADV;
+
+        ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1);
+        ptindex = ptindex & ~0x1;   /* clear bit 0 */
+        ptoffset0 = ptindex << shift;
+        ptoffset1 = (ptindex + 1) << shift;
+
+        phys = base | (odd ? ptoffset1 : ptoffset0);
+        tmp0 = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK;
+        ps = ptbase;
+    }
+
+    if (odd) {
+        env->CSR_TLBRELO1 = tmp0;
+    } else {
+        env->CSR_TLBRELO0 = tmp0;
+    }
+    env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps);
+}