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 : 3101518 : 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 : 3101518 : new_pgd = __pgd_alloc();
38 [ + + ]: 3101524 : if (!new_pgd)
39 : : goto no_pgd;
40 : :
41 : 3101506 : memset(new_pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
42 : :
43 : : /*
44 : : * Copy over the kernel and IO PGD entries
45 : : */
46 : 3101506 : init_pgd = pgd_offset_k(0);
47 : 3101506 : 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 : 3101506 : 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 [ - + ]: 3101506 : 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 : 3101520 : 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 : 3081958 : 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 [ + + ]: 3081958 : if (!pgd_base)
124 : 3081998 : 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 [ + + ]: 3081930 : if (pmd_none_or_clear_bad(pmd))
136 : : goto no_pmd;
137 : :
138 : 3013410 : pte = pmd_pgtable(*pmd);
139 : 3013410 : pmd_clear(pmd);
140 : 3013408 : 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 : 3081936 : __pgd_free(pgd_base);
171 : : }
|