xref: /kvm-unit-tests/lib/arm/mmu.c (revision 30b1bc86d937b6197d85590dbe92f6b9a05d45ad)
1 /*
2  * MMU enable and page table manipulation functions
3  *
4  * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
5  *
6  * This work is licensed under the terms of the GNU LGPL, version 2.
7  */
8 #include <asm/setup.h>
9 #include <asm/mmu.h>
10 
11 pgd_t *mmu_idmap;
12 
13 static bool mmu_on;
14 bool mmu_enabled(void)
15 {
16 	return mmu_on;
17 }
18 
19 extern void asm_mmu_enable(phys_addr_t pgtable);
20 void mmu_enable(pgd_t *pgtable)
21 {
22 	asm_mmu_enable(__pa(pgtable));
23 	flush_tlb_all();
24 	mmu_on = true;
25 }
26 
27 void mmu_set_range_ptes(pgd_t *pgtable, unsigned long virt_offset,
28 			unsigned long phys_start, unsigned long phys_end,
29 			pgprot_t prot)
30 {
31 	unsigned long vaddr = virt_offset & PAGE_MASK;
32 	unsigned long paddr = phys_start & PAGE_MASK;
33 	unsigned long virt_end = phys_end - paddr + vaddr;
34 
35 	for (; vaddr < virt_end; vaddr += PAGE_SIZE, paddr += PAGE_SIZE) {
36 		pgd_t *pgd = pgd_offset(pgtable, vaddr);
37 		pud_t *pud = pud_alloc(pgd, vaddr);
38 		pmd_t *pmd = pmd_alloc(pud, vaddr);
39 		pte_t *pte = pte_alloc(pmd, vaddr);
40 
41 		pte_val(*pte) = paddr;
42 		pte_val(*pte) |= PTE_TYPE_PAGE | PTE_AF | PTE_SHARED;
43 		pte_val(*pte) |= pgprot_val(prot);
44 	}
45 }
46 
47 void mmu_set_range_sect(pgd_t *pgtable, unsigned long virt_offset,
48 			unsigned long phys_start, unsigned long phys_end,
49 			pgprot_t prot)
50 {
51 	unsigned long vaddr = virt_offset & PGDIR_MASK;
52 	unsigned long paddr = phys_start & PGDIR_MASK;
53 	unsigned long virt_end = phys_end - paddr + vaddr;
54 
55 	for (; vaddr < virt_end; vaddr += PGDIR_SIZE, paddr += PGDIR_SIZE) {
56 		pgd_t *pgd = pgd_offset(pgtable, vaddr);
57 		pgd_val(*pgd) = paddr;
58 		pgd_val(*pgd) |= PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S;
59 		pgd_val(*pgd) |= pgprot_val(prot);
60 	}
61 }
62 
63 
64 void mmu_init_io_sect(pgd_t *pgtable, unsigned long virt_offset)
65 {
66 	mmu_set_range_sect(pgtable, virt_offset,
67 		PHYS_IO_OFFSET, PHYS_IO_END,
68 		__pgprot(PMD_SECT_UNCACHED | PMD_SECT_USER));
69 }
70 
71 void mmu_enable_idmap(void)
72 {
73 	unsigned long phys_end = sizeof(long) == 8 || !(PHYS_END >> 32)
74 						? PHYS_END : 0xfffff000;
75 
76 	mmu_idmap = pgd_alloc();
77 
78 	mmu_init_io_sect(mmu_idmap, PHYS_IO_OFFSET);
79 
80 	mmu_set_range_ptes(mmu_idmap, PHYS_OFFSET,
81 		PHYS_OFFSET, phys_end,
82 		__pgprot(PTE_WBWA | PTE_USER));
83 
84 	mmu_enable(mmu_idmap);
85 }
86