diff options
Diffstat (limited to 'arch/x86/mm/pageattr.c')
-rw-r--r-- | arch/x86/mm/pageattr.c | 28 |
1 files changed, 18 insertions, 10 deletions
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 532e7933d606..6f2a6b6deb6b 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c | |||
@@ -255,6 +255,7 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address, | |||
255 | unsigned long pfn) | 255 | unsigned long pfn) |
256 | { | 256 | { |
257 | pgprot_t forbidden = __pgprot(0); | 257 | pgprot_t forbidden = __pgprot(0); |
258 | pgprot_t required = __pgprot(0); | ||
258 | 259 | ||
259 | /* | 260 | /* |
260 | * The BIOS area between 640k and 1Mb needs to be executable for | 261 | * The BIOS area between 640k and 1Mb needs to be executable for |
@@ -278,6 +279,12 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address, | |||
278 | if (within(pfn, __pa((unsigned long)__start_rodata) >> PAGE_SHIFT, | 279 | if (within(pfn, __pa((unsigned long)__start_rodata) >> PAGE_SHIFT, |
279 | __pa((unsigned long)__end_rodata) >> PAGE_SHIFT)) | 280 | __pa((unsigned long)__end_rodata) >> PAGE_SHIFT)) |
280 | pgprot_val(forbidden) |= _PAGE_RW; | 281 | pgprot_val(forbidden) |= _PAGE_RW; |
282 | /* | ||
283 | * .data and .bss should always be writable. | ||
284 | */ | ||
285 | if (within(address, (unsigned long)_sdata, (unsigned long)_edata) || | ||
286 | within(address, (unsigned long)__bss_start, (unsigned long)__bss_stop)) | ||
287 | pgprot_val(required) |= _PAGE_RW; | ||
281 | 288 | ||
282 | #if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA) | 289 | #if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA) |
283 | /* | 290 | /* |
@@ -317,6 +324,7 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address, | |||
317 | #endif | 324 | #endif |
318 | 325 | ||
319 | prot = __pgprot(pgprot_val(prot) & ~pgprot_val(forbidden)); | 326 | prot = __pgprot(pgprot_val(prot) & ~pgprot_val(forbidden)); |
327 | prot = __pgprot(pgprot_val(prot) | pgprot_val(required)); | ||
320 | 328 | ||
321 | return prot; | 329 | return prot; |
322 | } | 330 | } |
@@ -393,7 +401,7 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, | |||
393 | { | 401 | { |
394 | unsigned long nextpage_addr, numpages, pmask, psize, flags, addr, pfn; | 402 | unsigned long nextpage_addr, numpages, pmask, psize, flags, addr, pfn; |
395 | pte_t new_pte, old_pte, *tmp; | 403 | pte_t new_pte, old_pte, *tmp; |
396 | pgprot_t old_prot, new_prot; | 404 | pgprot_t old_prot, new_prot, req_prot; |
397 | int i, do_split = 1; | 405 | int i, do_split = 1; |
398 | unsigned int level; | 406 | unsigned int level; |
399 | 407 | ||
@@ -438,10 +446,10 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, | |||
438 | * We are safe now. Check whether the new pgprot is the same: | 446 | * We are safe now. Check whether the new pgprot is the same: |
439 | */ | 447 | */ |
440 | old_pte = *kpte; | 448 | old_pte = *kpte; |
441 | old_prot = new_prot = pte_pgprot(old_pte); | 449 | old_prot = new_prot = req_prot = pte_pgprot(old_pte); |
442 | 450 | ||
443 | pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr); | 451 | pgprot_val(req_prot) &= ~pgprot_val(cpa->mask_clr); |
444 | pgprot_val(new_prot) |= pgprot_val(cpa->mask_set); | 452 | pgprot_val(req_prot) |= pgprot_val(cpa->mask_set); |
445 | 453 | ||
446 | /* | 454 | /* |
447 | * old_pte points to the large page base address. So we need | 455 | * old_pte points to the large page base address. So we need |
@@ -450,17 +458,17 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, | |||
450 | pfn = pte_pfn(old_pte) + ((address & (psize - 1)) >> PAGE_SHIFT); | 458 | pfn = pte_pfn(old_pte) + ((address & (psize - 1)) >> PAGE_SHIFT); |
451 | cpa->pfn = pfn; | 459 | cpa->pfn = pfn; |
452 | 460 | ||
453 | new_prot = static_protections(new_prot, address, pfn); | 461 | new_prot = static_protections(req_prot, address, pfn); |
454 | 462 | ||
455 | /* | 463 | /* |
456 | * We need to check the full range, whether | 464 | * We need to check the full range, whether |
457 | * static_protection() requires a different pgprot for one of | 465 | * static_protection() requires a different pgprot for one of |
458 | * the pages in the range we try to preserve: | 466 | * the pages in the range we try to preserve: |
459 | */ | 467 | */ |
460 | addr = address + PAGE_SIZE; | 468 | addr = address & pmask; |
461 | pfn++; | 469 | pfn = pte_pfn(old_pte); |
462 | for (i = 1; i < cpa->numpages; i++, addr += PAGE_SIZE, pfn++) { | 470 | for (i = 0; i < (psize >> PAGE_SHIFT); i++, addr += PAGE_SIZE, pfn++) { |
463 | pgprot_t chk_prot = static_protections(new_prot, addr, pfn); | 471 | pgprot_t chk_prot = static_protections(req_prot, addr, pfn); |
464 | 472 | ||
465 | if (pgprot_val(chk_prot) != pgprot_val(new_prot)) | 473 | if (pgprot_val(chk_prot) != pgprot_val(new_prot)) |
466 | goto out_unlock; | 474 | goto out_unlock; |
@@ -483,7 +491,7 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, | |||
483 | * that we limited the number of possible pages already to | 491 | * that we limited the number of possible pages already to |
484 | * the number of pages in the large page. | 492 | * the number of pages in the large page. |
485 | */ | 493 | */ |
486 | if (address == (nextpage_addr - psize) && cpa->numpages == numpages) { | 494 | if (address == (address & pmask) && cpa->numpages == (psize >> PAGE_SHIFT)) { |
487 | /* | 495 | /* |
488 | * The address is aligned and the number of pages | 496 | * The address is aligned and the number of pages |
489 | * covers the full page. | 497 | * covers the full page. |