xref: /kvm-unit-tests/lib/s390x/mmu.c (revision 4c8a99ca02252d4a2bee43f4558fe47ce5ab7ec0)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * s390x MMU
4  *
5  * Copyright (c) 2017 Red Hat Inc
6  *
7  * Authors:
8  *  David Hildenbrand <david@redhat.com>
9  */
10 
11 #include <libcflat.h>
12 #include <asm/pgtable.h>
13 #include <asm/arch_def.h>
14 #include <asm/barrier.h>
15 #include <vmalloc.h>
16 #include "mmu.h"
17 
18 /*
19  * The naming convention used here is the same as used in the Linux kernel;
20  * this is the correspondence between the s390x architectural names and the
21  * Linux ones:
22  *
23  * pgd - region 1 table entry
24  * p4d - region 2 table entry
25  * pud - region 3 table entry
26  * pmd - segment table entry
27  * pte - page table entry
28  */
29 
30 static pgd_t *table_root;
31 
32 static void mmu_enable(pgd_t *pgtable)
33 {
34 	const uint64_t asce = __pa(pgtable) | ASCE_DT_REGION1 |
35 			      REGION_TABLE_LENGTH;
36 
37 	/* set primary asce */
38 	lctlg(1, asce);
39 	assert(stctg(1) == asce);
40 
41 	/* enable dat (primary == 0 set as default) */
42 	enable_dat();
43 
44 	/* we can now also use DAT unconditionally in our PGM handler */
45 	lowcore.pgm_new_psw.mask |= PSW_MASK_DAT;
46 }
47 
48 /*
49  * Get the pud (region 3) DAT table entry for the given address and root,
50  * allocating it if necessary
51  */
52 static inline pud_t *get_pud(pgd_t *pgtable, uintptr_t vaddr)
53 {
54 	pgd_t *pgd = pgd_offset(pgtable, vaddr);
55 	p4d_t *p4d = p4d_alloc(pgd, vaddr);
56 	pud_t *pud = pud_alloc(p4d, vaddr);
57 
58 	return pud;
59 }
60 
61 /*
62  * Get the pmd (segment) DAT table entry for the given address and pud,
63  * allocating it if necessary.
64  * The pud must not be huge.
65  */
66 static inline pmd_t *get_pmd(pud_t *pud, uintptr_t vaddr)
67 {
68 	pmd_t *pmd;
69 
70 	assert(!pud_huge(*pud));
71 	pmd = pmd_alloc(pud, vaddr);
72 	return pmd;
73 }
74 
75 /*
76  * Get the pte (page) DAT table entry for the given address and pmd,
77  * allocating it if necessary.
78  * The pmd must not be large.
79  */
80 static inline pte_t *get_pte(pmd_t *pmd, uintptr_t vaddr)
81 {
82 	pte_t *pte;
83 
84 	assert(!pmd_large(*pmd));
85 	pte = pte_alloc(pmd, vaddr);
86 	return pte;
87 }
88 
89 /*
90  * Splits a large pmd (segment) DAT table entry into equivalent 4kB small
91  * pages.
92  * @pmd The pmd to split, it must be large.
93  * @va the virtual address corresponding to this pmd.
94  */
95 static void split_pmd(pmd_t *pmd, uintptr_t va)
96 {
97 	phys_addr_t pa = pmd_val(*pmd) & SEGMENT_ENTRY_SFAA;
98 	unsigned long i, prot;
99 	pte_t *pte;
100 
101 	assert(pmd_large(*pmd));
102 	pte = alloc_pages(PAGE_TABLE_ORDER);
103 	prot = pmd_val(*pmd) & (SEGMENT_ENTRY_IEP | SEGMENT_ENTRY_P);
104 	for (i = 0; i < PAGE_TABLE_ENTRIES; i++)
105 		pte_val(pte[i]) =  pa | PAGE_SIZE * i | prot;
106 	idte_pmdp(va, &pmd_val(*pmd));
107 	pmd_val(*pmd) = __pa(pte) | SEGMENT_ENTRY_TT_SEGMENT;
108 
109 }
110 
111 /*
112  * Splits a huge pud (region 3) DAT table entry into equivalent 1MB large
113  * pages.
114  * @pud The pud to split, it must be huge.
115  * @va the virtual address corresponding to this pud.
116  */
117 static void split_pud(pud_t *pud, uintptr_t va)
118 {
119 	phys_addr_t pa = pud_val(*pud) & REGION3_ENTRY_RFAA;
120 	unsigned long i, prot;
121 	pmd_t *pmd;
122 
123 	assert(pud_huge(*pud));
124 	pmd = alloc_pages(SEGMENT_TABLE_ORDER);
125 	prot = pud_val(*pud) & (REGION3_ENTRY_IEP | REGION_ENTRY_P);
126 	for (i = 0; i < SEGMENT_TABLE_ENTRIES; i++)
127 		pmd_val(pmd[i]) =  pa | SZ_1M * i | prot | SEGMENT_ENTRY_FC | SEGMENT_ENTRY_TT_SEGMENT;
128 	idte_pudp(va, &pud_val(*pud));
129 	pud_val(*pud) = __pa(pmd) | REGION_ENTRY_TT_REGION3 | REGION_TABLE_LENGTH;
130 }
131 
132 void *get_dat_entry(pgd_t *pgtable, void *vaddr, enum pgt_level level)
133 {
134 	uintptr_t va = (uintptr_t)vaddr;
135 	pgd_t *pgd;
136 	p4d_t *p4d;
137 	pud_t *pud;
138 	pmd_t *pmd;
139 
140 	assert(level && (level <= 5));
141 	pgd = pgd_offset(pgtable, va);
142 	if (level == pgtable_level_pgd)
143 		return pgd;
144 	p4d = p4d_alloc(pgd, va);
145 	if (level == pgtable_level_p4d)
146 		return p4d;
147 	pud = pud_alloc(p4d, va);
148 
149 	if (level == pgtable_level_pud)
150 		return pud;
151 	if (!pud_none(*pud) && pud_huge(*pud))
152 		split_pud(pud, va);
153 	pmd = get_pmd(pud, va);
154 	if (level == pgtable_level_pmd)
155 		return pmd;
156 	if (!pmd_none(*pmd) && pmd_large(*pmd))
157 		split_pmd(pmd, va);
158 	return get_pte(pmd, va);
159 }
160 
161 void *split_page(pgd_t *pgtable, void *vaddr, enum pgt_level level)
162 {
163 	assert((level >= 3) && (level <= 5));
164 	return get_dat_entry(pgtable ? pgtable : table_root, vaddr, level);
165 }
166 
167 phys_addr_t virt_to_pte_phys(pgd_t *pgtable, void *vaddr)
168 {
169 	uintptr_t va = (uintptr_t)vaddr;
170 	pud_t *pud;
171 	pmd_t *pmd;
172 	pte_t *pte;
173 
174 	pud = get_pud(pgtable, va);
175 	if (pud_huge(*pud))
176 		return (pud_val(*pud) & REGION3_ENTRY_RFAA) | (va & ~REGION3_ENTRY_RFAA);
177 	pmd = get_pmd(pud, va);
178 	if (pmd_large(*pmd))
179 		return (pmd_val(*pmd) & SEGMENT_ENTRY_SFAA) | (va & ~SEGMENT_ENTRY_SFAA);
180 	pte = get_pte(pmd, va);
181 	return (pte_val(*pte) & PAGE_MASK) | (va & ~PAGE_MASK);
182 }
183 
184 /*
185  * Get the DAT table entry of the given level for the given address,
186  * splitting if necessary. If the entry was not invalid, invalidate it, and
187  * return the pointer to the entry and, if requested, its old value.
188  * @pgtable root of the page tables
189  * @vaddr virtual address
190  * @level 3 (for 2GB pud), 4 (for 1MB pmd) or 5 (for 4kB pages)
191  * @old if not NULL, will be written with the old value of the DAT table
192  * entry before invalidation
193  */
194 static void *dat_get_and_invalidate(pgd_t *pgtable, void *vaddr, enum pgt_level level, unsigned long *old)
195 {
196 	unsigned long va = (unsigned long)vaddr;
197 	void *ptr;
198 
199 	ptr = get_dat_entry(pgtable, vaddr, level);
200 	if (old)
201 		*old = *(unsigned long *)ptr;
202 	if ((level == pgtable_level_pgd) && !pgd_none(*(pgd_t *)ptr))
203 		idte_pgdp(va, ptr);
204 	else if ((level == pgtable_level_p4d) && !p4d_none(*(p4d_t *)ptr))
205 		idte_p4dp(va, ptr);
206 	else if ((level == pgtable_level_pud) && !pud_none(*(pud_t *)ptr))
207 		idte_pudp(va, ptr);
208 	else if ((level == pgtable_level_pmd) && !pmd_none(*(pmd_t *)ptr))
209 		idte_pmdp(va, ptr);
210 	else if (!pte_none(*(pte_t *)ptr))
211 		ipte(va, ptr);
212 	return ptr;
213 }
214 
215 static void cleanup_pmd(pmd_t *pmd)
216 {
217 	/* was invalid or large, nothing to do */
218 	if (pmd_none(*pmd) || pmd_large(*pmd))
219 		return;
220 	/* was not large, free the corresponding page table */
221 	free_pages((void *)(pmd_val(*pmd) & PAGE_MASK));
222 }
223 
224 static void cleanup_pud(pud_t *pud)
225 {
226 	unsigned long i;
227 	pmd_t *pmd;
228 
229 	/* was invalid or large, nothing to do */
230 	if (pud_none(*pud) || pud_huge(*pud))
231 		return;
232 	/* recursively clean up all pmds if needed */
233 	pmd = (pmd_t *)(pud_val(*pud) & PAGE_MASK);
234 	for (i = 0; i < SEGMENT_TABLE_ENTRIES; i++)
235 		cleanup_pmd(pmd + i);
236 	/* free the corresponding segment table */
237 	free_pages(pmd);
238 }
239 
240 /*
241  * Set the DAT entry for the given level of the given virtual address. If a
242  * mapping already existed, it is overwritten. If an existing mapping with
243  * smaller pages existed, all the lower tables are freed.
244  * Returns the pointer to the DAT table entry.
245  * @pgtable root of the page tables
246  * @val the new value for the DAT table entry
247  * @vaddr the virtual address
248  * @level 3 for pud (region 3), 4 for pmd (segment) and 5 for pte (pages)
249  */
250 static void *set_dat_entry(pgd_t *pgtable, unsigned long val, void *vaddr, enum pgt_level level)
251 {
252 	unsigned long old, *res;
253 
254 	res = dat_get_and_invalidate(pgtable, vaddr, level, &old);
255 	if (level == pgtable_level_pmd)
256 		cleanup_pmd((pmd_t *)&old);
257 	if (level == pgtable_level_pud)
258 		cleanup_pud((pud_t *)&old);
259 	*res = val;
260 	return res;
261 }
262 
263 pteval_t *install_page(pgd_t *pgtable, phys_addr_t phys, void *vaddr)
264 {
265 	assert(IS_ALIGNED(phys, PAGE_SIZE));
266 	assert(IS_ALIGNED((uintptr_t)vaddr, PAGE_SIZE));
267 	return set_dat_entry(pgtable, phys, vaddr, pgtable_level_pte);
268 }
269 
270 pmdval_t *install_large_page(pgd_t *pgtable, phys_addr_t phys, void *vaddr)
271 {
272 	assert(IS_ALIGNED(phys, SZ_1M));
273 	assert(IS_ALIGNED((uintptr_t)vaddr, SZ_1M));
274 	return set_dat_entry(pgtable, phys | SEGMENT_ENTRY_FC, vaddr, pgtable_level_pmd);
275 }
276 
277 pudval_t *install_huge_page(pgd_t *pgtable, phys_addr_t phys, void *vaddr)
278 {
279 	assert(IS_ALIGNED(phys, SZ_2G));
280 	assert(IS_ALIGNED((uintptr_t)vaddr, SZ_2G));
281 	return set_dat_entry(pgtable, phys | REGION3_ENTRY_FC | REGION_ENTRY_TT_REGION3, vaddr, pgtable_level_pud);
282 }
283 
284 void protect_dat_entry(void *vaddr, unsigned long prot, enum pgt_level level)
285 {
286 	unsigned long old, *ptr;
287 
288 	ptr = dat_get_and_invalidate(table_root, vaddr, level, &old);
289 	*ptr = old | prot;
290 }
291 
292 void unprotect_dat_entry(void *vaddr, unsigned long prot, enum pgt_level level)
293 {
294 	unsigned long old, *ptr;
295 
296 	ptr = dat_get_and_invalidate(table_root, vaddr, level, &old);
297 	*ptr = old & ~prot;
298 }
299 
300 void protect_range(void *start, unsigned long len, unsigned long prot)
301 {
302 	uintptr_t curr = (uintptr_t)start & PAGE_MASK;
303 
304 	len &= PAGE_MASK;
305 	for (; len; len -= PAGE_SIZE, curr += PAGE_SIZE)
306 		protect_dat_entry((void *)curr, prot, 5);
307 }
308 
309 void unprotect_range(void *start, unsigned long len, unsigned long prot)
310 {
311 	uintptr_t curr = (uintptr_t)start & PAGE_MASK;
312 
313 	len &= PAGE_MASK;
314 	for (; len; len -= PAGE_SIZE, curr += PAGE_SIZE)
315 		unprotect_dat_entry((void *)curr, prot, 5);
316 }
317 
318 static void setup_identity(pgd_t *pgtable, phys_addr_t start_addr,
319 			   phys_addr_t end_addr)
320 {
321 	phys_addr_t cur;
322 
323 	start_addr &= PAGE_MASK;
324 	for (cur = start_addr; true; cur += PAGE_SIZE) {
325 		if (start_addr < end_addr && cur >= end_addr)
326 			break;
327 		if (start_addr > end_addr && cur <= end_addr)
328 			break;
329 		install_page(pgtable, cur, __va(cur));
330 	}
331 }
332 
333 void *setup_mmu(phys_addr_t phys_end, void *unused)
334 {
335 	pgd_t *page_root;
336 
337 	/* allocate a region-1 table */
338 	page_root = pgd_alloc_one();
339 
340 	/* map all physical memory 1:1 */
341 	setup_identity(page_root, 0, phys_end);
342 
343 	/* generate 128MB of invalid adresses at the end (for testing PGM) */
344 	init_alloc_vpage((void *) -(1UL << 27));
345 	setup_identity(page_root, -(1UL << 27), 0);
346 
347 	/* finally enable DAT with the new table */
348 	mmu_enable(page_root);
349 	table_root = page_root;
350 	return page_root;
351 }
352