1 /* SPDX-License-Identifier: GPL-2.0 */ 2 /* 3 * Copyright (C) 2020-2023 Loongson Technology Corporation Limited 4 */ 5 6 #ifndef __ASM_LOONGARCH_KVM_MMU_H__ 7 #define __ASM_LOONGARCH_KVM_MMU_H__ 8 9 #include <linux/kvm_host.h> 10 #include <asm/pgalloc.h> 11 #include <asm/tlb.h> 12 13 /* 14 * KVM_MMU_CACHE_MIN_PAGES is the number of GPA page table translation levels 15 * for which pages need to be cached. 16 */ 17 #define KVM_MMU_CACHE_MIN_PAGES (CONFIG_PGTABLE_LEVELS - 1) 18 19 #define _KVM_FLUSH_PGTABLE 0x1 20 #define _KVM_HAS_PGMASK 0x2 21 #define kvm_pfn_pte(pfn, prot) (((pfn) << PFN_PTE_SHIFT) | pgprot_val(prot)) 22 #define kvm_pte_pfn(x) ((phys_addr_t)((x & _PFN_MASK) >> PFN_PTE_SHIFT)) 23 24 typedef unsigned long kvm_pte_t; 25 typedef struct kvm_ptw_ctx kvm_ptw_ctx; 26 typedef int (*kvm_pte_ops)(kvm_pte_t *pte, phys_addr_t addr, kvm_ptw_ctx *ctx); 27 28 struct kvm_ptw_ctx { 29 kvm_pte_ops ops; 30 unsigned long flag; 31 32 /* for kvm_arch_mmu_enable_log_dirty_pt_masked use */ 33 unsigned long mask; 34 unsigned long gfn; 35 36 /* page walk mmu info */ 37 unsigned int level; 38 unsigned long pgtable_shift; 39 unsigned long invalid_entry; 40 unsigned long *invalid_ptes; 41 unsigned int *pte_shifts; 42 void *opaque; 43 44 /* free pte table page list */ 45 struct list_head list; 46 }; 47 48 kvm_pte_t *kvm_pgd_alloc(void); 49 50 static inline void kvm_set_pte(kvm_pte_t *ptep, kvm_pte_t val) 51 { 52 WRITE_ONCE(*ptep, val); 53 } 54 55 static inline int kvm_pte_write(kvm_pte_t pte) { return pte & _PAGE_WRITE; } 56 static inline int kvm_pte_dirty(kvm_pte_t pte) { return pte & _PAGE_DIRTY; } 57 static inline int kvm_pte_young(kvm_pte_t pte) { return pte & _PAGE_ACCESSED; } 58 static inline int kvm_pte_huge(kvm_pte_t pte) { return pte & _PAGE_HUGE; } 59 60 static inline kvm_pte_t kvm_pte_mkyoung(kvm_pte_t pte) 61 { 62 return pte | _PAGE_ACCESSED; 63 } 64 65 static inline kvm_pte_t kvm_pte_mkold(kvm_pte_t pte) 66 { 67 return pte & ~_PAGE_ACCESSED; 68 } 69 70 static inline kvm_pte_t kvm_pte_mkdirty(kvm_pte_t pte) 71 { 72 return pte | _PAGE_DIRTY; 73 } 74 75 static inline kvm_pte_t kvm_pte_mkclean(kvm_pte_t pte) 76 { 77 return pte & ~_PAGE_DIRTY; 78 } 79 80 static inline kvm_pte_t kvm_pte_mkhuge(kvm_pte_t pte) 81 { 82 return pte | _PAGE_HUGE; 83 } 84 85 static inline kvm_pte_t kvm_pte_mksmall(kvm_pte_t pte) 86 { 87 return pte & ~_PAGE_HUGE; 88 } 89 90 static inline int kvm_need_flush(kvm_ptw_ctx *ctx) 91 { 92 return ctx->flag & _KVM_FLUSH_PGTABLE; 93 } 94 95 static inline kvm_pte_t *kvm_pgtable_offset(kvm_ptw_ctx *ctx, kvm_pte_t *table, 96 phys_addr_t addr) 97 { 98 99 return table + ((addr >> ctx->pgtable_shift) & (PTRS_PER_PTE - 1)); 100 } 101 102 static inline phys_addr_t kvm_pgtable_addr_end(kvm_ptw_ctx *ctx, 103 phys_addr_t addr, phys_addr_t end) 104 { 105 phys_addr_t boundary, size; 106 107 size = 0x1UL << ctx->pgtable_shift; 108 boundary = (addr + size) & ~(size - 1); 109 return (boundary - 1 < end - 1) ? boundary : end; 110 } 111 112 static inline int kvm_pte_present(kvm_ptw_ctx *ctx, kvm_pte_t *entry) 113 { 114 if (!ctx || ctx->level == 0) 115 return !!(*entry & _PAGE_PRESENT); 116 117 return *entry != ctx->invalid_entry; 118 } 119 120 static inline int kvm_pte_none(kvm_ptw_ctx *ctx, kvm_pte_t *entry) 121 { 122 return *entry == ctx->invalid_entry; 123 } 124 125 static inline void kvm_ptw_enter(kvm_ptw_ctx *ctx) 126 { 127 ctx->level--; 128 ctx->pgtable_shift = ctx->pte_shifts[ctx->level]; 129 ctx->invalid_entry = ctx->invalid_ptes[ctx->level]; 130 } 131 132 static inline void kvm_ptw_exit(kvm_ptw_ctx *ctx) 133 { 134 ctx->level++; 135 ctx->pgtable_shift = ctx->pte_shifts[ctx->level]; 136 ctx->invalid_entry = ctx->invalid_ptes[ctx->level]; 137 } 138 139 #endif /* __ASM_LOONGARCH_KVM_MMU_H__ */ 140