162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2019 Western Digital Corporation or its affiliates. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Anup Patel <anup.patel@wdc.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/bitops.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/hugetlb.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/uaccess.h> 1562306a36Sopenharmony_ci#include <linux/vmalloc.h> 1662306a36Sopenharmony_ci#include <linux/kvm_host.h> 1762306a36Sopenharmony_ci#include <linux/sched/signal.h> 1862306a36Sopenharmony_ci#include <asm/csr.h> 1962306a36Sopenharmony_ci#include <asm/page.h> 2062306a36Sopenharmony_ci#include <asm/pgtable.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#ifdef CONFIG_64BIT 2362306a36Sopenharmony_cistatic unsigned long gstage_mode __ro_after_init = (HGATP_MODE_SV39X4 << HGATP_MODE_SHIFT); 2462306a36Sopenharmony_cistatic unsigned long gstage_pgd_levels __ro_after_init = 3; 2562306a36Sopenharmony_ci#define gstage_index_bits 9 2662306a36Sopenharmony_ci#else 2762306a36Sopenharmony_cistatic unsigned long gstage_mode __ro_after_init = (HGATP_MODE_SV32X4 << HGATP_MODE_SHIFT); 2862306a36Sopenharmony_cistatic unsigned long gstage_pgd_levels __ro_after_init = 2; 2962306a36Sopenharmony_ci#define gstage_index_bits 10 3062306a36Sopenharmony_ci#endif 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define gstage_pgd_xbits 2 3362306a36Sopenharmony_ci#define gstage_pgd_size (1UL << (HGATP_PAGE_SHIFT + gstage_pgd_xbits)) 3462306a36Sopenharmony_ci#define gstage_gpa_bits (HGATP_PAGE_SHIFT + \ 3562306a36Sopenharmony_ci (gstage_pgd_levels * gstage_index_bits) + \ 3662306a36Sopenharmony_ci gstage_pgd_xbits) 3762306a36Sopenharmony_ci#define gstage_gpa_size ((gpa_t)(1ULL << gstage_gpa_bits)) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define gstage_pte_leaf(__ptep) \ 4062306a36Sopenharmony_ci (pte_val(*(__ptep)) & (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC)) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic inline unsigned long gstage_pte_index(gpa_t addr, u32 level) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci unsigned long mask; 4562306a36Sopenharmony_ci unsigned long shift = HGATP_PAGE_SHIFT + (gstage_index_bits * level); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (level == (gstage_pgd_levels - 1)) 4862306a36Sopenharmony_ci mask = (PTRS_PER_PTE * (1UL << gstage_pgd_xbits)) - 1; 4962306a36Sopenharmony_ci else 5062306a36Sopenharmony_ci mask = PTRS_PER_PTE - 1; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return (addr >> shift) & mask; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic inline unsigned long gstage_pte_page_vaddr(pte_t pte) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci return (unsigned long)pfn_to_virt(__page_val_to_pfn(pte_val(pte))); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic int gstage_page_size_to_level(unsigned long page_size, u32 *out_level) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci u32 i; 6362306a36Sopenharmony_ci unsigned long psz = 1UL << 12; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci for (i = 0; i < gstage_pgd_levels; i++) { 6662306a36Sopenharmony_ci if (page_size == (psz << (i * gstage_index_bits))) { 6762306a36Sopenharmony_ci *out_level = i; 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return -EINVAL; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int gstage_level_to_page_order(u32 level, unsigned long *out_pgorder) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci if (gstage_pgd_levels < level) 7862306a36Sopenharmony_ci return -EINVAL; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci *out_pgorder = 12 + (level * gstage_index_bits); 8162306a36Sopenharmony_ci return 0; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int gstage_level_to_page_size(u32 level, unsigned long *out_pgsize) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci int rc; 8762306a36Sopenharmony_ci unsigned long page_order = PAGE_SHIFT; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci rc = gstage_level_to_page_order(level, &page_order); 9062306a36Sopenharmony_ci if (rc) 9162306a36Sopenharmony_ci return rc; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci *out_pgsize = BIT(page_order); 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic bool gstage_get_leaf_entry(struct kvm *kvm, gpa_t addr, 9862306a36Sopenharmony_ci pte_t **ptepp, u32 *ptep_level) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci pte_t *ptep; 10162306a36Sopenharmony_ci u32 current_level = gstage_pgd_levels - 1; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci *ptep_level = current_level; 10462306a36Sopenharmony_ci ptep = (pte_t *)kvm->arch.pgd; 10562306a36Sopenharmony_ci ptep = &ptep[gstage_pte_index(addr, current_level)]; 10662306a36Sopenharmony_ci while (ptep && pte_val(*ptep)) { 10762306a36Sopenharmony_ci if (gstage_pte_leaf(ptep)) { 10862306a36Sopenharmony_ci *ptep_level = current_level; 10962306a36Sopenharmony_ci *ptepp = ptep; 11062306a36Sopenharmony_ci return true; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (current_level) { 11462306a36Sopenharmony_ci current_level--; 11562306a36Sopenharmony_ci *ptep_level = current_level; 11662306a36Sopenharmony_ci ptep = (pte_t *)gstage_pte_page_vaddr(*ptep); 11762306a36Sopenharmony_ci ptep = &ptep[gstage_pte_index(addr, current_level)]; 11862306a36Sopenharmony_ci } else { 11962306a36Sopenharmony_ci ptep = NULL; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return false; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic void gstage_remote_tlb_flush(struct kvm *kvm, u32 level, gpa_t addr) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci unsigned long order = PAGE_SHIFT; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (gstage_level_to_page_order(level, &order)) 13162306a36Sopenharmony_ci return; 13262306a36Sopenharmony_ci addr &= ~(BIT(order) - 1); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci kvm_riscv_hfence_gvma_vmid_gpa(kvm, -1UL, 0, addr, BIT(order), order); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int gstage_set_pte(struct kvm *kvm, u32 level, 13862306a36Sopenharmony_ci struct kvm_mmu_memory_cache *pcache, 13962306a36Sopenharmony_ci gpa_t addr, const pte_t *new_pte) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci u32 current_level = gstage_pgd_levels - 1; 14262306a36Sopenharmony_ci pte_t *next_ptep = (pte_t *)kvm->arch.pgd; 14362306a36Sopenharmony_ci pte_t *ptep = &next_ptep[gstage_pte_index(addr, current_level)]; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (current_level < level) 14662306a36Sopenharmony_ci return -EINVAL; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci while (current_level != level) { 14962306a36Sopenharmony_ci if (gstage_pte_leaf(ptep)) 15062306a36Sopenharmony_ci return -EEXIST; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (!pte_val(*ptep)) { 15362306a36Sopenharmony_ci if (!pcache) 15462306a36Sopenharmony_ci return -ENOMEM; 15562306a36Sopenharmony_ci next_ptep = kvm_mmu_memory_cache_alloc(pcache); 15662306a36Sopenharmony_ci if (!next_ptep) 15762306a36Sopenharmony_ci return -ENOMEM; 15862306a36Sopenharmony_ci *ptep = pfn_pte(PFN_DOWN(__pa(next_ptep)), 15962306a36Sopenharmony_ci __pgprot(_PAGE_TABLE)); 16062306a36Sopenharmony_ci } else { 16162306a36Sopenharmony_ci if (gstage_pte_leaf(ptep)) 16262306a36Sopenharmony_ci return -EEXIST; 16362306a36Sopenharmony_ci next_ptep = (pte_t *)gstage_pte_page_vaddr(*ptep); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci current_level--; 16762306a36Sopenharmony_ci ptep = &next_ptep[gstage_pte_index(addr, current_level)]; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci *ptep = *new_pte; 17162306a36Sopenharmony_ci if (gstage_pte_leaf(ptep)) 17262306a36Sopenharmony_ci gstage_remote_tlb_flush(kvm, current_level, addr); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return 0; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic int gstage_map_page(struct kvm *kvm, 17862306a36Sopenharmony_ci struct kvm_mmu_memory_cache *pcache, 17962306a36Sopenharmony_ci gpa_t gpa, phys_addr_t hpa, 18062306a36Sopenharmony_ci unsigned long page_size, 18162306a36Sopenharmony_ci bool page_rdonly, bool page_exec) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci int ret; 18462306a36Sopenharmony_ci u32 level = 0; 18562306a36Sopenharmony_ci pte_t new_pte; 18662306a36Sopenharmony_ci pgprot_t prot; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci ret = gstage_page_size_to_level(page_size, &level); 18962306a36Sopenharmony_ci if (ret) 19062306a36Sopenharmony_ci return ret; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* 19362306a36Sopenharmony_ci * A RISC-V implementation can choose to either: 19462306a36Sopenharmony_ci * 1) Update 'A' and 'D' PTE bits in hardware 19562306a36Sopenharmony_ci * 2) Generate page fault when 'A' and/or 'D' bits are not set 19662306a36Sopenharmony_ci * PTE so that software can update these bits. 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * We support both options mentioned above. To achieve this, we 19962306a36Sopenharmony_ci * always set 'A' and 'D' PTE bits at time of creating G-stage 20062306a36Sopenharmony_ci * mapping. To support KVM dirty page logging with both options 20162306a36Sopenharmony_ci * mentioned above, we will write-protect G-stage PTEs to track 20262306a36Sopenharmony_ci * dirty pages. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (page_exec) { 20662306a36Sopenharmony_ci if (page_rdonly) 20762306a36Sopenharmony_ci prot = PAGE_READ_EXEC; 20862306a36Sopenharmony_ci else 20962306a36Sopenharmony_ci prot = PAGE_WRITE_EXEC; 21062306a36Sopenharmony_ci } else { 21162306a36Sopenharmony_ci if (page_rdonly) 21262306a36Sopenharmony_ci prot = PAGE_READ; 21362306a36Sopenharmony_ci else 21462306a36Sopenharmony_ci prot = PAGE_WRITE; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci new_pte = pfn_pte(PFN_DOWN(hpa), prot); 21762306a36Sopenharmony_ci new_pte = pte_mkdirty(new_pte); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return gstage_set_pte(kvm, level, pcache, gpa, &new_pte); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cienum gstage_op { 22362306a36Sopenharmony_ci GSTAGE_OP_NOP = 0, /* Nothing */ 22462306a36Sopenharmony_ci GSTAGE_OP_CLEAR, /* Clear/Unmap */ 22562306a36Sopenharmony_ci GSTAGE_OP_WP, /* Write-protect */ 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic void gstage_op_pte(struct kvm *kvm, gpa_t addr, 22962306a36Sopenharmony_ci pte_t *ptep, u32 ptep_level, enum gstage_op op) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci int i, ret; 23262306a36Sopenharmony_ci pte_t *next_ptep; 23362306a36Sopenharmony_ci u32 next_ptep_level; 23462306a36Sopenharmony_ci unsigned long next_page_size, page_size; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ret = gstage_level_to_page_size(ptep_level, &page_size); 23762306a36Sopenharmony_ci if (ret) 23862306a36Sopenharmony_ci return; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci BUG_ON(addr & (page_size - 1)); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (!pte_val(*ptep)) 24362306a36Sopenharmony_ci return; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (ptep_level && !gstage_pte_leaf(ptep)) { 24662306a36Sopenharmony_ci next_ptep = (pte_t *)gstage_pte_page_vaddr(*ptep); 24762306a36Sopenharmony_ci next_ptep_level = ptep_level - 1; 24862306a36Sopenharmony_ci ret = gstage_level_to_page_size(next_ptep_level, 24962306a36Sopenharmony_ci &next_page_size); 25062306a36Sopenharmony_ci if (ret) 25162306a36Sopenharmony_ci return; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (op == GSTAGE_OP_CLEAR) 25462306a36Sopenharmony_ci set_pte(ptep, __pte(0)); 25562306a36Sopenharmony_ci for (i = 0; i < PTRS_PER_PTE; i++) 25662306a36Sopenharmony_ci gstage_op_pte(kvm, addr + i * next_page_size, 25762306a36Sopenharmony_ci &next_ptep[i], next_ptep_level, op); 25862306a36Sopenharmony_ci if (op == GSTAGE_OP_CLEAR) 25962306a36Sopenharmony_ci put_page(virt_to_page(next_ptep)); 26062306a36Sopenharmony_ci } else { 26162306a36Sopenharmony_ci if (op == GSTAGE_OP_CLEAR) 26262306a36Sopenharmony_ci set_pte(ptep, __pte(0)); 26362306a36Sopenharmony_ci else if (op == GSTAGE_OP_WP) 26462306a36Sopenharmony_ci set_pte(ptep, __pte(pte_val(*ptep) & ~_PAGE_WRITE)); 26562306a36Sopenharmony_ci gstage_remote_tlb_flush(kvm, ptep_level, addr); 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic void gstage_unmap_range(struct kvm *kvm, gpa_t start, 27062306a36Sopenharmony_ci gpa_t size, bool may_block) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci int ret; 27362306a36Sopenharmony_ci pte_t *ptep; 27462306a36Sopenharmony_ci u32 ptep_level; 27562306a36Sopenharmony_ci bool found_leaf; 27662306a36Sopenharmony_ci unsigned long page_size; 27762306a36Sopenharmony_ci gpa_t addr = start, end = start + size; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci while (addr < end) { 28062306a36Sopenharmony_ci found_leaf = gstage_get_leaf_entry(kvm, addr, 28162306a36Sopenharmony_ci &ptep, &ptep_level); 28262306a36Sopenharmony_ci ret = gstage_level_to_page_size(ptep_level, &page_size); 28362306a36Sopenharmony_ci if (ret) 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (!found_leaf) 28762306a36Sopenharmony_ci goto next; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (!(addr & (page_size - 1)) && ((end - addr) >= page_size)) 29062306a36Sopenharmony_ci gstage_op_pte(kvm, addr, ptep, 29162306a36Sopenharmony_ci ptep_level, GSTAGE_OP_CLEAR); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cinext: 29462306a36Sopenharmony_ci addr += page_size; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* 29762306a36Sopenharmony_ci * If the range is too large, release the kvm->mmu_lock 29862306a36Sopenharmony_ci * to prevent starvation and lockup detector warnings. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_ci if (may_block && addr < end) 30162306a36Sopenharmony_ci cond_resched_lock(&kvm->mmu_lock); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic void gstage_wp_range(struct kvm *kvm, gpa_t start, gpa_t end) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci int ret; 30862306a36Sopenharmony_ci pte_t *ptep; 30962306a36Sopenharmony_ci u32 ptep_level; 31062306a36Sopenharmony_ci bool found_leaf; 31162306a36Sopenharmony_ci gpa_t addr = start; 31262306a36Sopenharmony_ci unsigned long page_size; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci while (addr < end) { 31562306a36Sopenharmony_ci found_leaf = gstage_get_leaf_entry(kvm, addr, 31662306a36Sopenharmony_ci &ptep, &ptep_level); 31762306a36Sopenharmony_ci ret = gstage_level_to_page_size(ptep_level, &page_size); 31862306a36Sopenharmony_ci if (ret) 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (!found_leaf) 32262306a36Sopenharmony_ci goto next; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (!(addr & (page_size - 1)) && ((end - addr) >= page_size)) 32562306a36Sopenharmony_ci gstage_op_pte(kvm, addr, ptep, 32662306a36Sopenharmony_ci ptep_level, GSTAGE_OP_WP); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cinext: 32962306a36Sopenharmony_ci addr += page_size; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic void gstage_wp_memory_region(struct kvm *kvm, int slot) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct kvm_memslots *slots = kvm_memslots(kvm); 33662306a36Sopenharmony_ci struct kvm_memory_slot *memslot = id_to_memslot(slots, slot); 33762306a36Sopenharmony_ci phys_addr_t start = memslot->base_gfn << PAGE_SHIFT; 33862306a36Sopenharmony_ci phys_addr_t end = (memslot->base_gfn + memslot->npages) << PAGE_SHIFT; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci spin_lock(&kvm->mmu_lock); 34162306a36Sopenharmony_ci gstage_wp_range(kvm, start, end); 34262306a36Sopenharmony_ci spin_unlock(&kvm->mmu_lock); 34362306a36Sopenharmony_ci kvm_flush_remote_tlbs(kvm); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ciint kvm_riscv_gstage_ioremap(struct kvm *kvm, gpa_t gpa, 34762306a36Sopenharmony_ci phys_addr_t hpa, unsigned long size, 34862306a36Sopenharmony_ci bool writable, bool in_atomic) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci pte_t pte; 35162306a36Sopenharmony_ci int ret = 0; 35262306a36Sopenharmony_ci unsigned long pfn; 35362306a36Sopenharmony_ci phys_addr_t addr, end; 35462306a36Sopenharmony_ci struct kvm_mmu_memory_cache pcache = { 35562306a36Sopenharmony_ci .gfp_custom = (in_atomic) ? GFP_ATOMIC | __GFP_ACCOUNT : 0, 35662306a36Sopenharmony_ci .gfp_zero = __GFP_ZERO, 35762306a36Sopenharmony_ci }; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci end = (gpa + size + PAGE_SIZE - 1) & PAGE_MASK; 36062306a36Sopenharmony_ci pfn = __phys_to_pfn(hpa); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci for (addr = gpa; addr < end; addr += PAGE_SIZE) { 36362306a36Sopenharmony_ci pte = pfn_pte(pfn, PAGE_KERNEL_IO); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (!writable) 36662306a36Sopenharmony_ci pte = pte_wrprotect(pte); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci ret = kvm_mmu_topup_memory_cache(&pcache, gstage_pgd_levels); 36962306a36Sopenharmony_ci if (ret) 37062306a36Sopenharmony_ci goto out; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci spin_lock(&kvm->mmu_lock); 37362306a36Sopenharmony_ci ret = gstage_set_pte(kvm, 0, &pcache, addr, &pte); 37462306a36Sopenharmony_ci spin_unlock(&kvm->mmu_lock); 37562306a36Sopenharmony_ci if (ret) 37662306a36Sopenharmony_ci goto out; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci pfn++; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ciout: 38262306a36Sopenharmony_ci kvm_mmu_free_memory_cache(&pcache); 38362306a36Sopenharmony_ci return ret; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_civoid kvm_riscv_gstage_iounmap(struct kvm *kvm, gpa_t gpa, unsigned long size) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci spin_lock(&kvm->mmu_lock); 38962306a36Sopenharmony_ci gstage_unmap_range(kvm, gpa, size, false); 39062306a36Sopenharmony_ci spin_unlock(&kvm->mmu_lock); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_civoid kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm, 39462306a36Sopenharmony_ci struct kvm_memory_slot *slot, 39562306a36Sopenharmony_ci gfn_t gfn_offset, 39662306a36Sopenharmony_ci unsigned long mask) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci phys_addr_t base_gfn = slot->base_gfn + gfn_offset; 39962306a36Sopenharmony_ci phys_addr_t start = (base_gfn + __ffs(mask)) << PAGE_SHIFT; 40062306a36Sopenharmony_ci phys_addr_t end = (base_gfn + __fls(mask) + 1) << PAGE_SHIFT; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci gstage_wp_range(kvm, start, end); 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_civoid kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_civoid kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_civoid kvm_arch_memslots_updated(struct kvm *kvm, u64 gen) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_civoid kvm_arch_flush_shadow_all(struct kvm *kvm) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci kvm_riscv_gstage_free_pgd(kvm); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_civoid kvm_arch_flush_shadow_memslot(struct kvm *kvm, 42362306a36Sopenharmony_ci struct kvm_memory_slot *slot) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci gpa_t gpa = slot->base_gfn << PAGE_SHIFT; 42662306a36Sopenharmony_ci phys_addr_t size = slot->npages << PAGE_SHIFT; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci spin_lock(&kvm->mmu_lock); 42962306a36Sopenharmony_ci gstage_unmap_range(kvm, gpa, size, false); 43062306a36Sopenharmony_ci spin_unlock(&kvm->mmu_lock); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_civoid kvm_arch_commit_memory_region(struct kvm *kvm, 43462306a36Sopenharmony_ci struct kvm_memory_slot *old, 43562306a36Sopenharmony_ci const struct kvm_memory_slot *new, 43662306a36Sopenharmony_ci enum kvm_mr_change change) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci /* 43962306a36Sopenharmony_ci * At this point memslot has been committed and there is an 44062306a36Sopenharmony_ci * allocated dirty_bitmap[], dirty pages will be tracked while 44162306a36Sopenharmony_ci * the memory slot is write protected. 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_ci if (change != KVM_MR_DELETE && new->flags & KVM_MEM_LOG_DIRTY_PAGES) 44462306a36Sopenharmony_ci gstage_wp_memory_region(kvm, new->id); 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ciint kvm_arch_prepare_memory_region(struct kvm *kvm, 44862306a36Sopenharmony_ci const struct kvm_memory_slot *old, 44962306a36Sopenharmony_ci struct kvm_memory_slot *new, 45062306a36Sopenharmony_ci enum kvm_mr_change change) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci hva_t hva, reg_end, size; 45362306a36Sopenharmony_ci gpa_t base_gpa; 45462306a36Sopenharmony_ci bool writable; 45562306a36Sopenharmony_ci int ret = 0; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (change != KVM_MR_CREATE && change != KVM_MR_MOVE && 45862306a36Sopenharmony_ci change != KVM_MR_FLAGS_ONLY) 45962306a36Sopenharmony_ci return 0; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* 46262306a36Sopenharmony_ci * Prevent userspace from creating a memory region outside of the GPA 46362306a36Sopenharmony_ci * space addressable by the KVM guest GPA space. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci if ((new->base_gfn + new->npages) >= 46662306a36Sopenharmony_ci (gstage_gpa_size >> PAGE_SHIFT)) 46762306a36Sopenharmony_ci return -EFAULT; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci hva = new->userspace_addr; 47062306a36Sopenharmony_ci size = new->npages << PAGE_SHIFT; 47162306a36Sopenharmony_ci reg_end = hva + size; 47262306a36Sopenharmony_ci base_gpa = new->base_gfn << PAGE_SHIFT; 47362306a36Sopenharmony_ci writable = !(new->flags & KVM_MEM_READONLY); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci mmap_read_lock(current->mm); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* 47862306a36Sopenharmony_ci * A memory region could potentially cover multiple VMAs, and 47962306a36Sopenharmony_ci * any holes between them, so iterate over all of them to find 48062306a36Sopenharmony_ci * out if we can map any of them right now. 48162306a36Sopenharmony_ci * 48262306a36Sopenharmony_ci * +--------------------------------------------+ 48362306a36Sopenharmony_ci * +---------------+----------------+ +----------------+ 48462306a36Sopenharmony_ci * | : VMA 1 | VMA 2 | | VMA 3 : | 48562306a36Sopenharmony_ci * +---------------+----------------+ +----------------+ 48662306a36Sopenharmony_ci * | memory region | 48762306a36Sopenharmony_ci * +--------------------------------------------+ 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ci do { 49062306a36Sopenharmony_ci struct vm_area_struct *vma = find_vma(current->mm, hva); 49162306a36Sopenharmony_ci hva_t vm_start, vm_end; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (!vma || vma->vm_start >= reg_end) 49462306a36Sopenharmony_ci break; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* 49762306a36Sopenharmony_ci * Mapping a read-only VMA is only allowed if the 49862306a36Sopenharmony_ci * memory region is configured as read-only. 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_ci if (writable && !(vma->vm_flags & VM_WRITE)) { 50162306a36Sopenharmony_ci ret = -EPERM; 50262306a36Sopenharmony_ci break; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* Take the intersection of this VMA with the memory region */ 50662306a36Sopenharmony_ci vm_start = max(hva, vma->vm_start); 50762306a36Sopenharmony_ci vm_end = min(reg_end, vma->vm_end); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (vma->vm_flags & VM_PFNMAP) { 51062306a36Sopenharmony_ci gpa_t gpa = base_gpa + (vm_start - hva); 51162306a36Sopenharmony_ci phys_addr_t pa; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci pa = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; 51462306a36Sopenharmony_ci pa += vm_start - vma->vm_start; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* IO region dirty page logging not allowed */ 51762306a36Sopenharmony_ci if (new->flags & KVM_MEM_LOG_DIRTY_PAGES) { 51862306a36Sopenharmony_ci ret = -EINVAL; 51962306a36Sopenharmony_ci goto out; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci ret = kvm_riscv_gstage_ioremap(kvm, gpa, pa, 52362306a36Sopenharmony_ci vm_end - vm_start, 52462306a36Sopenharmony_ci writable, false); 52562306a36Sopenharmony_ci if (ret) 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci hva = vm_end; 52962306a36Sopenharmony_ci } while (hva < reg_end); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (change == KVM_MR_FLAGS_ONLY) 53262306a36Sopenharmony_ci goto out; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (ret) 53562306a36Sopenharmony_ci kvm_riscv_gstage_iounmap(kvm, base_gpa, size); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ciout: 53862306a36Sopenharmony_ci mmap_read_unlock(current->mm); 53962306a36Sopenharmony_ci return ret; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cibool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci if (!kvm->arch.pgd) 54562306a36Sopenharmony_ci return false; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci gstage_unmap_range(kvm, range->start << PAGE_SHIFT, 54862306a36Sopenharmony_ci (range->end - range->start) << PAGE_SHIFT, 54962306a36Sopenharmony_ci range->may_block); 55062306a36Sopenharmony_ci return false; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cibool kvm_set_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci int ret; 55662306a36Sopenharmony_ci kvm_pfn_t pfn = pte_pfn(range->arg.pte); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (!kvm->arch.pgd) 55962306a36Sopenharmony_ci return false; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci WARN_ON(range->end - range->start != 1); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ret = gstage_map_page(kvm, NULL, range->start << PAGE_SHIFT, 56462306a36Sopenharmony_ci __pfn_to_phys(pfn), PAGE_SIZE, true, true); 56562306a36Sopenharmony_ci if (ret) { 56662306a36Sopenharmony_ci kvm_debug("Failed to map G-stage page (error %d)\n", ret); 56762306a36Sopenharmony_ci return true; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return false; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cibool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci pte_t *ptep; 57662306a36Sopenharmony_ci u32 ptep_level = 0; 57762306a36Sopenharmony_ci u64 size = (range->end - range->start) << PAGE_SHIFT; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (!kvm->arch.pgd) 58062306a36Sopenharmony_ci return false; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci WARN_ON(size != PAGE_SIZE && size != PMD_SIZE && size != PUD_SIZE); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (!gstage_get_leaf_entry(kvm, range->start << PAGE_SHIFT, 58562306a36Sopenharmony_ci &ptep, &ptep_level)) 58662306a36Sopenharmony_ci return false; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return ptep_test_and_clear_young(NULL, 0, ptep); 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cibool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci pte_t *ptep; 59462306a36Sopenharmony_ci u32 ptep_level = 0; 59562306a36Sopenharmony_ci u64 size = (range->end - range->start) << PAGE_SHIFT; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (!kvm->arch.pgd) 59862306a36Sopenharmony_ci return false; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci WARN_ON(size != PAGE_SIZE && size != PMD_SIZE && size != PUD_SIZE); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (!gstage_get_leaf_entry(kvm, range->start << PAGE_SHIFT, 60362306a36Sopenharmony_ci &ptep, &ptep_level)) 60462306a36Sopenharmony_ci return false; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return pte_young(*ptep); 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ciint kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, 61062306a36Sopenharmony_ci struct kvm_memory_slot *memslot, 61162306a36Sopenharmony_ci gpa_t gpa, unsigned long hva, bool is_write) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci int ret; 61462306a36Sopenharmony_ci kvm_pfn_t hfn; 61562306a36Sopenharmony_ci bool writable; 61662306a36Sopenharmony_ci short vma_pageshift; 61762306a36Sopenharmony_ci gfn_t gfn = gpa >> PAGE_SHIFT; 61862306a36Sopenharmony_ci struct vm_area_struct *vma; 61962306a36Sopenharmony_ci struct kvm *kvm = vcpu->kvm; 62062306a36Sopenharmony_ci struct kvm_mmu_memory_cache *pcache = &vcpu->arch.mmu_page_cache; 62162306a36Sopenharmony_ci bool logging = (memslot->dirty_bitmap && 62262306a36Sopenharmony_ci !(memslot->flags & KVM_MEM_READONLY)) ? true : false; 62362306a36Sopenharmony_ci unsigned long vma_pagesize, mmu_seq; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* We need minimum second+third level pages */ 62662306a36Sopenharmony_ci ret = kvm_mmu_topup_memory_cache(pcache, gstage_pgd_levels); 62762306a36Sopenharmony_ci if (ret) { 62862306a36Sopenharmony_ci kvm_err("Failed to topup G-stage cache\n"); 62962306a36Sopenharmony_ci return ret; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci mmap_read_lock(current->mm); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci vma = vma_lookup(current->mm, hva); 63562306a36Sopenharmony_ci if (unlikely(!vma)) { 63662306a36Sopenharmony_ci kvm_err("Failed to find VMA for hva 0x%lx\n", hva); 63762306a36Sopenharmony_ci mmap_read_unlock(current->mm); 63862306a36Sopenharmony_ci return -EFAULT; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (is_vm_hugetlb_page(vma)) 64262306a36Sopenharmony_ci vma_pageshift = huge_page_shift(hstate_vma(vma)); 64362306a36Sopenharmony_ci else 64462306a36Sopenharmony_ci vma_pageshift = PAGE_SHIFT; 64562306a36Sopenharmony_ci vma_pagesize = 1ULL << vma_pageshift; 64662306a36Sopenharmony_ci if (logging || (vma->vm_flags & VM_PFNMAP)) 64762306a36Sopenharmony_ci vma_pagesize = PAGE_SIZE; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (vma_pagesize == PMD_SIZE || vma_pagesize == PUD_SIZE) 65062306a36Sopenharmony_ci gfn = (gpa & huge_page_mask(hstate_vma(vma))) >> PAGE_SHIFT; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* 65362306a36Sopenharmony_ci * Read mmu_invalidate_seq so that KVM can detect if the results of 65462306a36Sopenharmony_ci * vma_lookup() or gfn_to_pfn_prot() become stale priort to acquiring 65562306a36Sopenharmony_ci * kvm->mmu_lock. 65662306a36Sopenharmony_ci * 65762306a36Sopenharmony_ci * Rely on mmap_read_unlock() for an implicit smp_rmb(), which pairs 65862306a36Sopenharmony_ci * with the smp_wmb() in kvm_mmu_invalidate_end(). 65962306a36Sopenharmony_ci */ 66062306a36Sopenharmony_ci mmu_seq = kvm->mmu_invalidate_seq; 66162306a36Sopenharmony_ci mmap_read_unlock(current->mm); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (vma_pagesize != PUD_SIZE && 66462306a36Sopenharmony_ci vma_pagesize != PMD_SIZE && 66562306a36Sopenharmony_ci vma_pagesize != PAGE_SIZE) { 66662306a36Sopenharmony_ci kvm_err("Invalid VMA page size 0x%lx\n", vma_pagesize); 66762306a36Sopenharmony_ci return -EFAULT; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci hfn = gfn_to_pfn_prot(kvm, gfn, is_write, &writable); 67162306a36Sopenharmony_ci if (hfn == KVM_PFN_ERR_HWPOISON) { 67262306a36Sopenharmony_ci send_sig_mceerr(BUS_MCEERR_AR, (void __user *)hva, 67362306a36Sopenharmony_ci vma_pageshift, current); 67462306a36Sopenharmony_ci return 0; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci if (is_error_noslot_pfn(hfn)) 67762306a36Sopenharmony_ci return -EFAULT; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* 68062306a36Sopenharmony_ci * If logging is active then we allow writable pages only 68162306a36Sopenharmony_ci * for write faults. 68262306a36Sopenharmony_ci */ 68362306a36Sopenharmony_ci if (logging && !is_write) 68462306a36Sopenharmony_ci writable = false; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci spin_lock(&kvm->mmu_lock); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (mmu_invalidate_retry(kvm, mmu_seq)) 68962306a36Sopenharmony_ci goto out_unlock; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (writable) { 69262306a36Sopenharmony_ci kvm_set_pfn_dirty(hfn); 69362306a36Sopenharmony_ci mark_page_dirty(kvm, gfn); 69462306a36Sopenharmony_ci ret = gstage_map_page(kvm, pcache, gpa, hfn << PAGE_SHIFT, 69562306a36Sopenharmony_ci vma_pagesize, false, true); 69662306a36Sopenharmony_ci } else { 69762306a36Sopenharmony_ci ret = gstage_map_page(kvm, pcache, gpa, hfn << PAGE_SHIFT, 69862306a36Sopenharmony_ci vma_pagesize, true, true); 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (ret) 70262306a36Sopenharmony_ci kvm_err("Failed to map in G-stage\n"); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ciout_unlock: 70562306a36Sopenharmony_ci spin_unlock(&kvm->mmu_lock); 70662306a36Sopenharmony_ci kvm_set_pfn_accessed(hfn); 70762306a36Sopenharmony_ci kvm_release_pfn_clean(hfn); 70862306a36Sopenharmony_ci return ret; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ciint kvm_riscv_gstage_alloc_pgd(struct kvm *kvm) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci struct page *pgd_page; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (kvm->arch.pgd != NULL) { 71662306a36Sopenharmony_ci kvm_err("kvm_arch already initialized?\n"); 71762306a36Sopenharmony_ci return -EINVAL; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci pgd_page = alloc_pages(GFP_KERNEL | __GFP_ZERO, 72162306a36Sopenharmony_ci get_order(gstage_pgd_size)); 72262306a36Sopenharmony_ci if (!pgd_page) 72362306a36Sopenharmony_ci return -ENOMEM; 72462306a36Sopenharmony_ci kvm->arch.pgd = page_to_virt(pgd_page); 72562306a36Sopenharmony_ci kvm->arch.pgd_phys = page_to_phys(pgd_page); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci return 0; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_civoid kvm_riscv_gstage_free_pgd(struct kvm *kvm) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci void *pgd = NULL; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci spin_lock(&kvm->mmu_lock); 73562306a36Sopenharmony_ci if (kvm->arch.pgd) { 73662306a36Sopenharmony_ci gstage_unmap_range(kvm, 0UL, gstage_gpa_size, false); 73762306a36Sopenharmony_ci pgd = READ_ONCE(kvm->arch.pgd); 73862306a36Sopenharmony_ci kvm->arch.pgd = NULL; 73962306a36Sopenharmony_ci kvm->arch.pgd_phys = 0; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci spin_unlock(&kvm->mmu_lock); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (pgd) 74462306a36Sopenharmony_ci free_pages((unsigned long)pgd, get_order(gstage_pgd_size)); 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_civoid kvm_riscv_gstage_update_hgatp(struct kvm_vcpu *vcpu) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci unsigned long hgatp = gstage_mode; 75062306a36Sopenharmony_ci struct kvm_arch *k = &vcpu->kvm->arch; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci hgatp |= (READ_ONCE(k->vmid.vmid) << HGATP_VMID_SHIFT) & HGATP_VMID; 75362306a36Sopenharmony_ci hgatp |= (k->pgd_phys >> PAGE_SHIFT) & HGATP_PPN; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci csr_write(CSR_HGATP, hgatp); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci if (!kvm_riscv_gstage_vmid_bits()) 75862306a36Sopenharmony_ci kvm_riscv_local_hfence_gvma_all(); 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_civoid __init kvm_riscv_gstage_mode_detect(void) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci#ifdef CONFIG_64BIT 76462306a36Sopenharmony_ci /* Try Sv57x4 G-stage mode */ 76562306a36Sopenharmony_ci csr_write(CSR_HGATP, HGATP_MODE_SV57X4 << HGATP_MODE_SHIFT); 76662306a36Sopenharmony_ci if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV57X4) { 76762306a36Sopenharmony_ci gstage_mode = (HGATP_MODE_SV57X4 << HGATP_MODE_SHIFT); 76862306a36Sopenharmony_ci gstage_pgd_levels = 5; 76962306a36Sopenharmony_ci goto skip_sv48x4_test; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* Try Sv48x4 G-stage mode */ 77362306a36Sopenharmony_ci csr_write(CSR_HGATP, HGATP_MODE_SV48X4 << HGATP_MODE_SHIFT); 77462306a36Sopenharmony_ci if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV48X4) { 77562306a36Sopenharmony_ci gstage_mode = (HGATP_MODE_SV48X4 << HGATP_MODE_SHIFT); 77662306a36Sopenharmony_ci gstage_pgd_levels = 4; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ciskip_sv48x4_test: 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci csr_write(CSR_HGATP, 0); 78162306a36Sopenharmony_ci kvm_riscv_local_hfence_gvma_all(); 78262306a36Sopenharmony_ci#endif 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ciunsigned long __init kvm_riscv_gstage_mode(void) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci return gstage_mode >> HGATP_MODE_SHIFT; 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ciint kvm_riscv_gstage_gpa_bits(void) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci return gstage_gpa_bits; 79362306a36Sopenharmony_ci} 794