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