xref: /kvm-unit-tests/lib/arm/mmu.c (revision b141dbac8717d6564edeb5c11f0d007b6257f661)
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>
12153d1936SAndrew Jones 
13db328a24SAndrew Jones extern unsigned long etext;
14db328a24SAndrew Jones 
152f3028cdSAndrew Jones pgd_t *mmu_idmap;
16153d1936SAndrew Jones 
17c33efcf3SAndrew Jones /* CPU 0 starts with disabled MMU */
18c33efcf3SAndrew Jones static cpumask_t mmu_disabled_cpumask = { {1} };
19*b141dbacSAndrew Jones unsigned int mmu_disabled_cpu_count = 1;
20c33efcf3SAndrew Jones 
21*b141dbacSAndrew Jones bool __mmu_enabled(void)
22153d1936SAndrew Jones {
23c33efcf3SAndrew Jones 	int cpu = current_thread_info()->cpu;
24eb225344SAndrew Jones 
25c33efcf3SAndrew Jones 	return !cpumask_test_cpu(cpu, &mmu_disabled_cpumask);
26153d1936SAndrew Jones }
27153d1936SAndrew Jones 
28153d1936SAndrew Jones extern void asm_mmu_enable(phys_addr_t pgtable);
29153d1936SAndrew Jones void mmu_enable(pgd_t *pgtable)
30153d1936SAndrew Jones {
31c33efcf3SAndrew Jones 	int cpu = current_thread_info()->cpu;
32c33efcf3SAndrew Jones 
33153d1936SAndrew Jones 	asm_mmu_enable(__pa(pgtable));
34153d1936SAndrew Jones 	flush_tlb_all();
35*b141dbacSAndrew Jones 
36*b141dbacSAndrew Jones 	if (cpumask_test_and_clear_cpu(cpu, &mmu_disabled_cpumask))
37*b141dbacSAndrew Jones 		--mmu_disabled_cpu_count;
38c33efcf3SAndrew Jones }
39c33efcf3SAndrew Jones 
40c33efcf3SAndrew Jones void mmu_mark_disabled(int cpu)
41c33efcf3SAndrew Jones {
42*b141dbacSAndrew Jones 	if (!cpumask_test_and_set_cpu(cpu, &mmu_disabled_cpumask))
43*b141dbacSAndrew Jones 		++mmu_disabled_cpu_count;
44153d1936SAndrew Jones }
45153d1936SAndrew Jones 
46e27b176bSAndrew Jones extern void asm_mmu_disable(void);
47e27b176bSAndrew Jones void mmu_disable(void)
48e27b176bSAndrew Jones {
49c33efcf3SAndrew Jones 	int cpu = current_thread_info()->cpu;
50c33efcf3SAndrew Jones 
51c33efcf3SAndrew Jones 	mmu_mark_disabled(cpu);
52c33efcf3SAndrew Jones 
53e27b176bSAndrew Jones 	asm_mmu_disable();
54e27b176bSAndrew Jones }
55e27b176bSAndrew Jones 
562f3028cdSAndrew Jones void mmu_set_range_ptes(pgd_t *pgtable, unsigned long virt_offset,
572f3028cdSAndrew Jones 			unsigned long phys_start, unsigned long phys_end,
582f3028cdSAndrew Jones 			pgprot_t prot)
59153d1936SAndrew Jones {
602f3028cdSAndrew Jones 	unsigned long vaddr = virt_offset & PAGE_MASK;
612f3028cdSAndrew Jones 	unsigned long paddr = phys_start & PAGE_MASK;
622f3028cdSAndrew Jones 	unsigned long virt_end = phys_end - paddr + vaddr;
632f3028cdSAndrew Jones 
642f3028cdSAndrew Jones 	for (; vaddr < virt_end; vaddr += PAGE_SIZE, paddr += PAGE_SIZE) {
652f3028cdSAndrew Jones 		pgd_t *pgd = pgd_offset(pgtable, vaddr);
662f3028cdSAndrew Jones 		pud_t *pud = pud_alloc(pgd, vaddr);
672f3028cdSAndrew Jones 		pmd_t *pmd = pmd_alloc(pud, vaddr);
682f3028cdSAndrew Jones 		pte_t *pte = pte_alloc(pmd, vaddr);
692f3028cdSAndrew Jones 
702f3028cdSAndrew Jones 		pte_val(*pte) = paddr;
712f3028cdSAndrew Jones 		pte_val(*pte) |= PTE_TYPE_PAGE | PTE_AF | PTE_SHARED;
722f3028cdSAndrew Jones 		pte_val(*pte) |= pgprot_val(prot);
732f3028cdSAndrew Jones 	}
742f3028cdSAndrew Jones }
752f3028cdSAndrew Jones 
762f3028cdSAndrew Jones void mmu_set_range_sect(pgd_t *pgtable, unsigned long virt_offset,
772f3028cdSAndrew Jones 			unsigned long phys_start, unsigned long phys_end,
782f3028cdSAndrew Jones 			pgprot_t prot)
792f3028cdSAndrew Jones {
802f3028cdSAndrew Jones 	unsigned long vaddr = virt_offset & PGDIR_MASK;
812f3028cdSAndrew Jones 	unsigned long paddr = phys_start & PGDIR_MASK;
822f3028cdSAndrew Jones 	unsigned long virt_end = phys_end - paddr + vaddr;
832f3028cdSAndrew Jones 
842f3028cdSAndrew Jones 	for (; vaddr < virt_end; vaddr += PGDIR_SIZE, paddr += PGDIR_SIZE) {
852f3028cdSAndrew Jones 		pgd_t *pgd = pgd_offset(pgtable, vaddr);
862f3028cdSAndrew Jones 		pgd_val(*pgd) = paddr;
872f3028cdSAndrew Jones 		pgd_val(*pgd) |= PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S;
882f3028cdSAndrew Jones 		pgd_val(*pgd) |= pgprot_val(prot);
892f3028cdSAndrew Jones 	}
902f3028cdSAndrew Jones }
912f3028cdSAndrew Jones 
922f3028cdSAndrew Jones 
932f3028cdSAndrew Jones void mmu_init_io_sect(pgd_t *pgtable, unsigned long virt_offset)
942f3028cdSAndrew Jones {
952f3028cdSAndrew Jones 	mmu_set_range_sect(pgtable, virt_offset,
962f3028cdSAndrew Jones 		PHYS_IO_OFFSET, PHYS_IO_END,
972f3028cdSAndrew Jones 		__pgprot(PMD_SECT_UNCACHED | PMD_SECT_USER));
98153d1936SAndrew Jones }
99153d1936SAndrew Jones 
100153d1936SAndrew Jones void mmu_enable_idmap(void)
101153d1936SAndrew Jones {
1022f3028cdSAndrew Jones 	unsigned long phys_end = sizeof(long) == 8 || !(PHYS_END >> 32)
1032f3028cdSAndrew Jones 						? PHYS_END : 0xfffff000;
104db328a24SAndrew Jones 	unsigned long code_end = (unsigned long)&etext;
105153d1936SAndrew Jones 
1062f3028cdSAndrew Jones 	mmu_idmap = pgd_alloc();
107153d1936SAndrew Jones 
1082f3028cdSAndrew Jones 	mmu_init_io_sect(mmu_idmap, PHYS_IO_OFFSET);
109153d1936SAndrew Jones 
110db328a24SAndrew Jones 	/* armv8 requires code shared between EL1 and EL0 to be read-only */
1112f3028cdSAndrew Jones 	mmu_set_range_ptes(mmu_idmap, PHYS_OFFSET,
112db328a24SAndrew Jones 		PHYS_OFFSET, code_end,
113db328a24SAndrew Jones 		__pgprot(PTE_WBWA | PTE_RDONLY | PTE_USER));
114db328a24SAndrew Jones 
115db328a24SAndrew Jones 	mmu_set_range_ptes(mmu_idmap, code_end,
116db328a24SAndrew Jones 		code_end, phys_end,
1172f3028cdSAndrew Jones 		__pgprot(PTE_WBWA | PTE_USER));
118153d1936SAndrew Jones 
1192f3028cdSAndrew Jones 	mmu_enable(mmu_idmap);
120153d1936SAndrew Jones }
121