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