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