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