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