aboutsummaryrefslogtreecommitdiff
path: root/target/loongarch/cpu_helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/loongarch/cpu_helper.c')
-rw-r--r--target/loongarch/cpu_helper.c227
1 files changed, 84 insertions, 143 deletions
diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c
index 930466c..867e7c8 100644
--- a/target/loongarch/cpu_helper.c
+++ b/target/loongarch/cpu_helper.c
@@ -7,28 +7,52 @@
*/
#include "qemu/osdep.h"
+#include "system/tcg.h"
#include "cpu.h"
+#include "accel/tcg/cpu-mmu-index.h"
+#include "exec/target_page.h"
#include "internals.h"
#include "cpu-csr.h"
+#include "cpu-mmu.h"
+#include "tcg/tcg_loongarch.h"
-#ifdef CONFIG_TCG
-static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical,
- int *prot, target_ulong address,
- int access_type, int index, int mmu_idx)
+void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
+ uint64_t *dir_width, unsigned int level)
+{
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ break;
+ }
+}
+
+TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context,
+ MMUAccessType access_type, int mmu_idx)
{
- LoongArchTLB *tlb = &env->tlb[index];
uint64_t plv = mmu_idx;
uint64_t tlb_entry, tlb_ppn;
- uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv;
+ uint8_t tlb_ps, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv;
- if (index >= LOONGARCH_STLB) {
- tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
- } else {
- tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
- }
- n = (address >> tlb_ps) & 0x1;/* Odd or even */
-
- tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0;
+ tlb_entry = context->pte;
+ tlb_ps = context->ps;
tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V);
tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D);
tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV);
@@ -45,7 +69,7 @@ static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical,
}
/* Remove sw bit between bit12 -- bit PS*/
- tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) -1));
+ tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) - 1));
/* Check access rights */
if (!tlb_v) {
@@ -69,87 +93,30 @@ static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical,
return TLBRET_DIRTY;
}
- *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) |
- (address & MAKE_64BIT_MASK(0, tlb_ps));
- *prot = PAGE_READ;
+ context->physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) |
+ (context->addr & MAKE_64BIT_MASK(0, tlb_ps));
+ context->prot = PAGE_READ;
if (tlb_d) {
- *prot |= PAGE_WRITE;
+ context->prot |= PAGE_WRITE;
}
if (!tlb_nx) {
- *prot |= PAGE_EXEC;
+ context->prot |= PAGE_EXEC;
}
return TLBRET_MATCH;
}
-/*
- * One tlb entry holds an adjacent odd/even pair, the vpn is the
- * content of the virtual page number divided by 2. So the
- * compare vpn is bit[47:15] for 16KiB page. while the vppn
- * field in tlb entry contains bit[47:13], so need adjust.
- * virt_vpn = vaddr[47:13]
- */
-bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr,
- int *index)
-{
- LoongArchTLB *tlb;
- uint16_t csr_asid, tlb_asid, stlb_idx;
- uint8_t tlb_e, tlb_ps, tlb_g, stlb_ps;
- int i, compare_shift;
- uint64_t vpn, tlb_vppn;
-
- csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
- stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
- vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1);
- stlb_idx = vpn & 0xff; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */
- 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_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
- if (tlb_e) {
- tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
- tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
- tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
-
- if ((tlb_g == 1 || tlb_asid == csr_asid) &&
- (vpn == (tlb_vppn >> compare_shift))) {
- *index = i * 256 + stlb_idx;
- return true;
- }
- }
- }
-
- /* Search MTLB */
- for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; ++i) {
- tlb = &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);
- tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
- tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
- tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
- compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
- vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1);
- if ((tlb_g == 1 || tlb_asid == csr_asid) &&
- (vpn == (tlb_vppn >> compare_shift))) {
- *index = i;
- return true;
- }
- }
- }
- return false;
-}
-
-static int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical,
- int *prot, target_ulong address)
+static TLBRet loongarch_page_table_walker(CPULoongArchState *env,
+ MMUContext *context,
+ int access_type, int mmu_idx)
{
CPUState *cs = env_cpu(env);
target_ulong index, phys;
uint64_t dir_base, dir_width;
uint64_t base;
int level;
+ vaddr address;
+ address = context->addr;
if ((address >> 63) & 0x1) {
base = env->CSR_PGDH;
} else {
@@ -191,65 +158,38 @@ static int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical,
base = ldq_phys(cs->as, phys);
}
- /* TODO: check plv and other bits? */
-
- /* base is pte, in normal pte format */
- if (!FIELD_EX64(base, TLBENTRY, V)) {
- return TLBRET_NOMATCH;
- }
-
- if (!FIELD_EX64(base, TLBENTRY, D)) {
- *prot = PAGE_READ;
- } else {
- *prot = PAGE_READ | PAGE_WRITE;
- }
-
- /* get TARGET_PAGE_SIZE aligned physical address */
- base += (address & TARGET_PHYS_MASK) & ((1 << dir_base) - 1);
- /* mask RPLV, NX, NR bits */
- base = FIELD_DP64(base, TLBENTRY_64, RPLV, 0);
- base = FIELD_DP64(base, TLBENTRY_64, NX, 0);
- base = FIELD_DP64(base, TLBENTRY_64, NR, 0);
- /* mask other attribute bits */
- *physical = base & TARGET_PAGE_MASK;
-
- return 0;
+ context->ps = dir_base;
+ context->pte = base;
+ return loongarch_check_pte(env, context, access_type, mmu_idx);
}
-static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical,
- int *prot, target_ulong address,
- MMUAccessType access_type, int mmu_idx,
- int is_debug)
+static TLBRet loongarch_map_address(CPULoongArchState *env,
+ MMUContext *context,
+ MMUAccessType access_type, int mmu_idx,
+ int is_debug)
{
- int index, match;
+ TLBRet ret;
+
+ if (tcg_enabled()) {
+ ret = loongarch_get_addr_from_tlb(env, context, access_type, mmu_idx);
+ if (ret != TLBRET_NOMATCH) {
+ return ret;
+ }
+ }
- match = loongarch_tlb_search(env, address, &index);
- if (match) {
- return loongarch_map_tlb_entry(env, physical, prot,
- address, access_type, index, mmu_idx);
- } else if (is_debug) {
+ if (is_debug) {
/*
* For debugger memory access, we want to do the map when there is a
* legal mapping, even if the mapping is not yet in TLB. return 0 if
* there is a valid map, else none zero.
*/
- return loongarch_page_table_walker(env, physical, prot, address);
+ return loongarch_page_table_walker(env, context, access_type, mmu_idx);
}
return TLBRET_NOMATCH;
}
-#else
-static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical,
- int *prot, target_ulong address,
- MMUAccessType access_type, int mmu_idx,
- int is_debug)
-{
- return TLBRET_NOMATCH;
-}
-#endif
-static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va,
- target_ulong dmw)
+static hwaddr dmw_va2pa(CPULoongArchState *env, vaddr va, target_ulong dmw)
{
if (is_la64(env)) {
return va & TARGET_VIRT_MASK;
@@ -260,9 +200,9 @@ static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va,
}
}
-int get_physical_address(CPULoongArchState *env, hwaddr *physical,
- int *prot, target_ulong address,
- MMUAccessType access_type, int mmu_idx, int is_debug)
+TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context,
+ MMUAccessType access_type, int mmu_idx,
+ int is_debug)
{
int user_mode = mmu_idx == MMU_USER_IDX;
int kernel_mode = mmu_idx == MMU_KERNEL_IDX;
@@ -270,11 +210,13 @@ int get_physical_address(CPULoongArchState *env, hwaddr *physical,
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);
+ vaddr address;
/* Check PG and DA */
+ address = context->addr;
if (da & !pg) {
- *physical = address & TARGET_PHYS_MASK;
- *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ context->physical = address & TARGET_PHYS_MASK;
+ context->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
return TLBRET_MATCH;
}
@@ -292,32 +234,31 @@ int get_physical_address(CPULoongArchState *env, hwaddr *physical,
base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_32, VSEG);
}
if ((plv & env->CSR_DMW[i]) && (base_c == base_v)) {
- *physical = dmw_va2pa(env, address, env->CSR_DMW[i]);
- *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ context->physical = dmw_va2pa(env, address, env->CSR_DMW[i]);
+ context->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
return TLBRET_MATCH;
}
}
/* Check valid extension */
- addr_high = sextract64(address, TARGET_VIRT_ADDR_SPACE_BITS, 16);
- if (!(addr_high == 0 || addr_high == -1)) {
+ addr_high = (int64_t)address >> (TARGET_VIRT_ADDR_SPACE_BITS - 1);
+ if (!(addr_high == 0 || addr_high == -1ULL)) {
return TLBRET_BADADDR;
}
/* Mapped address */
- return loongarch_map_address(env, physical, prot, address,
- access_type, mmu_idx, is_debug);
+ return loongarch_map_address(env, context, access_type, mmu_idx, is_debug);
}
hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
{
CPULoongArchState *env = cpu_env(cs);
- hwaddr phys_addr;
- int prot;
+ MMUContext context;
- if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD,
- cpu_mmu_index(cs, false), 1) != 0) {
+ context.addr = addr;
+ if (get_physical_address(env, &context, MMU_DATA_LOAD,
+ cpu_mmu_index(cs, false), 1) != TLBRET_MATCH) {
return -1;
}
- return phys_addr;
+ return context.physical;
}