* [PATCH v4 00/14] target/loongarch: add LVZ support for TCG
@ 2026-08-26 10:30 SignKirigami
2026-08-26 10:30 ` [PATCH v4 01/14] target/loongarch: Define LVZ CSR fields SignKirigami
` (13 more replies)
0 siblings, 14 replies; 23+ messages in thread
From: SignKirigami @ 2026-08-26 10:30 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, xianglai li, SignKirigami
This patch implements Loongson VirtualiZation (LVZ) extension support
for LoongArch's TCG target. With this patch, it is now possible to start
a nested KVM-accelerated virtual machine on a TCG-emulated virtual machine.
SignKirigami (14):
target/loongarch: Define LVZ CSR fields
target/loongarch: Add LVZ CPU state definitions
target/loongarch: Add LVZ CSR metadata
target/loongarch: Add LVZ CPU model support
target/loongarch: Add LVZ guest interrupt state
target/loongarch: Add LVZ guest timer support
target/loongarch: Add LVZ VM entry and exception handling
target/loongarch: Add LVZ guest CSR helpers
target/loongarch: Add LVZ migration state
target/loongarch: Add LVZ MMU and PTW support
target/loongarch: Translate LVZ guest CSR instructions
target/loongarch: Translate LVZ guest TLB instructions
target/loongarch: Translate LVZ guest traps and HVC
fix(tcg/loongarch): strip flag bits in lddir/ldpte address calculation
target/loongarch/arch_dump.c | 6 +-
target/loongarch/cpu-csr.h | 38 ++
target/loongarch/cpu-mmu.h | 38 +-
target/loongarch/cpu.c | 247 ++++++---
target/loongarch/cpu.h | 64 ++-
target/loongarch/cpu_helper.c | 84 +--
target/loongarch/csr.c | 112 ++--
target/loongarch/csr.h | 6 +-
target/loongarch/disas.c | 15 +
target/loongarch/insns.decode | 14 +
target/loongarch/internals.h | 10 +-
target/loongarch/machine.c | 111 +++-
target/loongarch/tcg/constant_timer.c | 67 ++-
target/loongarch/tcg/csr_helper.c | 224 +++++++-
target/loongarch/tcg/helper.h | 36 +-
.../tcg/insn_trans/trans_extra.c.inc | 2 +-
.../tcg/insn_trans/trans_privileged.c.inc | 388 +++++++++++---
target/loongarch/tcg/op_helper.c | 22 +
target/loongarch/tcg/tcg_cpu.c | 82 ++-
target/loongarch/tcg/tcg_loongarch.h | 3 +-
target/loongarch/tcg/tlb_helper.c | 498 ++++++++++++------
target/loongarch/tcg/translate.c | 6 +-
target/loongarch/translate.h | 2 +
23 files changed, 1588 insertions(+), 487 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v4 01/14] target/loongarch: Define LVZ CSR fields
2026-08-26 10:30 [PATCH v4 00/14] target/loongarch: add LVZ support for TCG SignKirigami
@ 2026-08-26 10:30 ` SignKirigami
2026-08-31 2:27 ` Bibo Mao
2026-08-26 10:30 ` [PATCH v4 02/14] target/loongarch: Add LVZ CPU state definitions SignKirigami
` (12 subsequent siblings)
13 siblings, 1 reply; 23+ messages in thread
From: SignKirigami @ 2026-08-26 10:30 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, xianglai li, SignKirigami, Hengyu Yu
Add LVZ CSR number and bit-field definitions for guest status,
configuration, interrupt, timer and guest TLB registers, preparing
the architectural constants needed by later LVZ CPU state, CSR and TCG
implementation patches.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu-csr.h | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/target/loongarch/cpu-csr.h b/target/loongarch/cpu-csr.h
index d860417af2..438da43a79 100644
--- a/target/loongarch/cpu-csr.h
+++ b/target/loongarch/cpu-csr.h
@@ -224,4 +224,42 @@ FIELD(CSR_DBG, ECODE, 16, 6)
#define LOONGARCH_CSR_DERA 0x501 /* Debug era */
#define LOONGARCH_CSR_DSAVE 0x502 /* Debug save */
+/* LVZ (LoongArch Virtualization) CSRs */
+#define LOONGARCH_CSR_GSTAT 0x50 /* Guest status */
+FIELD(CSR_GSTAT, VM, 0, 1)
+FIELD(CSR_GSTAT, PVM, 1, 1)
+FIELD(CSR_GSTAT, GIDBIT, 4, 6)
+FIELD(CSR_GSTAT, GID, 16, 8)
+
+#define LOONGARCH_CSR_GCFG 0x51 /* Guest config */
+FIELD(CSR_GCFG, MATP, 0, 4)
+FIELD(CSR_GCFG, MATC, 4, 2)
+FIELD(CSR_GCFG, TOPIP, 6, 1)
+FIELD(CSR_GCFG, TOPI, 7, 1)
+FIELD(CSR_GCFG, TOTIP, 8, 1)
+FIELD(CSR_GCFG, TOTI, 9, 1)
+FIELD(CSR_GCFG, TOEP, 10, 1)
+FIELD(CSR_GCFG, TOE, 11, 1)
+FIELD(CSR_GCFG, TOPP, 12, 1)
+FIELD(CSR_GCFG, TOP, 13, 1)
+FIELD(CSR_GCFG, TOHUP, 14, 1)
+FIELD(CSR_GCFG, TOHU, 15, 1)
+FIELD(CSR_GCFG, TOCIP, 16, 4)
+FIELD(CSR_GCFG, TOCI, 20, 2)
+FIELD(CSR_GCFG, GPMP, 23, 1)
+FIELD(CSR_GCFG, GPMNUM, 24, 3)
+
+#define LOONGARCH_CSR_GINTC 0x52 /* Guest interrupt config */
+FIELD(CSR_GINTC, HWIS, 0, 8)
+FIELD(CSR_GINTC, HWIP, 8, 8)
+FIELD(CSR_GINTC, HWIC, 16, 8)
+
+#define LOONGARCH_CSR_GCNTC 0x53 /* Guest counter compensation */
+
+#define LOONGARCH_CSR_GTLBC 0x15 /* Guest TLB control */
+FIELD(CSR_GTLBC, GMTLBSZ, 0, 6)
+FIELD(CSR_GTLBC, USETGID, 12, 1)
+FIELD(CSR_GTLBC, TOTI, 13, 1)
+FIELD(CSR_GTLBC, TGID, 16, 8)
+
#endif /* LOONGARCH_CPU_CSR_H */
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 02/14] target/loongarch: Add LVZ CPU state definitions
2026-08-26 10:30 [PATCH v4 00/14] target/loongarch: add LVZ support for TCG SignKirigami
2026-08-26 10:30 ` [PATCH v4 01/14] target/loongarch: Define LVZ CSR fields SignKirigami
@ 2026-08-26 10:30 ` SignKirigami
2026-08-31 2:56 ` Bibo Mao
2026-08-26 10:30 ` [PATCH v4 03/14] target/loongarch: Add LVZ CSR metadata SignKirigami
` (11 subsequent siblings)
13 siblings, 1 reply; 23+ messages in thread
From: SignKirigami @ 2026-08-26 10:30 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, xianglai li, SignKirigami, Hengyu Yu
Add host and guest system-state banks, LVZ exception and MMU indices,
CPU and translation state, and helpers for selecting a VM-level CSR bank.
Read host ERA and BADV explicitly when generating ELF core notes so the
result does not depend on the currently selected guest state.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/arch_dump.c | 6 ++--
target/loongarch/cpu.c | 4 +--
target/loongarch/cpu.h | 64 ++++++++++++++++++++++++++++++++++--
target/loongarch/translate.h | 2 ++
4 files changed, 68 insertions(+), 8 deletions(-)
diff --git a/target/loongarch/arch_dump.c b/target/loongarch/arch_dump.c
index 9d84faef96..711b23257b 100644
--- a/target/loongarch/arch_dump.c
+++ b/target/loongarch/arch_dump.c
@@ -116,7 +116,7 @@ int loongarch_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
{
struct loongarch_note note;
CPULoongArchState *env = &LOONGARCH_CPU(cs)->env;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
int ret, i;
loongarch_note_init(¬e, s, "CORE", 5, NT_PRSTATUS,
@@ -127,8 +127,8 @@ int loongarch_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
for (i = 0; i < 32; ++i) {
note.prstatus.pr_reg.gpr[i] = cpu_to_dump64(s, env->gpr[i]);
}
- note.prstatus.pr_reg.csr_era = cpu_to_dump64(s, sys->CSR_ERA);
- note.prstatus.pr_reg.csr_badv = cpu_to_dump64(s, sys->CSR_BADV);
+ note.prstatus.pr_reg.csr_era = cpu_to_dump64(s, host->CSR_ERA);
+ note.prstatus.pr_reg.csr_badv = cpu_to_dump64(s, host->CSR_BADV);
ret = f(¬e, LOONGARCH_PRSTATUS_NOTE_SIZE, s);
if (ret < 0) {
return -1;
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index fb03424ffa..2f7692dc93 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -282,7 +282,7 @@ static void loongarch_la464_initfn(Object *obj)
uint32_t data = 0, field;
int i;
- set_sys_state(env, &env->sys_states[0]);
+ set_sys(env, LOONGARCH_VM_LEVEL_HOST);
for (i = 0; i < 21; i++) {
env->cpucfg[i] = 0x0;
}
@@ -412,7 +412,7 @@ static void loongarch_la132_initfn(Object *obj)
uint32_t data = 0;
int i;
- set_sys_state(env, &env->sys_states[0]);
+ set_sys(env, LOONGARCH_VM_LEVEL_HOST);
for (i = 0; i < 21; i++) {
env->cpucfg[i] = 0x0;
}
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index ad30c73167..be982b7695 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -20,6 +20,8 @@
#include "cpu-csr.h"
#include "cpu-qom.h"
+#define CPU_INTERRUPT_GUEST CPU_INTERRUPT_TGT_EXT_0
+
#define FCSR0_M1 0x1f /* FCSR1 mask, Enables */
#define FCSR0_M2 0x1f1f0000 /* FCSR2 mask, Cause and Flags */
#define FCSR0_M3 0x300 /* FCSR3 mask, Round Mode */
@@ -93,6 +95,10 @@ FIELD(FCSR0, CAUSE, 24, 5)
#define EXCCODE_WPEM EXCODE(19, 1)
#define EXCCODE_BTD EXCODE(20, 0)
#define EXCCODE_BTE EXCODE(21, 0)
+#define EXCCODE_GSPR EXCODE(22, 0)
+#define EXCCODE_HVC EXCODE(23, 0)
+#define EXCCODE_GCSC EXCODE(24, 0)
+#define EXCCODE_GCHC EXCODE(25, 0)
#define EXCCODE_DBP EXCODE(26, 0) /* Reserved subcode used for debug */
/* cpucfg[0] bits */
@@ -255,6 +261,7 @@ FIELD(TLB_MISC, E, 0, 1)
FIELD(TLB_MISC, ASID, 1, 10)
FIELD(TLB_MISC, VPPN, 13, 35)
FIELD(TLB_MISC, PS, 48, 6)
+FIELD(TLB_MISC, GID, 54, 8)
/*Msg interrupt registers */
#define N_MSGIS 4
@@ -314,6 +321,10 @@ typedef struct LoongArchBT {
uint32_t ftop;
} lbt_t;
+#define LOONGARCH_VM_LEVEL_HOST 0
+#define LOONGARCH_VM_LEVEL_GUEST 1
+#define LOONGARCH_VM_LEVELS 2
+
#define CPU_VENDOR_LOONGSON "Loongson"
#define CPU_MODEL_3A5000 "3A5000"
#define CPU_MODEL_1C101 "1C101"
@@ -375,10 +386,18 @@ typedef struct CPUSysState {
uint64_t CSR_DBG;
uint64_t CSR_DERA;
uint64_t CSR_DSAVE;
+ uint64_t CSR_GSTAT;
+ uint64_t CSR_GCFG;
+ uint64_t CSR_GINTC;
+ uint64_t CSR_GCNTC;
+ uint64_t CSR_GTLBC;
/* Msg interrupt registers */
uint64_t CSR_MSGIS[N_MSGIS];
uint64_t CSR_MSGIR;
uint64_t CSR_MSGIE;
+#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY)
+ LoongArchTLB tlb[LOONGARCH_TLB_MAX];
+#endif
} CPUSysState;
typedef struct CPUArchState {
@@ -394,7 +413,7 @@ typedef struct CPUArchState {
uint32_t pv_features;
uint64_t vendor_id;
uint64_t cpu_id;
- CPUSysState sys_states[1];
+ CPUSysState sys_states[LOONGARCH_VM_LEVELS];
struct {
uint64_t guest_addr;
@@ -419,6 +438,7 @@ typedef struct CPUArchState {
uint32_t mp_state;
#endif
CPUSysState *sys_state;
+ bool vm_exit;
} CPULoongArchState;
typedef struct LoongArchCPUTopo {
@@ -438,8 +458,10 @@ struct ArchCPU {
CPULoongArchState env;
QEMUTimer timer;
+ QEMUTimer guest_timer;
uint32_t phy_id;
OnOffAuto lbt;
+ OnOffAuto lvz;
OnOffAuto pmu;
OnOffAuto ptw;
OnOffAuto lsx;
@@ -484,15 +506,44 @@ struct LoongArchCPUClass {
#define MMU_KERNEL_IDX MMU_PLV_KERNEL
#define MMU_USER_IDX MMU_PLV_USER
#define MMU_DA_IDX 4
+#define MMU_GUEST_IDX 5
+#define MMU_GUEST_DA_IDX 9
+
+static inline bool is_guest_mmu_idx(int mmu_idx)
+{
+ return mmu_idx >= MMU_GUEST_IDX;
+}
+
+static inline int mmu_idx_to_plv(int mmu_idx)
+{
+ if (mmu_idx == MMU_DA_IDX || mmu_idx == MMU_GUEST_DA_IDX) {
+ return 0;
+ }
+ if (is_guest_mmu_idx(mmu_idx)) {
+ return mmu_idx - MMU_GUEST_IDX;
+ }
+ return mmu_idx;
+}
static inline CPUSysState *env_sys(CPULoongArchState *env)
{
return env->sys_state;
}
-static inline void set_sys_state(CPULoongArchState *env, CPUSysState *sys)
+static inline CPUSysState *get_sys(CPULoongArchState *env, int vm_level)
+{
+ return &env->sys_states[vm_level];
+}
+
+static inline void set_sys(CPULoongArchState *env, int vm_level)
{
- env->sys_state = sys;
+ env->sys_state = get_sys(env, vm_level);
+}
+
+static inline int env_vm_level(CPULoongArchState *env)
+{
+ return env_sys(env) == get_sys(env, LOONGARCH_VM_LEVEL_GUEST) ?
+ LOONGARCH_VM_LEVEL_GUEST : LOONGARCH_VM_LEVEL_HOST;
}
static inline bool is_la64(CPULoongArchState *env)
@@ -530,6 +581,13 @@ static inline void set_pc(CPULoongArchState *env, uint64_t value)
#define HW_FLAGS_CRMD_PG R_CSR_CRMD_PG_MASK /* 0x10 */
#define HW_FLAGS_VA32 0x20
#define HW_FLAGS_EUEN_ASXE 0x40
+#define HW_FLAGS_GUEST_MODE 0x80
+
+bool has_lvz_capability(CPULoongArchState *env);
+bool will_return_to_guest(CPULoongArchState *env);
+uint8_t get_gid(CPULoongArchState *env);
+uint8_t get_tgid(CPULoongArchState *env);
+void trigger_vm_exit(CPULoongArchState *env);
#define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU
diff --git a/target/loongarch/translate.h b/target/loongarch/translate.h
index 8aa8325dc6..257c3bbddd 100644
--- a/target/loongarch/translate.h
+++ b/target/loongarch/translate.h
@@ -24,6 +24,7 @@
#define avail_FP(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, FP))
#define avail_FP_SP(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, FP_SP))
#define avail_FP_DP(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, FP_DP))
+#define avail_LVZ(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, LVZ))
#define avail_LSPW(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, LSPW))
#define avail_LAM(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, LAM))
#define avail_LAM_BH(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, LAM_BH))
@@ -66,6 +67,7 @@ typedef struct DisasContext {
TCGv zero;
bool la64; /* LoongArch64 mode */
bool va32; /* 32-bit virtual address */
+ bool guest_mode;
uint32_t cpucfg1;
uint32_t cpucfg2;
uint32_t cpucfg3;
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 03/14] target/loongarch: Add LVZ CSR metadata
2026-08-26 10:30 [PATCH v4 00/14] target/loongarch: add LVZ support for TCG SignKirigami
2026-08-26 10:30 ` [PATCH v4 01/14] target/loongarch: Define LVZ CSR fields SignKirigami
2026-08-26 10:30 ` [PATCH v4 02/14] target/loongarch: Add LVZ CPU state definitions SignKirigami
@ 2026-08-26 10:30 ` SignKirigami
2026-09-01 7:08 ` Bibo Mao
2026-08-26 10:30 ` [PATCH v4 04/14] target/loongarch: Add LVZ CPU model support SignKirigami
` (10 subsequent siblings)
13 siblings, 1 reply; 23+ messages in thread
From: SignKirigami @ 2026-08-26 10:30 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, xianglai li, SignKirigami, Hengyu Yu
Add LVZ host and guest CSR access metadata to the existing CSR table. Keep
one shared register description and calculate the host or guest bank
offset at translation time instead of duplicating CSRInfo entries.
Mark guest read-only, sensitive and unavailable CSRs for later LVZ
instruction translation.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu.c | 3 +-
target/loongarch/csr.c | 108 +++++++++---------
target/loongarch/csr.h | 2 +
| 2 +-
.../tcg/insn_trans/trans_privileged.c.inc | 6 +-
5 files changed, 65 insertions(+), 56 deletions(-)
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index 2f7692dc93..0f6a211949 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -783,7 +783,8 @@ static void loongarch_cpu_dump_csr(CPUState *cs, FILE *f)
qemu_fprintf(f, " CSR%03d:", col);
}
- addr = (void *)env + get_csr_offset(csr_info, 0);
+ addr = (void *)env + get_csr_offset(csr_info,
+ LOONGARCH_VM_LEVEL_HOST);
qemu_fprintf(f, " %s ", csr_info->name);
len = strlen(csr_info->name);
for (; len < 6; len++) {
diff --git a/target/loongarch/csr.c b/target/loongarch/csr.c
index 309b826ca9..fa018a4582 100644
--- a/target/loongarch/csr.c
+++ b/target/loongarch/csr.c
@@ -13,15 +13,16 @@
.flags = FL, .readfn = RD, .writefn = WR \
}
-#define CSR_OFF_ARRAY(NAME, N) \
+#define CSR_OFF_ARRAY_FLAGS(NAME, N, FL) \
[LOONGARCH_CSR_##NAME(N)] = { \
.name = (stringify(NAME##N)), \
.offset = CSR_OFFSET(CSR_##NAME[N]), \
- .flags = CSRFL_BASIC, .readfn = NULL, .writefn = NULL \
+ .flags = FL \
}
#define CSR_OFF_FLAGS(NAME, FL) CSR_OFF_FUNCS(NAME, FL, NULL, NULL)
#define CSR_OFF(NAME) CSR_OFF_FLAGS(NAME, CSRFL_BASIC)
+#define CSR_OFF_ARRAY(NAME, N) CSR_OFF_ARRAY_FLAGS(NAME, N, CSRFL_BASIC)
static CSRInfo csr_info[] = {
CSR_OFF_FLAGS(CRMD, CSRFL_EXITTB),
@@ -35,6 +36,7 @@ static CSRInfo csr_info[] = {
CSR_OFF_FLAGS(BADI, CSRFL_READONLY),
CSR_OFF(EENTRY),
CSR_OFF(TLBIDX),
+ CSR_OFF_FLAGS(GTLBC, CSRFL_GSPR),
CSR_OFF(TLBEHI),
CSR_OFF(TLBELO0),
CSR_OFF(TLBELO1),
@@ -71,9 +73,13 @@ static CSRInfo csr_info[] = {
CSR_OFF_FLAGS(TVAL, CSRFL_READONLY | CSRFL_IO),
CSR_OFF(CNTC),
CSR_OFF_FLAGS(TICLR, CSRFL_IO),
+ CSR_OFF_FLAGS(GSTAT, CSRFL_GSPR),
+ CSR_OFF_FLAGS(GCFG, CSRFL_GSPR),
+ CSR_OFF_FLAGS(GINTC, CSRFL_IO | CSRFL_GSPR),
+ CSR_OFF_FLAGS(GCNTC, CSRFL_GSPR),
CSR_OFF(LLBCTL),
- CSR_OFF(IMPCTL1),
- CSR_OFF(IMPCTL2),
+ CSR_OFF_FLAGS(IMPCTL1, CSRFL_GSPR),
+ CSR_OFF_FLAGS(IMPCTL2, CSRFL_GSPR),
CSR_OFF(TLBRENTRY),
CSR_OFF(TLBRBADV),
CSR_OFF(TLBRERA),
@@ -82,57 +88,57 @@ static CSRInfo csr_info[] = {
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_FLAGS(MERRCTL, CSRFL_GSPR),
+ CSR_OFF_FLAGS(MERRINFO1, CSRFL_GSPR),
+ CSR_OFF_FLAGS(MERRINFO2, CSRFL_GSPR),
+ CSR_OFF_FLAGS(MERRENTRY, CSRFL_GSPR),
+ CSR_OFF_FLAGS(MERRERA, CSRFL_GSPR),
+ CSR_OFF_FLAGS(MERRSAVE, CSRFL_GSPR),
+ CSR_OFF_FLAGS(CTAG, CSRFL_GSPR),
CSR_OFF_ARRAY(DMW, 0),
CSR_OFF_ARRAY(DMW, 1),
CSR_OFF_ARRAY(DMW, 2),
CSR_OFF_ARRAY(DMW, 3),
- CSR_OFF_ARRAY(PERFCTRL, 0),
- CSR_OFF_ARRAY(PERFCNTR, 0),
- CSR_OFF_ARRAY(PERFCTRL, 1),
- CSR_OFF_ARRAY(PERFCNTR, 1),
- CSR_OFF_ARRAY(PERFCTRL, 2),
- CSR_OFF_ARRAY(PERFCNTR, 2),
- CSR_OFF_ARRAY(PERFCTRL, 3),
- CSR_OFF_ARRAY(PERFCNTR, 3),
- CSR_OFF_ARRAY(PERFCTRL, 4),
- CSR_OFF_ARRAY(PERFCNTR, 4),
- CSR_OFF_ARRAY(PERFCTRL, 5),
- CSR_OFF_ARRAY(PERFCNTR, 5),
- CSR_OFF_ARRAY(PERFCTRL, 6),
- CSR_OFF_ARRAY(PERFCNTR, 6),
- CSR_OFF_ARRAY(PERFCTRL, 7),
- CSR_OFF_ARRAY(PERFCNTR, 7),
- CSR_OFF_ARRAY(PERFCTRL, 8),
- CSR_OFF_ARRAY(PERFCNTR, 8),
- CSR_OFF_ARRAY(PERFCTRL, 9),
- CSR_OFF_ARRAY(PERFCNTR, 9),
- CSR_OFF_ARRAY(PERFCTRL, 10),
- CSR_OFF_ARRAY(PERFCNTR, 10),
- CSR_OFF_ARRAY(PERFCTRL, 11),
- CSR_OFF_ARRAY(PERFCNTR, 11),
- CSR_OFF_ARRAY(PERFCTRL, 12),
- CSR_OFF_ARRAY(PERFCNTR, 12),
- CSR_OFF_ARRAY(PERFCTRL, 13),
- CSR_OFF_ARRAY(PERFCNTR, 13),
- CSR_OFF_ARRAY(PERFCTRL, 14),
- CSR_OFF_ARRAY(PERFCNTR, 14),
- CSR_OFF_ARRAY(PERFCTRL, 15),
- CSR_OFF_ARRAY(PERFCNTR, 15),
- CSR_OFF(DBG),
- CSR_OFF(DERA),
- CSR_OFF(DSAVE),
- CSR_OFF_ARRAY(MSGIS, 0),
- CSR_OFF_ARRAY(MSGIS, 1),
- CSR_OFF_ARRAY(MSGIS, 2),
- CSR_OFF_ARRAY(MSGIS, 3),
- CSR_OFF(MSGIR),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 0, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 0, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 1, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 1, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 2, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 2, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 3, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 3, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 4, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 4, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 5, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 5, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 6, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 6, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 7, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 7, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 8, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 8, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 9, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 9, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 10, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 10, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 11, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 11, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 12, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 12, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 13, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 13, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 14, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 14, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 15, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 15, CSRFL_GUEST_INVALID),
+ CSR_OFF_FLAGS(DBG, CSRFL_GSPR),
+ CSR_OFF_FLAGS(DERA, CSRFL_GSPR),
+ CSR_OFF_FLAGS(DSAVE, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(MSGIS, 0, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(MSGIS, 1, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(MSGIS, 2, CSRFL_GUEST_INVALID),
+ CSR_OFF_ARRAY_FLAGS(MSGIS, 3, CSRFL_GUEST_INVALID),
+ CSR_OFF_FLAGS(MSGIR, CSRFL_GUEST_INVALID),
};
CSRInfo *get_csr(unsigned int csr_num)
diff --git a/target/loongarch/csr.h b/target/loongarch/csr.h
index c2b6b882bc..647695e248 100644
--- a/target/loongarch/csr.h
+++ b/target/loongarch/csr.h
@@ -20,6 +20,8 @@ enum {
CSRFL_IO = (1 << 2),
CSRFL_UNUSED = (1 << 3),
CSRFL_BASIC = (1 << 4),
+ CSRFL_GSPR = (1 << 5),
+ CSRFL_GUEST_INVALID = (1 << 6),
};
typedef struct {
--git a/target/loongarch/tcg/insn_trans/trans_extra.c.inc b/target/loongarch/tcg/insn_trans/trans_extra.c.inc
index 655dce329e..e69384497f 100644
--- a/target/loongarch/tcg/insn_trans/trans_extra.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_extra.c.inc
@@ -55,7 +55,7 @@ static bool gen_rdtime(DisasContext *ctx, arg_rr *a,
tcg_gen_sextract_tl(dst1, dst1, high ? 32 : 0, 32);
}
- offset = CPU_CSR_OFFSET(CSR_TID, 0);
+ offset = CPU_CSR_OFFSET(CSR_TID, LOONGARCH_VM_LEVEL_HOST);
tcg_gen_ld_i64(dst2, tcg_env, offset);
return true;
diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
index 6728ce5ec9..bcfc977990 100644
--- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
@@ -122,7 +122,7 @@ static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
if (readfn) {
readfn(dest, tcg_env);
} else {
- offset = get_csr_offset(csr, 0);
+ offset = get_csr_offset(csr, LOONGARCH_VM_LEVEL_HOST);
tcg_gen_ld_tl(dest, tcg_env, offset);
}
}
@@ -157,7 +157,7 @@ static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
writefn(dest, tcg_env, src1);
} else {
dest = tcg_temp_new();
- offset = get_csr_offset(csr, 0);
+ offset = get_csr_offset(csr, LOONGARCH_VM_LEVEL_HOST);
tcg_gen_ld_tl(dest, tcg_env, offset);
tcg_gen_st_tl(src1, tcg_env, offset);
}
@@ -196,7 +196,7 @@ static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
newv = tcg_temp_new();
temp = tcg_temp_new();
- offset = get_csr_offset(csr, 0);
+ offset = get_csr_offset(csr, LOONGARCH_VM_LEVEL_HOST);
tcg_gen_ld_tl(oldv, tcg_env, offset);
tcg_gen_and_tl(newv, src1, mask);
tcg_gen_andc_tl(temp, oldv, mask);
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 04/14] target/loongarch: Add LVZ CPU model support
2026-08-26 10:30 [PATCH v4 00/14] target/loongarch: add LVZ support for TCG SignKirigami
` (2 preceding siblings ...)
2026-08-26 10:30 ` [PATCH v4 03/14] target/loongarch: Add LVZ CSR metadata SignKirigami
@ 2026-08-26 10:30 ` SignKirigami
2026-09-02 3:09 ` Bibo Mao
2026-08-26 10:30 ` [PATCH v4 05/14] target/loongarch: Add LVZ guest interrupt state SignKirigami
` (9 subsequent siblings)
13 siblings, 1 reply; 23+ messages in thread
From: SignKirigami @ 2026-08-26 10:30 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, xianglai li, SignKirigami, Hengyu Yu
Add the LVZ CPU property and initialize the host and guest state banks
for the LA464 model. Reset always returns to the host CSR and TLB bank
and initializes the implemented guest ID width.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu.c | 145 +++++++++++++++++++++++++----------------
1 file changed, 90 insertions(+), 55 deletions(-)
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index 0f6a211949..c5c7f3e0e3 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -114,12 +114,12 @@ static void loongarch_la464_init_csr(DeviceState *dev)
static bool initialized;
LoongArchCPU *cpu = LOONGARCH_CPU(dev);
CPULoongArchState *env = &cpu->env;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
int i, num;
if (!initialized) {
initialized = true;
- num = FIELD_EX64(sys->CSR_PRCFG1, CSR_PRCFG1, SAVE_NUM);
+ num = FIELD_EX64(host->CSR_PRCFG1, CSR_PRCFG1, SAVE_NUM);
for (i = num; i < 16; i++) {
set_csr_flag(LOONGARCH_CSR_SAVE(i), CSRFL_UNUSED);
}
@@ -251,11 +251,32 @@ static void loongarch_set_ptw(Object *obj, bool value, Error **errp)
cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, HPTW, value);
}
+static bool loongarch_get_lvz(Object *obj, Error **errp)
+{
+ return LOONGARCH_CPU(obj)->lvz != ON_OFF_AUTO_OFF;
+}
+
+static void loongarch_set_lvz(Object *obj, bool value, Error **errp)
+{
+ LoongArchCPU *cpu = LOONGARCH_CPU(obj);
+
+ cpu->lvz = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
+
+ if (kvm_enabled()) {
+ return;
+ }
+
+ cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LVZ, value);
+ cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LVZ_VER,
+ value ? 1 : 0);
+}
+
static void loongarch_cpu_post_init(Object *obj)
{
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
cpu->lbt = ON_OFF_AUTO_OFF;
+ cpu->lvz = ON_OFF_AUTO_AUTO;
cpu->pmu = ON_OFF_AUTO_OFF;
cpu->lsx = ON_OFF_AUTO_AUTO;
cpu->lasx = ON_OFF_AUTO_AUTO;
@@ -267,6 +288,8 @@ static void loongarch_cpu_post_init(Object *obj)
loongarch_set_msgint);
object_property_add_bool(obj, "ptw", loongarch_get_ptw,
loongarch_set_ptw);
+ object_property_add_bool(obj, "lvz", loongarch_get_lvz,
+ loongarch_set_lvz);
/* lbt is enabled only in kvm mode, not supported in tcg mode */
if (kvm_enabled()) {
@@ -278,7 +301,8 @@ static void loongarch_la464_initfn(Object *obj)
{
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
CPULoongArchState *env = &cpu->env;
- CPUSysState *sys;
+ CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
+ CPUSysState *guest = get_sys(env, LOONGARCH_VM_LEVEL_GUEST);
uint32_t data = 0, field;
int i;
@@ -322,6 +346,8 @@ static void loongarch_la464_initfn(Object *obj)
data = FIELD_DP32(data, CPUCFG2, FP_VER, 1);
data = FIELD_DP32(data, CPUCFG2, LSX, 1),
data = FIELD_DP32(data, CPUCFG2, LASX, 1),
+ data = FIELD_DP32(data, CPUCFG2, LVZ, 1);
+ data = FIELD_DP32(data, CPUCFG2, LVZ_VER, 1);
data = FIELD_DP32(data, CPUCFG2, LLFTP, 1);
data = FIELD_DP32(data, CPUCFG2, LLFTP_VER, 1);
data = FIELD_DP32(data, CPUCFG2, LSPW, 1);
@@ -386,19 +412,19 @@ static void loongarch_la464_initfn(Object *obj)
data = FIELD_DP32(data, CPUCFG20, L3IU_SIZE, 6);
env->cpucfg[20] = data;
- sys = env_sys(env);
- sys->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa);
+ host->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa);
+ guest->CSR_ASID = host->CSR_ASID;
- sys->CSR_PRCFG1 = FIELD_DP64(sys->CSR_PRCFG1, CSR_PRCFG1, SAVE_NUM, 8);
- sys->CSR_PRCFG1 = FIELD_DP64(sys->CSR_PRCFG1, CSR_PRCFG1, TIMER_BITS, 0x2f);
- sys->CSR_PRCFG1 = FIELD_DP64(sys->CSR_PRCFG1, CSR_PRCFG1, VSMAX, 7);
+ host->CSR_PRCFG1 = FIELD_DP64(host->CSR_PRCFG1, CSR_PRCFG1, SAVE_NUM, 8);
+ host->CSR_PRCFG1 = FIELD_DP64(host->CSR_PRCFG1, CSR_PRCFG1, TIMER_BITS, 0x2f);
+ host->CSR_PRCFG1 = FIELD_DP64(host->CSR_PRCFG1, CSR_PRCFG1, VSMAX, 7);
- sys->CSR_PRCFG2 = 0x3ffff000;
+ host->CSR_PRCFG2 = 0x3ffff000;
- sys->CSR_PRCFG3 = FIELD_DP64(sys->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2);
- sys->CSR_PRCFG3 = FIELD_DP64(sys->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63);
- sys->CSR_PRCFG3 = FIELD_DP64(sys->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7);
- sys->CSR_PRCFG3 = FIELD_DP64(sys->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8);
+ host->CSR_PRCFG3 = FIELD_DP64(host->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2);
+ host->CSR_PRCFG3 = FIELD_DP64(host->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63);
+ host->CSR_PRCFG3 = FIELD_DP64(host->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7);
+ host->CSR_PRCFG3 = FIELD_DP64(host->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8);
cpu->msgint = ON_OFF_AUTO_OFF;
cpu->ptw = ON_OFF_AUTO_OFF;
@@ -439,6 +465,7 @@ static void loongarch_la132_initfn(Object *obj)
data = FIELD_DP32(data, CPUCFG1, CRC, 1);
env->cpucfg[1] = data;
cpu->msgint = ON_OFF_AUTO_OFF;
+ cpu->lvz = ON_OFF_AUTO_OFF;
cpu->ptw = ON_OFF_AUTO_OFF;
}
@@ -600,7 +627,7 @@ static void loongarch_cpu_reset_hold(Object *obj, ResetType type)
CPUState *cs = CPU(obj);
LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(obj);
CPULoongArchState *env = cpu_env(cs);
- CPUSysState *sys = env_sys(env);
+ CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
if (lacc->parent_phases.hold) {
lacc->parent_phases.hold(obj, type);
@@ -624,61 +651,69 @@ static void loongarch_cpu_reset_hold(Object *obj, ResetType type)
int n;
/* Set csr registers value after reset, see the manual 6.4. */
- sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, PLV, 0);
- sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, IE, 0);
- sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, DA, 1);
- sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, PG, 0);
- sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, DATF, 0);
- sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, DATM, 0);
-
- sys->CSR_EUEN = FIELD_DP64(sys->CSR_EUEN, CSR_EUEN, FPE, 0);
- sys->CSR_EUEN = FIELD_DP64(sys->CSR_EUEN, CSR_EUEN, SXE, 0);
- sys->CSR_EUEN = FIELD_DP64(sys->CSR_EUEN, CSR_EUEN, ASXE, 0);
- sys->CSR_EUEN = FIELD_DP64(sys->CSR_EUEN, CSR_EUEN, BTE, 0);
-
- sys->CSR_MISC = 0;
-
- sys->CSR_ECFG = FIELD_DP64(sys->CSR_ECFG, CSR_ECFG, VS, 0);
- sys->CSR_ECFG = FIELD_DP64(sys->CSR_ECFG, CSR_ECFG, LIE, 0);
-
- sys->CSR_ESTAT = sys->CSR_ESTAT & (~MAKE_64BIT_MASK(0, 2));
- sys->CSR_RVACFG = FIELD_DP64(sys->CSR_RVACFG, CSR_RVACFG, RBITS, 0);
- sys->CSR_CPUID = cs->cpu_index;
- sys->CSR_TCFG = FIELD_DP64(sys->CSR_TCFG, CSR_TCFG, EN, 0);
- sys->CSR_LLBCTL = FIELD_DP64(sys->CSR_LLBCTL, CSR_LLBCTL, KLO, 0);
- sys->CSR_TLBRERA = FIELD_DP64(sys->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0);
- sys->CSR_MERRCTL = FIELD_DP64(sys->CSR_MERRCTL, CSR_MERRCTL, ISMERR, 0);
- sys->CSR_TID = cs->cpu_index;
+ host->CSR_CRMD = FIELD_DP64(host->CSR_CRMD, CSR_CRMD, PLV, 0);
+ host->CSR_CRMD = FIELD_DP64(host->CSR_CRMD, CSR_CRMD, IE, 0);
+ host->CSR_CRMD = FIELD_DP64(host->CSR_CRMD, CSR_CRMD, DA, 1);
+ host->CSR_CRMD = FIELD_DP64(host->CSR_CRMD, CSR_CRMD, PG, 0);
+ host->CSR_CRMD = FIELD_DP64(host->CSR_CRMD, CSR_CRMD, DATF, 0);
+ host->CSR_CRMD = FIELD_DP64(host->CSR_CRMD, CSR_CRMD, DATM, 0);
+
+ host->CSR_EUEN = FIELD_DP64(host->CSR_EUEN, CSR_EUEN, FPE, 0);
+ host->CSR_EUEN = FIELD_DP64(host->CSR_EUEN, CSR_EUEN, SXE, 0);
+ host->CSR_EUEN = FIELD_DP64(host->CSR_EUEN, CSR_EUEN, ASXE, 0);
+ host->CSR_EUEN = FIELD_DP64(host->CSR_EUEN, CSR_EUEN, BTE, 0);
+
+ host->CSR_MISC = 0;
+
+ host->CSR_ECFG = FIELD_DP64(host->CSR_ECFG, CSR_ECFG, VS, 0);
+ host->CSR_ECFG = FIELD_DP64(host->CSR_ECFG, CSR_ECFG, LIE, 0);
+
+ host->CSR_ESTAT = host->CSR_ESTAT & (~MAKE_64BIT_MASK(0, 2));
+ host->CSR_RVACFG = FIELD_DP64(host->CSR_RVACFG, CSR_RVACFG, RBITS, 0);
+ host->CSR_CPUID = cs->cpu_index;
+ host->CSR_TCFG = FIELD_DP64(host->CSR_TCFG, CSR_TCFG, EN, 0);
+ host->CSR_LLBCTL = FIELD_DP64(host->CSR_LLBCTL, CSR_LLBCTL, KLO, 0);
+ host->CSR_TLBRERA = FIELD_DP64(host->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0);
+ host->CSR_MERRCTL = FIELD_DP64(host->CSR_MERRCTL, CSR_MERRCTL, ISMERR, 0);
+ host->CSR_TID = cs->cpu_index;
+ host->CSR_GSTAT = 0;
+ host->CSR_GCFG = 0;
+ host->CSR_GINTC = 0;
+ host->CSR_GCNTC = 0;
+ host->CSR_GTLBC = 0;
/*
* Workaround for edk2-stable202408, CSR PGD register is set only if
* its value is equal to zero for boot cpu, it causes reboot issue.
*
* Here clear CSR registers relative with TLB.
*/
- sys->CSR_PGDH = 0;
- sys->CSR_PGDL = 0;
- sys->CSR_PWCH = 0;
- sys->CSR_EENTRY = 0;
- sys->CSR_TLBRENTRY = 0;
- sys->CSR_MERRENTRY = 0;
+ host->CSR_PGDH = 0;
+ host->CSR_PGDL = 0;
+ host->CSR_PWCH = 0;
+ host->CSR_EENTRY = 0;
+ host->CSR_TLBRENTRY = 0;
+ host->CSR_MERRENTRY = 0;
/* set CSR_PWCL.PTBASE and CSR_STLBPS.PS bits from CSR_PRCFG2 */
- if (sys->CSR_PRCFG2 == 0) {
- sys->CSR_PRCFG2 = 0x3fffff000;
+ if (host->CSR_PRCFG2 == 0) {
+ host->CSR_PRCFG2 = 0x3fffff000;
}
- tlb_ps = ctz32(sys->CSR_PRCFG2);
- sys->CSR_STLBPS = FIELD_DP64(sys->CSR_STLBPS, CSR_STLBPS, PS, tlb_ps);
- sys->CSR_PWCL = FIELD_DP64(sys->CSR_PWCL, CSR_PWCL, PTBASE, tlb_ps);
+ tlb_ps = ctz32(host->CSR_PRCFG2);
+ host->CSR_STLBPS = FIELD_DP64(host->CSR_STLBPS, CSR_STLBPS, PS, tlb_ps);
+ host->CSR_PWCL = FIELD_DP64(host->CSR_PWCL, CSR_PWCL, PTBASE, tlb_ps);
for (n = 0; n < 4; n++) {
- sys->CSR_DMW[n] = FIELD_DP64(sys->CSR_DMW[n], CSR_DMW, PLV0, 0);
- sys->CSR_DMW[n] = FIELD_DP64(sys->CSR_DMW[n], CSR_DMW, PLV1, 0);
- sys->CSR_DMW[n] = FIELD_DP64(sys->CSR_DMW[n], CSR_DMW, PLV2, 0);
- sys->CSR_DMW[n] = FIELD_DP64(sys->CSR_DMW[n], CSR_DMW, PLV3, 0);
+ host->CSR_DMW[n] = FIELD_DP64(host->CSR_DMW[n], CSR_DMW, PLV0, 0);
+ host->CSR_DMW[n] = FIELD_DP64(host->CSR_DMW[n], CSR_DMW, PLV1, 0);
+ host->CSR_DMW[n] = FIELD_DP64(host->CSR_DMW[n], CSR_DMW, PLV2, 0);
+ host->CSR_DMW[n] = FIELD_DP64(host->CSR_DMW[n], CSR_DMW, PLV3, 0);
+ }
+ if (FIELD_EX32(env->cpucfg[2], CPUCFG2, LVZ)) {
+ host->CSR_GSTAT = FIELD_DP64(host->CSR_GSTAT, CSR_GSTAT, GIDBIT, 8);
}
#ifndef CONFIG_USER_ONLY
env->pc = 0x1c000000;
#ifdef CONFIG_TCG
- memset(env->tlb, 0, sizeof(env->tlb));
+ memset(host->tlb, 0, sizeof(host->tlb));
#endif
if (kvm_enabled()) {
kvm_arch_reset_vcpu(cs);
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 05/14] target/loongarch: Add LVZ guest interrupt state
2026-08-26 10:30 [PATCH v4 00/14] target/loongarch: add LVZ support for TCG SignKirigami
` (3 preceding siblings ...)
2026-08-26 10:30 ` [PATCH v4 04/14] target/loongarch: Add LVZ CPU model support SignKirigami
@ 2026-08-26 10:30 ` SignKirigami
2026-09-02 3:33 ` Bibo Mao
2026-08-26 10:30 ` [PATCH v4 06/14] target/loongarch: Add LVZ guest timer support SignKirigami
` (8 subsequent siblings)
13 siblings, 1 reply; 23+ messages in thread
From: SignKirigami @ 2026-08-26 10:30 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, xianglai li, SignKirigami, Hengyu Yu
Route configured hardware interrupts into the guest CSR bank, track guest
interrupt requests separately, and expose guest pending and enable checks
to the TCG runtime.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu.c | 43 +++++++++++++++++++++++++---------
target/loongarch/internals.h | 4 +++-
target/loongarch/tcg/tcg_cpu.c | 36 +++++++++++++++++++---------
3 files changed, 60 insertions(+), 23 deletions(-)
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index c5c7f3e0e3..b12e8944de 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -57,41 +57,57 @@ static vaddr loongarch_cpu_get_pc(CPUState *cs)
#ifndef CONFIG_USER_ONLY
#include "hw/loongarch/virt.h"
-void loongarch_cpu_set_irq(void *opaque, int irq, int level)
+void loongarch_cpu_set_irq_impl(void *opaque, int irq, int level,
+ bool guest)
{
LoongArchCPU *cpu = opaque;
CPULoongArchState *env = &cpu->env;
CPUState *cs = CPU(cpu);
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, guest);
+ int interrupt = guest ? CPU_INTERRUPT_GUEST : CPU_INTERRUPT_HARD;
if (irq < 0 || irq >= N_IRQS) {
return;
}
- if (kvm_enabled()) {
+ if (!guest && kvm_enabled()) {
kvm_loongarch_set_interrupt(cpu, irq, level);
} else if (tcg_enabled()) {
+ if (!guest && irq >= 2 && irq < 10 &&
+ (FIELD_EX64(sys->CSR_GINTC, CSR_GINTC, HWIP) &
+ BIT(irq - 2))) {
+ loongarch_cpu_set_irq_impl(opaque, irq, level, true);
+ return;
+ }
sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, irq, 1, level != 0);
- if (FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS)) {
- cpu_interrupt(cs, CPU_INTERRUPT_HARD);
- } else {
- cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+ if (!guest || env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
+ if (FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS)) {
+ cpu_interrupt(cs, interrupt);
+ } else {
+ cpu_reset_interrupt(cs, interrupt);
+ }
}
}
}
+void loongarch_cpu_set_irq(void *opaque, int irq, int level)
+{
+ loongarch_cpu_set_irq_impl(opaque, irq, level, false);
+}
+
/* Check if there is pending and not masked out interrupt */
-bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env)
+bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env, bool guest)
{
uint32_t pending;
uint32_t status;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, guest);
pending = FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS);
- status = FIELD_EX64(sys->CSR_ECFG, CSR_ECFG, LIE);
+ status = FIELD_EX64(sys->CSR_ECFG, CSR_ECFG, LIE);
return (pending & status) != 0;
}
+
#endif
#ifndef CONFIG_USER_ONLY
@@ -100,7 +116,12 @@ bool loongarch_cpu_has_work(CPUState *cs)
bool has_work = false;
if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) &&
- cpu_loongarch_hw_interrupts_pending(cpu_env(cs))) {
+ cpu_loongarch_hw_interrupts_pending(cpu_env(cs), false)) {
+ has_work = true;
+ }
+
+ if (cpu_test_interrupt(cs, CPU_INTERRUPT_GUEST) &&
+ cpu_loongarch_hw_interrupts_pending(cpu_env(cs), true)) {
has_work = true;
}
diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h
index e01dbed40f..a23b0d3d58 100644
--- a/target/loongarch/internals.h
+++ b/target/loongarch/internals.h
@@ -32,6 +32,8 @@ void restore_fp_status(CPULoongArchState *env);
extern const VMStateDescription vmstate_loongarch_cpu;
void loongarch_cpu_set_irq(void *opaque, int irq, int level);
+void loongarch_cpu_set_irq_impl(void *opaque, int irq, int level,
+ bool guest);
void loongarch_constant_timer_cb(void *opaque);
uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu);
@@ -39,7 +41,7 @@ uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu);
void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu,
uint64_t value);
bool loongarch_cpu_has_work(CPUState *cs);
-bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env);
+bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env, bool guest);
#endif /* !CONFIG_USER_ONLY */
uint64_t read_fcc(CPULoongArchState *env);
diff --git a/target/loongarch/tcg/tcg_cpu.c b/target/loongarch/tcg/tcg_cpu.c
index 4b1d44a164..e84ef5f305 100644
--- a/target/loongarch/tcg/tcg_cpu.c
+++ b/target/loongarch/tcg/tcg_cpu.c
@@ -238,29 +238,43 @@ static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
}
}
-static inline bool cpu_loongarch_hw_interrupts_enabled(CPULoongArchState *env)
+static inline bool cpu_loongarch_hw_interrupts_enabled(CPULoongArchState *env, bool guest)
{
bool ret = 0;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, guest);
+ CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
ret = (FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, IE) &&
- !(FIELD_EX64(sys->CSR_DBG, CSR_DBG, DST)));
+ !(FIELD_EX64(host->CSR_DBG, CSR_DBG, DST)));
return ret;
}
static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
+ CPULoongArchState *env = cpu_env(cs);
+ bool has_interrupt = false;
+
if (interrupt_request & CPU_INTERRUPT_HARD) {
- CPULoongArchState *env = cpu_env(cs);
-
- if (cpu_loongarch_hw_interrupts_enabled(env) &&
- cpu_loongarch_hw_interrupts_pending(env)) {
- /* Raise it */
- cs->exception_index = EXCCODE_INT;
- loongarch_cpu_do_interrupt(cs);
- return true;
+ if (cpu_loongarch_hw_interrupts_enabled(env, false) &&
+ cpu_loongarch_hw_interrupts_pending(env, false)) {
+ if (env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
+ trigger_vm_exit(env);
+ }
+ has_interrupt = true;
}
+ } else if (interrupt_request & CPU_INTERRUPT_GUEST) {
+ if (cpu_loongarch_hw_interrupts_enabled(env, true) &&
+ cpu_loongarch_hw_interrupts_pending(env, true) &&
+ env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
+ has_interrupt = true;
+ }
+ }
+ if (has_interrupt) {
+ /* Raise it */
+ cs->exception_index = EXCCODE_INT;
+ loongarch_cpu_do_interrupt(cs);
+ return true;
}
return false;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 06/14] target/loongarch: Add LVZ guest timer support
2026-08-26 10:30 [PATCH v4 00/14] target/loongarch: add LVZ support for TCG SignKirigami
` (4 preceding siblings ...)
2026-08-26 10:30 ` [PATCH v4 05/14] target/loongarch: Add LVZ guest interrupt state SignKirigami
@ 2026-08-26 10:30 ` SignKirigami
2026-08-26 10:30 ` [PATCH v4 07/14] target/loongarch: Add LVZ VM entry and exception handling SignKirigami
` (7 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: SignKirigami @ 2026-08-26 10:30 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, xianglai li, SignKirigami, Hengyu Yu
Add a separate guest constant timer, preserve its remaining ticks across VM
exits, and provide TVAL and TCFG helpers for accesses to the guest CSR bank.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu.c | 2 +
target/loongarch/internals.h | 6 ++-
target/loongarch/tcg/constant_timer.c | 67 ++++++++++++++++++++++-----
target/loongarch/tcg/csr_helper.c | 22 ++++++++-
target/loongarch/tcg/helper.h | 2 +
5 files changed, 84 insertions(+), 15 deletions(-)
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index b12e8944de..ff36294aa5 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -795,6 +795,8 @@ static void loongarch_cpu_init(Object *obj)
#ifdef CONFIG_TCG
timer_init_ns(&cpu->timer, QEMU_CLOCK_VIRTUAL,
&loongarch_constant_timer_cb, cpu);
+ timer_init_ns(&cpu->guest_timer, QEMU_CLOCK_VIRTUAL,
+ &loongarch_constant_timer_cb_guest, cpu);
#endif
#endif
}
diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h
index a23b0d3d58..8ae3507c71 100644
--- a/target/loongarch/internals.h
+++ b/target/loongarch/internals.h
@@ -36,10 +36,12 @@ void loongarch_cpu_set_irq_impl(void *opaque, int irq, int level,
bool guest);
void loongarch_constant_timer_cb(void *opaque);
+void loongarch_constant_timer_cb_guest(void *opaque);
uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu);
-uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu);
+uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu, bool guest);
+void cpu_loongarch_set_guest_timer(LoongArchCPU *cpu, bool on);
void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu,
- uint64_t value);
+ uint64_t value, bool guest);
bool loongarch_cpu_has_work(CPUState *cs);
bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env, bool guest);
#endif /* !CONFIG_USER_ONLY */
diff --git a/target/loongarch/tcg/constant_timer.c b/target/loongarch/tcg/constant_timer.c
index f56e76d482..a341f48489 100644
--- a/target/loongarch/tcg/constant_timer.c
+++ b/target/loongarch/tcg/constant_timer.c
@@ -20,47 +20,92 @@ uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu)
return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD;
}
-uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu)
+uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu, bool guest)
{
+ CPULoongArchState *env = &cpu->env;
uint64_t now, expire;
+ CPUSysState *sys = get_sys(env, guest);
+
+ if (guest && env_vm_level(env) != LOONGARCH_VM_LEVEL_GUEST) {
+ return sys->CSR_TVAL;
+ }
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- expire = timer_expire_time_ns(&cpu->timer);
+ expire = timer_expire_time_ns(guest ? &cpu->guest_timer : &cpu->timer);
return (expire - now) / TIMER_PERIOD;
}
void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu,
- uint64_t value)
+ uint64_t value, bool guest)
{
CPULoongArchState *env = &cpu->env;
- CPUSysState *sys = env_sys(env);
uint64_t now, next;
+ CPUSysState *sys = get_sys(env, guest);
+ QEMUTimer *timer = guest ? &cpu->guest_timer : &cpu->timer;
sys->CSR_TCFG = value;
+ if (guest && env_vm_level(env) != LOONGARCH_VM_LEVEL_GUEST) {
+ return;
+ }
+
if (value & CONSTANT_TIMER_ENABLE) {
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
next = now + (value & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD;
- timer_mod(&cpu->timer, next);
+ timer_mod(timer, next);
} else {
- timer_del(&cpu->timer);
+ timer_del(timer);
}
}
-void loongarch_constant_timer_cb(void *opaque)
+void cpu_loongarch_set_guest_timer(LoongArchCPU *cpu, bool on)
{
- LoongArchCPU *cpu = opaque;
CPULoongArchState *env = &cpu->env;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *guest = get_sys(env, LOONGARCH_VM_LEVEL_GUEST);
+ uint64_t now, next, ticks;
+
+ if (!(guest->CSR_TCFG & CONSTANT_TIMER_ENABLE)) {
+ return;
+ }
+
+ if (on) {
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ ticks = guest->CSR_TVAL ? guest->CSR_TVAL :
+ (guest->CSR_TCFG & CONSTANT_TIMER_TICK_MASK);
+ guest->CSR_TVAL = 0;
+ next = now + ticks * TIMER_PERIOD;
+ timer_mod(&cpu->guest_timer, next);
+ } else {
+ guest->CSR_TVAL = cpu_loongarch_get_constant_timer_ticks(cpu, true);
+ timer_del(&cpu->guest_timer);
+ }
+}
+
+static void loongarch_constant_timer_cb_impl(void *opaque, bool guest)
+{
+ LoongArchCPU *cpu = opaque;
+ CPULoongArchState *env = &cpu->env;
+ CPUSysState *sys = get_sys(env, guest);
+ QEMUTimer *timer = guest ? &cpu->guest_timer : &cpu->timer;
uint64_t now, next;
if (FIELD_EX64(sys->CSR_TCFG, CSR_TCFG, PERIODIC)) {
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
next = now + (sys->CSR_TCFG & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD;
- timer_mod(&cpu->timer, next);
+ timer_mod(timer, next);
} else {
sys->CSR_TCFG = FIELD_DP64(sys->CSR_TCFG, CSR_TCFG, EN, 0);
}
- loongarch_cpu_set_irq(opaque, IRQ_TIMER, 1);
+ loongarch_cpu_set_irq_impl(opaque, IRQ_TIMER, 1, guest);
+}
+
+void loongarch_constant_timer_cb(void *opaque)
+{
+ loongarch_constant_timer_cb_impl(opaque, false);
+}
+
+void loongarch_constant_timer_cb_guest(void *opaque)
+{
+ loongarch_constant_timer_cb_impl(opaque, true);
}
diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c
index 7dc33bc180..fa75508244 100644
--- a/target/loongarch/tcg/csr_helper.c
+++ b/target/loongarch/tcg/csr_helper.c
@@ -74,7 +74,14 @@ target_ulong helper_csrrd_tval(CPULoongArchState *env)
{
LoongArchCPU *cpu = env_archcpu(env);
- return cpu_loongarch_get_constant_timer_ticks(cpu);
+ return cpu_loongarch_get_constant_timer_ticks(cpu, false);
+}
+
+target_ulong helper_gcsrrd_tval(CPULoongArchState *env)
+{
+ LoongArchCPU *cpu = env_archcpu(env);
+
+ return cpu_loongarch_get_constant_timer_ticks(cpu, true);
}
target_ulong helper_csrrd_msgir(CPULoongArchState *env)
@@ -129,7 +136,18 @@ target_ulong helper_csrwr_tcfg(CPULoongArchState *env, target_ulong val)
CPUSysState *sys = env_sys(env);
int64_t old_v = sys->CSR_TCFG;
- cpu_loongarch_store_constant_timer_config(cpu, val);
+ cpu_loongarch_store_constant_timer_config(cpu, val, false);
+
+ return old_v;
+}
+
+target_ulong helper_gcsrwr_tcfg(CPULoongArchState *env, target_ulong val)
+{
+ LoongArchCPU *cpu = env_archcpu(env);
+ CPUSysState *guest = get_sys(env, LOONGARCH_VM_LEVEL_GUEST);
+ int64_t old_v = guest->CSR_TCFG;
+
+ cpu_loongarch_store_constant_timer_config(cpu, val, true);
return old_v;
}
diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h
index 8a6c62f116..a003266827 100644
--- a/target/loongarch/tcg/helper.h
+++ b/target/loongarch/tcg/helper.h
@@ -100,11 +100,13 @@ DEF_HELPER_1(rdtime_d, i64, env)
DEF_HELPER_1(csrrd_pgd, i64, env)
DEF_HELPER_1(csrrd_cpuid, i64, env)
DEF_HELPER_1(csrrd_tval, i64, env)
+DEF_HELPER_1(gcsrrd_tval, i64, env)
DEF_HELPER_1(csrrd_msgir, i64, env)
DEF_HELPER_2(csrwr_stlbps, i64, env, tl)
DEF_HELPER_2(csrwr_estat, i64, env, tl)
DEF_HELPER_2(csrwr_asid, i64, env, tl)
DEF_HELPER_2(csrwr_tcfg, i64, env, tl)
+DEF_HELPER_2(gcsrwr_tcfg, i64, env, tl)
DEF_HELPER_2(csrwr_ticlr, i64, env, tl)
DEF_HELPER_2(csrwr_pwcl, i64, env, tl)
DEF_HELPER_2(csrwr_pwch, i64, env, tl)
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 07/14] target/loongarch: Add LVZ VM entry and exception handling
2026-08-26 10:30 [PATCH v4 00/14] target/loongarch: add LVZ support for TCG SignKirigami
` (5 preceding siblings ...)
2026-08-26 10:30 ` [PATCH v4 06/14] target/loongarch: Add LVZ guest timer support SignKirigami
@ 2026-08-26 10:30 ` SignKirigami
2026-09-02 9:19 ` Bibo Mao
2026-08-26 10:30 ` [PATCH v4 08/14] target/loongarch: Add LVZ guest CSR helpers SignKirigami
` (6 subsequent siblings)
13 siblings, 1 reply; 23+ messages in thread
From: SignKirigami @ 2026-08-26 10:30 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, xianglai li, SignKirigami, Hengyu Yu
Enter guest mode when ERTN restores GSTAT.PVM, and return to the host CSR
bank for guest-sensitive, hypercall and host interrupt exits. Select the
exception CSR bank before updating state and expose guest mode in TCG
flags and MMU indices.
Bound-check exceptions update the currently active CSR bank. HVC is
available only to guest PLV0.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu.c | 50 ++++++++++++++++++++++++++++++++
target/loongarch/tcg/helper.h | 2 +-
target/loongarch/tcg/op_helper.c | 22 ++++++++++++++
target/loongarch/tcg/tcg_cpu.c | 46 ++++++++++++++++++++++-------
4 files changed, 109 insertions(+), 11 deletions(-)
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index ff36294aa5..4b0acc7135 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -129,6 +129,56 @@ bool loongarch_cpu_has_work(CPUState *cs)
}
#endif /* !CONFIG_USER_ONLY */
+uint8_t get_tgid(CPULoongArchState *env)
+{
+ CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
+
+ if (env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
+ return get_gid(env);
+ }
+
+ if (FIELD_EX64(host->CSR_GTLBC, CSR_GTLBC, USETGID)) {
+ return FIELD_EX64(host->CSR_GTLBC, CSR_GTLBC, TGID);
+ } else if (will_return_to_guest(env)) {
+ return get_gid(env);
+ }
+ return 0;
+}
+
+bool will_return_to_guest(CPULoongArchState *env)
+{
+ if (!has_lvz_capability(env) ||
+ env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
+ return false;
+ }
+ return FIELD_EX64(get_sys(env, LOONGARCH_VM_LEVEL_HOST)->CSR_GSTAT,
+ CSR_GSTAT, PVM);
+}
+
+bool has_lvz_capability(CPULoongArchState *env)
+{
+ return FIELD_EX32(env->cpucfg[2], CPUCFG2, LVZ);
+}
+
+uint8_t get_gid(CPULoongArchState *env)
+{
+ return FIELD_EX64(get_sys(env, LOONGARCH_VM_LEVEL_HOST)->CSR_GSTAT,
+ CSR_GSTAT, GID);
+}
+
+void trigger_vm_exit(CPULoongArchState *env)
+{
+ CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
+
+ if (env_vm_level(env) != LOONGARCH_VM_LEVEL_GUEST) {
+ return;
+ }
+
+ cpu_loongarch_set_guest_timer(env_archcpu(env), false);
+ host->CSR_GSTAT = FIELD_DP64(host->CSR_GSTAT, CSR_GSTAT, PVM, 1);
+ env->vm_exit = true;
+}
+
static void loongarch_la464_init_csr(DeviceState *dev)
{
#ifndef CONFIG_USER_ONLY
diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h
index a003266827..b87de15a99 100644
--- a/target/loongarch/tcg/helper.h
+++ b/target/loongarch/tcg/helper.h
@@ -14,7 +14,7 @@ DEF_HELPER_FLAGS_3(asrtgt_d, TCG_CALL_NO_WG, void, env, tl, tl)
DEF_HELPER_FLAGS_3(crc32, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl)
DEF_HELPER_FLAGS_3(crc32c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl)
-DEF_HELPER_FLAGS_2(cpucfg, TCG_CALL_NO_RWG_SE, tl, env, tl)
+DEF_HELPER_FLAGS_2(cpucfg, TCG_CALL_NO_WG_SE, tl, env, tl)
/* Floating-point helper */
DEF_HELPER_FLAGS_3(fadd_s, TCG_CALL_NO_WG, i64, env, i64, i64)
diff --git a/target/loongarch/tcg/op_helper.c b/target/loongarch/tcg/op_helper.c
index e63ac66daa..11d204c61c 100644
--- a/target/loongarch/tcg/op_helper.c
+++ b/target/loongarch/tcg/op_helper.c
@@ -12,6 +12,7 @@
#include "exec/helper-proto.h"
#include "accel/tcg/cpu-ldst.h"
#include "internals.h"
+#include "qemu/main-loop.h"
#include "qemu/crc32c.h"
#include <zlib.h> /* for crc32 */
#include "cpu-csr.h"
@@ -19,6 +20,9 @@
/* Exceptions helpers */
void helper_raise_exception(CPULoongArchState *env, uint32_t exception)
{
+ if (exception == EXCCODE_GSPR || exception == EXCCODE_HVC) {
+ trigger_vm_exit(env);
+ }
do_raise_exception(env, exception, GETPC());
}
@@ -85,6 +89,10 @@ target_ulong helper_crc32c(target_ulong val, target_ulong m, uint64_t sz)
target_ulong helper_cpucfg(CPULoongArchState *env, target_ulong rj)
{
+ if (env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
+ trigger_vm_exit(env);
+ do_raise_exception(env, EXCCODE_GSPR, GETPC());
+ }
return rj >= ARRAY_SIZE(env->cpucfg) ? 0 : env->cpucfg[rj];
}
@@ -111,6 +119,8 @@ void helper_ertn(CPULoongArchState *env)
{
uint64_t csr_pplv, csr_pie;
CPUSysState *sys = env_sys(env);
+ CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
+ CPUSysState *guest = get_sys(env, LOONGARCH_VM_LEVEL_GUEST);
if (FIELD_EX64(sys->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
csr_pplv = FIELD_EX64(sys->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV);
@@ -134,6 +144,18 @@ void helper_ertn(CPULoongArchState *env)
sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, IE, csr_pie);
env->lladdr = 1;
+ if (will_return_to_guest(env)) {
+ host->CSR_GSTAT = FIELD_DP64(host->CSR_GSTAT, CSR_GSTAT, VM, 1);
+ set_sys(env, LOONGARCH_VM_LEVEL_GUEST);
+ cpu_loongarch_set_guest_timer(env_archcpu(env), true);
+ bql_lock();
+ if (FIELD_EX64(guest->CSR_ESTAT, CSR_ESTAT, IS)) {
+ cpu_interrupt(env_cpu(env), CPU_INTERRUPT_GUEST);
+ } else {
+ cpu_reset_interrupt(env_cpu(env), CPU_INTERRUPT_GUEST);
+ }
+ bql_unlock();
+ }
}
void helper_idle(CPULoongArchState *env)
diff --git a/target/loongarch/tcg/tcg_cpu.c b/target/loongarch/tcg/tcg_cpu.c
index e84ef5f305..6af885cda1 100644
--- a/target/loongarch/tcg/tcg_cpu.c
+++ b/target/loongarch/tcg/tcg_cpu.c
@@ -44,6 +44,10 @@ static const struct TypeExcp excp_names[] = {
{EXCCODE_BCE, "Bound Check Exception"},
{EXCCODE_SXD, "128 bit vector instructions Disable exception"},
{EXCCODE_ASXD, "256 bit vector instructions Disable exception"},
+ {EXCCODE_GSPR, "Guest Sensitive and Privileged Resources"},
+ {EXCCODE_HVC, "Hypervisor call"},
+ {EXCCODE_GCSC, "Guest CSR visited by Software"},
+ {EXCCODE_GCHC, "Guest CSR visited by Hardware"},
{EXCP_HLT, "EXCP_HLT"},
};
@@ -78,9 +82,11 @@ void G_NORETURN do_raise_exception(CPULoongArchState *env,
static void loongarch_cpu_do_interrupt(CPUState *cs)
{
CPULoongArchState *env = cpu_env(cs);
- CPUSysState *sys = env_sys(env);
bool update_badinstr = 1;
int cause = -1;
+ bool real_guest = !env->vm_exit && env_vm_level(env);
+ CPUSysState *sys = get_sys(env, real_guest);
+ CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
bool tlbfill = FIELD_EX64(sys->CSR_TLBRERA, CSR_TLBRERA, ISTLBR);
uint32_t vec_size = FIELD_EX64(sys->CSR_ECFG, CSR_ECFG, VS);
uint64_t last_pc = env->pc;
@@ -96,17 +102,17 @@ static void loongarch_cpu_do_interrupt(CPUState *cs)
switch (cs->exception_index) {
case EXCCODE_DBP:
- sys->CSR_DBG = FIELD_DP64(sys->CSR_DBG, CSR_DBG, DCL, 1);
- sys->CSR_DBG = FIELD_DP64(sys->CSR_DBG, CSR_DBG, ECODE, 0xC);
+ host->CSR_DBG = FIELD_DP64(host->CSR_DBG, CSR_DBG, DCL, 1);
+ host->CSR_DBG = FIELD_DP64(host->CSR_DBG, CSR_DBG, ECODE, 0xC);
goto set_DERA;
set_DERA:
- sys->CSR_DERA = env->pc;
- sys->CSR_DBG = FIELD_DP64(sys->CSR_DBG, CSR_DBG, DST, 1);
- set_pc(env, sys->CSR_EENTRY + 0x480);
+ host->CSR_DERA = env->pc;
+ host->CSR_DBG = FIELD_DP64(host->CSR_DBG, CSR_DBG, DST, 1);
+ set_pc(env, host->CSR_EENTRY + 0x480);
break;
case EXCCODE_INT:
- if (FIELD_EX64(sys->CSR_DBG, CSR_DBG, DST)) {
- sys->CSR_DBG = FIELD_DP64(sys->CSR_DBG, CSR_DBG, DEI, 1);
+ if (FIELD_EX64(host->CSR_DBG, CSR_DBG, DST)) {
+ host->CSR_DBG = FIELD_DP64(host->CSR_DBG, CSR_DBG, DEI, 1);
goto set_DERA;
}
QEMU_FALLTHROUGH;
@@ -117,6 +123,10 @@ static void loongarch_cpu_do_interrupt(CPUState *cs)
update_badinstr = 0;
break;
case EXCCODE_BCE:
+ case EXCCODE_GSPR:
+ case EXCCODE_GCHC:
+ case EXCCODE_GCSC:
+ case EXCCODE_HVC:
sys->CSR_BADV = env->pc;
QEMU_FALLTHROUGH;
case EXCCODE_SYS:
@@ -218,6 +228,12 @@ static void loongarch_cpu_do_interrupt(CPUState *cs)
qemu_plugin_vcpu_exception_cb(cs, last_pc);
}
cs->exception_index = -1;
+ if (env->vm_exit) {
+ host->CSR_GSTAT = FIELD_DP64(host->CSR_GSTAT, CSR_GSTAT, VM, 0);
+ set_sys(env, LOONGARCH_VM_LEVEL_HOST);
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_GUEST);
+ }
+ env->vm_exit = false;
}
static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
@@ -228,9 +244,9 @@ static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
uintptr_t retaddr)
{
CPULoongArchState *env = cpu_env(cs);
- CPUSysState *sys = env_sys(env);
+ CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
- sys->CSR_BADV = addr;
+ host->CSR_BADV = addr;
if (access_type == MMU_INST_FETCH) {
do_raise_exception(env, EXCCODE_ADEF, retaddr);
} else {
@@ -293,6 +309,9 @@ static TCGTBCPUState loongarch_get_tb_cpu_state(CPUState *cs)
uint32_t flags;
flags = sys->CSR_CRMD & (R_CSR_CRMD_PLV_MASK | R_CSR_CRMD_PG_MASK);
+ if (env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
+ flags |= HW_FLAGS_GUEST_MODE;
+ }
flags |= FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, FPE) * HW_FLAGS_EUEN_FPE;
flags |= FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, SXE) * HW_FLAGS_EUEN_SXE;
flags |= FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, ASXE) * HW_FLAGS_EUEN_ASXE;
@@ -320,6 +339,13 @@ static int loongarch_cpu_mmu_index(CPUState *cs, bool ifetch)
CPULoongArchState *env = cpu_env(cs);
CPUSysState *sys = env_sys(env);
+ if (env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
+ if (FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, PG)) {
+ return MMU_GUEST_IDX + FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, PLV);
+ }
+ return MMU_GUEST_DA_IDX;
+ }
+
if (FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, PG)) {
return FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, PLV);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 08/14] target/loongarch: Add LVZ guest CSR helpers
2026-08-26 10:30 [PATCH v4 00/14] target/loongarch: add LVZ support for TCG SignKirigami
` (6 preceding siblings ...)
2026-08-26 10:30 ` [PATCH v4 07/14] target/loongarch: Add LVZ VM entry and exception handling SignKirigami
@ 2026-08-26 10:30 ` SignKirigami
2026-09-02 9:55 ` Bibo Mao
2026-08-26 10:30 ` [PATCH v4 09/14] target/loongarch: Add LVZ migration state SignKirigami
` (5 subsequent siblings)
13 siblings, 1 reply; 23+ messages in thread
From: SignKirigami @ 2026-08-26 10:30 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, xianglai li, SignKirigami, Hengyu Yu
Add helper behavior for guest PGD, ESTAT, ASID and timer interrupt clearing,
and for the host LVZ control CSRs.
Synchronize injected and currently pending passthrough interrupts into
the guest ESTAT value whenever GINTC changes.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/tcg/csr_helper.c | 226 +++++++++++++++++++++++++-----
target/loongarch/tcg/helper.h | 10 ++
2 files changed, 203 insertions(+), 33 deletions(-)
diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c
index fa75508244..ba30060ba4 100644
--- a/target/loongarch/tcg/csr_helper.c
+++ b/target/loongarch/tcg/csr_helper.c
@@ -18,9 +18,10 @@
#include "cpu-csr.h"
#include "cpu-mmu.h"
-target_ulong helper_csrwr_stlbps(CPULoongArchState *env, target_ulong val)
+static target_ulong helper_csrwr_stlbps_impl(CPULoongArchState *env,
+ target_ulong val, bool guest)
{
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, guest);
int64_t old_v = sys->CSR_STLBPS;
/*
@@ -40,10 +41,21 @@ target_ulong helper_csrwr_stlbps(CPULoongArchState *env, target_ulong val)
return old_v;
}
-target_ulong helper_csrrd_pgd(CPULoongArchState *env)
+target_ulong helper_csrwr_stlbps(CPULoongArchState *env, target_ulong val)
+{
+ return helper_csrwr_stlbps_impl(env, val, false);
+}
+
+target_ulong helper_gcsrwr_stlbps(CPULoongArchState *env, target_ulong val)
+{
+ return helper_csrwr_stlbps_impl(env, val, true);
+}
+
+static target_ulong helper_csrrd_pgd_impl(CPULoongArchState *env,
+ bool guest)
{
int64_t v;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, guest);
if (sys->CSR_TLBRERA & 0x1) {
v = sys->CSR_TLBRBADV;
@@ -60,6 +72,16 @@ target_ulong helper_csrrd_pgd(CPULoongArchState *env)
return v;
}
+target_ulong helper_csrrd_pgd(CPULoongArchState *env)
+{
+ return helper_csrrd_pgd_impl(env, false);
+}
+
+target_ulong helper_gcsrrd_pgd(CPULoongArchState *env)
+{
+ return helper_csrrd_pgd_impl(env, true);
+}
+
target_ulong helper_csrrd_cpuid(CPULoongArchState *env)
{
LoongArchCPU *lac = env_archcpu(env);
@@ -70,18 +92,22 @@ target_ulong helper_csrrd_cpuid(CPULoongArchState *env)
return sys->CSR_CPUID;
}
-target_ulong helper_csrrd_tval(CPULoongArchState *env)
+static target_ulong helper_csrrd_tval_impl(CPULoongArchState *env,
+ bool guest)
{
LoongArchCPU *cpu = env_archcpu(env);
- return cpu_loongarch_get_constant_timer_ticks(cpu, false);
+ return cpu_loongarch_get_constant_timer_ticks(cpu, guest);
}
-target_ulong helper_gcsrrd_tval(CPULoongArchState *env)
+target_ulong helper_csrrd_tval(CPULoongArchState *env)
{
- LoongArchCPU *cpu = env_archcpu(env);
+ return helper_csrrd_tval_impl(env, false);
+}
- return cpu_loongarch_get_constant_timer_ticks(cpu, true);
+target_ulong helper_gcsrrd_tval(CPULoongArchState *env)
+{
+ return helper_csrrd_tval_impl(env, true);
}
target_ulong helper_csrrd_msgir(CPULoongArchState *env)
@@ -106,20 +132,45 @@ target_ulong helper_csrrd_msgir(CPULoongArchState *env)
return irq;
}
-target_ulong helper_csrwr_estat(CPULoongArchState *env, target_ulong val)
+static target_ulong helper_csrwr_estat_impl(CPULoongArchState *env,
+ target_ulong val, bool guest)
{
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, guest);
+ CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
int64_t old_v = sys->CSR_ESTAT;
/* Only IS[1:0] can be written */
sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, 0, 2, val);
+ if (guest && env_vm_level(env) != LOONGARCH_VM_LEVEL_GUEST) {
+ sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, 2, 11,
+ extract64(val, 2, 11));
+ if (extract64(val, 2, 8) &
+ FIELD_EX64(host->CSR_GINTC, CSR_GINTC, HWIC)) {
+ host->CSR_ESTAT = deposit64(host->CSR_ESTAT, 2, 8,
+ extract64(host->CSR_ESTAT, 2, 8) &
+ ~extract64(val, 2, 8));
+ }
+ sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, 16, 15,
+ extract64(val, 16, 15));
+ }
return old_v;
}
-target_ulong helper_csrwr_asid(CPULoongArchState *env, target_ulong val)
+target_ulong helper_csrwr_estat(CPULoongArchState *env, target_ulong val)
{
- CPUSysState *sys = env_sys(env);
+ return helper_csrwr_estat_impl(env, val, false);
+}
+
+target_ulong helper_gcsrwr_estat(CPULoongArchState *env, target_ulong val)
+{
+ return helper_csrwr_estat_impl(env, val, true);
+}
+
+static target_ulong helper_csrwr_asid_impl(CPULoongArchState *env,
+ target_ulong val, bool guest)
+{
+ CPUSysState *sys = get_sys(env, guest);
int64_t old_v = sys->CSR_ASID;
/* Only ASID filed of CSR_ASID can be written */
@@ -130,45 +181,132 @@ target_ulong helper_csrwr_asid(CPULoongArchState *env, target_ulong val)
return old_v;
}
-target_ulong helper_csrwr_tcfg(CPULoongArchState *env, target_ulong val)
+target_ulong helper_csrwr_asid(CPULoongArchState *env, target_ulong val)
+{
+ return helper_csrwr_asid_impl(env, val, false);
+}
+
+target_ulong helper_gcsrwr_asid(CPULoongArchState *env, target_ulong val)
+{
+ return helper_csrwr_asid_impl(env, val, true);
+}
+
+static target_ulong helper_csrwr_tcfg_impl(CPULoongArchState *env,
+ target_ulong val, bool guest)
{
LoongArchCPU *cpu = env_archcpu(env);
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, guest);
int64_t old_v = sys->CSR_TCFG;
- cpu_loongarch_store_constant_timer_config(cpu, val, false);
+ cpu_loongarch_store_constant_timer_config(cpu, val, guest);
return old_v;
}
-target_ulong helper_gcsrwr_tcfg(CPULoongArchState *env, target_ulong val)
+target_ulong helper_csrwr_tcfg(CPULoongArchState *env, target_ulong val)
{
- LoongArchCPU *cpu = env_archcpu(env);
- CPUSysState *guest = get_sys(env, LOONGARCH_VM_LEVEL_GUEST);
- int64_t old_v = guest->CSR_TCFG;
-
- cpu_loongarch_store_constant_timer_config(cpu, val, true);
+ return helper_csrwr_tcfg_impl(env, val, false);
+}
- return old_v;
+target_ulong helper_gcsrwr_tcfg(CPULoongArchState *env, target_ulong val)
+{
+ return helper_csrwr_tcfg_impl(env, val, true);
}
-target_ulong helper_csrwr_ticlr(CPULoongArchState *env, target_ulong val)
+static target_ulong helper_csrwr_ticlr_impl(CPULoongArchState *env,
+ target_ulong val, bool guest)
{
LoongArchCPU *cpu = env_archcpu(env);
int64_t old_v = 0;
if (val & 0x1) {
bql_lock();
- loongarch_cpu_set_irq(cpu, IRQ_TIMER, 0);
+ loongarch_cpu_set_irq_impl(cpu, IRQ_TIMER, 0, guest);
bql_unlock();
}
return old_v;
}
-target_ulong helper_csrwr_pwcl(CPULoongArchState *env, target_ulong val)
+target_ulong helper_csrwr_ticlr(CPULoongArchState *env, target_ulong val)
+{
+ return helper_csrwr_ticlr_impl(env, val, false);
+}
+
+target_ulong helper_gcsrwr_ticlr(CPULoongArchState *env, target_ulong val)
+{
+ return helper_csrwr_ticlr_impl(env, val, true);
+}
+
+target_ulong helper_csrwr_gstat(CPULoongArchState *env, target_ulong val)
+{
+ CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
+ int64_t old_v = host->CSR_GSTAT;
+ uint8_t old_gid = FIELD_EX64(host->CSR_GSTAT, CSR_GSTAT, GID);
+
+ host->CSR_GSTAT = FIELD_DP64(host->CSR_GSTAT, CSR_GSTAT, PVM,
+ FIELD_EX64(val, CSR_GSTAT, PVM));
+ host->CSR_GSTAT = FIELD_DP64(host->CSR_GSTAT, CSR_GSTAT, GID,
+ FIELD_EX64(val, CSR_GSTAT, GID));
+
+ if (old_gid != FIELD_EX64(host->CSR_GSTAT, CSR_GSTAT, GID)) {
+ tlb_flush(env_cpu(env));
+ }
+
+ return old_v;
+}
+
+target_ulong helper_csrwr_gtlbc(CPULoongArchState *env, target_ulong val)
+{
+ CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
+ int64_t old_v = host->CSR_GTLBC;
+ uint8_t old_use_tgid = FIELD_EX64(old_v, CSR_GTLBC, USETGID);
+ uint8_t old_tgid = FIELD_EX64(old_v, CSR_GTLBC, TGID);
+
+ host->CSR_GTLBC = val;
+ if (old_use_tgid != FIELD_EX64(host->CSR_GTLBC, CSR_GTLBC, USETGID) ||
+ old_tgid != FIELD_EX64(host->CSR_GTLBC, CSR_GTLBC, TGID)) {
+ tlb_flush(env_cpu(env));
+ }
+
+ return old_v;
+}
+
+target_ulong helper_csrwr_gintc(CPULoongArchState *env, target_ulong val)
+{
+ CPUState *cs = env_cpu(env);
+ CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
+ CPUSysState *guest = get_sys(env, LOONGARCH_VM_LEVEL_GUEST);
+ int64_t old_v = host->CSR_GINTC;
+ uint8_t hwis = FIELD_EX64(val, CSR_GINTC, HWIS);
+ uint8_t hwip = FIELD_EX64(val, CSR_GINTC, HWIP);
+ uint8_t pending = extract64(host->CSR_ESTAT, 2, 8);
+
+ host->CSR_GINTC = val & 0xffff00;
+ guest->CSR_ESTAT = deposit64(guest->CSR_ESTAT, 2, 8,
+ hwis | (pending & hwip));
+
+ bql_lock();
+ if (FIELD_EX64(host->CSR_ESTAT, CSR_ESTAT, IS) & ~(hwip << 2)) {
+ cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+ } else {
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+ }
+ if (env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST &&
+ FIELD_EX64(guest->CSR_ESTAT, CSR_ESTAT, IS)) {
+ cpu_interrupt(cs, CPU_INTERRUPT_GUEST);
+ } else {
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_GUEST);
+ }
+ bql_unlock();
+
+ return old_v;
+}
+
+static target_ulong helper_csrwr_pwcl_impl(CPULoongArchState *env,
+ target_ulong val, bool guest)
{
uint8_t shift, ptbase;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, guest);
int64_t old_v = sys->CSR_PWCL;
/*
@@ -190,18 +328,40 @@ target_ulong helper_csrwr_pwcl(CPULoongArchState *env, target_ulong val)
return old_v;
}
-target_ulong helper_csrwr_pwch(CPULoongArchState *env, target_ulong val)
+target_ulong helper_csrwr_pwcl(CPULoongArchState *env, target_ulong val)
{
- uint8_t has_ptw;
- CPUSysState *sys = env_sys(env);
+ return helper_csrwr_pwcl_impl(env, val, false);
+}
+
+target_ulong helper_gcsrwr_pwcl(CPULoongArchState *env, target_ulong val)
+{
+ return helper_csrwr_pwcl_impl(env, val, true);
+}
+
+static target_ulong helper_csrwr_pwch_impl(CPULoongArchState *env,
+ target_ulong val, bool guest)
+{
+ uint8_t host_has_ptw;
+ CPUSysState *sys = get_sys(env, guest);
int64_t old_v = sys->CSR_PWCH;
val = FIELD_DP64(val, CSR_PWCH, RESERVE, 0);
- has_ptw = FIELD_EX32(env->cpucfg[2], CPUCFG2, HPTW);
- if (!has_ptw) {
+ //TODO: host can't read guest cpucfg
+ host_has_ptw = FIELD_EX32(env->cpucfg[2], CPUCFG2, HPTW);
+ if (!host_has_ptw) {
val = FIELD_DP64(val, CSR_PWCH, HPTW_EN, 0);
}
sys->CSR_PWCH = val;
return old_v;
- }
+}
+
+target_ulong helper_csrwr_pwch(CPULoongArchState *env, target_ulong val)
+{
+ return helper_csrwr_pwch_impl(env, val, false);
+}
+
+target_ulong helper_gcsrwr_pwch(CPULoongArchState *env, target_ulong val)
+{
+ return helper_csrwr_pwch_impl(env, val, true);
+}
diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h
index b87de15a99..8e0ddd8693 100644
--- a/target/loongarch/tcg/helper.h
+++ b/target/loongarch/tcg/helper.h
@@ -98,18 +98,28 @@ DEF_HELPER_1(rdtime_d, i64, env)
#ifndef CONFIG_USER_ONLY
/* CSRs helper */
DEF_HELPER_1(csrrd_pgd, i64, env)
+DEF_HELPER_1(gcsrrd_pgd, i64, env)
DEF_HELPER_1(csrrd_cpuid, i64, env)
DEF_HELPER_1(csrrd_tval, i64, env)
DEF_HELPER_1(gcsrrd_tval, i64, env)
DEF_HELPER_1(csrrd_msgir, i64, env)
DEF_HELPER_2(csrwr_stlbps, i64, env, tl)
+DEF_HELPER_2(gcsrwr_stlbps, i64, env, tl)
DEF_HELPER_2(csrwr_estat, i64, env, tl)
+DEF_HELPER_2(gcsrwr_estat, i64, env, tl)
DEF_HELPER_2(csrwr_asid, i64, env, tl)
+DEF_HELPER_2(gcsrwr_asid, i64, env, tl)
DEF_HELPER_2(csrwr_tcfg, i64, env, tl)
DEF_HELPER_2(gcsrwr_tcfg, i64, env, tl)
DEF_HELPER_2(csrwr_ticlr, i64, env, tl)
+DEF_HELPER_2(gcsrwr_ticlr, i64, env, tl)
+DEF_HELPER_2(csrwr_gstat, i64, env, tl)
+DEF_HELPER_2(csrwr_gtlbc, i64, env, tl)
+DEF_HELPER_2(csrwr_gintc, i64, env, tl)
DEF_HELPER_2(csrwr_pwcl, i64, env, tl)
+DEF_HELPER_2(gcsrwr_pwcl, i64, env, tl)
DEF_HELPER_2(csrwr_pwch, i64, env, tl)
+DEF_HELPER_2(gcsrwr_pwch, i64, env, tl)
DEF_HELPER_2(iocsrrd_b, i64, env, tl)
DEF_HELPER_2(iocsrrd_h, i64, env, tl)
DEF_HELPER_2(iocsrrd_w, i64, env, tl)
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 09/14] target/loongarch: Add LVZ migration state
2026-08-26 10:30 [PATCH v4 00/14] target/loongarch: add LVZ support for TCG SignKirigami
` (7 preceding siblings ...)
2026-08-26 10:30 ` [PATCH v4 08/14] target/loongarch: Add LVZ guest CSR helpers SignKirigami
@ 2026-08-26 10:30 ` SignKirigami
2026-08-26 10:30 ` [PATCH v4 10/14] target/loongarch: Add LVZ MMU and PTW support SignKirigami
` (4 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: SignKirigami @ 2026-08-26 10:30 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, xianglai li, SignKirigami, Hengyu Yu
Migrate the host LVZ controls, guest CSR and TLB banks, and restore the
active VM level, guest timer and interrupt request after loading state.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/machine.c | 111 ++++++++++++++++++++++++++++++++++++-
1 file changed, 110 insertions(+), 1 deletion(-)
diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c
index 931a5ca5ba..2bf2475748 100644
--- a/target/loongarch/machine.c
+++ b/target/loongarch/machine.c
@@ -7,6 +7,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
+#include "internals.h"
#include "migration/vmstate.h"
#include "system/tcg.h"
#include "vec.h"
@@ -200,18 +201,125 @@ static const VMStateDescription vmstate_tlb = {
.minimum_version_id = 0,
.needed = tlb_needed,
.fields = (const VMStateField[]) {
- VMSTATE_STRUCT_ARRAY(env.tlb, LoongArchCPU, LOONGARCH_TLB_MAX,
+ VMSTATE_STRUCT_ARRAY(env.sys_states[0].tlb, LoongArchCPU,
+ LOONGARCH_TLB_MAX,
0, vmstate_tlb_entry, LoongArchTLB),
VMSTATE_END_OF_LIST()
}
};
+
+static const VMStateDescription vmstate_lvz_sys = {
+ .name = "cpu/lvz-sys",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT64(CSR_CRMD, CPUSysState),
+ VMSTATE_UINT64(CSR_PRMD, CPUSysState),
+ VMSTATE_UINT64(CSR_EUEN, CPUSysState),
+ VMSTATE_UINT64(CSR_MISC, CPUSysState),
+ VMSTATE_UINT64(CSR_ECFG, CPUSysState),
+ VMSTATE_UINT64(CSR_ESTAT, CPUSysState),
+ VMSTATE_UINT64(CSR_ERA, CPUSysState),
+ VMSTATE_UINT64(CSR_BADV, CPUSysState),
+ VMSTATE_UINT64(CSR_BADI, CPUSysState),
+ VMSTATE_UINT64(CSR_EENTRY, CPUSysState),
+ VMSTATE_UINT64(CSR_TLBIDX, CPUSysState),
+ VMSTATE_UINT64(CSR_TLBEHI, CPUSysState),
+ VMSTATE_UINT64(CSR_TLBELO0, CPUSysState),
+ VMSTATE_UINT64(CSR_TLBELO1, CPUSysState),
+ VMSTATE_UINT64(CSR_ASID, CPUSysState),
+ VMSTATE_UINT64(CSR_PGDL, CPUSysState),
+ VMSTATE_UINT64(CSR_PGDH, CPUSysState),
+ VMSTATE_UINT64(CSR_PGD, CPUSysState),
+ VMSTATE_UINT64(CSR_PWCL, CPUSysState),
+ VMSTATE_UINT64(CSR_PWCH, CPUSysState),
+ VMSTATE_UINT64(CSR_STLBPS, CPUSysState),
+ VMSTATE_UINT64(CSR_RVACFG, CPUSysState),
+ VMSTATE_UINT64(CSR_CPUID, CPUSysState),
+ VMSTATE_UINT64(CSR_PRCFG1, CPUSysState),
+ VMSTATE_UINT64(CSR_PRCFG2, CPUSysState),
+ VMSTATE_UINT64(CSR_PRCFG3, CPUSysState),
+ VMSTATE_UINT64_ARRAY(CSR_SAVE, CPUSysState, 16),
+ VMSTATE_UINT64(CSR_TID, CPUSysState),
+ VMSTATE_UINT64(CSR_TCFG, CPUSysState),
+ VMSTATE_UINT64(CSR_TVAL, CPUSysState),
+ VMSTATE_UINT64(CSR_CNTC, CPUSysState),
+ VMSTATE_UINT64(CSR_TICLR, CPUSysState),
+ VMSTATE_UINT64(CSR_LLBCTL, CPUSysState),
+ VMSTATE_UINT64(CSR_IMPCTL1, CPUSysState),
+ VMSTATE_UINT64(CSR_IMPCTL2, CPUSysState),
+ VMSTATE_UINT64(CSR_TLBRENTRY, CPUSysState),
+ VMSTATE_UINT64(CSR_TLBRBADV, CPUSysState),
+ VMSTATE_UINT64(CSR_TLBRERA, CPUSysState),
+ VMSTATE_UINT64(CSR_TLBRSAVE, CPUSysState),
+ VMSTATE_UINT64(CSR_TLBRELO0, CPUSysState),
+ VMSTATE_UINT64(CSR_TLBRELO1, CPUSysState),
+ VMSTATE_UINT64(CSR_TLBREHI, CPUSysState),
+ VMSTATE_UINT64(CSR_TLBRPRMD, CPUSysState),
+ VMSTATE_UINT64(CSR_MERRCTL, CPUSysState),
+ VMSTATE_UINT64(CSR_MERRINFO1, CPUSysState),
+ VMSTATE_UINT64(CSR_MERRINFO2, CPUSysState),
+ VMSTATE_UINT64(CSR_MERRENTRY, CPUSysState),
+ VMSTATE_UINT64(CSR_MERRERA, CPUSysState),
+ VMSTATE_UINT64(CSR_MERRSAVE, CPUSysState),
+ VMSTATE_UINT64(CSR_CTAG, CPUSysState),
+ VMSTATE_UINT64_ARRAY(CSR_DMW, CPUSysState, 4),
+ VMSTATE_UINT64(CSR_DBG, CPUSysState),
+ VMSTATE_UINT64(CSR_DERA, CPUSysState),
+ VMSTATE_UINT64(CSR_DSAVE, CPUSysState),
+ VMSTATE_UINT64(CSR_GSTAT, CPUSysState),
+ VMSTATE_UINT64(CSR_GCFG, CPUSysState),
+ VMSTATE_UINT64(CSR_GINTC, CPUSysState),
+ VMSTATE_UINT64(CSR_GCNTC, CPUSysState),
+ VMSTATE_UINT64(CSR_GTLBC, CPUSysState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static bool lvz_needed(void *opaque)
+{
+ LoongArchCPU *cpu = opaque;
+
+ return FIELD_EX64(cpu->env.cpucfg[2], CPUCFG2, LVZ);
+}
+
+static const VMStateDescription vmstate_lvz = {
+ .name = "cpu/lvz",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = lvz_needed,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT64(env.sys_states[0].CSR_GSTAT, LoongArchCPU),
+ VMSTATE_UINT64(env.sys_states[0].CSR_GCFG, LoongArchCPU),
+ VMSTATE_UINT64(env.sys_states[0].CSR_GINTC, LoongArchCPU),
+ VMSTATE_UINT64(env.sys_states[0].CSR_GCNTC, LoongArchCPU),
+ VMSTATE_UINT64(env.sys_states[0].CSR_GTLBC, LoongArchCPU),
+ VMSTATE_STRUCT(env.sys_states[1], LoongArchCPU, 0,
+ vmstate_lvz_sys, CPUSysState),
+ VMSTATE_STRUCT_ARRAY(env.sys_states[1].tlb, LoongArchCPU,
+ LOONGARCH_TLB_MAX,
+ 0, vmstate_tlb_entry, LoongArchTLB),
+ VMSTATE_END_OF_LIST()
+ },
+};
#endif
+static int loongarch_cpu_post_load(void *opaque, int version_id)
+{
+ LoongArchCPU *cpu = opaque;
+ CPULoongArchState *env = &cpu->env;
+ CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
+ bool guest = FIELD_EX64(host->CSR_GSTAT, CSR_GSTAT, VM);
+ set_sys(env, guest);
+ return 0;
+}
+
/* LoongArch CPU state */
const VMStateDescription vmstate_loongarch_cpu = {
.name = "cpu",
.version_id = 4,
.minimum_version_id = 4,
+ .post_load = loongarch_cpu_post_load,
.fields = (const VMStateField[]) {
VMSTATE_UINT64_ARRAY(env.gpr, LoongArchCPU, 32),
VMSTATE_UINT64(env.pc, LoongArchCPU),
@@ -285,6 +393,7 @@ const VMStateDescription vmstate_loongarch_cpu = {
&vmstate_lasx,
#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY)
&vmstate_tlb,
+ &vmstate_lvz,
#endif
&vmstate_lbt,
&vmstate_msgint,
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 10/14] target/loongarch: Add LVZ MMU and PTW support
2026-08-26 10:30 [PATCH v4 00/14] target/loongarch: add LVZ support for TCG SignKirigami
` (8 preceding siblings ...)
2026-08-26 10:30 ` [PATCH v4 09/14] target/loongarch: Add LVZ migration state SignKirigami
@ 2026-08-26 10:30 ` SignKirigami
2026-08-26 10:30 ` [PATCH v4 11/14] target/loongarch: Translate LVZ guest CSR instructions SignKirigami
` (3 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: SignKirigami @ 2026-08-26 10:30 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, xianglai li, SignKirigami, Hengyu Yu
Implement LVZ address translation with separate host and guest TLB banks,
GID matching, guest TLB helpers, LVZ-aware INVT LB handling, two-stage
translation and guest-mode hardware PTW updates through the host mapping.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu-mmu.h | 38 +-
target/loongarch/cpu_helper.c | 84 +--
target/loongarch/tcg/helper.h | 22 +-
.../tcg/insn_trans/trans_privileged.c.inc | 78 ++-
target/loongarch/tcg/tcg_loongarch.h | 3 +-
target/loongarch/tcg/tlb_helper.c | 486 ++++++++++++------
6 files changed, 470 insertions(+), 241 deletions(-)
diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h
index 54fb732d62..c7f83f17de 100644
--- a/target/loongarch/cpu-mmu.h
+++ b/target/loongarch/cpu-mmu.h
@@ -17,6 +17,14 @@ typedef enum TLBRet {
TLBRET_RI,
TLBRET_XI,
TLBRET_PE,
+ TLBRET_HOST_MATCH,
+ TLBRET_HOST_BADADDR,
+ TLBRET_HOST_NOMATCH,
+ TLBRET_HOST_INVALID,
+ TLBRET_HOST_DIRTY,
+ TLBRET_HOST_RI,
+ TLBRET_HOST_XI,
+ TLBRET_HOST_PE,
} TLBRet;
typedef struct MMUContext {
@@ -30,18 +38,19 @@ typedef struct MMUContext {
uint64_t pte_buddy[2];
} MMUContext;
-static inline bool cpu_has_ptw(CPULoongArchState *env)
+static inline bool cpu_has_ptw(CPULoongArchState *env, bool guest)
{
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, guest);
return !!FIELD_EX64(sys->CSR_PWCH, CSR_PWCH, HPTW_EN);
}
-static inline bool pte_present(CPULoongArchState *env, uint64_t entry)
+static inline bool pte_present(CPULoongArchState *env, uint64_t entry,
+ bool guest)
{
uint8_t present;
- if (cpu_has_ptw(env)) {
+ if (cpu_has_ptw(env, guest)) {
present = FIELD_EX64(entry, TLBENTRY, P);
} else {
present = FIELD_EX64(entry, TLBENTRY, V);
@@ -50,11 +59,12 @@ static inline bool pte_present(CPULoongArchState *env, uint64_t entry)
return !!present;
}
-static inline bool pte_write(CPULoongArchState *env, uint64_t entry)
+static inline bool pte_write(CPULoongArchState *env, uint64_t entry,
+ bool guest)
{
uint8_t writable;
- if (cpu_has_ptw(env)) {
+ if (cpu_has_ptw(env, guest)) {
writable = FIELD_EX64(entry, TLBENTRY, W);
} else {
writable = FIELD_EX64(entry, TLBENTRY, D);
@@ -91,14 +101,22 @@ static inline bool pte_dirty(uint64_t entry)
bool check_ps(CPULoongArchState *ent, uint8_t ps);
TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context,
- MMUAccessType access_type, int mmu_idx);
+ MMUAccessType access_type, int mmu_idx, bool guest);
TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context,
MMUAccessType access_type, int mmu_idx,
- int is_debug);
+ int is_debug, uintptr_t retaddr);
TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context,
- int access_type, int mmu_idx, int debug);
+ int access_type, int mmu_idx, int debug, bool guest,
+ uintptr_t retaddr);
void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
- uint64_t *dir_width, unsigned int level);
+ uint64_t *dir_width, unsigned int level, bool guest);
+hwaddr loongarch_get_host_address(CPULoongArchState *env, hwaddr gpa,
+ uintptr_t retaddr);
+TLBRet loongarch_map_host_address(CPULoongArchState *env, MMUContext *context,
+ MMUAccessType access_type, uintptr_t retaddr);
+TLBRet loongarch_map_address(CPULoongArchState *env, MMUContext *context,
+ MMUAccessType access_type, int mmu_idx,
+ int is_debug, bool guest, uintptr_t retaddr);
hwaddr loongarch_cpu_get_phys_addr_debug(CPUState *cpu, vaddr addr);
uint64_t loongarch_palen_mask(CPULoongArchState *env);
diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c
index 123cad3d93..a0aa8c2062 100644
--- a/target/loongarch/cpu_helper.c
+++ b/target/loongarch/cpu_helper.c
@@ -18,10 +18,11 @@
#include "cpu-mmu.h"
#include "tcg/tcg_loongarch.h"
-void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
- uint64_t *dir_width, unsigned int level)
+static void get_dir_base_width_csr(CPULoongArchState *env, uint64_t *dir_base,
+ uint64_t *dir_width, unsigned int level,
+ bool guest)
{
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, guest);
switch (level) {
case 1:
@@ -48,18 +49,24 @@ void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
}
}
+void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
+ uint64_t *dir_width, unsigned int level, bool guest)
+{
+ get_dir_base_width_csr(env, dir_base, dir_width, level, guest);
+}
+
TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context,
- MMUAccessType access_type, int mmu_idx)
+ MMUAccessType access_type, int mmu_idx, bool guest)
{
- uint64_t plv = mmu_idx;
+ uint64_t plv = mmu_idx_to_plv(mmu_idx);
uint64_t tlb_entry, tlb_ppn;
uint8_t tlb_ps, tlb_plv, tlb_nx, tlb_nr, tlb_rplv;
bool tlb_v, tlb_d;
tlb_entry = context->pte;
tlb_ps = context->ps;
- tlb_v = pte_present(env, tlb_entry);
- tlb_d = pte_write(env, tlb_entry);
+ tlb_v = pte_present(env, tlb_entry, guest);
+ tlb_d = pte_write(env, tlb_entry, guest);
tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV);
if (is_la64(env)) {
tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN);
@@ -101,7 +108,7 @@ TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context,
context->physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) |
(context->addr & MAKE_64BIT_MASK(0, tlb_ps));
context->prot = PAGE_READ;
- context->mmu_index = tlb_plv;
+ context->mmu_index = mmu_idx;
if (tlb_d) {
context->prot |= PAGE_WRITE;
}
@@ -147,7 +154,8 @@ static MemTxResult loongarch_cmpxchg_phys(CPUState *cs, hwaddr phys,
}
TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context,
- int access_type, int mmu_idx, int debug)
+ int access_type, int mmu_idx, int debug, bool guest,
+ uintptr_t retaddr)
{
const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
CPUState *cs = env_cpu(env);
@@ -159,7 +167,7 @@ TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context,
vaddr address;
TLBRet ret;
MemTxResult ret1;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, guest);
address = context->addr;
if ((address >> 63) & 0x1) {
@@ -170,7 +178,7 @@ TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context,
base &= palen_mask;
for (level = 4; level >= 0; level--) {
- get_dir_base_width(env, &dir_base, &dir_width, level);
+ get_dir_base_width(env, &dir_base, &dir_width, level, guest);
if (dir_width == 0) {
continue;
@@ -179,7 +187,10 @@ TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context,
/* get next level page directory */
index = (address >> dir_base) & ((1 << dir_width) - 1);
phys = base | index << 3;
- base = address_space_ldq_le(cs->as, phys, attrs, NULL);
+ base = address_space_ldq_le(
+ cs->as,
+ guest ? loongarch_get_host_address(env, phys, retaddr) : phys,
+ attrs, NULL);
if (level) {
if (FIELD_EX64(base, TLBENTRY, HUGE)) {
/* base is a huge pte */
@@ -208,19 +219,22 @@ restart:
context->pte_buddy[index] = base;
context->pte_buddy[1 - index] = base + BIT_ULL(dir_base);
base += (BIT_ULL(dir_base) & address);
- } else if (cpu_has_ptw(env)) {
+ } else if (cpu_has_ptw(env, guest)) {
uint64_t val;
index &= 1;
context->pte_buddy[index] = base;
- val = address_space_ldq_le(cs->as, phys + 8 * (1 - 2 * index),
- attrs, NULL);
+ val = address_space_ldq_le(
+ cs->as,
+ (guest ? loongarch_get_host_address(env, phys, retaddr) : phys) +
+ 8 * (1 - 2 * index),
+ attrs, NULL);
context->pte_buddy[1 - index] = val;
}
context->ps = dir_base;
context->pte = base;
- ret = loongarch_check_pte(env, context, access_type, mmu_idx);
+ ret = loongarch_check_pte(env, context, access_type, mmu_idx, guest);
if (debug) {
return ret;
}
@@ -231,7 +245,7 @@ restart:
* Need atomic compchxg operation with pte update, other vCPUs may
* update pte at the same time.
*/
- if (ret == TLBRET_MATCH && cpu_has_ptw(env)) {
+ if (ret == TLBRET_MATCH && cpu_has_ptw(env, guest)) {
if (access_type == MMU_DATA_STORE && pte_dirty(base)) {
return ret;
}
@@ -244,10 +258,15 @@ restart:
if (access_type == MMU_DATA_STORE) {
base = pte_mkdirty(base);
}
- ret1 = loongarch_cmpxchg_phys(cs, phys, pte, base);
+ ret1 = loongarch_cmpxchg_phys(
+ cs, guest ? loongarch_get_host_address(env, phys, retaddr) : phys,
+ pte, base);
/* PTE updated by other CPU, reload PTE entry */
if (ret1 == MEMTX_DECODE_ERROR) {
- base = address_space_ldq_le(cs->as, phys, attrs, NULL);
+ base = address_space_ldq_le(
+ cs->as,
+ guest ? loongarch_get_host_address(env, phys, retaddr) : phys,
+ attrs, NULL);
goto restart;
}
@@ -273,15 +292,15 @@ restart:
return ret;
}
-static TLBRet loongarch_map_address(CPULoongArchState *env,
- MMUContext *context,
- MMUAccessType access_type, int mmu_idx,
- int is_debug)
+TLBRet loongarch_map_address(CPULoongArchState *env, MMUContext *context,
+ MMUAccessType access_type, int mmu_idx,
+ int is_debug, bool guest, uintptr_t retaddr)
{
TLBRet ret;
if (tcg_enabled()) {
- ret = loongarch_get_addr_from_tlb(env, context, access_type, mmu_idx);
+ ret = loongarch_get_addr_from_tlb(env, context, access_type, mmu_idx,
+ guest);
if (ret != TLBRET_NOMATCH) {
return ret;
}
@@ -293,7 +312,8 @@ static TLBRet loongarch_map_address(CPULoongArchState *env,
* legal mapping, even if the mapping is not yet in TLB. return 0 if
* there is a valid map, else none zero.
*/
- return loongarch_ptw(env, context, access_type, mmu_idx, is_debug);
+ return loongarch_ptw(env, context, access_type, mmu_idx, is_debug,
+ guest, retaddr);
}
return TLBRET_NOMATCH;
@@ -312,13 +332,14 @@ static hwaddr dmw_va2pa(CPULoongArchState *env, vaddr va, uint64_t dmw)
TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context,
MMUAccessType access_type, int mmu_idx,
- int is_debug)
+ int is_debug, uintptr_t retaddr)
{
- int user_mode = mmu_idx == MMU_USER_IDX;
- int kernel_mode = mmu_idx == MMU_KERNEL_IDX;
+ bool guest = env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST;
+ CPUSysState *sys = get_sys(env, guest);
+ int user_mode = mmu_idx_to_plv(mmu_idx) == MMU_USER_IDX;
+ int kernel_mode = mmu_idx_to_plv(mmu_idx) == MMU_KERNEL_IDX;
uint32_t plv, base_c, base_v;
int64_t addr_high;
- CPUSysState *sys = env_sys(env);
uint8_t da = FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, DA);
uint8_t pg = FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, PG);
vaddr address;
@@ -360,7 +381,8 @@ TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context,
}
/* Mapped address */
- return loongarch_map_address(env, context, access_type, mmu_idx, is_debug);
+ return loongarch_map_address(env, context, access_type, mmu_idx, is_debug,
+ guest, retaddr);
}
hwaddr loongarch_cpu_get_phys_addr_debug(CPUState *cs, vaddr addr)
@@ -370,7 +392,7 @@ hwaddr loongarch_cpu_get_phys_addr_debug(CPUState *cs, vaddr addr)
context.addr = addr;
if (get_physical_address(env, &context, MMU_DATA_LOAD,
- cpu_mmu_index(cs, false), 1) != TLBRET_MATCH) {
+ cpu_mmu_index(cs, false), 1, 0) != TLBRET_MATCH) {
return -1;
}
return context.physical;
diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h
index 8e0ddd8693..0e87181deb 100644
--- a/target/loongarch/tcg/helper.h
+++ b/target/loongarch/tcg/helper.h
@@ -130,17 +130,17 @@ DEF_HELPER_3(iocsrwr_w, void, env, tl, tl)
DEF_HELPER_3(iocsrwr_d, void, env, tl, tl)
/* TLB helper */
-DEF_HELPER_1(tlbwr, void, env)
-DEF_HELPER_1(tlbfill, void, env)
-DEF_HELPER_1(tlbsrch, void, env)
-DEF_HELPER_1(tlbrd, void, env)
-DEF_HELPER_1(tlbclr, void, env)
-DEF_HELPER_1(tlbflush, void, env)
-DEF_HELPER_1(invtlb_all, void, env)
-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_2(tlbwr, void, env, i32)
+DEF_HELPER_2(tlbfill, void, env, i32)
+DEF_HELPER_2(tlbsrch, void, env, i32)
+DEF_HELPER_2(tlbrd, void, env, i32)
+DEF_HELPER_2(tlbclr, void, env, i32)
+DEF_HELPER_2(tlbflush, void, env, i32)
+DEF_HELPER_4(invtlb_all, void, env, tl, i32, i32)
+DEF_HELPER_4(invtlb_all_g, void, env, tl, i32, i32)
+DEF_HELPER_3(invtlb_all_asid, void, env, tl, i32)
+DEF_HELPER_4(invtlb_page_asid, void, env, tl, tl, i32)
+DEF_HELPER_4(invtlb_page_asid_or_g, void, env, tl, tl, i32)
DEF_HELPER_4(lddir, tl, env, tl, i32, i32)
DEF_HELPER_4(ldpte, void, env, tl, tl, i32)
diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
index bcfc977990..abedb132b9 100644
--- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
@@ -249,7 +249,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;
}
@@ -260,7 +260,7 @@ static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a)
if (check_plv(ctx)) {
return false;
}
- gen_helper_tlbsrch(tcg_env);
+ gen_helper_tlbsrch(tcg_env, tcg_constant_i32(ctx->guest_mode));
return true;
}
@@ -269,7 +269,7 @@ static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a)
if (check_plv(ctx)) {
return false;
}
- gen_helper_tlbrd(tcg_env);
+ gen_helper_tlbrd(tcg_env, tcg_constant_i32(ctx->guest_mode));
return true;
}
@@ -278,7 +278,7 @@ static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
if (check_plv(ctx)) {
return false;
}
- gen_helper_tlbwr(tcg_env);
+ gen_helper_tlbwr(tcg_env, tcg_constant_i32(ctx->guest_mode));
check_mmu_idx(ctx);
return true;
}
@@ -288,7 +288,7 @@ static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a)
if (check_plv(ctx)) {
return false;
}
- gen_helper_tlbfill(tcg_env);
+ gen_helper_tlbfill(tcg_env, tcg_constant_i32(ctx->guest_mode));
check_mmu_idx(ctx);
return true;
}
@@ -298,7 +298,7 @@ static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a)
if (check_plv(ctx)) {
return false;
}
- gen_helper_tlbclr(tcg_env);
+ gen_helper_tlbclr(tcg_env, tcg_constant_i32(ctx->guest_mode));
check_mmu_idx(ctx);
return true;
}
@@ -308,7 +308,7 @@ static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a)
if (check_plv(ctx)) {
return false;
}
- gen_helper_tlbflush(tcg_env);
+ gen_helper_tlbflush(tcg_env, tcg_constant_i32(ctx->guest_mode));
check_mmu_idx(ctx);
return true;
}
@@ -322,25 +322,63 @@ static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
return false;
}
+ if (!avail_LVZ(ctx) && a->imm > 0x6) {
+ return false;
+ }
+
switch (a->imm) {
- case 0:
- case 1:
- gen_helper_invtlb_all(tcg_env);
+ case 0x0:
+ case 0x1:
+ gen_helper_invtlb_all(tcg_env, rj, tcg_constant_i32(a->imm),
+ tcg_constant_i32(0));
+ break;
+ case 0x2:
+ gen_helper_invtlb_all_g(tcg_env, rj, tcg_constant_i32(1),
+ tcg_constant_i32(0));
+ break;
+ case 0x3:
+ gen_helper_invtlb_all_g(tcg_env, rj, tcg_constant_i32(0),
+ tcg_constant_i32(0));
+ break;
+ case 0x4:
+ gen_helper_invtlb_all_asid(tcg_env, rj, tcg_constant_i32(0));
+ break;
+ case 0x5:
+ gen_helper_invtlb_page_asid(tcg_env, rj, rk, tcg_constant_i32(0));
+ break;
+ case 0x6:
+ gen_helper_invtlb_page_asid_or_g(tcg_env, rj, rk,
+ tcg_constant_i32(0));
+ break;
+ case 0x9:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ //TODO: refine 0x9-0x16
+ gen_helper_invtlb_all(tcg_env, rj, tcg_constant_i32(0),
+ tcg_constant_i32(0));
break;
- case 2:
- gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(1));
+ case 0xa:
+ gen_helper_invtlb_all_g(tcg_env, rj, tcg_constant_i32(1),
+ tcg_constant_i32(1));
break;
- case 3:
- gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(0));
+ case 0xb:
+ gen_helper_invtlb_all_g(tcg_env, rj, tcg_constant_i32(0),
+ tcg_constant_i32(1));
break;
- case 4:
- gen_helper_invtlb_all_asid(tcg_env, rj);
+ case 0xc:
+ gen_helper_invtlb_all_asid(tcg_env, rj, tcg_constant_i32(1));
break;
- case 5:
- gen_helper_invtlb_page_asid(tcg_env, rj, rk);
+ case 0xd:
+ gen_helper_invtlb_page_asid(tcg_env, rj, rk, tcg_constant_i32(1));
break;
- case 6:
- gen_helper_invtlb_page_asid_or_g(tcg_env, rj, rk);
+ case 0xe:
+ gen_helper_invtlb_page_asid_or_g(tcg_env, rj, rk,
+ tcg_constant_i32(1));
break;
default:
return false;
diff --git a/target/loongarch/tcg/tcg_loongarch.h b/target/loongarch/tcg/tcg_loongarch.h
index 7fb627f2d6..5acaf49d47 100644
--- a/target/loongarch/tcg/tcg_loongarch.h
+++ b/target/loongarch/tcg/tcg_loongarch.h
@@ -18,6 +18,7 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env,
MMUContext *context,
- MMUAccessType access_type, int mmu_idx);
+ MMUAccessType access_type, int mmu_idx,
+ bool guest);
#endif /* TARGET_LOONGARCH_TCG_LOONGARCH_H */
diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c
index a4b90beca6..776626072d 100644
--- a/target/loongarch/tcg/tlb_helper.c
+++ b/target/loongarch/tcg/tlb_helper.c
@@ -8,7 +8,6 @@
#include "qemu/osdep.h"
#include "qemu/guest-random.h"
-
#include "cpu.h"
#include "cpu-mmu.h"
#include "internals.h"
@@ -18,6 +17,8 @@
#include "exec/target_page.h"
#include "accel/tcg/cpu-ldst.h"
#include "accel/tcg/cpu-loop.h"
+#include "accel/tcg/probe.h"
+#include "exec/tlb-flags.h"
#include "exec/log.h"
#include "cpu-csr.h"
#include "tcg/tcg_loongarch.h"
@@ -35,29 +36,49 @@ static bool tlb_match_asid(bool global, int asid, int tlb_asid)
return !global && tlb_asid == asid;
}
+static inline bool tlb_entry_matches_gid(LoongArchTLB *tlb, uint8_t gid)
+{
+ return FIELD_EX64(tlb->tlb_misc, TLB_MISC, GID) == gid;
+}
+
+static inline LoongArchTLB *tlb_bank(CPULoongArchState *env, bool guest)
+{
+ return get_sys(env, guest)->tlb;
+}
+
bool check_ps(CPULoongArchState *env, uint8_t tlb_ps)
{
- CPUSysState *sys = env_sys(env);
+ CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
if (tlb_ps >= 64) {
return false;
}
- return BIT_ULL(tlb_ps) & (sys->CSR_PRCFG2);
+ return BIT_ULL(tlb_ps) & (host->CSR_PRCFG2);
}
static void raise_mmu_exception(CPULoongArchState *env, vaddr address,
MMUAccessType access_type, TLBRet tlb_error)
{
CPUState *cs = env_cpu(env);
- CPUSysState *sys = env_sys(env);
+ bool real_guest;
+
+ if (env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST &&
+ tlb_error > TLBRET_HOST_MATCH) {
+ trigger_vm_exit(env);
+ }
+
+ real_guest = !env->vm_exit && env_vm_level(env);
+ CPUSysState *sys = get_sys(env, real_guest);
switch (tlb_error) {
default:
case TLBRET_BADADDR:
+ case TLBRET_HOST_BADADDR:
cs->exception_index = access_type == MMU_INST_FETCH
? EXCCODE_ADEF : EXCCODE_ADEM;
break;
case TLBRET_NOMATCH:
+ case TLBRET_HOST_NOMATCH:
/* No TLB match for a mapped address */
if (access_type == MMU_DATA_LOAD) {
cs->exception_index = EXCCODE_PIL;
@@ -69,6 +90,7 @@ static void raise_mmu_exception(CPULoongArchState *env, vaddr address,
sys->CSR_TLBRERA = FIELD_DP64(sys->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 1);
break;
case TLBRET_INVALID:
+ case TLBRET_HOST_INVALID:
/* TLB match with no valid bit */
if (access_type == MMU_DATA_LOAD) {
cs->exception_index = EXCCODE_PIL;
@@ -79,24 +101,28 @@ static void raise_mmu_exception(CPULoongArchState *env, vaddr address,
}
break;
case TLBRET_DIRTY:
+ case TLBRET_HOST_DIRTY:
/* TLB match but 'D' bit is cleared */
cs->exception_index = EXCCODE_PME;
break;
case TLBRET_XI:
+ case TLBRET_HOST_XI:
/* Execute-Inhibit Exception */
cs->exception_index = EXCCODE_PNX;
break;
case TLBRET_RI:
+ case TLBRET_HOST_RI:
/* Read-Inhibit Exception */
cs->exception_index = EXCCODE_PNR;
break;
case TLBRET_PE:
+ case TLBRET_HOST_PE:
/* Privileged Exception */
cs->exception_index = EXCCODE_PPI;
break;
}
- if (tlb_error == TLBRET_NOMATCH) {
+ if (tlb_error == TLBRET_NOMATCH || tlb_error == TLBRET_HOST_NOMATCH) {
sys->CSR_TLBRBADV = address;
if (is_la64(env)) {
sys->CSR_TLBREHI = FIELD_DP64(sys->CSR_TLBREHI, CSR_TLBREHI_64,
@@ -110,15 +136,19 @@ static void raise_mmu_exception(CPULoongArchState *env, vaddr address,
sys->CSR_BADV = address;
}
sys->CSR_TLBEHI = address & (TARGET_PAGE_MASK << 1);
- }
+ }
}
-static void invalidate_tlb_entry(CPULoongArchState *env, int index)
+static void invalidate_tlb_entry(CPULoongArchState *env, int index, bool guest)
{
target_ulong addr, mask, pagesize;
uint8_t tlb_ps;
- LoongArchTLB *tlb = &env->tlb[index];
- int idxmap = BIT(MMU_KERNEL_IDX) | BIT(MMU_USER_IDX);
+ LoongArchTLB *tlb = &tlb_bank(env, guest)[index];
+ int idxmap = guest ? (BIT(MMU_GUEST_IDX) | BIT(MMU_GUEST_IDX + 1) |
+ BIT(MMU_GUEST_IDX + 2) | BIT(MMU_GUEST_IDX + 3) |
+ BIT(MMU_GUEST_DA_IDX)) :
+ (BIT(MMU_KERNEL_IDX) | BIT(MMU_USER_IDX) |
+ BIT(MMU_DA_IDX));
uint64_t tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
bool tlb_v;
@@ -128,28 +158,27 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index)
addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask;
addr = sextract64(addr, 0, TARGET_VIRT_ADDR_SPACE_BITS);
- tlb_v = pte_present(env, tlb->tlb_entry0);
+ tlb_v = pte_present(env, tlb->tlb_entry0, guest);
if (tlb_v) {
tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize,
idxmap, TARGET_LONG_BITS);
}
- tlb_v = pte_present(env, tlb->tlb_entry1);
+ tlb_v = pte_present(env, tlb->tlb_entry1, guest);
if (tlb_v) {
tlb_flush_range_by_mmuidx(env_cpu(env), addr + pagesize, pagesize,
idxmap, TARGET_LONG_BITS);
}
}
-static void invalidate_tlb(CPULoongArchState *env, int index)
+static void invalidate_tlb(CPULoongArchState *env, int index, bool guest)
{
- LoongArchTLB *tlb;
+ LoongArchTLB *tlb = &tlb_bank(env, guest)[index];
+ CPUSysState *sys = get_sys(env, guest);
uint16_t csr_asid, tlb_asid, tlb_g;
uint8_t tlb_e;
- CPUSysState *sys = env_sys(env);
csr_asid = FIELD_EX64(sys->CSR_ASID, CSR_ASID, ASID);
- tlb = &env->tlb[index];
tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
if (!tlb_e) {
return;
@@ -158,19 +187,19 @@ static void invalidate_tlb(CPULoongArchState *env, int index)
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
- /* QEMU TLB is flushed when asid is changed */
if (tlb_g == 0 && tlb_asid != csr_asid) {
return;
}
- invalidate_tlb_entry(env, index);
+ invalidate_tlb_entry(env, index, guest);
}
/* Prepare tlb entry information in software PTW mode */
-static void sptw_prepare_context(CPULoongArchState *env, MMUContext *context)
+static void sptw_prepare_context(CPULoongArchState *env, MMUContext *context,
+ bool guest)
{
+ CPUSysState *sys = get_sys(env, guest);
uint64_t lo0, lo1, csr_vppn;
uint8_t csr_ps;
- CPUSysState *sys = env_sys(env);
if (FIELD_EX64(sys->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
csr_ps = FIELD_EX64(sys->CSR_TLBREHI, CSR_TLBREHI, PS);
@@ -199,24 +228,24 @@ static void sptw_prepare_context(CPULoongArchState *env, MMUContext *context)
}
static void fill_tlb_entry(CPULoongArchState *env, LoongArchTLB *tlb,
- MMUContext *context)
+ MMUContext *context, bool guest)
{
+ CPUSysState *sys = get_sys(env, guest);
uint64_t lo0, lo1, csr_vppn;
uint16_t csr_asid;
uint8_t csr_ps;
- CPUSysState *sys = env_sys(env);
csr_vppn = context->addr >> R_TLB_MISC_VPPN_SHIFT;
csr_ps = context->ps;
lo0 = context->pte_buddy[0];
lo1 = context->pte_buddy[1];
- /* Store page size in field PS */
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps);
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn);
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1);
csr_asid = FIELD_EX64(sys->CSR_ASID, CSR_ASID, ASID);
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, ASID, csr_asid);
+ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, GID, get_tgid(env));
tlb->tlb_entry0 = lo0;
tlb->tlb_entry1 = lo1;
@@ -240,24 +269,24 @@ static uint32_t get_random_tlb(uint32_t low, uint32_t high)
*/
static LoongArchTLB *loongarch_tlb_search_cb(CPULoongArchState *env,
vaddr vaddr, int csr_asid,
- tlb_match func)
+ tlb_match func, bool guest,
+ uint8_t gid)
{
+ CPUSysState *sys = get_sys(env, guest);
LoongArchTLB *tlb;
uint16_t tlb_asid, stlb_idx;
uint8_t tlb_e, tlb_ps, stlb_ps;
bool tlb_g;
int i, compare_shift;
uint64_t vpn, tlb_vppn;
- CPUSysState *sys = env_sys(env);
stlb_ps = FIELD_EX64(sys->CSR_STLBPS, CSR_STLBPS, PS);
vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1);
- stlb_idx = vpn & 0xff; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */
+ stlb_idx = vpn & 0xff;
compare_shift = stlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
- /* Search STLB */
for (i = 0; i < 8; ++i) {
- tlb = &env->tlb[i * 256 + stlb_idx];
+ tlb = &tlb_bank(env, guest)[i * 256 + stlb_idx];
tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
if (tlb_e) {
tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
@@ -265,15 +294,15 @@ static LoongArchTLB *loongarch_tlb_search_cb(CPULoongArchState *env,
tlb_g = !!FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
if (func(tlb_g, csr_asid, tlb_asid) &&
+ tlb_entry_matches_gid(tlb, gid) &&
(vpn == (tlb_vppn >> compare_shift))) {
return tlb;
}
}
}
- /* Search MTLB */
for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; ++i) {
- tlb = &env->tlb[i];
+ tlb = &tlb_bank(env, guest)[i];
tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
if (tlb_e) {
tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
@@ -283,6 +312,7 @@ static LoongArchTLB *loongarch_tlb_search_cb(CPULoongArchState *env,
compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1);
if (func(tlb_g, csr_asid, tlb_asid) &&
+ tlb_entry_matches_gid(tlb, gid) &&
(vpn == (tlb_vppn >> compare_shift))) {
return tlb;
}
@@ -292,35 +322,36 @@ static LoongArchTLB *loongarch_tlb_search_cb(CPULoongArchState *env,
}
static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr,
- int *index)
+ int *index, bool guest, uint8_t gid)
{
- int csr_asid;
- tlb_match func;
+ CPUSysState *sys = get_sys(env, guest);
LoongArchTLB *tlb;
- CPUSysState *sys = env_sys(env);
- func = tlb_match_any;
- csr_asid = FIELD_EX64(sys->CSR_ASID, CSR_ASID, ASID);
- tlb = loongarch_tlb_search_cb(env, vaddr, csr_asid, func);
+ tlb = loongarch_tlb_search_cb(env, vaddr,
+ FIELD_EX64(sys->CSR_ASID, CSR_ASID, ASID),
+ tlb_match_any, guest, gid);
if (tlb) {
- *index = tlb - env->tlb;
+ *index = tlb - tlb_bank(env, guest);
return true;
}
return false;
}
-void helper_tlbsrch(CPULoongArchState *env)
+void helper_tlbsrch(CPULoongArchState *env, uint32_t guest)
{
+ CPUSysState *sys = get_sys(env, guest);
int index, match;
- CPUSysState *sys = env_sys(env);
+ vaddr search_ehi;
if (FIELD_EX64(sys->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
- match = loongarch_tlb_search(env, sys->CSR_TLBREHI, &index);
+ search_ehi = sys->CSR_TLBREHI;
} else {
- match = loongarch_tlb_search(env, sys->CSR_TLBEHI, &index);
+ search_ehi = sys->CSR_TLBEHI;
}
+ match = loongarch_tlb_search(env, search_ehi, &index, guest,
+ get_tgid(env));
if (match) {
sys->CSR_TLBIDX = FIELD_DP64(sys->CSR_TLBIDX, CSR_TLBIDX, INDEX, index);
sys->CSR_TLBIDX = FIELD_DP64(sys->CSR_TLBIDX, CSR_TLBIDX, NE, 0);
@@ -330,20 +361,19 @@ void helper_tlbsrch(CPULoongArchState *env)
sys->CSR_TLBIDX = FIELD_DP64(sys->CSR_TLBIDX, CSR_TLBIDX, NE, 1);
}
-void helper_tlbrd(CPULoongArchState *env)
+void helper_tlbrd(CPULoongArchState *env, uint32_t guest)
{
+ CPUSysState *sys = get_sys(env, guest);
LoongArchTLB *tlb;
int index;
uint8_t tlb_ps, tlb_e;
- CPUSysState *sys = env_sys(env);
index = FIELD_EX64(sys->CSR_TLBIDX, CSR_TLBIDX, INDEX);
- tlb = &env->tlb[index];
+ tlb = &tlb_bank(env, guest)[index];
tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
if (!tlb_e) {
- /* Invalid TLB entry */
sys->CSR_TLBIDX = FIELD_DP64(sys->CSR_TLBIDX, CSR_TLBIDX, NE, 1);
sys->CSR_ASID = FIELD_DP64(sys->CSR_ASID, CSR_ASID, ASID, 0);
sys->CSR_TLBEHI = 0;
@@ -351,79 +381,73 @@ void helper_tlbrd(CPULoongArchState *env)
sys->CSR_TLBELO1 = 0;
sys->CSR_TLBIDX = FIELD_DP64(sys->CSR_TLBIDX, CSR_TLBIDX, PS, 0);
} else {
- /* Valid TLB entry */
sys->CSR_TLBIDX = FIELD_DP64(sys->CSR_TLBIDX, CSR_TLBIDX, NE, 0);
sys->CSR_TLBIDX = FIELD_DP64(sys->CSR_TLBIDX, CSR_TLBIDX,
- PS, (tlb_ps & 0x3f));
+ PS, tlb_ps & 0x3f);
sys->CSR_TLBEHI = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN) <<
- R_TLB_MISC_VPPN_SHIFT;
+ R_TLB_MISC_VPPN_SHIFT;
sys->CSR_TLBELO0 = tlb->tlb_entry0;
sys->CSR_TLBELO1 = tlb->tlb_entry1;
}
}
static void update_tlb_index(CPULoongArchState *env, MMUContext *context,
- int index)
+ int index, bool guest)
{
LoongArchTLB *old, new = {};
bool skip_inv = false, tlb_v0, tlb_v1;
- old = env->tlb + index;
- fill_tlb_entry(env, &new, context);
- /* Check whether ASID/VPPN is the same */
+ old = tlb_bank(env, guest) + index;
+ fill_tlb_entry(env, &new, context, guest);
if (old->tlb_misc == new.tlb_misc) {
- /* Check whether both even/odd pages is the same or invalid */
- tlb_v0 = pte_present(env, old->tlb_entry0);
- tlb_v1 = pte_present(env, old->tlb_entry1);
+ tlb_v0 = pte_present(env, old->tlb_entry0, guest);
+ tlb_v1 = pte_present(env, old->tlb_entry1, guest);
if ((!tlb_v0 || new.tlb_entry0 == old->tlb_entry0) &&
(!tlb_v1 || new.tlb_entry1 == old->tlb_entry1)) {
skip_inv = true;
}
}
- /* flush tlb before updating the entry */
if (!skip_inv) {
- invalidate_tlb(env, index);
+ invalidate_tlb(env, index, guest);
}
*old = new;
}
-void helper_tlbwr(CPULoongArchState *env)
+void helper_tlbwr(CPULoongArchState *env, uint32_t guest)
{
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, guest);
int index = FIELD_EX64(sys->CSR_TLBIDX, CSR_TLBIDX, INDEX);
MMUContext context;
if (FIELD_EX64(sys->CSR_TLBIDX, CSR_TLBIDX, NE)) {
- invalidate_tlb(env, index);
+ invalidate_tlb(env, index, guest);
return;
}
- sptw_prepare_context(env, &context);
- update_tlb_index(env, &context, index);
+ sptw_prepare_context(env, &context, guest);
+ update_tlb_index(env, &context, index, guest);
}
static int get_tlb_random_index(CPULoongArchState *env, vaddr addr,
- int pagesize)
+ int pagesize, bool guest)
{
+ CPUSysState *sys = get_sys(env, guest);
uint64_t address;
int index, set, i, stlb_idx;
uint16_t asid, tlb_asid, stlb_ps;
LoongArchTLB *tlb;
uint8_t tlb_e, tlb_g;
- CPUSysState *sys = env_sys(env);
- /* Validity of stlb_ps is checked in helper_csrwr_stlbps() */
stlb_ps = FIELD_EX64(sys->CSR_STLBPS, CSR_STLBPS, PS);
asid = FIELD_EX64(sys->CSR_ASID, CSR_ASID, ASID);
if (pagesize == stlb_ps) {
- /* Only write into STLB bits [47:13] */
address = addr & ~MAKE_64BIT_MASK(0, R_CSR_TLBEHI_64_VPPN_SHIFT);
set = -1;
- stlb_idx = (address >> (stlb_ps + 1)) & 0xff; /* [0,255] */
+ stlb_idx = (address >> (stlb_ps + 1)) & 0xff;
for (i = 0; i < 8; ++i) {
- tlb = &env->tlb[i * 256 + stlb_idx];
+ tlb = &tlb_bank(env, guest)[i * 256 + stlb_idx];
tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
if (!tlb_e) {
set = i;
@@ -432,21 +456,20 @@ static int get_tlb_random_index(CPULoongArchState *env, vaddr addr,
tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
- if (tlb_g == 0 && asid != tlb_asid) {
+ if (tlb_g == 0 && asid != tlb_asid &&
+ tlb_entry_matches_gid(tlb, get_tgid(env))) {
set = i;
}
}
- /* Choose one set randomly */
if (set < 0) {
set = get_random_tlb(0, 7);
}
index = set * 256 + stlb_idx;
} else {
- /* Only write into MTLB */
index = -1;
for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) {
- tlb = &env->tlb[i];
+ tlb = &tlb_bank(env, guest)[i];
tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
if (!tlb_e) {
@@ -456,7 +479,8 @@ static int get_tlb_random_index(CPULoongArchState *env, vaddr addr,
tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
- if (tlb_g == 0 && asid != tlb_asid) {
+ if (tlb_g == 0 && asid != tlb_asid &&
+ tlb_entry_matches_gid(tlb, get_tgid(env))) {
index = i;
}
}
@@ -469,56 +493,54 @@ static int get_tlb_random_index(CPULoongArchState *env, vaddr addr,
return index;
}
-void helper_tlbfill(CPULoongArchState *env)
+void helper_tlbfill(CPULoongArchState *env, uint32_t guest)
{
+ CPUSysState *sys = get_sys(env, guest);
vaddr entryhi;
int index, pagesize;
MMUContext context;
- CPUSysState *sys = env_sys(env);
if (FIELD_EX64(sys->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
entryhi = sys->CSR_TLBREHI;
- /* Validity of pagesize is checked in helper_ldpte() */
pagesize = FIELD_EX64(sys->CSR_TLBREHI, CSR_TLBREHI, PS);
} else {
entryhi = sys->CSR_TLBEHI;
- /* Validity of pagesize is checked in helper_tlbrd() */
pagesize = FIELD_EX64(sys->CSR_TLBIDX, CSR_TLBIDX, PS);
}
- sptw_prepare_context(env, &context);
- index = get_tlb_random_index(env, entryhi, pagesize);
- invalidate_tlb(env, index);
- fill_tlb_entry(env, env->tlb + index, &context);
+ sptw_prepare_context(env, &context, guest);
+ index = get_tlb_random_index(env, entryhi, pagesize, guest);
+ invalidate_tlb(env, index, guest);
+ fill_tlb_entry(env, tlb_bank(env, guest) + index, &context, guest);
}
-void helper_tlbclr(CPULoongArchState *env)
+void helper_tlbclr(CPULoongArchState *env, uint32_t guest)
{
+ CPUSysState *sys = get_sys(env, guest);
LoongArchTLB *tlb;
int i, index;
uint16_t csr_asid, tlb_asid, tlb_g;
- CPUSysState *sys = env_sys(env);
csr_asid = FIELD_EX64(sys->CSR_ASID, CSR_ASID, ASID);
index = FIELD_EX64(sys->CSR_TLBIDX, CSR_TLBIDX, INDEX);
if (index < LOONGARCH_STLB) {
- /* STLB. One line per operation */
for (i = 0; i < 8; i++) {
- tlb = &env->tlb[i * 256 + (index % 256)];
+ tlb = &tlb_bank(env, guest)[i * 256 + (index % 256)];
tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
- if (!tlb_g && tlb_asid == csr_asid) {
+ if (!tlb_g && tlb_asid == csr_asid &&
+ tlb_entry_matches_gid(tlb, get_tgid(env))) {
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
}
}
} else if (index < LOONGARCH_TLB_MAX) {
- /* All MTLB entries */
for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) {
- tlb = &env->tlb[i];
+ tlb = &tlb_bank(env, guest)[i];
tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
- if (!tlb_g && tlb_asid == csr_asid) {
+ if (!tlb_g && tlb_asid == csr_asid &&
+ tlb_entry_matches_gid(tlb, get_tgid(env))) {
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
}
}
@@ -527,63 +549,97 @@ void helper_tlbclr(CPULoongArchState *env)
tlb_flush(env_cpu(env));
}
-void helper_tlbflush(CPULoongArchState *env)
+void helper_tlbflush(CPULoongArchState *env, uint32_t guest)
{
+ CPUSysState *sys = get_sys(env, guest);
int i, index;
- CPUSysState *sys = env_sys(env);
index = FIELD_EX64(sys->CSR_TLBIDX, CSR_TLBIDX, INDEX);
if (index < LOONGARCH_STLB) {
- /* STLB. One line per operation */
for (i = 0; i < 8; i++) {
int s_idx = i * 256 + (index % 256);
- env->tlb[s_idx].tlb_misc = FIELD_DP64(env->tlb[s_idx].tlb_misc,
- TLB_MISC, E, 0);
+ LoongArchTLB *tlb = &tlb_bank(env, guest)[s_idx];
+
+ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
}
} else if (index < LOONGARCH_TLB_MAX) {
- /* All MTLB entries */
for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) {
- env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc,
- TLB_MISC, E, 0);
+ LoongArchTLB *tlb = &tlb_bank(env, guest)[i];
+
+ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
}
}
tlb_flush(env_cpu(env));
}
-void helper_invtlb_all(CPULoongArchState *env)
+void helper_invtlb_all(CPULoongArchState *env, target_ulong info, uint32_t op,
+ uint32_t to_guest)
{
+ uint16_t gid = to_guest ? (info & 0xff) : get_tgid(env);
+
+ if (to_guest && env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
+ do_raise_exception(env, EXCCODE_IPE, GETPC());
+ }
+
+ to_guest |= env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST;
+
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
- env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc,
- TLB_MISC, E, 0);
+ LoongArchTLB *tlb = &tlb_bank(env, false)[i];
+ LoongArchTLB *gtlb = &tlb_bank(env, true)[i];
+
+ if (!to_guest && (op == 0 || tlb_entry_matches_gid(tlb, 0))) {
+ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
+ }
+ if ((!to_guest && op == 0) ||
+ (to_guest && tlb_entry_matches_gid(gtlb, gid))) {
+ gtlb->tlb_misc = FIELD_DP64(gtlb->tlb_misc, TLB_MISC, E, 0);
+ }
}
tlb_flush(env_cpu(env));
}
-void helper_invtlb_all_g(CPULoongArchState *env, uint32_t g)
+void helper_invtlb_all_g(CPULoongArchState *env, target_ulong info, uint32_t g,
+ uint32_t to_guest)
{
+ uint16_t gid = to_guest ? (info & 0xff) : get_tgid(env);
+
+ if (to_guest && env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
+ do_raise_exception(env, EXCCODE_IPE, GETPC());
+ }
+
+ to_guest |= env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST;
+
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
- LoongArchTLB *tlb = &env->tlb[i];
+ LoongArchTLB *tlb = &tlb_bank(env, to_guest)[i];
uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
- if (tlb_g == g) {
+ if (tlb_g == g && tlb_entry_matches_gid(tlb, gid)) {
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
}
}
tlb_flush(env_cpu(env));
}
-void helper_invtlb_all_asid(CPULoongArchState *env, target_ulong info)
+void helper_invtlb_all_asid(CPULoongArchState *env, target_ulong info,
+ uint32_t to_guest)
{
uint16_t asid = info & R_CSR_ASID_ASID_MASK;
+ uint16_t gid = to_guest ? ((info >> 16) & 0xff) : get_tgid(env);
+
+ if (to_guest && env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
+ do_raise_exception(env, EXCCODE_IPE, GETPC());
+ }
+
+ to_guest |= env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST;
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
- LoongArchTLB *tlb = &env->tlb[i];
+ LoongArchTLB *tlb = &tlb_bank(env, to_guest)[i];
uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
- if (!tlb_g && (tlb_asid == asid)) {
+ if (!tlb_g && tlb_asid == asid && tlb_entry_matches_gid(tlb, gid)) {
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
}
}
@@ -591,105 +647,159 @@ void helper_invtlb_all_asid(CPULoongArchState *env, target_ulong info)
}
void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info,
- target_ulong addr)
+ target_ulong addr, uint32_t to_guest)
{
- int asid = info & 0x3ff;
+ uint16_t asid = info & R_CSR_ASID_ASID_MASK;
+ uint16_t gid = to_guest ? ((info >> 16) & 0xff) : get_tgid(env);
LoongArchTLB *tlb;
- tlb_match func;
+ int index;
+
+ if (to_guest && env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
+ do_raise_exception(env, EXCCODE_IPE, GETPC());
+ }
+ to_guest |= env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST;
- func = tlb_match_asid;
- tlb = loongarch_tlb_search_cb(env, addr, asid, func);
+ tlb = loongarch_tlb_search_cb(env, addr, asid, tlb_match_asid,
+ to_guest, gid);
if (tlb) {
- invalidate_tlb(env, tlb - env->tlb);
+ index = tlb - tlb_bank(env, to_guest);
+ invalidate_tlb(env, index, to_guest);
}
}
-void helper_invtlb_page_asid_or_g(CPULoongArchState *env,
- target_ulong info, target_ulong addr)
+void helper_invtlb_page_asid_or_g(CPULoongArchState *env, target_ulong info,
+ target_ulong addr, uint32_t to_guest)
{
- int asid = info & 0x3ff;
+ uint16_t asid = info & R_CSR_ASID_ASID_MASK;
+ uint16_t gid = to_guest ? ((info >> 16) & 0xff) : get_tgid(env);
LoongArchTLB *tlb;
- tlb_match func;
+ int index;
+
+ if (to_guest && env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
+ do_raise_exception(env, EXCCODE_IPE, GETPC());
+ }
+
+ to_guest |= env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST;
- func = tlb_match_any;
- tlb = loongarch_tlb_search_cb(env, addr, asid, func);
+ tlb = loongarch_tlb_search_cb(env, addr, asid, tlb_match_any,
+ to_guest, gid);
if (tlb) {
- invalidate_tlb(env, tlb - env->tlb);
+ index = tlb - tlb_bank(env, to_guest);
+ invalidate_tlb(env, index, to_guest);
}
}
-static void ptw_update_tlb(CPULoongArchState *env, MMUContext *context)
+static void ptw_update_tlb(CPULoongArchState *env, MMUContext *context,
+ bool guest)
{
int index;
index = context->tlb_index;
if (index < 0) {
- index = get_tlb_random_index(env, context->addr, context->ps);
+ index = get_tlb_random_index(env, context->addr, context->ps, guest);
}
- update_tlb_index(env, context, index);
+ update_tlb_index(env, context, index, guest);
}
-bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr)
+TLBRet loongarch_map_host_address(CPULoongArchState *env, MMUContext *context,
+ MMUAccessType access_type, uintptr_t retaddr)
{
- CPULoongArchState *env = cpu_env(cs);
- hwaddr physical;
- int prot;
- MMUContext context;
TLBRet ret;
- /* Data access */
- context.addr = address;
- context.tlb_index = -1;
- ret = get_physical_address(env, &context, access_type, mmu_idx, 0);
- if (ret == TLBRET_MATCH && context.mmu_index != MMU_DA_IDX
- && cpu_has_ptw(env)) {
+ ret = loongarch_map_address(env, context, access_type, MMU_KERNEL_IDX,
+ false, false, retaddr);
+ return TLBRET_HOST_MATCH + ret;
+}
+
+static void loongarch_try_ptw(CPULoongArchState *env, MMUContext *context,
+ MMUAccessType access_type, int mmu_index,
+ TLBRet *status, bool guest, uintptr_t retaddr)
+{
+ if ((*status == TLBRET_MATCH || *status == TLBRET_HOST_MATCH) &&
+ context->mmu_index != MMU_DA_IDX &&
+ context->mmu_index != MMU_GUEST_DA_IDX && cpu_has_ptw(env, guest)) {
bool need_update = true;
- if (access_type == MMU_DATA_STORE && pte_dirty(context.pte)) {
+ if (access_type == MMU_DATA_STORE && pte_dirty(context->pte)) {
need_update = false;
- } else if (access_type != MMU_DATA_STORE && pte_access(context.pte)) {
+ } else if (access_type != MMU_DATA_STORE && pte_access(context->pte)) {
need_update = false;
-
- /*
- * FIXME: should context.prot be set without PAGE_WRITE with
- * pte_write(context.pte) && !pte_dirty(context.pte)??
- *
- * Otherwise there will be no loongarch_cpu_tlb_fill() function call
- * for MMU_DATA_STORE access_type in future since QEMU TLB with
- * prot PAGE_WRITE is added already
- */
}
if (need_update) {
- /* Need update bit A/D in PTE entry, take PTW again */
- ret = TLBRET_NOMATCH;
+ *status = (env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST &&
+ !guest) ?
+ TLBRET_HOST_NOMATCH : TLBRET_NOMATCH;
}
}
- if (ret != TLBRET_MATCH && cpu_has_ptw(env)) {
- /* Take HW PTW if TLB missed or bit P is zero */
- if (ret == TLBRET_NOMATCH || ret == TLBRET_INVALID) {
- ret = loongarch_ptw(env, &context, access_type, mmu_idx, 0);
- if (ret == TLBRET_MATCH) {
- ptw_update_tlb(env, &context);
+ if (*status != TLBRET_MATCH && *status != TLBRET_HOST_MATCH &&
+ cpu_has_ptw(env, guest)) {
+ if (*status == TLBRET_NOMATCH || *status == TLBRET_INVALID ||
+ *status == TLBRET_HOST_NOMATCH || *status == TLBRET_HOST_INVALID) {
+ *status = ((env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST &&
+ !guest) ?
+ TLBRET_HOST_MATCH : TLBRET_MATCH) +
+ loongarch_ptw(env, context, access_type, mmu_index, 0,
+ guest, retaddr);
+ if (*status == TLBRET_MATCH || *status == TLBRET_HOST_MATCH) {
+ ptw_update_tlb(env, context, guest);
}
- } else if (context.tlb_index >= 0) {
- invalidate_tlb(env, context.tlb_index);
+ } else if (context->tlb_index >= 0) {
+ invalidate_tlb(env, context->tlb_index, guest);
}
}
+}
+
+bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+ MMUAccessType access_type, int mmu_idx,
+ bool probe, uintptr_t retaddr)
+{
+ CPULoongArchState *env = cpu_env(cs);
+ bool guest = env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST;
+ MMUContext host_context;
+ hwaddr physical;
+ int prot, host_prot;
+ MMUContext context;
+ TLBRet ret;
+
+ /* Data access */
+ context.addr = address;
+ context.tlb_index = -1;
+ ret = get_physical_address(env, &context, access_type, mmu_idx, 0, retaddr);
+ loongarch_try_ptw(env, &context, access_type, mmu_idx, &ret, guest,
+ retaddr);
if (ret == TLBRET_MATCH) {
physical = context.physical;
prot = context.prot;
+ if (guest) {
+ host_context.addr = physical;
+ host_context.tlb_index = -1;
+ ret = loongarch_map_host_address(env, &host_context, access_type,
+ retaddr);
+ loongarch_try_ptw(env, &host_context, access_type, MMU_KERNEL_IDX,
+ &ret, false, retaddr);
+ if (ret != TLBRET_HOST_MATCH) {
+ if (probe) {
+ return false;
+ }
+ raise_mmu_exception(env, physical, access_type, ret);
+ cpu_loop_exit_restore(cs, retaddr);
+ return false;
+ }
+ physical = host_context.physical;
+ host_prot = host_context.prot;
+ prot &= host_prot;
+ }
tlb_set_page(cs, address & TARGET_PAGE_MASK,
physical & TARGET_PAGE_MASK, prot,
mmu_idx, TARGET_PAGE_SIZE);
qemu_log_mask(CPU_LOG_MMU,
"%s address=%" VADDR_PRIx " physical " HWADDR_FMT_plx
- " prot %d\n", __func__, address, physical, prot);
+ " prot %d guest %d\n", __func__, address, physical,
+ prot, is_guest_mmu_idx(mmu_idx));
return true;
} else {
qemu_log_mask(CPU_LOG_MMU,
@@ -718,6 +828,33 @@ static inline uint64_t loongarch_sanitize_hw_pte(CPULoongArchState *env,
return (pte & ~ppn_mask) | ((pte & ppn_mask) & palen_mask);
}
+hwaddr loongarch_get_host_address(CPULoongArchState *env, hwaddr gpa,
+ uintptr_t retaddr)
+{
+ MMUContext host_context;
+ TLBRet ret;
+ CPUTLBEntryFull *full;
+ int flags = probe_access_full_mmu(env, gpa, 1, MMU_DATA_LOAD,
+ MMU_KERNEL_IDX + MMU_GUEST_IDX, NULL, &full);
+ if (!(flags & TLB_INVALID_MASK)) {
+ return (full->phys_addr & TARGET_PAGE_MASK) | (gpa & ~TARGET_PAGE_MASK);
+ }
+
+ host_context.addr = gpa;
+ host_context.tlb_index = -1;
+ ret = loongarch_map_host_address(env, &host_context, MMU_DATA_LOAD,
+ retaddr);
+ loongarch_try_ptw(env, &host_context, MMU_DATA_LOAD, MMU_KERNEL_IDX,
+ &ret, false, retaddr);
+
+ if (ret != TLBRET_HOST_MATCH) {
+ raise_mmu_exception(env, gpa, MMU_DATA_LOAD, ret);
+ cpu_loop_exit_restore(env_cpu(env), retaddr);
+ }
+
+ return host_context.physical;
+}
+
target_ulong helper_lddir(CPULoongArchState *env, target_ulong base,
uint32_t level, uint32_t mem_idx)
{
@@ -751,10 +888,16 @@ target_ulong helper_lddir(CPULoongArchState *env, target_ulong base,
badvaddr = sys->CSR_TLBRBADV;
base = base & palen_mask;
- get_dir_base_width(env, &dir_base, &dir_width, level);
+ get_dir_base_width(env, &dir_base, &dir_width, level,
+ env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST);
index = (badvaddr >> dir_base) & ((1 << dir_width) - 1);
phys = base | index << 3;
- val = address_space_ldq_le(cs->as, phys, MEMTXATTRS_UNSPECIFIED, NULL);
+ val = address_space_ldq_le(
+ cs->as,
+ env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST ?
+ loongarch_get_host_address(env, phys, GETPC()) :
+ phys,
+ MEMTXATTRS_UNSPECIFIED, NULL);
return val & palen_mask;
}
@@ -789,7 +932,8 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd,
* Move HGLOBAL bit to GLOBAL bit.
*/
get_dir_base_width(env, &dir_base, &dir_width,
- FIELD_EX64(base, TLBENTRY, LEVEL));
+ FIELD_EX64(base, TLBENTRY, LEVEL),
+ env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST);
base = FIELD_DP64(base, TLBENTRY, LEVEL, 0);
base = FIELD_DP64(base, TLBENTRY, HUGE, 0);
@@ -823,8 +967,12 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd,
ptoffset0 = ptindex << 3;
ptoffset1 = (ptindex + 1) << 3;
phys = base | (odd ? ptoffset1 : ptoffset0);
- pte_raw = address_space_ldq_le(cs->as, phys,
- MEMTXATTRS_UNSPECIFIED, NULL);
+ pte_raw = address_space_ldq_le(
+ cs->as,
+ env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST ?
+ loongarch_get_host_address(env, phys, GETPC()) :
+ phys,
+ MEMTXATTRS_UNSPECIFIED, NULL);
tmp0 = loongarch_sanitize_hw_pte(env, pte_raw);
ps = ptbase;
}
@@ -840,9 +988,9 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd,
static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env,
MMUContext *context,
MMUAccessType access_type, int index,
- int mmu_idx)
+ int mmu_idx, bool guest)
{
- LoongArchTLB *tlb = &env->tlb[index];
+ LoongArchTLB *tlb = &tlb_bank(env, guest)[index];
uint8_t tlb_ps, n;
tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
@@ -850,19 +998,21 @@ static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env,
context->pte = n ? tlb->tlb_entry1 : tlb->tlb_entry0;
context->ps = tlb_ps;
context->tlb_index = index;
- return loongarch_check_pte(env, context, access_type, mmu_idx);
+ return loongarch_check_pte(env, context, access_type, mmu_idx, guest);
}
TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env,
MMUContext *context,
- MMUAccessType access_type, int mmu_idx)
+ MMUAccessType access_type, int mmu_idx,
+ bool guest)
{
int index, match;
- match = loongarch_tlb_search(env, context->addr, &index);
+ match = loongarch_tlb_search(env, context->addr, &index,
+ guest, get_tgid(env));
if (match) {
return loongarch_map_tlb_entry(env, context, access_type, index,
- mmu_idx);
+ mmu_idx, guest);
}
return TLBRET_NOMATCH;
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 11/14] target/loongarch: Translate LVZ guest CSR instructions
2026-08-26 10:30 [PATCH v4 00/14] target/loongarch: add LVZ support for TCG SignKirigami
` (9 preceding siblings ...)
2026-08-26 10:30 ` [PATCH v4 10/14] target/loongarch: Add LVZ MMU and PTW support SignKirigami
@ 2026-08-26 10:30 ` SignKirigami
2026-08-26 10:30 ` [PATCH v4 12/14] target/loongarch: Translate LVZ guest TLB instructions SignKirigami
` (2 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: SignKirigami @ 2026-08-26 10:30 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, xianglai li, SignKirigami, Hengyu Yu
Decode and translate GCSRRD, GCSRWR and GCSRXCHG, and select the host or
guest helper and CSR offset from one shared CSRInfo table.
Guest execution of sensitive CSRs raises GSPR without writing an artificial
value to the destination register. Host guest-CSR instructions reuse the
ordinary CSR translation implementation.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/csr.c | 4 +-
target/loongarch/csr.h | 4 +-
target/loongarch/disas.c | 8 +
target/loongarch/insns.decode | 6 +
.../tcg/insn_trans/trans_privileged.c.inc | 218 ++++++++++++++----
target/loongarch/tcg/translate.c | 6 +-
6 files changed, 193 insertions(+), 53 deletions(-)
diff --git a/target/loongarch/csr.c b/target/loongarch/csr.c
index fa018a4582..928ffeb193 100644
--- a/target/loongarch/csr.c
+++ b/target/loongarch/csr.c
@@ -10,7 +10,9 @@
[LOONGARCH_CSR_##NAME] = { \
.name = (stringify(NAME)), \
.offset = CSR_OFFSET(CSR_##NAME), \
- .flags = FL, .readfn = RD, .writefn = WR \
+ .flags = FL, \
+ .readfn[LOONGARCH_VM_LEVEL_HOST] = RD, \
+ .writefn[LOONGARCH_VM_LEVEL_HOST] = WR \
}
#define CSR_OFF_ARRAY_FLAGS(NAME, N, FL) \
diff --git a/target/loongarch/csr.h b/target/loongarch/csr.h
index 647695e248..50f06a359f 100644
--- a/target/loongarch/csr.h
+++ b/target/loongarch/csr.h
@@ -28,8 +28,8 @@ typedef struct {
const char *name;
int offset;
int flags;
- GenCSRFunc readfn;
- GenCSRFunc writefn;
+ GenCSRFunc readfn[LOONGARCH_VM_LEVELS];
+ GenCSRFunc writefn[LOONGARCH_VM_LEVELS];
} CSRInfo;
CSRInfo *get_csr(unsigned int csr_num);
diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c
index 3249ab7ac6..59d6a55c78 100644
--- a/target/loongarch/disas.c
+++ b/target/loongarch/disas.c
@@ -51,6 +51,7 @@ static const char * const csr_names[] = {
CSR_NAME(BADI),
CSR_NAME(EENTRY),
CSR_NAME(TLBIDX),
+ CSR_NAME(GTLBC),
CSR_NAME(TLBEHI),
CSR_NAME(TLBELO0),
CSR_NAME(TLBELO1),
@@ -87,6 +88,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 +703,9 @@ 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(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..4612af9433 100644
--- a/target/loongarch/insns.decode
+++ b/target/loongarch/insns.decode
@@ -493,6 +493,12 @@ 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
+}
+
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 abedb132b9..117f2c888c 100644
--- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
@@ -39,6 +39,9 @@ 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)
#else
@@ -54,8 +57,8 @@ static bool check_plv(DisasContext *ctx)
return false;
}
-static bool set_csr_trans_func(unsigned int csr_num, GenCSRRead readfn,
- GenCSRWrite writefn)
+static bool set_csr_trans_func(unsigned int csr_num, int guest,
+ GenCSRRead readfn, GenCSRWrite writefn)
{
CSRInfo *csr;
@@ -64,13 +67,17 @@ static bool set_csr_trans_func(unsigned int csr_num, GenCSRRead readfn,
return false;
}
- csr->readfn = (GenCSRFunc)readfn;
- csr->writefn = (GenCSRFunc)writefn;
+ csr->readfn[guest] = (GenCSRFunc)readfn;
+ csr->writefn[guest] = (GenCSRFunc)writefn;
return true;
}
#define SET_CSR_FUNC(NAME, read, write) \
- set_csr_trans_func(LOONGARCH_CSR_##NAME, read, write)
+ set_csr_trans_func(LOONGARCH_CSR_##NAME, \
+ LOONGARCH_VM_LEVEL_HOST, read, write)
+#define SET_GCSR_FUNC(NAME, read, write) \
+ set_csr_trans_func(LOONGARCH_CSR_##NAME, \
+ LOONGARCH_VM_LEVEL_GUEST, read, write)
void loongarch_csr_translate_init(void)
{
@@ -85,12 +92,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(STLBPS, NULL, gen_helper_gcsrwr_stlbps);
+ 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(PWCL, NULL, gen_helper_gcsrwr_pwcl);
+ SET_GCSR_FUNC(PWCH, NULL, gen_helper_gcsrwr_pwch);
+ 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)
+static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr,
+ int guest, bool write)
{
- if ((csr->flags & CSRFL_READONLY) && write) {
+ if ((guest == LOONGARCH_VM_LEVEL_HOST || ctx->guest_mode) &&
+ (csr->flags & CSRFL_READONLY) && write) {
return false;
}
if ((csr->flags & CSRFL_IO) && translator_io_start(&ctx->base)) {
@@ -101,117 +124,214 @@ static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write)
return true;
}
-static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
+static const CSRInfo *get_csr_for_level(unsigned int csr_num, int guest)
+{
+ const CSRInfo *csr = get_csr(csr_num);
+
+ if (csr && guest == LOONGARCH_VM_LEVEL_GUEST &&
+ (csr->flags & CSRFL_GUEST_INVALID)) {
+ return NULL;
+ }
+ return csr;
+}
+
+static bool trans_csrrd_impl(DisasContext *ctx, int rd, unsigned int csr_num,
+ int guest)
{
TCGv dest;
const CSRInfo *csr;
GenCSRRead readfn;
- tcg_target_long offset;
- if (check_plv(ctx)) {
- return false;
- }
- csr = get_csr(a->csr);
+ csr = get_csr_for_level(csr_num, guest);
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);
- readfn = (GenCSRRead)csr->readfn;
- if (readfn) {
- readfn(dest, tcg_env);
+ check_csr_flags(ctx, csr, guest, false);
+ if (guest == LOONGARCH_VM_LEVEL_GUEST &&
+ (csr->flags & CSRFL_GSPR)) {
+ if (ctx->guest_mode) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ return true;
+ }
+ dest = tcg_constant_tl(0);
} else {
- offset = get_csr_offset(csr, LOONGARCH_VM_LEVEL_HOST);
- tcg_gen_ld_tl(dest, tcg_env, offset);
+ dest = gpr_dst(ctx, rd, EXT_NONE);
+ readfn = (GenCSRRead)csr->readfn[guest];
+ if (readfn) {
+ readfn(dest, tcg_env);
+ } else {
+ tcg_gen_ld_tl(dest, tcg_env,
+ get_csr_offset(csr, guest));
+ }
}
}
- gen_set_gpr(a->rd, dest, EXT_NONE);
+ gen_set_gpr(rd, dest, EXT_NONE);
return true;
}
-static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
+static bool trans_csrwr_impl(DisasContext *ctx, int rd, unsigned int csr_num,
+ int guest)
{
TCGv dest, src1;
const CSRInfo *csr;
GenCSRWrite writefn;
tcg_target_long offset;
- if (check_plv(ctx)) {
- return false;
- }
- csr = get_csr(a->csr);
+ csr = get_csr_for_level(csr_num, guest);
if (csr == NULL) {
/* CSR is undefined: write ignored, read old_value as 0. */
- gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
+ gen_set_gpr(rd, tcg_constant_tl(0), EXT_NONE);
+ return true;
+ }
+ if (guest == LOONGARCH_VM_LEVEL_GUEST &&
+ (csr->flags & CSRFL_GSPR)) {
+ if (ctx->guest_mode) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ } else {
+ gen_set_gpr(rd, tcg_constant_tl(0), EXT_NONE);
+ }
return true;
}
- if (!check_csr_flags(ctx, csr, true)) {
+ if (!check_csr_flags(ctx, csr, guest, true)) {
/* CSR is readonly: trap. */
return false;
}
- src1 = gpr_src(ctx, a->rd, EXT_NONE);
- writefn = (GenCSRWrite)csr->writefn;
+ src1 = gpr_src(ctx, rd, EXT_NONE);
+ writefn = (GenCSRWrite)csr->writefn[guest];
if (writefn) {
- dest = gpr_dst(ctx, a->rd, EXT_NONE);
+ dest = gpr_dst(ctx, rd, EXT_NONE);
writefn(dest, tcg_env, src1);
} else {
dest = tcg_temp_new();
- offset = get_csr_offset(csr, LOONGARCH_VM_LEVEL_HOST);
+ offset = get_csr_offset(csr, guest);
tcg_gen_ld_tl(dest, tcg_env, offset);
tcg_gen_st_tl(src1, tcg_env, offset);
}
- gen_set_gpr(a->rd, dest, EXT_NONE);
+ gen_set_gpr(rd, dest, EXT_NONE);
return true;
}
-static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
+static bool trans_csrxchg_impl(DisasContext *ctx, int rd, int rj,
+ unsigned int csr_num, int guest)
{
TCGv src1, mask, oldv, newv, temp;
const CSRInfo *csr;
GenCSRWrite writefn;
tcg_target_long offset;
- if (check_plv(ctx)) {
- return false;
- }
- csr = get_csr(a->csr);
+ csr = get_csr_for_level(csr_num, guest);
if (csr == NULL) {
/* CSR is undefined: write ignored, read old_value as 0. */
- gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
+ gen_set_gpr(rd, tcg_constant_tl(0), EXT_NONE);
+ return true;
+ }
+ if (guest == LOONGARCH_VM_LEVEL_GUEST &&
+ (csr->flags & CSRFL_GSPR)) {
+ if (ctx->guest_mode) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ } else {
+ gen_set_gpr(rd, tcg_constant_tl(0), EXT_NONE);
+ }
return true;
}
- if (!check_csr_flags(ctx, csr, true)) {
+ if (!check_csr_flags(ctx, csr, guest, 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);
+ src1 = gpr_src(ctx, rd, EXT_NONE);
+ mask = gpr_src(ctx, rj, EXT_NONE);
oldv = tcg_temp_new();
newv = tcg_temp_new();
temp = tcg_temp_new();
+ offset = get_csr_offset(csr, guest);
- offset = get_csr_offset(csr, LOONGARCH_VM_LEVEL_HOST);
- tcg_gen_ld_tl(oldv, tcg_env, offset);
+ if (csr->readfn[guest]) {
+ GenCSRRead readfn = (GenCSRRead)csr->readfn[guest];
+ readfn(oldv, tcg_env);
+ } else {
+ tcg_gen_ld_tl(oldv, tcg_env, 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;
+ writefn = (GenCSRWrite)csr->writefn[guest];
if (writefn) {
writefn(oldv, tcg_env, newv);
} else {
tcg_gen_st_tl(newv, tcg_env, offset);
}
- gen_set_gpr(a->rd, oldv, EXT_NONE);
+ gen_set_gpr(rd, oldv, EXT_NONE);
return true;
}
+static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
+{
+ if (check_plv(ctx)) {
+ return false;
+ }
+ return trans_csrrd_impl(ctx, a->rd, a->csr, ctx->guest_mode);
+}
+
+static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
+{
+ if (check_plv(ctx)) {
+ return false;
+ }
+ return trans_csrwr_impl(ctx, a->rd, a->csr, ctx->guest_mode);
+}
+
+static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
+{
+ if (check_plv(ctx)) {
+ return false;
+ }
+ return trans_csrxchg_impl(ctx, a->rd, a->rj, a->csr,
+ ctx->guest_mode);
+}
+
+static bool trans_gcsrrd(DisasContext *ctx, arg_gcsrrd *a)
+{
+ if (check_plv(ctx) || !avail_LVZ(ctx)) {
+ return false;
+ }
+ if (ctx->guest_mode) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ return true;
+ }
+ return trans_csrrd_impl(ctx, a->rd, a->csr,
+ LOONGARCH_VM_LEVEL_GUEST);
+}
+
+static bool trans_gcsrwr(DisasContext *ctx, arg_gcsrwr *a)
+{
+ if (check_plv(ctx) || !avail_LVZ(ctx)) {
+ return false;
+ }
+ if (ctx->guest_mode) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ return true;
+ }
+ return trans_csrwr_impl(ctx, a->rd, a->csr,
+ LOONGARCH_VM_LEVEL_GUEST);
+}
+
+static bool trans_gcsrxchg(DisasContext *ctx, arg_gcsrxchg *a)
+{
+ if (check_plv(ctx) || !avail_LVZ(ctx)) {
+ return false;
+ }
+ if (ctx->guest_mode) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ return true;
+ }
+ return trans_csrxchg_impl(ctx, a->rd, a->rj, a->csr,
+ LOONGARCH_VM_LEVEL_GUEST);
+}
+
static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a,
void (*func)(TCGv, TCGv_ptr, TCGv))
{
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. */
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 12/14] target/loongarch: Translate LVZ guest TLB instructions
2026-08-26 10:30 [PATCH v4 00/14] target/loongarch: add LVZ support for TCG SignKirigami
` (10 preceding siblings ...)
2026-08-26 10:30 ` [PATCH v4 11/14] target/loongarch: Translate LVZ guest CSR instructions SignKirigami
@ 2026-08-26 10:30 ` SignKirigami
2026-08-26 10:30 ` [PATCH v4 13/14] target/loongarch: Translate LVZ guest traps and HVC SignKirigami
2026-08-26 10:30 ` [PATCH v4 14/14] fix(tcg/loongarch): strip flag bits in lddir/ldpte address calculation SignKirigami
13 siblings, 0 replies; 23+ messages in thread
From: SignKirigami @ 2026-08-26 10:30 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, xianglai li, SignKirigami, Hengyu Yu
Decode, disassemble and translate GTLBSRCH, GTLBRD, GTLBWR, GTLBFILL,
GTLBCLR and GTLBFLUSH for host LVZ execution.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/disas.c | 6 ++
target/loongarch/insns.decode | 7 +++
.../tcg/insn_trans/trans_privileged.c.inc | 60 +++++++++++++++++++
3 files changed, 73 insertions(+)
diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c
index 59d6a55c78..54c35b8d03 100644
--- a/target/loongarch/disas.c
+++ b/target/loongarch/disas.c
@@ -706,6 +706,12 @@ 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(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 4612af9433..cbcc30c31f 100644
--- a/target/loongarch/insns.decode
+++ b/target/loongarch/insns.decode
@@ -499,6 +499,13 @@ bgeu 0110 11 ................ ..... ..... @rr_offs16
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
+
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 117f2c888c..fb6318b438 100644
--- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
@@ -42,6 +42,12 @@ 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)
#else
@@ -384,6 +390,15 @@ static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *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_tlbsrch(tcg_env, tcg_constant_i32(1));
+ return true;
+}
+
static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a)
{
if (check_plv(ctx)) {
@@ -393,6 +408,15 @@ static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a)
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_tlbrd(tcg_env, tcg_constant_i32(1));
+ return true;
+}
+
static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
{
if (check_plv(ctx)) {
@@ -403,6 +427,15 @@ 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_tlbwr(tcg_env, tcg_constant_i32(1));
+ return true;
+}
+
static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a)
{
if (check_plv(ctx)) {
@@ -413,6 +446,15 @@ 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_tlbfill(tcg_env, tcg_constant_i32(1));
+ return true;
+}
+
static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a)
{
if (check_plv(ctx)) {
@@ -423,6 +465,15 @@ 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_tlbclr(tcg_env, tcg_constant_i32(1));
+ return true;
+}
+
static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a)
{
if (check_plv(ctx)) {
@@ -433,6 +484,15 @@ 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_tlbflush(tcg_env, tcg_constant_i32(1));
+ return true;
+}
+
static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
{
TCGv rj = gpr_src(ctx, a->rj, EXT_NONE);
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 13/14] target/loongarch: Translate LVZ guest traps and HVC
2026-08-26 10:30 [PATCH v4 00/14] target/loongarch: add LVZ support for TCG SignKirigami
` (11 preceding siblings ...)
2026-08-26 10:30 ` [PATCH v4 12/14] target/loongarch: Translate LVZ guest TLB instructions SignKirigami
@ 2026-08-26 10:30 ` SignKirigami
2026-08-26 10:30 ` [PATCH v4 14/14] fix(tcg/loongarch): strip flag bits in lddir/ldpte address calculation SignKirigami
13 siblings, 0 replies; 23+ messages in thread
From: SignKirigami @ 2026-08-26 10:30 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, xianglai li, SignKirigami, Hengyu Yu
Raise GSPR when a guest accesses IOCSR, debug or idle resources, and
decode HVC for guest PLV0.
Use the common exception path so trapping instructions do not manufacture
destination values or require a helper named after the exception cause.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/disas.c | 1 +
target/loongarch/insns.decode | 1 +
.../tcg/insn_trans/trans_privileged.c.inc | 32 +++++++++++++++++++
3 files changed, 34 insertions(+)
diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c
index 54c35b8d03..82b0bf868f 100644
--- a/target/loongarch/disas.c
+++ b/target/loongarch/disas.c
@@ -712,6 +712,7 @@ 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 cbcc30c31f..b40fabe9d7 100644
--- a/target/loongarch/insns.decode
+++ b/target/loongarch/insns.decode
@@ -505,6 +505,7 @@ 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
diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
index fb6318b438..2dedcadfbb 100644
--- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
@@ -48,6 +48,7 @@ GEN_FALSE_TRANS(gtlbsrch)
GEN_FALSE_TRANS(gtlbrd)
GEN_FALSE_TRANS(gtlbwr)
GEN_FALSE_TRANS(gtlbfill)
+GEN_FALSE_TRANS(hvcl)
#else
@@ -347,6 +348,10 @@ static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a,
if (check_plv(ctx)) {
return false;
}
+ if (ctx->guest_mode) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ return true;
+ }
func(dest, tcg_env, src1);
return true;
}
@@ -360,6 +365,10 @@ static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a,
if (check_plv(ctx)) {
return false;
}
+ if (ctx->guest_mode) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ return true;
+ }
func(tcg_env, addr, val);
return true;
}
@@ -624,6 +633,10 @@ static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a)
if (check_plv(ctx)) {
return false;
}
+ if (ctx->guest_mode) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ return true;
+ }
generate_exception(ctx, EXCCODE_DBP);
return true;
}
@@ -634,9 +647,28 @@ static bool trans_idle(DisasContext *ctx, arg_idle *a)
return false;
}
+ if (ctx->guest_mode) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ 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;
+ }
+ if (ctx->plv != MMU_PLV_KERNEL) {
+ generate_exception(ctx, EXCCODE_IPE);
+ return true;
+ }
+
+ generate_exception(ctx, EXCCODE_HVC);
+ return true;
+}
#endif
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 14/14] fix(tcg/loongarch): strip flag bits in lddir/ldpte address calculation
2026-08-26 10:30 [PATCH v4 00/14] target/loongarch: add LVZ support for TCG SignKirigami
` (12 preceding siblings ...)
2026-08-26 10:30 ` [PATCH v4 13/14] target/loongarch: Translate LVZ guest traps and HVC SignKirigami
@ 2026-08-26 10:30 ` SignKirigami
13 siblings, 0 replies; 23+ messages in thread
From: SignKirigami @ 2026-08-26 10:30 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, xianglai li, SignKirigami, numpy1314
helper_lddir and helper_ldpte used TARGET_PHYS_MASK (bits 0-47) when
extracting the base address from a directory entry. This retained flag
bits (V, D, PLV, MAT, etc.) at positions 0-11, which were then OR'd
with the next-level index, producing a wrong physical address whenever
the directory entry carried non-zero flags.
Fix both helpers to extract only the PPN field (bits 12-47) before
computing the address of the next-level page table entry.
Signed-off-by: numpy1314 <xzj13@mail.ustc.edu.cn>
---
target/loongarch/tcg/tlb_helper.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c
index 776626072d..d75e20baf0 100644
--- a/target/loongarch/tcg/tlb_helper.c
+++ b/target/loongarch/tcg/tlb_helper.c
@@ -887,7 +887,10 @@ target_ulong helper_lddir(CPULoongArchState *env, target_ulong base,
}
badvaddr = sys->CSR_TLBRBADV;
- base = base & palen_mask;
+ /* Extract only the PPN field (bits 12-47) from the directory entry.
+ * Flag bits (V, D, PLV, MAT, etc.) at positions 0-11 must not
+ * participate in the next-level address calculation. */
+ base = (target_ulong)FIELD_EX64(base, TLBENTRY_64, PPN) << TARGET_PAGE_BITS;
get_dir_base_width(env, &dir_base, &dir_width, level,
env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST);
index = (badvaddr >> dir_base) & ((1 << dir_width) - 1);
@@ -912,11 +915,9 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd,
uint64_t badv;
uint64_t ptbase = FIELD_EX64(sys->CSR_PWCL, CSR_PWCL, PTBASE);
uint64_t ptwidth = FIELD_EX64(sys->CSR_PWCL, CSR_PWCL, PTWIDTH);
- uint64_t palen_mask = loongarch_palen_mask(env);
uint64_t dir_base, dir_width;
uint8_t ps;
-
/*
* The parameter "base" has only two types,
* one is the page table base address,
@@ -959,9 +960,8 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd,
}
} else {
badv = sys->CSR_TLBRBADV;
-
- base = base & palen_mask;
-
+ /* Strip flag bits (0-11) from base before address calculation. */
+ base = (target_ulong)FIELD_EX64(base, TLBENTRY_64, PPN) << TARGET_PAGE_BITS;
ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1);
ptindex = ptindex & ~0x1; /* clear bit 0 */
ptoffset0 = ptindex << 3;
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v4 01/14] target/loongarch: Define LVZ CSR fields
2026-08-26 10:30 ` [PATCH v4 01/14] target/loongarch: Define LVZ CSR fields SignKirigami
@ 2026-08-31 2:27 ` Bibo Mao
0 siblings, 0 replies; 23+ messages in thread
From: Bibo Mao @ 2026-08-31 2:27 UTC (permalink / raw)
To: SignKirigami, qemu-devel; +Cc: xianglai li, Hengyu Yu
On 2026/8/26 下午6:30, SignKirigami wrote:
> Add LVZ CSR number and bit-field definitions for guest status,
> configuration, interrupt, timer and guest TLB registers, preparing
> the architectural constants needed by later LVZ CPU state, CSR and TCG
> implementation patches.
>
> Signed-off-by: SignKirigami <prcups@krgm.moe>
> Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
> ---
> target/loongarch/cpu-csr.h | 38 ++++++++++++++++++++++++++++++++++++++
> 1 file changed, 38 insertions(+)
>
> diff --git a/target/loongarch/cpu-csr.h b/target/loongarch/cpu-csr.h
> index d860417af2..438da43a79 100644
> --- a/target/loongarch/cpu-csr.h
> +++ b/target/loongarch/cpu-csr.h
> @@ -224,4 +224,42 @@ FIELD(CSR_DBG, ECODE, 16, 6)
> #define LOONGARCH_CSR_DERA 0x501 /* Debug era */
> #define LOONGARCH_CSR_DSAVE 0x502 /* Debug save */
>
> +/* LVZ (LoongArch Virtualization) CSRs */
> +#define LOONGARCH_CSR_GSTAT 0x50 /* Guest status */
> +FIELD(CSR_GSTAT, VM, 0, 1)
> +FIELD(CSR_GSTAT, PVM, 1, 1)
> +FIELD(CSR_GSTAT, GIDBIT, 4, 6)
> +FIELD(CSR_GSTAT, GID, 16, 8)
> +
> +#define LOONGARCH_CSR_GCFG 0x51 /* Guest config */
> +FIELD(CSR_GCFG, MATP, 0, 4)
> +FIELD(CSR_GCFG, MATC, 4, 2)
> +FIELD(CSR_GCFG, TOPIP, 6, 1)
> +FIELD(CSR_GCFG, TOPI, 7, 1)
> +FIELD(CSR_GCFG, TOTIP, 8, 1)
> +FIELD(CSR_GCFG, TOTI, 9, 1)
> +FIELD(CSR_GCFG, TOEP, 10, 1)
> +FIELD(CSR_GCFG, TOE, 11, 1)
> +FIELD(CSR_GCFG, TOPP, 12, 1)
> +FIELD(CSR_GCFG, TOP, 13, 1)
> +FIELD(CSR_GCFG, TOHUP, 14, 1)
> +FIELD(CSR_GCFG, TOHU, 15, 1)
> +FIELD(CSR_GCFG, TOCIP, 16, 4)
> +FIELD(CSR_GCFG, TOCI, 20, 2)
> +FIELD(CSR_GCFG, GPMP, 23, 1)
> +FIELD(CSR_GCFG, GPMNUM, 24, 3)
> +
> +#define LOONGARCH_CSR_GINTC 0x52 /* Guest interrupt config */
> +FIELD(CSR_GINTC, HWIS, 0, 8)
> +FIELD(CSR_GINTC, HWIP, 8, 8)
> +FIELD(CSR_GINTC, HWIC, 16, 8)
> +
> +#define LOONGARCH_CSR_GCNTC 0x53 /* Guest counter compensation */
> +
> +#define LOONGARCH_CSR_GTLBC 0x15 /* Guest TLB control */
> +FIELD(CSR_GTLBC, GMTLBSZ, 0, 6)
> +FIELD(CSR_GTLBC, USETGID, 12, 1)
> +FIELD(CSR_GTLBC, TOTI, 13, 1)
> +FIELD(CSR_GTLBC, TGID, 16, 8)
> +
CSR register LOONGARCH_CSR_GTLBC had better to put ahead of
LOONGARCH_CSR_GSTAT by CSR index order. The others looks good to me.
Reviewed-by: Bibo Mao <maobibo@loongson.cn>
> #endif /* LOONGARCH_CPU_CSR_H */
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v4 02/14] target/loongarch: Add LVZ CPU state definitions
2026-08-26 10:30 ` [PATCH v4 02/14] target/loongarch: Add LVZ CPU state definitions SignKirigami
@ 2026-08-31 2:56 ` Bibo Mao
0 siblings, 0 replies; 23+ messages in thread
From: Bibo Mao @ 2026-08-31 2:56 UTC (permalink / raw)
To: SignKirigami, qemu-devel; +Cc: xianglai li, Hengyu Yu
On 2026/8/26 下午6:30, SignKirigami wrote:
> Add host and guest system-state banks, LVZ exception and MMU indices,
> CPU and translation state, and helpers for selecting a VM-level CSR bank.
>
> Read host ERA and BADV explicitly when generating ELF core notes so the
> result does not depend on the currently selected guest state.
>
> Signed-off-by: SignKirigami <prcups@krgm.moe>
> Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
> ---
> target/loongarch/arch_dump.c | 6 ++--
> target/loongarch/cpu.c | 4 +--
> target/loongarch/cpu.h | 64 ++++++++++++++++++++++++++++++++++--
> target/loongarch/translate.h | 2 ++
> 4 files changed, 68 insertions(+), 8 deletions(-)
>
> diff --git a/target/loongarch/arch_dump.c b/target/loongarch/arch_dump.c
> index 9d84faef96..711b23257b 100644
> --- a/target/loongarch/arch_dump.c
> +++ b/target/loongarch/arch_dump.c
> @@ -116,7 +116,7 @@ int loongarch_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
> {
> struct loongarch_note note;
> CPULoongArchState *env = &LOONGARCH_CPU(cs)->env;
> - CPUSysState *sys = env_sys(env);
> + CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
> int ret, i;
yes, it need similar modification with loongarch_cpu_write_elf64_note().
can we keep local variable sys unchanged so that the change is smaller?
BTW I do not know which name is better between LOONGARCH_VM_LEVEL_HOST
and LOONGARCH_VM_LEVEL0. Such as in KVM mode, it is to dump CSR
resisters of guest VM; however in TCG mode, it is for host.
>
> loongarch_note_init(¬e, s, "CORE", 5, NT_PRSTATUS,
> @@ -127,8 +127,8 @@ int loongarch_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
> for (i = 0; i < 32; ++i) {
> note.prstatus.pr_reg.gpr[i] = cpu_to_dump64(s, env->gpr[i]);
> }
> - note.prstatus.pr_reg.csr_era = cpu_to_dump64(s, sys->CSR_ERA);
> - note.prstatus.pr_reg.csr_badv = cpu_to_dump64(s, sys->CSR_BADV);
> + note.prstatus.pr_reg.csr_era = cpu_to_dump64(s, host->CSR_ERA);
> + note.prstatus.pr_reg.csr_badv = cpu_to_dump64(s, host->CSR_BADV);
> ret = f(¬e, LOONGARCH_PRSTATUS_NOTE_SIZE, s);
> if (ret < 0) {
> return -1;
> diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
> index fb03424ffa..2f7692dc93 100644
> --- a/target/loongarch/cpu.c
> +++ b/target/loongarch/cpu.c
> @@ -282,7 +282,7 @@ static void loongarch_la464_initfn(Object *obj)
> uint32_t data = 0, field;
> int i;
>
> - set_sys_state(env, &env->sys_states[0]);
> + set_sys(env, LOONGARCH_VM_LEVEL_HOST);
> for (i = 0; i < 21; i++) {
> env->cpucfg[i] = 0x0;
> }
> @@ -412,7 +412,7 @@ static void loongarch_la132_initfn(Object *obj)
> uint32_t data = 0;
> int i;
>
> - set_sys_state(env, &env->sys_states[0]);
> + set_sys(env, LOONGARCH_VM_LEVEL_HOST);
> for (i = 0; i < 21; i++) {
> env->cpucfg[i] = 0x0;
> }
I still think that it need split to smaller version, such as
1. get_sys()/set_sys() and its user.
2. mmu_idx change for new added LVZ
3. others
> diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
> index ad30c73167..be982b7695 100644
> --- a/target/loongarch/cpu.h
> +++ b/target/loongarch/cpu.h
> @@ -20,6 +20,8 @@
> #include "cpu-csr.h"
> #include "cpu-qom.h"
>
> +#define CPU_INTERRUPT_GUEST CPU_INTERRUPT_TGT_EXT_0
> +
> #define FCSR0_M1 0x1f /* FCSR1 mask, Enables */
> #define FCSR0_M2 0x1f1f0000 /* FCSR2 mask, Cause and Flags */
> #define FCSR0_M3 0x300 /* FCSR3 mask, Round Mode */
> @@ -93,6 +95,10 @@ FIELD(FCSR0, CAUSE, 24, 5)
> #define EXCCODE_WPEM EXCODE(19, 1)
> #define EXCCODE_BTD EXCODE(20, 0)
> #define EXCCODE_BTE EXCODE(21, 0)
> +#define EXCCODE_GSPR EXCODE(22, 0)
> +#define EXCCODE_HVC EXCODE(23, 0)
> +#define EXCCODE_GCSC EXCODE(24, 0)
> +#define EXCCODE_GCHC EXCODE(25, 0)
The exception for LVZ is added however not used, it had better be added
in patch where it is actually used.
> #define EXCCODE_DBP EXCODE(26, 0) /* Reserved subcode used for debug */
>
> /* cpucfg[0] bits */
> @@ -255,6 +261,7 @@ FIELD(TLB_MISC, E, 0, 1)
> FIELD(TLB_MISC, ASID, 1, 10)
> FIELD(TLB_MISC, VPPN, 13, 35)
> FIELD(TLB_MISC, PS, 48, 6)
> +FIELD(TLB_MISC, GID, 54, 8)
>
> /*Msg interrupt registers */
> #define N_MSGIS 4
> @@ -314,6 +321,10 @@ typedef struct LoongArchBT {
> uint32_t ftop;
> } lbt_t;
>
> +#define LOONGARCH_VM_LEVEL_HOST 0
> +#define LOONGARCH_VM_LEVEL_GUEST 1
> +#define LOONGARCH_VM_LEVELS 2
> +
> #define CPU_VENDOR_LOONGSON "Loongson"
> #define CPU_MODEL_3A5000 "3A5000"
> #define CPU_MODEL_1C101 "1C101"
> @@ -375,10 +386,18 @@ typedef struct CPUSysState {
> uint64_t CSR_DBG;
> uint64_t CSR_DERA;
> uint64_t CSR_DSAVE;
> + uint64_t CSR_GSTAT;
> + uint64_t CSR_GCFG;
> + uint64_t CSR_GINTC;
> + uint64_t CSR_GCNTC;
> + uint64_t CSR_GTLBC;
I think this patch can move to patch 1.
> /* Msg interrupt registers */
> uint64_t CSR_MSGIS[N_MSGIS];
> uint64_t CSR_MSGIR;
> uint64_t CSR_MSGIE;
> +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY)
> + LoongArchTLB tlb[LOONGARCH_TLB_MAX];
> +#endif
It can be deleted in this patch.
> } CPUSysState;
>
> typedef struct CPUArchState {
> @@ -394,7 +413,7 @@ typedef struct CPUArchState {
> uint32_t pv_features;
> uint64_t vendor_id;
> uint64_t cpu_id;
> - CPUSysState sys_states[1];
> + CPUSysState sys_states[LOONGARCH_VM_LEVELS];
>
> struct {
> uint64_t guest_addr;
> @@ -419,6 +438,7 @@ typedef struct CPUArchState {
> uint32_t mp_state;
> #endif
> CPUSysState *sys_state;
> + bool vm_exit;
> } CPULoongArchState;
>
> typedef struct LoongArchCPUTopo {
> @@ -438,8 +458,10 @@ struct ArchCPU {
>
> CPULoongArchState env;
> QEMUTimer timer;
> + QEMUTimer guest_timer;
> uint32_t phy_id;
> OnOffAuto lbt;
> + OnOffAuto lvz;
> OnOffAuto pmu;
> OnOffAuto ptw;
> OnOffAuto lsx;
> @@ -484,15 +506,44 @@ struct LoongArchCPUClass {
> #define MMU_KERNEL_IDX MMU_PLV_KERNEL
> #define MMU_USER_IDX MMU_PLV_USER
> #define MMU_DA_IDX 4
> +#define MMU_GUEST_IDX 5
> +#define MMU_GUEST_DA_IDX 9
> +
> +static inline bool is_guest_mmu_idx(int mmu_idx)
> +{
> + return mmu_idx >= MMU_GUEST_IDX;
> +}
> +
> +static inline int mmu_idx_to_plv(int mmu_idx)
> +{
> + if (mmu_idx == MMU_DA_IDX || mmu_idx == MMU_GUEST_DA_IDX) {
> + return 0;
> + }
> + if (is_guest_mmu_idx(mmu_idx)) {
> + return mmu_idx - MMU_GUEST_IDX;
> + }
> + return mmu_idx;
> +}
>
> static inline CPUSysState *env_sys(CPULoongArchState *env)
> {
> return env->sys_state;
> }
>
> -static inline void set_sys_state(CPULoongArchState *env, CPUSysState *sys)
> +static inline CPUSysState *get_sys(CPULoongArchState *env, int vm_level)
> +{
> + return &env->sys_states[vm_level];
> +}
> +
> +static inline void set_sys(CPULoongArchState *env, int vm_level)
> {
> - env->sys_state = sys;
> + env->sys_state = get_sys(env, vm_level);
> +}
> +
> +static inline int env_vm_level(CPULoongArchState *env)
> +{
> + return env_sys(env) == get_sys(env, LOONGARCH_VM_LEVEL_GUEST) ?
> + LOONGARCH_VM_LEVEL_GUEST : LOONGARCH_VM_LEVEL_HOST;
> }
>
> static inline bool is_la64(CPULoongArchState *env)
> @@ -530,6 +581,13 @@ static inline void set_pc(CPULoongArchState *env, uint64_t value)
> #define HW_FLAGS_CRMD_PG R_CSR_CRMD_PG_MASK /* 0x10 */
> #define HW_FLAGS_VA32 0x20
> #define HW_FLAGS_EUEN_ASXE 0x40
> +#define HW_FLAGS_GUEST_MODE 0x80
> +
> +bool has_lvz_capability(CPULoongArchState *env);
> +bool will_return_to_guest(CPULoongArchState *env);
> +uint8_t get_gid(CPULoongArchState *env);
> +uint8_t get_tgid(CPULoongArchState *env);
> +void trigger_vm_exit(CPULoongArchState *env);
There is only function declaration, no function implementation and
called. I think it can be removed in this patch.
In general it requires that every single patch can pass to compile and work.
Regards
Bibo Mao
>
> #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU
>
> diff --git a/target/loongarch/translate.h b/target/loongarch/translate.h
> index 8aa8325dc6..257c3bbddd 100644
> --- a/target/loongarch/translate.h
> +++ b/target/loongarch/translate.h
> @@ -24,6 +24,7 @@
> #define avail_FP(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, FP))
> #define avail_FP_SP(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, FP_SP))
> #define avail_FP_DP(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, FP_DP))
> +#define avail_LVZ(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, LVZ))
> #define avail_LSPW(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, LSPW))
> #define avail_LAM(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, LAM))
> #define avail_LAM_BH(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, LAM_BH))
> @@ -66,6 +67,7 @@ typedef struct DisasContext {
> TCGv zero;
> bool la64; /* LoongArch64 mode */
> bool va32; /* 32-bit virtual address */
> + bool guest_mode;
> uint32_t cpucfg1;
> uint32_t cpucfg2;
> uint32_t cpucfg3;
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v4 03/14] target/loongarch: Add LVZ CSR metadata
2026-08-26 10:30 ` [PATCH v4 03/14] target/loongarch: Add LVZ CSR metadata SignKirigami
@ 2026-09-01 7:08 ` Bibo Mao
2026-09-02 3:09 ` SignKirigami
0 siblings, 1 reply; 23+ messages in thread
From: Bibo Mao @ 2026-09-01 7:08 UTC (permalink / raw)
To: SignKirigami, qemu-devel; +Cc: xianglai li, Hengyu Yu
On 2026/8/26 下午6:30, SignKirigami wrote:
> Add LVZ host and guest CSR access metadata to the existing CSR table. Keep
> one shared register description and calculate the host or guest bank
> offset at translation time instead of duplicating CSRInfo entries.
>
> Mark guest read-only, sensitive and unavailable CSRs for later LVZ
> instruction translation.
>
> Signed-off-by: SignKirigami <prcups@krgm.moe>
> Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
> ---
> target/loongarch/cpu.c | 3 +-
> target/loongarch/csr.c | 108 +++++++++---------
> target/loongarch/csr.h | 2 +
> .../tcg/insn_trans/trans_extra.c.inc | 2 +-
> .../tcg/insn_trans/trans_privileged.c.inc | 6 +-
> 5 files changed, 65 insertions(+), 56 deletions(-)
>
> diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
> index 2f7692dc93..0f6a211949 100644
> --- a/target/loongarch/cpu.c
> +++ b/target/loongarch/cpu.c
> @@ -783,7 +783,8 @@ static void loongarch_cpu_dump_csr(CPUState *cs, FILE *f)
> qemu_fprintf(f, " CSR%03d:", col);
> }
>
> - addr = (void *)env + get_csr_offset(csr_info, 0);
> + addr = (void *)env + get_csr_offset(csr_info,
> + LOONGARCH_VM_LEVEL_HOST);
> qemu_fprintf(f, " %s ", csr_info->name);
> len = strlen(csr_info->name);
> for (; len < 6; len++) {
> diff --git a/target/loongarch/csr.c b/target/loongarch/csr.c
> index 309b826ca9..fa018a4582 100644
> --- a/target/loongarch/csr.c
> +++ b/target/loongarch/csr.c
> @@ -13,15 +13,16 @@
> .flags = FL, .readfn = RD, .writefn = WR \
> }
>
> -#define CSR_OFF_ARRAY(NAME, N) \
> +#define CSR_OFF_ARRAY_FLAGS(NAME, N, FL) \
> [LOONGARCH_CSR_##NAME(N)] = { \
> .name = (stringify(NAME##N)), \
> .offset = CSR_OFFSET(CSR_##NAME[N]), \
> - .flags = CSRFL_BASIC, .readfn = NULL, .writefn = NULL \
> + .flags = FL \
> }
>
> #define CSR_OFF_FLAGS(NAME, FL) CSR_OFF_FUNCS(NAME, FL, NULL, NULL)
> #define CSR_OFF(NAME) CSR_OFF_FLAGS(NAME, CSRFL_BASIC)
> +#define CSR_OFF_ARRAY(NAME, N) CSR_OFF_ARRAY_FLAGS(NAME, N, CSRFL_BASIC)
>
> static CSRInfo csr_info[] = {
> CSR_OFF_FLAGS(CRMD, CSRFL_EXITTB),
> @@ -35,6 +36,7 @@ static CSRInfo csr_info[] = {
> CSR_OFF_FLAGS(BADI, CSRFL_READONLY),
> CSR_OFF(EENTRY),
> CSR_OFF(TLBIDX),
> + CSR_OFF_FLAGS(GTLBC, CSRFL_GSPR),
> CSR_OFF(TLBEHI),
> CSR_OFF(TLBELO0),
> CSR_OFF(TLBELO1),
> @@ -71,9 +73,13 @@ static CSRInfo csr_info[] = {
> CSR_OFF_FLAGS(TVAL, CSRFL_READONLY | CSRFL_IO),
> CSR_OFF(CNTC),
> CSR_OFF_FLAGS(TICLR, CSRFL_IO),
Should previous CSR_OFF_FLAGS(GTLBC, CSRFL_GSPR) be moved to here? since
all these registers relative with LVZ.
> + CSR_OFF_FLAGS(GSTAT, CSRFL_GSPR),
> + CSR_OFF_FLAGS(GCFG, CSRFL_GSPR),
> + CSR_OFF_FLAGS(GINTC, CSRFL_IO | CSRFL_GSPR),
> + CSR_OFF_FLAGS(GCNTC, CSRFL_GSPR),
> CSR_OFF(LLBCTL),
> - CSR_OFF(IMPCTL1),
> - CSR_OFF(IMPCTL2),
> + CSR_OFF_FLAGS(IMPCTL1, CSRFL_GSPR),
> + CSR_OFF_FLAGS(IMPCTL2, CSRFL_GSPR),
It change the old flags, previous its flag is CSRFL_BASIC, and not it is
replaced with CSRFL_GSPR. How about CSRFL_BASIC | CSRFL_GSPR and keep
old flag still on?
> CSR_OFF(TLBRENTRY),
> CSR_OFF(TLBRBADV),
> CSR_OFF(TLBRERA),
> @@ -82,57 +88,57 @@ static CSRInfo csr_info[] = {
> 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_FLAGS(MERRCTL, CSRFL_GSPR),
> + CSR_OFF_FLAGS(MERRINFO1, CSRFL_GSPR),
> + CSR_OFF_FLAGS(MERRINFO2, CSRFL_GSPR),
> + CSR_OFF_FLAGS(MERRENTRY, CSRFL_GSPR),
> + CSR_OFF_FLAGS(MERRERA, CSRFL_GSPR),
> + CSR_OFF_FLAGS(MERRSAVE, CSRFL_GSPR),
> + CSR_OFF_FLAGS(CTAG, CSRFL_GSPR),
> CSR_OFF_ARRAY(DMW, 0),
> CSR_OFF_ARRAY(DMW, 1),
> CSR_OFF_ARRAY(DMW, 2),
> CSR_OFF_ARRAY(DMW, 3),
> - CSR_OFF_ARRAY(PERFCTRL, 0),
> - CSR_OFF_ARRAY(PERFCNTR, 0),
> - CSR_OFF_ARRAY(PERFCTRL, 1),
> - CSR_OFF_ARRAY(PERFCNTR, 1),
> - CSR_OFF_ARRAY(PERFCTRL, 2),
> - CSR_OFF_ARRAY(PERFCNTR, 2),
> - CSR_OFF_ARRAY(PERFCTRL, 3),
> - CSR_OFF_ARRAY(PERFCNTR, 3),
> - CSR_OFF_ARRAY(PERFCTRL, 4),
> - CSR_OFF_ARRAY(PERFCNTR, 4),
> - CSR_OFF_ARRAY(PERFCTRL, 5),
> - CSR_OFF_ARRAY(PERFCNTR, 5),
> - CSR_OFF_ARRAY(PERFCTRL, 6),
> - CSR_OFF_ARRAY(PERFCNTR, 6),
> - CSR_OFF_ARRAY(PERFCTRL, 7),
> - CSR_OFF_ARRAY(PERFCNTR, 7),
> - CSR_OFF_ARRAY(PERFCTRL, 8),
> - CSR_OFF_ARRAY(PERFCNTR, 8),
> - CSR_OFF_ARRAY(PERFCTRL, 9),
> - CSR_OFF_ARRAY(PERFCNTR, 9),
> - CSR_OFF_ARRAY(PERFCTRL, 10),
> - CSR_OFF_ARRAY(PERFCNTR, 10),
> - CSR_OFF_ARRAY(PERFCTRL, 11),
> - CSR_OFF_ARRAY(PERFCNTR, 11),
> - CSR_OFF_ARRAY(PERFCTRL, 12),
> - CSR_OFF_ARRAY(PERFCNTR, 12),
> - CSR_OFF_ARRAY(PERFCTRL, 13),
> - CSR_OFF_ARRAY(PERFCNTR, 13),
> - CSR_OFF_ARRAY(PERFCTRL, 14),
> - CSR_OFF_ARRAY(PERFCNTR, 14),
> - CSR_OFF_ARRAY(PERFCTRL, 15),
> - CSR_OFF_ARRAY(PERFCNTR, 15),
> - CSR_OFF(DBG),
> - CSR_OFF(DERA),
> - CSR_OFF(DSAVE),
> - CSR_OFF_ARRAY(MSGIS, 0),
> - CSR_OFF_ARRAY(MSGIS, 1),
> - CSR_OFF_ARRAY(MSGIS, 2),
> - CSR_OFF_ARRAY(MSGIS, 3),
> - CSR_OFF(MSGIR),
> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 0, CSRFL_GUEST_INVALID),
what is different between flag CSRFL_GSPR and CSRFL_GUEST_INVALID?
With CSR register access instruction, on host value zero is return if it
is not implemented. On guest mode, I think that there should be GSPR
exception and trap to host and let hypervisor to handle this if it is
not implemented. There is no much meaning to return zero and does not
trap to host.
> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 0, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 1, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 1, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 2, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 2, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 3, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 3, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 4, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 4, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 5, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 5, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 6, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 6, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 7, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 7, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 8, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 8, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 9, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 9, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 10, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 10, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 11, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 11, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 12, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 12, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 13, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 13, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 14, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 14, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 15, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 15, CSRFL_GUEST_INVALID),
> + CSR_OFF_FLAGS(DBG, CSRFL_GSPR),
> + CSR_OFF_FLAGS(DERA, CSRFL_GSPR),
> + CSR_OFF_FLAGS(DSAVE, CSRFL_GSPR),
> + CSR_OFF_ARRAY_FLAGS(MSGIS, 0, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(MSGIS, 1, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(MSGIS, 2, CSRFL_GUEST_INVALID),
> + CSR_OFF_ARRAY_FLAGS(MSGIS, 3, CSRFL_GUEST_INVALID),
> + CSR_OFF_FLAGS(MSGIR, CSRFL_GUEST_INVALID),
> };
>
> CSRInfo *get_csr(unsigned int csr_num)
> diff --git a/target/loongarch/csr.h b/target/loongarch/csr.h
> index c2b6b882bc..647695e248 100644
> --- a/target/loongarch/csr.h
> +++ b/target/loongarch/csr.h
> @@ -20,6 +20,8 @@ enum {
> CSRFL_IO = (1 << 2),
> CSRFL_UNUSED = (1 << 3),
> CSRFL_BASIC = (1 << 4),
> + CSRFL_GSPR = (1 << 5),
> + CSRFL_GUEST_INVALID = (1 << 6),
> };
>
> typedef struct {
> diff --git a/target/loongarch/tcg/insn_trans/trans_extra.c.inc b/target/loongarch/tcg/insn_trans/trans_extra.c.inc
> index 655dce329e..e69384497f 100644
> --- a/target/loongarch/tcg/insn_trans/trans_extra.c.inc
> +++ b/target/loongarch/tcg/insn_trans/trans_extra.c.inc
> @@ -55,7 +55,7 @@ static bool gen_rdtime(DisasContext *ctx, arg_rr *a,
> tcg_gen_sextract_tl(dst1, dst1, high ? 32 : 0, 32);
> }
>
> - offset = CPU_CSR_OFFSET(CSR_TID, 0);
> + offset = CPU_CSR_OFFSET(CSR_TID, LOONGARCH_VM_LEVEL_HOST);
This should be another small patch.
Regards
Bibo Mao
> tcg_gen_ld_i64(dst2, tcg_env, offset);
>
> return true;
> diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
> index 6728ce5ec9..bcfc977990 100644
> --- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
> +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
> @@ -122,7 +122,7 @@ static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
> if (readfn) {
> readfn(dest, tcg_env);
> } else {
> - offset = get_csr_offset(csr, 0);
> + offset = get_csr_offset(csr, LOONGARCH_VM_LEVEL_HOST);
> tcg_gen_ld_tl(dest, tcg_env, offset);
> }
> }
> @@ -157,7 +157,7 @@ static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
> writefn(dest, tcg_env, src1);
> } else {
> dest = tcg_temp_new();
> - offset = get_csr_offset(csr, 0);
> + offset = get_csr_offset(csr, LOONGARCH_VM_LEVEL_HOST);
> tcg_gen_ld_tl(dest, tcg_env, offset);
> tcg_gen_st_tl(src1, tcg_env, offset);
> }
> @@ -196,7 +196,7 @@ static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
> newv = tcg_temp_new();
> temp = tcg_temp_new();
>
> - offset = get_csr_offset(csr, 0);
> + offset = get_csr_offset(csr, LOONGARCH_VM_LEVEL_HOST);
> tcg_gen_ld_tl(oldv, tcg_env, offset);
> tcg_gen_and_tl(newv, src1, mask);
> tcg_gen_andc_tl(temp, oldv, mask);
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v4 03/14] target/loongarch: Add LVZ CSR metadata
2026-09-01 7:08 ` Bibo Mao
@ 2026-09-02 3:09 ` SignKirigami
0 siblings, 0 replies; 23+ messages in thread
From: SignKirigami @ 2026-09-02 3:09 UTC (permalink / raw)
To: Bibo Mao, qemu-devel; +Cc: xianglai li, Hengyu Yu
于 2026年9月1日 GMT+08:00 15:08:59,Bibo Mao <maobibo@loongson.cn> 写道:
>
>
>On 2026/8/26 下午6:30, SignKirigami wrote:
>> Add LVZ host and guest CSR access metadata to the existing CSR table. Keep
>> one shared register description and calculate the host or guest bank
>> offset at translation time instead of duplicating CSRInfo entries.
>>
>> Mark guest read-only, sensitive and unavailable CSRs for later LVZ
>> instruction translation.
>>
>> Signed-off-by: SignKirigami <prcups@krgm.moe>
>> Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
>> ---
>> target/loongarch/cpu.c | 3 +-
>> target/loongarch/csr.c | 108 +++++++++---------
>> target/loongarch/csr.h | 2 +
>> .../tcg/insn_trans/trans_extra.c.inc | 2 +-
>> .../tcg/insn_trans/trans_privileged.c.inc | 6 +-
>> 5 files changed, 65 insertions(+), 56 deletions(-)
>>
>> diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
>> index 2f7692dc93..0f6a211949 100644
>> --- a/target/loongarch/cpu.c
>> +++ b/target/loongarch/cpu.c
>> @@ -783,7 +783,8 @@ static void loongarch_cpu_dump_csr(CPUState *cs, FILE *f)
>> qemu_fprintf(f, " CSR%03d:", col);
>> }
>> - addr = (void *)env + get_csr_offset(csr_info, 0);
>> + addr = (void *)env + get_csr_offset(csr_info,
>> + LOONGARCH_VM_LEVEL_HOST);
>> qemu_fprintf(f, " %s ", csr_info->name);
>> len = strlen(csr_info->name);
>> for (; len < 6; len++) {
>> diff --git a/target/loongarch/csr.c b/target/loongarch/csr.c
>> index 309b826ca9..fa018a4582 100644
>> --- a/target/loongarch/csr.c
>> +++ b/target/loongarch/csr.c
>> @@ -13,15 +13,16 @@
>> .flags = FL, .readfn = RD, .writefn = WR \
>> }
>> -#define CSR_OFF_ARRAY(NAME, N) \
>> +#define CSR_OFF_ARRAY_FLAGS(NAME, N, FL) \
>> [LOONGARCH_CSR_##NAME(N)] = { \
>> .name = (stringify(NAME##N)), \
>> .offset = CSR_OFFSET(CSR_##NAME[N]), \
>> - .flags = CSRFL_BASIC, .readfn = NULL, .writefn = NULL \
>> + .flags = FL \
>> }
>> #define CSR_OFF_FLAGS(NAME, FL) CSR_OFF_FUNCS(NAME, FL, NULL, NULL)
>> #define CSR_OFF(NAME) CSR_OFF_FLAGS(NAME, CSRFL_BASIC)
>> +#define CSR_OFF_ARRAY(NAME, N) CSR_OFF_ARRAY_FLAGS(NAME, N, CSRFL_BASIC)
>> static CSRInfo csr_info[] = {
>> CSR_OFF_FLAGS(CRMD, CSRFL_EXITTB),
>> @@ -35,6 +36,7 @@ static CSRInfo csr_info[] = {
>> CSR_OFF_FLAGS(BADI, CSRFL_READONLY),
>> CSR_OFF(EENTRY),
>> CSR_OFF(TLBIDX),
>> + CSR_OFF_FLAGS(GTLBC, CSRFL_GSPR),
>> CSR_OFF(TLBEHI),
>> CSR_OFF(TLBELO0),
>> CSR_OFF(TLBELO1),
>> @@ -71,9 +73,13 @@ static CSRInfo csr_info[] = {
>> CSR_OFF_FLAGS(TVAL, CSRFL_READONLY | CSRFL_IO),
>> CSR_OFF(CNTC),
>> CSR_OFF_FLAGS(TICLR, CSRFL_IO),
>Should previous CSR_OFF_FLAGS(GTLBC, CSRFL_GSPR) be moved to here? since all these registers relative with LVZ.
>> + CSR_OFF_FLAGS(GSTAT, CSRFL_GSPR),
>> + CSR_OFF_FLAGS(GCFG, CSRFL_GSPR),
>> + CSR_OFF_FLAGS(GINTC, CSRFL_IO | CSRFL_GSPR),
>> + CSR_OFF_FLAGS(GCNTC, CSRFL_GSPR),
>> CSR_OFF(LLBCTL),
>> - CSR_OFF(IMPCTL1),
>> - CSR_OFF(IMPCTL2),
>> + CSR_OFF_FLAGS(IMPCTL1, CSRFL_GSPR),
>> + CSR_OFF_FLAGS(IMPCTL2, CSRFL_GSPR),
>It change the old flags, previous its flag is CSRFL_BASIC, and not it is replaced with CSRFL_GSPR. How about CSRFL_BASIC | CSRFL_GSPR and keep old flag still on?
CSRFL_BASIC is for "no flag but valid", as new get_csr sees csr_info with flag 0 as invalid,
no other code uses this flag so it is ok to delete this flag when another flag exists.
Regards
SignKirigami
>
>> CSR_OFF(TLBRENTRY),
>> CSR_OFF(TLBRBADV),
>> CSR_OFF(TLBRERA),
>> @@ -82,57 +88,57 @@ static CSRInfo csr_info[] = {
>> 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_FLAGS(MERRCTL, CSRFL_GSPR),
>> + CSR_OFF_FLAGS(MERRINFO1, CSRFL_GSPR),
>> + CSR_OFF_FLAGS(MERRINFO2, CSRFL_GSPR),
>> + CSR_OFF_FLAGS(MERRENTRY, CSRFL_GSPR),
>> + CSR_OFF_FLAGS(MERRERA, CSRFL_GSPR),
>> + CSR_OFF_FLAGS(MERRSAVE, CSRFL_GSPR),
>> + CSR_OFF_FLAGS(CTAG, CSRFL_GSPR),
>> CSR_OFF_ARRAY(DMW, 0),
>> CSR_OFF_ARRAY(DMW, 1),
>> CSR_OFF_ARRAY(DMW, 2),
>> CSR_OFF_ARRAY(DMW, 3),
>> - CSR_OFF_ARRAY(PERFCTRL, 0),
>> - CSR_OFF_ARRAY(PERFCNTR, 0),
>> - CSR_OFF_ARRAY(PERFCTRL, 1),
>> - CSR_OFF_ARRAY(PERFCNTR, 1),
>> - CSR_OFF_ARRAY(PERFCTRL, 2),
>> - CSR_OFF_ARRAY(PERFCNTR, 2),
>> - CSR_OFF_ARRAY(PERFCTRL, 3),
>> - CSR_OFF_ARRAY(PERFCNTR, 3),
>> - CSR_OFF_ARRAY(PERFCTRL, 4),
>> - CSR_OFF_ARRAY(PERFCNTR, 4),
>> - CSR_OFF_ARRAY(PERFCTRL, 5),
>> - CSR_OFF_ARRAY(PERFCNTR, 5),
>> - CSR_OFF_ARRAY(PERFCTRL, 6),
>> - CSR_OFF_ARRAY(PERFCNTR, 6),
>> - CSR_OFF_ARRAY(PERFCTRL, 7),
>> - CSR_OFF_ARRAY(PERFCNTR, 7),
>> - CSR_OFF_ARRAY(PERFCTRL, 8),
>> - CSR_OFF_ARRAY(PERFCNTR, 8),
>> - CSR_OFF_ARRAY(PERFCTRL, 9),
>> - CSR_OFF_ARRAY(PERFCNTR, 9),
>> - CSR_OFF_ARRAY(PERFCTRL, 10),
>> - CSR_OFF_ARRAY(PERFCNTR, 10),
>> - CSR_OFF_ARRAY(PERFCTRL, 11),
>> - CSR_OFF_ARRAY(PERFCNTR, 11),
>> - CSR_OFF_ARRAY(PERFCTRL, 12),
>> - CSR_OFF_ARRAY(PERFCNTR, 12),
>> - CSR_OFF_ARRAY(PERFCTRL, 13),
>> - CSR_OFF_ARRAY(PERFCNTR, 13),
>> - CSR_OFF_ARRAY(PERFCTRL, 14),
>> - CSR_OFF_ARRAY(PERFCNTR, 14),
>> - CSR_OFF_ARRAY(PERFCTRL, 15),
>> - CSR_OFF_ARRAY(PERFCNTR, 15),
>> - CSR_OFF(DBG),
>> - CSR_OFF(DERA),
>> - CSR_OFF(DSAVE),
>> - CSR_OFF_ARRAY(MSGIS, 0),
>> - CSR_OFF_ARRAY(MSGIS, 1),
>> - CSR_OFF_ARRAY(MSGIS, 2),
>> - CSR_OFF_ARRAY(MSGIS, 3),
>> - CSR_OFF(MSGIR),
>> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 0, CSRFL_GUEST_INVALID),
>what is different between flag CSRFL_GSPR and CSRFL_GUEST_INVALID?
>
>With CSR register access instruction, on host value zero is return if it is not implemented. On guest mode, I think that there should be GSPR exception and trap to host and let hypervisor to handle this if it is not implemented. There is no much meaning to return zero and does not trap to host.
>
>> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 0, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 1, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 1, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 2, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 2, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 3, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 3, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 4, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 4, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 5, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 5, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 6, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 6, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 7, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 7, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 8, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 8, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 9, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 9, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 10, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 10, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 11, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 11, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 12, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 12, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 13, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 13, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 14, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 14, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCTRL, 15, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(PERFCNTR, 15, CSRFL_GUEST_INVALID),
>> + CSR_OFF_FLAGS(DBG, CSRFL_GSPR),
>> + CSR_OFF_FLAGS(DERA, CSRFL_GSPR),
>> + CSR_OFF_FLAGS(DSAVE, CSRFL_GSPR),
>> + CSR_OFF_ARRAY_FLAGS(MSGIS, 0, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(MSGIS, 1, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(MSGIS, 2, CSRFL_GUEST_INVALID),
>> + CSR_OFF_ARRAY_FLAGS(MSGIS, 3, CSRFL_GUEST_INVALID),
>> + CSR_OFF_FLAGS(MSGIR, CSRFL_GUEST_INVALID),
>> };
>> CSRInfo *get_csr(unsigned int csr_num)
>> diff --git a/target/loongarch/csr.h b/target/loongarch/csr.h
>> index c2b6b882bc..647695e248 100644
>> --- a/target/loongarch/csr.h
>> +++ b/target/loongarch/csr.h
>> @@ -20,6 +20,8 @@ enum {
>> CSRFL_IO = (1 << 2),
>> CSRFL_UNUSED = (1 << 3),
>> CSRFL_BASIC = (1 << 4),
>> + CSRFL_GSPR = (1 << 5),
>> + CSRFL_GUEST_INVALID = (1 << 6),
>> };
>> typedef struct {
>> diff --git a/target/loongarch/tcg/insn_trans/trans_extra.c.inc b/target/loongarch/tcg/insn_trans/trans_extra.c.inc
>> index 655dce329e..e69384497f 100644
>> --- a/target/loongarch/tcg/insn_trans/trans_extra.c.inc
>> +++ b/target/loongarch/tcg/insn_trans/trans_extra.c.inc
>> @@ -55,7 +55,7 @@ static bool gen_rdtime(DisasContext *ctx, arg_rr *a,
>> tcg_gen_sextract_tl(dst1, dst1, high ? 32 : 0, 32);
>> }
>> - offset = CPU_CSR_OFFSET(CSR_TID, 0);
>> + offset = CPU_CSR_OFFSET(CSR_TID, LOONGARCH_VM_LEVEL_HOST);
>This should be another small patch.
>
>Regards
>Bibo Mao
>> tcg_gen_ld_i64(dst2, tcg_env, offset);
>> return true;
>> diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
>> index 6728ce5ec9..bcfc977990 100644
>> --- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
>> +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
>> @@ -122,7 +122,7 @@ static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
>> if (readfn) {
>> readfn(dest, tcg_env);
>> } else {
>> - offset = get_csr_offset(csr, 0);
>> + offset = get_csr_offset(csr, LOONGARCH_VM_LEVEL_HOST);
>> tcg_gen_ld_tl(dest, tcg_env, offset);
>> }
>> }
>> @@ -157,7 +157,7 @@ static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
>> writefn(dest, tcg_env, src1);
>> } else {
>> dest = tcg_temp_new();
>> - offset = get_csr_offset(csr, 0);
>> + offset = get_csr_offset(csr, LOONGARCH_VM_LEVEL_HOST);
>> tcg_gen_ld_tl(dest, tcg_env, offset);
>> tcg_gen_st_tl(src1, tcg_env, offset);
>> }
>> @@ -196,7 +196,7 @@ static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
>> newv = tcg_temp_new();
>> temp = tcg_temp_new();
>> - offset = get_csr_offset(csr, 0);
>> + offset = get_csr_offset(csr, LOONGARCH_VM_LEVEL_HOST);
>> tcg_gen_ld_tl(oldv, tcg_env, offset);
>> tcg_gen_and_tl(newv, src1, mask);
>> tcg_gen_andc_tl(temp, oldv, mask);
>>
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v4 04/14] target/loongarch: Add LVZ CPU model support
2026-08-26 10:30 ` [PATCH v4 04/14] target/loongarch: Add LVZ CPU model support SignKirigami
@ 2026-09-02 3:09 ` Bibo Mao
0 siblings, 0 replies; 23+ messages in thread
From: Bibo Mao @ 2026-09-02 3:09 UTC (permalink / raw)
To: SignKirigami, qemu-devel; +Cc: xianglai li, Hengyu Yu
On 2026/8/26 下午6:30, SignKirigami wrote:
> Add the LVZ CPU property and initialize the host and guest state banks
> for the LA464 model. Reset always returns to the host CSR and TLB bank
> and initializes the implemented guest ID width.
>
> Signed-off-by: SignKirigami <prcups@krgm.moe>
> Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
> ---
> target/loongarch/cpu.c | 145 +++++++++++++++++++++++++----------------
> 1 file changed, 90 insertions(+), 55 deletions(-)
>
> diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
> index 0f6a211949..c5c7f3e0e3 100644
> --- a/target/loongarch/cpu.c
> +++ b/target/loongarch/cpu.c
> @@ -114,12 +114,12 @@ static void loongarch_la464_init_csr(DeviceState *dev)
> static bool initialized;
> LoongArchCPU *cpu = LOONGARCH_CPU(dev);
> CPULoongArchState *env = &cpu->env;
> - CPUSysState *sys = env_sys(env);
> + CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
There is set_sys_state() called in loongarch_la464_initfn(), it is host
sysstate in the beginning, I think that it is not necessary to change
this piece of code.
> int i, num;
>
> if (!initialized) {
> initialized = true;
> - num = FIELD_EX64(sys->CSR_PRCFG1, CSR_PRCFG1, SAVE_NUM);
> + num = FIELD_EX64(host->CSR_PRCFG1, CSR_PRCFG1, SAVE_NUM);
> for (i = num; i < 16; i++) {
> set_csr_flag(LOONGARCH_CSR_SAVE(i), CSRFL_UNUSED);
> }
> @@ -251,11 +251,32 @@ static void loongarch_set_ptw(Object *obj, bool value, Error **errp)
> cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, HPTW, value);
> }
>
> +static bool loongarch_get_lvz(Object *obj, Error **errp)
> +{
> + return LOONGARCH_CPU(obj)->lvz != ON_OFF_AUTO_OFF;
> +}
> +
> +static void loongarch_set_lvz(Object *obj, bool value, Error **errp)
> +{
> + LoongArchCPU *cpu = LOONGARCH_CPU(obj);
> +
> + cpu->lvz = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
> +
> + if (kvm_enabled()) {
> + return;
> + }
This can be removed if lvz property is only added in TCG mode.
> +
> + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LVZ, value);
> + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LVZ_VER,
> + value ? 1 : 0);
> +}
> +
> static void loongarch_cpu_post_init(Object *obj)
> {
> LoongArchCPU *cpu = LOONGARCH_CPU(obj);
>
> cpu->lbt = ON_OFF_AUTO_OFF;
> + cpu->lvz = ON_OFF_AUTO_AUTO;
> cpu->pmu = ON_OFF_AUTO_OFF;
> cpu->lsx = ON_OFF_AUTO_AUTO;
> cpu->lasx = ON_OFF_AUTO_AUTO;
> @@ -267,6 +288,8 @@ static void loongarch_cpu_post_init(Object *obj)
> loongarch_set_msgint);
> object_property_add_bool(obj, "ptw", loongarch_get_ptw,
> loongarch_set_ptw);
> + object_property_add_bool(obj, "lvz", loongarch_get_lvz,
> + loongarch_set_lvz);
Can lvz property be added only in TCG mode?
> /* lbt is enabled only in kvm mode, not supported in tcg mode */
>
> if (kvm_enabled()) {
> @@ -278,7 +301,8 @@ static void loongarch_la464_initfn(Object *obj)
> {
> LoongArchCPU *cpu = LOONGARCH_CPU(obj);
> CPULoongArchState *env = &cpu->env;
> - CPUSysState *sys;
> + CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
Variable name sys had better keep unchanged, so that smallest modification.
> + CPUSysState *guest = get_sys(env, LOONGARCH_VM_LEVEL_GUEST);
> uint32_t data = 0, field;
> int i;
>
> @@ -322,6 +346,8 @@ static void loongarch_la464_initfn(Object *obj)
> data = FIELD_DP32(data, CPUCFG2, FP_VER, 1);
> data = FIELD_DP32(data, CPUCFG2, LSX, 1),
> data = FIELD_DP32(data, CPUCFG2, LASX, 1),
> + data = FIELD_DP32(data, CPUCFG2, LVZ, 1);
> + data = FIELD_DP32(data, CPUCFG2, LVZ_VER, 1);
Add LVZ feature checking here only if LVZ feature is enabled.
> data = FIELD_DP32(data, CPUCFG2, LLFTP, 1);
> data = FIELD_DP32(data, CPUCFG2, LLFTP_VER, 1);
> data = FIELD_DP32(data, CPUCFG2, LSPW, 1);
> @@ -386,19 +412,19 @@ static void loongarch_la464_initfn(Object *obj)
> data = FIELD_DP32(data, CPUCFG20, L3IU_SIZE, 6);
> env->cpucfg[20] = data;
>
> - sys = env_sys(env);
> - sys->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa);
> + host->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa);
> + guest->CSR_ASID = host->CSR_ASID;Had better add lvz feature checking, such as
if (cpu->lvz == ON_OFF_AUTO_ON) {
guest->CSR_ASID = host->CSR_ASID;
}
>
> - sys->CSR_PRCFG1 = FIELD_DP64(sys->CSR_PRCFG1, CSR_PRCFG1, SAVE_NUM, 8);
> - sys->CSR_PRCFG1 = FIELD_DP64(sys->CSR_PRCFG1, CSR_PRCFG1, TIMER_BITS, 0x2f);
> - sys->CSR_PRCFG1 = FIELD_DP64(sys->CSR_PRCFG1, CSR_PRCFG1, VSMAX, 7);
> + host->CSR_PRCFG1 = FIELD_DP64(host->CSR_PRCFG1, CSR_PRCFG1, SAVE_NUM, 8);
> + host->CSR_PRCFG1 = FIELD_DP64(host->CSR_PRCFG1, CSR_PRCFG1, TIMER_BITS, 0x2f);
> + host->CSR_PRCFG1 = FIELD_DP64(host->CSR_PRCFG1, CSR_PRCFG1, VSMAX, 7);
>
> - sys->CSR_PRCFG2 = 0x3ffff000;
> + host->CSR_PRCFG2 = 0x3ffff000;
>
> - sys->CSR_PRCFG3 = FIELD_DP64(sys->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2);
> - sys->CSR_PRCFG3 = FIELD_DP64(sys->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63);
> - sys->CSR_PRCFG3 = FIELD_DP64(sys->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7);
> - sys->CSR_PRCFG3 = FIELD_DP64(sys->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8);
> + host->CSR_PRCFG3 = FIELD_DP64(host->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2);
> + host->CSR_PRCFG3 = FIELD_DP64(host->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63);
> + host->CSR_PRCFG3 = FIELD_DP64(host->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7);
> + host->CSR_PRCFG3 = FIELD_DP64(host->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8);
>
> cpu->msgint = ON_OFF_AUTO_OFF;
> cpu->ptw = ON_OFF_AUTO_OFF;
> @@ -439,6 +465,7 @@ static void loongarch_la132_initfn(Object *obj)
> data = FIELD_DP32(data, CPUCFG1, CRC, 1);
> env->cpucfg[1] = data;
> cpu->msgint = ON_OFF_AUTO_OFF;
> + cpu->lvz = ON_OFF_AUTO_OFF;
> cpu->ptw = ON_OFF_AUTO_OFF;
> }
>
> @@ -600,7 +627,7 @@ static void loongarch_cpu_reset_hold(Object *obj, ResetType type)
> CPUState *cs = CPU(obj);
> LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(obj);
> CPULoongArchState *env = cpu_env(cs);
> - CPUSysState *sys = env_sys(env);
> + CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
Had better keep name *sys* unchanged for smallest modification.
Regards
Bibo Mao
>
> if (lacc->parent_phases.hold) {
> lacc->parent_phases.hold(obj, type);
> @@ -624,61 +651,69 @@ static void loongarch_cpu_reset_hold(Object *obj, ResetType type)
>
> int n;
> /* Set csr registers value after reset, see the manual 6.4. */
> - sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, PLV, 0);
> - sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, IE, 0);
> - sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, DA, 1);
> - sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, PG, 0);
> - sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, DATF, 0);
> - sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, DATM, 0);
> -
> - sys->CSR_EUEN = FIELD_DP64(sys->CSR_EUEN, CSR_EUEN, FPE, 0);
> - sys->CSR_EUEN = FIELD_DP64(sys->CSR_EUEN, CSR_EUEN, SXE, 0);
> - sys->CSR_EUEN = FIELD_DP64(sys->CSR_EUEN, CSR_EUEN, ASXE, 0);
> - sys->CSR_EUEN = FIELD_DP64(sys->CSR_EUEN, CSR_EUEN, BTE, 0);
> -
> - sys->CSR_MISC = 0;
> -
> - sys->CSR_ECFG = FIELD_DP64(sys->CSR_ECFG, CSR_ECFG, VS, 0);
> - sys->CSR_ECFG = FIELD_DP64(sys->CSR_ECFG, CSR_ECFG, LIE, 0);
> -
> - sys->CSR_ESTAT = sys->CSR_ESTAT & (~MAKE_64BIT_MASK(0, 2));
> - sys->CSR_RVACFG = FIELD_DP64(sys->CSR_RVACFG, CSR_RVACFG, RBITS, 0);
> - sys->CSR_CPUID = cs->cpu_index;
> - sys->CSR_TCFG = FIELD_DP64(sys->CSR_TCFG, CSR_TCFG, EN, 0);
> - sys->CSR_LLBCTL = FIELD_DP64(sys->CSR_LLBCTL, CSR_LLBCTL, KLO, 0);
> - sys->CSR_TLBRERA = FIELD_DP64(sys->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0);
> - sys->CSR_MERRCTL = FIELD_DP64(sys->CSR_MERRCTL, CSR_MERRCTL, ISMERR, 0);
> - sys->CSR_TID = cs->cpu_index;
> + host->CSR_CRMD = FIELD_DP64(host->CSR_CRMD, CSR_CRMD, PLV, 0);
> + host->CSR_CRMD = FIELD_DP64(host->CSR_CRMD, CSR_CRMD, IE, 0);
> + host->CSR_CRMD = FIELD_DP64(host->CSR_CRMD, CSR_CRMD, DA, 1);
> + host->CSR_CRMD = FIELD_DP64(host->CSR_CRMD, CSR_CRMD, PG, 0);
> + host->CSR_CRMD = FIELD_DP64(host->CSR_CRMD, CSR_CRMD, DATF, 0);
> + host->CSR_CRMD = FIELD_DP64(host->CSR_CRMD, CSR_CRMD, DATM, 0);
> +
> + host->CSR_EUEN = FIELD_DP64(host->CSR_EUEN, CSR_EUEN, FPE, 0);
> + host->CSR_EUEN = FIELD_DP64(host->CSR_EUEN, CSR_EUEN, SXE, 0);
> + host->CSR_EUEN = FIELD_DP64(host->CSR_EUEN, CSR_EUEN, ASXE, 0);
> + host->CSR_EUEN = FIELD_DP64(host->CSR_EUEN, CSR_EUEN, BTE, 0);
> +
> + host->CSR_MISC = 0;
> +
> + host->CSR_ECFG = FIELD_DP64(host->CSR_ECFG, CSR_ECFG, VS, 0);
> + host->CSR_ECFG = FIELD_DP64(host->CSR_ECFG, CSR_ECFG, LIE, 0);
> +
> + host->CSR_ESTAT = host->CSR_ESTAT & (~MAKE_64BIT_MASK(0, 2));
> + host->CSR_RVACFG = FIELD_DP64(host->CSR_RVACFG, CSR_RVACFG, RBITS, 0);
> + host->CSR_CPUID = cs->cpu_index;
> + host->CSR_TCFG = FIELD_DP64(host->CSR_TCFG, CSR_TCFG, EN, 0);
> + host->CSR_LLBCTL = FIELD_DP64(host->CSR_LLBCTL, CSR_LLBCTL, KLO, 0);
> + host->CSR_TLBRERA = FIELD_DP64(host->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0);
> + host->CSR_MERRCTL = FIELD_DP64(host->CSR_MERRCTL, CSR_MERRCTL, ISMERR, 0);
> + host->CSR_TID = cs->cpu_index;
> + host->CSR_GSTAT = 0;
> + host->CSR_GCFG = 0;
> + host->CSR_GINTC = 0;
> + host->CSR_GCNTC = 0;
> + host->CSR_GTLBC = 0;
> /*
> * Workaround for edk2-stable202408, CSR PGD register is set only if
> * its value is equal to zero for boot cpu, it causes reboot issue.
> *
> * Here clear CSR registers relative with TLB.
> */
> - sys->CSR_PGDH = 0;
> - sys->CSR_PGDL = 0;
> - sys->CSR_PWCH = 0;
> - sys->CSR_EENTRY = 0;
> - sys->CSR_TLBRENTRY = 0;
> - sys->CSR_MERRENTRY = 0;
> + host->CSR_PGDH = 0;
> + host->CSR_PGDL = 0;
> + host->CSR_PWCH = 0;
> + host->CSR_EENTRY = 0;
> + host->CSR_TLBRENTRY = 0;
> + host->CSR_MERRENTRY = 0;
> /* set CSR_PWCL.PTBASE and CSR_STLBPS.PS bits from CSR_PRCFG2 */
> - if (sys->CSR_PRCFG2 == 0) {
> - sys->CSR_PRCFG2 = 0x3fffff000;
> + if (host->CSR_PRCFG2 == 0) {
> + host->CSR_PRCFG2 = 0x3fffff000;
> }
> - tlb_ps = ctz32(sys->CSR_PRCFG2);
> - sys->CSR_STLBPS = FIELD_DP64(sys->CSR_STLBPS, CSR_STLBPS, PS, tlb_ps);
> - sys->CSR_PWCL = FIELD_DP64(sys->CSR_PWCL, CSR_PWCL, PTBASE, tlb_ps);
> + tlb_ps = ctz32(host->CSR_PRCFG2);
> + host->CSR_STLBPS = FIELD_DP64(host->CSR_STLBPS, CSR_STLBPS, PS, tlb_ps);
> + host->CSR_PWCL = FIELD_DP64(host->CSR_PWCL, CSR_PWCL, PTBASE, tlb_ps);
> for (n = 0; n < 4; n++) {
> - sys->CSR_DMW[n] = FIELD_DP64(sys->CSR_DMW[n], CSR_DMW, PLV0, 0);
> - sys->CSR_DMW[n] = FIELD_DP64(sys->CSR_DMW[n], CSR_DMW, PLV1, 0);
> - sys->CSR_DMW[n] = FIELD_DP64(sys->CSR_DMW[n], CSR_DMW, PLV2, 0);
> - sys->CSR_DMW[n] = FIELD_DP64(sys->CSR_DMW[n], CSR_DMW, PLV3, 0);
> + host->CSR_DMW[n] = FIELD_DP64(host->CSR_DMW[n], CSR_DMW, PLV0, 0);
> + host->CSR_DMW[n] = FIELD_DP64(host->CSR_DMW[n], CSR_DMW, PLV1, 0);
> + host->CSR_DMW[n] = FIELD_DP64(host->CSR_DMW[n], CSR_DMW, PLV2, 0);
> + host->CSR_DMW[n] = FIELD_DP64(host->CSR_DMW[n], CSR_DMW, PLV3, 0);
> + }
> + if (FIELD_EX32(env->cpucfg[2], CPUCFG2, LVZ)) {
> + host->CSR_GSTAT = FIELD_DP64(host->CSR_GSTAT, CSR_GSTAT, GIDBIT, 8);
> }
>
> #ifndef CONFIG_USER_ONLY
> env->pc = 0x1c000000;
> #ifdef CONFIG_TCG
> - memset(env->tlb, 0, sizeof(env->tlb));
> + memset(host->tlb, 0, sizeof(host->tlb));
> #endif
> if (kvm_enabled()) {
> kvm_arch_reset_vcpu(cs);
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v4 05/14] target/loongarch: Add LVZ guest interrupt state
2026-08-26 10:30 ` [PATCH v4 05/14] target/loongarch: Add LVZ guest interrupt state SignKirigami
@ 2026-09-02 3:33 ` Bibo Mao
0 siblings, 0 replies; 23+ messages in thread
From: Bibo Mao @ 2026-09-02 3:33 UTC (permalink / raw)
To: SignKirigami, qemu-devel; +Cc: xianglai li, Hengyu Yu
On 2026/8/26 下午6:30, SignKirigami wrote:
> Route configured hardware interrupts into the guest CSR bank, track guest
> interrupt requests separately, and expose guest pending and enable checks
> to the TCG runtime.
>
> Signed-off-by: SignKirigami <prcups@krgm.moe>
> Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
> ---
> target/loongarch/cpu.c | 43 +++++++++++++++++++++++++---------
> target/loongarch/internals.h | 4 +++-
> target/loongarch/tcg/tcg_cpu.c | 36 +++++++++++++++++++---------
> 3 files changed, 60 insertions(+), 23 deletions(-)
>
> diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
> index c5c7f3e0e3..b12e8944de 100644
> --- a/target/loongarch/cpu.c
> +++ b/target/loongarch/cpu.c
> @@ -57,41 +57,57 @@ static vaddr loongarch_cpu_get_pc(CPUState *cs)
> #ifndef CONFIG_USER_ONLY
> #include "hw/loongarch/virt.h"
>
> -void loongarch_cpu_set_irq(void *opaque, int irq, int level)
> +void loongarch_cpu_set_irq_impl(void *opaque, int irq, int level,
> + bool guest)
> {
> LoongArchCPU *cpu = opaque;
> CPULoongArchState *env = &cpu->env;
> CPUState *cs = CPU(cpu);
> - CPUSysState *sys = env_sys(env);
> + CPUSysState *sys = get_sys(env, guest);
> + int interrupt = guest ? CPU_INTERRUPT_GUEST : CPU_INTERRUPT_HARD;
>
Is it ok to keep function loongarch_cpu_set_irq() unchanged? using irq
number to differentiate guest HW, value of irq of guest timer is defined
as IRQ_VTIMER, such as
#define IRQ_VTIMER (IRQ_TIMER + N_IRQS)
if (irq >= N_IRQS) {
irq -= N_IRQS;
guest = true;
}
The same for guest PMU/MSGINT HW in future.
> if (irq < 0 || irq >= N_IRQS) {
> return;
> }
>
> - if (kvm_enabled()) {
> + if (!guest && kvm_enabled()) {
> kvm_loongarch_set_interrupt(cpu, irq, level);
> } else if (tcg_enabled()) {
> + if (!guest && irq >= 2 && irq < 10 &&
> + (FIELD_EX64(sys->CSR_GINTC, CSR_GINTC, HWIP) &
> + BIT(irq - 2))) {
> + loongarch_cpu_set_irq_impl(opaque, irq, level, true);
> + return;
> + }
It is not necessary to check CSR_GINTC.
Regards
Bibo Mao
> sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, irq, 1, level != 0);
> - if (FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS)) {
> - cpu_interrupt(cs, CPU_INTERRUPT_HARD);
> - } else {
> - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
> + if (!guest || env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
> + if (FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS)) {
> + cpu_interrupt(cs, interrupt);
> + } else {
> + cpu_reset_interrupt(cs, interrupt);
> + }
> }
> }
> }
>
> +void loongarch_cpu_set_irq(void *opaque, int irq, int level)
> +{
> + loongarch_cpu_set_irq_impl(opaque, irq, level, false);
> +}
> +
> /* Check if there is pending and not masked out interrupt */
> -bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env)
> +bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env, bool guest)
> {
> uint32_t pending;
> uint32_t status;
> - CPUSysState *sys = env_sys(env);
> + CPUSysState *sys = get_sys(env, guest);
>
> pending = FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS);
> - status = FIELD_EX64(sys->CSR_ECFG, CSR_ECFG, LIE);
> + status = FIELD_EX64(sys->CSR_ECFG, CSR_ECFG, LIE);
>
> return (pending & status) != 0;
> }
> +
> #endif
>
> #ifndef CONFIG_USER_ONLY
> @@ -100,7 +116,12 @@ bool loongarch_cpu_has_work(CPUState *cs)
> bool has_work = false;
>
> if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) &&
> - cpu_loongarch_hw_interrupts_pending(cpu_env(cs))) {
> + cpu_loongarch_hw_interrupts_pending(cpu_env(cs), false)) {
> + has_work = true;
> + }
> +
> + if (cpu_test_interrupt(cs, CPU_INTERRUPT_GUEST) &&
> + cpu_loongarch_hw_interrupts_pending(cpu_env(cs), true)) {
> has_work = true;
> }
>
> diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h
> index e01dbed40f..a23b0d3d58 100644
> --- a/target/loongarch/internals.h
> +++ b/target/loongarch/internals.h
> @@ -32,6 +32,8 @@ void restore_fp_status(CPULoongArchState *env);
> extern const VMStateDescription vmstate_loongarch_cpu;
>
> void loongarch_cpu_set_irq(void *opaque, int irq, int level);
> +void loongarch_cpu_set_irq_impl(void *opaque, int irq, int level,
> + bool guest);
>
> void loongarch_constant_timer_cb(void *opaque);
> uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu);
> @@ -39,7 +41,7 @@ uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu);
> void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu,
> uint64_t value);
> bool loongarch_cpu_has_work(CPUState *cs);
> -bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env);
> +bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env, bool guest);
> #endif /* !CONFIG_USER_ONLY */
>
> uint64_t read_fcc(CPULoongArchState *env);
> diff --git a/target/loongarch/tcg/tcg_cpu.c b/target/loongarch/tcg/tcg_cpu.c
> index 4b1d44a164..e84ef5f305 100644
> --- a/target/loongarch/tcg/tcg_cpu.c
> +++ b/target/loongarch/tcg/tcg_cpu.c
> @@ -238,29 +238,43 @@ static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
> }
> }
>
> -static inline bool cpu_loongarch_hw_interrupts_enabled(CPULoongArchState *env)
> +static inline bool cpu_loongarch_hw_interrupts_enabled(CPULoongArchState *env, bool guest)
> {
> bool ret = 0;
> - CPUSysState *sys = env_sys(env);
> + CPUSysState *sys = get_sys(env, guest);
> + CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
>
> ret = (FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, IE) &&
> - !(FIELD_EX64(sys->CSR_DBG, CSR_DBG, DST)));
> + !(FIELD_EX64(host->CSR_DBG, CSR_DBG, DST)));
>
> return ret;
> }
>
> static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
> {
> + CPULoongArchState *env = cpu_env(cs);
> + bool has_interrupt = false;
> +
> if (interrupt_request & CPU_INTERRUPT_HARD) {
> - CPULoongArchState *env = cpu_env(cs);
> -
> - if (cpu_loongarch_hw_interrupts_enabled(env) &&
> - cpu_loongarch_hw_interrupts_pending(env)) {
> - /* Raise it */
> - cs->exception_index = EXCCODE_INT;
> - loongarch_cpu_do_interrupt(cs);
> - return true;
> + if (cpu_loongarch_hw_interrupts_enabled(env, false) &&
> + cpu_loongarch_hw_interrupts_pending(env, false)) {
> + if (env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
> + trigger_vm_exit(env);
> + }
> + has_interrupt = true;
> }
> + } else if (interrupt_request & CPU_INTERRUPT_GUEST) {
> + if (cpu_loongarch_hw_interrupts_enabled(env, true) &&
> + cpu_loongarch_hw_interrupts_pending(env, true) &&
> + env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
> + has_interrupt = true;
> + }
> + }
> + if (has_interrupt) {
> + /* Raise it */
> + cs->exception_index = EXCCODE_INT;
> + loongarch_cpu_do_interrupt(cs);
> + return true;
> }
> return false;
> }
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v4 07/14] target/loongarch: Add LVZ VM entry and exception handling
2026-08-26 10:30 ` [PATCH v4 07/14] target/loongarch: Add LVZ VM entry and exception handling SignKirigami
@ 2026-09-02 9:19 ` Bibo Mao
0 siblings, 0 replies; 23+ messages in thread
From: Bibo Mao @ 2026-09-02 9:19 UTC (permalink / raw)
To: SignKirigami, qemu-devel; +Cc: xianglai li, Hengyu Yu
On 2026/8/26 下午6:30, SignKirigami wrote:
> Enter guest mode when ERTN restores GSTAT.PVM, and return to the host CSR
> bank for guest-sensitive, hypercall and host interrupt exits. Select the
> exception CSR bank before updating state and expose guest mode in TCG
> flags and MMU indices.
>
> Bound-check exceptions update the currently active CSR bank. HVC is
> available only to guest PLV0.
>
> Signed-off-by: SignKirigami <prcups@krgm.moe>
> Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
> ---
> target/loongarch/cpu.c | 50 ++++++++++++++++++++++++++++++++
> target/loongarch/tcg/helper.h | 2 +-
> target/loongarch/tcg/op_helper.c | 22 ++++++++++++++
> target/loongarch/tcg/tcg_cpu.c | 46 ++++++++++++++++++++++-------
> 4 files changed, 109 insertions(+), 11 deletions(-)
>
> diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
> index ff36294aa5..4b0acc7135 100644
> --- a/target/loongarch/cpu.c
> +++ b/target/loongarch/cpu.c
> @@ -129,6 +129,56 @@ bool loongarch_cpu_has_work(CPUState *cs)
> }
> #endif /* !CONFIG_USER_ONLY */
>
> +uint8_t get_tgid(CPULoongArchState *env)
> +{
> + CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
> +
> + if (env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
> + return get_gid(env);
> + }
> +
> + if (FIELD_EX64(host->CSR_GTLBC, CSR_GTLBC, USETGID)) {
> + return FIELD_EX64(host->CSR_GTLBC, CSR_GTLBC, TGID);
> + } else if (will_return_to_guest(env)) {
> + return get_gid(env);
> + }
> + return 0;
> +}
> +
> +bool will_return_to_guest(CPULoongArchState *env)
> +{
> + if (!has_lvz_capability(env) ||
> + env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
> + return false;
> + }
> + return FIELD_EX64(get_sys(env, LOONGARCH_VM_LEVEL_HOST)->CSR_GSTAT,
> + CSR_GSTAT, PVM);
> +}
> +
> +bool has_lvz_capability(CPULoongArchState *env)
> +{
> + return FIELD_EX32(env->cpucfg[2], CPUCFG2, LVZ);
> +}
> +
> +uint8_t get_gid(CPULoongArchState *env)
> +{
> + return FIELD_EX64(get_sys(env, LOONGARCH_VM_LEVEL_HOST)->CSR_GSTAT,
> + CSR_GSTAT, GID);
> +}
> +
> +void trigger_vm_exit(CPULoongArchState *env)
> +{
> + CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
> +
> + if (env_vm_level(env) != LOONGARCH_VM_LEVEL_GUEST) {
> + return;
> + }
> +
> + cpu_loongarch_set_guest_timer(env_archcpu(env), false);
why to disable guest timer when exiting to VM?
> + host->CSR_GSTAT = FIELD_DP64(host->CSR_GSTAT, CSR_GSTAT, PVM, 1);
I think it is unnecesary, PVM bit of CSR_GSTAT is set already when
entering to VM mode.
> + env->vm_exit = true;
> +}
> +
> static void loongarch_la464_init_csr(DeviceState *dev)
> {
> #ifndef CONFIG_USER_ONLY
> diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h
> index a003266827..b87de15a99 100644
> --- a/target/loongarch/tcg/helper.h
> +++ b/target/loongarch/tcg/helper.h
> @@ -14,7 +14,7 @@ DEF_HELPER_FLAGS_3(asrtgt_d, TCG_CALL_NO_WG, void, env, tl, tl)
>
> DEF_HELPER_FLAGS_3(crc32, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl)
> DEF_HELPER_FLAGS_3(crc32c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl)
> -DEF_HELPER_FLAGS_2(cpucfg, TCG_CALL_NO_RWG_SE, tl, env, tl)
> +DEF_HELPER_FLAGS_2(cpucfg, TCG_CALL_NO_WG_SE, tl, env, tl)
>
> /* Floating-point helper */
> DEF_HELPER_FLAGS_3(fadd_s, TCG_CALL_NO_WG, i64, env, i64, i64)
> diff --git a/target/loongarch/tcg/op_helper.c b/target/loongarch/tcg/op_helper.c
> index e63ac66daa..11d204c61c 100644
> --- a/target/loongarch/tcg/op_helper.c
> +++ b/target/loongarch/tcg/op_helper.c
> @@ -12,6 +12,7 @@
> #include "exec/helper-proto.h"
> #include "accel/tcg/cpu-ldst.h"
> #include "internals.h"
> +#include "qemu/main-loop.h"
> #include "qemu/crc32c.h"
> #include <zlib.h> /* for crc32 */
> #include "cpu-csr.h"
> @@ -19,6 +20,9 @@
> /* Exceptions helpers */
> void helper_raise_exception(CPULoongArchState *env, uint32_t exception)
> {
> + if (exception == EXCCODE_GSPR || exception == EXCCODE_HVC) {
> + trigger_vm_exit(env);
> + }
Had better to add space line here though it is not forcly.
> do_raise_exception(env, exception, GETPC());
> }
>
> @@ -85,6 +89,10 @@ target_ulong helper_crc32c(target_ulong val, target_ulong m, uint64_t sz)
>
> target_ulong helper_cpucfg(CPULoongArchState *env, target_ulong rj)
> {
> + if (env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
> + trigger_vm_exit(env);
> + do_raise_exception(env, EXCCODE_GSPR, GETPC());
> + }
> return rj >= ARRAY_SIZE(env->cpucfg) ? 0 : env->cpucfg[rj];
> }
>
> @@ -111,6 +119,8 @@ void helper_ertn(CPULoongArchState *env)
> {
> uint64_t csr_pplv, csr_pie;
> CPUSysState *sys = env_sys(env);
> + CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
> + CPUSysState *guest = get_sys(env, LOONGARCH_VM_LEVEL_GUEST);
>
> if (FIELD_EX64(sys->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
> csr_pplv = FIELD_EX64(sys->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV);
> @@ -134,6 +144,18 @@ void helper_ertn(CPULoongArchState *env)
> sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, IE, csr_pie);
>
> env->lladdr = 1;
> + if (will_return_to_guest(env)) {
> + host->CSR_GSTAT = FIELD_DP64(host->CSR_GSTAT, CSR_GSTAT, VM, 1);
> + set_sys(env, LOONGARCH_VM_LEVEL_GUEST);
> + cpu_loongarch_set_guest_timer(env_archcpu(env), true);
I think it is not necessary to enable guest timer, it is always on even
if system enter host mode.
> + bql_lock();
> + if (FIELD_EX64(guest->CSR_ESTAT, CSR_ESTAT, IS)) {
> + cpu_interrupt(env_cpu(env), CPU_INTERRUPT_GUEST);
> + } else {
> + cpu_reset_interrupt(env_cpu(env), CPU_INTERRUPT_GUEST);
> + }
> + bql_unlock();
> + }
> }
>
> void helper_idle(CPULoongArchState *env)
> diff --git a/target/loongarch/tcg/tcg_cpu.c b/target/loongarch/tcg/tcg_cpu.c
> index e84ef5f305..6af885cda1 100644
> --- a/target/loongarch/tcg/tcg_cpu.c
> +++ b/target/loongarch/tcg/tcg_cpu.c
> @@ -44,6 +44,10 @@ static const struct TypeExcp excp_names[] = {
> {EXCCODE_BCE, "Bound Check Exception"},
> {EXCCODE_SXD, "128 bit vector instructions Disable exception"},
> {EXCCODE_ASXD, "256 bit vector instructions Disable exception"},
> + {EXCCODE_GSPR, "Guest Sensitive and Privileged Resources"},
> + {EXCCODE_HVC, "Hypervisor call"},
> + {EXCCODE_GCSC, "Guest CSR visited by Software"},
> + {EXCCODE_GCHC, "Guest CSR visited by Hardware"},
> {EXCP_HLT, "EXCP_HLT"},
> };
>
> @@ -78,9 +82,11 @@ void G_NORETURN do_raise_exception(CPULoongArchState *env,
> static void loongarch_cpu_do_interrupt(CPUState *cs)
> {
> CPULoongArchState *env = cpu_env(cs);
> - CPUSysState *sys = env_sys(env);
> bool update_badinstr = 1;
> int cause = -1;
> + bool real_guest = !env->vm_exit && env_vm_level(env);
> + CPUSysState *sys = get_sys(env, real_guest);
> + CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
> bool tlbfill = FIELD_EX64(sys->CSR_TLBRERA, CSR_TLBRERA, ISTLBR);
> uint32_t vec_size = FIELD_EX64(sys->CSR_ECFG, CSR_ECFG, VS);
> uint64_t last_pc = env->pc;
> @@ -96,17 +102,17 @@ static void loongarch_cpu_do_interrupt(CPUState *cs)
>
> switch (cs->exception_index) {
> case EXCCODE_DBP:
> - sys->CSR_DBG = FIELD_DP64(sys->CSR_DBG, CSR_DBG, DCL, 1);
> - sys->CSR_DBG = FIELD_DP64(sys->CSR_DBG, CSR_DBG, ECODE, 0xC);
> + host->CSR_DBG = FIELD_DP64(host->CSR_DBG, CSR_DBG, DCL, 1);
> + host->CSR_DBG = FIELD_DP64(host->CSR_DBG, CSR_DBG, ECODE, 0xC);
> goto set_DERA;
> set_DERA:
> - sys->CSR_DERA = env->pc;
> - sys->CSR_DBG = FIELD_DP64(sys->CSR_DBG, CSR_DBG, DST, 1);
> - set_pc(env, sys->CSR_EENTRY + 0x480);
> + host->CSR_DERA = env->pc;
> + host->CSR_DBG = FIELD_DP64(host->CSR_DBG, CSR_DBG, DST, 1);
> + set_pc(env, host->CSR_EENTRY + 0x480);
> break;
> case EXCCODE_INT:
> - if (FIELD_EX64(sys->CSR_DBG, CSR_DBG, DST)) {
> - sys->CSR_DBG = FIELD_DP64(sys->CSR_DBG, CSR_DBG, DEI, 1);
> + if (FIELD_EX64(host->CSR_DBG, CSR_DBG, DST)) {
> + host->CSR_DBG = FIELD_DP64(host->CSR_DBG, CSR_DBG, DEI, 1);
> goto set_DERA;
> }
> QEMU_FALLTHROUGH;
> @@ -117,6 +123,10 @@ static void loongarch_cpu_do_interrupt(CPUState *cs)
> update_badinstr = 0;
> break;
> case EXCCODE_BCE:
> + case EXCCODE_GSPR:
> + case EXCCODE_GCHC:
> + case EXCCODE_GCSC:
> + case EXCCODE_HVC:
> sys->CSR_BADV = env->pc;
> QEMU_FALLTHROUGH;
> case EXCCODE_SYS:
> @@ -218,6 +228,12 @@ static void loongarch_cpu_do_interrupt(CPUState *cs)
> qemu_plugin_vcpu_exception_cb(cs, last_pc);
> }
> cs->exception_index = -1;
> + if (env->vm_exit) {
> + host->CSR_GSTAT = FIELD_DP64(host->CSR_GSTAT, CSR_GSTAT, VM, 0);
> + set_sys(env, LOONGARCH_VM_LEVEL_HOST);
> + cpu_reset_interrupt(cs, CPU_INTERRUPT_GUEST);
why clear CPU_INTERRUPT_GUEST when exiting to host mode?
I think loongarch_cpu_has_work() need modified as following:
1. If cpu work in VM mode, checking both CPU_INTERRUPT_HARD and
CPU_INTERRUPT_GUEST.
2. If cpu work in host mode, only checking CPU_INTERRUPT_HARD.
Regards
Bibo Mao
> + }
> + env->vm_exit = false;
> }
>
> static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
> @@ -228,9 +244,9 @@ static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
> uintptr_t retaddr)
> {
> CPULoongArchState *env = cpu_env(cs);
> - CPUSysState *sys = env_sys(env);
> + CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
>
> - sys->CSR_BADV = addr;
> + host->CSR_BADV = addr;
> if (access_type == MMU_INST_FETCH) {
> do_raise_exception(env, EXCCODE_ADEF, retaddr);
> } else {
> @@ -293,6 +309,9 @@ static TCGTBCPUState loongarch_get_tb_cpu_state(CPUState *cs)
> uint32_t flags;
>
> flags = sys->CSR_CRMD & (R_CSR_CRMD_PLV_MASK | R_CSR_CRMD_PG_MASK);
> + if (env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
> + flags |= HW_FLAGS_GUEST_MODE;
> + }
> flags |= FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, FPE) * HW_FLAGS_EUEN_FPE;
> flags |= FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, SXE) * HW_FLAGS_EUEN_SXE;
> flags |= FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, ASXE) * HW_FLAGS_EUEN_ASXE;
> @@ -320,6 +339,13 @@ static int loongarch_cpu_mmu_index(CPUState *cs, bool ifetch)
> CPULoongArchState *env = cpu_env(cs);
> CPUSysState *sys = env_sys(env);
>
> + if (env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
> + if (FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, PG)) {
> + return MMU_GUEST_IDX + FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, PLV);
> + }
> + return MMU_GUEST_DA_IDX;
> + }
> +
> if (FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, PG)) {
> return FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, PLV);
> }
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v4 08/14] target/loongarch: Add LVZ guest CSR helpers
2026-08-26 10:30 ` [PATCH v4 08/14] target/loongarch: Add LVZ guest CSR helpers SignKirigami
@ 2026-09-02 9:55 ` Bibo Mao
0 siblings, 0 replies; 23+ messages in thread
From: Bibo Mao @ 2026-09-02 9:55 UTC (permalink / raw)
To: SignKirigami, qemu-devel; +Cc: xianglai li, Hengyu Yu
On 2026/8/26 下午6:30, SignKirigami wrote:
> Add helper behavior for guest PGD, ESTAT, ASID and timer interrupt clearing,
> and for the host LVZ control CSRs.
>
> Synchronize injected and currently pending passthrough interrupts into
> the guest ESTAT value whenever GINTC changes.
>
> Signed-off-by: SignKirigami <prcups@krgm.moe>
> Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
> ---
> target/loongarch/tcg/csr_helper.c | 226 +++++++++++++++++++++++++-----
> target/loongarch/tcg/helper.h | 10 ++
> 2 files changed, 203 insertions(+), 33 deletions(-)
>
> diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c
> index fa75508244..ba30060ba4 100644
> --- a/target/loongarch/tcg/csr_helper.c
> +++ b/target/loongarch/tcg/csr_helper.c
> @@ -18,9 +18,10 @@
> #include "cpu-csr.h"
> #include "cpu-mmu.h"
>
> -target_ulong helper_csrwr_stlbps(CPULoongArchState *env, target_ulong val)
> +static target_ulong helper_csrwr_stlbps_impl(CPULoongArchState *env,
> + target_ulong val, bool guest)
> {
> - CPUSysState *sys = env_sys(env);
> + CPUSysState *sys = get_sys(env, guest);
> int64_t old_v = sys->CSR_STLBPS;
>
> /*
> @@ -40,10 +41,21 @@ target_ulong helper_csrwr_stlbps(CPULoongArchState *env, target_ulong val)
> return old_v;
> }
>
> -target_ulong helper_csrrd_pgd(CPULoongArchState *env)
> +target_ulong helper_csrwr_stlbps(CPULoongArchState *env, target_ulong val)
> +{
> + return helper_csrwr_stlbps_impl(env, val, false);
> +}
> +
> +target_ulong helper_gcsrwr_stlbps(CPULoongArchState *env, target_ulong val)
> +{
> + return helper_csrwr_stlbps_impl(env, val, true);
> +}
> +
> +static target_ulong helper_csrrd_pgd_impl(CPULoongArchState *env,
> + bool guest)
> {
> int64_t v;
> - CPUSysState *sys = env_sys(env);
> + CPUSysState *sys = get_sys(env, guest);
>
> if (sys->CSR_TLBRERA & 0x1) {
> v = sys->CSR_TLBRBADV;
> @@ -60,6 +72,16 @@ target_ulong helper_csrrd_pgd(CPULoongArchState *env)
> return v;
> }
>
> +target_ulong helper_csrrd_pgd(CPULoongArchState *env)
> +{
> + return helper_csrrd_pgd_impl(env, false);
> +}
> +
> +target_ulong helper_gcsrrd_pgd(CPULoongArchState *env)
> +{
> + return helper_csrrd_pgd_impl(env, true);
> +}
> +
> target_ulong helper_csrrd_cpuid(CPULoongArchState *env)
> {
> LoongArchCPU *lac = env_archcpu(env);
> @@ -70,18 +92,22 @@ target_ulong helper_csrrd_cpuid(CPULoongArchState *env)
> return sys->CSR_CPUID;
> }
>
> -target_ulong helper_csrrd_tval(CPULoongArchState *env)
> +static target_ulong helper_csrrd_tval_impl(CPULoongArchState *env,
> + bool guest)
> {
> LoongArchCPU *cpu = env_archcpu(env);
>
> - return cpu_loongarch_get_constant_timer_ticks(cpu, false);
> + return cpu_loongarch_get_constant_timer_ticks(cpu, guest);
> }
>
> -target_ulong helper_gcsrrd_tval(CPULoongArchState *env)
> +target_ulong helper_csrrd_tval(CPULoongArchState *env)
> {
> - LoongArchCPU *cpu = env_archcpu(env);
> + return helper_csrrd_tval_impl(env, false);
> +}
>
> - return cpu_loongarch_get_constant_timer_ticks(cpu, true);
> +target_ulong helper_gcsrrd_tval(CPULoongArchState *env)
> +{
> + return helper_csrrd_tval_impl(env, true);
> }
>
> target_ulong helper_csrrd_msgir(CPULoongArchState *env)
> @@ -106,20 +132,45 @@ target_ulong helper_csrrd_msgir(CPULoongArchState *env)
> return irq;
> }
>
> -target_ulong helper_csrwr_estat(CPULoongArchState *env, target_ulong val)
> +static target_ulong helper_csrwr_estat_impl(CPULoongArchState *env,
> + target_ulong val, bool guest)
> {
> - CPUSysState *sys = env_sys(env);
> + CPUSysState *sys = get_sys(env, guest);
> + CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
> int64_t old_v = sys->CSR_ESTAT;
>
> /* Only IS[1:0] can be written */
> sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, 0, 2, val);
> + if (guest && env_vm_level(env) != LOONGARCH_VM_LEVEL_GUEST) {
> + sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, 2, 11,
> + extract64(val, 2, 11));
> + if (extract64(val, 2, 8) &
> + FIELD_EX64(host->CSR_GINTC, CSR_GINTC, HWIC)) {
> + host->CSR_ESTAT = deposit64(host->CSR_ESTAT, 2, 8,
> + extract64(host->CSR_ESTAT, 2, 8) &
> + ~extract64(val, 2, 8));
> + }
> + sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, 16, 15,
> + extract64(val, 16, 15));
> + }
>
> return old_v;
> }
>
> -target_ulong helper_csrwr_asid(CPULoongArchState *env, target_ulong val)
> +target_ulong helper_csrwr_estat(CPULoongArchState *env, target_ulong val)
> {
> - CPUSysState *sys = env_sys(env);
> + return helper_csrwr_estat_impl(env, val, false);
> +}
> +
> +target_ulong helper_gcsrwr_estat(CPULoongArchState *env, target_ulong val)
> +{
> + return helper_csrwr_estat_impl(env, val, true);
> +}
> +
> +static target_ulong helper_csrwr_asid_impl(CPULoongArchState *env,
> + target_ulong val, bool guest)
> +{
> + CPUSysState *sys = get_sys(env, guest);
> int64_t old_v = sys->CSR_ASID;
>
> /* Only ASID filed of CSR_ASID can be written */
> @@ -130,45 +181,132 @@ target_ulong helper_csrwr_asid(CPULoongArchState *env, target_ulong val)
> return old_v;
> }
>
> -target_ulong helper_csrwr_tcfg(CPULoongArchState *env, target_ulong val)
> +target_ulong helper_csrwr_asid(CPULoongArchState *env, target_ulong val)
> +{
> + return helper_csrwr_asid_impl(env, val, false);
> +}
> +
> +target_ulong helper_gcsrwr_asid(CPULoongArchState *env, target_ulong val)
> +{
> + return helper_csrwr_asid_impl(env, val, true);
> +}
> +
> +static target_ulong helper_csrwr_tcfg_impl(CPULoongArchState *env,
> + target_ulong val, bool guest)
> {
> LoongArchCPU *cpu = env_archcpu(env);
> - CPUSysState *sys = env_sys(env);
> + CPUSysState *sys = get_sys(env, guest);
> int64_t old_v = sys->CSR_TCFG;
>
> - cpu_loongarch_store_constant_timer_config(cpu, val, false);
> + cpu_loongarch_store_constant_timer_config(cpu, val, guest);
>
> return old_v;
> }
>
> -target_ulong helper_gcsrwr_tcfg(CPULoongArchState *env, target_ulong val)
> +target_ulong helper_csrwr_tcfg(CPULoongArchState *env, target_ulong val)
> {
> - LoongArchCPU *cpu = env_archcpu(env);
> - CPUSysState *guest = get_sys(env, LOONGARCH_VM_LEVEL_GUEST);
> - int64_t old_v = guest->CSR_TCFG;
> -
> - cpu_loongarch_store_constant_timer_config(cpu, val, true);
> + return helper_csrwr_tcfg_impl(env, val, false);
> +}
>
> - return old_v;
> +target_ulong helper_gcsrwr_tcfg(CPULoongArchState *env, target_ulong val)
> +{
> + return helper_csrwr_tcfg_impl(env, val, true);
> }
>
> -target_ulong helper_csrwr_ticlr(CPULoongArchState *env, target_ulong val)
> +static target_ulong helper_csrwr_ticlr_impl(CPULoongArchState *env,
> + target_ulong val, bool guest)
> {
> LoongArchCPU *cpu = env_archcpu(env);
> int64_t old_v = 0;
>
> if (val & 0x1) {
> bql_lock();
> - loongarch_cpu_set_irq(cpu, IRQ_TIMER, 0);
> + loongarch_cpu_set_irq_impl(cpu, IRQ_TIMER, 0, guest);
> bql_unlock();
> }
> return old_v;
> }
>
> -target_ulong helper_csrwr_pwcl(CPULoongArchState *env, target_ulong val)
> +target_ulong helper_csrwr_ticlr(CPULoongArchState *env, target_ulong val)
> +{
> + return helper_csrwr_ticlr_impl(env, val, false);
> +}
> +
> +target_ulong helper_gcsrwr_ticlr(CPULoongArchState *env, target_ulong val)
> +{
> + return helper_csrwr_ticlr_impl(env, val, true);
> +}
> +
> +target_ulong helper_csrwr_gstat(CPULoongArchState *env, target_ulong val)
> +{
> + CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
> + int64_t old_v = host->CSR_GSTAT;
> + uint8_t old_gid = FIELD_EX64(host->CSR_GSTAT, CSR_GSTAT, GID);
> +
> + host->CSR_GSTAT = FIELD_DP64(host->CSR_GSTAT, CSR_GSTAT, PVM,
> + FIELD_EX64(val, CSR_GSTAT, PVM));
> + host->CSR_GSTAT = FIELD_DP64(host->CSR_GSTAT, CSR_GSTAT, GID,
> + FIELD_EX64(val, CSR_GSTAT, GID));
> +
> + if (old_gid != FIELD_EX64(host->CSR_GSTAT, CSR_GSTAT, GID)) {
> + tlb_flush(env_cpu(env));
> + }
> +
> + return old_v;
> +}
> +
> +target_ulong helper_csrwr_gtlbc(CPULoongArchState *env, target_ulong val)
> +{
> + CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
> + int64_t old_v = host->CSR_GTLBC;
> + uint8_t old_use_tgid = FIELD_EX64(old_v, CSR_GTLBC, USETGID);
> + uint8_t old_tgid = FIELD_EX64(old_v, CSR_GTLBC, TGID);
> +
> + host->CSR_GTLBC = val;
> + if (old_use_tgid != FIELD_EX64(host->CSR_GTLBC, CSR_GTLBC, USETGID) ||
> + old_tgid != FIELD_EX64(host->CSR_GTLBC, CSR_GTLBC, TGID)) {
> + tlb_flush(env_cpu(env));
> + }
> +
> + return old_v;
> +}
> +
> +target_ulong helper_csrwr_gintc(CPULoongArchState *env, target_ulong val)
> +{
> + CPUState *cs = env_cpu(env);
> + CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
> + CPUSysState *guest = get_sys(env, LOONGARCH_VM_LEVEL_GUEST);
> + int64_t old_v = host->CSR_GINTC;
> + uint8_t hwis = FIELD_EX64(val, CSR_GINTC, HWIS);
> + uint8_t hwip = FIELD_EX64(val, CSR_GINTC, HWIP);
> + uint8_t pending = extract64(host->CSR_ESTAT, 2, 8);
> +
> + host->CSR_GINTC = val & 0xffff00;
> + guest->CSR_ESTAT = deposit64(guest->CSR_ESTAT, 2, 8,
> + hwis | (pending & hwip));
> +
> + bql_lock();
> + if (FIELD_EX64(host->CSR_ESTAT, CSR_ESTAT, IS) & ~(hwip << 2)) {
> + cpu_interrupt(cs, CPU_INTERRUPT_HARD);
> + } else {
> + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
> + }
it is not necessary to check CSR_ESTAT of host, and inject irq to host.
> + if (env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST &&
When writing register gintc, CPU should work in host mode, is that
right? so it will never reach with env_vm_level(env) ==
LOONGARCH_VM_LEVEL_GUEST.
> + FIELD_EX64(guest->CSR_ESTAT, CSR_ESTAT, IS)) {
> + cpu_interrupt(cs, CPU_INTERRUPT_GUEST);
It should only set pending info and not necessary to kick cpu, how about
something like this:
cpu_set_interrupt(cs, CPU_INTERRUPT_GUEST);
> + } else {
> + cpu_reset_interrupt(cs, CPU_INTERRUPT_GUEST);
> + }
> + bql_unlock();
> +
> + return old_v;
> +}
> +
> +static target_ulong helper_csrwr_pwcl_impl(CPULoongArchState *env,
> + target_ulong val, bool guest)
> {
> uint8_t shift, ptbase;
> - CPUSysState *sys = env_sys(env);
> + CPUSysState *sys = get_sys(env, guest);
> int64_t old_v = sys->CSR_PWCL;
>
> /*
> @@ -190,18 +328,40 @@ target_ulong helper_csrwr_pwcl(CPULoongArchState *env, target_ulong val)
> return old_v;
> }
>
> -target_ulong helper_csrwr_pwch(CPULoongArchState *env, target_ulong val)
> +target_ulong helper_csrwr_pwcl(CPULoongArchState *env, target_ulong val)
> {
> - uint8_t has_ptw;
> - CPUSysState *sys = env_sys(env);
> + return helper_csrwr_pwcl_impl(env, val, false);
> +}
> +
> +target_ulong helper_gcsrwr_pwcl(CPULoongArchState *env, target_ulong val)
> +{
> + return helper_csrwr_pwcl_impl(env, val, true);
> +}
> +
> +static target_ulong helper_csrwr_pwch_impl(CPULoongArchState *env,
> + target_ulong val, bool guest)
> +{
> + uint8_t host_has_ptw;
> + CPUSysState *sys = get_sys(env, guest);
> int64_t old_v = sys->CSR_PWCH;
>
> val = FIELD_DP64(val, CSR_PWCH, RESERVE, 0);
> - has_ptw = FIELD_EX32(env->cpucfg[2], CPUCFG2, HPTW);
> - if (!has_ptw) {
> + //TODO: host can't read guest cpucfg
> + host_has_ptw = FIELD_EX32(env->cpucfg[2], CPUCFG2, HPTW);
> + if (!host_has_ptw) {
> val = FIELD_DP64(val, CSR_PWCH, HPTW_EN, 0);
> }
>
> sys->CSR_PWCH = val;
> return old_v;
> - }
> +}
> +
> +target_ulong helper_csrwr_pwch(CPULoongArchState *env, target_ulong val)
> +{
> + return helper_csrwr_pwch_impl(env, val, false);
> +}
> +
> +target_ulong helper_gcsrwr_pwch(CPULoongArchState *env, target_ulong val)
> +{
> + return helper_csrwr_pwch_impl(env, val, true);
> +}
> diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h
> index b87de15a99..8e0ddd8693 100644
> --- a/target/loongarch/tcg/helper.h
> +++ b/target/loongarch/tcg/helper.h
> @@ -98,18 +98,28 @@ DEF_HELPER_1(rdtime_d, i64, env)
> #ifndef CONFIG_USER_ONLY
> /* CSRs helper */
> DEF_HELPER_1(csrrd_pgd, i64, env)
> +DEF_HELPER_1(gcsrrd_pgd, i64, env)
> DEF_HELPER_1(csrrd_cpuid, i64, env)
> DEF_HELPER_1(csrrd_tval, i64, env)
> DEF_HELPER_1(gcsrrd_tval, i64, env)
> DEF_HELPER_1(csrrd_msgir, i64, env)
> DEF_HELPER_2(csrwr_stlbps, i64, env, tl)
> +DEF_HELPER_2(gcsrwr_stlbps, i64, env, tl)
> DEF_HELPER_2(csrwr_estat, i64, env, tl)
> +DEF_HELPER_2(gcsrwr_estat, i64, env, tl)
> DEF_HELPER_2(csrwr_asid, i64, env, tl)
> +DEF_HELPER_2(gcsrwr_asid, i64, env, tl)
> DEF_HELPER_2(csrwr_tcfg, i64, env, tl)
> DEF_HELPER_2(gcsrwr_tcfg, i64, env, tl)
> DEF_HELPER_2(csrwr_ticlr, i64, env, tl)
> +DEF_HELPER_2(gcsrwr_ticlr, i64, env, tl)
> +DEF_HELPER_2(csrwr_gstat, i64, env, tl)
> +DEF_HELPER_2(csrwr_gtlbc, i64, env, tl)
> +DEF_HELPER_2(csrwr_gintc, i64, env, tl)
It will be better if it is split into two patches, one is about
gstat/gtlbc/gintc, the other is gcsrwr/gcsrrd operation.
Regards
Bibo Mao
> DEF_HELPER_2(csrwr_pwcl, i64, env, tl)
> +DEF_HELPER_2(gcsrwr_pwcl, i64, env, tl)
> DEF_HELPER_2(csrwr_pwch, i64, env, tl)
> +DEF_HELPER_2(gcsrwr_pwch, i64, env, tl)
> DEF_HELPER_2(iocsrrd_b, i64, env, tl)
> DEF_HELPER_2(iocsrrd_h, i64, env, tl)
> DEF_HELPER_2(iocsrrd_w, i64, env, tl)
>
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2026-09-02 9:56 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-26 10:30 [PATCH v4 00/14] target/loongarch: add LVZ support for TCG SignKirigami
2026-08-26 10:30 ` [PATCH v4 01/14] target/loongarch: Define LVZ CSR fields SignKirigami
2026-08-31 2:27 ` Bibo Mao
2026-08-26 10:30 ` [PATCH v4 02/14] target/loongarch: Add LVZ CPU state definitions SignKirigami
2026-08-31 2:56 ` Bibo Mao
2026-08-26 10:30 ` [PATCH v4 03/14] target/loongarch: Add LVZ CSR metadata SignKirigami
2026-09-01 7:08 ` Bibo Mao
2026-09-02 3:09 ` SignKirigami
2026-08-26 10:30 ` [PATCH v4 04/14] target/loongarch: Add LVZ CPU model support SignKirigami
2026-09-02 3:09 ` Bibo Mao
2026-08-26 10:30 ` [PATCH v4 05/14] target/loongarch: Add LVZ guest interrupt state SignKirigami
2026-09-02 3:33 ` Bibo Mao
2026-08-26 10:30 ` [PATCH v4 06/14] target/loongarch: Add LVZ guest timer support SignKirigami
2026-08-26 10:30 ` [PATCH v4 07/14] target/loongarch: Add LVZ VM entry and exception handling SignKirigami
2026-09-02 9:19 ` Bibo Mao
2026-08-26 10:30 ` [PATCH v4 08/14] target/loongarch: Add LVZ guest CSR helpers SignKirigami
2026-09-02 9:55 ` Bibo Mao
2026-08-26 10:30 ` [PATCH v4 09/14] target/loongarch: Add LVZ migration state SignKirigami
2026-08-26 10:30 ` [PATCH v4 10/14] target/loongarch: Add LVZ MMU and PTW support SignKirigami
2026-08-26 10:30 ` [PATCH v4 11/14] target/loongarch: Translate LVZ guest CSR instructions SignKirigami
2026-08-26 10:30 ` [PATCH v4 12/14] target/loongarch: Translate LVZ guest TLB instructions SignKirigami
2026-08-26 10:30 ` [PATCH v4 13/14] target/loongarch: Translate LVZ guest traps and HVC SignKirigami
2026-08-26 10:30 ` [PATCH v4 14/14] fix(tcg/loongarch): strip flag bits in lddir/ldpte address calculation SignKirigami
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.