xref: /openbmc/qemu/target/loongarch/cpu_helper.c (revision 937e2cb759589105fb73f796c11fe7ae80b0d66e)
127edd504SSong Gao /* SPDX-License-Identifier: GPL-2.0-or-later */
227edd504SSong Gao /*
327edd504SSong Gao  * LoongArch CPU helpers for qemu
427edd504SSong Gao  *
527edd504SSong Gao  * Copyright (c) 2024 Loongson Technology Corporation Limited
627edd504SSong Gao  *
727edd504SSong Gao  */
827edd504SSong Gao 
927edd504SSong Gao #include "qemu/osdep.h"
1027edd504SSong Gao #include "cpu.h"
1127edd504SSong Gao #include "internals.h"
1227edd504SSong Gao #include "cpu-csr.h"
1327edd504SSong Gao 
14*6f703a48SBibo Mao #ifdef CONFIG_TCG
loongarch_map_tlb_entry(CPULoongArchState * env,hwaddr * physical,int * prot,target_ulong address,int access_type,int index,int mmu_idx)1527edd504SSong Gao static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical,
1627edd504SSong Gao                                    int *prot, target_ulong address,
1727edd504SSong Gao                                    int access_type, int index, int mmu_idx)
1827edd504SSong Gao {
1927edd504SSong Gao     LoongArchTLB *tlb = &env->tlb[index];
2027edd504SSong Gao     uint64_t plv = mmu_idx;
2127edd504SSong Gao     uint64_t tlb_entry, tlb_ppn;
2227edd504SSong Gao     uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv;
2327edd504SSong Gao 
2427edd504SSong Gao     if (index >= LOONGARCH_STLB) {
2527edd504SSong Gao         tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
2627edd504SSong Gao     } else {
2727edd504SSong Gao         tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
2827edd504SSong Gao     }
2927edd504SSong Gao     n = (address >> tlb_ps) & 0x1;/* Odd or even */
3027edd504SSong Gao 
3127edd504SSong Gao     tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0;
3227edd504SSong Gao     tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V);
3327edd504SSong Gao     tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D);
3427edd504SSong Gao     tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV);
3527edd504SSong Gao     if (is_la64(env)) {
3627edd504SSong Gao         tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN);
3727edd504SSong Gao         tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX);
3827edd504SSong Gao         tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR);
3927edd504SSong Gao         tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV);
4027edd504SSong Gao     } else {
4127edd504SSong Gao         tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN);
4227edd504SSong Gao         tlb_nx = 0;
4327edd504SSong Gao         tlb_nr = 0;
4427edd504SSong Gao         tlb_rplv = 0;
4527edd504SSong Gao     }
4627edd504SSong Gao 
4727edd504SSong Gao     /* Remove sw bit between bit12 -- bit PS*/
4827edd504SSong Gao     tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) -1));
4927edd504SSong Gao 
5027edd504SSong Gao     /* Check access rights */
5127edd504SSong Gao     if (!tlb_v) {
5227edd504SSong Gao         return TLBRET_INVALID;
5327edd504SSong Gao     }
5427edd504SSong Gao 
5527edd504SSong Gao     if (access_type == MMU_INST_FETCH && tlb_nx) {
5627edd504SSong Gao         return TLBRET_XI;
5727edd504SSong Gao     }
5827edd504SSong Gao 
5927edd504SSong Gao     if (access_type == MMU_DATA_LOAD && tlb_nr) {
6027edd504SSong Gao         return TLBRET_RI;
6127edd504SSong Gao     }
6227edd504SSong Gao 
6327edd504SSong Gao     if (((tlb_rplv == 0) && (plv > tlb_plv)) ||
6427edd504SSong Gao         ((tlb_rplv == 1) && (plv != tlb_plv))) {
6527edd504SSong Gao         return TLBRET_PE;
6627edd504SSong Gao     }
6727edd504SSong Gao 
6827edd504SSong Gao     if ((access_type == MMU_DATA_STORE) && !tlb_d) {
6927edd504SSong Gao         return TLBRET_DIRTY;
7027edd504SSong Gao     }
7127edd504SSong Gao 
7227edd504SSong Gao     *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) |
7327edd504SSong Gao                 (address & MAKE_64BIT_MASK(0, tlb_ps));
7427edd504SSong Gao     *prot = PAGE_READ;
7527edd504SSong Gao     if (tlb_d) {
7627edd504SSong Gao         *prot |= PAGE_WRITE;
7727edd504SSong Gao     }
7827edd504SSong Gao     if (!tlb_nx) {
7927edd504SSong Gao         *prot |= PAGE_EXEC;
8027edd504SSong Gao     }
8127edd504SSong Gao     return TLBRET_MATCH;
8227edd504SSong Gao }
8327edd504SSong Gao 
8427edd504SSong Gao /*
8527edd504SSong Gao  * One tlb entry holds an adjacent odd/even pair, the vpn is the
8627edd504SSong Gao  * content of the virtual page number divided by 2. So the
8727edd504SSong Gao  * compare vpn is bit[47:15] for 16KiB page. while the vppn
8827edd504SSong Gao  * field in tlb entry contains bit[47:13], so need adjust.
8927edd504SSong Gao  * virt_vpn = vaddr[47:13]
9027edd504SSong Gao  */
loongarch_tlb_search(CPULoongArchState * env,target_ulong vaddr,int * index)9127edd504SSong Gao bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr,
9227edd504SSong Gao                           int *index)
9327edd504SSong Gao {
9427edd504SSong Gao     LoongArchTLB *tlb;
9527edd504SSong Gao     uint16_t csr_asid, tlb_asid, stlb_idx;
9627edd504SSong Gao     uint8_t tlb_e, tlb_ps, tlb_g, stlb_ps;
9727edd504SSong Gao     int i, compare_shift;
9827edd504SSong Gao     uint64_t vpn, tlb_vppn;
9927edd504SSong Gao 
10027edd504SSong Gao     csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
10127edd504SSong Gao     stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
10227edd504SSong Gao     vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1);
10327edd504SSong Gao     stlb_idx = vpn & 0xff; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */
10427edd504SSong Gao     compare_shift = stlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
10527edd504SSong Gao 
10627edd504SSong Gao     /* Search STLB */
10727edd504SSong Gao     for (i = 0; i < 8; ++i) {
10827edd504SSong Gao         tlb = &env->tlb[i * 256 + stlb_idx];
10927edd504SSong Gao         tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
11027edd504SSong Gao         if (tlb_e) {
11127edd504SSong Gao             tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
11227edd504SSong Gao             tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
11327edd504SSong Gao             tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
11427edd504SSong Gao 
11527edd504SSong Gao             if ((tlb_g == 1 || tlb_asid == csr_asid) &&
11627edd504SSong Gao                 (vpn == (tlb_vppn >> compare_shift))) {
11727edd504SSong Gao                 *index = i * 256 + stlb_idx;
11827edd504SSong Gao                 return true;
11927edd504SSong Gao             }
12027edd504SSong Gao         }
12127edd504SSong Gao     }
12227edd504SSong Gao 
12327edd504SSong Gao     /* Search MTLB */
12427edd504SSong Gao     for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; ++i) {
12527edd504SSong Gao         tlb = &env->tlb[i];
12627edd504SSong Gao         tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
12727edd504SSong Gao         if (tlb_e) {
12827edd504SSong Gao             tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
12927edd504SSong Gao             tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
13027edd504SSong Gao             tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
13127edd504SSong Gao             tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
13227edd504SSong Gao             compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
13327edd504SSong Gao             vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1);
13427edd504SSong Gao             if ((tlb_g == 1 || tlb_asid == csr_asid) &&
13527edd504SSong Gao                 (vpn == (tlb_vppn >> compare_shift))) {
13627edd504SSong Gao                 *index = i;
13727edd504SSong Gao                 return true;
13827edd504SSong Gao             }
13927edd504SSong Gao         }
14027edd504SSong Gao     }
14127edd504SSong Gao     return false;
14227edd504SSong Gao }
14327edd504SSong Gao 
loongarch_map_address(CPULoongArchState * env,hwaddr * physical,int * prot,target_ulong address,MMUAccessType access_type,int mmu_idx)14427edd504SSong Gao static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical,
14527edd504SSong Gao                                  int *prot, target_ulong address,
14627edd504SSong Gao                                  MMUAccessType access_type, int mmu_idx)
14727edd504SSong Gao {
14827edd504SSong Gao     int index, match;
14927edd504SSong Gao 
15027edd504SSong Gao     match = loongarch_tlb_search(env, address, &index);
15127edd504SSong Gao     if (match) {
15227edd504SSong Gao         return loongarch_map_tlb_entry(env, physical, prot,
15327edd504SSong Gao                                        address, access_type, index, mmu_idx);
15427edd504SSong Gao     }
15527edd504SSong Gao 
15627edd504SSong Gao     return TLBRET_NOMATCH;
15727edd504SSong Gao }
158*6f703a48SBibo Mao #else
loongarch_map_address(CPULoongArchState * env,hwaddr * physical,int * prot,target_ulong address,MMUAccessType access_type,int mmu_idx)159*6f703a48SBibo Mao static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical,
160*6f703a48SBibo Mao                                  int *prot, target_ulong address,
161*6f703a48SBibo Mao                                  MMUAccessType access_type, int mmu_idx)
162*6f703a48SBibo Mao {
163*6f703a48SBibo Mao     return TLBRET_NOMATCH;
164*6f703a48SBibo Mao }
165*6f703a48SBibo Mao #endif
16627edd504SSong Gao 
dmw_va2pa(CPULoongArchState * env,target_ulong va,target_ulong dmw)16727edd504SSong Gao static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va,
16827edd504SSong Gao                         target_ulong dmw)
16927edd504SSong Gao {
17027edd504SSong Gao     if (is_la64(env)) {
17127edd504SSong Gao         return va & TARGET_VIRT_MASK;
17227edd504SSong Gao     } else {
17327edd504SSong Gao         uint32_t pseg = FIELD_EX32(dmw, CSR_DMW_32, PSEG);
17427edd504SSong Gao         return (va & MAKE_64BIT_MASK(0, R_CSR_DMW_32_VSEG_SHIFT)) | \
17527edd504SSong Gao             (pseg << R_CSR_DMW_32_VSEG_SHIFT);
17627edd504SSong Gao     }
17727edd504SSong Gao }
17827edd504SSong Gao 
get_physical_address(CPULoongArchState * env,hwaddr * physical,int * prot,target_ulong address,MMUAccessType access_type,int mmu_idx)17927edd504SSong Gao int get_physical_address(CPULoongArchState *env, hwaddr *physical,
18027edd504SSong Gao                          int *prot, target_ulong address,
18127edd504SSong Gao                          MMUAccessType access_type, int mmu_idx)
18227edd504SSong Gao {
1833f262d25SRichard Henderson     int user_mode = mmu_idx == MMU_USER_IDX;
1843f262d25SRichard Henderson     int kernel_mode = mmu_idx == MMU_KERNEL_IDX;
18527edd504SSong Gao     uint32_t plv, base_c, base_v;
18627edd504SSong Gao     int64_t addr_high;
18727edd504SSong Gao     uint8_t da = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, DA);
18827edd504SSong Gao     uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG);
18927edd504SSong Gao 
19027edd504SSong Gao     /* Check PG and DA */
19127edd504SSong Gao     if (da & !pg) {
19227edd504SSong Gao         *physical = address & TARGET_PHYS_MASK;
19327edd504SSong Gao         *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
19427edd504SSong Gao         return TLBRET_MATCH;
19527edd504SSong Gao     }
19627edd504SSong Gao 
19727edd504SSong Gao     plv = kernel_mode | (user_mode << R_CSR_DMW_PLV3_SHIFT);
19827edd504SSong Gao     if (is_la64(env)) {
19927edd504SSong Gao         base_v = address >> R_CSR_DMW_64_VSEG_SHIFT;
20027edd504SSong Gao     } else {
20127edd504SSong Gao         base_v = address >> R_CSR_DMW_32_VSEG_SHIFT;
20227edd504SSong Gao     }
20327edd504SSong Gao     /* Check direct map window */
20427edd504SSong Gao     for (int i = 0; i < 4; i++) {
20527edd504SSong Gao         if (is_la64(env)) {
20627edd504SSong Gao             base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_64, VSEG);
20727edd504SSong Gao         } else {
20827edd504SSong Gao             base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_32, VSEG);
20927edd504SSong Gao         }
21027edd504SSong Gao         if ((plv & env->CSR_DMW[i]) && (base_c == base_v)) {
21127edd504SSong Gao             *physical = dmw_va2pa(env, address, env->CSR_DMW[i]);
21227edd504SSong Gao             *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
21327edd504SSong Gao             return TLBRET_MATCH;
21427edd504SSong Gao         }
21527edd504SSong Gao     }
21627edd504SSong Gao 
21727edd504SSong Gao     /* Check valid extension */
21827edd504SSong Gao     addr_high = sextract64(address, TARGET_VIRT_ADDR_SPACE_BITS, 16);
21927edd504SSong Gao     if (!(addr_high == 0 || addr_high == -1)) {
22027edd504SSong Gao         return TLBRET_BADADDR;
22127edd504SSong Gao     }
22227edd504SSong Gao 
22327edd504SSong Gao     /* Mapped address */
22427edd504SSong Gao     return loongarch_map_address(env, physical, prot, address,
22527edd504SSong Gao                                  access_type, mmu_idx);
22627edd504SSong Gao }
22727edd504SSong Gao 
loongarch_cpu_get_phys_page_debug(CPUState * cs,vaddr addr)22827edd504SSong Gao hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
22927edd504SSong Gao {
230f3b603b9SPhilippe Mathieu-Daudé     CPULoongArchState *env = cpu_env(cs);
23127edd504SSong Gao     hwaddr phys_addr;
23227edd504SSong Gao     int prot;
23327edd504SSong Gao 
23427edd504SSong Gao     if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD,
2353b916140SRichard Henderson                              cpu_mmu_index(cs, false)) != 0) {
23627edd504SSong Gao         return -1;
23727edd504SSong Gao     }
23827edd504SSong Gao     return phys_addr;
23927edd504SSong Gao }
240