Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only 2 : : /* 3 : : * linux/arch/arm/mm/pgd.c 4 : : * 5 : : * Copyright (C) 1998-2005 Russell King 6 : : */ 7 : : #include <linux/mm.h> 8 : : #include <linux/gfp.h> 9 : : #include <linux/highmem.h> 10 : : #include <linux/slab.h> 11 : : 12 : : #include <asm/cp15.h> 13 : : #include <asm/pgalloc.h> 14 : : #include <asm/page.h> 15 : : #include <asm/tlbflush.h> 16 : : 17 : : #include "mm.h" 18 : : 19 : : #ifdef CONFIG_ARM_LPAE 20 : : #define __pgd_alloc() kmalloc_array(PTRS_PER_PGD, sizeof(pgd_t), GFP_KERNEL) 21 : : #define __pgd_free(pgd) kfree(pgd) 22 : : #else 23 : : #define __pgd_alloc() (pgd_t *)__get_free_pages(GFP_KERNEL, 2) 24 : : #define __pgd_free(pgd) free_pages((unsigned long)pgd, 2) 25 : : #endif 26 : : 27 : : /* 28 : : * need to get a 16k page for level 1 29 : : */ 30 : 3 : pgd_t *pgd_alloc(struct mm_struct *mm) 31 : : { 32 : : pgd_t *new_pgd, *init_pgd; 33 : : pud_t *new_pud, *init_pud; 34 : : pmd_t *new_pmd, *init_pmd; 35 : : pte_t *new_pte, *init_pte; 36 : : 37 : 3 : new_pgd = __pgd_alloc(); 38 : 3 : if (!new_pgd) 39 : : goto no_pgd; 40 : : 41 : 3 : memset(new_pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); 42 : : 43 : : /* 44 : : * Copy over the kernel and IO PGD entries 45 : : */ 46 : 3 : init_pgd = pgd_offset_k(0); 47 : 3 : memcpy(new_pgd + USER_PTRS_PER_PGD, init_pgd + USER_PTRS_PER_PGD, 48 : : (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); 49 : : 50 : 3 : clean_dcache_area(new_pgd, PTRS_PER_PGD * sizeof(pgd_t)); 51 : : 52 : : #ifdef CONFIG_ARM_LPAE 53 : : /* 54 : : * Allocate PMD table for modules and pkmap mappings. 55 : : */ 56 : : new_pud = pud_alloc(mm, new_pgd + pgd_index(MODULES_VADDR), 57 : : MODULES_VADDR); 58 : : if (!new_pud) 59 : : goto no_pud; 60 : : 61 : : new_pmd = pmd_alloc(mm, new_pud, 0); 62 : : if (!new_pmd) 63 : : goto no_pmd; 64 : : #endif 65 : : 66 : 3 : if (!vectors_high()) { 67 : : /* 68 : : * On ARM, first page must always be allocated since it 69 : : * contains the machine vectors. The vectors are always high 70 : : * with LPAE. 71 : : */ 72 : : new_pud = pud_alloc(mm, new_pgd, 0); 73 : 0 : if (!new_pud) 74 : : goto no_pud; 75 : : 76 : : new_pmd = pmd_alloc(mm, new_pud, 0); 77 : 0 : if (!new_pmd) 78 : : goto no_pmd; 79 : : 80 : 0 : new_pte = pte_alloc_map(mm, new_pmd, 0); 81 : 0 : if (!new_pte) 82 : : goto no_pte; 83 : : 84 : : #ifndef CONFIG_ARM_LPAE 85 : : /* 86 : : * Modify the PTE pointer to have the correct domain. This 87 : : * needs to be the vectors domain to avoid the low vectors 88 : : * being unmapped. 89 : : */ 90 : 0 : pmd_val(*new_pmd) &= ~PMD_DOMAIN_MASK; 91 : 0 : pmd_val(*new_pmd) |= PMD_DOMAIN(DOMAIN_VECTORS); 92 : : #endif 93 : : 94 : : init_pud = pud_offset(init_pgd, 0); 95 : : init_pmd = pmd_offset(init_pud, 0); 96 : 0 : init_pte = pte_offset_map(init_pmd, 0); 97 : 0 : set_pte_ext(new_pte + 0, init_pte[0], 0); 98 : 0 : set_pte_ext(new_pte + 1, init_pte[1], 0); 99 : : pte_unmap(init_pte); 100 : : pte_unmap(new_pte); 101 : : } 102 : : 103 : 3 : return new_pgd; 104 : : 105 : : no_pte: 106 : : pmd_free(mm, new_pmd); 107 : : mm_dec_nr_pmds(mm); 108 : : no_pmd: 109 : : pud_free(mm, new_pud); 110 : : no_pud: 111 : 0 : __pgd_free(new_pgd); 112 : : no_pgd: 113 : : return NULL; 114 : : } 115 : : 116 : 3 : void pgd_free(struct mm_struct *mm, pgd_t *pgd_base) 117 : : { 118 : : pgd_t *pgd; 119 : : pud_t *pud; 120 : : pmd_t *pmd; 121 : : pgtable_t pte; 122 : : 123 : 3 : if (!pgd_base) 124 : 3 : return; 125 : : 126 : : pgd = pgd_base + pgd_index(0); 127 : : if (pgd_none_or_clear_bad(pgd)) 128 : : goto no_pgd; 129 : : 130 : : pud = pud_offset(pgd, 0); 131 : : if (pud_none_or_clear_bad(pud)) 132 : : goto no_pud; 133 : : 134 : : pmd = pmd_offset(pud, 0); 135 : 3 : if (pmd_none_or_clear_bad(pmd)) 136 : : goto no_pmd; 137 : : 138 : 3 : pte = pmd_pgtable(*pmd); 139 : 3 : pmd_clear(pmd); 140 : 3 : pte_free(mm, pte); 141 : : mm_dec_nr_ptes(mm); 142 : : no_pmd: 143 : : pud_clear(pud); 144 : : pmd_free(mm, pmd); 145 : : mm_dec_nr_pmds(mm); 146 : : no_pud: 147 : : pgd_clear(pgd); 148 : : pud_free(mm, pud); 149 : : no_pgd: 150 : : #ifdef CONFIG_ARM_LPAE 151 : : /* 152 : : * Free modules/pkmap or identity pmd tables. 153 : : */ 154 : : for (pgd = pgd_base; pgd < pgd_base + PTRS_PER_PGD; pgd++) { 155 : : if (pgd_none_or_clear_bad(pgd)) 156 : : continue; 157 : : if (pgd_val(*pgd) & L_PGD_SWAPPER) 158 : : continue; 159 : : pud = pud_offset(pgd, 0); 160 : : if (pud_none_or_clear_bad(pud)) 161 : : continue; 162 : : pmd = pmd_offset(pud, 0); 163 : : pud_clear(pud); 164 : : pmd_free(mm, pmd); 165 : : mm_dec_nr_pmds(mm); 166 : : pgd_clear(pgd); 167 : : pud_free(mm, pud); 168 : : } 169 : : #endif 170 : 3 : __pgd_free(pgd_base); 171 : : }