aboutsummaryrefslogtreecommitdiff
path: root/target/loongarch/insn_trans
diff options
context:
space:
mode:
authorXiaojuan Yang <yangxiaojuan@loongson.cn>2022-06-06 20:43:15 +0800
committerRichard Henderson <richard.henderson@linaro.org>2022-06-06 18:09:03 +0000
commit5b1dedfe848b61521aa5b46b81a4cc676e9e7c1b (patch)
tree5e119c1e5637aa79bb852e765ccd4e93271bfdb3 /target/loongarch/insn_trans
parentdd615fa48da89b2308a907cc4e4956771c75d68f (diff)
target/loongarch: Add LoongArch CSR instruction
This includes: - CSRRD - CSRWR - CSRXCHG 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-26-yangxiaojuan@loongson.cn> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'target/loongarch/insn_trans')
-rw-r--r--target/loongarch/insn_trans/trans_privileged.c.inc264
1 files changed, 264 insertions, 0 deletions
diff --git a/target/loongarch/insn_trans/trans_privileged.c.inc b/target/loongarch/insn_trans/trans_privileged.c.inc
new file mode 100644
index 0000000000..f984e3f68e
--- /dev/null
+++ b/target/loongarch/insn_trans/trans_privileged.c.inc
@@ -0,0 +1,264 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2021 Loongson Technology Corporation Limited
+ *
+ * LoongArch translation routines for the privileged instructions.
+ */
+
+#include "cpu-csr.h"
+
+typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env);
+typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src);
+
+typedef struct {
+ int offset;
+ int flags;
+ GenCSRRead readfn;
+ GenCSRWrite writefn;
+} CSRInfo;
+
+enum {
+ CSRFL_READONLY = (1 << 0),
+ CSRFL_EXITTB = (1 << 1),
+ CSRFL_IO = (1 << 2),
+};
+
+#define CSR_OFF_FUNCS(NAME, FL, RD, WR) \
+ [LOONGARCH_CSR_##NAME] = { \
+ .offset = offsetof(CPULoongArchState, CSR_##NAME), \
+ .flags = FL, .readfn = RD, .writefn = WR \
+ }
+
+#define CSR_OFF_ARRAY(NAME, N) \
+ [LOONGARCH_CSR_##NAME(N)] = { \
+ .offset = offsetof(CPULoongArchState, CSR_##NAME[N]), \
+ .flags = 0, .readfn = NULL, .writefn = NULL \
+ }
+
+#define CSR_OFF_FLAGS(NAME, FL) \
+ CSR_OFF_FUNCS(NAME, FL, NULL, NULL)
+
+#define CSR_OFF(NAME) \
+ CSR_OFF_FLAGS(NAME, 0)
+
+static const CSRInfo csr_info[] = {
+ CSR_OFF_FLAGS(CRMD, CSRFL_EXITTB),
+ CSR_OFF(PRMD),
+ CSR_OFF_FLAGS(EUEN, CSRFL_EXITTB),
+ CSR_OFF_FLAGS(MISC, CSRFL_READONLY),
+ CSR_OFF(ECFG),
+ CSR_OFF_FUNCS(ESTAT, CSRFL_EXITTB, NULL, gen_helper_csrwr_estat),
+ CSR_OFF(ERA),
+ CSR_OFF(BADV),
+ CSR_OFF_FLAGS(BADI, CSRFL_READONLY),
+ CSR_OFF(EENTRY),
+ CSR_OFF(TLBIDX),
+ CSR_OFF(TLBEHI),
+ CSR_OFF(TLBELO0),
+ CSR_OFF(TLBELO1),
+ CSR_OFF_FUNCS(ASID, CSRFL_EXITTB, NULL, gen_helper_csrwr_asid),
+ CSR_OFF(PGDL),
+ CSR_OFF(PGDH),
+ CSR_OFF_FUNCS(PGD, CSRFL_READONLY, gen_helper_csrrd_pgd, NULL),
+ CSR_OFF(PWCL),
+ CSR_OFF(PWCH),
+ CSR_OFF(STLBPS),
+ CSR_OFF(RVACFG),
+ [LOONGARCH_CSR_CPUID] = {
+ .offset = (int)offsetof(CPUState, cpu_index)
+ - (int)offsetof(LoongArchCPU, env),
+ .flags = CSRFL_READONLY,
+ .readfn = NULL,
+ .writefn = NULL
+ },
+ CSR_OFF_FLAGS(PRCFG1, CSRFL_READONLY),
+ CSR_OFF_FLAGS(PRCFG2, CSRFL_READONLY),
+ CSR_OFF_FLAGS(PRCFG3, CSRFL_READONLY),
+ CSR_OFF_ARRAY(SAVE, 0),
+ CSR_OFF_ARRAY(SAVE, 1),
+ CSR_OFF_ARRAY(SAVE, 2),
+ CSR_OFF_ARRAY(SAVE, 3),
+ CSR_OFF_ARRAY(SAVE, 4),
+ CSR_OFF_ARRAY(SAVE, 5),
+ CSR_OFF_ARRAY(SAVE, 6),
+ CSR_OFF_ARRAY(SAVE, 7),
+ CSR_OFF_ARRAY(SAVE, 8),
+ CSR_OFF_ARRAY(SAVE, 9),
+ CSR_OFF_ARRAY(SAVE, 10),
+ CSR_OFF_ARRAY(SAVE, 11),
+ CSR_OFF_ARRAY(SAVE, 12),
+ CSR_OFF_ARRAY(SAVE, 13),
+ CSR_OFF_ARRAY(SAVE, 14),
+ CSR_OFF_ARRAY(SAVE, 15),
+ CSR_OFF(TID),
+ CSR_OFF_FUNCS(TCFG, CSRFL_IO, NULL, gen_helper_csrwr_tcfg),
+ CSR_OFF_FUNCS(TVAL, CSRFL_READONLY | CSRFL_IO, gen_helper_csrrd_tval, NULL),
+ CSR_OFF(CNTC),
+ CSR_OFF_FUNCS(TICLR, CSRFL_IO, NULL, gen_helper_csrwr_ticlr),
+ CSR_OFF(LLBCTL),
+ CSR_OFF(IMPCTL1),
+ CSR_OFF(IMPCTL2),
+ CSR_OFF(TLBRENTRY),
+ CSR_OFF(TLBRBADV),
+ CSR_OFF(TLBRERA),
+ CSR_OFF(TLBRSAVE),
+ CSR_OFF(TLBRELO0),
+ CSR_OFF(TLBRELO1),
+ CSR_OFF(TLBREHI),
+ CSR_OFF(TLBRPRMD),
+ CSR_OFF(MERRCTL),
+ CSR_OFF(MERRINFO1),
+ CSR_OFF(MERRINFO2),
+ CSR_OFF(MERRENTRY),
+ CSR_OFF(MERRERA),
+ CSR_OFF(MERRSAVE),
+ CSR_OFF(CTAG),
+ CSR_OFF_ARRAY(DMW, 0),
+ CSR_OFF_ARRAY(DMW, 1),
+ CSR_OFF_ARRAY(DMW, 2),
+ CSR_OFF_ARRAY(DMW, 3),
+ CSR_OFF(DBG),
+ CSR_OFF(DERA),
+ CSR_OFF(DSAVE),
+};
+
+static bool check_plv(DisasContext *ctx)
+{
+ if (ctx->base.tb->flags == MMU_USER_IDX) {
+ generate_exception(ctx, EXCCODE_IPE);
+ return true;
+ }
+ return false;
+}
+
+static const CSRInfo *get_csr(unsigned csr_num)
+{
+ const CSRInfo *csr;
+
+ if (csr_num >= ARRAY_SIZE(csr_info)) {
+ return NULL;
+ }
+ csr = &csr_info[csr_num];
+ if (csr->offset == 0) {
+ return NULL;
+ }
+ return csr;
+}
+
+static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write)
+{
+ if ((csr->flags & CSRFL_READONLY) && write) {
+ return false;
+ }
+ if ((csr->flags & CSRFL_IO) &&
+ (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT)) {
+ gen_io_start();
+ ctx->base.is_jmp = DISAS_EXIT_UPDATE;
+ } else if ((csr->flags & CSRFL_EXITTB) && write) {
+ ctx->base.is_jmp = DISAS_EXIT_UPDATE;
+ }
+ return true;
+}
+
+static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
+{
+ TCGv dest;
+ const CSRInfo *csr;
+
+ if (check_plv(ctx)) {
+ return false;
+ }
+ csr = get_csr(a->csr);
+ if (csr == NULL) {
+ /* CSR is undefined: read as 0. */
+ dest = tcg_constant_tl(0);
+ } else {
+ check_csr_flags(ctx, csr, false);
+ dest = gpr_dst(ctx, a->rd, EXT_NONE);
+ if (csr->readfn) {
+ csr->readfn(dest, cpu_env);
+ } else {
+ tcg_gen_ld_tl(dest, cpu_env, csr->offset);
+ }
+ }
+ gen_set_gpr(a->rd, dest, EXT_NONE);
+ return true;
+}
+
+static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
+{
+ TCGv dest, src1;
+ const CSRInfo *csr;
+
+ if (check_plv(ctx)) {
+ return false;
+ }
+ csr = get_csr(a->csr);
+ if (csr == NULL) {
+ /* CSR is undefined: write ignored, read old_value as 0. */
+ gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
+ return true;
+ }
+ if (!check_csr_flags(ctx, csr, true)) {
+ /* CSR is readonly: trap. */
+ return false;
+ }
+ src1 = gpr_src(ctx, a->rd, EXT_NONE);
+ if (csr->writefn) {
+ dest = gpr_dst(ctx, a->rd, EXT_NONE);
+ csr->writefn(dest, cpu_env, src1);
+ } else {
+ dest = temp_new(ctx);
+ tcg_gen_ld_tl(dest, cpu_env, csr->offset);
+ tcg_gen_st_tl(src1, cpu_env, csr->offset);
+ }
+ gen_set_gpr(a->rd, dest, EXT_NONE);
+ return true;
+}
+
+static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
+{
+ TCGv src1, mask, oldv, newv, temp;
+ const CSRInfo *csr;
+
+ if (check_plv(ctx)) {
+ return false;
+ }
+ csr = get_csr(a->csr);
+ if (csr == NULL) {
+ /* CSR is undefined: write ignored, read old_value as 0. */
+ gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
+ return true;
+ }
+
+ if (!check_csr_flags(ctx, csr, true)) {
+ /* CSR is readonly: trap. */
+ return false;
+ }
+
+ /* So far only readonly csrs have readfn. */
+ assert(csr->readfn == NULL);
+
+ src1 = gpr_src(ctx, a->rd, EXT_NONE);
+ mask = gpr_src(ctx, a->rj, EXT_NONE);
+ oldv = tcg_temp_new();
+ newv = tcg_temp_new();
+ temp = tcg_temp_new();
+
+ tcg_gen_ld_tl(oldv, cpu_env, csr->offset);
+ tcg_gen_and_tl(newv, src1, mask);
+ tcg_gen_andc_tl(temp, oldv, mask);
+ tcg_gen_or_tl(newv, newv, temp);
+
+ if (csr->writefn) {
+ csr->writefn(oldv, cpu_env, newv);
+ } else {
+ tcg_gen_st_tl(newv, cpu_env, csr->offset);
+ }
+ gen_set_gpr(a->rd, oldv, EXT_NONE);
+
+ tcg_temp_free(temp);
+ tcg_temp_free(newv);
+ tcg_temp_free(oldv);
+ return true;
+}