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