Message ID | 20240125061401.52526-1-gaosong@loongson.cn |
---|---|
State | New |
Headers | show |
Series | target/loongarch: Fix qtest test-hmp error when KVM-only build | expand |
With qemu is compiled with option "--target-list=loongarch64-softmmu --disable-tcg", it passes to run with "make check" command. Also gdb debug for LoongArch kvm support depends on this. Tested-by: Bibo Mao <maobibo@loongson.cn> On 2024/1/25 下午2:14, Song Gao wrote: > The cc->sysemu_ops->get_phys_page_debug() is NULL when > KVM-only build. this patch fixes it. > > Signed-off-by: Song Gao <gaosong@loongson.cn> > --- > target/loongarch/internals.h | 20 ++- > target/loongarch/cpu.c | 2 - > target/loongarch/cpu_helper.c | 231 ++++++++++++++++++++++++++++++ > target/loongarch/tcg/tlb_helper.c | 230 ----------------------------- > target/loongarch/meson.build | 1 + > 5 files changed, 250 insertions(+), 234 deletions(-) > create mode 100644 target/loongarch/cpu_helper.c > > diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h > index 0beb034748..a2fc54c8a7 100644 > --- a/target/loongarch/internals.h > +++ b/target/loongarch/internals.h > @@ -37,6 +37,17 @@ void restore_fp_status(CPULoongArchState *env); > #endif > > #ifndef CONFIG_USER_ONLY > +enum { > + TLBRET_MATCH = 0, > + TLBRET_BADADDR = 1, > + TLBRET_NOMATCH = 2, > + TLBRET_INVALID = 3, > + TLBRET_DIRTY = 4, > + TLBRET_RI = 5, > + TLBRET_XI = 6, > + TLBRET_PE = 7, > +}; > + > extern const VMStateDescription vmstate_loongarch_cpu; > > void loongarch_cpu_set_irq(void *opaque, int irq, int level); > @@ -46,12 +57,17 @@ uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu); > uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu); > void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu, > uint64_t value); > +bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, > + int *index); > +int get_physical_address(CPULoongArchState *env, hwaddr *physical, > + int *prot, target_ulong address, > + MMUAccessType access_type, int mmu_idx); > +hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); > + > #ifdef CONFIG_TCG > bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, > MMUAccessType access_type, int mmu_idx, > bool probe, uintptr_t retaddr); > - > -hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); > #endif > #endif /* !CONFIG_USER_ONLY */ > > diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c > index 064540397d..1e58c160ab 100644 > --- a/target/loongarch/cpu.c > +++ b/target/loongarch/cpu.c > @@ -752,9 +752,7 @@ static struct TCGCPUOps loongarch_tcg_ops = { > #include "hw/core/sysemu-cpu-ops.h" > > static const struct SysemuCPUOps loongarch_sysemu_ops = { > -#ifdef CONFIG_TCG > .get_phys_page_debug = loongarch_cpu_get_phys_page_debug, > -#endif > }; > > static int64_t loongarch_cpu_get_arch_id(CPUState *cs) > diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c > new file mode 100644 > index 0000000000..f68d63f466 > --- /dev/null > +++ b/target/loongarch/cpu_helper.c > @@ -0,0 +1,231 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * LoongArch CPU helpers for qemu > + * > + * Copyright (c) 2024 Loongson Technology Corporation Limited > + * > + */ > + > +#include "qemu/osdep.h" > +#include "cpu.h" > +#include "internals.h" > +#include "cpu-csr.h" > + > +static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, > + int *prot, target_ulong address, > + int access_type, int index, 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; > + > + 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_v = FIELD_EX64(tlb_entry, TLBENTRY, V); > + tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); > + tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); > + if (is_la64(env)) { > + tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); > + tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); > + tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); > + tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); > + } else { > + tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); > + tlb_nx = 0; > + tlb_nr = 0; > + tlb_rplv = 0; > + } > + > + /* Remove sw bit between bit12 -- bit PS*/ > + tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) -1)); > + > + /* Check access rights */ > + if (!tlb_v) { > + return TLBRET_INVALID; > + } > + > + if (access_type == MMU_INST_FETCH && tlb_nx) { > + return TLBRET_XI; > + } > + > + if (access_type == MMU_DATA_LOAD && tlb_nr) { > + return TLBRET_RI; > + } > + > + if (((tlb_rplv == 0) && (plv > tlb_plv)) || > + ((tlb_rplv == 1) && (plv != tlb_plv))) { > + return TLBRET_PE; > + } > + > + if ((access_type == MMU_DATA_STORE) && !tlb_d) { > + return TLBRET_DIRTY; > + } > + > + *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | > + (address & MAKE_64BIT_MASK(0, tlb_ps)); > + *prot = PAGE_READ; > + if (tlb_d) { > + *prot |= PAGE_WRITE; > + } > + if (!tlb_nx) { > + *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_map_address(CPULoongArchState *env, hwaddr *physical, > + int *prot, target_ulong address, > + MMUAccessType access_type, int mmu_idx) > +{ > + int index, match; > + > + match = loongarch_tlb_search(env, address, &index); > + if (match) { > + return loongarch_map_tlb_entry(env, physical, prot, > + address, access_type, index, mmu_idx); > + } > + > + return TLBRET_NOMATCH; > +} > + > +static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, > + target_ulong dmw) > +{ > + if (is_la64(env)) { > + return va & TARGET_VIRT_MASK; > + } else { > + uint32_t pseg = FIELD_EX32(dmw, CSR_DMW_32, PSEG); > + return (va & MAKE_64BIT_MASK(0, R_CSR_DMW_32_VSEG_SHIFT)) | \ > + (pseg << R_CSR_DMW_32_VSEG_SHIFT); > + } > +} > + > +int get_physical_address(CPULoongArchState *env, hwaddr *physical, > + int *prot, target_ulong address, > + MMUAccessType access_type, int mmu_idx) > +{ > + int user_mode = mmu_idx == MMU_IDX_USER; > + int kernel_mode = mmu_idx == MMU_IDX_KERNEL; > + 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); > + > + /* Check PG and DA */ > + if (da & !pg) { > + *physical = address & TARGET_PHYS_MASK; > + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; > + return TLBRET_MATCH; > + } > + > + plv = kernel_mode | (user_mode << R_CSR_DMW_PLV3_SHIFT); > + if (is_la64(env)) { > + base_v = address >> R_CSR_DMW_64_VSEG_SHIFT; > + } else { > + base_v = address >> R_CSR_DMW_32_VSEG_SHIFT; > + } > + /* 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); > + } else { > + 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; > + return TLBRET_MATCH; > + } > + } > + > + /* Check valid extension */ > + addr_high = sextract64(address, TARGET_VIRT_ADDR_SPACE_BITS, 16); > + if (!(addr_high == 0 || addr_high == -1)) { > + return TLBRET_BADADDR; > + } > + > + /* Mapped address */ > + return loongarch_map_address(env, physical, prot, address, > + access_type, mmu_idx); > +} > + > +hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) > +{ > + LoongArchCPU *cpu = LOONGARCH_CPU(cs); > + CPULoongArchState *env = &cpu->env; > + hwaddr phys_addr; > + int prot; > + > + if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, > + cpu_mmu_index(env, false)) != 0) { > + return -1; > + } > + return phys_addr; > +} > diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c > index 449043c68b..804ab7a263 100644 > --- a/target/loongarch/tcg/tlb_helper.c > +++ b/target/loongarch/tcg/tlb_helper.c > @@ -17,236 +17,6 @@ > #include "exec/log.h" > #include "cpu-csr.h" > > -enum { > - TLBRET_MATCH = 0, > - TLBRET_BADADDR = 1, > - TLBRET_NOMATCH = 2, > - TLBRET_INVALID = 3, > - TLBRET_DIRTY = 4, > - TLBRET_RI = 5, > - TLBRET_XI = 6, > - TLBRET_PE = 7, > -}; > - > -static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, > - int *prot, target_ulong address, > - int access_type, int index, 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; > - > - 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_v = FIELD_EX64(tlb_entry, TLBENTRY, V); > - tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); > - tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); > - if (is_la64(env)) { > - tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); > - tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); > - tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); > - tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); > - } else { > - tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); > - tlb_nx = 0; > - tlb_nr = 0; > - tlb_rplv = 0; > - } > - > - /* Remove sw bit between bit12 -- bit PS*/ > - tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) -1)); > - > - /* Check access rights */ > - if (!tlb_v) { > - return TLBRET_INVALID; > - } > - > - if (access_type == MMU_INST_FETCH && tlb_nx) { > - return TLBRET_XI; > - } > - > - if (access_type == MMU_DATA_LOAD && tlb_nr) { > - return TLBRET_RI; > - } > - > - if (((tlb_rplv == 0) && (plv > tlb_plv)) || > - ((tlb_rplv == 1) && (plv != tlb_plv))) { > - return TLBRET_PE; > - } > - > - if ((access_type == MMU_DATA_STORE) && !tlb_d) { > - return TLBRET_DIRTY; > - } > - > - *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | > - (address & MAKE_64BIT_MASK(0, tlb_ps)); > - *prot = PAGE_READ; > - if (tlb_d) { > - *prot |= PAGE_WRITE; > - } > - if (!tlb_nx) { > - *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] > - */ > -static 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_map_address(CPULoongArchState *env, hwaddr *physical, > - int *prot, target_ulong address, > - MMUAccessType access_type, int mmu_idx) > -{ > - int index, match; > - > - match = loongarch_tlb_search(env, address, &index); > - if (match) { > - return loongarch_map_tlb_entry(env, physical, prot, > - address, access_type, index, mmu_idx); > - } > - > - return TLBRET_NOMATCH; > -} > - > -static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, > - target_ulong dmw) > -{ > - if (is_la64(env)) { > - return va & TARGET_VIRT_MASK; > - } else { > - uint32_t pseg = FIELD_EX32(dmw, CSR_DMW_32, PSEG); > - return (va & MAKE_64BIT_MASK(0, R_CSR_DMW_32_VSEG_SHIFT)) | \ > - (pseg << R_CSR_DMW_32_VSEG_SHIFT); > - } > -} > - > -static int get_physical_address(CPULoongArchState *env, hwaddr *physical, > - int *prot, target_ulong address, > - MMUAccessType access_type, int mmu_idx) > -{ > - int user_mode = mmu_idx == MMU_IDX_USER; > - int kernel_mode = mmu_idx == MMU_IDX_KERNEL; > - 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); > - > - /* Check PG and DA */ > - if (da & !pg) { > - *physical = address & TARGET_PHYS_MASK; > - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; > - return TLBRET_MATCH; > - } > - > - plv = kernel_mode | (user_mode << R_CSR_DMW_PLV3_SHIFT); > - if (is_la64(env)) { > - base_v = address >> R_CSR_DMW_64_VSEG_SHIFT; > - } else { > - base_v = address >> R_CSR_DMW_32_VSEG_SHIFT; > - } > - /* 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); > - } else { > - 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; > - return TLBRET_MATCH; > - } > - } > - > - /* Check valid extension */ > - addr_high = sextract64(address, TARGET_VIRT_ADDR_SPACE_BITS, 16); > - if (!(addr_high == 0 || addr_high == -1)) { > - return TLBRET_BADADDR; > - } > - > - /* Mapped address */ > - return loongarch_map_address(env, physical, prot, address, > - access_type, mmu_idx); > -} > - > -hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) > -{ > - LoongArchCPU *cpu = LOONGARCH_CPU(cs); > - CPULoongArchState *env = &cpu->env; > - hwaddr phys_addr; > - int prot; > - > - if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, > - cpu_mmu_index(env, false)) != 0) { > - return -1; > - } > - return phys_addr; > -} > - > static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, > MMUAccessType access_type, int tlb_error) > { > diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build > index db310f6022..e002e9aaf6 100644 > --- a/target/loongarch/meson.build > +++ b/target/loongarch/meson.build > @@ -8,6 +8,7 @@ loongarch_ss.add(files( > > loongarch_system_ss = ss.source_set() > loongarch_system_ss.add(files( > + 'cpu_helper.c', > 'loongarch-qmp-cmds.c', > 'machine.c', > )) >
diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index 0beb034748..a2fc54c8a7 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -37,6 +37,17 @@ void restore_fp_status(CPULoongArchState *env); #endif #ifndef CONFIG_USER_ONLY +enum { + TLBRET_MATCH = 0, + TLBRET_BADADDR = 1, + TLBRET_NOMATCH = 2, + TLBRET_INVALID = 3, + TLBRET_DIRTY = 4, + TLBRET_RI = 5, + TLBRET_XI = 6, + TLBRET_PE = 7, +}; + extern const VMStateDescription vmstate_loongarch_cpu; void loongarch_cpu_set_irq(void *opaque, int irq, int level); @@ -46,12 +57,17 @@ uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu); uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu); void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu, uint64_t value); +bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, + int *index); +int get_physical_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx); +hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); + #ifdef CONFIG_TCG bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); - -hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); #endif #endif /* !CONFIG_USER_ONLY */ diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 064540397d..1e58c160ab 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -752,9 +752,7 @@ static struct TCGCPUOps loongarch_tcg_ops = { #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps loongarch_sysemu_ops = { -#ifdef CONFIG_TCG .get_phys_page_debug = loongarch_cpu_get_phys_page_debug, -#endif }; static int64_t loongarch_cpu_get_arch_id(CPUState *cs) diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c new file mode 100644 index 0000000000..f68d63f466 --- /dev/null +++ b/target/loongarch/cpu_helper.c @@ -0,0 +1,231 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch CPU helpers for qemu + * + * Copyright (c) 2024 Loongson Technology Corporation Limited + * + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "internals.h" +#include "cpu-csr.h" + +static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + int access_type, int index, 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; + + 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_v = FIELD_EX64(tlb_entry, TLBENTRY, V); + tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); + tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); + if (is_la64(env)) { + tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); + tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); + tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); + tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); + } else { + tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); + tlb_nx = 0; + tlb_nr = 0; + tlb_rplv = 0; + } + + /* Remove sw bit between bit12 -- bit PS*/ + tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) -1)); + + /* Check access rights */ + if (!tlb_v) { + return TLBRET_INVALID; + } + + if (access_type == MMU_INST_FETCH && tlb_nx) { + return TLBRET_XI; + } + + if (access_type == MMU_DATA_LOAD && tlb_nr) { + return TLBRET_RI; + } + + if (((tlb_rplv == 0) && (plv > tlb_plv)) || + ((tlb_rplv == 1) && (plv != tlb_plv))) { + return TLBRET_PE; + } + + if ((access_type == MMU_DATA_STORE) && !tlb_d) { + return TLBRET_DIRTY; + } + + *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | + (address & MAKE_64BIT_MASK(0, tlb_ps)); + *prot = PAGE_READ; + if (tlb_d) { + *prot |= PAGE_WRITE; + } + if (!tlb_nx) { + *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_map_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + int index, match; + + match = loongarch_tlb_search(env, address, &index); + if (match) { + return loongarch_map_tlb_entry(env, physical, prot, + address, access_type, index, mmu_idx); + } + + return TLBRET_NOMATCH; +} + +static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, + target_ulong dmw) +{ + if (is_la64(env)) { + return va & TARGET_VIRT_MASK; + } else { + uint32_t pseg = FIELD_EX32(dmw, CSR_DMW_32, PSEG); + return (va & MAKE_64BIT_MASK(0, R_CSR_DMW_32_VSEG_SHIFT)) | \ + (pseg << R_CSR_DMW_32_VSEG_SHIFT); + } +} + +int get_physical_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + int user_mode = mmu_idx == MMU_IDX_USER; + int kernel_mode = mmu_idx == MMU_IDX_KERNEL; + 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); + + /* Check PG and DA */ + if (da & !pg) { + *physical = address & TARGET_PHYS_MASK; + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return TLBRET_MATCH; + } + + plv = kernel_mode | (user_mode << R_CSR_DMW_PLV3_SHIFT); + if (is_la64(env)) { + base_v = address >> R_CSR_DMW_64_VSEG_SHIFT; + } else { + base_v = address >> R_CSR_DMW_32_VSEG_SHIFT; + } + /* 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); + } else { + 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; + return TLBRET_MATCH; + } + } + + /* Check valid extension */ + addr_high = sextract64(address, TARGET_VIRT_ADDR_SPACE_BITS, 16); + if (!(addr_high == 0 || addr_high == -1)) { + return TLBRET_BADADDR; + } + + /* Mapped address */ + return loongarch_map_address(env, physical, prot, address, + access_type, mmu_idx); +} + +hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = &cpu->env; + hwaddr phys_addr; + int prot; + + if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, + cpu_mmu_index(env, false)) != 0) { + return -1; + } + return phys_addr; +} diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 449043c68b..804ab7a263 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -17,236 +17,6 @@ #include "exec/log.h" #include "cpu-csr.h" -enum { - TLBRET_MATCH = 0, - TLBRET_BADADDR = 1, - TLBRET_NOMATCH = 2, - TLBRET_INVALID = 3, - TLBRET_DIRTY = 4, - TLBRET_RI = 5, - TLBRET_XI = 6, - TLBRET_PE = 7, -}; - -static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - int access_type, int index, 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; - - 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_v = FIELD_EX64(tlb_entry, TLBENTRY, V); - tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); - tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); - if (is_la64(env)) { - tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); - tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); - tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); - tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); - } else { - tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); - tlb_nx = 0; - tlb_nr = 0; - tlb_rplv = 0; - } - - /* Remove sw bit between bit12 -- bit PS*/ - tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) -1)); - - /* Check access rights */ - if (!tlb_v) { - return TLBRET_INVALID; - } - - if (access_type == MMU_INST_FETCH && tlb_nx) { - return TLBRET_XI; - } - - if (access_type == MMU_DATA_LOAD && tlb_nr) { - return TLBRET_RI; - } - - if (((tlb_rplv == 0) && (plv > tlb_plv)) || - ((tlb_rplv == 1) && (plv != tlb_plv))) { - return TLBRET_PE; - } - - if ((access_type == MMU_DATA_STORE) && !tlb_d) { - return TLBRET_DIRTY; - } - - *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | - (address & MAKE_64BIT_MASK(0, tlb_ps)); - *prot = PAGE_READ; - if (tlb_d) { - *prot |= PAGE_WRITE; - } - if (!tlb_nx) { - *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] - */ -static 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_map_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) -{ - int index, match; - - match = loongarch_tlb_search(env, address, &index); - if (match) { - return loongarch_map_tlb_entry(env, physical, prot, - address, access_type, index, mmu_idx); - } - - return TLBRET_NOMATCH; -} - -static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, - target_ulong dmw) -{ - if (is_la64(env)) { - return va & TARGET_VIRT_MASK; - } else { - uint32_t pseg = FIELD_EX32(dmw, CSR_DMW_32, PSEG); - return (va & MAKE_64BIT_MASK(0, R_CSR_DMW_32_VSEG_SHIFT)) | \ - (pseg << R_CSR_DMW_32_VSEG_SHIFT); - } -} - -static int get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) -{ - int user_mode = mmu_idx == MMU_IDX_USER; - int kernel_mode = mmu_idx == MMU_IDX_KERNEL; - 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); - - /* Check PG and DA */ - if (da & !pg) { - *physical = address & TARGET_PHYS_MASK; - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return TLBRET_MATCH; - } - - plv = kernel_mode | (user_mode << R_CSR_DMW_PLV3_SHIFT); - if (is_la64(env)) { - base_v = address >> R_CSR_DMW_64_VSEG_SHIFT; - } else { - base_v = address >> R_CSR_DMW_32_VSEG_SHIFT; - } - /* 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); - } else { - 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; - return TLBRET_MATCH; - } - } - - /* Check valid extension */ - addr_high = sextract64(address, TARGET_VIRT_ADDR_SPACE_BITS, 16); - if (!(addr_high == 0 || addr_high == -1)) { - return TLBRET_BADADDR; - } - - /* Mapped address */ - return loongarch_map_address(env, physical, prot, address, - access_type, mmu_idx); -} - -hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - CPULoongArchState *env = &cpu->env; - hwaddr phys_addr; - int prot; - - if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, - cpu_mmu_index(env, false)) != 0) { - return -1; - } - return phys_addr; -} - static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, MMUAccessType access_type, int tlb_error) { diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build index db310f6022..e002e9aaf6 100644 --- a/target/loongarch/meson.build +++ b/target/loongarch/meson.build @@ -8,6 +8,7 @@ loongarch_ss.add(files( loongarch_system_ss = ss.source_set() loongarch_system_ss.add(files( + 'cpu_helper.c', 'loongarch-qmp-cmds.c', 'machine.c', ))
The cc->sysemu_ops->get_phys_page_debug() is NULL when KVM-only build. this patch fixes it. Signed-off-by: Song Gao <gaosong@loongson.cn> --- target/loongarch/internals.h | 20 ++- target/loongarch/cpu.c | 2 - target/loongarch/cpu_helper.c | 231 ++++++++++++++++++++++++++++++ target/loongarch/tcg/tlb_helper.c | 230 ----------------------------- target/loongarch/meson.build | 1 + 5 files changed, 250 insertions(+), 234 deletions(-) create mode 100644 target/loongarch/cpu_helper.c