* [PATCH v6 00/26] add LVZ support for TCG
@ 2026-11-13 8:45 SignKirigami
2026-11-13 8:45 ` [PATCH v6 01/26] target/loongarch: add get_sys() helper SignKirigami
` (25 more replies)
0 siblings, 26 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, 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 (26):
target/loongarch: add get_sys() helper
target/loongarch: add LVZ feature switcher
target/loongarch: add LVZ CSR metadata
target/loongarch: add vm level 1 definition
target/loongarch: add definition about guest mmu_idx
target/loongarch: move timer initialization to
loongarh_cpu_realizefn()
target/loongarch: add LVZ guest interrupt state
target/loongarch: add LVZ guest timer
target/loongarch: handle return to guest in ertn
target/loongarch: handle LVZ exception and vm_exit
target/loongarch: add host TLBRet and handle
target/loongarch: add gid management functions
target/loongarch: add LVZ parameters to TLB functions
target/loongarch: add LVZ to TLB functions
target/loongarch: add 2-level address mapping to
loongarch_cpu_tlb_fill()
target/loongarch: add LVZ to invtlb 0x0-0x6
target/loongarch: add LVZ to invtlb 0xa-0xe
target/loongarch: add LVZ to ptw
target/loongarch: add LVZ migration state
target/loongarch: add LVZ to CSR operations
target/loongarch: add LVZ to CSR helpers
target/loongarch: add guest CSR operations
target/loongarch: add LVZ CSRs
target/loongarch: translate LVZ guest traps and HVC
target/loongarch: initialize CSR related to LVZ
target/loongarch: exit to vm level 0 for fpd, sxd and asxd of vm level
0
target/loongarch/cpu-csr.h | 38 ++
target/loongarch/cpu-mmu.h | 31 +-
target/loongarch/cpu.c | 117 +++++--
target/loongarch/cpu.h | 90 ++++-
target/loongarch/cpu_helper.c | 52 +--
target/loongarch/csr.c | 102 +++---
target/loongarch/csr.h | 1 +
target/loongarch/disas.c | 9 +
target/loongarch/insns.decode | 7 +
target/loongarch/internals.h | 4 +-
target/loongarch/machine.c | 139 ++++++++
target/loongarch/tcg/csr_helper.c | 131 +++++--
target/loongarch/tcg/helper.h | 37 +-
.../tcg/insn_trans/trans_extra.c.inc | 5 +
.../tcg/insn_trans/trans_privileged.c.inc | 212 ++++++++++--
target/loongarch/tcg/op_helper.c | 37 ++
target/loongarch/tcg/tcg_cpu.c | 89 +++--
target/loongarch/tcg/tlb_helper.c | 324 ++++++++++++------
target/loongarch/tcg/translate.c | 3 +
target/loongarch/translate.h | 1 +
20 files changed, 1107 insertions(+), 322 deletions(-)
base-commit: 49559567804523cb28cad002aa8e97f444e23ad7
--
2.56.0
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v6 01/26] target/loongarch: add get_sys() helper
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 02/26] target/loongarch: add LVZ feature switcher SignKirigami
` (24 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add get_sys() to retrieve a CPUSysState by VM level. Use it to access
the host state in CPU reset path to handle possible reset requests under
vm_level 1.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu.c | 2 +-
target/loongarch/cpu.h | 5 +++++
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index 1f3022265a..5a88ecf522 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -624,7 +624,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 *sys = get_sys(env, VM_LEVEL0);
if (lacc->parent_phases.hold) {
lacc->parent_phases.hold(obj, type);
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 8dae4e12cf..f39925fd69 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -520,6 +520,11 @@ static inline void set_sys(CPULoongArchState *env, int vm_level)
}
}
+static inline CPUSysState *get_sys(CPULoongArchState *env, int vm_level)
+{
+ return &env->sys_states[vm_level];
+}
+
static inline CPUTimerState *env_timer(CPULoongArchState *env)
{
return &env->sys_states[0].timer_state;
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 02/26] target/loongarch: add LVZ feature switcher
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
2026-11-13 8:45 ` [PATCH v6 01/26] target/loongarch: add get_sys() helper SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 03/26] target/loongarch: add LVZ CSR metadata SignKirigami
` (23 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add LVZ property and AVAIL_LVZ macro based on cpucfg.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu.c | 29 +++++++++++++++++++++++++++--
target/loongarch/cpu.h | 6 ++++++
target/loongarch/translate.h | 1 +
3 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index 5a88ecf522..d8f3af4f31 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -270,11 +270,27 @@ 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;
+ 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;
@@ -286,6 +302,11 @@ static void loongarch_cpu_post_init(Object *obj)
loongarch_set_msgint);
object_property_add_bool(obj, "ptw", loongarch_get_ptw,
loongarch_set_ptw);
+ if (tcg_enabled()) {
+ 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()) {
@@ -339,8 +360,12 @@ static void loongarch_la464_initfn(Object *obj)
data = FIELD_DP32(data, CPUCFG2, FP_SP, 1);
data = FIELD_DP32(data, CPUCFG2, FP_DP, 1);
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, LSX, 1);
+ data = FIELD_DP32(data, CPUCFG2, LASX, 1);
+ if (tcg_enabled()) {
+ 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);
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index f39925fd69..623b57e332 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -462,6 +462,7 @@ struct ArchCPU {
CPULoongArchState env;
uint32_t phy_id;
OnOffAuto lbt;
+ OnOffAuto lvz;
OnOffAuto pmu;
OnOffAuto ptw;
OnOffAuto lsx;
@@ -525,6 +526,11 @@ static inline CPUSysState *get_sys(CPULoongArchState *env, int vm_level)
return &env->sys_states[vm_level];
}
+static inline bool cpu_has_lvz(CPULoongArchState *env)
+{
+ return FIELD_EX32(env->cpucfg[2], CPUCFG2, LVZ);
+}
+
static inline CPUTimerState *env_timer(CPULoongArchState *env)
{
return &env->sys_states[0].timer_state;
diff --git a/target/loongarch/translate.h b/target/loongarch/translate.h
index 8aa8325dc6..a7c165982c 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))
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 03/26] target/loongarch: add LVZ CSR metadata
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
2026-11-13 8:45 ` [PATCH v6 01/26] target/loongarch: add get_sys() helper SignKirigami
2026-11-13 8:45 ` [PATCH v6 02/26] target/loongarch: add LVZ feature switcher SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 04/26] target/loongarch: add vm level 1 definition SignKirigami
` (22 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add LVZ host and guest CSR access metadata to the existing CSR table. Mark
guest read-only 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-csr.h | 38 ++++++++++++++
target/loongarch/cpu.h | 6 +++
target/loongarch/csr.c | 102 ++++++++++++++++++++-----------------
target/loongarch/csr.h | 1 +
target/loongarch/disas.c | 5 ++
5 files changed, 104 insertions(+), 48 deletions(-)
diff --git a/target/loongarch/cpu-csr.h b/target/loongarch/cpu-csr.h
index d860417af2..e0061e57c3 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_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)
+
+#define LOONGARCH_CSR_GSTAT 0x50 /* Guest status */
+FIELD(CSR_GSTAT, VM, 0, 1)
+FIELD(CSR_GSTAT, PVM, 1, 1)
+FIELD(CSR_GSTAT, GIDBITS, 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 */
+
#endif /* LOONGARCH_CPU_CSR_H */
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 623b57e332..05309336d2 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -255,6 +255,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
@@ -382,6 +383,10 @@ typedef struct CPUSysState {
uint64_t CSR_DBG;
uint64_t CSR_DERA;
uint64_t CSR_DSAVE;
+ 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;
@@ -390,6 +395,7 @@ typedef struct CPUSysState {
/* Fields up to this point are cleared by a CPU reset */
struct {} end_reset_fields;
+ uint64_t CSR_GSTAT;
uint64_t CSR_ASID;
uint64_t CSR_PRCFG1;
uint64_t CSR_PRCFG2;
diff --git a/target/loongarch/csr.c b/target/loongarch/csr.c
index e474941337..4a730ca971 100644
--- a/target/loongarch/csr.c
+++ b/target/loongarch/csr.c
@@ -13,14 +13,15 @@
.flags = FL \
}
-#define CSR_OFF_ARRAY(NAME, N) \
- [LOONGARCH_CSR_##NAME(N)] = { \
- .name = (stringify(NAME##N)), \
- .offset = CSR_OFFSET(CSR_##NAME[N]), \
- .flags = CSRFL_BASIC \
- }
+#define CSR_OFF_ARRAY_FLAGS(NAME, N, FL) \
+ [LOONGARCH_CSR_##NAME(N)] = { \
+ .name = (stringify(NAME##N)), \
+ .offset = CSR_OFFSET(CSR_##NAME[N]), \
+ .flags = FL \
+ }
#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),
@@ -70,9 +71,14 @@ static CSRInfo csr_info[] = {
CSR_OFF_FLAGS(TVAL, CSRFL_READONLY | CSRFL_IO),
CSR_OFF(CNTC),
CSR_OFF_FLAGS(TICLR, CSRFL_IO),
+ CSR_OFF_FLAGS(GTLBC, CSRFL_GSPR),
+ 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),
@@ -86,46 +92,46 @@ static CSRInfo csr_info[] = {
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_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 0, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 1, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 1, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 2, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 2, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 3, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 3, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 4, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 4, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 5, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 5, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 6, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 6, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 7, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 7, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 8, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 8, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 9, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 9, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 10, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 10, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 11, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 11, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 12, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 12, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 13, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 13, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 14, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 14, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCTRL, 15, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(PERFCNTR, 15, CSRFL_GSPR),
+ 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_GSPR),
+ CSR_OFF_ARRAY_FLAGS(MSGIS, 1, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(MSGIS, 2, CSRFL_GSPR),
+ CSR_OFF_ARRAY_FLAGS(MSGIS, 3, CSRFL_GSPR),
+ CSR_OFF_FLAGS(MSGIR, CSRFL_GSPR),
};
CSRInfo *get_csr(unsigned int csr_num)
diff --git a/target/loongarch/csr.h b/target/loongarch/csr.h
index c2b6b882bc..393ac09b61 100644
--- a/target/loongarch/csr.h
+++ b/target/loongarch/csr.h
@@ -20,6 +20,7 @@ enum {
CSRFL_IO = (1 << 2),
CSRFL_UNUSED = (1 << 3),
CSRFL_BASIC = (1 << 4),
+ CSRFL_GSPR = (1 << 5),
};
typedef struct {
diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c
index 928b8b94c2..03d1e560dd 100644
--- a/target/loongarch/disas.c
+++ b/target/loongarch/disas.c
@@ -87,6 +87,11 @@ static const char * const csr_names[] = {
CSR_NAME(TVAL),
CSR_NAME(CNTC),
CSR_NAME(TICLR),
+ CSR_NAME(GTLBC),
+ CSR_NAME(GSTAT),
+ CSR_NAME(GCFG),
+ CSR_NAME(GINTC),
+ CSR_NAME(GCNTC),
CSR_NAME(LLBCTL),
CSR_NAME(IMPCTL1),
CSR_NAME(IMPCTL2),
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 04/26] target/loongarch: add vm level 1 definition
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (2 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 03/26] target/loongarch: add LVZ CSR metadata SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 05/26] target/loongarch: add definition about guest mmu_idx SignKirigami
` (21 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add VM_LEVEL1 macro, env_vm_level() and trigger_vm_exit() functions, preparing
for later interrupt virtualization.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu.h | 22 +++++++++++++++++++++-
target/loongarch/tcg/tcg_cpu.c | 3 +++
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 05309336d2..ff22cde702 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -324,7 +324,8 @@ typedef struct CPUTimerState {
} CPUTimerState;
#define VM_LEVEL0 0
-#define VM_LEVELS 1
+#define VM_LEVEL1 1
+#define VM_LEVELS 2
#define CPU_VENDOR_LOONGSON "Loongson"
#define CPU_MODEL_3A5000 "3A5000"
@@ -448,6 +449,7 @@ typedef struct CPUArchState {
#endif
int vm_level;
unsigned int irq_nr;
+ bool vm_exit;
} CPULoongArchState;
typedef struct LoongArchCPUTopo {
@@ -520,6 +522,11 @@ static inline CPUSysState *env_sys(CPULoongArchState *env)
return &env->sys_states[env->vm_level];
}
+static inline int env_vm_level(CPULoongArchState *env)
+{
+ return env_sys(env) - env->sys_states;
+}
+
static inline void set_sys(CPULoongArchState *env, int vm_level)
{
if (vm_level >= 0 && vm_level < VM_LEVELS) {
@@ -537,6 +544,18 @@ static inline bool cpu_has_lvz(CPULoongArchState *env)
return FIELD_EX32(env->cpucfg[2], CPUCFG2, LVZ);
}
+static inline void trigger_vm_exit(CPULoongArchState *env)
+{
+ CPUSysState *host = get_sys(env, VM_LEVEL0);
+
+ if (env_vm_level(env) != VM_LEVEL1) {
+ return;
+ }
+
+ host->CSR_GSTAT = FIELD_DP64(host->CSR_GSTAT, CSR_GSTAT, PVM, 1);
+ env->vm_exit = true;
+}
+
static inline CPUTimerState *env_timer(CPULoongArchState *env)
{
return &env->sys_states[0].timer_state;
@@ -583,6 +602,7 @@ 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
#define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU
diff --git a/target/loongarch/tcg/tcg_cpu.c b/target/loongarch/tcg/tcg_cpu.c
index 0549506035..c2792c8db1 100644
--- a/target/loongarch/tcg/tcg_cpu.c
+++ b/target/loongarch/tcg/tcg_cpu.c
@@ -239,6 +239,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) == VM_LEVEL1) {
+ 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;
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 05/26] target/loongarch: add definition about guest mmu_idx
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (3 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 04/26] target/loongarch: add vm level 1 definition SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 06/26] target/loongarch: move timer initialization to loongarh_cpu_realizefn() SignKirigami
` (20 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add MMU_GUEST_IDX and MMU_GUEST_DA_IDX. Provide is_guest_mmu idx() and
mmu_idx_to_plv to calculate plv with new mmu_idx.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu.h | 18 ++++++++++++++++++
.../tcg/insn_trans/trans_privileged.c.inc | 2 +-
target/loongarch/tcg/tcg_cpu.c | 7 +++++++
target/loongarch/tcg/translate.c | 3 +++
4 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index ff22cde702..14316fc9c5 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -516,6 +516,24 @@ 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 (MMU_GUEST_IDX + MMU_DA_IDX)
+
+static inline int mmu_idx_vm_level(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 (mmu_idx_vm_level(mmu_idx)) {
+ return mmu_idx - MMU_GUEST_IDX;
+ }
+ return mmu_idx;
+}
static inline CPUSysState *env_sys(CPULoongArchState *env)
{
diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
index 6728ce5ec9..7ee3bb8e27 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;
}
diff --git a/target/loongarch/tcg/tcg_cpu.c b/target/loongarch/tcg/tcg_cpu.c
index c2792c8db1..7babc1a1f9 100644
--- a/target/loongarch/tcg/tcg_cpu.c
+++ b/target/loongarch/tcg/tcg_cpu.c
@@ -269,6 +269,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) == VM_LEVEL1) {
+ 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);
}
diff --git a/target/loongarch/tcg/translate.c b/target/loongarch/tcg/translate.c
index 124dce6269..4ab06b48da 100644
--- a/target/loongarch/tcg/translate.c
+++ b/target/loongarch/tcg/translate.c
@@ -129,6 +129,9 @@ static void loongarch_tr_init_disas_context(DisasContextBase *dcbase,
} else {
ctx->mem_idx = MMU_DA_IDX;
}
+ if (ctx->base.tb->flags & HW_FLAGS_GUEST_MODE) {
+ ctx->mem_idx += MMU_GUEST_IDX;
+ }
/* Bound the number of insns to execute to those left on the page. */
bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4;
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 06/26] target/loongarch: move timer initialization to loongarh_cpu_realizefn()
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (4 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 05/26] target/loongarch: add definition about guest mmu_idx SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 07/26] target/loongarch: add LVZ guest interrupt state SignKirigami
` (19 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
After guest_timer initialization is added, irq_nr needs to be determined
using cpu_has_lvz(), which relys on CPUCFG LVZ bit. So move it for
preparation.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu.c | 37 +++++++++++++++----------------------
1 file changed, 15 insertions(+), 22 deletions(-)
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index d8f3af4f31..fc432cfda0 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -721,6 +721,21 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp)
CPUState *cs = CPU(dev);
LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev);
Error *local_err = NULL;
+ CPULoongArchState *env = cpu_env(cs);
+
+#ifdef CONFIG_TCG
+ CPUTimerState *timer;
+#endif
+
+ env->irq_nr = N_IRQS;
+ qdev_init_gpio_in(dev, loongarch_cpu_set_irq, env->irq_nr);
+#ifdef CONFIG_TCG
+ timer = env_timer(env);
+ timer->irq = IRQ_TIMER;
+ timer->cs = cs;
+ timer_init_ns(&timer->timer, QEMU_CLOCK_VIRTUAL,
+ &cpu_loongarch_timer_cb, timer);
+#endif
cpu_common_realize(cs, &local_err);
if (local_err != NULL) {
@@ -748,27 +763,6 @@ static void loongarch_cpu_unrealizefn(DeviceState *dev)
lacc->parent_unrealize(dev);
}
-static void loongarch_cpu_init(Object *obj)
-{
-#ifndef CONFIG_USER_ONLY
- LoongArchCPU *cpu = LOONGARCH_CPU(obj);
- CPULoongArchState *env = &cpu->env;
-#ifdef CONFIG_TCG
- CPUTimerState *timer;
-#endif
-
- env->irq_nr = N_IRQS;
- qdev_init_gpio_in(DEVICE(cpu), loongarch_cpu_set_irq, env->irq_nr);
-#ifdef CONFIG_TCG
- timer = env_timer(env);
- timer->irq = IRQ_TIMER;
- timer->cs = CPU(obj);
- timer_init_ns(&timer->timer, QEMU_CLOCK_VIRTUAL,
- &cpu_loongarch_timer_cb, timer);
-#endif
-#endif
-}
-
static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model)
{
ObjectClass *oc;
@@ -1002,7 +996,6 @@ static const TypeInfo loongarch_cpu_type_infos[] = {
.parent = TYPE_CPU,
.instance_size = sizeof(LoongArchCPU),
.instance_align = __alignof(LoongArchCPU),
- .instance_init = loongarch_cpu_init,
.abstract = true,
.class_size = sizeof(LoongArchCPUClass),
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 07/26] target/loongarch: add LVZ guest interrupt state
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (5 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 06/26] target/loongarch: move timer initialization to loongarh_cpu_realizefn() SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 08/26] target/loongarch: add LVZ guest timer SignKirigami
` (18 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, 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 | 32 +++++++++++++++++++--------
target/loongarch/cpu.h | 3 +++
target/loongarch/internals.h | 4 ++--
target/loongarch/tcg/csr_helper.c | 2 +-
target/loongarch/tcg/tcg_cpu.c | 36 ++++++++++++++++++++++---------
5 files changed, 55 insertions(+), 22 deletions(-)
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index fc432cfda0..20209c1bfc 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -58,16 +58,20 @@ static vaddr loongarch_cpu_get_pc(CPUState *cs)
#ifndef CONFIG_USER_ONLY
#include "hw/loongarch/virt.h"
-void loongarch_cpu_update_irq(LoongArchCPU *cpu)
+void loongarch_cpu_update_irq(LoongArchCPU *cpu, int vm_level)
{
CPULoongArchState *env = &cpu->env;
CPUState *cs = CPU(cpu);
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
+ int interrupt = vm_level ? CPU_INTERRUPT_GUEST : CPU_INTERRUPT_HARD;
+ if (vm_level == VM_LEVEL1 && env_vm_level(env) != VM_LEVEL1) {
+ return;
+ }
if (FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS)) {
- cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+ cpu_interrupt(cs, interrupt);
} else {
- cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+ cpu_reset_interrupt(cs, interrupt);
}
}
@@ -75,13 +79,19 @@ static void loongarch_cpu_self_set_irq(CPUState *cs, run_on_cpu_data data)
{
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
CPULoongArchState *env = cpu_env(cs);
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys;
+ int vm_level = 0;
int irq, level;
irq = data.host_int & ~BIT(31);
level = (data.host_int >> 31) & 1;
+ if (irq >= N_IRQS) {
+ irq -= N_IRQS;
+ vm_level = 1;
+ }
+ sys = get_sys(env, vm_level);
sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, irq, 1, level != 0);
- loongarch_cpu_update_irq(cpu);
+ loongarch_cpu_update_irq(cpu, vm_level);
}
void loongarch_cpu_set_irq(void *opaque, int irq, int level)
@@ -104,11 +114,11 @@ void loongarch_cpu_set_irq(void *opaque, int irq, int level)
}
/* 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, int vm_level)
{
uint32_t pending;
uint32_t status;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
pending = FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS);
status = FIELD_EX64(sys->CSR_ECFG, CSR_ECFG, LIE);
@@ -123,7 +133,11 @@ static 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), VM_LEVEL0)) {
+ has_work = true;
+ }
+ if (cpu_test_interrupt(cs, CPU_INTERRUPT_GUEST) &&
+ cpu_loongarch_hw_interrupts_pending(cpu_env(cs), VM_LEVEL1)) {
has_work = true;
}
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 14316fc9c5..22f2b0aa47 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 */
@@ -242,6 +244,7 @@ extern const char * const fregnames[32];
#define IRQ_TIMER 11
#define IRQ_IPI 12
#define INT_DMSI 14
+#define N_VIRQS (N_IRQS * 2)
#define MAX_PERF_EVENTS 16
#define LOONGARCH_STLB 2048 /* 2048 STLB */
diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h
index bc32bba7f7..616afd2e94 100644
--- a/target/loongarch/internals.h
+++ b/target/loongarch/internals.h
@@ -31,14 +31,14 @@ void restore_fp_status(CPULoongArchState *env);
#ifndef CONFIG_USER_ONLY
extern const VMStateDescription vmstate_loongarch_cpu;
-void loongarch_cpu_update_irq(LoongArchCPU *cpu);
+void loongarch_cpu_update_irq(LoongArchCPU *cpu, int vm_level);
void loongarch_cpu_set_irq(void *opaque, int irq, int level);
void cpu_loongarch_timer_cb(void *opaque);
uint64_t cpu_loongarch_get_timer_counter(CPUTimerState *timer);
uint64_t cpu_loongarch_get_timer_ticks(CPUTimerState *timer);
void cpu_loongarch_set_timer_config(CPUTimerState *timer, uint64_t value);
-bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env);
+bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env, int vm_level);
#endif /* !CONFIG_USER_ONLY */
const char *loongarch_exception_name(int exception);
diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c
index 7cba8035df..e0fcac8da8 100644
--- a/target/loongarch/tcg/csr_helper.c
+++ b/target/loongarch/tcg/csr_helper.c
@@ -111,7 +111,7 @@ target_ulong HELPER(csrwr_estat)(CPULoongArchState *env, target_ulong val)
*/
if (sys->CSR_ESTAT != old_v) {
bql_lock();
- loongarch_cpu_update_irq(env_archcpu(env));
+ loongarch_cpu_update_irq(env_archcpu(env), false);
bql_unlock();
}
diff --git a/target/loongarch/tcg/tcg_cpu.c b/target/loongarch/tcg/tcg_cpu.c
index 7babc1a1f9..8765bda0d1 100644
--- a/target/loongarch/tcg/tcg_cpu.c
+++ b/target/loongarch/tcg/tcg_cpu.c
@@ -198,10 +198,10 @@ 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, int vm_level)
{
bool ret = 0;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
ret = (FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, IE) &&
!(FIELD_EX64(sys->CSR_DBG, CSR_DBG, DST)));
@@ -211,17 +211,33 @@ static inline bool cpu_loongarch_hw_interrupts_enabled(CPULoongArchState *env)
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, VM_LEVEL0) &&
+ cpu_loongarch_hw_interrupts_pending(env, VM_LEVEL0)) {
+ if (env_vm_level(env) != VM_LEVEL0) {
+ trigger_vm_exit(env);
+ }
+ has_interrupt = true;
}
+ } else if (interrupt_request & CPU_INTERRUPT_GUEST) {
+ if (cpu_loongarch_hw_interrupts_enabled(env, VM_LEVEL1) &&
+ cpu_loongarch_hw_interrupts_pending(env, VM_LEVEL1) &&
+ env_vm_level(env) == VM_LEVEL1) {
+ has_interrupt = true;
+ }
+ }
+
+ if (has_interrupt) {
+ /* Raise it */
+ cs->exception_index = EXCCODE_INT;
+ loongarch_cpu_do_interrupt(cs);
+ return true;
}
+
return false;
}
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 08/26] target/loongarch: add LVZ guest timer
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (6 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 07/26] target/loongarch: add LVZ guest interrupt state SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 09/26] target/loongarch: handle return to guest in ertn SignKirigami
` (17 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add a separate guest timer and IRQ_VTIMER. To use cpu_has_ptw(), timer init should
be after loongarch_la464_initfn() when CPUCFG is set.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu.c | 16 ++++++++++++++--
target/loongarch/cpu.h | 6 ++++++
2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index 20209c1bfc..3a351e43cc 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -738,10 +738,14 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp)
CPULoongArchState *env = cpu_env(cs);
#ifdef CONFIG_TCG
- CPUTimerState *timer;
+ CPUTimerState *timer, *guest_timer;
#endif
- env->irq_nr = N_IRQS;
+ if (cpu_has_lvz(env)) {
+ env->irq_nr = N_VIRQS;
+ } else {
+ env->irq_nr = N_IRQS;
+ }
qdev_init_gpio_in(dev, loongarch_cpu_set_irq, env->irq_nr);
#ifdef CONFIG_TCG
timer = env_timer(env);
@@ -749,6 +753,14 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp)
timer->cs = cs;
timer_init_ns(&timer->timer, QEMU_CLOCK_VIRTUAL,
&cpu_loongarch_timer_cb, timer);
+
+ if (cpu_has_lvz(env)) {
+ guest_timer = env_guest_timer(env);
+ guest_timer->irq = IRQ_VTIMER;
+ guest_timer->cs = cs;
+ timer_init_ns(&guest_timer->timer, QEMU_CLOCK_VIRTUAL,
+ &cpu_loongarch_timer_cb, guest_timer);
+ }
#endif
cpu_common_realize(cs, &local_err);
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 22f2b0aa47..c1a59882f7 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -245,6 +245,7 @@ extern const char * const fregnames[32];
#define IRQ_IPI 12
#define INT_DMSI 14
#define N_VIRQS (N_IRQS * 2)
+#define IRQ_VTIMER (N_IRQS + IRQ_TIMER)
#define MAX_PERF_EVENTS 16
#define LOONGARCH_STLB 2048 /* 2048 STLB */
@@ -582,6 +583,11 @@ static inline CPUTimerState *env_timer(CPULoongArchState *env)
return &env->sys_states[0].timer_state;
}
+static inline CPUTimerState *env_guest_timer(CPULoongArchState *env)
+{
+ return &env->sys_states[1].timer_state;
+}
+
static inline bool is_la64(CPULoongArchState *env)
{
return FIELD_EX32(env->cpucfg[1], CPUCFG1, ARCH) == CPUCFG1_ARCH_LA64;
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 09/26] target/loongarch: handle return to guest in ertn
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (7 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 08/26] target/loongarch: add LVZ guest timer SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 10/26] target/loongarch: handle LVZ exception and vm_exit SignKirigami
` (16 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add will_return_to_guest() function, and handle process of return to guest at
end of helper_ertn().
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu.h | 8 ++++++++
target/loongarch/tcg/op_helper.c | 14 ++++++++++++++
2 files changed, 22 insertions(+)
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index c1a59882f7..3c21b7633e 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -578,6 +578,14 @@ static inline void trigger_vm_exit(CPULoongArchState *env)
env->vm_exit = true;
}
+static inline bool will_return_to_guest(CPULoongArchState *env)
+{
+ if (!cpu_has_lvz(env) || env_vm_level(env) == VM_LEVEL1) {
+ return false;
+ }
+ return FIELD_EX64(get_sys(env, VM_LEVEL0)->CSR_GSTAT, CSR_GSTAT, PVM);
+}
+
static inline CPUTimerState *env_timer(CPULoongArchState *env)
{
return &env->sys_states[0].timer_state;
diff --git a/target/loongarch/tcg/op_helper.c b/target/loongarch/tcg/op_helper.c
index f8a91bed5d..7b77ac49a6 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"
@@ -110,6 +111,8 @@ void HELPER(ertn)(CPULoongArchState *env)
{
uint64_t csr_pplv, csr_pie;
CPUSysState *sys = env_sys(env);
+ CPUSysState *host = get_sys(env, VM_LEVEL0);
+ CPUSysState *guest = get_sys(env, VM_LEVEL1);
if (FIELD_EX64(sys->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
csr_pplv = FIELD_EX64(sys->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV);
@@ -133,6 +136,17 @@ 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, VM_LEVEL1);
+ 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)
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 10/26] target/loongarch: handle LVZ exception and vm_exit
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (8 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 09/26] target/loongarch: handle return to guest in ertn SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 11/26] target/loongarch: add host TLBRet and handle SignKirigami
` (15 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add EXCCODE_GSPR and EXCCODE_HVC macros. Execute real vm exit at end of
loongarch_cpu_do_interrupt().
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu.h | 2 ++
target/loongarch/tcg/tcg_cpu.c | 26 ++++++++++++++++++--------
2 files changed, 20 insertions(+), 8 deletions(-)
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 3c21b7633e..42305e4b05 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -95,6 +95,8 @@ 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_DBP EXCODE(26, 0) /* Reserved subcode used for debug */
/* cpucfg[0] bits */
diff --git a/target/loongarch/tcg/tcg_cpu.c b/target/loongarch/tcg/tcg_cpu.c
index 8765bda0d1..1301a9c6ad 100644
--- a/target/loongarch/tcg/tcg_cpu.c
+++ b/target/loongarch/tcg/tcg_cpu.c
@@ -37,9 +37,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, VM_LEVEL0);
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;
@@ -55,17 +57,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(sys->CSR_DBG, CSR_DBG, DCL, 1);
+ host->CSR_DBG = FIELD_DP64(sys->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(sys->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;
@@ -76,6 +78,8 @@ static void loongarch_cpu_do_interrupt(CPUState *cs)
update_badinstr = 0;
break;
case EXCCODE_BCE:
+ case EXCCODE_GSPR:
+ case EXCCODE_HVC:
sys->CSR_BADV = env->pc;
QEMU_FALLTHROUGH;
case EXCCODE_SYS:
@@ -178,6 +182,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, VM_LEVEL0);
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_GUEST);
+ }
+ env->vm_exit = false;
}
static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 11/26] target/loongarch: add host TLBRet and handle
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (9 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 10/26] target/loongarch: handle LVZ exception and vm_exit SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 12/26] target/loongarch: add gid management functions SignKirigami
` (14 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add host value in enum TLBRet, and use it in raise_mmu_exception().
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu-mmu.h | 8 ++++++++
target/loongarch/tcg/tlb_helper.c | 18 ++++++++++++++++--
2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h
index 491eb514d9..17fb92a213 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 {
diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c
index 5662b2c49a..33d5e04eb4 100644
--- a/target/loongarch/tcg/tlb_helper.c
+++ b/target/loongarch/tcg/tlb_helper.c
@@ -48,15 +48,24 @@ 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) == VM_LEVEL1 && 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;
@@ -68,6 +77,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;
@@ -78,24 +88,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,
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 12/26] target/loongarch: add gid management functions
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (10 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 11/26] target/loongarch: add host TLBRet and handle SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 13/26] target/loongarch: add LVZ parameters to TLB functions SignKirigami
` (13 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add get_gid(), get_tgid() and tlb_entry_matches_gid(), to get and use gid in
lvz functions.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu.h | 13 +++++++++++++
target/loongarch/tcg/tlb_helper.c | 5 +++++
2 files changed, 18 insertions(+)
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 42305e4b05..9987d47734 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -588,6 +588,19 @@ static inline bool will_return_to_guest(CPULoongArchState *env)
return FIELD_EX64(get_sys(env, VM_LEVEL0)->CSR_GSTAT, CSR_GSTAT, PVM);
}
+static inline uint8_t get_gid(CPULoongArchState *env, int vm_level)
+{
+ CPUSysState *host = get_sys(env, VM_LEVEL0);
+
+ if (vm_level) {
+ return FIELD_EX64(host->CSR_GSTAT, CSR_GSTAT, GID);
+ }
+ if (FIELD_EX64(host->CSR_GTLBC, CSR_GTLBC, USETGID)) {
+ return FIELD_EX64(host->CSR_GTLBC, CSR_GTLBC, TGID);
+ }
+ return 0;
+}
+
static inline CPUTimerState *env_timer(CPULoongArchState *env)
{
return &env->sys_states[0].timer_state;
diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c
index 33d5e04eb4..178f55a67e 100644
--- a/target/loongarch/tcg/tlb_helper.c
+++ b/target/loongarch/tcg/tlb_helper.c
@@ -34,6 +34,11 @@ static bool tlb_match_asid(bool global, int asid, int tlb_asid)
return !global && tlb_asid == asid;
}
+static inline bool tlb_entry_matches_gid(const LoongArchTLB *tlb, uint8_t gid)
+{
+ return FIELD_EX64(tlb->tlb_misc, TLB_MISC, GID) == gid;
+}
+
bool check_ps(CPULoongArchState *env, uint8_t tlb_ps)
{
CPUSysState *sys = env_sys(env);
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 13/26] target/loongarch: add LVZ parameters to TLB functions
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (11 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 12/26] target/loongarch: add gid management functions SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 14/26] target/loongarch: add LVZ " SignKirigami
` (12 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add parameters related to LVZ (mainly vm_level) to statements and definitions
of TLB, so that LVZ patches to TLB can be divided into parts later.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu-mmu.h | 21 ++--
target/loongarch/cpu_helper.c | 33 ++---
target/loongarch/tcg/helper.h | 10 +-
.../tcg/insn_trans/trans_privileged.c.inc | 30 +++--
target/loongarch/tcg/tlb_helper.c | 117 ++++++++++--------
5 files changed, 120 insertions(+), 91 deletions(-)
diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h
index 17fb92a213..20fe1cd836 100644
--- a/target/loongarch/cpu-mmu.h
+++ b/target/loongarch/cpu-mmu.h
@@ -38,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, int vm_level)
{
CPUSysState *sys = env_sys(env);
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,
+ int vm_level)
{
uint8_t present;
- if (cpu_has_ptw(env)) {
+ if (cpu_has_ptw(env, vm_level)) {
present = FIELD_EX64(entry, TLBENTRY, P);
} else {
present = FIELD_EX64(entry, TLBENTRY, V);
@@ -58,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,
+ int vm_level)
{
uint8_t writable;
- if (cpu_has_ptw(env)) {
+ if (cpu_has_ptw(env, vm_level)) {
writable = FIELD_EX64(entry, TLBENTRY, W);
} else {
writable = FIELD_EX64(entry, TLBENTRY, D);
@@ -102,11 +104,14 @@ TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context,
MMUAccessType access_type, int mmu_idx);
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, 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, int vm_level);
+TLBRet loongarch_map_address(CPULoongArchState *env, MMUContext *context,
+ MMUAccessType access_type, int mmu_idx,
+ int is_debug, uintptr_t retaddr);
bool loongarch_cpu_translate_for_debug(CPUState *cpu, vaddr addr,
TranslateForDebugResult *result);
uint64_t loongarch_palen_mask(CPULoongArchState *env);
diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c
index 6b98fff691..0e7390cd88 100644
--- a/target/loongarch/cpu_helper.c
+++ b/target/loongarch/cpu_helper.c
@@ -20,7 +20,7 @@
#include "tcg/tcg_loongarch.h"
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, int vm_level)
{
CPUSysState *sys = env_sys(env);
@@ -56,11 +56,12 @@ TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context,
uint64_t tlb_entry, tlb_ppn;
uint8_t tlb_ps, tlb_plv, tlb_nx, tlb_nr, tlb_rplv;
bool tlb_v, tlb_d;
+ int vm_level = mmu_idx_vm_level(mmu_idx);
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, vm_level);
+ tlb_d = pte_write(env, tlb_entry, vm_level);
tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV);
if (is_la64(env)) {
tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN);
@@ -116,12 +117,13 @@ TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context,
}
TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context,
- int access_type, int mmu_idx, int debug)
+ int access_type, int mmu_idx, int debug, uintptr_t retaddr)
{
hwaddr index = 0, phys = 0;
uint64_t palen_mask = loongarch_palen_mask(env);
uint64_t dir_base, dir_width;
uint64_t base, pte, old;
+ int vm_level = mmu_idx_vm_level(mmu_idx);
int level, ptw_idx = MMU_DA_IDX;
vaddr address;
TLBRet ret;
@@ -137,7 +139,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, vm_level);
if (dir_width == 0) {
continue;
@@ -175,7 +177,7 @@ 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, vm_level)) {
uint64_t val;
index &= 1;
@@ -198,7 +200,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, vm_level)) {
if (access_type == MMU_DATA_STORE && pte_dirty(base)) {
return ret;
}
@@ -242,10 +244,9 @@ 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, uintptr_t retaddr)
{
TLBRet ret;
@@ -262,7 +263,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,
+ retaddr);
}
return TLBRET_NOMATCH;
@@ -281,7 +283,7 @@ 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;
@@ -327,7 +329,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,
+ retaddr);
}
bool loongarch_cpu_translate_for_debug(CPUState *cs, vaddr addr,
@@ -338,7 +341,7 @@ bool loongarch_cpu_translate_for_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 false;
}
*result = (TranslateForDebugResult) {
diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h
index 7ee655b027..4643ec14d5 100644
--- a/target/loongarch/tcg/helper.h
+++ b/target/loongarch/tcg/helper.h
@@ -122,11 +122,11 @@ 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_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 7ee3bb8e27..db637249f1 100644
--- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
@@ -323,24 +323,28 @@ static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
}
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 2:
- gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(1));
+ case 0x2:
+ gen_helper_invtlb_all_g(tcg_env, rj, tcg_constant_i32(1),
+ tcg_constant_i32(0));
break;
- case 3:
- gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(0));
+ case 0x3:
+ gen_helper_invtlb_all_g(tcg_env, rj, tcg_constant_i32(0),
+ tcg_constant_i32(0));
break;
- case 4:
- gen_helper_invtlb_all_asid(tcg_env, rj);
+ case 0x4:
+ gen_helper_invtlb_all_asid(tcg_env, rj, tcg_constant_i32(0));
break;
- case 5:
- gen_helper_invtlb_page_asid(tcg_env, rj, rk);
+ case 0x5:
+ gen_helper_invtlb_page_asid(tcg_env, rj, rk, tcg_constant_i32(0));
break;
- case 6:
- gen_helper_invtlb_page_asid_or_g(tcg_env, rj, rk);
+ case 0x6:
+ gen_helper_invtlb_page_asid_or_g(tcg_env, rj, rk,
+ tcg_constant_i32(0));
break;
default:
return false;
diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c
index 178f55a67e..8ce3528871 100644
--- a/target/loongarch/tcg/tlb_helper.c
+++ b/target/loongarch/tcg/tlb_helper.c
@@ -131,7 +131,7 @@ static void raise_mmu_exception(CPULoongArchState *env, vaddr address,
}
}
-static void invalidate_tlb_entry(CPULoongArchState *env, int index)
+static void invalidate_tlb_entry(CPULoongArchState *env, int index, int vm_level)
{
target_ulong addr, mask, pagesize;
uint8_t tlb_ps;
@@ -146,20 +146,20 @@ 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, vm_level);
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, vm_level);
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, int vm_level)
{
LoongArchTLB *tlb;
uint16_t csr_asid, tlb_asid, tlb_g;
@@ -180,11 +180,12 @@ static void invalidate_tlb(CPULoongArchState *env, int index)
if (tlb_g == 0 && tlb_asid != csr_asid) {
return;
}
- invalidate_tlb_entry(env, index);
+ invalidate_tlb_entry(env, index, vm_level);
}
/* 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,
+ int vm_level)
{
uint64_t lo0, lo1, csr_vppn;
uint8_t csr_ps;
@@ -217,7 +218,7 @@ static void sptw_prepare_context(CPULoongArchState *env, MMUContext *context)
}
static void fill_tlb_entry(CPULoongArchState *env, LoongArchTLB *tlb,
- MMUContext *context)
+ MMUContext *context, int vm_level)
{
uint64_t lo0, lo1, csr_vppn;
uint16_t csr_asid;
@@ -256,9 +257,11 @@ static uint32_t get_random_tlb(uint32_t low, uint32_t high)
* field in tlb entry contains bit[47:13], so need adjust.
* virt_vpn = vaddr[47:13]
*/
-static const LoongArchTLB *
-loongarch_tlb_search_cb(CPULoongArchState *env, vaddr vaddr,
- int csr_asid, tlb_match func)
+
+static const LoongArchTLB *loongarch_tlb_search_cb(CPULoongArchState *env,
+ vaddr vaddr, int csr_asid,
+ tlb_match func, int vm_level,
+ uint8_t gid)
{
const LoongArchTLB *tlb;
uint16_t tlb_asid, stlb_idx;
@@ -310,7 +313,7 @@ loongarch_tlb_search_cb(CPULoongArchState *env, vaddr vaddr,
}
static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr,
- int *index)
+ int *index, int vm_level, uint8_t gid)
{
int csr_asid;
tlb_match func;
@@ -319,7 +322,7 @@ static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr,
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, csr_asid, func, vm_level, gid);
if (tlb) {
*index = tlb - env->tlb;
return true;
@@ -332,11 +335,14 @@ void HELPER(tlbsrch)(CPULoongArchState *env)
{
int index, match;
CPUSysState *sys = env_sys(env);
+ int vm_level = env_vm_level(env);
if (FIELD_EX64(sys->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
- match = loongarch_tlb_search(env, sys->CSR_TLBREHI, &index);
+ match = loongarch_tlb_search(env, sys->CSR_TLBREHI,
+ &index, vm_level, get_gid(env, vm_level));
} else {
- match = loongarch_tlb_search(env, sys->CSR_TLBEHI, &index);
+ match = loongarch_tlb_search(env, sys->CSR_TLBEHI,
+ &index, vm_level, get_gid(env, vm_level));
}
if (match) {
@@ -381,18 +387,18 @@ void HELPER(tlbrd)(CPULoongArchState *env)
}
static void update_tlb_index(CPULoongArchState *env, MMUContext *context,
- int index)
+ int index, int vm_level)
{
LoongArchTLB *old, new = {};
bool skip_inv = false, tlb_v0, tlb_v1;
old = env->tlb + index;
- fill_tlb_entry(env, &new, context);
+ fill_tlb_entry(env, &new, context, vm_level);
/* Check whether ASID/VPPN is the same */
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, vm_level);
+ tlb_v1 = pte_present(env, old->tlb_entry1, vm_level);
if ((!tlb_v0 || new.tlb_entry0 == old->tlb_entry0) &&
(!tlb_v1 || new.tlb_entry1 == old->tlb_entry1)) {
skip_inv = true;
@@ -401,7 +407,7 @@ static void update_tlb_index(CPULoongArchState *env, MMUContext *context,
/* flush tlb before updating the entry */
if (!skip_inv) {
- invalidate_tlb(env, index);
+ invalidate_tlb(env, index, vm_level);
}
*old = new;
@@ -412,18 +418,19 @@ void HELPER(tlbwr)(CPULoongArchState *env)
CPUSysState *sys = env_sys(env);
int index = FIELD_EX64(sys->CSR_TLBIDX, CSR_TLBIDX, INDEX);
MMUContext context;
+ int vm_level = env_vm_level(env);
if (FIELD_EX64(sys->CSR_TLBIDX, CSR_TLBIDX, NE)) {
- invalidate_tlb(env, index);
+ invalidate_tlb(env, index, vm_level);
return;
}
- sptw_prepare_context(env, &context);
- update_tlb_index(env, &context, index);
+ sptw_prepare_context(env, &context, vm_level);
+ update_tlb_index(env, &context, index, vm_level);
}
static int get_tlb_random_index(CPULoongArchState *env, vaddr addr,
- int pagesize)
+ int pagesize, int vm_level)
{
uint64_t address;
int index, set, i, stlb_idx;
@@ -493,6 +500,7 @@ void HELPER(tlbfill)(CPULoongArchState *env)
int index, pagesize;
MMUContext context;
CPUSysState *sys = env_sys(env);
+ int vm_level = env_vm_level(env);
if (FIELD_EX64(sys->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
entryhi = sys->CSR_TLBREHI;
@@ -504,10 +512,10 @@ void HELPER(tlbfill)(CPULoongArchState *env)
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, vm_level);
+ index = get_tlb_random_index(env, entryhi, pagesize, vm_level);
+ invalidate_tlb(env, index, vm_level);
+ fill_tlb_entry(env, env->tlb + index, &context, vm_level);
}
void HELPER(tlbclr)(CPULoongArchState *env)
@@ -570,7 +578,8 @@ void HELPER(tlbflush)(CPULoongArchState *env)
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 vm_level)
{
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc,
@@ -579,7 +588,8 @@ void HELPER(invtlb_all)(CPULoongArchState *env)
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 vm_level)
{
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
LoongArchTLB *tlb = &env->tlb[i];
@@ -592,7 +602,8 @@ void HELPER(invtlb_all_g)(CPULoongArchState *env, uint32_t g)
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 vm_level)
{
uint16_t asid = info & R_CSR_ASID_ASID_MASK;
@@ -609,43 +620,45 @@ 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 vm_level)
{
int asid = info & 0x3ff;
const LoongArchTLB *tlb;
tlb_match func;
func = tlb_match_asid;
- tlb = loongarch_tlb_search_cb(env, addr, asid, func);
+ tlb = loongarch_tlb_search_cb(env, addr, asid, func, env_vm_level(env), 0);
if (tlb) {
- invalidate_tlb(env, tlb - env->tlb);
+ invalidate_tlb(env, tlb - env->tlb, env_vm_level(env));
}
}
-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 vm_level)
{
int asid = info & 0x3ff;
const LoongArchTLB *tlb;
tlb_match func;
func = tlb_match_any;
- tlb = loongarch_tlb_search_cb(env, addr, asid, func);
+ tlb = loongarch_tlb_search_cb(env, addr, asid, func,
+ env_vm_level(env), 0);
if (tlb) {
- invalidate_tlb(env, tlb - env->tlb);
+ invalidate_tlb(env, tlb - env->tlb, env_vm_level(env));
}
}
-static void ptw_update_tlb(CPULoongArchState *env, MMUContext *context)
+static void ptw_update_tlb(CPULoongArchState *env, MMUContext *context,
+ int vm_level)
{
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, vm_level);
}
- update_tlb_index(env, context, index);
+ update_tlb_index(env, context, index, vm_level);
}
bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
@@ -661,9 +674,9 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
/* Data access */
context.addr = address;
context.tlb_index = -1;
- ret = get_physical_address(env, &context, access_type, mmu_idx, 0);
+ ret = get_physical_address(env, &context, access_type, mmu_idx, 0, retaddr);
if (ret == TLBRET_MATCH && context.mmu_index != MMU_DA_IDX
- && cpu_has_ptw(env)) {
+ && cpu_has_ptw(env, env_vm_level(env))) {
bool need_update = true;
if (access_type == MMU_DATA_STORE && pte_dirty(context.pte)) {
@@ -678,15 +691,15 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
}
}
- if (ret != TLBRET_MATCH && cpu_has_ptw(env)) {
+ if (ret != TLBRET_MATCH && cpu_has_ptw(env, env_vm_level(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);
+ ret = loongarch_ptw(env, &context, access_type, mmu_idx, 0, retaddr);
if (ret == TLBRET_MATCH) {
- ptw_update_tlb(env, &context);
+ ptw_update_tlb(env, &context, env_vm_level(env));
}
} else if (context.tlb_index >= 0) {
- invalidate_tlb(env, context.tlb_index);
+ invalidate_tlb(env, context.tlb_index, env_vm_level(env));
}
}
@@ -701,7 +714,7 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
* Store walks have already handled D, but context.pte may still
* contain its old value. Direct mappings have no PTE to check.
*/
- if (cpu_has_ptw(env) && context.mmu_index != MMU_DA_IDX &&
+ if (cpu_has_ptw(env, env_vm_level(env)) && context.mmu_index != MMU_DA_IDX &&
access_type != MMU_DATA_STORE && !pte_dirty(context.pte)) {
prot &= ~PAGE_WRITE;
}
@@ -788,7 +801,8 @@ 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));
index = (badvaddr >> dir_base) & ((1 << dir_width) - 1);
phys = base | index << 3;
val = cpu_ldq_le_data_ra(env, phys, GETPC());
@@ -825,7 +839,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));
base = FIELD_DP64(base, TLBENTRY, LEVEL, 0);
base = FIELD_DP64(base, TLBENTRY, HUGE, 0);
@@ -893,8 +908,10 @@ TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env,
MMUAccessType access_type, int mmu_idx)
{
int index, match;
+ int vm_level = env_vm_level(env);
- match = loongarch_tlb_search(env, context->addr, &index);
+ match = loongarch_tlb_search(env, context->addr, &index,
+ vm_level, get_gid(env, vm_level));
if (match) {
return loongarch_map_tlb_entry(env, context, access_type, index,
mmu_idx);
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 14/26] target/loongarch: add LVZ to TLB functions
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (12 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 13/26] target/loongarch: add LVZ parameters to TLB functions SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 15/26] target/loongarch: add 2-level address mapping to loongarch_cpu_tlb_fill() SignKirigami
` (11 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add LVZ to most TLB handler, except invtlb and ptw.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu.h | 1 +
target/loongarch/cpu_helper.c | 13 ++++----
target/loongarch/tcg/tlb_helper.c | 50 ++++++++++++++++++++++---------
3 files changed, 44 insertions(+), 20 deletions(-)
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 9987d47734..f27c4ada07 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -262,6 +262,7 @@ FIELD(TLB_MISC, ASID, 1, 10)
FIELD(TLB_MISC, VPPN, 13, 35)
FIELD(TLB_MISC, PS, 48, 6)
FIELD(TLB_MISC, GID, 54, 8)
+FIELD(TLB_MISC, VM_LEVEL, 62, 1)
/*Msg interrupt registers */
#define N_MSGIS 4
diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c
index 0e7390cd88..245bb24b35 100644
--- a/target/loongarch/cpu_helper.c
+++ b/target/loongarch/cpu_helper.c
@@ -22,7 +22,7 @@
void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
uint64_t *dir_width, unsigned int level, int vm_level)
{
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
switch (level) {
case 1:
@@ -52,7 +52,7 @@ void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context,
MMUAccessType access_type, int mmu_idx)
{
- 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;
@@ -285,16 +285,17 @@ TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context,
MMUAccessType access_type, int mmu_idx,
int is_debug, uintptr_t retaddr)
{
- int user_mode = mmu_idx == MMU_USER_IDX;
- int kernel_mode = mmu_idx == MMU_KERNEL_IDX;
+ int user_mode = mmu_idx_to_plv(mmu_idx) == MMU_USER_IDX;
+ int kernel_mode = mmu_idx_to_plv(mmu_idx) == MMU_KERNEL_IDX;
+ int vm_level = mmu_idx_vm_level(mmu_idx);
uint32_t plv, base_c, base_v;
int64_t addr_high;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
vaddr address;
/* Check PG and DA */
address = context->addr;
- if (mmu_idx == MMU_DA_IDX) {
+ if (mmu_idx == MMU_DA_IDX || mmu_idx == MMU_GUEST_DA_IDX) {
context->physical = address & loongarch_palen_mask(env);
context->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
context->mmu_index = MMU_DA_IDX;
diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c
index 8ce3528871..0c93d0fcb4 100644
--- a/target/loongarch/tcg/tlb_helper.c
+++ b/target/loongarch/tcg/tlb_helper.c
@@ -18,6 +18,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"
@@ -39,9 +41,15 @@ static inline bool tlb_entry_matches_gid(const LoongArchTLB *tlb, uint8_t gid)
return FIELD_EX64(tlb->tlb_misc, TLB_MISC, GID) == gid;
}
+static inline bool tlb_entry_matches_vm_level(const LoongArchTLB *tlb,
+ uint8_t vm_level)
+{
+ return FIELD_EX64(tlb->tlb_misc, TLB_MISC, VM_LEVEL) == vm_level;
+}
+
bool check_ps(CPULoongArchState *env, uint8_t tlb_ps)
{
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, VM_LEVEL0);
if (tlb_ps >= 64) {
return false;
@@ -136,7 +144,9 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index, int vm_level
target_ulong addr, mask, pagesize;
uint8_t tlb_ps;
LoongArchTLB *tlb = &env->tlb[index];
- int idxmap = BIT(MMU_KERNEL_IDX) | BIT(MMU_USER_IDX);
+ int bit_offset = vm_level ? MMU_GUEST_IDX : 0;
+ int idxmap = (BIT(MMU_KERNEL_IDX + bit_offset)
+ | BIT(MMU_USER_IDX + bit_offset));
uint64_t tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
bool tlb_v;
@@ -164,7 +174,7 @@ static void invalidate_tlb(CPULoongArchState *env, int index, int vm_level)
LoongArchTLB *tlb;
uint16_t csr_asid, tlb_asid, tlb_g;
uint8_t tlb_e;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
csr_asid = FIELD_EX64(sys->CSR_ASID, CSR_ASID, ASID);
tlb = &env->tlb[index];
@@ -189,7 +199,7 @@ static void sptw_prepare_context(CPULoongArchState *env, MMUContext *context,
{
uint64_t lo0, lo1, csr_vppn;
uint8_t csr_ps;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
if (FIELD_EX64(sys->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
csr_ps = FIELD_EX64(sys->CSR_TLBREHI, CSR_TLBREHI, PS);
@@ -223,7 +233,7 @@ static void fill_tlb_entry(CPULoongArchState *env, LoongArchTLB *tlb,
uint64_t lo0, lo1, csr_vppn;
uint16_t csr_asid;
uint8_t csr_ps;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
csr_vppn = context->addr >> R_TLB_MISC_VPPN_SHIFT;
csr_ps = context->ps;
@@ -236,6 +246,8 @@ static void fill_tlb_entry(CPULoongArchState *env, LoongArchTLB *tlb,
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_gid(env, vm_level));
+ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VM_LEVEL, vm_level);
tlb->tlb_entry0 = lo0;
tlb->tlb_entry1 = lo1;
@@ -269,7 +281,7 @@ static const LoongArchTLB *loongarch_tlb_search_cb(CPULoongArchState *env,
bool tlb_g;
int i, compare_shift;
uint64_t vpn, tlb_vppn;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
stlb_ps = FIELD_EX64(sys->CSR_STLBPS, CSR_STLBPS, PS);
vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1);
@@ -286,6 +298,8 @@ static const 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) &&
+ tlb_entry_matches_vm_level(tlb, vm_level) &&
(vpn == (tlb_vppn >> compare_shift))) {
return tlb;
}
@@ -304,6 +318,8 @@ static const 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) &&
+ tlb_entry_matches_vm_level(tlb, vm_level) &&
(vpn == (tlb_vppn >> compare_shift))) {
return tlb;
}
@@ -318,7 +334,7 @@ static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr,
int csr_asid;
tlb_match func;
const LoongArchTLB *tlb;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
func = tlb_match_any;
csr_asid = FIELD_EX64(sys->CSR_ASID, CSR_ASID, ASID);
@@ -437,7 +453,7 @@ static int get_tlb_random_index(CPULoongArchState *env, vaddr addr,
uint16_t asid, tlb_asid, stlb_ps;
LoongArchTLB *tlb;
uint8_t tlb_e, tlb_g;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
/* Validity of stlb_ps is checked in helper_csrwr_stlbps() */
stlb_ps = FIELD_EX64(sys->CSR_STLBPS, CSR_STLBPS, PS);
@@ -457,7 +473,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_gid(env, vm_level))) {
set = i;
}
}
@@ -481,7 +498,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_gid(env, vm_level))) {
index = i;
}
}
@@ -524,6 +542,7 @@ void HELPER(tlbclr)(CPULoongArchState *env)
int i, index;
uint16_t csr_asid, tlb_asid, tlb_g;
CPUSysState *sys = env_sys(env);
+ int vm_level = env_vm_level(env);
csr_asid = FIELD_EX64(sys->CSR_ASID, CSR_ASID, ASID);
index = FIELD_EX64(sys->CSR_TLBIDX, CSR_TLBIDX, INDEX);
@@ -534,7 +553,8 @@ void HELPER(tlbclr)(CPULoongArchState *env)
tlb = &env->tlb[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_gid(env, vm_level))) {
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
}
}
@@ -544,7 +564,8 @@ void HELPER(tlbclr)(CPULoongArchState *env)
tlb = &env->tlb[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_gid(env, vm_level))) {
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
}
}
@@ -724,7 +745,8 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
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, mmu_idx_vm_level(mmu_idx));
return true;
} else {
qemu_log_mask(CPU_LOG_MMU,
@@ -908,7 +930,7 @@ TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env,
MMUAccessType access_type, int mmu_idx)
{
int index, match;
- int vm_level = env_vm_level(env);
+ int vm_level = mmu_idx_vm_level(mmu_idx);
match = loongarch_tlb_search(env, context->addr, &index,
vm_level, get_gid(env, vm_level));
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 15/26] target/loongarch: add 2-level address mapping to loongarch_cpu_tlb_fill()
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (13 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 14/26] target/loongarch: add LVZ " SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 16/26] target/loongarch: add LVZ to invtlb 0x0-0x6 SignKirigami
` (10 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Use a while loop to map GVA to HPA.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/tcg/tlb_helper.c | 49 +++++++++++++++++++++++--------
1 file changed, 36 insertions(+), 13 deletions(-)
diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c
index 0c93d0fcb4..82b4ef4333 100644
--- a/target/loongarch/tcg/tlb_helper.c
+++ b/target/loongarch/tcg/tlb_helper.c
@@ -695,7 +695,42 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
/* Data access */
context.addr = address;
context.tlb_index = -1;
- ret = get_physical_address(env, &context, access_type, mmu_idx, 0, retaddr);
+ context.prot = PAGE_RWX;
+ int curr_mmu_idx = mmu_idx;
+
+ while (1) {
+ prot = context.prot;
+ ret = get_physical_address(env, &context, access_type, curr_mmu_idx, 0,
+ retaddr);
+ if (ret != TLBRET_MATCH) {
+ address = context.addr;
+ ret += TLBRET_HOST_MATCH *
+ (mmu_idx_vm_level(mmu_idx) - mmu_idx_vm_level(curr_mmu_idx));
+ break;
+ } else {
+ /*
+ * Loads or instruction fetches must not cache write access to a
+ * clean PTE. The first store must enter PTW to set D, whether this
+ * translation came from the LoongArch TLB or a page table walk.
+ * Store walks have already handled D, but context.pte may still
+ * contain its old value. Direct mappings have no PTE to check.
+ */
+ if (cpu_has_ptw(env, mmu_idx_vm_level(curr_mmu_idx)) &&
+ context.mmu_index != MMU_DA_IDX &&
+ access_type != MMU_DATA_STORE && !pte_dirty(context.pte)) {
+ prot &= ~PAGE_WRITE;
+ }
+ }
+ context.addr = context.physical;
+ context.tlb_index = -1;
+ context.prot &= prot;
+ if (mmu_idx_vm_level(curr_mmu_idx)) {
+ curr_mmu_idx = (mmu_idx_vm_level(curr_mmu_idx) - 1) * MMU_KERNEL_IDX;
+ } else {
+ break;
+ }
+ }
+
if (ret == TLBRET_MATCH && context.mmu_index != MMU_DA_IDX
&& cpu_has_ptw(env, env_vm_level(env))) {
bool need_update = true;
@@ -728,18 +763,6 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
physical = context.physical;
prot = context.prot;
- /*
- * Loads or instruction fetches must not cache write access to a
- * clean PTE. The first store must enter PTW to set D, whether this
- * translation came from the LoongArch TLB or a page table walk.
- * Store walks have already handled D, but context.pte may still
- * contain its old value. Direct mappings have no PTE to check.
- */
- if (cpu_has_ptw(env, env_vm_level(env)) && context.mmu_index != MMU_DA_IDX &&
- access_type != MMU_DATA_STORE && !pte_dirty(context.pte)) {
- prot &= ~PAGE_WRITE;
- }
-
tlb_set_page(cs, address & TARGET_PAGE_MASK,
physical & TARGET_PAGE_MASK, prot,
mmu_idx, TARGET_PAGE_SIZE);
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 16/26] target/loongarch: add LVZ to invtlb 0x0-0x6
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (14 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 15/26] target/loongarch: add 2-level address mapping to loongarch_cpu_tlb_fill() SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 17/26] target/loongarch: add LVZ to invtlb 0xa-0xe SignKirigami
` (9 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add guest handling to invtlb 0x0-0x6, vm_level stays ignored.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/tcg/tlb_helper.c | 25 +++++++++++++++++--------
1 file changed, 17 insertions(+), 8 deletions(-)
diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c
index 82b4ef4333..4bf9dacd0c 100644
--- a/target/loongarch/tcg/tlb_helper.c
+++ b/target/loongarch/tcg/tlb_helper.c
@@ -602,9 +602,12 @@ void HELPER(tlbflush)(CPULoongArchState *env)
void HELPER(invtlb_all)(CPULoongArchState *env, target_ulong info, uint32_t op,
uint32_t vm_level)
{
+ uint16_t gid = get_gid(env, env_vm_level(env));
+
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 = &env->tlb[i];
+ if ((op == 0 && env_vm_level(env) == 0) || tlb_entry_matches_gid(tlb, gid))
+ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
}
tlb_flush(env_cpu(env));
}
@@ -612,11 +615,13 @@ void HELPER(invtlb_all)(CPULoongArchState *env, target_ulong info, uint32_t op,
void HELPER(invtlb_all_g)(CPULoongArchState *env, target_ulong info, uint32_t g,
uint32_t vm_level)
{
+ uint16_t gid = get_gid(env, env_vm_level(env));
+
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
LoongArchTLB *tlb = &env->tlb[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);
}
}
@@ -627,13 +632,14 @@ void HELPER(invtlb_all_asid)(CPULoongArchState *env, target_ulong info,
uint32_t vm_level)
{
uint16_t asid = info & R_CSR_ASID_ASID_MASK;
+ uint16_t gid = get_gid(env, env_vm_level(env));
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
LoongArchTLB *tlb = &env->tlb[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);
}
}
@@ -643,12 +649,14 @@ void HELPER(invtlb_all_asid)(CPULoongArchState *env, target_ulong info,
void HELPER(invtlb_page_asid)(CPULoongArchState *env, target_ulong info,
target_ulong addr, uint32_t vm_level)
{
- int asid = info & 0x3ff;
+
+ uint16_t asid = info & R_CSR_ASID_ASID_MASK;
+ uint16_t gid = get_gid(env, env_vm_level(env));
const LoongArchTLB *tlb;
tlb_match func;
func = tlb_match_asid;
- tlb = loongarch_tlb_search_cb(env, addr, asid, func, env_vm_level(env), 0);
+ tlb = loongarch_tlb_search_cb(env, addr, asid, func, env_vm_level(env), gid);
if (tlb) {
invalidate_tlb(env, tlb - env->tlb, env_vm_level(env));
}
@@ -657,13 +665,14 @@ void HELPER(invtlb_page_asid)(CPULoongArchState *env, target_ulong info,
void HELPER(invtlb_page_asid_or_g)(CPULoongArchState *env, target_ulong info,
target_ulong addr, uint32_t vm_level)
{
- int asid = info & 0x3ff;
+ uint16_t asid = info & R_CSR_ASID_ASID_MASK;
+ uint16_t gid = get_gid(env, env_vm_level(env));
const LoongArchTLB *tlb;
tlb_match func;
func = tlb_match_any;
tlb = loongarch_tlb_search_cb(env, addr, asid, func,
- env_vm_level(env), 0);
+ env_vm_level(env), gid);
if (tlb) {
invalidate_tlb(env, tlb - env->tlb, env_vm_level(env));
}
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 17/26] target/loongarch: add LVZ to invtlb 0xa-0xe
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (15 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 16/26] target/loongarch: add LVZ to invtlb 0x0-0x6 SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 18/26] target/loongarch: add LVZ to ptw SignKirigami
` (8 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Use vm_level to determine whether operation is for guest under host.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
.../tcg/insn_trans/trans_privileged.c.inc | 34 ++++++++++++++
target/loongarch/tcg/tlb_helper.c | 46 +++++++++++++++----
2 files changed, 70 insertions(+), 10 deletions(-)
diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
index db637249f1..5575bba9e1 100644
--- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
@@ -322,6 +322,10 @@ static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
return false;
}
+ if (!avail_LVZ(ctx) && a->imm > 0x6) {
+ return false;
+ }
+
switch (a->imm) {
case 0x0:
case 0x1:
@@ -346,6 +350,36 @@ static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
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 0xa:
+ gen_helper_invtlb_all_g(tcg_env, rj, tcg_constant_i32(1),
+ tcg_constant_i32(1));
+ break;
+ case 0xb:
+ gen_helper_invtlb_all_g(tcg_env, rj, tcg_constant_i32(0),
+ tcg_constant_i32(1));
+ break;
+ case 0xc:
+ gen_helper_invtlb_all_asid(tcg_env, rj, tcg_constant_i32(1));
+ break;
+ case 0xd:
+ gen_helper_invtlb_page_asid(tcg_env, rj, rk, tcg_constant_i32(1));
+ break;
+ 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/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c
index 4bf9dacd0c..1248b44a46 100644
--- a/target/loongarch/tcg/tlb_helper.c
+++ b/target/loongarch/tcg/tlb_helper.c
@@ -602,11 +602,17 @@ void HELPER(tlbflush)(CPULoongArchState *env)
void HELPER(invtlb_all)(CPULoongArchState *env, target_ulong info, uint32_t op,
uint32_t vm_level)
{
- uint16_t gid = get_gid(env, env_vm_level(env));
+ uint16_t gid = vm_level ? (info & 0xff) : get_gid(env, env_vm_level(env));
+
+ if (vm_level && env_vm_level(env) == VM_LEVEL1) {
+ do_raise_exception(env, EXCCODE_IPE, GETPC());
+ }
+
+ vm_level |= env_vm_level(env) == VM_LEVEL1;
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
LoongArchTLB *tlb = &env->tlb[i];
- if ((op == 0 && env_vm_level(env) == 0) || tlb_entry_matches_gid(tlb, gid))
+ if ((op == 0 && vm_level == 0) || tlb_entry_matches_gid(tlb, gid))
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
}
tlb_flush(env_cpu(env));
@@ -615,7 +621,13 @@ void HELPER(invtlb_all)(CPULoongArchState *env, target_ulong info, uint32_t op,
void HELPER(invtlb_all_g)(CPULoongArchState *env, target_ulong info, uint32_t g,
uint32_t vm_level)
{
- uint16_t gid = get_gid(env, env_vm_level(env));
+ uint16_t gid = vm_level ? (info & 0xff) : get_gid(env, env_vm_level(env));
+
+ if (vm_level && env_vm_level(env) == VM_LEVEL1) {
+ do_raise_exception(env, EXCCODE_IPE, GETPC());
+ }
+
+ vm_level |= env_vm_level(env) == VM_LEVEL1;
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
LoongArchTLB *tlb = &env->tlb[i];
@@ -632,7 +644,12 @@ void HELPER(invtlb_all_asid)(CPULoongArchState *env, target_ulong info,
uint32_t vm_level)
{
uint16_t asid = info & R_CSR_ASID_ASID_MASK;
- uint16_t gid = get_gid(env, env_vm_level(env));
+ uint16_t gid = vm_level ? ((info >> 16) & 0xff) : get_gid(env, env_vm_level(env));
+
+ if (vm_level) {
+ do_raise_exception(env, EXCCODE_IPE, GETPC());
+ }
+ vm_level |= env_vm_level(env);
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
LoongArchTLB *tlb = &env->tlb[i];
@@ -651,14 +668,19 @@ void HELPER(invtlb_page_asid)(CPULoongArchState *env, target_ulong info,
{
uint16_t asid = info & R_CSR_ASID_ASID_MASK;
- uint16_t gid = get_gid(env, env_vm_level(env));
+ uint16_t gid = vm_level ? ((info >> 16) & 0xff) : get_gid(env, env_vm_level(env));
const LoongArchTLB *tlb;
tlb_match func;
+ if (vm_level && env_vm_level(env)) {
+ do_raise_exception(env, EXCCODE_IPE, GETPC());
+ }
+ vm_level |= env_vm_level(env);
+
func = tlb_match_asid;
- tlb = loongarch_tlb_search_cb(env, addr, asid, func, env_vm_level(env), gid);
+ tlb = loongarch_tlb_search_cb(env, addr, asid, func, vm_level, gid);
if (tlb) {
- invalidate_tlb(env, tlb - env->tlb, env_vm_level(env));
+ invalidate_tlb(env, tlb - env->tlb, vm_level);
}
}
@@ -666,15 +688,19 @@ void HELPER(invtlb_page_asid_or_g)(CPULoongArchState *env, target_ulong info,
target_ulong addr, uint32_t vm_level)
{
uint16_t asid = info & R_CSR_ASID_ASID_MASK;
- uint16_t gid = get_gid(env, env_vm_level(env));
+ uint16_t gid = vm_level ? ((info >> 16) & 0xff) : get_gid(env, env_vm_level(env));
const LoongArchTLB *tlb;
tlb_match func;
func = tlb_match_any;
+ if (vm_level) {
+ do_raise_exception(env, EXCCODE_IPE, GETPC());
+ }
+ vm_level |= env_vm_level(env);
tlb = loongarch_tlb_search_cb(env, addr, asid, func,
- env_vm_level(env), gid);
+ vm_level, gid);
if (tlb) {
- invalidate_tlb(env, tlb - env->tlb, env_vm_level(env));
+ invalidate_tlb(env, tlb - env->tlb, vm_level);
}
}
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 18/26] target/loongarch: add LVZ to ptw
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (16 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 17/26] target/loongarch: add LVZ to invtlb 0xa-0xe SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 19/26] target/loongarch: add LVZ migration state SignKirigami
` (7 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add function loongarch_try_ptw() and use it in both vm level 0 and 1 address
mapping.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu-mmu.h | 2 +-
target/loongarch/cpu_helper.c | 6 +--
target/loongarch/tcg/tlb_helper.c | 64 +++++++++++++++++--------------
3 files changed, 39 insertions(+), 33 deletions(-)
diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h
index 20fe1cd836..9c65d790b5 100644
--- a/target/loongarch/cpu-mmu.h
+++ b/target/loongarch/cpu-mmu.h
@@ -40,7 +40,7 @@ typedef struct MMUContext {
static inline bool cpu_has_ptw(CPULoongArchState *env, int vm_level)
{
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
return !!FIELD_EX64(sys->CSR_PWCH, CSR_PWCH, HPTW_EN);
}
diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c
index 245bb24b35..80ee656d0e 100644
--- a/target/loongarch/cpu_helper.c
+++ b/target/loongarch/cpu_helper.c
@@ -124,11 +124,11 @@ TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context,
uint64_t dir_base, dir_width;
uint64_t base, pte, old;
int vm_level = mmu_idx_vm_level(mmu_idx);
- int level, ptw_idx = MMU_DA_IDX;
+ int level, ptw_idx = vm_level ? MMU_GUEST_DA_IDX : MMU_DA_IDX;
vaddr address;
TLBRet ret;
- CPUSysState *sys = env_sys(env);
MemOpIdx oi;
+ CPUSysState *sys = get_sys(env, vm_level);
address = context->addr;
if ((address >> 63) & 0x1) {
@@ -148,7 +148,7 @@ 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 = cpu_ldq_le_mmuidx_ra(env, phys, ptw_idx, GETPC());
+ base = cpu_ldq_le_mmuidx_ra(env, phys, ptw_idx, GETPC());
if (level) {
if (FIELD_EX64(base, TLBENTRY, HUGE)) {
/* base is a huge pte */
diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c
index 1248b44a46..fd3041a877 100644
--- a/target/loongarch/tcg/tlb_helper.c
+++ b/target/loongarch/tcg/tlb_helper.c
@@ -717,6 +717,40 @@ static void ptw_update_tlb(CPULoongArchState *env, MMUContext *context,
update_tlb_index(env, context, index, vm_level);
}
+static void loongarch_try_ptw(CPULoongArchState *env, MMUContext *context,
+ MMUAccessType access_type, int mmu_index,
+ TLBRet *status, uintptr_t retaddr)
+{
+ int vm_level = mmu_idx_vm_level(mmu_index);
+
+ if (*status == TLBRET_MATCH && context->mmu_index != MMU_DA_IDX
+ && cpu_has_ptw(env, vm_level)) {
+ bool need_update = true;
+
+ if (access_type == MMU_DATA_STORE && pte_dirty(context->pte)) {
+ need_update = false;
+ } else if (access_type != MMU_DATA_STORE && pte_access(context->pte)) {
+ need_update = false;
+ }
+
+ if (need_update) {
+ *status = TLBRET_NOMATCH;
+ }
+ }
+
+ if (*status != TLBRET_MATCH && cpu_has_ptw(env, vm_level)) {
+ if (*status == TLBRET_NOMATCH || *status == TLBRET_INVALID) {
+ *status = loongarch_ptw(env, context, access_type, mmu_index, 0,
+ retaddr);
+ if (*status == TLBRET_MATCH) {
+ ptw_update_tlb(env, context, vm_level);
+ }
+ } else if (context->tlb_index >= 0) {
+ invalidate_tlb(env, context->tlb_index, vm_level);
+ }
+ }
+}
+
bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr)
@@ -732,11 +766,11 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
context.tlb_index = -1;
context.prot = PAGE_RWX;
int curr_mmu_idx = mmu_idx;
-
while (1) {
prot = context.prot;
ret = get_physical_address(env, &context, access_type, curr_mmu_idx, 0,
retaddr);
+ loongarch_try_ptw(env, &context, access_type, curr_mmu_idx, &ret, retaddr);
if (ret != TLBRET_MATCH) {
address = context.addr;
ret += TLBRET_HOST_MATCH *
@@ -766,34 +800,6 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
}
}
- if (ret == TLBRET_MATCH && context.mmu_index != MMU_DA_IDX
- && cpu_has_ptw(env, env_vm_level(env))) {
- bool need_update = true;
-
- if (access_type == MMU_DATA_STORE && pte_dirty(context.pte)) {
- need_update = false;
- } else if (access_type != MMU_DATA_STORE && pte_access(context.pte)) {
- need_update = false;
- }
-
- if (need_update) {
- /* Need update bit A/D in PTE entry, take PTW again */
- ret = TLBRET_NOMATCH;
- }
- }
-
- if (ret != TLBRET_MATCH && cpu_has_ptw(env, env_vm_level(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, retaddr);
- if (ret == TLBRET_MATCH) {
- ptw_update_tlb(env, &context, env_vm_level(env));
- }
- } else if (context.tlb_index >= 0) {
- invalidate_tlb(env, context.tlb_index, env_vm_level(env));
- }
- }
-
if (ret == TLBRET_MATCH) {
physical = context.physical;
prot = context.prot;
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 19/26] target/loongarch: add LVZ migration state
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (17 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 18/26] target/loongarch: add LVZ to ptw SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 20/26] target/loongarch: add LVZ to CSR operations SignKirigami
` (6 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, 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 | 139 +++++++++++++++++++++++++++++++++++++
1 file changed, 139 insertions(+)
diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c
index 4acab2a4a3..10dfdbd202 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"
@@ -175,11 +176,145 @@ static const VMStateDescription vmstate_pmu = {
},
};
+#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY)
+static bool tlb_needed(void *opaque)
+{
+ return tcg_enabled();
+}
+
+/* TLB state */
+static const VMStateDescription vmstate_tlb_entry = {
+ .name = "cpu/tlb_entry",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT64(tlb_misc, LoongArchTLB),
+ VMSTATE_UINT64(tlb_entry0, LoongArchTLB),
+ VMSTATE_UINT64(tlb_entry1, LoongArchTLB),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_tlb = {
+ .name = "cpu/tlb",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .needed = tlb_needed,
+ .fields = (const VMStateField[]) {
+ VMSTATE_STRUCT_ARRAY(env.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_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, VM_LEVEL0);
+ 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),
@@ -251,6 +386,10 @@ const VMStateDescription vmstate_loongarch_cpu = {
&vmstate_fpu,
&vmstate_lsx,
&vmstate_lasx,
+#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY)
+ &vmstate_tlb,
+ &vmstate_lvz,
+#endif
&vmstate_lbt,
&vmstate_msgint,
&vmstate_pmu,
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 20/26] target/loongarch: add LVZ to CSR operations
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (18 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 19/26] target/loongarch: add LVZ migration state SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 21/26] target/loongarch: add LVZ to CSR helpers SignKirigami
` (5 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add vm_level argument to CSR helpers. Make csrrd. Add LVZ handling to trans_csrrd,
trans_csrwr and trans_csrxchg.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/tcg/csr_helper.c | 29 ++++---
target/loongarch/tcg/helper.h | 22 ++---
.../tcg/insn_trans/trans_privileged.c.inc | 84 ++++++++++++++-----
3 files changed, 93 insertions(+), 42 deletions(-)
diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c
index e0fcac8da8..5ff89f0d10 100644
--- a/target/loongarch/tcg/csr_helper.c
+++ b/target/loongarch/tcg/csr_helper.c
@@ -18,7 +18,8 @@
#include "cpu-csr.h"
#include "cpu-mmu.h"
-target_ulong HELPER(csrwr_stlbps)(CPULoongArchState *env, target_ulong val)
+target_ulong HELPER(csrwr_stlbps)(CPULoongArchState *env,
+ target_ulong val, uint32_t vm_level)
{
CPUSysState *sys = env_sys(env);
int64_t old_v = sys->CSR_STLBPS;
@@ -40,7 +41,7 @@ target_ulong HELPER(csrwr_stlbps)(CPULoongArchState *env, target_ulong val)
return old_v;
}
-target_ulong HELPER(csrrd_pgd)(CPULoongArchState *env)
+target_ulong HELPER(csrrd_pgd)(CPULoongArchState *env, uint32_t vm_level)
{
int64_t v;
CPUSysState *sys = env_sys(env);
@@ -60,7 +61,7 @@ target_ulong HELPER(csrrd_pgd)(CPULoongArchState *env)
return v;
}
-target_ulong HELPER(csrrd_cpuid)(CPULoongArchState *env)
+target_ulong HELPER(csrrd_cpuid)(CPULoongArchState *env, uint32_t vm_level)
{
LoongArchCPU *lac = env_archcpu(env);
CPUSysState *sys = env_sys(env);
@@ -70,12 +71,12 @@ target_ulong HELPER(csrrd_cpuid)(CPULoongArchState *env)
return sys->CSR_CPUID;
}
-target_ulong HELPER(csrrd_tval)(CPULoongArchState *env)
+target_ulong HELPER(csrrd_tval)(CPULoongArchState *env, uint32_t vm_level)
{
return cpu_loongarch_get_timer_ticks(env_timer(env));
}
-target_ulong HELPER(csrrd_msgir)(CPULoongArchState *env)
+target_ulong HELPER(csrrd_msgir)(CPULoongArchState *env, uint32_t vm_level)
{
int irq, new;
CPUSysState *sys = env_sys(env);
@@ -97,7 +98,8 @@ target_ulong HELPER(csrrd_msgir)(CPULoongArchState *env)
return irq;
}
-target_ulong HELPER(csrwr_estat)(CPULoongArchState *env, target_ulong val)
+target_ulong HELPER(csrwr_estat)(CPULoongArchState *env,
+ target_ulong val, uint32_t vm_level)
{
CPUSysState *sys = env_sys(env);
int64_t old_v = sys->CSR_ESTAT;
@@ -118,7 +120,8 @@ target_ulong HELPER(csrwr_estat)(CPULoongArchState *env, target_ulong val)
return old_v;
}
-target_ulong HELPER(csrwr_asid)(CPULoongArchState *env, target_ulong val)
+target_ulong HELPER(csrwr_asid)(CPULoongArchState *env,
+ target_ulong val, uint32_t vm_level)
{
CPUSysState *sys = env_sys(env);
int64_t old_v = sys->CSR_ASID;
@@ -131,7 +134,8 @@ 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_tcfg)(CPULoongArchState *env,
+ target_ulong val, uint32_t vm_level)
{
CPUTimerState *timer = env_timer(env);
CPUSysState *sys = container_of(timer, CPUSysState, timer_state);
@@ -142,7 +146,8 @@ target_ulong HELPER(csrwr_tcfg)(CPULoongArchState *env, target_ulong val)
return old_v;
}
-target_ulong HELPER(csrwr_ticlr)(CPULoongArchState *env, target_ulong val)
+target_ulong HELPER(csrwr_ticlr)(CPULoongArchState *env,
+ target_ulong val, uint32_t vm_level)
{
CPUTimerState *timer = env_timer(env);
int64_t old_v = 0;
@@ -155,7 +160,8 @@ target_ulong HELPER(csrwr_ticlr)(CPULoongArchState *env, target_ulong val)
return old_v;
}
-target_ulong HELPER(csrwr_pwcl)(CPULoongArchState *env, target_ulong val)
+target_ulong HELPER(csrwr_pwcl)(CPULoongArchState *env,
+ target_ulong val, uint32_t vm_level)
{
uint8_t shift, ptbase;
CPUSysState *sys = env_sys(env);
@@ -180,7 +186,8 @@ 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_pwch)(CPULoongArchState *env,
+ target_ulong val, uint32_t vm_level)
{
uint8_t has_ptw;
CPUSysState *sys = env_sys(env);
diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h
index 4643ec14d5..1c15e7544a 100644
--- a/target/loongarch/tcg/helper.h
+++ b/target/loongarch/tcg/helper.h
@@ -95,17 +95,17 @@ DEF_HELPER_1(rdtime_d, i64, env)
#ifndef CONFIG_USER_ONLY
/* CSRs helper */
-DEF_HELPER_1(csrrd_pgd, i64, env)
-DEF_HELPER_1(csrrd_cpuid, i64, env)
-DEF_HELPER_1(csrrd_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(csrwr_ticlr, i64, env, tl)
-DEF_HELPER_2(csrwr_pwcl, i64, env, tl)
-DEF_HELPER_2(csrwr_pwch, i64, env, tl)
+DEF_HELPER_2(csrrd_pgd, i64, env, i32)
+DEF_HELPER_2(csrrd_cpuid, i64, env, i32)
+DEF_HELPER_2(csrrd_tval, i64, env, i32)
+DEF_HELPER_2(csrrd_msgir, i64, env, i32)
+DEF_HELPER_3(csrwr_stlbps, i64, env, tl, i32)
+DEF_HELPER_3(csrwr_estat, i64, env, tl, i32)
+DEF_HELPER_3(csrwr_asid, i64, env, tl, i32)
+DEF_HELPER_3(csrwr_tcfg, i64, env, tl, i32)
+DEF_HELPER_3(csrwr_ticlr, i64, env, tl, i32)
+DEF_HELPER_3(csrwr_pwcl, i64, env, tl, i32)
+DEF_HELPER_3(csrwr_pwch, i64, env, tl, i32)
DEF_HELPER_2(iocsrrd_b, i64, env, tl)
DEF_HELPER_2(iocsrrd_h, i64, env, tl)
DEF_HELPER_2(iocsrrd_w, i64, env, tl)
diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
index 5575bba9e1..39d217877d 100644
--- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
@@ -42,8 +42,8 @@ GEN_FALSE_TRANS(idle)
#else
-typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env);
-typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src);
+typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env, TCGv_i32 vm_level);
+typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src, TCGv_i32 vm_level);
static bool check_plv(DisasContext *ctx)
{
@@ -88,10 +88,14 @@ void loongarch_csr_translate_init(void)
}
#undef SET_CSR_FUNC
-static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write)
+static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr,
+ bool write, int vm_level)
{
if ((csr->flags & CSRFL_READONLY) && write) {
- return false;
+ /* host writes guest read-only CSRs is allowed */
+ if (mmu_idx_vm_level(ctx->mem_idx) || vm_level == VM_LEVEL0) {
+ return false;
+ }
}
if ((csr->flags & CSRFL_IO) && translator_io_start(&ctx->base)) {
ctx->base.is_jmp = DISAS_EXIT_UPDATE;
@@ -101,7 +105,7 @@ static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write)
return true;
}
-static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
+static bool trans_csrrd_impl(DisasContext *ctx, arg_r_csr *a, int vm_level)
{
TCGv dest;
const CSRInfo *csr;
@@ -116,21 +120,29 @@ static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
/* 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, false, vm_level);
+ if (vm_level && (csr->flags & CSRFL_GSPR)) {
+ if (mmu_idx_vm_level(ctx->mem_idx)) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ return true;
+ }
+ dest = tcg_constant_tl(0);
} else {
- offset = get_csr_offset(csr, 0);
- tcg_gen_ld_tl(dest, tcg_env, offset);
+ dest = gpr_dst(ctx, a->rd, EXT_NONE);
+ readfn = (GenCSRRead)csr->readfn;
+ if (readfn) {
+ readfn(dest, tcg_env, tcg_constant_i32(vm_level));
+ } else {
+ offset = get_csr_offset(csr, vm_level);
+ tcg_gen_ld_tl(dest, tcg_env, offset);
+ }
}
}
gen_set_gpr(a->rd, dest, EXT_NONE);
return true;
}
-static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
+static bool trans_csrwr_impl(DisasContext *ctx, arg_r_csr *a, int vm_level)
{
TCGv dest, src1;
const CSRInfo *csr;
@@ -146,18 +158,26 @@ static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
return true;
}
- if (!check_csr_flags(ctx, csr, true)) {
+ if (!check_csr_flags(ctx, csr, true, vm_level)) {
/* CSR is readonly: trap. */
return false;
}
+ if (vm_level && (csr->flags & CSRFL_GSPR)) {
+ if (mmu_idx_vm_level(ctx->mem_idx)) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ return true;
+ }
+ gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
+ return true;
+ }
src1 = gpr_src(ctx, a->rd, EXT_NONE);
writefn = (GenCSRWrite)csr->writefn;
if (writefn) {
dest = gpr_dst(ctx, a->rd, EXT_NONE);
- writefn(dest, tcg_env, src1);
+ writefn(dest, tcg_env, src1, tcg_constant_i32(vm_level));
} else {
dest = tcg_temp_new();
- offset = get_csr_offset(csr, 0);
+ offset = get_csr_offset(csr, vm_level);
tcg_gen_ld_tl(dest, tcg_env, offset);
tcg_gen_st_tl(src1, tcg_env, offset);
}
@@ -165,7 +185,7 @@ static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
return true;
}
-static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
+static bool trans_csrxchg_impl(DisasContext *ctx, arg_rr_csr *a, int vm_level)
{
TCGv src1, mask, oldv, newv, temp;
const CSRInfo *csr;
@@ -182,11 +202,20 @@ static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
return true;
}
- if (!check_csr_flags(ctx, csr, true)) {
+ if (!check_csr_flags(ctx, csr, true, vm_level)) {
/* CSR is readonly: trap. */
return false;
}
+ if (vm_level && (csr->flags & CSRFL_GSPR)) {
+ if (mmu_idx_vm_level(ctx->mem_idx)) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ return true;
+ }
+ gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
+ return true;
+ }
+
/* So far only readonly csrs have readfn. */
assert(csr->readfn == NULL);
@@ -196,7 +225,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, vm_level);
tcg_gen_ld_tl(oldv, tcg_env, offset);
tcg_gen_and_tl(newv, src1, mask);
tcg_gen_andc_tl(temp, oldv, mask);
@@ -204,7 +233,7 @@ static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
writefn = (GenCSRWrite)csr->writefn;
if (writefn) {
- writefn(oldv, tcg_env, newv);
+ writefn(oldv, tcg_env, newv, tcg_constant_i32(vm_level));
} else {
tcg_gen_st_tl(newv, tcg_env, offset);
}
@@ -212,6 +241,21 @@ static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
return true;
}
+static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
+{
+ return trans_csrrd_impl(ctx, a, mmu_idx_vm_level(ctx->mem_idx));
+}
+
+static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
+{
+ return trans_csrwr_impl(ctx, a, mmu_idx_vm_level(ctx->mem_idx));
+}
+
+static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
+{
+ return trans_csrxchg_impl(ctx, a, mmu_idx_vm_level(ctx->mem_idx));
+}
+
static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a,
void (*func)(TCGv, TCGv_ptr, TCGv))
{
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 21/26] target/loongarch: add LVZ to CSR helpers
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (19 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 20/26] target/loongarch: add LVZ to CSR operations SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 22/26] target/loongarch: add guest CSR operations SignKirigami
` (4 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add actual LVZ handling to all CSR helpers.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/tcg/csr_helper.c | 49 ++++++++++++++++++++-----------
1 file changed, 32 insertions(+), 17 deletions(-)
diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c
index 5ff89f0d10..f5b6a3d1f9 100644
--- a/target/loongarch/tcg/csr_helper.c
+++ b/target/loongarch/tcg/csr_helper.c
@@ -21,7 +21,7 @@
target_ulong HELPER(csrwr_stlbps)(CPULoongArchState *env,
target_ulong val, uint32_t vm_level)
{
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
int64_t old_v = sys->CSR_STLBPS;
/*
@@ -44,7 +44,7 @@ target_ulong HELPER(csrwr_stlbps)(CPULoongArchState *env,
target_ulong HELPER(csrrd_pgd)(CPULoongArchState *env, uint32_t vm_level)
{
int64_t v;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
if (sys->CSR_TLBRERA & 0x1) {
v = sys->CSR_TLBRBADV;
@@ -64,7 +64,11 @@ target_ulong HELPER(csrrd_pgd)(CPULoongArchState *env, uint32_t vm_level)
target_ulong HELPER(csrrd_cpuid)(CPULoongArchState *env, uint32_t vm_level)
{
LoongArchCPU *lac = env_archcpu(env);
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
+
+ if (vm_level) {
+ return sys->CSR_CPUID;
+ }
sys->CSR_CPUID = CPU(lac)->cpu_index;
@@ -73,13 +77,14 @@ target_ulong HELPER(csrrd_cpuid)(CPULoongArchState *env, uint32_t vm_level)
target_ulong HELPER(csrrd_tval)(CPULoongArchState *env, uint32_t vm_level)
{
- return cpu_loongarch_get_timer_ticks(env_timer(env));
+ CPUTimerState *timer = vm_level ? env_guest_timer(env) : env_timer(env);
+ return cpu_loongarch_get_timer_ticks(timer);
}
target_ulong HELPER(csrrd_msgir)(CPULoongArchState *env, uint32_t vm_level)
{
int irq, new;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
irq = find_first_bit((unsigned long *)sys->CSR_MSGIS, 256);
if (irq < 256) {
@@ -101,11 +106,20 @@ target_ulong HELPER(csrrd_msgir)(CPULoongArchState *env, uint32_t vm_level)
target_ulong HELPER(csrwr_estat)(CPULoongArchState *env,
target_ulong val, uint32_t vm_level)
{
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
int64_t old_v = sys->CSR_ESTAT;
- /* Only IS[1:0] can be written */
+ /* When guest = 0, only IS[1:0] can be written.
+ * When guest = 1, ecode and esubcode of
+ * VM_LEVEL1 can be written by VM_LEVEL0.
+ */
sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, 0, 2, val);
+ if (vm_level) {
+ sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, 2, 11,
+ extract64(val, 2, 11));
+ sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, 16, 15,
+ extract64(val, 16, 15));
+ }
/*
* Software interrupts (SWI0/SWI1) are latched in CSR.ESTAT.IS[1:0].
* Make sure the CPU interrupt request state tracks the pending bits,
@@ -113,7 +127,7 @@ target_ulong HELPER(csrwr_estat)(CPULoongArchState *env,
*/
if (sys->CSR_ESTAT != old_v) {
bql_lock();
- loongarch_cpu_update_irq(env_archcpu(env), false);
+ loongarch_cpu_update_irq(env_archcpu(env), vm_level);
bql_unlock();
}
@@ -123,7 +137,7 @@ target_ulong HELPER(csrwr_estat)(CPULoongArchState *env,
target_ulong HELPER(csrwr_asid)(CPULoongArchState *env,
target_ulong val, uint32_t vm_level)
{
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
int64_t old_v = sys->CSR_ASID;
/* Only ASID filed of CSR_ASID can be written */
@@ -137,7 +151,7 @@ target_ulong HELPER(csrwr_asid)(CPULoongArchState *env,
target_ulong HELPER(csrwr_tcfg)(CPULoongArchState *env,
target_ulong val, uint32_t vm_level)
{
- CPUTimerState *timer = env_timer(env);
+ CPUTimerState *timer = vm_level ? env_guest_timer(env) : env_timer(env);
CPUSysState *sys = container_of(timer, CPUSysState, timer_state);
int64_t old_v = sys->CSR_TCFG;
@@ -149,7 +163,7 @@ target_ulong HELPER(csrwr_tcfg)(CPULoongArchState *env,
target_ulong HELPER(csrwr_ticlr)(CPULoongArchState *env,
target_ulong val, uint32_t vm_level)
{
- CPUTimerState *timer = env_timer(env);
+ CPUTimerState *timer = vm_level ? env_guest_timer(env) : env_timer(env);
int64_t old_v = 0;
if (val & 0x1) {
@@ -164,7 +178,7 @@ target_ulong HELPER(csrwr_pwcl)(CPULoongArchState *env,
target_ulong val, uint32_t vm_level)
{
uint8_t shift, ptbase;
- CPUSysState *sys = env_sys(env);
+ CPUSysState *sys = get_sys(env, vm_level);
int64_t old_v = sys->CSR_PWCL;
/*
@@ -189,16 +203,17 @@ target_ulong HELPER(csrwr_pwcl)(CPULoongArchState *env,
target_ulong HELPER(csrwr_pwch)(CPULoongArchState *env,
target_ulong val, uint32_t vm_level)
{
- uint8_t has_ptw;
- CPUSysState *sys = env_sys(env);
+ uint8_t host_has_ptw;
+ CPUSysState *sys = get_sys(env, vm_level);
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) {
+ //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;
- }
+}
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 22/26] target/loongarch: add guest CSR operations
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (20 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 21/26] target/loongarch: add LVZ to CSR helpers SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 23/26] target/loongarch: add LVZ CSRs SignKirigami
` (3 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add gcsrrd, gcsrwr and gcsrxchg for guest CSR management.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/disas.c | 3 +++
target/loongarch/insns.decode | 6 +++++
.../tcg/insn_trans/trans_privileged.c.inc | 27 +++++++++++++++++++
3 files changed, 36 insertions(+)
diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c
index 03d1e560dd..3010e16608 100644
--- a/target/loongarch/disas.c
+++ b/target/loongarch/disas.c
@@ -682,6 +682,9 @@ INSN(bgeu, rr_offs)
INSN(csrrd, r_csr)
INSN(csrwr, r_csr)
INSN(csrxchg, rr_csr)
+INSN(gcsrrd, r_csr)
+INSN(gcsrwr, r_csr)
+INSN(gcsrxchg, rr_csr)
INSN(iocsrrd_b, rr)
INSN(iocsrrd_h, rr)
INSN(iocsrrd_w, rr)
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 39d217877d..cfcd234340 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
@@ -256,6 +259,30 @@ static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
return trans_csrxchg_impl(ctx, a, mmu_idx_vm_level(ctx->mem_idx));
}
+static bool trans_gcsrrd(DisasContext *ctx, arg_gcsrrd *a)
+{
+ if (!avail_LVZ(ctx) || mmu_idx_vm_level(ctx->mem_idx)) {
+ return false;
+ }
+ return trans_csrrd_impl(ctx, a, VM_LEVEL1);
+}
+
+static bool trans_gcsrwr(DisasContext *ctx, arg_gcsrwr *a)
+{
+ if (!avail_LVZ(ctx) || mmu_idx_vm_level(ctx->mem_idx)) {
+ return false;
+ }
+ return trans_csrwr_impl(ctx, a, VM_LEVEL1);
+}
+
+static bool trans_gcsrxchg(DisasContext *ctx, arg_gcsrxchg *a)
+{
+ if (!avail_LVZ(ctx) || mmu_idx_vm_level(ctx->mem_idx)) {
+ return false;
+ }
+ return trans_csrxchg_impl(ctx, a, VM_LEVEL1);
+}
+
static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a,
void (*func)(TCGv, TCGv_ptr, TCGv))
{
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 23/26] target/loongarch: add LVZ CSRs
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (21 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 22/26] target/loongarch: add guest CSR operations SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 24/26] target/loongarch: translate LVZ guest traps and HVC SignKirigami
` (2 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Add gstat, gtlbc and gintc definitions and corresponding helpers.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/tcg/csr_helper.c | 53 +++++++++++++++++++
target/loongarch/tcg/helper.h | 3 ++
.../tcg/insn_trans/trans_privileged.c.inc | 3 ++
3 files changed, 59 insertions(+)
diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c
index f5b6a3d1f9..c48b328540 100644
--- a/target/loongarch/tcg/csr_helper.c
+++ b/target/loongarch/tcg/csr_helper.c
@@ -217,3 +217,56 @@ target_ulong HELPER(csrwr_pwch)(CPULoongArchState *env,
sys->CSR_PWCH = val;
return old_v;
}
+
+target_ulong HELPER(csrwr_gstat)(CPULoongArchState *env,
+ target_ulong val, uint32_t vm_level)
+{
+ CPUSysState *sys = get_sys(env, VM_LEVEL0);
+ int64_t old_v = sys->CSR_GSTAT;
+ uint8_t old_gid = FIELD_EX64(sys->CSR_GSTAT, CSR_GSTAT, GID);
+
+ sys->CSR_GSTAT = FIELD_DP64(sys->CSR_GSTAT, CSR_GSTAT, PVM,
+ FIELD_EX64(val, CSR_GSTAT, PVM));
+ sys->CSR_GSTAT = FIELD_DP64(sys->CSR_GSTAT, CSR_GSTAT, GID,
+ FIELD_EX64(val, CSR_GSTAT, GID));
+
+ if (old_gid != FIELD_EX64(sys->CSR_GSTAT, CSR_GSTAT, GID)) {
+ tlb_flush(env_cpu(env));
+ }
+
+ return old_v;
+}
+
+target_ulong HELPER(csrwr_gtlbc)(CPULoongArchState *env,
+ target_ulong val, uint32_t vm_level)
+{
+ CPUSysState *sys = get_sys(env, VM_LEVEL0);
+ int64_t old_v = sys->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);
+
+ sys->CSR_GTLBC = val;
+ if (old_use_tgid != FIELD_EX64(sys->CSR_GTLBC, CSR_GTLBC, USETGID) ||
+ old_tgid != FIELD_EX64(sys->CSR_GTLBC, CSR_GTLBC, TGID)) {
+ tlb_flush(env_cpu(env));
+ }
+
+ return old_v;
+}
+
+target_ulong HELPER(csrwr_gintc)(CPULoongArchState *env,
+ target_ulong val, uint32_t vm_level)
+{
+ CPUSysState *host = get_sys(env, VM_LEVEL0);
+ CPUSysState *guest = get_sys(env, VM_LEVEL1);
+ int64_t old_v = host->CSR_GINTC;
+ uint8_t hwis = FIELD_EX64(val, CSR_GINTC, HWIS);
+ /* TODO: hwip and hwic support */
+
+ host->CSR_GINTC = val & 0xffff00;
+ guest->CSR_ESTAT = deposit64(guest->CSR_ESTAT, 2, 8,
+ hwis);
+
+ return old_v;
+}
+
diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h
index 1c15e7544a..63d37a00b0 100644
--- a/target/loongarch/tcg/helper.h
+++ b/target/loongarch/tcg/helper.h
@@ -106,6 +106,9 @@ DEF_HELPER_3(csrwr_tcfg, i64, env, tl, i32)
DEF_HELPER_3(csrwr_ticlr, i64, env, tl, i32)
DEF_HELPER_3(csrwr_pwcl, i64, env, tl, i32)
DEF_HELPER_3(csrwr_pwch, i64, env, tl, i32)
+DEF_HELPER_3(csrwr_gstat, i64, env, tl, i32)
+DEF_HELPER_3(csrwr_gtlbc, i64, env, tl, i32)
+DEF_HELPER_3(csrwr_gintc, i64, env, tl, i32)
DEF_HELPER_2(iocsrrd_b, i64, env, tl)
DEF_HELPER_2(iocsrrd_h, i64, env, tl)
DEF_HELPER_2(iocsrrd_w, i64, env, tl)
diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
index cfcd234340..384580c988 100644
--- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
@@ -88,6 +88,9 @@ 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);
}
#undef SET_CSR_FUNC
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 24/26] target/loongarch: translate LVZ guest traps and HVC
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (22 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 23/26] target/loongarch: add LVZ CSRs SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 25/26] target/loongarch: initialize CSR related to LVZ SignKirigami
2026-11-13 8:45 ` [PATCH v6 26/26] target/loongarch: exit to vm level 0 for fpd, sxd and asxd of vm level 0 SignKirigami
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Raise GSPR when a guest accesses IOCSR, debug, idle and cpucfg resources,
and decode HVC for guest PLV0.
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 +
target/loongarch/tcg/helper.h | 2 +-
| 5 +++
.../tcg/insn_trans/trans_privileged.c.inc | 32 +++++++++++++++++++
target/loongarch/tcg/op_helper.c | 3 ++
6 files changed, 43 insertions(+), 1 deletion(-)
diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c
index 3010e16608..0e0a3a242a 100644
--- a/target/loongarch/disas.c
+++ b/target/loongarch/disas.c
@@ -700,6 +700,7 @@ INSN(tlbfill, empty)
INSN(tlbclr, empty)
INSN(tlbflush, empty)
INSN(invtlb, i_rr)
+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 4612af9433..0881f3d018 100644
--- a/target/loongarch/insns.decode
+++ b/target/loongarch/insns.decode
@@ -514,6 +514,7 @@ tlbfill 0000 01100100 10000 01101 00000 00000 @empty
tlbclr 0000 01100100 10000 01000 00000 00000 @empty
tlbflush 0000 01100100 10000 01001 00000 00000 @empty
invtlb 0000 01100100 10011 ..... ..... ..... @i_rr
+hvcl 0000 0000 0010 1011 1 ............... @i15
cacop 0000 011000 ............ ..... ..... @cop_r_i
lddir 0000 01100100 00 ........ ..... ..... @rr_ui8
ldpte 0000 01100100 01 ........ ..... 00000 @j_i
diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h
index 63d37a00b0..b47485105a 100644
--- a/target/loongarch/tcg/helper.h
+++ b/target/loongarch/tcg/helper.h
@@ -12,7 +12,7 @@ DEF_HELPER_FLAGS_3(asrtgt_du, 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)
--git a/target/loongarch/tcg/insn_trans/trans_extra.c.inc b/target/loongarch/tcg/insn_trans/trans_extra.c.inc
index 655dce329e..aea033e054 100644
--- a/target/loongarch/tcg/insn_trans/trans_extra.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_extra.c.inc
@@ -82,6 +82,11 @@ static bool trans_rdtime_d(DisasContext *ctx, arg_rdtime_d *a)
static bool trans_cpucfg(DisasContext *ctx, arg_cpucfg *a)
{
+ if (mmu_idx_vm_level(ctx->mem_idx)) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ return true;
+ }
+
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
index 384580c988..3b8f9b9fb8 100644
--- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
@@ -42,6 +42,7 @@ GEN_FALSE_TRANS(idle)
GEN_FALSE_TRANS(gcsrrd)
GEN_FALSE_TRANS(gcsrwr)
GEN_FALSE_TRANS(gcsrxchg)
+GEN_FALSE_TRANS(hvcl)
#else
@@ -295,6 +296,10 @@ static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a,
if (check_plv(ctx)) {
return false;
}
+ if (mmu_idx_vm_level(ctx->mem_idx)) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ return true;
+ }
func(dest, tcg_env, src1);
return true;
}
@@ -308,6 +313,10 @@ static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a,
if (check_plv(ctx)) {
return false;
}
+ if (mmu_idx_vm_level(ctx->mem_idx)) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ return true;
+ }
func(tcg_env, addr, val);
return true;
}
@@ -518,6 +527,10 @@ static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a)
if (check_plv(ctx)) {
return false;
}
+ if (mmu_idx_vm_level(ctx->mem_idx)) {
+ generate_exception(ctx, EXCCODE_GSPR);
+ return true;
+ }
generate_exception(ctx, EXCCODE_DBP);
return true;
}
@@ -528,9 +541,28 @@ static bool trans_idle(DisasContext *ctx, arg_idle *a)
return false;
}
+ if (mmu_idx_vm_level(ctx->mem_idx)) {
+ 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) || !mmu_idx_vm_level(ctx->mem_idx)) {
+ return false;
+ }
+
+ if (check_plv(ctx)) {
+ return false;
+ }
+
+ generate_exception(ctx, EXCCODE_HVC);
+ return true;
+}
#endif
diff --git a/target/loongarch/tcg/op_helper.c b/target/loongarch/tcg/op_helper.c
index 7b77ac49a6..3b32e04268 100644
--- a/target/loongarch/tcg/op_helper.c
+++ b/target/loongarch/tcg/op_helper.c
@@ -20,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());
}
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 25/26] target/loongarch: initialize CSR related to LVZ
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (23 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 24/26] target/loongarch: translate LVZ guest traps and HVC SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
2026-11-13 8:45 ` [PATCH v6 26/26] target/loongarch: exit to vm level 0 for fpd, sxd and asxd of vm level 0 SignKirigami
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami, Hengyu Yu
Initialize CSR.GSTAT.GIDBITS and GCSR.ASID.ASIDBITS at cpu init function.
Signed-off-by: SignKirigami <prcups@krgm.moe>
Signed-off-by: Hengyu Yu <yuhengyu25@mails.ucas.ac.cn>
---
target/loongarch/cpu.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index 3a351e43cc..4bc2971d28 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -332,7 +332,7 @@ static void loongarch_la464_initfn(Object *obj)
{
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
CPULoongArchState *env = &cpu->env;
- CPUSysState *sys;
+ CPUSysState *sys, *guest;
uint32_t data = 0, field;
int i;
@@ -446,6 +446,9 @@ static void loongarch_la464_initfn(Object *obj)
sys = env_sys(env);
sys->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa);
+ sys->CSR_GSTAT = FIELD_DP64(0, CSR_GSTAT, GIDBITS, 8);
+ guest = get_sys(env, VM_LEVEL1);
+ guest->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa);
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);
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v6 26/26] target/loongarch: exit to vm level 0 for fpd, sxd and asxd of vm level 0
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
` (24 preceding siblings ...)
2026-11-13 8:45 ` [PATCH v6 25/26] target/loongarch: initialize CSR related to LVZ SignKirigami
@ 2026-11-13 8:45 ` SignKirigami
25 siblings, 0 replies; 27+ messages in thread
From: SignKirigami @ 2026-11-13 8:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Bibo Mao, SignKirigami
When vm level 0 fpe, sxe and asxe is set to 0, although on vm level 1 these
are enabled, in TCGTBCPUState flags corresponding bit should not be set, and
when handling exceptions, executing trigger_vm_exit() in this situation.
Signed-off-by: SignKirigami <prcups@krgm.moe>
---
target/loongarch/tcg/op_helper.c | 20 ++++++++++++++++++++
target/loongarch/tcg/tcg_cpu.c | 17 ++++++++++++-----
2 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/target/loongarch/tcg/op_helper.c b/target/loongarch/tcg/op_helper.c
index 3b32e04268..3eaf2cfd69 100644
--- a/target/loongarch/tcg/op_helper.c
+++ b/target/loongarch/tcg/op_helper.c
@@ -20,9 +20,29 @@
/* Exceptions helpers */
void HELPER(raise_exception)(CPULoongArchState *env, uint32_t exception)
{
+ CPUSysState *host;
+ int vm_exit = 0;
+
if (exception == EXCCODE_GSPR || exception == EXCCODE_HVC) {
+ vm_exit = 1;
+ } else if (env_vm_level(env)) {
+ host = get_sys(env, VM_LEVEL0);
+ if (exception == EXCCODE_FPD &&
+ FIELD_EX64(host->CSR_EUEN, CSR_EUEN, FPE) == 0) {
+ vm_exit = 1;
+ } else if (exception == EXCCODE_SXD &&
+ FIELD_EX64(host->CSR_EUEN, CSR_EUEN, SXE) == 0) {
+ vm_exit = 1;
+ }
+ if (exception == EXCCODE_ASXD &&
+ FIELD_EX64(host->CSR_EUEN, CSR_EUEN, ASXE) == 0) {
+ vm_exit = 1;
+ }
+ }
+ if (vm_exit) {
trigger_vm_exit(env);
}
+
do_raise_exception(env, exception, GETPC());
}
diff --git a/target/loongarch/tcg/tcg_cpu.c b/target/loongarch/tcg/tcg_cpu.c
index 1301a9c6ad..523e0e02a1 100644
--- a/target/loongarch/tcg/tcg_cpu.c
+++ b/target/loongarch/tcg/tcg_cpu.c
@@ -261,16 +261,23 @@ static vaddr loongarch_pointer_wrap(CPUState *cs, int mmu_idx,
static TCGTBCPUState loongarch_get_tb_cpu_state(CPUState *cs)
{
CPULoongArchState *env = cpu_env(cs);
- CPUSysState *sys = env_sys(env);
- uint32_t flags;
+ CPUSysState *sys = env_sys(env), *host;
+ uint32_t flags, fpe, sxe, asxe;
flags = sys->CSR_CRMD & (R_CSR_CRMD_PLV_MASK | R_CSR_CRMD_PG_MASK);
+ fpe = FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, FPE);
+ sxe = FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, SXE);
+ asxe = FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, ASXE);
if (env_vm_level(env) == VM_LEVEL1) {
flags |= HW_FLAGS_GUEST_MODE;
+ host = get_sys(env, VM_LEVEL0);
+ fpe &= FIELD_EX64(host->CSR_EUEN, CSR_EUEN, FPE);
+ sxe &= FIELD_EX64(host->CSR_EUEN, CSR_EUEN, SXE);
+ asxe &= FIELD_EX64(host->CSR_EUEN, CSR_EUEN, ASXE);
}
- 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;
+ flags |= fpe * HW_FLAGS_EUEN_FPE;
+ flags |= sxe * HW_FLAGS_EUEN_SXE;
+ flags |= asxe * HW_FLAGS_EUEN_ASXE;
flags |= is_va32(env) * HW_FLAGS_VA32;
return (TCGTBCPUState){ .pc = env->pc, .flags = flags };
--
2.56.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
end of thread, other threads:[~2026-11-13 8:51 UTC | newest]
Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-13 8:45 [PATCH v6 00/26] add LVZ support for TCG SignKirigami
2026-11-13 8:45 ` [PATCH v6 01/26] target/loongarch: add get_sys() helper SignKirigami
2026-11-13 8:45 ` [PATCH v6 02/26] target/loongarch: add LVZ feature switcher SignKirigami
2026-11-13 8:45 ` [PATCH v6 03/26] target/loongarch: add LVZ CSR metadata SignKirigami
2026-11-13 8:45 ` [PATCH v6 04/26] target/loongarch: add vm level 1 definition SignKirigami
2026-11-13 8:45 ` [PATCH v6 05/26] target/loongarch: add definition about guest mmu_idx SignKirigami
2026-11-13 8:45 ` [PATCH v6 06/26] target/loongarch: move timer initialization to loongarh_cpu_realizefn() SignKirigami
2026-11-13 8:45 ` [PATCH v6 07/26] target/loongarch: add LVZ guest interrupt state SignKirigami
2026-11-13 8:45 ` [PATCH v6 08/26] target/loongarch: add LVZ guest timer SignKirigami
2026-11-13 8:45 ` [PATCH v6 09/26] target/loongarch: handle return to guest in ertn SignKirigami
2026-11-13 8:45 ` [PATCH v6 10/26] target/loongarch: handle LVZ exception and vm_exit SignKirigami
2026-11-13 8:45 ` [PATCH v6 11/26] target/loongarch: add host TLBRet and handle SignKirigami
2026-11-13 8:45 ` [PATCH v6 12/26] target/loongarch: add gid management functions SignKirigami
2026-11-13 8:45 ` [PATCH v6 13/26] target/loongarch: add LVZ parameters to TLB functions SignKirigami
2026-11-13 8:45 ` [PATCH v6 14/26] target/loongarch: add LVZ " SignKirigami
2026-11-13 8:45 ` [PATCH v6 15/26] target/loongarch: add 2-level address mapping to loongarch_cpu_tlb_fill() SignKirigami
2026-11-13 8:45 ` [PATCH v6 16/26] target/loongarch: add LVZ to invtlb 0x0-0x6 SignKirigami
2026-11-13 8:45 ` [PATCH v6 17/26] target/loongarch: add LVZ to invtlb 0xa-0xe SignKirigami
2026-11-13 8:45 ` [PATCH v6 18/26] target/loongarch: add LVZ to ptw SignKirigami
2026-11-13 8:45 ` [PATCH v6 19/26] target/loongarch: add LVZ migration state SignKirigami
2026-11-13 8:45 ` [PATCH v6 20/26] target/loongarch: add LVZ to CSR operations SignKirigami
2026-11-13 8:45 ` [PATCH v6 21/26] target/loongarch: add LVZ to CSR helpers SignKirigami
2026-11-13 8:45 ` [PATCH v6 22/26] target/loongarch: add guest CSR operations SignKirigami
2026-11-13 8:45 ` [PATCH v6 23/26] target/loongarch: add LVZ CSRs SignKirigami
2026-11-13 8:45 ` [PATCH v6 24/26] target/loongarch: translate LVZ guest traps and HVC SignKirigami
2026-11-13 8:45 ` [PATCH v6 25/26] target/loongarch: initialize CSR related to LVZ SignKirigami
2026-11-13 8:45 ` [PATCH v6 26/26] target/loongarch: exit to vm level 0 for fpd, sxd and asxd of vm level 0 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.