1
2
3
4
5
6
7
8#include <linux/mm.h>
9#include <linux/hugetlb.h>
10
11static inline unsigned long __pte_to_rste(pte_t pte)
12{
13 int none, young, prot;
14 unsigned long rste;
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 if (pte_present(pte)) {
36 rste = pte_val(pte) & PAGE_MASK;
37 if (pte_val(pte) & _PAGE_INVALID)
38 rste |= _SEGMENT_ENTRY_INVALID;
39 none = (pte_val(pte) & _PAGE_PRESENT) &&
40 !(pte_val(pte) & _PAGE_READ) &&
41 !(pte_val(pte) & _PAGE_WRITE);
42 prot = (pte_val(pte) & _PAGE_PROTECT) &&
43 !(pte_val(pte) & _PAGE_WRITE);
44 young = pte_val(pte) & _PAGE_YOUNG;
45 if (none || young)
46 rste |= _SEGMENT_ENTRY_YOUNG;
47 if (prot || (none && young))
48 rste |= _SEGMENT_ENTRY_PROTECT;
49 } else
50 rste = _SEGMENT_ENTRY_INVALID;
51 return rste;
52}
53
54static inline pte_t __rste_to_pte(unsigned long rste)
55{
56 int present;
57 pte_t pte;
58
59 if ((rste & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3)
60 present = pud_present(__pud(rste));
61 else
62 present = pmd_present(__pmd(rste));
63
64
65
66
67
68
69
70
71
72
73
74
75
76 if (present) {
77 pte_val(pte) = _PAGE_PRESENT | _PAGE_LARGE | _PAGE_DIRTY |
78 (rste & PAGE_MASK);
79 if (rste & _SEGMENT_ENTRY_INVALID)
80 pte_val(pte) |= _PAGE_INVALID;
81 if (pmd_prot_none(__pmd(rste))) {
82 if (rste & _SEGMENT_ENTRY_PROTECT)
83 pte_val(pte) |= _PAGE_YOUNG;
84 } else {
85 pte_val(pte) |= _PAGE_READ;
86 if (rste & _SEGMENT_ENTRY_PROTECT)
87 pte_val(pte) |= _PAGE_PROTECT;
88 else
89 pte_val(pte) |= _PAGE_WRITE;
90 if (rste & _SEGMENT_ENTRY_YOUNG)
91 pte_val(pte) |= _PAGE_YOUNG;
92 }
93 } else
94 pte_val(pte) = _PAGE_INVALID;
95 return pte;
96}
97
98void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
99 pte_t *ptep, pte_t pte)
100{
101 unsigned long rste = __pte_to_rste(pte);
102
103
104 if ((pte_val(*ptep) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3)
105 rste |= _REGION_ENTRY_TYPE_R3 | _REGION3_ENTRY_LARGE;
106 else {
107 if (!MACHINE_HAS_HPAGE) {
108 rste &= ~_SEGMENT_ENTRY_ORIGIN;
109 rste |= pte_page(pte)[1].index;
110 } else
111 rste |= _SEGMENT_ENTRY_LARGE | _SEGMENT_ENTRY_CO;
112 }
113 pte_val(*ptep) = rste;
114}
115
116pte_t huge_ptep_get(pte_t *ptep)
117{
118 unsigned long origin, rste;
119
120 rste = pte_val(*ptep);
121 if ((pte_val(*ptep) & _REGION_ENTRY_TYPE_MASK) < _REGION_ENTRY_TYPE_R3)
122 if (!MACHINE_HAS_HPAGE && pmd_present(__pmd(rste))) {
123 origin = rste & _SEGMENT_ENTRY_ORIGIN;
124 rste &= ~_SEGMENT_ENTRY_ORIGIN;
125 rste |= *(unsigned long *) origin;
126 }
127 return __rste_to_pte(rste);
128}
129
130pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
131 unsigned long addr, pte_t *ptep)
132{
133 pte_t pte = huge_ptep_get(ptep);
134 pmd_t *pmdp = (pmd_t *) ptep;
135 pud_t *pudp = (pud_t *) ptep;
136
137 if ((pte_val(*ptep) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) {
138 __pudp_idte(addr, pudp);
139 pud_val(*pudp) = _REGION3_ENTRY_EMPTY;
140 } else {
141 if (MACHINE_HAS_IDTE)
142 __pmd_idte(addr, pmdp);
143 else
144 __pmd_csp(pmdp);
145 pmd_val(*pmdp) = _SEGMENT_ENTRY_EMPTY;
146 }
147 return pte;
148}
149
150int arch_prepare_hugepage(struct page *page)
151{
152 unsigned long addr = page_to_phys(page);
153 pte_t pte;
154 pte_t *ptep;
155 int i;
156
157 if (MACHINE_HAS_HPAGE)
158 return 0;
159
160 ptep = (pte_t *) pte_alloc_one(&init_mm, addr);
161 if (!ptep)
162 return -ENOMEM;
163
164 pte_val(pte) = addr;
165 for (i = 0; i < PTRS_PER_PTE; i++) {
166 set_pte_at(&init_mm, addr + i * PAGE_SIZE, ptep + i, pte);
167 pte_val(pte) += PAGE_SIZE;
168 }
169 page[1].index = (unsigned long) ptep;
170 return 0;
171}
172
173void arch_release_hugepage(struct page *page)
174{
175 pte_t *ptep;
176
177 if (MACHINE_HAS_HPAGE)
178 return;
179
180 ptep = (pte_t *) page[1].index;
181 if (!ptep)
182 return;
183 clear_table((unsigned long *) ptep, _PAGE_INVALID,
184 PTRS_PER_PTE * sizeof(pte_t));
185 page_table_free(&init_mm, (unsigned long *) ptep);
186 page[1].index = 0;
187}
188
189pte_t *huge_pte_alloc(struct mm_struct *mm,
190 unsigned long addr, unsigned long sz)
191{
192 pgd_t *pgdp;
193 pud_t *pudp;
194 pmd_t *pmdp = NULL;
195
196 pgdp = pgd_offset(mm, addr);
197 pudp = pud_alloc(mm, pgdp, addr);
198 if (pudp) {
199 if (sz == PUD_SIZE)
200 return (pte_t *) pudp;
201 else if (sz == PMD_SIZE)
202 pmdp = pmd_alloc(mm, pudp, addr);
203 }
204 return (pte_t *) pmdp;
205}
206
207pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr)
208{
209 pgd_t *pgdp;
210 pud_t *pudp;
211 pmd_t *pmdp = NULL;
212
213 pgdp = pgd_offset(mm, addr);
214 if (pgd_present(*pgdp)) {
215 pudp = pud_offset(pgdp, addr);
216 if (pud_present(*pudp)) {
217 if (pud_large(*pudp))
218 return (pte_t *) pudp;
219 pmdp = pmd_offset(pudp, addr);
220 }
221 }
222 return (pte_t *) pmdp;
223}
224
225int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep)
226{
227 return 0;
228}
229
230int pmd_huge(pmd_t pmd)
231{
232 if (!MACHINE_HAS_HPAGE)
233 return 0;
234
235 return !!(pmd_val(pmd) & _SEGMENT_ENTRY_LARGE);
236}
237
238int pud_huge(pud_t pud)
239{
240 return pud_large(pud);
241}
242
243struct page *
244follow_huge_pud(struct mm_struct *mm, unsigned long address,
245 pud_t *pud, int flags)
246{
247 if (flags & FOLL_GET)
248 return NULL;
249
250 return pud_page(*pud) + ((address & ~PUD_MASK) >> PAGE_SHIFT);
251}
252
253static __init int setup_hugepagesz(char *opt)
254{
255 unsigned long size;
256 char *string = opt;
257
258 size = memparse(opt, &opt);
259 if (MACHINE_HAS_EDAT1 && size == PMD_SIZE) {
260 hugetlb_add_hstate(PMD_SHIFT - PAGE_SHIFT);
261 } else if (MACHINE_HAS_EDAT2 && size == PUD_SIZE) {
262 hugetlb_add_hstate(PUD_SHIFT - PAGE_SHIFT);
263 } else {
264 pr_err("hugepagesz= specifies an unsupported page size %s\n",
265 string);
266 return 0;
267 }
268 return 1;
269}
270__setup("hugepagesz=", setup_hugepagesz);
271