diff options
author | Ingo Molnar <mingo@elte.hu> | 2008-01-30 07:34:09 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-01-30 07:34:09 -0500 |
commit | 86f03989d99cfa2e1216cdd7aa996852236909cf (patch) | |
tree | 6fae63f51c4adf08f94975b48e656b31c6bced62 /arch/x86/mm/pageattr.c | |
parent | aba8391f7323294e88e3a665513434aba4042a7d (diff) |
x86: cpa: fix the self-test
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/x86/mm/pageattr.c')
-rw-r--r-- | arch/x86/mm/pageattr.c | 74 |
1 files changed, 38 insertions, 36 deletions
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 97ec9e7d29d9..532a40bc0e7e 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c | |||
@@ -197,10 +197,11 @@ static int split_large_page(pte_t *kpte, unsigned long address) | |||
197 | unsigned long addr; | 197 | unsigned long addr; |
198 | pte_t *pbase, *tmp; | 198 | pte_t *pbase, *tmp; |
199 | struct page *base; | 199 | struct page *base; |
200 | int i, level; | 200 | unsigned int i, level; |
201 | 201 | ||
202 | #ifdef CONFIG_DEBUG_PAGEALLOC | 202 | #ifdef CONFIG_DEBUG_PAGEALLOC |
203 | gfp_flags = GFP_ATOMIC; | 203 | gfp_flags = __GFP_HIGH | __GFP_NOFAIL | __GFP_NOWARN; |
204 | gfp_flags = GFP_ATOMIC | __GFP_NOWARN; | ||
204 | #endif | 205 | #endif |
205 | base = alloc_pages(gfp_flags, 0); | 206 | base = alloc_pages(gfp_flags, 0); |
206 | if (!base) | 207 | if (!base) |
@@ -224,6 +225,7 @@ static int split_large_page(pte_t *kpte, unsigned long address) | |||
224 | paravirt_alloc_pt(&init_mm, page_to_pfn(base)); | 225 | paravirt_alloc_pt(&init_mm, page_to_pfn(base)); |
225 | #endif | 226 | #endif |
226 | 227 | ||
228 | pgprot_val(ref_prot) &= ~_PAGE_NX; | ||
227 | for (i = 0; i < PTRS_PER_PTE; i++, addr += PAGE_SIZE) | 229 | for (i = 0; i < PTRS_PER_PTE; i++, addr += PAGE_SIZE) |
228 | set_pte(&pbase[i], pfn_pte(addr >> PAGE_SHIFT, ref_prot)); | 230 | set_pte(&pbase[i], pfn_pte(addr >> PAGE_SHIFT, ref_prot)); |
229 | 231 | ||
@@ -248,7 +250,8 @@ out_unlock: | |||
248 | } | 250 | } |
249 | 251 | ||
250 | static int | 252 | static int |
251 | __change_page_attr(unsigned long address, unsigned long pfn, pgprot_t prot) | 253 | __change_page_attr(unsigned long address, unsigned long pfn, |
254 | pgprot_t mask_set, pgprot_t mask_clr) | ||
252 | { | 255 | { |
253 | struct page *kpte_page; | 256 | struct page *kpte_page; |
254 | int level, err = 0; | 257 | int level, err = 0; |
@@ -267,15 +270,20 @@ repeat: | |||
267 | BUG_ON(PageLRU(kpte_page)); | 270 | BUG_ON(PageLRU(kpte_page)); |
268 | BUG_ON(PageCompound(kpte_page)); | 271 | BUG_ON(PageCompound(kpte_page)); |
269 | 272 | ||
270 | prot = static_protections(prot, address); | ||
271 | |||
272 | if (level == PG_LEVEL_4K) { | 273 | if (level == PG_LEVEL_4K) { |
273 | WARN_ON_ONCE(pgprot_val(prot) & _PAGE_PSE); | 274 | pgprot_t new_prot = pte_pgprot(*kpte); |
274 | set_pte_atomic(kpte, pfn_pte(pfn, canon_pgprot(prot))); | 275 | pte_t new_pte, old_pte = *kpte; |
275 | } else { | 276 | |
276 | /* Clear the PSE bit for the 4k level pages ! */ | 277 | pgprot_val(new_prot) &= ~pgprot_val(mask_clr); |
277 | pgprot_val(prot) = pgprot_val(prot) & ~_PAGE_PSE; | 278 | pgprot_val(new_prot) |= pgprot_val(mask_set); |
279 | |||
280 | new_prot = static_protections(new_prot, address); | ||
281 | |||
282 | new_pte = pfn_pte(pfn, canon_pgprot(new_prot)); | ||
283 | BUG_ON(pte_pfn(new_pte) != pte_pfn(old_pte)); | ||
278 | 284 | ||
285 | set_pte_atomic(kpte, new_pte); | ||
286 | } else { | ||
279 | err = split_large_page(kpte, address); | 287 | err = split_large_page(kpte, address); |
280 | if (!err) | 288 | if (!err) |
281 | goto repeat; | 289 | goto repeat; |
@@ -297,22 +305,26 @@ repeat: | |||
297 | * Modules and drivers should use the set_memory_* APIs instead. | 305 | * Modules and drivers should use the set_memory_* APIs instead. |
298 | */ | 306 | */ |
299 | 307 | ||
300 | static int change_page_attr_addr(unsigned long address, pgprot_t prot) | 308 | static int |
309 | change_page_attr_addr(unsigned long address, pgprot_t mask_set, | ||
310 | pgprot_t mask_clr) | ||
301 | { | 311 | { |
302 | int err = 0, kernel_map = 0; | 312 | int err = 0, kernel_map = 0; |
303 | unsigned long pfn = __pa(address) >> PAGE_SHIFT; | 313 | unsigned long pfn; |
304 | 314 | ||
305 | #ifdef CONFIG_X86_64 | 315 | #ifdef CONFIG_X86_64 |
306 | if (address >= __START_KERNEL_map && | 316 | if (address >= __START_KERNEL_map && |
307 | address < __START_KERNEL_map + KERNEL_TEXT_SIZE) { | 317 | address < __START_KERNEL_map + KERNEL_TEXT_SIZE) { |
308 | 318 | ||
309 | address = (unsigned long)__va(__pa(address)); | 319 | address = (unsigned long)__va(__pa((void *)address)); |
310 | kernel_map = 1; | 320 | kernel_map = 1; |
311 | } | 321 | } |
312 | #endif | 322 | #endif |
313 | 323 | ||
314 | if (!kernel_map || pte_present(pfn_pte(0, prot))) { | 324 | pfn = __pa(address) >> PAGE_SHIFT; |
315 | err = __change_page_attr(address, pfn, prot); | 325 | |
326 | if (!kernel_map || 1) { | ||
327 | err = __change_page_attr(address, pfn, mask_set, mask_clr); | ||
316 | if (err) | 328 | if (err) |
317 | return err; | 329 | return err; |
318 | } | 330 | } |
@@ -324,12 +336,15 @@ static int change_page_attr_addr(unsigned long address, pgprot_t prot) | |||
324 | */ | 336 | */ |
325 | if (__pa(address) < KERNEL_TEXT_SIZE) { | 337 | if (__pa(address) < KERNEL_TEXT_SIZE) { |
326 | unsigned long addr2; | 338 | unsigned long addr2; |
327 | pgprot_t prot2; | ||
328 | 339 | ||
329 | addr2 = __START_KERNEL_map + __pa(address); | 340 | addr2 = __pa(address) + __START_KERNEL_map - phys_base; |
330 | /* Make sure the kernel mappings stay executable */ | 341 | /* Make sure the kernel mappings stay executable */ |
331 | prot2 = pte_pgprot(pte_mkexec(pfn_pte(0, prot))); | 342 | pgprot_val(mask_clr) |= _PAGE_NX; |
332 | err = __change_page_attr(addr2, pfn, prot2); | 343 | /* |
344 | * Our high aliases are imprecise, so do not propagate | ||
345 | * failures back to users: | ||
346 | */ | ||
347 | __change_page_attr(addr2, pfn, mask_set, mask_clr); | ||
333 | } | 348 | } |
334 | #endif | 349 | #endif |
335 | 350 | ||
@@ -339,26 +354,13 @@ static int change_page_attr_addr(unsigned long address, pgprot_t prot) | |||
339 | static int __change_page_attr_set_clr(unsigned long addr, int numpages, | 354 | static int __change_page_attr_set_clr(unsigned long addr, int numpages, |
340 | pgprot_t mask_set, pgprot_t mask_clr) | 355 | pgprot_t mask_set, pgprot_t mask_clr) |
341 | { | 356 | { |
342 | pgprot_t new_prot; | 357 | unsigned int i; |
343 | int level; | 358 | int ret; |
344 | pte_t *pte; | ||
345 | int i, ret; | ||
346 | |||
347 | for (i = 0; i < numpages ; i++) { | ||
348 | |||
349 | pte = lookup_address(addr, &level); | ||
350 | if (!pte) | ||
351 | return -EINVAL; | ||
352 | |||
353 | new_prot = pte_pgprot(*pte); | ||
354 | |||
355 | pgprot_val(new_prot) &= ~pgprot_val(mask_clr); | ||
356 | pgprot_val(new_prot) |= pgprot_val(mask_set); | ||
357 | 359 | ||
358 | ret = change_page_attr_addr(addr, new_prot); | 360 | for (i = 0; i < numpages ; i++, addr += PAGE_SIZE) { |
361 | ret = change_page_attr_addr(addr, mask_set, mask_clr); | ||
359 | if (ret) | 362 | if (ret) |
360 | return ret; | 363 | return ret; |
361 | addr += PAGE_SIZE; | ||
362 | } | 364 | } |
363 | 365 | ||
364 | return 0; | 366 | return 0; |