xref: /kvm-unit-tests/lib/arm/mmu.c (revision 1f0a5c199460c5acf46107d7e062998303b43f77)
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>
9eb225344SAndrew Jones #include <asm/thread_info.h>
10eb225344SAndrew Jones #include <asm/cpumask.h>
118cca5668SAndrew Jones #include <asm/mmu.h>
12c2a95639SPaolo Bonzini #include <asm/setup.h>
13c2a95639SPaolo Bonzini #include <asm/page.h>
14c2a95639SPaolo Bonzini 
15031755dbSPaolo Bonzini #include "alloc_page.h"
16031755dbSPaolo Bonzini #include "vmalloc.h"
17c2a95639SPaolo Bonzini #include <asm/pgtable-hwdef.h>
18c2a95639SPaolo Bonzini #include <asm/pgtable.h>
19153d1936SAndrew Jones 
20db328a24SAndrew Jones extern unsigned long etext;
21db328a24SAndrew Jones 
222f3028cdSAndrew Jones pgd_t *mmu_idmap;
23153d1936SAndrew Jones 
24c33efcf3SAndrew Jones /* CPU 0 starts with disabled MMU */
25c33efcf3SAndrew Jones static cpumask_t mmu_disabled_cpumask = { {1} };
26b141dbacSAndrew Jones unsigned int mmu_disabled_cpu_count = 1;
27c33efcf3SAndrew Jones 
28b141dbacSAndrew Jones bool __mmu_enabled(void)
29153d1936SAndrew Jones {
30c33efcf3SAndrew Jones 	int cpu = current_thread_info()->cpu;
31eb225344SAndrew Jones 
321742c67aSAndrew Jones 	/*
331742c67aSAndrew Jones 	 * mmu_enabled is called from places that are guarding the
341742c67aSAndrew Jones 	 * use of exclusive ops (which require the mmu to be enabled).
351742c67aSAndrew Jones 	 * That means we CANNOT call anything from here that may use a
361742c67aSAndrew Jones 	 * spinlock, atomic bitop, etc., otherwise we'll recurse.
371742c67aSAndrew Jones 	 * [cpumask_]test_bit is safe though.
381742c67aSAndrew Jones 	 */
39c33efcf3SAndrew Jones 	return !cpumask_test_cpu(cpu, &mmu_disabled_cpumask);
40153d1936SAndrew Jones }
41153d1936SAndrew Jones 
421742c67aSAndrew Jones void mmu_mark_enabled(int cpu)
431742c67aSAndrew Jones {
441742c67aSAndrew Jones 	if (cpumask_test_and_clear_cpu(cpu, &mmu_disabled_cpumask))
451742c67aSAndrew Jones 		--mmu_disabled_cpu_count;
461742c67aSAndrew Jones }
471742c67aSAndrew Jones 
481742c67aSAndrew Jones void mmu_mark_disabled(int cpu)
491742c67aSAndrew Jones {
501742c67aSAndrew Jones 	if (!cpumask_test_and_set_cpu(cpu, &mmu_disabled_cpumask))
511742c67aSAndrew Jones 		++mmu_disabled_cpu_count;
521742c67aSAndrew Jones }
531742c67aSAndrew Jones 
54153d1936SAndrew Jones extern void asm_mmu_enable(phys_addr_t pgtable);
55153d1936SAndrew Jones void mmu_enable(pgd_t *pgtable)
56153d1936SAndrew Jones {
5736b50de9SAndrew Jones 	struct thread_info *info = current_thread_info();
58c33efcf3SAndrew Jones 
59153d1936SAndrew Jones 	asm_mmu_enable(__pa(pgtable));
60153d1936SAndrew Jones 	flush_tlb_all();
61b141dbacSAndrew Jones 
6236b50de9SAndrew Jones 	info->pgtable = pgtable;
6336b50de9SAndrew Jones 	mmu_mark_enabled(info->cpu);
64153d1936SAndrew Jones }
65153d1936SAndrew Jones 
66e27b176bSAndrew Jones extern void asm_mmu_disable(void);
67e27b176bSAndrew Jones void mmu_disable(void)
68e27b176bSAndrew Jones {
69c33efcf3SAndrew Jones 	int cpu = current_thread_info()->cpu;
70c33efcf3SAndrew Jones 
71c33efcf3SAndrew Jones 	mmu_mark_disabled(cpu);
72c33efcf3SAndrew Jones 
73e27b176bSAndrew Jones 	asm_mmu_disable();
74e27b176bSAndrew Jones }
75e27b176bSAndrew Jones 
76f8891de2SAndrew Jones static void flush_entry(pgd_t *pgtable, uintptr_t vaddr)
77f8891de2SAndrew Jones {
78f8891de2SAndrew Jones 	pgd_t *pgd = pgd_offset(pgtable, vaddr);
79f8891de2SAndrew Jones 	pmd_t *pmd = pmd_offset(pgd, vaddr);
80f8891de2SAndrew Jones 
81f8891de2SAndrew Jones 	flush_dcache_addr((ulong)pgd);
82f8891de2SAndrew Jones 	flush_dcache_addr((ulong)pmd);
83f8891de2SAndrew Jones 	flush_dcache_addr((ulong)pte_offset(pmd, vaddr));
84f8891de2SAndrew Jones 	flush_tlb_page(vaddr);
85f8891de2SAndrew Jones }
86f8891de2SAndrew Jones 
87031755dbSPaolo Bonzini static pteval_t *get_pte(pgd_t *pgtable, uintptr_t vaddr)
88031755dbSPaolo Bonzini {
89031755dbSPaolo Bonzini 	pgd_t *pgd = pgd_offset(pgtable, vaddr);
90031755dbSPaolo Bonzini 	pmd_t *pmd = pmd_alloc(pgd, vaddr);
91031755dbSPaolo Bonzini 	pte_t *pte = pte_alloc(pmd, vaddr);
92031755dbSPaolo Bonzini 
93031755dbSPaolo Bonzini 	return &pte_val(*pte);
94031755dbSPaolo Bonzini }
95031755dbSPaolo Bonzini 
96031755dbSPaolo Bonzini static pteval_t *install_pte(pgd_t *pgtable, uintptr_t vaddr, pteval_t pte)
97031755dbSPaolo Bonzini {
98031755dbSPaolo Bonzini 	pteval_t *p_pte = get_pte(pgtable, vaddr);
99f8891de2SAndrew Jones 
100031755dbSPaolo Bonzini 	*p_pte = pte;
101f8891de2SAndrew Jones 	flush_entry(pgtable, vaddr);
102031755dbSPaolo Bonzini 	return p_pte;
103031755dbSPaolo Bonzini }
104031755dbSPaolo Bonzini 
105031755dbSPaolo Bonzini static pteval_t *install_page_prot(pgd_t *pgtable, phys_addr_t phys,
106031755dbSPaolo Bonzini 				   uintptr_t vaddr, pgprot_t prot)
107031755dbSPaolo Bonzini {
108031755dbSPaolo Bonzini 	pteval_t pte = phys;
109031755dbSPaolo Bonzini 	pte |= PTE_TYPE_PAGE | PTE_AF | PTE_SHARED;
110031755dbSPaolo Bonzini 	pte |= pgprot_val(prot);
111031755dbSPaolo Bonzini 	return install_pte(pgtable, vaddr, pte);
112031755dbSPaolo Bonzini }
113031755dbSPaolo Bonzini 
114031755dbSPaolo Bonzini pteval_t *install_page(pgd_t *pgtable, phys_addr_t phys, void *virt)
115031755dbSPaolo Bonzini {
116031755dbSPaolo Bonzini 	return install_page_prot(pgtable, phys, (uintptr_t)virt,
117031755dbSPaolo Bonzini 				 __pgprot(PTE_WBWA | PTE_USER));
118031755dbSPaolo Bonzini }
119031755dbSPaolo Bonzini 
120031755dbSPaolo Bonzini phys_addr_t virt_to_pte_phys(pgd_t *pgtable, void *mem)
121031755dbSPaolo Bonzini {
122031755dbSPaolo Bonzini 	return (*get_pte(pgtable, (uintptr_t)mem) & PHYS_MASK & -PAGE_SIZE)
123031755dbSPaolo Bonzini 		+ ((ulong)mem & (PAGE_SIZE - 1));
124031755dbSPaolo Bonzini }
125031755dbSPaolo Bonzini 
126f0671a7bSPaolo Bonzini void mmu_set_range_ptes(pgd_t *pgtable, uintptr_t virt_offset,
127f0671a7bSPaolo Bonzini 			phys_addr_t phys_start, phys_addr_t phys_end,
1282f3028cdSAndrew Jones 			pgprot_t prot)
129153d1936SAndrew Jones {
130f0671a7bSPaolo Bonzini 	phys_addr_t paddr = phys_start & PAGE_MASK;
131f0671a7bSPaolo Bonzini 	uintptr_t vaddr = virt_offset & PAGE_MASK;
132f0671a7bSPaolo Bonzini 	uintptr_t virt_end = phys_end - paddr + vaddr;
1332f3028cdSAndrew Jones 
134031755dbSPaolo Bonzini 	for (; vaddr < virt_end; vaddr += PAGE_SIZE, paddr += PAGE_SIZE)
135031755dbSPaolo Bonzini 		install_page_prot(pgtable, paddr, vaddr, prot);
1362f3028cdSAndrew Jones }
1372f3028cdSAndrew Jones 
138f0671a7bSPaolo Bonzini void mmu_set_range_sect(pgd_t *pgtable, uintptr_t virt_offset,
139f0671a7bSPaolo Bonzini 			phys_addr_t phys_start, phys_addr_t phys_end,
1402f3028cdSAndrew Jones 			pgprot_t prot)
1412f3028cdSAndrew Jones {
142f0671a7bSPaolo Bonzini 	phys_addr_t paddr = phys_start & PGDIR_MASK;
143f0671a7bSPaolo Bonzini 	uintptr_t vaddr = virt_offset & PGDIR_MASK;
144f0671a7bSPaolo Bonzini 	uintptr_t virt_end = phys_end - paddr + vaddr;
1452f3028cdSAndrew Jones 
1462f3028cdSAndrew Jones 	for (; vaddr < virt_end; vaddr += PGDIR_SIZE, paddr += PGDIR_SIZE) {
1472f3028cdSAndrew Jones 		pgd_t *pgd = pgd_offset(pgtable, vaddr);
1482f3028cdSAndrew Jones 		pgd_val(*pgd) = paddr;
1492f3028cdSAndrew Jones 		pgd_val(*pgd) |= PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S;
1502f3028cdSAndrew Jones 		pgd_val(*pgd) |= pgprot_val(prot);
151f8891de2SAndrew Jones 		flush_dcache_addr((ulong)pgd);
152f8891de2SAndrew Jones 		flush_tlb_page(vaddr);
1532f3028cdSAndrew Jones 	}
1542f3028cdSAndrew Jones }
1552f3028cdSAndrew Jones 
156031755dbSPaolo Bonzini void *setup_mmu(phys_addr_t phys_end)
157153d1936SAndrew Jones {
158f0671a7bSPaolo Bonzini 	uintptr_t code_end = (uintptr_t)&etext;
159153d1936SAndrew Jones 
160031755dbSPaolo Bonzini 	/* 0G-1G = I/O, 1G-3G = identity, 3G-4G = vmalloc */
161031755dbSPaolo Bonzini 	if (phys_end > (3ul << 30))
162031755dbSPaolo Bonzini 		phys_end = 3ul << 30;
163031755dbSPaolo Bonzini 
164031755dbSPaolo Bonzini #ifdef __aarch64__
165031755dbSPaolo Bonzini 	init_alloc_vpage((void*)(4ul << 30));
166031755dbSPaolo Bonzini #endif
167031755dbSPaolo Bonzini 
168031755dbSPaolo Bonzini 	mmu_idmap = alloc_page();
169031755dbSPaolo Bonzini 	memset(mmu_idmap, 0, PAGE_SIZE);
170153d1936SAndrew Jones 
171*1f0a5c19SAndrew Jones 	/*
172*1f0a5c19SAndrew Jones 	 * mach-virt I/O regions:
173*1f0a5c19SAndrew Jones 	 *   - The first 1G (arm/arm64)
174*1f0a5c19SAndrew Jones 	 *   - 512M at 256G (arm64, arm uses highmem=off)
175*1f0a5c19SAndrew Jones 	 *   - 512G at 512G (arm64, arm uses highmem=off)
176*1f0a5c19SAndrew Jones 	 */
177*1f0a5c19SAndrew Jones 	mmu_set_range_sect(mmu_idmap,
178*1f0a5c19SAndrew Jones 		0, 0, (1ul << 30),
179f0671a7bSPaolo Bonzini 		__pgprot(PMD_SECT_UNCACHED | PMD_SECT_USER));
180*1f0a5c19SAndrew Jones #ifdef __aarch64__
181*1f0a5c19SAndrew Jones 	mmu_set_range_sect(mmu_idmap,
182*1f0a5c19SAndrew Jones 		(1ul << 38), (1ul << 38), (1ul << 38) | (1ul << 29),
183*1f0a5c19SAndrew Jones 		__pgprot(PMD_SECT_UNCACHED | PMD_SECT_USER));
184*1f0a5c19SAndrew Jones 	mmu_set_range_sect(mmu_idmap,
185*1f0a5c19SAndrew Jones 		(1ul << 39), (1ul << 39), (1ul << 40),
186*1f0a5c19SAndrew Jones 		__pgprot(PMD_SECT_UNCACHED | PMD_SECT_USER));
187*1f0a5c19SAndrew Jones #endif
188153d1936SAndrew Jones 
189db328a24SAndrew Jones 	/* armv8 requires code shared between EL1 and EL0 to be read-only */
1902f3028cdSAndrew Jones 	mmu_set_range_ptes(mmu_idmap, PHYS_OFFSET,
191db328a24SAndrew Jones 		PHYS_OFFSET, code_end,
192db328a24SAndrew Jones 		__pgprot(PTE_WBWA | PTE_RDONLY | PTE_USER));
193db328a24SAndrew Jones 
194db328a24SAndrew Jones 	mmu_set_range_ptes(mmu_idmap, code_end,
195db328a24SAndrew Jones 		code_end, phys_end,
1962f3028cdSAndrew Jones 		__pgprot(PTE_WBWA | PTE_USER));
197153d1936SAndrew Jones 
1982f3028cdSAndrew Jones 	mmu_enable(mmu_idmap);
199031755dbSPaolo Bonzini 	return mmu_idmap;
200153d1936SAndrew Jones }
201f02b6363SAndrew Jones 
202f02b6363SAndrew Jones phys_addr_t __virt_to_phys(unsigned long addr)
203f02b6363SAndrew Jones {
204f02b6363SAndrew Jones 	if (mmu_enabled()) {
205f02b6363SAndrew Jones 		pgd_t *pgtable = current_thread_info()->pgtable;
206f02b6363SAndrew Jones 		return virt_to_pte_phys(pgtable, (void *)addr);
207f02b6363SAndrew Jones 	}
208f02b6363SAndrew Jones 	return addr;
209f02b6363SAndrew Jones }
210f02b6363SAndrew Jones 
211f02b6363SAndrew Jones unsigned long __phys_to_virt(phys_addr_t addr)
212f02b6363SAndrew Jones {
213f02b6363SAndrew Jones 	/*
214f02b6363SAndrew Jones 	 * We don't guarantee that phys_to_virt(virt_to_phys(vaddr)) == vaddr, but
215f02b6363SAndrew Jones 	 * the default page tables do identity map all physical addresses, which
216f02b6363SAndrew Jones 	 * means phys_to_virt(virt_to_phys((void *)paddr)) == paddr.
217f02b6363SAndrew Jones 	 */
218f02b6363SAndrew Jones 	assert(!mmu_enabled() || __virt_to_phys(addr) == addr);
219f02b6363SAndrew Jones 	return addr;
220f02b6363SAndrew Jones }
221