new file mode 100644
@@ -0,0 +1,322 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * LoongArch emulation helpers for csr registers
+ *
+ * Copyright (c) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "cpu.h"
+#include "internals.h"
+#include "qemu/host-utils.h"
+#include "exec/helper-proto.h"
+#include "exec/exec-all.h"
+#include "exec/cpu_ldst.h"
+#include "hw/irq.h"
+#include "cpu-csr.h"
+#include "hw/loongarch/loongarch.h"
+#include "tcg/tcg-ldst.h"
+
+target_ulong helper_csr_rdq(CPULoongArchState *env, uint64_t csr)
+{
+ int64_t v;
+
+ switch (csr) {
+ case LOONGARCH_CSR_PGD:
+ if (env->CSR_TLBRERA & 0x1) {
+ v = env->CSR_TLBRBADV;
+ } else {
+ v = env->CSR_BADV;
+ }
+
+ if ((v >> 63) & 0x1) {
+ v = env->CSR_PGDH;
+ } else {
+ v = env->CSR_PGDL;
+ }
+ v = v & TARGET_PHYS_MASK;
+ break;
+ case LOONGARCH_CSR_CPUID:
+ v = (env_cpu(env))->cpu_index;
+ break;
+ case LOONGARCH_CSR_TVAL:
+ v = cpu_loongarch_get_stable_timer_ticks(env);
+ break;
+ default:
+ assert(0);
+ }
+
+ return v;
+}
+
+target_ulong helper_csr_wrq(CPULoongArchState *env, target_ulong val,
+ uint64_t csr)
+{
+ int64_t old_v;
+ old_v = -1;
+
+ switch (csr) {
+ case LOONGARCH_CSR_ASID:
+ old_v = env->CSR_ASID;
+ env->CSR_ASID = val;
+ if (old_v != val) {
+ tlb_flush(env_cpu(env));
+ }
+ break;
+ case LOONGARCH_CSR_TCFG:
+ old_v = env->CSR_TCFG;
+ cpu_loongarch_store_stable_timer_config(env, val);
+ break;
+ case LOONGARCH_CSR_TINTCLR:
+ old_v = 0;
+ qemu_irq_lower(env->irq[IRQ_TIMER]);
+ break;
+ default:
+ assert(0);
+ }
+
+ return old_v;
+}
+
+target_ulong helper_csr_xchgq(CPULoongArchState *env, target_ulong val,
+ target_ulong mask, uint64_t csr)
+{
+ target_ulong tmp;
+ target_ulong v = val & mask;
+
+#define CASE_CSR_XCHGQ(csr) \
+ case LOONGARCH_CSR_ ## csr: \
+ { \
+ val = env->CSR_ ## csr; \
+ env->CSR_ ## csr = (env->CSR_ ## csr) & (~mask); \
+ env->CSR_ ## csr = (env->CSR_ ## csr) | v; \
+ break; \
+ }; \
+
+ switch (csr) {
+ CASE_CSR_XCHGQ(CRMD)
+ CASE_CSR_XCHGQ(PRMD)
+ CASE_CSR_XCHGQ(EUEN)
+ CASE_CSR_XCHGQ(MISC)
+ CASE_CSR_XCHGQ(ECFG)
+ CASE_CSR_XCHGQ(ESTAT)
+ CASE_CSR_XCHGQ(ERA)
+ CASE_CSR_XCHGQ(BADV)
+ CASE_CSR_XCHGQ(BADI)
+ CASE_CSR_XCHGQ(EENTRY)
+ CASE_CSR_XCHGQ(TLBIDX)
+ CASE_CSR_XCHGQ(TLBEHI)
+ CASE_CSR_XCHGQ(TLBELO0)
+ CASE_CSR_XCHGQ(TLBELO1)
+ CASE_CSR_XCHGQ(ASID)
+ CASE_CSR_XCHGQ(PGDL)
+ CASE_CSR_XCHGQ(PGDH)
+ CASE_CSR_XCHGQ(PGD)
+ CASE_CSR_XCHGQ(PWCL)
+ CASE_CSR_XCHGQ(PWCH)
+ CASE_CSR_XCHGQ(STLBPS)
+ CASE_CSR_XCHGQ(RVACFG)
+ CASE_CSR_XCHGQ(CPUID)
+ CASE_CSR_XCHGQ(PRCFG1)
+ CASE_CSR_XCHGQ(PRCFG2)
+ CASE_CSR_XCHGQ(PRCFG3)
+ CASE_CSR_XCHGQ(SAVE0)
+ CASE_CSR_XCHGQ(SAVE1)
+ CASE_CSR_XCHGQ(SAVE2)
+ CASE_CSR_XCHGQ(SAVE3)
+ CASE_CSR_XCHGQ(SAVE4)
+ CASE_CSR_XCHGQ(SAVE5)
+ CASE_CSR_XCHGQ(SAVE6)
+ CASE_CSR_XCHGQ(SAVE7)
+ CASE_CSR_XCHGQ(TMID)
+ case LOONGARCH_CSR_TCFG:
+ val = env->CSR_TCFG;
+ tmp = val & ~mask;
+ tmp |= v;
+ cpu_loongarch_store_stable_timer_config(env, tmp);
+ break;
+ CASE_CSR_XCHGQ(TVAL)
+ CASE_CSR_XCHGQ(CNTC)
+ CASE_CSR_XCHGQ(TINTCLR)
+ CASE_CSR_XCHGQ(LLBCTL)
+ CASE_CSR_XCHGQ(IMPCTL1)
+ CASE_CSR_XCHGQ(IMPCTL2)
+ CASE_CSR_XCHGQ(TLBRENTRY)
+ CASE_CSR_XCHGQ(TLBRBADV)
+ CASE_CSR_XCHGQ(TLBRERA)
+ CASE_CSR_XCHGQ(TLBRSAVE)
+ CASE_CSR_XCHGQ(TLBRELO0)
+ CASE_CSR_XCHGQ(TLBRELO1)
+ CASE_CSR_XCHGQ(TLBREHI)
+ CASE_CSR_XCHGQ(TLBRPRMD)
+ CASE_CSR_XCHGQ(MERRCTL)
+ CASE_CSR_XCHGQ(MERRINFO)
+ CASE_CSR_XCHGQ(MERRINFO1)
+ CASE_CSR_XCHGQ(MERRENT)
+ CASE_CSR_XCHGQ(MERRERA)
+ CASE_CSR_XCHGQ(MERRSAVE)
+ CASE_CSR_XCHGQ(CTAG)
+ CASE_CSR_XCHGQ(DMWIN0)
+ CASE_CSR_XCHGQ(DMWIN1)
+ CASE_CSR_XCHGQ(DMWIN2)
+ CASE_CSR_XCHGQ(DMWIN3)
+ CASE_CSR_XCHGQ(PERFCTRL0)
+ CASE_CSR_XCHGQ(PERFCNTR0)
+ CASE_CSR_XCHGQ(PERFCTRL1)
+ CASE_CSR_XCHGQ(PERFCNTR1)
+ CASE_CSR_XCHGQ(PERFCTRL2)
+ CASE_CSR_XCHGQ(PERFCNTR2)
+ CASE_CSR_XCHGQ(PERFCTRL3)
+ CASE_CSR_XCHGQ(PERFCNTR3)
+ /* debug */
+ CASE_CSR_XCHGQ(MWPC)
+ CASE_CSR_XCHGQ(MWPS)
+ CASE_CSR_XCHGQ(DB0ADDR)
+ CASE_CSR_XCHGQ(DB0MASK)
+ CASE_CSR_XCHGQ(DB0CTL)
+ CASE_CSR_XCHGQ(DB0ASID)
+ CASE_CSR_XCHGQ(DB1ADDR)
+ CASE_CSR_XCHGQ(DB1MASK)
+ CASE_CSR_XCHGQ(DB1CTL)
+ CASE_CSR_XCHGQ(DB1ASID)
+ CASE_CSR_XCHGQ(DB2ADDR)
+ CASE_CSR_XCHGQ(DB2MASK)
+ CASE_CSR_XCHGQ(DB2CTL)
+ CASE_CSR_XCHGQ(DB2ASID)
+ CASE_CSR_XCHGQ(DB3ADDR)
+ CASE_CSR_XCHGQ(DB3MASK)
+ CASE_CSR_XCHGQ(DB3CTL)
+ CASE_CSR_XCHGQ(DB3ASID)
+ CASE_CSR_XCHGQ(FWPC)
+ CASE_CSR_XCHGQ(FWPS)
+ CASE_CSR_XCHGQ(IB0ADDR)
+ CASE_CSR_XCHGQ(IB0MASK)
+ CASE_CSR_XCHGQ(IB0CTL)
+ CASE_CSR_XCHGQ(IB0ASID)
+ CASE_CSR_XCHGQ(IB1ADDR)
+ CASE_CSR_XCHGQ(IB1MASK)
+ CASE_CSR_XCHGQ(IB1CTL)
+ CASE_CSR_XCHGQ(IB1ASID)
+ CASE_CSR_XCHGQ(IB2ADDR)
+ CASE_CSR_XCHGQ(IB2MASK)
+ CASE_CSR_XCHGQ(IB2CTL)
+ CASE_CSR_XCHGQ(IB2ASID)
+ CASE_CSR_XCHGQ(IB3ADDR)
+ CASE_CSR_XCHGQ(IB3MASK)
+ CASE_CSR_XCHGQ(IB3CTL)
+ CASE_CSR_XCHGQ(IB3ASID)
+ CASE_CSR_XCHGQ(IB4ADDR)
+ CASE_CSR_XCHGQ(IB4MASK)
+ CASE_CSR_XCHGQ(IB4CTL)
+ CASE_CSR_XCHGQ(IB4ASID)
+ CASE_CSR_XCHGQ(IB5ADDR)
+ CASE_CSR_XCHGQ(IB5MASK)
+ CASE_CSR_XCHGQ(IB5CTL)
+ CASE_CSR_XCHGQ(IB5ASID)
+ CASE_CSR_XCHGQ(IB6ADDR)
+ CASE_CSR_XCHGQ(IB6MASK)
+ CASE_CSR_XCHGQ(IB6CTL)
+ CASE_CSR_XCHGQ(IB6ASID)
+ CASE_CSR_XCHGQ(IB7ADDR)
+ CASE_CSR_XCHGQ(IB7MASK)
+ CASE_CSR_XCHGQ(IB7CTL)
+ CASE_CSR_XCHGQ(IB7ASID)
+ CASE_CSR_XCHGQ(DBG)
+ CASE_CSR_XCHGQ(DERA)
+ CASE_CSR_XCHGQ(DESAVE)
+ default :
+ assert(0);
+ }
+
+#undef CASE_CSR_XCHGQ
+ return val;
+}
+
+/*
+ * For per core address 0x10xx(IPI) 0x18xx(EXTIOI)
+ * need extra adjust the iocsr addr.
+ */
+uint64_t helper_iocsr_read(CPULoongArchState *env, target_ulong r_addr,
+ uint32_t size)
+{
+ LoongArchMachineState *lams = LOONGARCH_MACHINE(qdev_get_machine());
+ int cpuid = env_cpu(env)->cpu_index;
+
+ if (((r_addr & 0xff00) == 0x1000) || ((r_addr & 0xff00) == 0x1800)) {
+ r_addr = r_addr + ((target_ulong)(cpuid & 0x3) << 8);
+ }
+
+ if (size == 1) {
+ return address_space_ldub(lams->address_space_iocsr, r_addr,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ } else if (size == 2) {
+ return address_space_lduw(lams->address_space_iocsr, r_addr,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ } else if (size == 4) {
+ return address_space_ldl(lams->address_space_iocsr, r_addr,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ } else if (size == 8) {
+ return address_space_ldq(lams->address_space_iocsr, r_addr,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ }
+ return 0;
+}
+
+void helper_iocsr_write(CPULoongArchState *env, target_ulong w_addr,
+ target_ulong val, uint32_t size)
+{
+ LoongArchMachineState *lams = LOONGARCH_MACHINE(qdev_get_machine());
+ int cpuid = env_cpu(env)->cpu_index;
+ int mask, i;
+
+ /*
+ * For IPI send, Mail send, ANY send adjust addr and val
+ * according to their real meaning
+ */
+ if (w_addr == 0x1040) { /* IPI send */
+ cpuid = (val >> 16) & 0x3ff;
+ val = 1UL << (val & 0x1f);
+ w_addr = 0x1008;
+ } else if (w_addr == 0x1048) { /* Mail Send */
+ cpuid = (val >> 16) & 0x3ff;
+ w_addr = 0x1020 + (val & 0x1c);
+ val = val >> 32;
+ mask = (val >> 27) & 0xf;
+ size = 4;
+ } else if (w_addr == 0x1158) { /* ANY send */
+ cpuid = (val >> 16) & 0x3ff;
+ w_addr = val & 0xffff;
+ val = val >> 32;
+ mask = (val >> 27) & 0xf;
+ size = 1;
+
+ for (i = 0; i < 4; i++) {
+ if (!((mask >> i) & 1)) {
+ address_space_stb(lams->address_space_iocsr, w_addr,
+ val, MEMTXATTRS_UNSPECIFIED, NULL);
+ }
+ w_addr = w_addr + 1;
+ val = val >> 8;
+ }
+ return;
+ }
+
+ if (((w_addr & 0xff00) == 0x1000) || ((w_addr & 0xff00) == 0x1800)) {
+ w_addr = w_addr + ((target_ulong)(cpuid & 0x3) << 8);
+ }
+
+ if (size == 1) {
+ address_space_stb(lams->address_space_iocsr, w_addr,
+ val, MEMTXATTRS_UNSPECIFIED, NULL);
+ } else if (size == 2) {
+ address_space_stw(lams->address_space_iocsr, w_addr,
+ val, MEMTXATTRS_UNSPECIFIED, NULL);
+ } else if (size == 4) {
+ address_space_stl(lams->address_space_iocsr, w_addr,
+ val, MEMTXATTRS_UNSPECIFIED, NULL);
+ } else if (size == 8) {
+ address_space_stq(lams->address_space_iocsr, w_addr,
+ val, MEMTXATTRS_UNSPECIFIED, NULL);
+ }
+}
@@ -92,3 +92,14 @@ DEF_HELPER_2(frint_s, i64, env, i64)
DEF_HELPER_2(frint_d, i64, env, i64)
DEF_HELPER_FLAGS_2(set_rounding_mode, TCG_CALL_NO_RWG, void, env, i32)
+
+/*Core functions */
+#ifndef CONFIG_USER_ONLY
+DEF_HELPER_1(check_plv, void, env)
+
+DEF_HELPER_2(csr_rdq, i64, env, i64)
+DEF_HELPER_3(csr_wrq, i64, env, tl, i64)
+DEF_HELPER_4(csr_xchgq, i64, env, tl, tl, i64)
+DEF_HELPER_3(iocsr_read, i64, env, tl, i32)
+DEF_HELPER_4(iocsr_write, void, env, tl, tl, i32)
+#endif /* !CONFIG_USER_ONLY */
new file mode 100644
@@ -0,0 +1,437 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * LoongArch translate functions for system mode
+ *
+ * Copyright (c) 2021 Loongson Technology Corporation Limited
+ */
+
+/* Privileged instruction translation */
+
+#include "cpu-csr.h"
+
+#ifdef CONFIG_USER_ONLY
+
+#define GEN_FALSE_TRANS(name) \
+static bool trans_##name(DisasContext *ctx, arg_##name * a) \
+{ \
+ return false; \
+}
+
+GEN_FALSE_TRANS(csrrd)
+GEN_FALSE_TRANS(csrwr)
+GEN_FALSE_TRANS(csrxchg)
+GEN_FALSE_TRANS(iocsrrd_b)
+GEN_FALSE_TRANS(iocsrrd_h)
+GEN_FALSE_TRANS(iocsrrd_w)
+GEN_FALSE_TRANS(iocsrrd_d)
+GEN_FALSE_TRANS(iocsrwr_b)
+GEN_FALSE_TRANS(iocsrwr_h)
+GEN_FALSE_TRANS(iocsrwr_w)
+GEN_FALSE_TRANS(iocsrwr_d)
+
+#else
+static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
+{
+ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+
+ gen_helper_check_plv(cpu_env);
+
+#define CASE_CSR_RDQ(csr) \
+ case LOONGARCH_CSR_ ## csr: \
+ { \
+ tcg_gen_ld_tl(dest, cpu_env, offsetof(CPULoongArchState, CSR_##csr)); \
+ break; \
+ }; \
+
+ switch (a->csr) {
+ CASE_CSR_RDQ(CRMD)
+ CASE_CSR_RDQ(PRMD)
+ CASE_CSR_RDQ(EUEN)
+ CASE_CSR_RDQ(MISC)
+ CASE_CSR_RDQ(ECFG)
+ CASE_CSR_RDQ(ESTAT)
+ CASE_CSR_RDQ(ERA)
+ CASE_CSR_RDQ(BADV)
+ CASE_CSR_RDQ(BADI)
+ CASE_CSR_RDQ(EENTRY)
+ CASE_CSR_RDQ(TLBIDX)
+ CASE_CSR_RDQ(TLBEHI)
+ CASE_CSR_RDQ(TLBELO0)
+ CASE_CSR_RDQ(TLBELO1)
+ CASE_CSR_RDQ(ASID)
+ CASE_CSR_RDQ(PGDL)
+ CASE_CSR_RDQ(PGDH)
+ CASE_CSR_RDQ(PWCL)
+ CASE_CSR_RDQ(PWCH)
+ CASE_CSR_RDQ(STLBPS)
+ CASE_CSR_RDQ(RVACFG)
+ CASE_CSR_RDQ(PRCFG1)
+ CASE_CSR_RDQ(PRCFG2)
+ CASE_CSR_RDQ(PRCFG3)
+ CASE_CSR_RDQ(SAVE0)
+ CASE_CSR_RDQ(SAVE1)
+ CASE_CSR_RDQ(SAVE2)
+ CASE_CSR_RDQ(SAVE3)
+ CASE_CSR_RDQ(SAVE4)
+ CASE_CSR_RDQ(SAVE5)
+ CASE_CSR_RDQ(SAVE6)
+ CASE_CSR_RDQ(SAVE7)
+ CASE_CSR_RDQ(TMID)
+ CASE_CSR_RDQ(TCFG)
+ CASE_CSR_RDQ(CNTC)
+ CASE_CSR_RDQ(TINTCLR)
+ CASE_CSR_RDQ(LLBCTL)
+ CASE_CSR_RDQ(IMPCTL1)
+ CASE_CSR_RDQ(IMPCTL2)
+ CASE_CSR_RDQ(TLBRENTRY)
+ CASE_CSR_RDQ(TLBRBADV)
+ CASE_CSR_RDQ(TLBRERA)
+ CASE_CSR_RDQ(TLBRSAVE)
+ CASE_CSR_RDQ(TLBRELO0)
+ CASE_CSR_RDQ(TLBRELO1)
+ CASE_CSR_RDQ(TLBREHI)
+ CASE_CSR_RDQ(TLBRPRMD)
+ CASE_CSR_RDQ(MERRCTL)
+ CASE_CSR_RDQ(MERRINFO)
+ CASE_CSR_RDQ(MERRINFO1)
+ CASE_CSR_RDQ(MERRENT)
+ CASE_CSR_RDQ(MERRERA)
+ CASE_CSR_RDQ(MERRSAVE)
+ CASE_CSR_RDQ(CTAG)
+ CASE_CSR_RDQ(DMWIN0)
+ CASE_CSR_RDQ(DMWIN1)
+ CASE_CSR_RDQ(DMWIN2)
+ CASE_CSR_RDQ(DMWIN3)
+ CASE_CSR_RDQ(PERFCTRL0)
+ CASE_CSR_RDQ(PERFCNTR0)
+ CASE_CSR_RDQ(PERFCTRL1)
+ CASE_CSR_RDQ(PERFCNTR1)
+ CASE_CSR_RDQ(PERFCTRL2)
+ CASE_CSR_RDQ(PERFCNTR2)
+ CASE_CSR_RDQ(PERFCTRL3)
+ CASE_CSR_RDQ(PERFCNTR3)
+ /* Debug */
+ CASE_CSR_RDQ(MWPC)
+ CASE_CSR_RDQ(MWPS)
+ CASE_CSR_RDQ(DB0ADDR)
+ CASE_CSR_RDQ(DB0MASK)
+ CASE_CSR_RDQ(DB0CTL)
+ CASE_CSR_RDQ(DB0ASID)
+ CASE_CSR_RDQ(DB1ADDR)
+ CASE_CSR_RDQ(DB1MASK)
+ CASE_CSR_RDQ(DB1CTL)
+ CASE_CSR_RDQ(DB1ASID)
+ CASE_CSR_RDQ(DB2ADDR)
+ CASE_CSR_RDQ(DB2MASK)
+ CASE_CSR_RDQ(DB2CTL)
+ CASE_CSR_RDQ(DB2ASID)
+ CASE_CSR_RDQ(DB3ADDR)
+ CASE_CSR_RDQ(DB3MASK)
+ CASE_CSR_RDQ(DB3CTL)
+ CASE_CSR_RDQ(DB3ASID)
+ CASE_CSR_RDQ(FWPC)
+ CASE_CSR_RDQ(FWPS)
+ CASE_CSR_RDQ(IB0ADDR)
+ CASE_CSR_RDQ(IB0MASK)
+ CASE_CSR_RDQ(IB0CTL)
+ CASE_CSR_RDQ(IB0ASID)
+ CASE_CSR_RDQ(IB1ADDR)
+ CASE_CSR_RDQ(IB1MASK)
+ CASE_CSR_RDQ(IB1CTL)
+ CASE_CSR_RDQ(IB1ASID)
+ CASE_CSR_RDQ(IB2ADDR)
+ CASE_CSR_RDQ(IB2MASK)
+ CASE_CSR_RDQ(IB2CTL)
+ CASE_CSR_RDQ(IB2ASID)
+ CASE_CSR_RDQ(IB3ADDR)
+ CASE_CSR_RDQ(IB3MASK)
+ CASE_CSR_RDQ(IB3CTL)
+ CASE_CSR_RDQ(IB3ASID)
+ CASE_CSR_RDQ(IB4ADDR)
+ CASE_CSR_RDQ(IB4MASK)
+ CASE_CSR_RDQ(IB4CTL)
+ CASE_CSR_RDQ(IB4ASID)
+ CASE_CSR_RDQ(IB5ADDR)
+ CASE_CSR_RDQ(IB5MASK)
+ CASE_CSR_RDQ(IB5CTL)
+ CASE_CSR_RDQ(IB5ASID)
+ CASE_CSR_RDQ(IB6ADDR)
+ CASE_CSR_RDQ(IB6MASK)
+ CASE_CSR_RDQ(IB6CTL)
+ CASE_CSR_RDQ(IB6ASID)
+ CASE_CSR_RDQ(IB7ADDR)
+ CASE_CSR_RDQ(IB7MASK)
+ CASE_CSR_RDQ(IB7CTL)
+ CASE_CSR_RDQ(IB7ASID)
+ CASE_CSR_RDQ(DBG)
+ CASE_CSR_RDQ(DERA)
+ CASE_CSR_RDQ(DESAVE)
+ case LOONGARCH_CSR_PGD:
+ case LOONGARCH_CSR_CPUID:
+ case LOONGARCH_CSR_TVAL:
+ gen_helper_csr_rdq(dest, cpu_env, tcg_constant_i64(a->csr));
+ break;
+ default:
+ assert(0);
+ }
+#undef CASE_CSR_RDQ
+
+ return true;
+}
+
+static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
+{
+ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+ TCGv src1 = gpr_src(ctx, a->rd, EXT_NONE);
+ TCGv temp = tcg_temp_new();
+
+ gen_helper_check_plv(cpu_env);
+#define CASE_CSR_WRQ(csr) \
+ case LOONGARCH_CSR_ ## csr: \
+ { \
+ tcg_gen_ld_tl(temp, cpu_env, offsetof(CPULoongArchState, CSR_##csr)); \
+ tcg_gen_st_tl(src1, cpu_env, offsetof(CPULoongArchState, CSR_##csr)); \
+ tcg_gen_mov_tl(dest, temp); \
+ tcg_temp_free(temp); \
+ break; \
+ }; \
+
+ switch (a->csr) {
+ case LOONGARCH_CSR_CRMD:
+ tcg_gen_ld_tl(temp, cpu_env, offsetof(CPULoongArchState, CSR_CRMD));
+ tcg_gen_st_tl(src1, cpu_env, offsetof(CPULoongArchState, CSR_CRMD));
+ tcg_gen_mov_tl(dest, temp);
+ tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
+ tcg_temp_free(temp);
+ ctx->base.is_jmp = DISAS_EXIT;
+ break;
+ case LOONGARCH_CSR_EUEN:
+ tcg_gen_ld_tl(temp, cpu_env, offsetof(CPULoongArchState, CSR_EUEN));
+ tcg_gen_st_tl(src1, cpu_env, offsetof(CPULoongArchState, CSR_EUEN));
+ tcg_gen_mov_tl(dest, temp);
+ tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
+ tcg_temp_free(temp);
+ ctx->base.is_jmp = DISAS_EXIT;
+ break;
+ CASE_CSR_WRQ(PRMD)
+ CASE_CSR_WRQ(MISC)
+ CASE_CSR_WRQ(ECFG)
+ CASE_CSR_WRQ(ESTAT)
+ CASE_CSR_WRQ(ERA)
+ CASE_CSR_WRQ(BADV)
+ CASE_CSR_WRQ(BADI)
+ CASE_CSR_WRQ(EENTRY)
+ CASE_CSR_WRQ(TLBIDX)
+ CASE_CSR_WRQ(TLBEHI)
+ CASE_CSR_WRQ(TLBELO0)
+ CASE_CSR_WRQ(TLBELO1)
+ CASE_CSR_WRQ(PGDL)
+ CASE_CSR_WRQ(PGDH)
+ CASE_CSR_WRQ(PWCL)
+ CASE_CSR_WRQ(PWCH)
+ CASE_CSR_WRQ(STLBPS)
+ CASE_CSR_WRQ(RVACFG)
+ CASE_CSR_WRQ(PRCFG1)
+ CASE_CSR_WRQ(PRCFG2)
+ CASE_CSR_WRQ(PRCFG3)
+ CASE_CSR_WRQ(SAVE0)
+ CASE_CSR_WRQ(SAVE1)
+ CASE_CSR_WRQ(SAVE2)
+ CASE_CSR_WRQ(SAVE3)
+ CASE_CSR_WRQ(SAVE4)
+ CASE_CSR_WRQ(SAVE5)
+ CASE_CSR_WRQ(SAVE6)
+ CASE_CSR_WRQ(SAVE7)
+ CASE_CSR_WRQ(TMID)
+ CASE_CSR_WRQ(TVAL)
+ CASE_CSR_WRQ(CNTC)
+ CASE_CSR_WRQ(LLBCTL)
+ CASE_CSR_WRQ(IMPCTL1)
+ CASE_CSR_WRQ(IMPCTL2)
+ CASE_CSR_WRQ(TLBRENTRY)
+ CASE_CSR_WRQ(TLBRBADV)
+ CASE_CSR_WRQ(TLBRERA)
+ CASE_CSR_WRQ(TLBRSAVE)
+ CASE_CSR_WRQ(TLBRELO0)
+ CASE_CSR_WRQ(TLBRELO1)
+ CASE_CSR_WRQ(TLBREHI)
+ CASE_CSR_WRQ(TLBRPRMD)
+ CASE_CSR_WRQ(MERRCTL)
+ CASE_CSR_WRQ(MERRINFO)
+ CASE_CSR_WRQ(MERRINFO1)
+ CASE_CSR_WRQ(MERRENT)
+ CASE_CSR_WRQ(MERRERA)
+ CASE_CSR_WRQ(MERRSAVE)
+ CASE_CSR_WRQ(CTAG)
+ CASE_CSR_WRQ(DMWIN0)
+ CASE_CSR_WRQ(DMWIN1)
+ CASE_CSR_WRQ(DMWIN2)
+ CASE_CSR_WRQ(DMWIN3)
+ CASE_CSR_WRQ(PERFCTRL0)
+ CASE_CSR_WRQ(PERFCNTR0)
+ CASE_CSR_WRQ(PERFCTRL1)
+ CASE_CSR_WRQ(PERFCNTR1)
+ CASE_CSR_WRQ(PERFCTRL2)
+ CASE_CSR_WRQ(PERFCNTR2)
+ CASE_CSR_WRQ(PERFCTRL3)
+ CASE_CSR_WRQ(PERFCNTR3)
+ /* Debug */
+ CASE_CSR_WRQ(MWPC)
+ CASE_CSR_WRQ(MWPS)
+ CASE_CSR_WRQ(DB0ADDR)
+ CASE_CSR_WRQ(DB0MASK)
+ CASE_CSR_WRQ(DB0CTL)
+ CASE_CSR_WRQ(DB0ASID)
+ CASE_CSR_WRQ(DB1ADDR)
+ CASE_CSR_WRQ(DB1MASK)
+ CASE_CSR_WRQ(DB1CTL)
+ CASE_CSR_WRQ(DB1ASID)
+ CASE_CSR_WRQ(DB2ADDR)
+ CASE_CSR_WRQ(DB2MASK)
+ CASE_CSR_WRQ(DB2CTL)
+ CASE_CSR_WRQ(DB2ASID)
+ CASE_CSR_WRQ(DB3ADDR)
+ CASE_CSR_WRQ(DB3MASK)
+ CASE_CSR_WRQ(DB3CTL)
+ CASE_CSR_WRQ(DB3ASID)
+ CASE_CSR_WRQ(FWPC)
+ CASE_CSR_WRQ(FWPS)
+ CASE_CSR_WRQ(IB0ADDR)
+ CASE_CSR_WRQ(IB0MASK)
+ CASE_CSR_WRQ(IB0CTL)
+ CASE_CSR_WRQ(IB0ASID)
+ CASE_CSR_WRQ(IB1ADDR)
+ CASE_CSR_WRQ(IB1MASK)
+ CASE_CSR_WRQ(IB1CTL)
+ CASE_CSR_WRQ(IB1ASID)
+ CASE_CSR_WRQ(IB2ADDR)
+ CASE_CSR_WRQ(IB2MASK)
+ CASE_CSR_WRQ(IB2CTL)
+ CASE_CSR_WRQ(IB2ASID)
+ CASE_CSR_WRQ(IB3ADDR)
+ CASE_CSR_WRQ(IB3MASK)
+ CASE_CSR_WRQ(IB3CTL)
+ CASE_CSR_WRQ(IB3ASID)
+ CASE_CSR_WRQ(IB4ADDR)
+ CASE_CSR_WRQ(IB4MASK)
+ CASE_CSR_WRQ(IB4CTL)
+ CASE_CSR_WRQ(IB4ASID)
+ CASE_CSR_WRQ(IB5ADDR)
+ CASE_CSR_WRQ(IB5MASK)
+ CASE_CSR_WRQ(IB5CTL)
+ CASE_CSR_WRQ(IB5ASID)
+ CASE_CSR_WRQ(IB6ADDR)
+ CASE_CSR_WRQ(IB6MASK)
+ CASE_CSR_WRQ(IB6CTL)
+ CASE_CSR_WRQ(IB6ASID)
+ CASE_CSR_WRQ(IB7ADDR)
+ CASE_CSR_WRQ(IB7MASK)
+ CASE_CSR_WRQ(IB7CTL)
+ CASE_CSR_WRQ(IB7ASID)
+ CASE_CSR_WRQ(DBG)
+ CASE_CSR_WRQ(DERA)
+ CASE_CSR_WRQ(DESAVE)
+ case LOONGARCH_CSR_ASID:
+ case LOONGARCH_CSR_TCFG:
+ case LOONGARCH_CSR_TINTCLR:
+ gen_helper_csr_wrq(dest, cpu_env, src1, tcg_constant_i64(a->csr));
+ break;
+ default:
+ assert(0);
+ }
+#undef CASE_CSR_WRQ
+
+ return true;
+}
+
+static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
+{
+ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+ TCGv src1 = gpr_src(ctx, a->rd, EXT_NONE);
+ TCGv src2 = gpr_src(ctx, a->rj, EXT_NONE);
+
+ gen_helper_check_plv(cpu_env);
+ gen_helper_csr_xchgq(dest, cpu_env, src1, src2, tcg_constant_i64(a->csr));
+ return true;
+}
+
+static bool trans_iocsrrd_b(DisasContext *ctx, arg_iocsrrd_b *a)
+{
+ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
+
+ gen_helper_check_plv(cpu_env);
+ gen_helper_iocsr_read(dest, cpu_env, src1, tcg_constant_i32(1));
+ return true;
+}
+
+static bool trans_iocsrrd_h(DisasContext *ctx, arg_iocsrrd_h *a)
+{
+ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
+
+ gen_helper_check_plv(cpu_env);
+ gen_helper_iocsr_read(dest, cpu_env, src1, tcg_constant_i32(2));
+ return true;
+}
+
+static bool trans_iocsrrd_w(DisasContext *ctx, arg_iocsrrd_w *a)
+{
+ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
+
+ gen_helper_check_plv(cpu_env);
+ gen_helper_iocsr_read(dest, cpu_env, src1, tcg_constant_i32(4));
+ return true;
+}
+
+static bool trans_iocsrrd_d(DisasContext *ctx, arg_iocsrrd_d *a)
+{
+ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
+
+ gen_helper_check_plv(cpu_env);
+ gen_helper_iocsr_read(dest, cpu_env, src1, tcg_constant_i32(8));
+ return true;
+}
+
+static bool trans_iocsrwr_b(DisasContext *ctx, arg_iocsrwr_b *a)
+{
+ TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
+ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
+
+ gen_helper_check_plv(cpu_env);
+ gen_helper_iocsr_write(cpu_env, addr, val, tcg_constant_i32(1));
+ return true;
+}
+
+static bool trans_iocsrwr_h(DisasContext *ctx, arg_iocsrwr_h *a)
+{
+ TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
+ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
+
+ gen_helper_check_plv(cpu_env);
+ gen_helper_iocsr_write(cpu_env, addr, val, tcg_constant_i32(2));
+ return true;
+}
+
+static bool trans_iocsrwr_w(DisasContext *ctx, arg_iocsrwr_w *a)
+{
+ TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
+ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
+
+ gen_helper_check_plv(cpu_env);
+ gen_helper_iocsr_write(cpu_env, addr, val, tcg_constant_i32(4));
+ return true;
+}
+
+static bool trans_iocsrwr_d(DisasContext *ctx, arg_iocsrwr_d *a)
+{
+ TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
+ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
+
+ gen_helper_check_plv(cpu_env);
+ gen_helper_iocsr_write(cpu_env, addr, val, tcg_constant_i32(8));
+ return true;
+}
+#endif
@@ -40,6 +40,7 @@
%offs21 0:s5 10:16
%offs16 10:s16
%offs 0:s10 10:16
+%csr 10:14
#
# Argument sets
@@ -81,6 +82,8 @@
&fmt_rdrjoffs16 rd rj offs16
&fmt_offs offs
&fmt_rjrdoffs16 rj rd offs16
+&fmt_rdcsr rd csr
+&fmt_rdrjcsr rd rj csr
#
# Formats
@@ -122,6 +125,8 @@
@fmt_rdrjoffs16 .... .. ................ ..... ..... &fmt_rdrjoffs16 %rd %rj %offs16
@fmt_offs .... .. .......................... &fmt_offs %offs
@fmt_rjrdoffs16 .... .. ................ ..... ..... &fmt_rjrdoffs16 %rj %rd %offs16
+@fmt_rdcsr .... .... .............. ..... ..... &fmt_rdcsr %rd %csr
+@fmt_rdrjcsr .... .... .............. ..... ..... &fmt_rdrjcsr %rd %rj %csr
#
# Fixed point arithmetic operation instruction
@@ -477,3 +482,20 @@ blt 0110 00 ................ ..... ..... @fmt_rjrdoffs16
bge 0110 01 ................ ..... ..... @fmt_rjrdoffs16
bltu 0110 10 ................ ..... ..... @fmt_rjrdoffs16
bgeu 0110 11 ................ ..... ..... @fmt_rjrdoffs16
+
+#
+# Core instructions
+#
+{
+ csrrd 0000 0100 .............. 00000 ..... @fmt_rdcsr
+ csrwr 0000 0100 .............. 00001 ..... @fmt_rdcsr
+ csrxchg 0000 0100 .............. ..... ..... @fmt_rdrjcsr
+}
+iocsrrd_b 0000 01100100 10000 00000 ..... ..... @fmt_rdrj
+iocsrrd_h 0000 01100100 10000 00001 ..... ..... @fmt_rdrj
+iocsrrd_w 0000 01100100 10000 00010 ..... ..... @fmt_rdrj
+iocsrrd_d 0000 01100100 10000 00011 ..... ..... @fmt_rdrj
+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
@@ -19,6 +19,7 @@ loongarch_softmmu_ss.add(files(
'machine.c',
'stabletimer.c',
'tlb_helper.c',
+ 'csr_helper.c',
))
loongarch_ss.add_all(when: 'CONFIG_TCG', if_true: [loongarch_tcg_ss])
@@ -82,3 +82,13 @@ target_ulong helper_cpucfg(CPULoongArchState *env, target_ulong rj)
{
return env->cpucfg[rj];
}
+
+#ifndef CONFIG_USER_ONLY
+void helper_check_plv(CPULoongArchState *env)
+{
+ uint64_t plv = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV);
+ if (plv) {
+ do_raise_exception(env, EXCP_IPE, GETPC());
+ }
+}
+#endif /* !CONFIG_USER_ONLY */
@@ -26,6 +26,7 @@ TCGv_i32 cpu_fcsr0;
TCGv_i64 cpu_fpr[32];
#define DISAS_STOP DISAS_TARGET_0
+#define DISAS_EXIT DISAS_TARGET_1
/*
* LoongArch the upper 32 bits are undefined ("can be any value").
@@ -191,6 +192,7 @@ static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext)
#include "insn_trans/trans_fmov.c.inc"
#include "insn_trans/trans_fmemory.c.inc"
#include "insn_trans/trans_branch.c.inc"
+#include "insn_trans/trans_core.c.inc"
static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
{
@@ -228,6 +230,9 @@ static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
break;
case DISAS_NORETURN:
break;
+ case DISAS_EXIT:
+ tcg_gen_exit_tb(NULL, 0);
+ break;
default:
g_assert_not_reached();
}