Signed-off-by: SignKirigami <[email protected]>
Signed-off-by: Hengyu Yu <[email protected]>
---
target/loongarch/cpu-mmu.h | 37 +-
target/loongarch/cpu_helper.c | 119 ++--
target/loongarch/disas.c | 6 +
target/loongarch/insns.decode | 6 +
target/loongarch/tcg/helper.h | 17 +-
.../tcg/insn_trans/trans_privileged.c.inc | 128 +++-
target/loongarch/tcg/tcg_loongarch.h | 6 +-
target/loongarch/tcg/tlb_helper.c | 629 +++++++++++++-----
8 files changed, 694 insertions(+), 254 deletions(-)
diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h
index 2d7ebb2d72..31093641ff 100644
--- a/target/loongarch/cpu-mmu.h
+++ b/target/loongarch/cpu-mmu.h
@@ -17,6 +17,14 @@ typedef enum TLBRet {
TLBRET_RI,
TLBRET_XI,
TLBRET_PE,
+ TLBRET_HOST_MATCH,
+ TLBRET_HOST_BADADDR,
+ TLBRET_HOST_NOMATCH,
+ TLBRET_HOST_INVALID,
+ TLBRET_HOST_DIRTY,
+ TLBRET_HOST_RI,
+ TLBRET_HOST_XI,
+ TLBRET_HOST_PE,
} TLBRet;
typedef struct MMUContext {
@@ -30,16 +38,17 @@ typedef struct MMUContext {
uint64_t pte_buddy[2];
} MMUContext;
-static inline bool cpu_has_ptw(CPULoongArchState *env)
+static inline bool cpu_has_ptw(CPULoongArchState *env, bool guest)
{
- return !!FIELD_EX64(env->CSR_PWCH, CSR_PWCH, HPTW_EN);
+ return !!FIELD_EX64(GET_CSR_IF(guest, PWCH), CSR_PWCH, HPTW_EN);
}
-static inline bool pte_present(CPULoongArchState *env, uint64_t entry)
+static inline bool pte_present(CPULoongArchState *env, uint64_t entry,
+ bool guest)
{
uint8_t present;
- if (cpu_has_ptw(env)) {
+ if (cpu_has_ptw(env, guest)) {
present = FIELD_EX64(entry, TLBENTRY, P);
} else {
present = FIELD_EX64(entry, TLBENTRY, V);
@@ -48,11 +57,11 @@ static inline bool pte_present(CPULoongArchState *env,
uint64_t entry)
return !!present;
}
-static inline bool pte_write(CPULoongArchState *env, uint64_t entry)
+static inline bool pte_write(CPULoongArchState *env, uint64_t entry, bool
guest)
{
uint8_t writable;
- if (cpu_has_ptw(env)) {
+ if (cpu_has_ptw(env, guest)) {
writable = FIELD_EX64(entry, TLBENTRY, W);
} else {
writable = FIELD_EX64(entry, TLBENTRY, D);
@@ -89,14 +98,22 @@ static inline bool pte_dirty(uint64_t entry)
bool check_ps(CPULoongArchState *ent, uint8_t ps);
TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context,
- MMUAccessType access_type, int mmu_idx);
+ MMUAccessType access_type, int mmu_idx, bool guest);
TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context,
MMUAccessType access_type, int mmu_idx,
- int is_debug);
+ int is_debug, uintptr_t retaddr);
TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context,
- int access_type, int mmu_idx, int debug);
+ int access_type, int mmu_idx, int debug, bool guest,
+ uintptr_t retaddr);
+hwaddr loongarch_get_host_address(CPULoongArchState *env, hwaddr gpa,
+ uintptr_t retaddr);
+TLBRet loongarch_map_host_address(CPULoongArchState *env, MMUContext *context,
+ MMUAccessType access_type, uintptr_t
retaddr);
+TLBRet loongarch_map_address(CPULoongArchState *env, MMUContext *context,
+ MMUAccessType access_type, int mmu_idx,
+ int is_debug, bool guest, uintptr_t retaddr);
void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
- uint64_t *dir_width, unsigned int level);
+ uint64_t *dir_width, unsigned int level, bool guest);
hwaddr loongarch_cpu_get_phys_addr_debug(CPUState *cpu, vaddr addr);
uint64_t loongarch_palen_mask(CPULoongArchState *env);
diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c
index eb9684a4a1..bad6062ac9 100644
--- a/target/loongarch/cpu_helper.c
+++ b/target/loongarch/cpu_helper.c
@@ -17,46 +17,56 @@
#include "cpu-mmu.h"
#include "tcg/tcg_loongarch.h"
-void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
- uint64_t *dir_width, unsigned int level)
+static void get_dir_base_width_csr(CPULoongArchState *env, uint64_t *dir_base,
+ uint64_t *dir_width, unsigned int level,
+ bool guest)
{
+ uint64_t pwcl = GET_CSR_IF(guest, PWCL);
+ uint64_t pwch = GET_CSR_IF(guest, PWCH);
+
switch (level) {
case 1:
- *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE);
- *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH);
+ *dir_base = FIELD_EX64(pwcl, CSR_PWCL, DIR1_BASE);
+ *dir_width = FIELD_EX64(pwcl, CSR_PWCL, DIR1_WIDTH);
break;
case 2:
- *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE);
- *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH);
+ *dir_base = FIELD_EX64(pwcl, CSR_PWCL, DIR2_BASE);
+ *dir_width = FIELD_EX64(pwcl, CSR_PWCL, DIR2_WIDTH);
break;
case 3:
- *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE);
- *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH);
+ *dir_base = FIELD_EX64(pwch, CSR_PWCH, DIR3_BASE);
+ *dir_width = FIELD_EX64(pwch, CSR_PWCH, DIR3_WIDTH);
break;
case 4:
- *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE);
- *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH);
+ *dir_base = FIELD_EX64(pwch, CSR_PWCH, DIR4_BASE);
+ *dir_width = FIELD_EX64(pwch, CSR_PWCH, DIR4_WIDTH);
break;
default:
/* level may be zero for ldpte */
- *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE);
- *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH);
+ *dir_base = FIELD_EX64(pwcl, CSR_PWCL, PTBASE);
+ *dir_width = FIELD_EX64(pwcl, CSR_PWCL, PTWIDTH);
break;
}
}
+void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
+ uint64_t *dir_width, unsigned int level, bool guest)
+{
+ get_dir_base_width_csr(env, dir_base, dir_width, level, guest);
+}
+
TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context,
- MMUAccessType access_type, int mmu_idx)
+ MMUAccessType access_type, int mmu_idx, bool guest)
{
- uint64_t plv = mmu_idx;
+ uint64_t plv = mmu_idx_to_plv(mmu_idx);
uint64_t tlb_entry, tlb_ppn;
uint8_t tlb_ps, tlb_plv, tlb_nx, tlb_nr, tlb_rplv;
bool tlb_v, tlb_d;
tlb_entry = context->pte;
tlb_ps = context->ps;
- tlb_v = pte_present(env, tlb_entry);
- tlb_d = pte_write(env, tlb_entry);
+ tlb_v = pte_present(env, tlb_entry, guest);
+ tlb_d = pte_write(env, tlb_entry, guest);
tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV);
if (is_la64(env)) {
tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN);
@@ -98,7 +108,7 @@ TLBRet loongarch_check_pte(CPULoongArchState *env,
MMUContext *context,
context->physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) |
(context->addr & MAKE_64BIT_MASK(0, tlb_ps));
context->prot = PAGE_READ;
- context->mmu_index = tlb_plv;
+ context->mmu_index = mmu_idx;
if (tlb_d) {
context->prot |= PAGE_WRITE;
}
@@ -144,7 +154,8 @@ static MemTxResult loongarch_cmpxchg_phys(CPUState *cs,
hwaddr phys,
}
TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context,
- int access_type, int mmu_idx, int debug)
+ int access_type, int mmu_idx, int debug, bool guest,
+ uintptr_t retaddr)
{
const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
CPUState *cs = env_cpu(env);
@@ -160,14 +171,14 @@ TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext
*context,
address = context->addr;
if ((address >> 63) & 0x1) {
- base = env->CSR_PGDH;
+ base = GET_CSR_IF(guest, PGDH);
} else {
- base = env->CSR_PGDL;
+ base = GET_CSR_IF(guest, PGDL);
}
base &= palen_mask;
for (level = 4; level >= 0; level--) {
- get_dir_base_width(env, &dir_base, &dir_width, level);
+ get_dir_base_width(env, &dir_base, &dir_width, level, guest);
if (dir_width == 0) {
continue;
@@ -176,7 +187,10 @@ TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext
*context,
/* get next level page directory */
index = (address >> dir_base) & ((1 << dir_width) - 1);
phys = base | index << 3;
- base = address_space_ldq_le(cs->as, phys, attrs, NULL);
+ base = address_space_ldq_le(
+ cs->as,
+ (guest ? loongarch_get_host_address(env, phys, retaddr) : phys),
+ attrs, NULL);
if (level) {
if (FIELD_EX64(base, TLBENTRY, HUGE)) {
/* base is a huge pte */
@@ -205,19 +219,22 @@ restart:
context->pte_buddy[index] = base;
context->pte_buddy[1 - index] = base + BIT_ULL(dir_base);
base += (BIT_ULL(dir_base) & address);
- } else if (cpu_has_ptw(env)) {
+ } else if (cpu_has_ptw(env, guest)) {
uint64_t val;
index &= 1;
context->pte_buddy[index] = base;
- val = address_space_ldq_le(cs->as, phys + 8 * (1 - 2 * index),
- attrs, NULL);
+ val = address_space_ldq_le(
+ cs->as,
+ (guest ? loongarch_get_host_address(env, phys, retaddr) : phys) +
+ 8 * (1 - 2 * index),
+ attrs, NULL);
context->pte_buddy[1 - index] = val;
}
context->ps = dir_base;
context->pte = base;
- ret = loongarch_check_pte(env, context, access_type, mmu_idx);
+ ret = loongarch_check_pte(env, context, access_type, mmu_idx, guest);
if (debug) {
return ret;
}
@@ -228,7 +245,7 @@ restart:
* Need atomic compchxg operation with pte update, other vCPUs may
* update pte at the same time.
*/
- if (ret == TLBRET_MATCH && cpu_has_ptw(env)) {
+ if (ret == TLBRET_MATCH && cpu_has_ptw(env, guest)) {
if (access_type == MMU_DATA_STORE && pte_dirty(base)) {
return ret;
}
@@ -241,10 +258,15 @@ restart:
if (access_type == MMU_DATA_STORE) {
base = pte_mkdirty(base);
}
- ret1 = loongarch_cmpxchg_phys(cs, phys, pte, base);
+ ret1 = loongarch_cmpxchg_phys(
+ cs, (guest ? loongarch_get_host_address(env, phys, retaddr) :
phys),
+ pte, base);
/* PTE updated by other CPU, reload PTE entry */
if (ret1 == MEMTX_DECODE_ERROR) {
- base = address_space_ldq_le(cs->as, phys, attrs, NULL);
+ base = address_space_ldq_le(
+ cs->as,
+ (guest ? loongarch_get_host_address(env, phys, retaddr) :
phys),
+ attrs, NULL);
goto restart;
}
@@ -270,15 +292,15 @@ restart:
return ret;
}
-static TLBRet loongarch_map_address(CPULoongArchState *env,
- MMUContext *context,
- MMUAccessType access_type, int mmu_idx,
- int is_debug)
+TLBRet loongarch_map_address(CPULoongArchState *env, MMUContext *context,
+ MMUAccessType access_type, int mmu_idx,
+ int is_debug, bool guest, uintptr_t retaddr)
{
TLBRet ret;
if (tcg_enabled()) {
- ret = loongarch_get_addr_from_tlb(env, context, access_type, mmu_idx);
+ ret = loongarch_get_addr_from_tlb(env, context, access_type, mmu_idx,
+ guest);
if (ret != TLBRET_NOMATCH) {
return ret;
}
@@ -290,7 +312,8 @@ static TLBRet loongarch_map_address(CPULoongArchState *env,
* legal mapping, even if the mapping is not yet in TLB. return 0 if
* there is a valid map, else none zero.
*/
- return loongarch_ptw(env, context, access_type, mmu_idx, is_debug);
+ return loongarch_ptw(env, context, access_type, mmu_idx, is_debug,
+ guest, retaddr);
}
return TLBRET_NOMATCH;
@@ -309,14 +332,14 @@ static hwaddr dmw_va2pa(CPULoongArchState *env, vaddr va,
uint64_t dmw)
TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context,
MMUAccessType access_type, int mmu_idx,
- int is_debug)
+ int is_debug, uintptr_t retaddr)
{
- int user_mode = mmu_idx == MMU_USER_IDX;
- int kernel_mode = mmu_idx == MMU_KERNEL_IDX;
+ int user_mode = mmu_idx_to_plv(mmu_idx) == MMU_USER_IDX;
+ int kernel_mode = mmu_idx_to_plv(mmu_idx) == MMU_KERNEL_IDX;
uint32_t plv, base_c, base_v;
int64_t addr_high;
- uint8_t da = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, DA);
- uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG);
+ uint8_t da = FIELD_EX64(GET_CSR_IF(env->guest, CRMD), CSR_CRMD, DA);
+ uint8_t pg = FIELD_EX64(GET_CSR_IF(env->guest, CRMD), CSR_CRMD, PG);
vaddr address;
/* Check PG and DA */
@@ -337,12 +360,15 @@ TLBRet get_physical_address(CPULoongArchState *env,
MMUContext *context,
/* Check direct map window */
for (int i = 0; i < 4; i++) {
if (is_la64(env)) {
- base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_64, VSEG);
+ base_c =
+ FIELD_EX64(GET_CSR_IF(env->guest, DMW[i]), CSR_DMW_64, VSEG);
} else {
- base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_32, VSEG);
+ base_c =
+ FIELD_EX64(GET_CSR_IF(env->guest, DMW[i]), CSR_DMW_32, VSEG);
}
- if ((plv & env->CSR_DMW[i]) && (base_c == base_v)) {
- context->physical = dmw_va2pa(env, address, env->CSR_DMW[i]);
+ if ((plv & GET_CSR_IF(env->guest, DMW[i])) && (base_c == base_v)) {
+ context->physical =
+ dmw_va2pa(env, address, GET_CSR_IF(env->guest, DMW[i]));
context->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
context->mmu_index = MMU_DA_IDX;
return TLBRET_MATCH;
@@ -356,7 +382,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,
+ env->guest, retaddr);
}
hwaddr loongarch_cpu_get_phys_addr_debug(CPUState *cs, vaddr addr)
@@ -366,7 +393,7 @@ hwaddr loongarch_cpu_get_phys_addr_debug(CPUState *cs,
vaddr addr)
context.addr = addr;
if (get_physical_address(env, &context, MMU_DATA_LOAD,
- cpu_mmu_index(cs, false), 1) != TLBRET_MATCH) {
+ cpu_mmu_index(cs, false), 1, 0) != TLBRET_MATCH) {
return -1;
}
return context.physical;
diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c
index cae416266b..b282da6ea6 100644
--- a/target/loongarch/disas.c
+++ b/target/loongarch/disas.c
@@ -701,6 +701,12 @@ INSN(invtlb, i_rr)
INSN(gcsrrd, r_csr)
INSN(gcsrwr, r_csr)
INSN(gcsrxchg, rr_csr)
+INSN(gtlbclr, empty)
+INSN(gtlbflush, empty)
+INSN(gtlbsrch, empty)
+INSN(gtlbrd, empty)
+INSN(gtlbwr, empty)
+INSN(gtlbfill, empty)
INSN(hvcl, i)
INSN(cacop, cop_r_i)
INSN(lddir, rr_i)
diff --git a/target/loongarch/insns.decode b/target/loongarch/insns.decode
index 2ade384081..b19c40b423 100644
--- a/target/loongarch/insns.decode
+++ b/target/loongarch/insns.decode
@@ -502,6 +502,12 @@ bgeu 0110 11 ................ ..... .....
@rr_offs16
gcsrxchg 0000 0101 .............. ..... ..... @rr_csr
}
+gtlbclr 0000 01100100 10000 01000 00000 00001 @empty
+gtlbflush 0000 01100100 10000 01001 00000 00001 @empty
+gtlbsrch 0000 01100100 10000 01010 00000 00001 @empty
+gtlbrd 0000 01100100 10000 01011 00000 00001 @empty
+gtlbwr 0000 01100100 10000 01100 00000 00001 @empty
+gtlbfill 0000 01100100 10000 01101 00000 00001 @empty
hvcl 0000 0000 0010 1011 1 ............... @i15
iocsrrd_b 0000 01100100 10000 00000 ..... ..... @rr
diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h
index 69f6cb352a..648328a7ef 100644
--- a/target/loongarch/tcg/helper.h
+++ b/target/loongarch/tcg/helper.h
@@ -133,11 +133,18 @@ 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_1(gtlbwr, void, env)
+DEF_HELPER_1(gtlbfill, void, env)
+DEF_HELPER_1(gtlbsrch, void, env)
+DEF_HELPER_1(gtlbrd, void, env)
+DEF_HELPER_1(gtlbclr, void, env)
+DEF_HELPER_1(gtlbflush, void, env)
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 0cb629e5d7..19b02e6a78 100644
--- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
@@ -42,6 +42,12 @@ GEN_FALSE_TRANS(idle)
GEN_FALSE_TRANS(gcsrrd)
GEN_FALSE_TRANS(gcsrwr)
GEN_FALSE_TRANS(gcsrxchg)
+GEN_FALSE_TRANS(gtlbclr)
+GEN_FALSE_TRANS(gtlbflush)
+GEN_FALSE_TRANS(gtlbsrch)
+GEN_FALSE_TRANS(gtlbrd)
+GEN_FALSE_TRANS(gtlbwr)
+GEN_FALSE_TRANS(gtlbfill)
GEN_FALSE_TRANS(hvcl)
#else
@@ -305,7 +311,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;
}
@@ -378,25 +384,61 @@ static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
return false;
}
+ if (!avail_LVZ(ctx) && a->imm > 0x6) {
+ return false;
+ }
+
+ //TODO: futher refinement of op 0x9 and 0x10-0x16
switch (a->imm) {
- case 0:
- case 1:
- gen_helper_invtlb_all(tcg_env);
+ case 0x0:
+ case 0x1:
+ gen_helper_invtlb_all(tcg_env, rj, tcg_constant_i32(a->imm),
+ tcg_constant_i32(0));
+ break;
+ case 0x2:
+ gen_helper_invtlb_all_g(tcg_env, rj, tcg_constant_i32(1),
+ tcg_constant_i32(0));
+ break;
+ case 0x3:
+ gen_helper_invtlb_all_g(tcg_env, rj, tcg_constant_i32(0),
+ tcg_constant_i32(0));
+ break;
+ case 0x4:
+ gen_helper_invtlb_all_asid(tcg_env, rj, tcg_constant_i32(0));
+ break;
+ case 0x5:
+ gen_helper_invtlb_page_asid(tcg_env, rj, rk, tcg_constant_i32(0));
break;
- case 2:
- gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(1));
+ case 0x6:
+ gen_helper_invtlb_page_asid_or_g(tcg_env, rj, rk, tcg_constant_i32(0));
break;
- case 3:
- gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(0));
+ case 0x9:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ gen_helper_invtlb_all(tcg_env, rj, tcg_constant_i32(0),
+ tcg_constant_i32(0));
break;
- case 4:
- gen_helper_invtlb_all_asid(tcg_env, rj);
+ case 0xa:
+ gen_helper_invtlb_all_g(tcg_env, rj, tcg_constant_i32(1),
+ tcg_constant_i32(1));
break;
- case 5:
- gen_helper_invtlb_page_asid(tcg_env, rj, rk);
+ case 0xb:
+ gen_helper_invtlb_all_g(tcg_env, rj, tcg_constant_i32(0),
+ tcg_constant_i32(1));
break;
- case 6:
- gen_helper_invtlb_page_asid_or_g(tcg_env, rj, rk);
+ 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;
@@ -613,6 +655,64 @@ static bool trans_gcsrxchg(DisasContext *ctx, arg_gcsrxchg
*a)
return true;
}
+static bool trans_gtlbsrch(DisasContext *ctx, arg_gtlbsrch *a)
+{
+ if (check_plv(ctx) || !avail_LVZ(ctx) || ctx->guest_mode) {
+ return false;
+ }
+ gen_helper_gtlbsrch(tcg_env);
+ return true;
+}
+
+static bool trans_gtlbrd(DisasContext *ctx, arg_gtlbrd *a)
+{
+ if (check_plv(ctx) || !avail_LVZ(ctx) || ctx->guest_mode) {
+ return false;
+ }
+ gen_helper_gtlbrd(tcg_env);
+ return true;
+}
+
+static bool trans_gtlbwr(DisasContext *ctx, arg_gtlbwr *a)
+{
+ if (check_plv(ctx) || !avail_LVZ(ctx) || ctx->guest_mode) {
+ return false;
+ }
+ gen_helper_gtlbwr(tcg_env);
+ check_mmu_idx(ctx);
+ return true;
+}
+
+static bool trans_gtlbfill(DisasContext *ctx, arg_gtlbfill *a)
+{
+ if (check_plv(ctx) || !avail_LVZ(ctx) || ctx->guest_mode) {
+ return false;
+ }
+ gen_helper_gtlbfill(tcg_env);
+ check_mmu_idx(ctx);
+ return true;
+}
+
+static bool trans_gtlbclr(DisasContext *ctx, arg_gtlbclr *a)
+{
+ if (check_plv(ctx) || !avail_LVZ(ctx) || ctx->guest_mode) {
+ return false;
+ }
+ gen_helper_gtlbclr(tcg_env);
+ check_mmu_idx(ctx);
+ return true;
+}
+
+static bool trans_gtlbflush(DisasContext *ctx, arg_gtlbflush *a)
+{
+ if (check_plv(ctx) || !avail_LVZ(ctx) || ctx->guest_mode) {
+ return false;
+ }
+ gen_helper_gtlbflush(tcg_env);
+ check_mmu_idx(ctx);
+ return true;
+}
+
static bool trans_hvcl(DisasContext *ctx, arg_hvcl *a)
{
if (!avail_LVZ(ctx)) {
diff --git a/target/loongarch/tcg/tcg_loongarch.h
b/target/loongarch/tcg/tcg_loongarch.h
index 7fb627f2d6..ceba1e4062 100644
--- a/target/loongarch/tcg/tcg_loongarch.h
+++ b/target/loongarch/tcg/tcg_loongarch.h
@@ -16,8 +16,8 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int
size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr);
-TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env,
- MMUContext *context,
- MMUAccessType access_type, int mmu_idx);
+TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, MMUContext *context,
+ MMUAccessType access_type, int mmu_idx,
+ bool guest);
#endif /* TARGET_LOONGARCH_TCG_LOONGARCH_H */
diff --git a/target/loongarch/tcg/tlb_helper.c
b/target/loongarch/tcg/tlb_helper.c
index 892e0eb473..fec0aeeb57 100644
--- a/target/loongarch/tcg/tlb_helper.c
+++ b/target/loongarch/tcg/tlb_helper.c
@@ -34,6 +34,13 @@ static bool tlb_match_asid(bool global, int asid, int
tlb_asid)
return !global && tlb_asid == asid;
}
+static inline bool tlb_entry_matches_gid(LoongArchTLB *tlb, uint8_t gid)
+{
+ uint8_t entry_gid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, GID);
+
+ return entry_gid == gid;
+}
+
bool check_ps(CPULoongArchState *env, uint8_t tlb_ps)
{
if (tlb_ps >= 64) {
@@ -46,14 +53,22 @@ static void raise_mmu_exception(CPULoongArchState *env,
vaddr address,
MMUAccessType access_type, TLBRet tlb_error)
{
CPUState *cs = env_cpu(env);
+ bool real_guest;
+
+ if (env->guest && tlb_error > TLBRET_HOST_MATCH) {
+ trigger_vm_exit(env);
+ }
+ real_guest = !env->vm_exit && env->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;
@@ -62,9 +77,12 @@ static void raise_mmu_exception(CPULoongArchState *env,
vaddr address,
} else if (access_type == MMU_INST_FETCH) {
cs->exception_index = EXCCODE_PIF;
}
- env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR,
1);
+ SET_CSR_IF(real_guest, TLBRERA,
+ FIELD_DP64(GET_CSR_IF(real_guest, 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;
@@ -75,46 +93,58 @@ 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) {
- env->CSR_TLBRBADV = address;
+ if (tlb_error == TLBRET_NOMATCH || tlb_error == TLBRET_HOST_NOMATCH) {
+ SET_CSR_IF(real_guest, TLBRBADV, address);
if (is_la64(env)) {
- env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI_64,
- VPPN, extract64(address, 13, 35));
+ SET_CSR_IF(real_guest, TLBREHI,
+ FIELD_DP64(GET_CSR_IF(real_guest, TLBREHI),
+ CSR_TLBREHI_64, VPPN,
+ extract64(address, 13, 35)));
} else {
- env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI_32,
- VPPN, extract64(address, 13, 19));
+ SET_CSR_IF(real_guest, TLBREHI,
+ FIELD_DP64(GET_CSR_IF(real_guest, TLBREHI),
+ CSR_TLBREHI_32, VPPN,
+ extract64(address, 13, 19)));
}
} else {
if (!FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) {
- env->CSR_BADV = address;
+ SET_CSR_IF(real_guest, BADV, address);
}
- env->CSR_TLBEHI = address & (TARGET_PAGE_MASK << 1);
- }
+ SET_CSR_IF(real_guest, TLBEHI, address & (TARGET_PAGE_MASK << 1));
+ }
}
-static void invalidate_tlb_entry(CPULoongArchState *env, int index)
+static void invalidate_tlb_entry(CPULoongArchState *env, int index, bool guest)
{
target_ulong addr, mask, pagesize;
uint8_t tlb_ps;
- LoongArchTLB *tlb = &env->tlb[index];
- int idxmap = BIT(MMU_KERNEL_IDX) | BIT(MMU_USER_IDX);
+ LoongArchTLB *tlb = guest ? &env->gtlb[index] : &env->tlb[index];
+ int idxmap =
+ guest ? (BIT(MMU_GUEST_IDX) | BIT(MMU_GUEST_IDX + 1) |
+ BIT(MMU_GUEST_IDX + 2) | BIT(MMU_GUEST_IDX + 3) |
+ BIT(MMU_GUEST_DA_IDX)) :
+ (BIT(MMU_KERNEL_IDX) | BIT(MMU_USER_IDX) | BIT(MMU_DA_IDX));
uint64_t tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
bool tlb_v;
@@ -124,27 +154,27 @@ static void invalidate_tlb_entry(CPULoongArchState *env,
int index)
addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask;
addr = sextract64(addr, 0, TARGET_VIRT_ADDR_SPACE_BITS);
- tlb_v = pte_present(env, tlb->tlb_entry0);
+ tlb_v = pte_present(env, tlb->tlb_entry0, guest);
if (tlb_v) {
tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize,
idxmap, TARGET_LONG_BITS);
}
- tlb_v = pte_present(env, tlb->tlb_entry1);
+ tlb_v = pte_present(env, tlb->tlb_entry1, guest);
if (tlb_v) {
tlb_flush_range_by_mmuidx(env_cpu(env), addr + pagesize, pagesize,
idxmap, TARGET_LONG_BITS);
}
}
-static void invalidate_tlb(CPULoongArchState *env, int index)
+static void invalidate_tlb(CPULoongArchState *env, int index, bool guest)
{
LoongArchTLB *tlb;
uint16_t csr_asid, tlb_asid, tlb_g;
uint8_t tlb_e;
- csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
- tlb = &env->tlb[index];
+ csr_asid = FIELD_EX64(GET_CSR_IF(guest, ASID), CSR_ASID, ASID);
+ tlb = guest ? &env->gtlb[index] : &env->tlb[index];
tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
if (!tlb_e) {
return;
@@ -157,33 +187,38 @@ 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, guest);
}
/* Prepare tlb entry information in software PTW mode */
-static void sptw_prepare_context(CPULoongArchState *env, MMUContext *context)
+static void sptw_prepare_context(CPULoongArchState *env, MMUContext *context,
+ bool guest)
{
uint64_t lo0, lo1, csr_vppn;
uint8_t csr_ps;
- if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
- csr_ps = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS);
+ if (FIELD_EX64(GET_CSR_IF(guest, TLBRERA), CSR_TLBRERA, ISTLBR)) {
+ csr_ps = FIELD_EX64(GET_CSR_IF(guest, TLBREHI), CSR_TLBREHI, PS);
if (is_la64(env)) {
- csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI_64, VPPN);
+ csr_vppn =
+ FIELD_EX64(GET_CSR_IF(guest, TLBREHI), CSR_TLBREHI_64, VPPN);
} else {
- csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI_32, VPPN);
+ csr_vppn =
+ FIELD_EX64(GET_CSR_IF(guest, TLBREHI), CSR_TLBREHI_32, VPPN);
}
- lo0 = env->CSR_TLBRELO0;
- lo1 = env->CSR_TLBRELO1;
+ lo0 = GET_CSR_IF(guest, TLBRELO0);
+ lo1 = GET_CSR_IF(guest, TLBRELO1);
} else {
- csr_ps = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS);
+ csr_ps = FIELD_EX64(GET_CSR_IF(guest, TLBIDX), CSR_TLBIDX, PS);
if (is_la64(env)) {
- csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI_64, VPPN);
+ csr_vppn =
+ FIELD_EX64(GET_CSR_IF(guest, TLBEHI), CSR_TLBEHI_64, VPPN);
} else {
- csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI_32, VPPN);
+ csr_vppn =
+ FIELD_EX64(GET_CSR_IF(guest, TLBEHI), CSR_TLBEHI_32, VPPN);
}
- lo0 = env->CSR_TLBELO0;
- lo1 = env->CSR_TLBELO1;
+ lo0 = GET_CSR_IF(guest, TLBELO0);
+ lo1 = GET_CSR_IF(guest, TLBELO1);
}
context->ps = csr_ps;
@@ -193,7 +228,7 @@ static void sptw_prepare_context(CPULoongArchState *env,
MMUContext *context)
}
static void fill_tlb_entry(CPULoongArchState *env, LoongArchTLB *tlb,
- MMUContext *context)
+ MMUContext *context, bool guest)
{
uint64_t lo0, lo1, csr_vppn;
uint16_t csr_asid;
@@ -208,8 +243,9 @@ static void fill_tlb_entry(CPULoongArchState *env,
LoongArchTLB *tlb,
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps);
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn);
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1);
- csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
+ csr_asid = FIELD_EX64(GET_CSR_IF(guest, ASID), CSR_ASID, ASID);
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, ASID, csr_asid);
+ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, GID, get_tgid(env));
tlb->tlb_entry0 = lo0;
tlb->tlb_entry1 = lo1;
@@ -233,7 +269,8 @@ static uint32_t get_random_tlb(uint32_t low, uint32_t high)
*/
static LoongArchTLB *loongarch_tlb_search_cb(CPULoongArchState *env,
vaddr vaddr, int csr_asid,
- tlb_match func)
+ tlb_match func, bool guest,
+ uint8_t gid)
{
LoongArchTLB *tlb;
uint16_t tlb_asid, stlb_idx;
@@ -242,14 +279,15 @@ static LoongArchTLB
*loongarch_tlb_search_cb(CPULoongArchState *env,
int i, compare_shift;
uint64_t vpn, tlb_vppn;
- stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
+ stlb_ps = FIELD_EX64(GET_CSR_IF(guest, STLBPS), CSR_STLBPS, PS);
vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1);
stlb_idx = vpn & 0xff; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */
compare_shift = stlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
/* Search STLB */
for (i = 0; i < 8; ++i) {
- tlb = &env->tlb[i * 256 + stlb_idx];
+ tlb = guest ? &env->gtlb[i * 256 + stlb_idx] :
+ &env->tlb[i * 256 + stlb_idx];
tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
if (tlb_e) {
tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
@@ -257,6 +295,7 @@ static LoongArchTLB
*loongarch_tlb_search_cb(CPULoongArchState *env,
tlb_g = !!FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
if (func(tlb_g, csr_asid, tlb_asid) &&
+ tlb_entry_matches_gid(tlb, gid) &&
(vpn == (tlb_vppn >> compare_shift))) {
return tlb;
}
@@ -265,7 +304,7 @@ static LoongArchTLB
*loongarch_tlb_search_cb(CPULoongArchState *env,
/* Search MTLB */
for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; ++i) {
- tlb = &env->tlb[i];
+ tlb = guest ? &env->gtlb[i] : &env->tlb[i];
tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
if (tlb_e) {
tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
@@ -275,6 +314,7 @@ static LoongArchTLB
*loongarch_tlb_search_cb(CPULoongArchState *env,
compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1);
if (func(tlb_g, csr_asid, tlb_asid) &&
+ tlb_entry_matches_gid(tlb, gid) &&
(vpn == (tlb_vppn >> compare_shift))) {
return tlb;
}
@@ -284,17 +324,17 @@ static LoongArchTLB
*loongarch_tlb_search_cb(CPULoongArchState *env,
}
static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr,
- int *index)
+ int *index, bool guest, uint8_t gid)
{
int csr_asid;
tlb_match func;
LoongArchTLB *tlb;
func = tlb_match_any;
- csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
- tlb = loongarch_tlb_search_cb(env, vaddr, csr_asid, func);
+ csr_asid = FIELD_EX64(GET_CSR_IF(guest, ASID), CSR_ASID, ASID);
+ tlb = loongarch_tlb_search_cb(env, vaddr, csr_asid, func, guest, gid);
if (tlb) {
- *index = tlb - env->tlb;
+ *index = guest ? (tlb - env->gtlb) : (tlb - env->tlb);
return true;
}
@@ -304,66 +344,112 @@ static bool loongarch_tlb_search(CPULoongArchState *env,
vaddr vaddr,
void helper_tlbsrch(CPULoongArchState *env)
{
int index, match;
+ vaddr search_ehi;
- if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
- match = loongarch_tlb_search(env, env->CSR_TLBREHI, &index);
+ if (FIELD_EX64(GET_CSR_IF(env->guest, TLBRERA), CSR_TLBRERA, ISTLBR)) {
+ search_ehi = GET_CSR_IF(env->guest, TLBREHI);
} else {
- match = loongarch_tlb_search(env, env->CSR_TLBEHI, &index);
+ search_ehi = GET_CSR_IF(env->guest, TLBEHI);
}
+ match = loongarch_tlb_search(env, search_ehi, &index, env->guest,
+ get_tgid(env));
+
if (match) {
- env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX,
index);
- env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0);
+ SET_CSR_IF(env->guest, TLBIDX,
+ FIELD_DP64(GET_CSR_IF(env->guest, TLBIDX), CSR_TLBIDX,
INDEX,
+ index));
+ SET_CSR_IF(
+ env->guest, TLBIDX,
+ FIELD_DP64(GET_CSR_IF(env->guest, TLBIDX), CSR_TLBIDX, NE, 0));
return;
}
- env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1);
+ SET_CSR_IF(env->guest, TLBIDX,
+ FIELD_DP64(GET_CSR_IF(env->guest, TLBIDX), CSR_TLBIDX, NE, 1));
}
-void helper_tlbrd(CPULoongArchState *env)
+static void read_tlb(CPULoongArchState *env, bool guest)
{
LoongArchTLB *tlb;
int index;
uint8_t tlb_ps, tlb_e;
- index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
- tlb = &env->tlb[index];
+ index = FIELD_EX64(GET_CSR_IF(guest, TLBIDX), CSR_TLBIDX, INDEX);
+ tlb = guest ? &env->gtlb[index] : &env->tlb[index];
tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
if (!tlb_e) {
/* Invalid TLB entry */
- env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1);
- env->CSR_ASID = FIELD_DP64(env->CSR_ASID, CSR_ASID, ASID, 0);
- env->CSR_TLBEHI = 0;
- env->CSR_TLBELO0 = 0;
- env->CSR_TLBELO1 = 0;
- env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, PS, 0);
+ SET_CSR_IF(guest, TLBIDX,
+ FIELD_DP64(GET_CSR_IF(guest, TLBIDX), CSR_TLBIDX, NE, 1));
+ SET_CSR_IF(guest, ASID,
+ FIELD_DP64(GET_CSR_IF(guest, ASID), CSR_ASID, ASID, 0));
+ SET_CSR_IF(guest, TLBEHI, 0);
+ SET_CSR_IF(guest, TLBELO0, 0);
+ SET_CSR_IF(guest, TLBELO1, 0);
+ SET_CSR_IF(guest, TLBIDX,
+ FIELD_DP64(GET_CSR_IF(guest, TLBIDX), CSR_TLBIDX, PS, 0));
} else {
/* Valid TLB entry */
- env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0);
- env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
- PS, (tlb_ps & 0x3f));
- env->CSR_TLBEHI = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN) <<
- R_TLB_MISC_VPPN_SHIFT;
- env->CSR_TLBELO0 = tlb->tlb_entry0;
- env->CSR_TLBELO1 = tlb->tlb_entry1;
+ SET_CSR_IF(guest, TLBIDX,
+ FIELD_DP64(GET_CSR_IF(guest, TLBIDX), CSR_TLBIDX, NE, 0));
+ SET_CSR_IF(guest, TLBIDX,
+ FIELD_DP64(GET_CSR_IF(guest, TLBIDX), CSR_TLBIDX, PS,
+ tlb_ps & 0x3f));
+ SET_CSR_IF(guest, TLBEHI,
+ FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN)
+ << R_TLB_MISC_VPPN_SHIFT);
+ SET_CSR_IF(guest, TLBELO0, tlb->tlb_entry0);
+ SET_CSR_IF(guest, TLBELO1, tlb->tlb_entry1);
+ }
+}
+
+void helper_tlbrd(CPULoongArchState *env)
+{
+ read_tlb(env, env->guest);
+}
+
+void helper_gtlbsrch(CPULoongArchState *env)
+{
+ int index, match;
+ vaddr search_ehi;
+
+ if (FIELD_EX64(env->GCSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
+ search_ehi = env->GCSR_TLBREHI;
+ } else {
+ search_ehi = env->GCSR_TLBEHI;
+ }
+
+ match = loongarch_tlb_search(env, search_ehi, &index, true, get_tgid(env));
+ if (match) {
+ env->GCSR_TLBIDX =
+ FIELD_DP64(env->GCSR_TLBIDX, CSR_TLBIDX, INDEX, index);
+ env->GCSR_TLBIDX = FIELD_DP64(env->GCSR_TLBIDX, CSR_TLBIDX, NE, 0);
+ return;
}
+ env->GCSR_TLBIDX = FIELD_DP64(env->GCSR_TLBIDX, CSR_TLBIDX, NE, 1);
+}
+
+void helper_gtlbrd(CPULoongArchState *env)
+{
+ read_tlb(env, true);
}
static void update_tlb_index(CPULoongArchState *env, MMUContext *context,
- int index)
+ int index, bool guest)
{
LoongArchTLB *old, new = {};
bool skip_inv = false, tlb_v0, tlb_v1;
- old = env->tlb + index;
- fill_tlb_entry(env, &new, context);
+ old = guest ? env->gtlb + index : env->tlb + index;
+ fill_tlb_entry(env, &new, context, guest);
/* 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, guest);
+ tlb_v1 = pte_present(env, old->tlb_entry1, guest);
if ((!tlb_v0 || new.tlb_entry0 == old->tlb_entry0) &&
(!tlb_v1 || new.tlb_entry1 == old->tlb_entry1)) {
skip_inv = true;
@@ -372,7 +458,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, guest);
}
*old = new;
@@ -380,20 +466,34 @@ static void update_tlb_index(CPULoongArchState *env,
MMUContext *context,
void helper_tlbwr(CPULoongArchState *env)
{
- int index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
+ int index = FIELD_EX64(GET_CSR_IF(env->guest, TLBIDX), CSR_TLBIDX, INDEX);
+ MMUContext context;
+
+ if (FIELD_EX64(GET_CSR_IF(env->guest, TLBIDX), CSR_TLBIDX, NE)) {
+ invalidate_tlb(env, index, env->guest);
+ return;
+ }
+
+ sptw_prepare_context(env, &context, env->guest);
+ update_tlb_index(env, &context, index, env->guest);
+}
+
+void helper_gtlbwr(CPULoongArchState *env)
+{
+ int index = FIELD_EX64(env->GCSR_TLBIDX, CSR_TLBIDX, INDEX);
MMUContext context;
- if (FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, NE)) {
- invalidate_tlb(env, index);
+ if (FIELD_EX64(env->GCSR_TLBIDX, CSR_TLBIDX, NE)) {
+ invalidate_tlb(env, index, true);
return;
}
- sptw_prepare_context(env, &context);
- update_tlb_index(env, &context, index);
+ sptw_prepare_context(env, &context, true);
+ update_tlb_index(env, &context, index, true);
}
static int get_tlb_random_index(CPULoongArchState *env, vaddr addr,
- int pagesize)
+ int pagesize, bool guest)
{
uint64_t address;
int index, set, i, stlb_idx;
@@ -402,15 +502,16 @@ static int get_tlb_random_index(CPULoongArchState *env,
vaddr addr,
uint8_t tlb_e, tlb_g;
/* Validity of stlb_ps is checked in helper_csrwr_stlbps() */
- stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
- asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
+ stlb_ps = FIELD_EX64(GET_CSR_IF(guest, STLBPS), CSR_STLBPS, PS);
+ asid = FIELD_EX64(GET_CSR_IF(guest, ASID), CSR_ASID, ASID);
if (pagesize == stlb_ps) {
/* Only write into STLB bits [47:13] */
address = addr & ~MAKE_64BIT_MASK(0, R_CSR_TLBEHI_64_VPPN_SHIFT);
set = -1;
stlb_idx = (address >> (stlb_ps + 1)) & 0xff; /* [0,255] */
for (i = 0; i < 8; ++i) {
- tlb = &env->tlb[i * 256 + stlb_idx];
+ tlb = guest ? &env->gtlb[i * 256 + stlb_idx] :
+ &env->tlb[i * 256 + stlb_idx];
tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
if (!tlb_e) {
set = i;
@@ -419,7 +520,8 @@ static int get_tlb_random_index(CPULoongArchState *env,
vaddr addr,
tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
- if (tlb_g == 0 && asid != tlb_asid) {
+ if (tlb_g == 0 && asid != tlb_asid &&
+ tlb_entry_matches_gid(tlb, get_tgid(env))) {
set = i;
}
}
@@ -433,7 +535,7 @@ static int get_tlb_random_index(CPULoongArchState *env,
vaddr addr,
/* Only write into MTLB */
index = -1;
for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) {
- tlb = &env->tlb[i];
+ tlb = guest ? &env->gtlb[i] : &env->tlb[i];
tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
if (!tlb_e) {
@@ -443,7 +545,8 @@ static int get_tlb_random_index(CPULoongArchState *env,
vaddr addr,
tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
- if (tlb_g == 0 && asid != tlb_asid) {
+ if (tlb_g == 0 && asid != tlb_asid &&
+ tlb_entry_matches_gid(tlb, get_tgid(env))) {
index = i;
}
}
@@ -462,48 +565,70 @@ void helper_tlbfill(CPULoongArchState *env)
int index, pagesize;
MMUContext context;
- if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
- entryhi = env->CSR_TLBREHI;
+ if (FIELD_EX64(GET_CSR_IF(env->guest, TLBRERA), CSR_TLBRERA, ISTLBR)) {
+ entryhi = GET_CSR_IF(env->guest, TLBREHI);
/* Validity of pagesize is checked in helper_ldpte() */
- pagesize = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS);
+ pagesize = FIELD_EX64(GET_CSR_IF(env->guest, TLBREHI), CSR_TLBREHI,
PS);
} else {
- entryhi = env->CSR_TLBEHI;
+ entryhi = GET_CSR_IF(env->guest, TLBEHI);
/* Validity of pagesize is checked in helper_tlbrd() */
- pagesize = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS);
+ pagesize = FIELD_EX64(GET_CSR_IF(env->guest, 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, env->guest);
+ index = get_tlb_random_index(env, entryhi, pagesize, env->guest);
+ invalidate_tlb(env, index, env->guest);
+ fill_tlb_entry(env, env->guest ? env->gtlb + index : env->tlb + index,
+ &context, env->guest);
}
-void helper_tlbclr(CPULoongArchState *env)
+void helper_gtlbfill(CPULoongArchState *env)
+{
+ vaddr entryhi;
+ int index, pagesize;
+ MMUContext context;
+
+ if (FIELD_EX64(env->GCSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
+ entryhi = env->GCSR_TLBREHI;
+ pagesize = FIELD_EX64(env->GCSR_TLBREHI, CSR_TLBREHI, PS);
+ } else {
+ entryhi = env->GCSR_TLBEHI;
+ pagesize = FIELD_EX64(env->GCSR_TLBIDX, CSR_TLBIDX, PS);
+ }
+
+ sptw_prepare_context(env, &context, true);
+ index = get_tlb_random_index(env, entryhi, pagesize, true);
+ invalidate_tlb(env, index, true);
+ fill_tlb_entry(env, env->gtlb + index, &context, true);
+}
+
+static void clear_tlb_by_index(CPULoongArchState *env, bool guest)
{
LoongArchTLB *tlb;
int i, index;
uint16_t csr_asid, tlb_asid, tlb_g;
- csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
- index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
+ csr_asid = FIELD_EX64(GET_CSR_IF(guest, ASID), CSR_ASID, ASID);
+ index = FIELD_EX64(GET_CSR_IF(guest, TLBIDX), CSR_TLBIDX, INDEX);
if (index < LOONGARCH_STLB) {
- /* STLB. One line per operation */
for (i = 0; i < 8; i++) {
- tlb = &env->tlb[i * 256 + (index % 256)];
+ tlb = guest ? &env->gtlb[i * 256 + (index % 256)] :
+ &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_tgid(env))) {
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
}
}
} else if (index < LOONGARCH_TLB_MAX) {
- /* All MTLB entries */
for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) {
- tlb = &env->tlb[i];
+ tlb = guest ? &env->gtlb[i] : &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_tgid(env))) {
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
}
}
@@ -512,62 +637,116 @@ void helper_tlbclr(CPULoongArchState *env)
tlb_flush(env_cpu(env));
}
-void helper_tlbflush(CPULoongArchState *env)
+void helper_tlbclr(CPULoongArchState *env)
+{
+ clear_tlb_by_index(env, env->guest);
+}
+
+void helper_gtlbclr(CPULoongArchState *env)
+{
+ clear_tlb_by_index(env, true);
+}
+
+static void flush_tlb_by_index(CPULoongArchState *env, bool guest)
{
int i, index;
- index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
+ index = FIELD_EX64(GET_CSR_IF(guest, TLBIDX), CSR_TLBIDX, INDEX);
if (index < LOONGARCH_STLB) {
- /* STLB. One line per operation */
for (i = 0; i < 8; i++) {
int s_idx = i * 256 + (index % 256);
- env->tlb[s_idx].tlb_misc = FIELD_DP64(env->tlb[s_idx].tlb_misc,
- TLB_MISC, E, 0);
+ LoongArchTLB *tlb = guest ? &env->gtlb[s_idx] : &env->tlb[s_idx];
+
+ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
}
} else if (index < LOONGARCH_TLB_MAX) {
- /* All MTLB entries */
for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) {
- env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc,
- TLB_MISC, E, 0);
+ LoongArchTLB *tlb = guest ? &env->gtlb[i] : &env->tlb[i];
+
+ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
}
}
tlb_flush(env_cpu(env));
}
-void helper_invtlb_all(CPULoongArchState *env)
+void helper_tlbflush(CPULoongArchState *env)
+{
+ flush_tlb_by_index(env, env->guest);
+}
+
+void helper_gtlbflush(CPULoongArchState *env)
+{
+ flush_tlb_by_index(env, true);
+}
+
+void helper_invtlb_all(CPULoongArchState *env, target_ulong info, uint32_t op,
+ uint32_t to_guest)
{
+ uint16_t gid = to_guest ? (info & 0xff) : get_tgid(env);
+
+ if (to_guest && env->guest) {
+ do_raise_exception(env, EXCCODE_IPE, GETPC());
+ }
+
+ to_guest |= env->guest;
+
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
- env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc,
- TLB_MISC, E, 0);
+ LoongArchTLB *tlb = &env->tlb[i];
+ LoongArchTLB *gtlb = &env->gtlb[i];
+
+ if (!to_guest && (op == 0 || tlb_entry_matches_gid(tlb, 0))) {
+ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
+ }
+ if ((!to_guest && op == 0) ||
+ (to_guest && tlb_entry_matches_gid(gtlb, gid))) {
+ gtlb->tlb_misc = FIELD_DP64(gtlb->tlb_misc, TLB_MISC, E, 0);
+ }
}
tlb_flush(env_cpu(env));
}
-void helper_invtlb_all_g(CPULoongArchState *env, uint32_t g)
+void helper_invtlb_all_g(CPULoongArchState *env, target_ulong info, uint32_t g,
+ uint32_t to_guest)
{
+ uint16_t gid = to_guest ? (info & 0xff) : get_tgid(env);
+
+ if (to_guest && env->guest) {
+ do_raise_exception(env, EXCCODE_IPE, GETPC());
+ }
+
+ to_guest |= env->guest;
+
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
- LoongArchTLB *tlb = &env->tlb[i];
+ LoongArchTLB *tlb = to_guest ? &env->gtlb[i] : &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);
}
}
tlb_flush(env_cpu(env));
}
-void helper_invtlb_all_asid(CPULoongArchState *env, target_ulong info)
+void helper_invtlb_all_asid(CPULoongArchState *env, target_ulong info,
+ uint32_t to_guest)
{
uint16_t asid = info & R_CSR_ASID_ASID_MASK;
+ uint16_t gid = to_guest ? ((info >> 16) & 0xff) : get_tgid(env);
+
+ if (to_guest && env->guest) {
+ do_raise_exception(env, EXCCODE_IPE, GETPC());
+ }
+
+ to_guest |= env->guest;
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
- LoongArchTLB *tlb = &env->tlb[i];
+ LoongArchTLB *tlb = to_guest ? &env->gtlb[i] : &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);
}
}
@@ -575,66 +754,82 @@ void helper_invtlb_all_asid(CPULoongArchState *env,
target_ulong info)
}
void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info,
- target_ulong addr)
+ target_ulong addr, uint32_t to_guest)
{
- int asid = info & 0x3ff;
+ uint16_t asid = info & R_CSR_ASID_ASID_MASK;
+ uint16_t gid = to_guest ? ((info >> 16) & 0xff) : get_tgid(env);
LoongArchTLB *tlb;
- tlb_match func;
+ int index;
+
+ if (to_guest && env->guest) {
+ do_raise_exception(env, EXCCODE_IPE, GETPC());
+ }
+ to_guest |= env->guest;
- func = tlb_match_asid;
- tlb = loongarch_tlb_search_cb(env, addr, asid, func);
+ tlb =
+ loongarch_tlb_search_cb(env, addr, asid, tlb_match_asid, to_guest,
gid);
if (tlb) {
- invalidate_tlb(env, tlb - env->tlb);
+ index = to_guest ? (tlb - env->gtlb) : (tlb - env->tlb);
+ invalidate_tlb(env, index, to_guest);
}
}
-void helper_invtlb_page_asid_or_g(CPULoongArchState *env,
- target_ulong info, target_ulong addr)
+void helper_invtlb_page_asid_or_g(CPULoongArchState *env, target_ulong info,
+ target_ulong addr, uint32_t to_guest)
{
- int asid = info & 0x3ff;
+ uint16_t asid = info & R_CSR_ASID_ASID_MASK;
+ uint16_t gid = to_guest ? ((info >> 16) & 0xff) : get_tgid(env);
LoongArchTLB *tlb;
- tlb_match func;
+ int index;
- func = tlb_match_any;
- tlb = loongarch_tlb_search_cb(env, addr, asid, func);
+ if (to_guest && env->guest) {
+ do_raise_exception(env, EXCCODE_IPE, GETPC());
+ }
+
+ to_guest |= env->guest;
+
+ tlb =
+ loongarch_tlb_search_cb(env, addr, asid, tlb_match_any, to_guest, gid);
if (tlb) {
- invalidate_tlb(env, tlb - env->tlb);
+ index = to_guest ? (tlb - env->gtlb) : (tlb - env->tlb);
+ invalidate_tlb(env, index, to_guest);
}
}
-static void ptw_update_tlb(CPULoongArchState *env, MMUContext *context)
+static void ptw_update_tlb(CPULoongArchState *env, MMUContext *context,
+ bool guest)
{
int index;
index = context->tlb_index;
if (index < 0) {
- index = get_tlb_random_index(env, context->addr, context->ps);
+ index = get_tlb_random_index(env, context->addr, context->ps, guest);
}
- update_tlb_index(env, context, index);
+ update_tlb_index(env, context, index, guest);
}
-bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr)
+TLBRet loongarch_map_host_address(CPULoongArchState *env, MMUContext *context,
+ MMUAccessType access_type, uintptr_t retaddr)
{
- CPULoongArchState *env = cpu_env(cs);
- hwaddr physical;
- int prot;
- MMUContext context;
TLBRet ret;
+ ret = loongarch_map_address(env, context, access_type, MMU_KERNEL_IDX,
+ false, false, retaddr);
+ return TLBRET_HOST_MATCH + ret;
+}
- /* Data access */
- context.addr = address;
- context.tlb_index = -1;
- ret = get_physical_address(env, &context, access_type, mmu_idx, 0);
- if (ret == TLBRET_MATCH && context.mmu_index != MMU_DA_IDX
- && cpu_has_ptw(env)) {
+static void loongarch_try_ptw(CPULoongArchState *env, MMUContext *context,
+ MMUAccessType access_type, int mmu_index,
+ TLBRet *status, bool guest, uintptr_t retaddr)
+{
+ if ((*status == TLBRET_MATCH || *status == TLBRET_HOST_MATCH) &&
+ context->mmu_index != MMU_DA_IDX &&
+ context->mmu_index != MMU_GUEST_DA_IDX && cpu_has_ptw(env, guest)) {
bool need_update = true;
- if (access_type == MMU_DATA_STORE && pte_dirty(context.pte)) {
+ if (access_type == MMU_DATA_STORE && pte_dirty(context->pte)) {
need_update = false;
- } else if (access_type != MMU_DATA_STORE && pte_access(context.pte)) {
+ } else if (access_type != MMU_DATA_STORE && pte_access(context->pte)) {
need_update = false;
/*
@@ -649,31 +844,77 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address,
int size,
if (need_update) {
/* Need update bit A/D in PTE entry, take PTW again */
- ret = TLBRET_NOMATCH;
+ *status =
+ (env->guest && !guest) ? TLBRET_HOST_NOMATCH : TLBRET_NOMATCH;
}
}
- if (ret != TLBRET_MATCH && cpu_has_ptw(env)) {
+ if (*status != TLBRET_MATCH && *status != TLBRET_HOST_MATCH &&
+ cpu_has_ptw(env, guest)) {
/* Take HW PTW if TLB missed or bit P is zero */
- if (ret == TLBRET_NOMATCH || ret == TLBRET_INVALID) {
- ret = loongarch_ptw(env, &context, access_type, mmu_idx, 0);
- if (ret == TLBRET_MATCH) {
- ptw_update_tlb(env, &context);
+ if (*status == TLBRET_NOMATCH || *status == TLBRET_INVALID ||
+ *status == TLBRET_HOST_NOMATCH || *status == TLBRET_HOST_INVALID) {
+ *status =
+ ((env->guest && !guest) ? TLBRET_HOST_MATCH : TLBRET_MATCH) +
+ loongarch_ptw(env, context, access_type, mmu_index, 0, guest,
+ retaddr);
+ if (*status == TLBRET_MATCH || *status == TLBRET_HOST_MATCH) {
+ ptw_update_tlb(env, context, guest);
}
- } else if (context.tlb_index >= 0) {
- invalidate_tlb(env, context.tlb_index);
+ } else if (context->tlb_index >= 0) {
+ invalidate_tlb(env, context->tlb_index, guest);
}
}
+}
+
+bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+ MMUAccessType access_type, int mmu_idx, bool probe,
+ uintptr_t retaddr)
+{
+ CPULoongArchState *env = cpu_env(cs);
+ MMUContext host_context;
+ hwaddr physical;
+ int prot, host_prot;
+ MMUContext context;
+ TLBRet ret;
+
+ /* Data access */
+ context.addr = address;
+ context.tlb_index = -1;
+ ret = get_physical_address(env, &context, access_type, mmu_idx, 0,
retaddr);
+ loongarch_try_ptw(env, &context, access_type, mmu_idx, &ret, env->guest,
+ retaddr);
if (ret == TLBRET_MATCH) {
physical = context.physical;
prot = context.prot;
+ if (env->guest) {
+ host_context.addr = physical;
+ host_context.tlb_index = -1;
+ ret = loongarch_map_host_address(env, &host_context, access_type,
+ retaddr);
+ loongarch_try_ptw(env, &host_context, access_type, MMU_KERNEL_IDX,
+ &ret, false, retaddr);
+ if (ret != TLBRET_HOST_MATCH) {
+ if (probe) {
+ return false;
+ }
+ raise_mmu_exception(env, physical, access_type, ret);
+ cpu_loop_exit_restore(cs, retaddr);
+ return false;
+ }
+ physical = host_context.physical;
+ host_prot = host_context.prot;
+ prot &= host_prot;
+ }
tlb_set_page(cs, address & TARGET_PAGE_MASK,
physical & TARGET_PAGE_MASK, prot,
mmu_idx, TARGET_PAGE_SIZE);
qemu_log_mask(CPU_LOG_MMU,
"%s address=%" VADDR_PRIx " physical " HWADDR_FMT_plx
- " prot %d\n", __func__, address, physical, prot);
+ " prot %d guest %d\n",
+ __func__, address, physical, prot,
+ is_guest_mmu_idx(mmu_idx));
return true;
} else {
qemu_log_mask(CPU_LOG_MMU,
@@ -702,6 +943,31 @@ static inline uint64_t
loongarch_sanitize_hw_pte(CPULoongArchState *env,
return (pte & ~ppn_mask) | ((pte & ppn_mask) & palen_mask);
}
+hwaddr loongarch_get_host_address(CPULoongArchState *env, hwaddr gpa,
+ uintptr_t retaddr)
+{
+ MMUContext host_context;
+ TLBRet ret;
+
+ if (!env->guest) {
+ return gpa;
+ }
+
+ host_context.addr = gpa;
+ host_context.tlb_index = -1;
+ ret =
+ loongarch_map_host_address(env, &host_context, MMU_DATA_LOAD, retaddr);
+ loongarch_try_ptw(env, &host_context, MMU_DATA_LOAD, MMU_KERNEL_IDX, &ret,
+ false, retaddr);
+
+ if (ret != TLBRET_HOST_MATCH) {
+ raise_mmu_exception(env, gpa, MMU_DATA_LOAD, ret);
+ cpu_loop_exit_restore(env_cpu(env), retaddr);
+ }
+
+ return host_context.physical;
+}
+
target_ulong helper_lddir(CPULoongArchState *env, target_ulong base,
uint32_t level, uint32_t mem_idx)
{
@@ -732,12 +998,15 @@ target_ulong helper_lddir(CPULoongArchState *env,
target_ulong base,
}
}
- badvaddr = env->CSR_TLBRBADV;
+ badvaddr = GET_CSR_IF(env->guest, 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->guest);
index = (badvaddr >> dir_base) & ((1 << dir_width) - 1);
phys = base | index << 3;
- val = address_space_ldq_le(cs->as, phys, MEMTXATTRS_UNSPECIFIED, NULL);
+ val = address_space_ldq_le(
+ cs->as,
+ (env->guest ? loongarch_get_host_address(env, phys, GETPC()) : phys),
+ MEMTXATTRS_UNSPECIFIED, NULL);
return val & palen_mask;
}
@@ -749,8 +1018,10 @@ void helper_ldpte(CPULoongArchState *env, target_ulong
base, target_ulong odd,
hwaddr phys, tmp0, ptindex, ptoffset0, ptoffset1;
uint64_t pte_raw;
uint64_t badv;
- uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE);
- uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH);
+ uint64_t ptbase =
+ FIELD_EX64(GET_CSR_IF(env->guest, PWCL), CSR_PWCL, PTBASE);
+ uint64_t ptwidth =
+ FIELD_EX64(GET_CSR_IF(env->guest, PWCL), CSR_PWCL, PTWIDTH);
uint64_t palen_mask = loongarch_palen_mask(env);
uint64_t dir_base, dir_width;
uint8_t ps;
@@ -771,7 +1042,7 @@ 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->guest);
base = FIELD_DP64(base, TLBENTRY, LEVEL, 0);
base = FIELD_DP64(base, TLBENTRY, HUGE, 0);
@@ -796,7 +1067,7 @@ void helper_ldpte(CPULoongArchState *env, target_ulong
base, target_ulong odd,
return;
}
} else {
- badv = env->CSR_TLBRBADV;
+ badv = GET_CSR_IF(env->guest, TLBRBADV);
base = base & palen_mask;
@@ -805,26 +1076,31 @@ void helper_ldpte(CPULoongArchState *env, target_ulong
base, target_ulong odd,
ptoffset0 = ptindex << 3;
ptoffset1 = (ptindex + 1) << 3;
phys = base | (odd ? ptoffset1 : ptoffset0);
- pte_raw = address_space_ldq_le(cs->as, phys,
- MEMTXATTRS_UNSPECIFIED, NULL);
+ pte_raw = address_space_ldq_le(
+ cs->as,
+ (env->guest ? loongarch_get_host_address(env, phys, GETPC()) :
+ phys),
+ MEMTXATTRS_UNSPECIFIED, NULL);
tmp0 = loongarch_sanitize_hw_pte(env, pte_raw);
ps = ptbase;
}
if (odd) {
- env->CSR_TLBRELO1 = tmp0;
+ SET_CSR_IF(env->guest, TLBRELO1, tmp0);
} else {
- env->CSR_TLBRELO0 = tmp0;
+ SET_CSR_IF(env->guest, TLBRELO0, tmp0);
}
- env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps);
+ SET_CSR_IF(
+ env->guest, TLBREHI,
+ FIELD_DP64(GET_CSR_IF(env->guest, TLBREHI), CSR_TLBREHI, PS, ps));
}
static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env,
MMUContext *context,
MMUAccessType access_type, int index,
- int mmu_idx)
+ int mmu_idx, bool guest)
{
- LoongArchTLB *tlb = &env->tlb[index];
+ LoongArchTLB *tlb = guest ? &env->gtlb[index] : &env->tlb[index];
uint8_t tlb_ps, n;
tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
@@ -832,19 +1108,20 @@ static TLBRet loongarch_map_tlb_entry(CPULoongArchState
*env,
context->pte = n ? tlb->tlb_entry1 : tlb->tlb_entry0;
context->ps = tlb_ps;
context->tlb_index = index;
- return loongarch_check_pte(env, context, access_type, mmu_idx);
+ return loongarch_check_pte(env, context, access_type, mmu_idx, guest);
}
-TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env,
- MMUContext *context,
- MMUAccessType access_type, int mmu_idx)
+TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, MMUContext *context,
+ MMUAccessType access_type, int mmu_idx,
+ bool guest)
{
int index, match;
- match = loongarch_tlb_search(env, context->addr, &index);
+ match =
+ loongarch_tlb_search(env, context->addr, &index, guest, get_tgid(env));
if (match) {
return loongarch_map_tlb_entry(env, context, access_type, index,
- mmu_idx);
+ mmu_idx, guest);
}
return TLBRET_NOMATCH;
--
2.52.0