xref: /kvm-unit-tests/lib/arm/mmu.c (revision 2c96b77ec9d3b1fcec7525174e23a6240ee05949)
1 /*
2  * MMU enable and page table manipulation functions
3  *
4  * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
5  *
6  * This work is licensed under the terms of the GNU LGPL, version 2.
7  */
8 #include <asm/setup.h>
9 #include <asm/thread_info.h>
10 #include <asm/cpumask.h>
11 #include <asm/mmu.h>
12 #include <asm/setup.h>
13 #include <asm/page.h>
14 #include <asm/io.h>
15 
16 #include "alloc_page.h"
17 #include "vmalloc.h"
18 #include <asm/pgtable-hwdef.h>
19 #include <asm/pgtable.h>
20 
21 #include <linux/compiler.h>
22 
23 pgd_t *mmu_idmap;
24 
25 /* CPU 0 starts with disabled MMU */
26 static cpumask_t mmu_enabled_cpumask;
27 
28 bool mmu_enabled(void)
29 {
30 	/*
31 	 * mmu_enabled is called from places that are guarding the
32 	 * use of exclusive ops (which require the mmu to be enabled).
33 	 * That means we CANNOT call anything from here that may use a
34 	 * spinlock, atomic bitop, etc., otherwise we'll recurse.
35 	 * [cpumask_]test_bit is safe though.
36 	 */
37 	if (is_user()) {
38 		int cpu = current_thread_info()->cpu;
39 		return cpumask_test_cpu(cpu, &mmu_enabled_cpumask);
40 	}
41 
42 	return __mmu_enabled();
43 }
44 
45 void mmu_mark_enabled(int cpu)
46 {
47 	cpumask_set_cpu(cpu, &mmu_enabled_cpumask);
48 }
49 
50 void mmu_mark_disabled(int cpu)
51 {
52 	cpumask_clear_cpu(cpu, &mmu_enabled_cpumask);
53 }
54 
55 extern void asm_mmu_enable(phys_addr_t pgtable);
56 void mmu_enable(pgd_t *pgtable)
57 {
58 	struct thread_info *info = current_thread_info();
59 
60 	asm_mmu_enable(__pa(pgtable));
61 
62 	info->pgtable = pgtable;
63 	mmu_mark_enabled(info->cpu);
64 }
65 
66 extern void asm_mmu_disable(void);
67 void mmu_disable(void)
68 {
69 	unsigned long sp = current_stack_pointer;
70 	int cpu = current_thread_info()->cpu;
71 
72 	assert_msg(__virt_to_phys(sp) == sp,
73 			"Attempting to disable MMU with non-identity mapped stack");
74 
75 	mmu_mark_disabled(cpu);
76 
77 	asm_mmu_disable();
78 }
79 
80 static pteval_t *get_pte(pgd_t *pgtable, uintptr_t vaddr)
81 {
82 	pgd_t *pgd = pgd_offset(pgtable, vaddr);
83 	pud_t *pud = pud_alloc(pgd, vaddr);
84 	pmd_t *pmd = pmd_alloc(pud, vaddr);
85 	pte_t *pte = pte_alloc(pmd, vaddr);
86 
87 	return &pte_val(*pte);
88 }
89 
90 static pteval_t *install_pte(pgd_t *pgtable, uintptr_t vaddr, pteval_t pte)
91 {
92 	pteval_t *p_pte = get_pte(pgtable, vaddr);
93 
94 	WRITE_ONCE(*p_pte, pte);
95 	flush_tlb_page(vaddr);
96 	return p_pte;
97 }
98 
99 static pteval_t *install_page_prot(pgd_t *pgtable, phys_addr_t phys,
100 				   uintptr_t vaddr, pgprot_t prot)
101 {
102 	pteval_t pte = phys;
103 	pte |= PTE_TYPE_PAGE | PTE_AF | PTE_SHARED;
104 	pte |= pgprot_val(prot);
105 	return install_pte(pgtable, vaddr, pte);
106 }
107 
108 pteval_t *install_page(pgd_t *pgtable, phys_addr_t phys, void *virt)
109 {
110 	return install_page_prot(pgtable, phys, (uintptr_t)virt,
111 				 __pgprot(PTE_WBWA | PTE_USER));
112 }
113 
114 phys_addr_t virt_to_pte_phys(pgd_t *pgtable, void *mem)
115 {
116 	return (*get_pte(pgtable, (uintptr_t)mem) & PHYS_MASK & -PAGE_SIZE)
117 		+ ((ulong)mem & (PAGE_SIZE - 1));
118 }
119 
120 void mmu_set_range_ptes(pgd_t *pgtable, uintptr_t virt_offset,
121 			phys_addr_t phys_start, phys_addr_t phys_end,
122 			pgprot_t prot)
123 {
124 	phys_addr_t paddr = phys_start & PAGE_MASK;
125 	uintptr_t vaddr = virt_offset & PAGE_MASK;
126 	uintptr_t virt_end = phys_end - paddr + vaddr;
127 
128 	for (; vaddr < virt_end; vaddr += PAGE_SIZE, paddr += PAGE_SIZE)
129 		install_page_prot(pgtable, paddr, vaddr, prot);
130 }
131 
132 void mmu_set_range_sect(pgd_t *pgtable, uintptr_t virt_offset,
133 			phys_addr_t phys_start, phys_addr_t phys_end,
134 			pgprot_t prot)
135 {
136 	phys_addr_t paddr = phys_start & PMD_MASK;
137 	uintptr_t vaddr = virt_offset & PMD_MASK;
138 	uintptr_t virt_end = phys_end - paddr + vaddr;
139 	pgd_t *pgd;
140 	pud_t *pud;
141 	pmd_t *pmd;
142 	pmd_t entry;
143 
144 	for (; vaddr < virt_end; vaddr += PMD_SIZE, paddr += PMD_SIZE) {
145 		pmd_val(entry) = paddr;
146 		pmd_val(entry) |= PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S;
147 		pmd_val(entry) |= pgprot_val(prot);
148 		pgd = pgd_offset(pgtable, vaddr);
149 		pud = pud_alloc(pgd, vaddr);
150 		pmd = pmd_alloc(pud, vaddr);
151 		WRITE_ONCE(*pmd, entry);
152 		flush_tlb_page(vaddr);
153 	}
154 }
155 
156 void *setup_mmu(phys_addr_t phys_end, void *unused)
157 {
158 	struct mem_region *r;
159 
160 	/* 3G-4G region is reserved for vmalloc, cap phys_end at 3G */
161 	if (phys_end > (3ul << 30))
162 		phys_end = 3ul << 30;
163 
164 #ifdef __aarch64__
165 	init_alloc_vpage((void*)(4ul << 30));
166 
167 	assert_msg(system_supports_granule(PAGE_SIZE),
168 			"Unsupported translation granule %ld\n", PAGE_SIZE);
169 #endif
170 
171 	if (!mmu_idmap)
172 		mmu_idmap = alloc_page();
173 
174 	for (r = mem_regions; r->end; ++r) {
175 		if (r->flags & MR_F_IO) {
176 			continue;
177 		} else if (r->flags & MR_F_CODE) {
178 			/* armv8 requires code shared between EL1 and EL0 to be read-only */
179 			mmu_set_range_ptes(mmu_idmap, r->start, r->start, r->end,
180 					   __pgprot(PTE_WBWA | PTE_USER | PTE_RDONLY));
181 		} else {
182 			mmu_set_range_ptes(mmu_idmap, r->start, r->start, r->end,
183 					   __pgprot(PTE_WBWA | PTE_USER));
184 		}
185 	}
186 
187 	mmu_enable(mmu_idmap);
188 	return mmu_idmap;
189 }
190 
191 void __iomem *__ioremap(phys_addr_t phys_addr, size_t size)
192 {
193 	phys_addr_t paddr_aligned = phys_addr & PAGE_MASK;
194 	phys_addr_t paddr_end = PAGE_ALIGN(phys_addr + size);
195 	pgprot_t prot = __pgprot(PTE_UNCACHED | PTE_USER | PTE_UXN | PTE_PXN);
196 	pgd_t *pgtable;
197 
198 	assert(sizeof(long) == 8 || !(phys_addr >> 32));
199 
200 	if (mmu_enabled()) {
201 		pgtable = current_thread_info()->pgtable;
202 	} else {
203 		if (!mmu_idmap)
204 			mmu_idmap = alloc_page();
205 		pgtable = mmu_idmap;
206 	}
207 
208 	mmu_set_range_ptes(pgtable, paddr_aligned, paddr_aligned,
209 			   paddr_end, prot);
210 
211 	return (void __iomem *)(unsigned long)phys_addr;
212 }
213 
214 phys_addr_t __virt_to_phys(unsigned long addr)
215 {
216 	if (mmu_enabled()) {
217 		pgd_t *pgtable = current_thread_info()->pgtable;
218 		return virt_to_pte_phys(pgtable, (void *)addr);
219 	}
220 	return addr;
221 }
222 
223 unsigned long __phys_to_virt(phys_addr_t addr)
224 {
225 	/*
226 	 * We don't guarantee that phys_to_virt(virt_to_phys(vaddr)) == vaddr, but
227 	 * the default page tables do identity map all physical addresses, which
228 	 * means phys_to_virt(virt_to_phys((void *)paddr)) == paddr.
229 	 */
230 	assert(!mmu_enabled() || __virt_to_phys(addr) == addr);
231 	return addr;
232 }
233 
234 /*
235  * NOTE: The Arm architecture might require the use of a
236  * break-before-make sequence before making changes to a PTE and
237  * certain conditions are met (see Arm ARM D5-2669 for AArch64 and
238  * B3-1378 for AArch32 for more details).
239  */
240 pteval_t *mmu_get_pte(pgd_t *pgtable, uintptr_t vaddr)
241 {
242 	pgd_t *pgd;
243 	pud_t *pud;
244 	pmd_t *pmd;
245 	pte_t *pte;
246 
247 	if (!mmu_enabled())
248 		return NULL;
249 
250 	pgd = pgd_offset(pgtable, vaddr);
251 	assert(pgd_valid(*pgd));
252 	pud = pud_offset(pgd, vaddr);
253 	assert(pud_valid(*pud));
254 	pmd = pmd_offset(pud, vaddr);
255 	assert(pmd_valid(*pmd));
256 
257 	if (pmd_huge(*pmd))
258 		return &pmd_val(*pmd);
259 
260 	pte = pte_offset(pmd, vaddr);
261 	assert(pte_valid(*pte));
262 
263         return &pte_val(*pte);
264 }
265 
266 void mmu_clear_user(pgd_t *pgtable, unsigned long vaddr)
267 {
268 	pteval_t *p_pte = mmu_get_pte(pgtable, vaddr);
269 	if (p_pte) {
270 		pteval_t entry = *p_pte & ~PTE_USER;
271 		WRITE_ONCE(*p_pte, entry);
272 		flush_tlb_page(vaddr);
273 	}
274 }
275