xref: /kvm-unit-tests/lib/arm/mmu.c (revision 2f3028cdf31c377a2961ca19ff64272e4ec6eb5e)
1153d1936SAndrew Jones /*
2153d1936SAndrew Jones  * MMU enable and page table manipulation functions
3153d1936SAndrew Jones  *
4153d1936SAndrew Jones  * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
5153d1936SAndrew Jones  *
6153d1936SAndrew Jones  * This work is licensed under the terms of the GNU LGPL, version 2.
7153d1936SAndrew Jones  */
88cca5668SAndrew Jones #include <asm/setup.h>
98cca5668SAndrew Jones #include <asm/mmu.h>
10153d1936SAndrew Jones 
11*2f3028cdSAndrew Jones pgd_t *mmu_idmap;
12153d1936SAndrew Jones 
13*2f3028cdSAndrew Jones static bool mmu_on;
14153d1936SAndrew Jones bool mmu_enabled(void)
15153d1936SAndrew Jones {
16153d1936SAndrew Jones 	return mmu_on;
17153d1936SAndrew Jones }
18153d1936SAndrew Jones 
19153d1936SAndrew Jones extern void asm_mmu_enable(phys_addr_t pgtable);
20153d1936SAndrew Jones void mmu_enable(pgd_t *pgtable)
21153d1936SAndrew Jones {
22153d1936SAndrew Jones 	asm_mmu_enable(__pa(pgtable));
23153d1936SAndrew Jones 	flush_tlb_all();
24153d1936SAndrew Jones 	mmu_on = true;
25153d1936SAndrew Jones }
26153d1936SAndrew Jones 
27*2f3028cdSAndrew Jones void mmu_set_range_ptes(pgd_t *pgtable, unsigned long virt_offset,
28*2f3028cdSAndrew Jones 			unsigned long phys_start, unsigned long phys_end,
29*2f3028cdSAndrew Jones 			pgprot_t prot)
30153d1936SAndrew Jones {
31*2f3028cdSAndrew Jones 	unsigned long vaddr = virt_offset & PAGE_MASK;
32*2f3028cdSAndrew Jones 	unsigned long paddr = phys_start & PAGE_MASK;
33*2f3028cdSAndrew Jones 	unsigned long virt_end = phys_end - paddr + vaddr;
34*2f3028cdSAndrew Jones 
35*2f3028cdSAndrew Jones 	for (; vaddr < virt_end; vaddr += PAGE_SIZE, paddr += PAGE_SIZE) {
36*2f3028cdSAndrew Jones 		pgd_t *pgd = pgd_offset(pgtable, vaddr);
37*2f3028cdSAndrew Jones 		pud_t *pud = pud_alloc(pgd, vaddr);
38*2f3028cdSAndrew Jones 		pmd_t *pmd = pmd_alloc(pud, vaddr);
39*2f3028cdSAndrew Jones 		pte_t *pte = pte_alloc(pmd, vaddr);
40*2f3028cdSAndrew Jones 
41*2f3028cdSAndrew Jones 		pte_val(*pte) = paddr;
42*2f3028cdSAndrew Jones 		pte_val(*pte) |= PTE_TYPE_PAGE | PTE_AF | PTE_SHARED;
43*2f3028cdSAndrew Jones 		pte_val(*pte) |= pgprot_val(prot);
44*2f3028cdSAndrew Jones 	}
45*2f3028cdSAndrew Jones }
46*2f3028cdSAndrew Jones 
47*2f3028cdSAndrew Jones void mmu_set_range_sect(pgd_t *pgtable, unsigned long virt_offset,
48*2f3028cdSAndrew Jones 			unsigned long phys_start, unsigned long phys_end,
49*2f3028cdSAndrew Jones 			pgprot_t prot)
50*2f3028cdSAndrew Jones {
51*2f3028cdSAndrew Jones 	unsigned long vaddr = virt_offset & PGDIR_MASK;
52*2f3028cdSAndrew Jones 	unsigned long paddr = phys_start & PGDIR_MASK;
53*2f3028cdSAndrew Jones 	unsigned long virt_end = phys_end - paddr + vaddr;
54*2f3028cdSAndrew Jones 
55*2f3028cdSAndrew Jones 	for (; vaddr < virt_end; vaddr += PGDIR_SIZE, paddr += PGDIR_SIZE) {
56*2f3028cdSAndrew Jones 		pgd_t *pgd = pgd_offset(pgtable, vaddr);
57*2f3028cdSAndrew Jones 		pgd_val(*pgd) = paddr;
58*2f3028cdSAndrew Jones 		pgd_val(*pgd) |= PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S;
59*2f3028cdSAndrew Jones 		pgd_val(*pgd) |= pgprot_val(prot);
60*2f3028cdSAndrew Jones 	}
61*2f3028cdSAndrew Jones }
62*2f3028cdSAndrew Jones 
63*2f3028cdSAndrew Jones 
64*2f3028cdSAndrew Jones void mmu_init_io_sect(pgd_t *pgtable, unsigned long virt_offset)
65*2f3028cdSAndrew Jones {
66*2f3028cdSAndrew Jones 	mmu_set_range_sect(pgtable, virt_offset,
67*2f3028cdSAndrew Jones 		PHYS_IO_OFFSET, PHYS_IO_END,
68*2f3028cdSAndrew Jones 		__pgprot(PMD_SECT_UNCACHED | PMD_SECT_USER));
69153d1936SAndrew Jones }
70153d1936SAndrew Jones 
71153d1936SAndrew Jones void mmu_enable_idmap(void)
72153d1936SAndrew Jones {
73*2f3028cdSAndrew Jones 	unsigned long phys_end = sizeof(long) == 8 || !(PHYS_END >> 32)
74*2f3028cdSAndrew Jones 						? PHYS_END : 0xfffff000;
75153d1936SAndrew Jones 
76*2f3028cdSAndrew Jones 	mmu_idmap = pgd_alloc();
77153d1936SAndrew Jones 
78*2f3028cdSAndrew Jones 	mmu_init_io_sect(mmu_idmap, PHYS_IO_OFFSET);
79153d1936SAndrew Jones 
80*2f3028cdSAndrew Jones 	mmu_set_range_ptes(mmu_idmap, PHYS_OFFSET,
81*2f3028cdSAndrew Jones 		PHYS_OFFSET, phys_end,
82*2f3028cdSAndrew Jones 		__pgprot(PTE_WBWA | PTE_USER));
83153d1936SAndrew Jones 
84*2f3028cdSAndrew Jones 	mmu_enable(mmu_idmap);
85153d1936SAndrew Jones }
86