18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright SUSE Linux Products GmbH 2009 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Authors: Alexander Graf <agraf@suse.de> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/types.h> 108c2ecf20Sopenharmony_ci#include <linux/string.h> 118c2ecf20Sopenharmony_ci#include <linux/kvm.h> 128c2ecf20Sopenharmony_ci#include <linux/kvm_host.h> 138c2ecf20Sopenharmony_ci#include <linux/highmem.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <asm/kvm_ppc.h> 168c2ecf20Sopenharmony_ci#include <asm/kvm_book3s.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* #define DEBUG_MMU */ 198c2ecf20Sopenharmony_ci/* #define DEBUG_MMU_PTE */ 208c2ecf20Sopenharmony_ci/* #define DEBUG_MMU_PTE_IP 0xfff14c40 */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#ifdef DEBUG_MMU 238c2ecf20Sopenharmony_ci#define dprintk(X...) printk(KERN_INFO X) 248c2ecf20Sopenharmony_ci#else 258c2ecf20Sopenharmony_ci#define dprintk(X...) do { } while(0) 268c2ecf20Sopenharmony_ci#endif 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#ifdef DEBUG_MMU_PTE 298c2ecf20Sopenharmony_ci#define dprintk_pte(X...) printk(KERN_INFO X) 308c2ecf20Sopenharmony_ci#else 318c2ecf20Sopenharmony_ci#define dprintk_pte(X...) do { } while(0) 328c2ecf20Sopenharmony_ci#endif 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define PTEG_FLAG_ACCESSED 0x00000100 358c2ecf20Sopenharmony_ci#define PTEG_FLAG_DIRTY 0x00000080 368c2ecf20Sopenharmony_ci#ifndef SID_SHIFT 378c2ecf20Sopenharmony_ci#define SID_SHIFT 28 388c2ecf20Sopenharmony_ci#endif 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic inline bool check_debug_ip(struct kvm_vcpu *vcpu) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci#ifdef DEBUG_MMU_PTE_IP 438c2ecf20Sopenharmony_ci return vcpu->arch.regs.nip == DEBUG_MMU_PTE_IP; 448c2ecf20Sopenharmony_ci#else 458c2ecf20Sopenharmony_ci return true; 468c2ecf20Sopenharmony_ci#endif 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic inline u32 sr_vsid(u32 sr_raw) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci return sr_raw & 0x0fffffff; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic inline bool sr_valid(u32 sr_raw) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci return (sr_raw & 0x80000000) ? false : true; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic inline bool sr_ks(u32 sr_raw) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci return (sr_raw & 0x40000000) ? true: false; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic inline bool sr_kp(u32 sr_raw) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci return (sr_raw & 0x20000000) ? true: false; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int kvmppc_mmu_book3s_32_xlate_bat(struct kvm_vcpu *vcpu, gva_t eaddr, 708c2ecf20Sopenharmony_ci struct kvmppc_pte *pte, bool data, 718c2ecf20Sopenharmony_ci bool iswrite); 728c2ecf20Sopenharmony_cistatic int kvmppc_mmu_book3s_32_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid, 738c2ecf20Sopenharmony_ci u64 *vsid); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic u32 find_sr(struct kvm_vcpu *vcpu, gva_t eaddr) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci return kvmppc_get_sr(vcpu, (eaddr >> 28) & 0xf); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic u64 kvmppc_mmu_book3s_32_ea_to_vp(struct kvm_vcpu *vcpu, gva_t eaddr, 818c2ecf20Sopenharmony_ci bool data) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci u64 vsid; 848c2ecf20Sopenharmony_ci struct kvmppc_pte pte; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (!kvmppc_mmu_book3s_32_xlate_bat(vcpu, eaddr, &pte, data, false)) 878c2ecf20Sopenharmony_ci return pte.vpage; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci kvmppc_mmu_book3s_32_esid_to_vsid(vcpu, eaddr >> SID_SHIFT, &vsid); 908c2ecf20Sopenharmony_ci return (((u64)eaddr >> 12) & 0xffff) | (vsid << 16); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic hva_t kvmppc_mmu_book3s_32_get_pteg(struct kvm_vcpu *vcpu, 948c2ecf20Sopenharmony_ci u32 sre, gva_t eaddr, 958c2ecf20Sopenharmony_ci bool primary) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu); 988c2ecf20Sopenharmony_ci u32 page, hash, pteg, htabmask; 998c2ecf20Sopenharmony_ci hva_t r; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci page = (eaddr & 0x0FFFFFFF) >> 12; 1028c2ecf20Sopenharmony_ci htabmask = ((vcpu_book3s->sdr1 & 0x1FF) << 16) | 0xFFC0; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci hash = ((sr_vsid(sre) ^ page) << 6); 1058c2ecf20Sopenharmony_ci if (!primary) 1068c2ecf20Sopenharmony_ci hash = ~hash; 1078c2ecf20Sopenharmony_ci hash &= htabmask; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci pteg = (vcpu_book3s->sdr1 & 0xffff0000) | hash; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci dprintk("MMU: pc=0x%lx eaddr=0x%lx sdr1=0x%llx pteg=0x%x vsid=0x%x\n", 1128c2ecf20Sopenharmony_ci kvmppc_get_pc(vcpu), eaddr, vcpu_book3s->sdr1, pteg, 1138c2ecf20Sopenharmony_ci sr_vsid(sre)); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci r = gfn_to_hva(vcpu->kvm, pteg >> PAGE_SHIFT); 1168c2ecf20Sopenharmony_ci if (kvm_is_error_hva(r)) 1178c2ecf20Sopenharmony_ci return r; 1188c2ecf20Sopenharmony_ci return r | (pteg & ~PAGE_MASK); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic u32 kvmppc_mmu_book3s_32_get_ptem(u32 sre, gva_t eaddr, bool primary) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci return ((eaddr & 0x0fffffff) >> 22) | (sr_vsid(sre) << 7) | 1248c2ecf20Sopenharmony_ci (primary ? 0 : 0x40) | 0x80000000; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int kvmppc_mmu_book3s_32_xlate_bat(struct kvm_vcpu *vcpu, gva_t eaddr, 1288c2ecf20Sopenharmony_ci struct kvmppc_pte *pte, bool data, 1298c2ecf20Sopenharmony_ci bool iswrite) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu); 1328c2ecf20Sopenharmony_ci struct kvmppc_bat *bat; 1338c2ecf20Sopenharmony_ci int i; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 1368c2ecf20Sopenharmony_ci if (data) 1378c2ecf20Sopenharmony_ci bat = &vcpu_book3s->dbat[i]; 1388c2ecf20Sopenharmony_ci else 1398c2ecf20Sopenharmony_ci bat = &vcpu_book3s->ibat[i]; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (kvmppc_get_msr(vcpu) & MSR_PR) { 1428c2ecf20Sopenharmony_ci if (!bat->vp) 1438c2ecf20Sopenharmony_ci continue; 1448c2ecf20Sopenharmony_ci } else { 1458c2ecf20Sopenharmony_ci if (!bat->vs) 1468c2ecf20Sopenharmony_ci continue; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (check_debug_ip(vcpu)) 1508c2ecf20Sopenharmony_ci { 1518c2ecf20Sopenharmony_ci dprintk_pte("%cBAT %02d: 0x%lx - 0x%x (0x%x)\n", 1528c2ecf20Sopenharmony_ci data ? 'd' : 'i', i, eaddr, bat->bepi, 1538c2ecf20Sopenharmony_ci bat->bepi_mask); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci if ((eaddr & bat->bepi_mask) == bat->bepi) { 1568c2ecf20Sopenharmony_ci u64 vsid; 1578c2ecf20Sopenharmony_ci kvmppc_mmu_book3s_32_esid_to_vsid(vcpu, 1588c2ecf20Sopenharmony_ci eaddr >> SID_SHIFT, &vsid); 1598c2ecf20Sopenharmony_ci vsid <<= 16; 1608c2ecf20Sopenharmony_ci pte->vpage = (((u64)eaddr >> 12) & 0xffff) | vsid; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci pte->raddr = bat->brpn | (eaddr & ~bat->bepi_mask); 1638c2ecf20Sopenharmony_ci pte->may_read = bat->pp; 1648c2ecf20Sopenharmony_ci pte->may_write = bat->pp > 1; 1658c2ecf20Sopenharmony_ci pte->may_execute = true; 1668c2ecf20Sopenharmony_ci if (!pte->may_read) { 1678c2ecf20Sopenharmony_ci printk(KERN_INFO "BAT is not readable!\n"); 1688c2ecf20Sopenharmony_ci continue; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci if (iswrite && !pte->may_write) { 1718c2ecf20Sopenharmony_ci dprintk_pte("BAT is read-only!\n"); 1728c2ecf20Sopenharmony_ci continue; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return -ENOENT; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int kvmppc_mmu_book3s_32_xlate_pte(struct kvm_vcpu *vcpu, gva_t eaddr, 1838c2ecf20Sopenharmony_ci struct kvmppc_pte *pte, bool data, 1848c2ecf20Sopenharmony_ci bool iswrite, bool primary) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci u32 sre; 1878c2ecf20Sopenharmony_ci hva_t ptegp; 1888c2ecf20Sopenharmony_ci u32 pteg[16]; 1898c2ecf20Sopenharmony_ci u32 pte0, pte1; 1908c2ecf20Sopenharmony_ci u32 ptem = 0; 1918c2ecf20Sopenharmony_ci int i; 1928c2ecf20Sopenharmony_ci int found = 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci sre = find_sr(vcpu, eaddr); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci dprintk_pte("SR 0x%lx: vsid=0x%x, raw=0x%x\n", eaddr >> 28, 1978c2ecf20Sopenharmony_ci sr_vsid(sre), sre); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci pte->vpage = kvmppc_mmu_book3s_32_ea_to_vp(vcpu, eaddr, data); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci ptegp = kvmppc_mmu_book3s_32_get_pteg(vcpu, sre, eaddr, primary); 2028c2ecf20Sopenharmony_ci if (kvm_is_error_hva(ptegp)) { 2038c2ecf20Sopenharmony_ci printk(KERN_INFO "KVM: Invalid PTEG!\n"); 2048c2ecf20Sopenharmony_ci goto no_page_found; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci ptem = kvmppc_mmu_book3s_32_get_ptem(sre, eaddr, primary); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if(copy_from_user(pteg, (void __user *)ptegp, sizeof(pteg))) { 2108c2ecf20Sopenharmony_ci printk_ratelimited(KERN_ERR 2118c2ecf20Sopenharmony_ci "KVM: Can't copy data from 0x%lx!\n", ptegp); 2128c2ecf20Sopenharmony_ci goto no_page_found; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci for (i=0; i<16; i+=2) { 2168c2ecf20Sopenharmony_ci pte0 = be32_to_cpu(pteg[i]); 2178c2ecf20Sopenharmony_ci pte1 = be32_to_cpu(pteg[i + 1]); 2188c2ecf20Sopenharmony_ci if (ptem == pte0) { 2198c2ecf20Sopenharmony_ci u8 pp; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci pte->raddr = (pte1 & ~(0xFFFULL)) | (eaddr & 0xFFF); 2228c2ecf20Sopenharmony_ci pp = pte1 & 3; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if ((sr_kp(sre) && (kvmppc_get_msr(vcpu) & MSR_PR)) || 2258c2ecf20Sopenharmony_ci (sr_ks(sre) && !(kvmppc_get_msr(vcpu) & MSR_PR))) 2268c2ecf20Sopenharmony_ci pp |= 4; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci pte->may_write = false; 2298c2ecf20Sopenharmony_ci pte->may_read = false; 2308c2ecf20Sopenharmony_ci pte->may_execute = true; 2318c2ecf20Sopenharmony_ci switch (pp) { 2328c2ecf20Sopenharmony_ci case 0: 2338c2ecf20Sopenharmony_ci case 1: 2348c2ecf20Sopenharmony_ci case 2: 2358c2ecf20Sopenharmony_ci case 6: 2368c2ecf20Sopenharmony_ci pte->may_write = true; 2378c2ecf20Sopenharmony_ci fallthrough; 2388c2ecf20Sopenharmony_ci case 3: 2398c2ecf20Sopenharmony_ci case 5: 2408c2ecf20Sopenharmony_ci case 7: 2418c2ecf20Sopenharmony_ci pte->may_read = true; 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci dprintk_pte("MMU: Found PTE -> %x %x - %x\n", 2468c2ecf20Sopenharmony_ci pte0, pte1, pp); 2478c2ecf20Sopenharmony_ci found = 1; 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* Update PTE C and A bits, so the guest's swapper knows we used the 2538c2ecf20Sopenharmony_ci page */ 2548c2ecf20Sopenharmony_ci if (found) { 2558c2ecf20Sopenharmony_ci u32 pte_r = pte1; 2568c2ecf20Sopenharmony_ci char __user *addr = (char __user *) (ptegp + (i+1) * sizeof(u32)); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* 2598c2ecf20Sopenharmony_ci * Use single-byte writes to update the HPTE, to 2608c2ecf20Sopenharmony_ci * conform to what real hardware does. 2618c2ecf20Sopenharmony_ci */ 2628c2ecf20Sopenharmony_ci if (pte->may_read && !(pte_r & PTEG_FLAG_ACCESSED)) { 2638c2ecf20Sopenharmony_ci pte_r |= PTEG_FLAG_ACCESSED; 2648c2ecf20Sopenharmony_ci put_user(pte_r >> 8, addr + 2); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci if (iswrite && pte->may_write && !(pte_r & PTEG_FLAG_DIRTY)) { 2678c2ecf20Sopenharmony_ci pte_r |= PTEG_FLAG_DIRTY; 2688c2ecf20Sopenharmony_ci put_user(pte_r, addr + 3); 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci if (!pte->may_read || (iswrite && !pte->may_write)) 2718c2ecf20Sopenharmony_ci return -EPERM; 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cino_page_found: 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (check_debug_ip(vcpu)) { 2788c2ecf20Sopenharmony_ci dprintk_pte("KVM MMU: No PTE found (sdr1=0x%llx ptegp=0x%lx)\n", 2798c2ecf20Sopenharmony_ci to_book3s(vcpu)->sdr1, ptegp); 2808c2ecf20Sopenharmony_ci for (i=0; i<16; i+=2) { 2818c2ecf20Sopenharmony_ci dprintk_pte(" %02d: 0x%x - 0x%x (0x%x)\n", 2828c2ecf20Sopenharmony_ci i, be32_to_cpu(pteg[i]), 2838c2ecf20Sopenharmony_ci be32_to_cpu(pteg[i+1]), ptem); 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return -ENOENT; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int kvmppc_mmu_book3s_32_xlate(struct kvm_vcpu *vcpu, gva_t eaddr, 2918c2ecf20Sopenharmony_ci struct kvmppc_pte *pte, bool data, 2928c2ecf20Sopenharmony_ci bool iswrite) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci int r; 2958c2ecf20Sopenharmony_ci ulong mp_ea = vcpu->arch.magic_page_ea; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci pte->eaddr = eaddr; 2988c2ecf20Sopenharmony_ci pte->page_size = MMU_PAGE_4K; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* Magic page override */ 3018c2ecf20Sopenharmony_ci if (unlikely(mp_ea) && 3028c2ecf20Sopenharmony_ci unlikely((eaddr & ~0xfffULL) == (mp_ea & ~0xfffULL)) && 3038c2ecf20Sopenharmony_ci !(kvmppc_get_msr(vcpu) & MSR_PR)) { 3048c2ecf20Sopenharmony_ci pte->vpage = kvmppc_mmu_book3s_32_ea_to_vp(vcpu, eaddr, data); 3058c2ecf20Sopenharmony_ci pte->raddr = vcpu->arch.magic_page_pa | (pte->raddr & 0xfff); 3068c2ecf20Sopenharmony_ci pte->raddr &= KVM_PAM; 3078c2ecf20Sopenharmony_ci pte->may_execute = true; 3088c2ecf20Sopenharmony_ci pte->may_read = true; 3098c2ecf20Sopenharmony_ci pte->may_write = true; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return 0; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci r = kvmppc_mmu_book3s_32_xlate_bat(vcpu, eaddr, pte, data, iswrite); 3158c2ecf20Sopenharmony_ci if (r < 0) 3168c2ecf20Sopenharmony_ci r = kvmppc_mmu_book3s_32_xlate_pte(vcpu, eaddr, pte, 3178c2ecf20Sopenharmony_ci data, iswrite, true); 3188c2ecf20Sopenharmony_ci if (r == -ENOENT) 3198c2ecf20Sopenharmony_ci r = kvmppc_mmu_book3s_32_xlate_pte(vcpu, eaddr, pte, 3208c2ecf20Sopenharmony_ci data, iswrite, false); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return r; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic u32 kvmppc_mmu_book3s_32_mfsrin(struct kvm_vcpu *vcpu, u32 srnum) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci return kvmppc_get_sr(vcpu, srnum); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic void kvmppc_mmu_book3s_32_mtsrin(struct kvm_vcpu *vcpu, u32 srnum, 3328c2ecf20Sopenharmony_ci ulong value) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci kvmppc_set_sr(vcpu, srnum, value); 3358c2ecf20Sopenharmony_ci kvmppc_mmu_map_segment(vcpu, srnum << SID_SHIFT); 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void kvmppc_mmu_book3s_32_tlbie(struct kvm_vcpu *vcpu, ulong ea, bool large) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci int i; 3418c2ecf20Sopenharmony_ci struct kvm_vcpu *v; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* flush this VA on all cpus */ 3448c2ecf20Sopenharmony_ci kvm_for_each_vcpu(i, v, vcpu->kvm) 3458c2ecf20Sopenharmony_ci kvmppc_mmu_pte_flush(v, ea, 0x0FFFF000); 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int kvmppc_mmu_book3s_32_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid, 3498c2ecf20Sopenharmony_ci u64 *vsid) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci ulong ea = esid << SID_SHIFT; 3528c2ecf20Sopenharmony_ci u32 sr; 3538c2ecf20Sopenharmony_ci u64 gvsid = esid; 3548c2ecf20Sopenharmony_ci u64 msr = kvmppc_get_msr(vcpu); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (msr & (MSR_DR|MSR_IR)) { 3578c2ecf20Sopenharmony_ci sr = find_sr(vcpu, ea); 3588c2ecf20Sopenharmony_ci if (sr_valid(sr)) 3598c2ecf20Sopenharmony_ci gvsid = sr_vsid(sr); 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* In case we only have one of MSR_IR or MSR_DR set, let's put 3638c2ecf20Sopenharmony_ci that in the real-mode context (and hope RM doesn't access 3648c2ecf20Sopenharmony_ci high memory) */ 3658c2ecf20Sopenharmony_ci switch (msr & (MSR_DR|MSR_IR)) { 3668c2ecf20Sopenharmony_ci case 0: 3678c2ecf20Sopenharmony_ci *vsid = VSID_REAL | esid; 3688c2ecf20Sopenharmony_ci break; 3698c2ecf20Sopenharmony_ci case MSR_IR: 3708c2ecf20Sopenharmony_ci *vsid = VSID_REAL_IR | gvsid; 3718c2ecf20Sopenharmony_ci break; 3728c2ecf20Sopenharmony_ci case MSR_DR: 3738c2ecf20Sopenharmony_ci *vsid = VSID_REAL_DR | gvsid; 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci case MSR_DR|MSR_IR: 3768c2ecf20Sopenharmony_ci if (sr_valid(sr)) 3778c2ecf20Sopenharmony_ci *vsid = sr_vsid(sr); 3788c2ecf20Sopenharmony_ci else 3798c2ecf20Sopenharmony_ci *vsid = VSID_BAT | gvsid; 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci default: 3828c2ecf20Sopenharmony_ci BUG(); 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (msr & MSR_PR) 3868c2ecf20Sopenharmony_ci *vsid |= VSID_PR; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci return 0; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic bool kvmppc_mmu_book3s_32_is_dcbz32(struct kvm_vcpu *vcpu) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci return true; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_civoid kvmppc_mmu_book3s_32_init(struct kvm_vcpu *vcpu) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct kvmppc_mmu *mmu = &vcpu->arch.mmu; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci mmu->mtsrin = kvmppc_mmu_book3s_32_mtsrin; 4028c2ecf20Sopenharmony_ci mmu->mfsrin = kvmppc_mmu_book3s_32_mfsrin; 4038c2ecf20Sopenharmony_ci mmu->xlate = kvmppc_mmu_book3s_32_xlate; 4048c2ecf20Sopenharmony_ci mmu->tlbie = kvmppc_mmu_book3s_32_tlbie; 4058c2ecf20Sopenharmony_ci mmu->esid_to_vsid = kvmppc_mmu_book3s_32_esid_to_vsid; 4068c2ecf20Sopenharmony_ci mmu->ea_to_vp = kvmppc_mmu_book3s_32_ea_to_vp; 4078c2ecf20Sopenharmony_ci mmu->is_dcbz32 = kvmppc_mmu_book3s_32_is_dcbz32; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci mmu->slbmte = NULL; 4108c2ecf20Sopenharmony_ci mmu->slbmfee = NULL; 4118c2ecf20Sopenharmony_ci mmu->slbmfev = NULL; 4128c2ecf20Sopenharmony_ci mmu->slbfee = NULL; 4138c2ecf20Sopenharmony_ci mmu->slbie = NULL; 4148c2ecf20Sopenharmony_ci mmu->slbia = NULL; 4158c2ecf20Sopenharmony_ci} 416