xref: /linux/arch/loongarch/include/asm/kvm_mmu.h (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
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 
kvm_set_pte(kvm_pte_t * ptep,kvm_pte_t val)50 static inline void kvm_set_pte(kvm_pte_t *ptep, kvm_pte_t val)
51 {
52 	WRITE_ONCE(*ptep, val);
53 }
54 
kvm_pte_write(kvm_pte_t pte)55 static inline int kvm_pte_write(kvm_pte_t pte) { return pte & _PAGE_WRITE; }
kvm_pte_dirty(kvm_pte_t pte)56 static inline int kvm_pte_dirty(kvm_pte_t pte) { return pte & _PAGE_DIRTY; }
kvm_pte_young(kvm_pte_t pte)57 static inline int kvm_pte_young(kvm_pte_t pte) { return pte & _PAGE_ACCESSED; }
kvm_pte_huge(kvm_pte_t pte)58 static inline int kvm_pte_huge(kvm_pte_t pte) { return pte & _PAGE_HUGE; }
59 
kvm_pte_mkyoung(kvm_pte_t pte)60 static inline kvm_pte_t kvm_pte_mkyoung(kvm_pte_t pte)
61 {
62 	return pte | _PAGE_ACCESSED;
63 }
64 
kvm_pte_mkold(kvm_pte_t pte)65 static inline kvm_pte_t kvm_pte_mkold(kvm_pte_t pte)
66 {
67 	return pte & ~_PAGE_ACCESSED;
68 }
69 
kvm_pte_mkdirty(kvm_pte_t pte)70 static inline kvm_pte_t kvm_pte_mkdirty(kvm_pte_t pte)
71 {
72 	return pte | _PAGE_DIRTY;
73 }
74 
kvm_pte_mkclean(kvm_pte_t pte)75 static inline kvm_pte_t kvm_pte_mkclean(kvm_pte_t pte)
76 {
77 	return pte & ~_PAGE_DIRTY;
78 }
79 
kvm_pte_mkhuge(kvm_pte_t pte)80 static inline kvm_pte_t kvm_pte_mkhuge(kvm_pte_t pte)
81 {
82 	return pte | _PAGE_HUGE;
83 }
84 
kvm_pte_mksmall(kvm_pte_t pte)85 static inline kvm_pte_t kvm_pte_mksmall(kvm_pte_t pte)
86 {
87 	return pte & ~_PAGE_HUGE;
88 }
89 
kvm_need_flush(kvm_ptw_ctx * ctx)90 static inline int kvm_need_flush(kvm_ptw_ctx *ctx)
91 {
92 	return ctx->flag & _KVM_FLUSH_PGTABLE;
93 }
94 
kvm_pgtable_offset(kvm_ptw_ctx * ctx,kvm_pte_t * table,phys_addr_t addr)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 
kvm_pgtable_addr_end(kvm_ptw_ctx * ctx,phys_addr_t addr,phys_addr_t end)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 
kvm_pte_present(kvm_ptw_ctx * ctx,kvm_pte_t * entry)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 
kvm_pte_none(kvm_ptw_ctx * ctx,kvm_pte_t * entry)120 static inline int kvm_pte_none(kvm_ptw_ctx *ctx, kvm_pte_t *entry)
121 {
122 	return *entry == ctx->invalid_entry;
123 }
124 
kvm_ptw_enter(kvm_ptw_ctx * ctx)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 
kvm_ptw_exit(kvm_ptw_ctx * ctx)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