On 2026/7/10 下午5:00, SignKirigami wrote:
Add decode, disassembly and TCG translation for LVZ guest CSR access,
guest TLB management and hypervisor call instructions, including
guest-mode CSR trapping, guest read-only checks and translation context
setup using the LVZ infrastructure added by earlier patches.
Signed-off-by: SignKirigami <[email protected]>
Signed-off-by: Hengyu Yu <[email protected]>
---
target/loongarch/disas.c | 16 +
target/loongarch/insns.decode | 14 +
.../tcg/insn_trans/trans_privileged.c.inc | 275 +++++++++++++++++-
target/loongarch/tcg/translate.c | 6 +-
4 files changed, 303 insertions(+), 8 deletions(-)
diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c
index 3249ab7ac6..db0e556adb 100644
--- a/target/loongarch/disas.c
+++ b/target/loongarch/disas.c
@@ -51,6 +51,8 @@ static const char * const csr_names[] = {
CSR_NAME(BADI),
CSR_NAME(EENTRY),
CSR_NAME(TLBIDX),
+ CSR_NAME(GTLBC),
+ CSR_NAME(TRGP),
CSR_NAME(TLBEHI),
CSR_NAME(TLBELO0),
CSR_NAME(TLBELO1),
@@ -87,6 +89,10 @@ static const char * const csr_names[] = {
CSR_NAME(TVAL),
CSR_NAME(CNTC),
CSR_NAME(TICLR),
+ CSR_NAME(GSTAT),
+ CSR_NAME(GCFG),
+ CSR_NAME(GINTC),
+ CSR_NAME(GCNTC),
CSR_NAME(LLBCTL),
CSR_NAME(IMPCTL1),
CSR_NAME(IMPCTL2),
@@ -698,6 +704,16 @@ INSN(tlbfill, empty)
INSN(tlbclr, empty)
INSN(tlbflush, empty)
INSN(invtlb, i_rr)
+INSN(gcsrrd, r_csr)
+INSN(gcsrwr, r_csr)
+INSN(gcsrxchg, rr_csr)
+INSN(gtlbclr, empty)
+INSN(gtlbflush, empty)
+INSN(gtlbsrch, empty)
+INSN(gtlbrd, empty)
+INSN(gtlbwr, empty)
+INSN(gtlbfill, empty)
+INSN(hvcl, i)
INSN(cacop, cop_r_i)
INSN(lddir, rr_i)
INSN(ldpte, j_i)
diff --git a/target/loongarch/insns.decode b/target/loongarch/insns.decode
index 3089d42044..b40fabe9d7 100644
--- a/target/loongarch/insns.decode
+++ b/target/loongarch/insns.decode
@@ -493,6 +493,20 @@ bgeu 0110 11 ................ ..... .....
@rr_offs16
csrxchg 0000 0100 .............. ..... ..... @rr_csr
}
+{
+ gcsrrd 0000 0101 .............. 00000 ..... @r_csr
+ gcsrwr 0000 0101 .............. 00001 ..... @r_csr
+ gcsrxchg 0000 0101 .............. ..... ..... @rr_csr
+}
+
+gtlbclr 0000 01100100 10000 01000 00000 00001 @empty
+gtlbflush 0000 01100100 10000 01001 00000 00001 @empty
+gtlbsrch 0000 01100100 10000 01010 00000 00001 @empty
+gtlbrd 0000 01100100 10000 01011 00000 00001 @empty
+gtlbwr 0000 01100100 10000 01100 00000 00001 @empty
+gtlbfill 0000 01100100 10000 01101 00000 00001 @empty
+hvcl 0000 0000 0010 1011 1 ............... @i15
+
iocsrrd_b 0000 01100100 10000 00000 ..... ..... @rr
iocsrrd_h 0000 01100100 10000 00001 ..... ..... @rr
iocsrrd_w 0000 01100100 10000 00010 ..... ..... @rr
diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
index 48a42779d3..adf088627e 100644
--- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
@@ -39,6 +39,16 @@ GEN_FALSE_TRANS(lddir)
GEN_FALSE_TRANS(ertn)
GEN_FALSE_TRANS(dbcl)
GEN_FALSE_TRANS(idle)
+GEN_FALSE_TRANS(gcsrrd)
+GEN_FALSE_TRANS(gcsrwr)
+GEN_FALSE_TRANS(gcsrxchg)
+GEN_FALSE_TRANS(gtlbclr)
+GEN_FALSE_TRANS(gtlbflush)
+GEN_FALSE_TRANS(gtlbsrch)
+GEN_FALSE_TRANS(gtlbrd)
+GEN_FALSE_TRANS(gtlbwr)
+GEN_FALSE_TRANS(gtlbfill)
+GEN_FALSE_TRANS(hvcl)
#else
@@ -69,8 +79,25 @@ static bool set_csr_trans_func(unsigned int csr_num, GenCSRRead readfn,
return true;
}
+static bool set_gcsr_trans_func(unsigned int csr_num, GenCSRRead readfn,
+ GenCSRWrite writefn)
+{
+ CSRInfo *csr;
+
+ csr = get_gcsr(csr_num);
+ if (!csr) {
+ return false;
+ }
+
+ csr->readfn = (GenCSRFunc)readfn;
+ csr->writefn = (GenCSRFunc)writefn;
+ return true;
+}
+
#define SET_CSR_FUNC(NAME, read, write) \
set_csr_trans_func(LOONGARCH_CSR_##NAME, read, write)
+#define SET_GCSR_FUNC(NAME, read, write) \
+ set_gcsr_trans_func(LOONGARCH_CSR_##NAME, read, write)
void loongarch_csr_translate_init(void)
{
@@ -85,14 +112,28 @@ void loongarch_csr_translate_init(void)
SET_CSR_FUNC(TVAL, gen_helper_csrrd_tval, NULL);
SET_CSR_FUNC(TICLR, NULL, gen_helper_csrwr_ticlr);
SET_CSR_FUNC(MSGIR, gen_helper_csrrd_msgir, NULL);
+ SET_CSR_FUNC(GSTAT, NULL, gen_helper_csrwr_gstat);
+ SET_CSR_FUNC(GTLBC, NULL, gen_helper_csrwr_gtlbc);
+ SET_CSR_FUNC(GINTC, NULL, gen_helper_csrwr_gintc);
+
+ SET_GCSR_FUNC(ESTAT, NULL, gen_helper_gcsrwr_estat);
+ SET_GCSR_FUNC(ASID, NULL, gen_helper_gcsrwr_asid);
+ SET_GCSR_FUNC(PGD, gen_helper_gcsrrd_pgd, NULL);
+ SET_GCSR_FUNC(TCFG, NULL, gen_helper_gcsrwr_tcfg);
+ SET_GCSR_FUNC(TVAL, gen_helper_gcsrrd_tval, NULL);
+ SET_GCSR_FUNC(TICLR, NULL, gen_helper_gcsrwr_ticlr);
}
#undef SET_CSR_FUNC
+#undef SET_GCSR_FUNC
static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write)
{
if ((csr->flags & CSRFL_READONLY) && write) {
return false;
}
+ if ((csr->flags & CSRFL_GUEST_READONLY) && ctx->guest_mode && write) {
+ return false;
+ }
if ((csr->flags & CSRFL_IO) && translator_io_start(&ctx->base)) {
ctx->base.is_jmp = DISAS_EXIT_UPDATE;
} else if ((csr->flags & CSRFL_EXITTB) && write) {
@@ -110,12 +151,17 @@ static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
if (check_plv(ctx)) {
return false;
}
- csr = get_csr(a->csr);
+ csr = ctx->guest_mode ? get_gcsr(a->csr) : get_csr(a->csr);
Can guest CSRInfo and host CSRInfo be merged into one?
if (csr == NULL) {
/* CSR is undefined: read as 0. */
dest = tcg_constant_tl(0);
} else {
check_csr_flags(ctx, csr, false);
+ if (ctx->guest_mode && (csr->flags & CSRFL_GSPR)) {
+ gen_helper_gspr(tcg_env);
+ gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
why the value of rd is 0 when emulating CSR read in guest mode?
+ return true;
+ }
dest = gpr_dst(ctx, a->rd, EXT_NONE);
readfn = (GenCSRRead)csr->readfn;
if (readfn) {
@@ -137,12 +183,17 @@ static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
if (check_plv(ctx)) {
return false;
}
- csr = get_csr(a->csr);
+ csr = ctx->guest_mode ? get_gcsr(a->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 (ctx->guest_mode && (csr->flags & CSRFL_GSPR)) {
+ gen_helper_gspr(tcg_env);
+ 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;
@@ -170,20 +221,136 @@ static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg
*a)
if (check_plv(ctx)) {
return false;
}
- csr = get_csr(a->csr);
+ csr = ctx->guest_mode ? get_gcsr(a->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 (ctx->guest_mode && (csr->flags & CSRFL_GSPR)) {
+ gen_helper_gspr(tcg_env);
+ 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();
+
+ if (csr->readfn) {
+ GenCSRRead readfn = (GenCSRRead)csr->readfn;
+ readfn(oldv, tcg_env);
+ } else {
+ tcg_gen_ld_tl(oldv, tcg_env, csr->offset);
+ }
+ tcg_gen_and_tl(newv, src1, mask);
+ tcg_gen_andc_tl(temp, oldv, mask);
+ tcg_gen_or_tl(newv, newv, temp);
+
+ writefn = (GenCSRWrite)csr->writefn;
+ if (writefn) {
+ writefn(oldv, tcg_env, newv);
+ } else {
+ tcg_gen_st_tl(newv, tcg_env, csr->offset);
+ }
+ gen_set_gpr(a->rd, oldv, EXT_NONE);
+ return true;
+}
+
+static bool trans_gcsrrd(DisasContext *ctx, arg_gcsrrd *a)
+{
+ TCGv dest;
+ const CSRInfo *csr;
+ GenCSRRead readfn;
+
+ if (check_plv(ctx) || !avail_LVZ(ctx)) {
+ return false;
+ }
Most of the folloing mode is simiar with trans_csrrd(),
can we modify trans_csrrd() with non-guest mode support, and call
trans_csrrd() here?
Regards
Bibo Mao
+ csr = get_gcsr(a->csr);
+ if (csr == NULL) {
+ dest = tcg_constant_tl(0);
+ } else {
+ dest = gpr_dst(ctx, a->rd, EXT_NONE);
+ if (csr->flags & CSRFL_GSPR) {
+ tcg_gen_movi_tl(dest, 0);
+ } else {
+ readfn = (GenCSRRead)csr->readfn;
+ if (readfn) {
+ readfn(dest, tcg_env);
+ } else {
+ tcg_gen_ld_tl(dest, tcg_env, csr->offset);
+ }
+ }
+ }
+ gen_set_gpr(a->rd, dest, EXT_NONE);
+ return true;
+}
+
+static bool trans_gcsrwr(DisasContext *ctx, arg_gcsrwr *a)
+{
+ TCGv dest, src1;
+ const CSRInfo *csr;
+ GenCSRWrite writefn;
+
+ if (check_plv(ctx) || !avail_LVZ(ctx)) {
+ return false;
+ }
+ csr = get_gcsr(a->csr);
+ if (csr == NULL) {
+ gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
+ return true;
+ }
+ if (!check_csr_flags(ctx, csr, true)) {
+ return false;
+ }
+ if (csr->flags & CSRFL_GSPR) {
+ gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
+ return true;
+ }
+
+ src1 = gpr_src(ctx, a->rd, EXT_NONE);
+ writefn = (GenCSRWrite)csr->writefn;
+ if (writefn) {
+ dest = gpr_dst(ctx, a->rd, EXT_NONE);
+ writefn(dest, tcg_env, src1);
+ } else {
+ dest = tcg_temp_new();
+ tcg_gen_ld_tl(dest, tcg_env, csr->offset);
+ tcg_gen_st_tl(src1, tcg_env, csr->offset);
+ }
+ gen_set_gpr(a->rd, dest, EXT_NONE);
+ return true;
+}
+
+static bool trans_gcsrxchg(DisasContext *ctx, arg_gcsrxchg *a)
+{
+ TCGv src1, mask, oldv, newv, temp;
+ const CSRInfo *csr;
+ GenCSRRead readfn;
+ GenCSRWrite writefn;
+
+ if (check_plv(ctx) || !avail_LVZ(ctx)) {
+ return false;
+ }
+ csr = get_gcsr(a->csr);
+ if (csr == NULL) {
+ gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
+ return true;
+ }
+ if (!check_csr_flags(ctx, csr, true)) {
+ return false;
+ }
+ if (csr->flags & CSRFL_GSPR) {
+ gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
+ return true;
+ }
src1 = gpr_src(ctx, a->rd, EXT_NONE);
mask = gpr_src(ctx, a->rj, EXT_NONE);
@@ -191,7 +358,12 @@ static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg
*a)
newv = tcg_temp_new();
temp = tcg_temp_new();
- tcg_gen_ld_tl(oldv, tcg_env, csr->offset);
+ readfn = (GenCSRRead)csr->readfn;
+ if (readfn) {
+ readfn(oldv, tcg_env);
+ } else {
+ tcg_gen_ld_tl(oldv, tcg_env, csr->offset);
+ }
tcg_gen_and_tl(newv, src1, mask);
tcg_gen_andc_tl(temp, oldv, mask);
tcg_gen_or_tl(newv, newv, temp);
@@ -212,6 +384,11 @@ static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a,
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
+ if (ctx->guest_mode) {
+ gen_helper_gspr(tcg_env);
+ return true;
+ }
+
if (check_plv(ctx)) {
return false;
}
@@ -225,6 +402,11 @@ static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a,
TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
+ if (ctx->guest_mode) {
+ gen_helper_gspr(tcg_env);
+ return true;
+ }
+
if (check_plv(ctx)) {
return false;
}
@@ -243,7 +425,7 @@ TRANS64(iocsrwr_d, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_d)
static void check_mmu_idx(DisasContext *ctx)
{
- if (ctx->mem_idx != MMU_DA_IDX) {
+ if (ctx->mem_idx != MMU_DA_IDX && ctx->mem_idx != MMU_GUEST_DA_IDX) {
tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
ctx->base.is_jmp = DISAS_EXIT;
}
@@ -267,6 +449,24 @@ static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a)
return true;
}
+static bool trans_gtlbsrch(DisasContext *ctx, arg_gtlbsrch *a)
+{
+ if (check_plv(ctx) || !avail_LVZ(ctx) || ctx->guest_mode) {
+ return false;
+ }
+ gen_helper_gtlbsrch(tcg_env);
+ return true;
+}
+
+static bool trans_gtlbrd(DisasContext *ctx, arg_gtlbrd *a)
+{
+ if (check_plv(ctx) || !avail_LVZ(ctx) || ctx->guest_mode) {
+ return false;
+ }
+ gen_helper_gtlbrd(tcg_env);
+ return true;
+}
+
static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
{
if (check_plv(ctx)) {
@@ -277,6 +477,16 @@ static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
return true;
}
+static bool trans_gtlbwr(DisasContext *ctx, arg_gtlbwr *a)
+{
+ if (check_plv(ctx) || !avail_LVZ(ctx) || ctx->guest_mode) {
+ return false;
+ }
+ gen_helper_gtlbwr(tcg_env);
+ check_mmu_idx(ctx);
+ return true;
+}
+
static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a)
{
if (check_plv(ctx)) {
@@ -287,6 +497,16 @@ static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill
*a)
return true;
}
+static bool trans_gtlbfill(DisasContext *ctx, arg_gtlbfill *a)
+{
+ if (check_plv(ctx) || !avail_LVZ(ctx) || ctx->guest_mode) {
+ return false;
+ }
+ gen_helper_gtlbfill(tcg_env);
+ check_mmu_idx(ctx);
+ return true;
+}
+
static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a)
{
if (check_plv(ctx)) {
@@ -297,6 +517,16 @@ static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a)
return true;
}
+static bool trans_gtlbclr(DisasContext *ctx, arg_gtlbclr *a)
+{
+ if (check_plv(ctx) || !avail_LVZ(ctx) || ctx->guest_mode) {
+ return false;
+ }
+ gen_helper_gtlbclr(tcg_env);
+ check_mmu_idx(ctx);
+ return true;
+}
+
static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a)
{
if (check_plv(ctx)) {
@@ -307,6 +537,16 @@ static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush
*a)
return true;
}
+static bool trans_gtlbflush(DisasContext *ctx, arg_gtlbflush *a)
+{
+ if (check_plv(ctx) || !avail_LVZ(ctx) || ctx->guest_mode) {
+ return false;
+ }
+ gen_helper_gtlbflush(tcg_env);
+ check_mmu_idx(ctx);
+ return true;
+}
+
static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
{
TCGv rj = gpr_src(ctx, a->rj, EXT_NONE);
@@ -438,6 +678,10 @@ static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a)
if (check_plv(ctx)) {
return false;
}
+ if (ctx->guest_mode) {
+ gen_helper_gspr(tcg_env);
+ return true;
+ }
generate_exception(ctx, EXCCODE_DBP);
return true;
}
@@ -448,9 +692,26 @@ static bool trans_idle(DisasContext *ctx, arg_idle *a)
return false;
}
+ if (ctx->guest_mode) {
+ gen_helper_gspr(tcg_env);
+ return true;
+ }
+
tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
gen_helper_idle(tcg_env);
ctx->base.is_jmp = DISAS_NORETURN;
return true;
}
+
+static bool trans_hvcl(DisasContext *ctx, arg_hvcl *a)
+{
+ if (!avail_LVZ(ctx) || !ctx->guest_mode) {
+ return false;
+ }
+
+ tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
+ gen_helper_hvcl(tcg_env, tcg_constant_i32(a->imm));
+ ctx->base.is_jmp = DISAS_NORETURN;
+ return true;
+}
#endif
diff --git a/target/loongarch/tcg/translate.c b/target/loongarch/tcg/translate.c
index 124dce6269..15c83ef72d 100644
--- a/target/loongarch/tcg/translate.c
+++ b/target/loongarch/tcg/translate.c
@@ -122,12 +122,16 @@ static void
loongarch_tr_init_disas_context(DisasContextBase *dcbase,
CPULoongArchState *env = cpu_env(cs);
DisasContext *ctx = container_of(dcbase, DisasContext, base);
+ ctx->guest_mode = (ctx->base.tb->flags & HW_FLAGS_GUEST_MODE) != 0;
ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK;
ctx->plv = ctx->base.tb->flags & HW_FLAGS_PLV_MASK;
if (ctx->base.tb->flags & HW_FLAGS_CRMD_PG) {
ctx->mem_idx = ctx->plv;
+ if (ctx->guest_mode) {
+ ctx->mem_idx += MMU_GUEST_IDX;
+ }
} else {
- ctx->mem_idx = MMU_DA_IDX;
+ ctx->mem_idx = ctx->guest_mode ? MMU_GUEST_DA_IDX : MMU_DA_IDX;
}
/* Bound the number of insns to execute to those left on the page. */