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