@@ -45,6 +45,7 @@ static const char * const excp_names[EXCP_LAST + 1] = {
[EXCP_TLBPE] = "TLB priviledged error",
[EXCP_TLBNX] = "TLB execute-inhibit",
[EXCP_TLBNR] = "TLB read-inhibit",
+ [EXCP_DBP] = "debug breakpoint",
};
const char *loongarch_exception_name(int32_t exception)
@@ -166,10 +166,13 @@ enum {
EXCP_TLBPE,
EXCP_TLBNX,
EXCP_TLBNR,
+ EXCP_DBP,
- EXCP_LAST = EXCP_TLBNR,
+ EXCP_LAST = EXCP_DBP,
};
+#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
@@ -112,4 +112,8 @@ DEF_HELPER_1(tlbflush, void, env)
DEF_HELPER_4(invtlb, void, env, tl, tl, tl)
DEF_HELPER_2(iocsr_read, i64, env, tl)
DEF_HELPER_4(iocsr_write, void, env, tl, tl, i32)
+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 */
@@ -34,6 +34,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
static bool trans_csrrd(DisasContext *ctx, unsigned rd, unsigned csr)
@@ -173,6 +179,11 @@ static bool trans_iocsrwr_d(DisasContext *ctx, arg_iocsrwr_d *a)
return true;
}
+static bool trans_cacop(DisasContext *ctx, arg_cacop *a)
+{
+ return false;
+}
+
static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a)
{
gen_helper_tlbsrch(cpu_env);
@@ -219,4 +230,50 @@ static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
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);
+
+ gen_helper_ldpte(cpu_env, src1, tcg_constant_tl(a->seq), 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);
+
+ gen_helper_lddir(dest, cpu_env, src, tcg_constant_tl(a->level), mem_idx);
+ return true;
+}
+
+static bool trans_ertn(DisasContext *ctx, arg_ertn *a)
+{
+ 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...
+ */
+ generate_exception(ctx, EXCP_DBP);
+ return true;
+}
+
+static bool trans_idle(DisasContext *ctx, arg_idle *a)
+{
+ ctx->base.pc_next += 4;
+ tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
+ ctx->base.pc_next -= 4;
+ gen_helper_idle(cpu_env);
+ ctx->base.is_jmp = DISAS_NORETURN;
+ return true;
+}
+
#endif
@@ -42,9 +42,12 @@
%offs16 10:s16
%offs 0:s10 10:16
%csr 10:14
+%cop 0:5
%addr 10:5
%info 5:5
%invop 0:5
+%level 10:8
+%seq 10:8
#
# Argument sets
@@ -87,8 +90,11 @@
&fmt_offs offs
&fmt_rjrdoffs16 rj rd offs16
&fmt_rdrjcsr rd rj csr
+&fmt_coprjsi12 cop rj si12
&fmt_empty
&fmt_invtlb addr info invop
+&fmt_rdrjlevel rd rj level
+&fmt_rjseq rj seq
#
# Formats
@@ -131,8 +137,11 @@
@fmt_offs .... .. .......................... &fmt_offs %offs
@fmt_rjrdoffs16 .... .. ................ ..... ..... &fmt_rjrdoffs16 %rj %rd %offs16
@fmt_rdrjcsr .... .... .............. ..... ..... &fmt_rdrjcsr %rd %rj %csr
+@fmt_coprjsi12 .... ...... ............ ..... ..... &fmt_coprjsi12 %cop %rj %si12
@fmt_empty .... ........ ..... ..... ..... ..... &fmt_empty
@fmt_invtlb ...... ...... ..... ..... ..... ..... &fmt_invtlb %addr %info %invop
+@fmt_rdrjlevel .... ........ .. ........ ..... ..... &fmt_rdrjlevel %rd %rj %level
+@fmt_rjseq .... ........ .. ........ ..... ..... &fmt_rjseq %rj %seq
#
# Fixed point arithmetic operation instruction
@@ -501,6 +510,7 @@ iocsrwr_b 0000 01100100 10000 00100 ..... ..... @fmt_rdrj
iocsrwr_h 0000 01100100 10000 00101 ..... ..... @fmt_rdrj
iocsrwr_w 0000 01100100 10000 00110 ..... ..... @fmt_rdrj
iocsrwr_d 0000 01100100 10000 00111 ..... ..... @fmt_rdrj
+cacop 0000 011000 ............ ..... ..... @fmt_coprjsi12
tlbsrch 0000 01100100 10000 01010 00000 00000 @fmt_empty
tlbrd 0000 01100100 10000 01011 00000 00000 @fmt_empty
tlbwr 0000 01100100 10000 01100 00000 00000 @fmt_empty
@@ -508,3 +518,8 @@ tlbfill 0000 01100100 10000 01101 00000 00000 @fmt_empty
tlbclr 0000 01100100 10000 01000 00000 00000 @fmt_empty
tlbflush 0000 01100100 10000 01001 00000 00000 @fmt_empty
invtlb 0000 01100100 10011 ..... ..... ..... @fmt_invtlb
+lddir 0000 01100100 00 ........ ..... ..... @fmt_rdrjlevel
+ldpte 0000 01100100 01 ........ ..... 00000 @fmt_rjseq
+ertn 0000 01100100 10000 01110 00000 00000 @fmt_empty
+idle 0000 01100100 10001 ............... @fmt_whint
+dbcl 0000 00000010 10101 ............... @fmt_code
@@ -83,3 +83,51 @@ target_ulong helper_cpucfg(CPULoongArchState *env, target_ulong rj)
{
return env->cpucfg[rj];
}
+
+#ifndef CONFIG_USER_ONLY
+static inline void exception_return(CPULoongArchState *env)
+{
+ if (env->CSR_TLBRERA & 0x1) {
+ env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV,
+ FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV));
+ env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE,
+ FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE));
+ /* Clear Refill flag and set pc */
+ env->CSR_TLBRERA &= (~0x1);
+ env->pc = env->CSR_TLBRERA;
+ if (qemu_loglevel_mask(CPU_LOG_INT)) {
+ qemu_log("%s: TLBRERA 0x%lx\n", __func__, env->CSR_TLBRERA);
+ }
+ } else {
+ env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV,
+ FIELD_EX64(env->CSR_PRMD, CSR_PRMD, PPLV));
+ env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE,
+ FIELD_EX64(env->CSR_PRMD, CSR_PRMD, PIE));
+ /* set pc*/
+ env->pc = env->CSR_ERA;
+ if (qemu_loglevel_mask(CPU_LOG_INT)) {
+ qemu_log("%s: ERA 0x%lx\n", __func__, env->CSR_ERA);
+ }
+ }
+}
+
+void helper_ertn(CPULoongArchState *env)
+{
+ exception_return(env);
+ 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 */
@@ -833,3 +833,86 @@ 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)
+{
+ target_ulong badvaddr;
+ target_ulong index;
+ target_ulong vaddr;
+ int shift;
+ uint64_t dir1_base, dir1_width;
+ uint64_t dir3_base, dir3_width;
+
+ badvaddr = env->CSR_TLBRBADV;
+
+ /* 0:8B, 1:16B, 2:32B, 3:64B */
+ shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH);
+ shift = (shift + 1) * 3;
+
+ 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, EXCP_INE, GETPC());
+ return 0;
+ }
+
+ vaddr = base | index << shift;
+ return cpu_ldq_mmuidx_ra(env, vaddr, mem_idx, GETPC());
+}
+
+void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd,
+ uint32_t mem_idx)
+{
+ target_ulong vaddr;
+ target_ulong tmp0;
+ target_ulong ptindex, ptoffset0, ptoffset1;
+ target_ulong pagesize;
+ target_ulong badv;
+ int shift;
+ bool huge = base & LOONGARCH_PAGE_HUGE;
+ uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE);
+ uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH);
+
+ if (huge) {
+ /* Huge Page. base is paddr */
+ tmp0 = base ^ LOONGARCH_PAGE_HUGE;
+ /* move Global bit */
+ tmp0 |= ((tmp0 & LOONGARCH_HUGE_GLOBAL)
+ >> (LOONGARCH_HUGE_GLOBAL_SH - R_CSR_TLBELO0_G_SHIFT));
+ pagesize = ptbase + ptwidth - 1;
+ if (odd) {
+ tmp0 += (1 << pagesize);
+ }
+ } 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;
+
+ vaddr = base | (odd ? ptoffset1 : ptoffset0);
+ tmp0 = cpu_ldq_mmuidx_ra(env, vaddr, mem_idx, GETPC());
+ pagesize = ptbase;
+ }
+ if (odd) {
+ env->CSR_TLBRELO1 = tmp0;
+ } else {
+ env->CSR_TLBRELO0 = tmp0;
+ }
+ env->CSR_TLBREHI = env->CSR_TLBREHI & (~0x3f);
+ env->CSR_TLBREHI = env->CSR_TLBREHI | pagesize;
+}