diff options
Diffstat (limited to 'arch/x86/mm/pageattr.c')
-rw-r--r-- | arch/x86/mm/pageattr.c | 50 |
1 files changed, 47 insertions, 3 deletions
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index ca1f1c2bb7be..091934e1d0d9 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c | |||
@@ -473,6 +473,19 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, | |||
473 | pgprot_val(req_prot) |= pgprot_val(cpa->mask_set); | 473 | pgprot_val(req_prot) |= pgprot_val(cpa->mask_set); |
474 | 474 | ||
475 | /* | 475 | /* |
476 | * Set the PSE and GLOBAL flags only if the PRESENT flag is | ||
477 | * set otherwise pmd_present/pmd_huge will return true even on | ||
478 | * a non present pmd. The canon_pgprot will clear _PAGE_GLOBAL | ||
479 | * for the ancient hardware that doesn't support it. | ||
480 | */ | ||
481 | if (pgprot_val(new_prot) & _PAGE_PRESENT) | ||
482 | pgprot_val(new_prot) |= _PAGE_PSE | _PAGE_GLOBAL; | ||
483 | else | ||
484 | pgprot_val(new_prot) &= ~(_PAGE_PSE | _PAGE_GLOBAL); | ||
485 | |||
486 | new_prot = canon_pgprot(new_prot); | ||
487 | |||
488 | /* | ||
476 | * old_pte points to the large page base address. So we need | 489 | * old_pte points to the large page base address. So we need |
477 | * to add the offset of the virtual address: | 490 | * to add the offset of the virtual address: |
478 | */ | 491 | */ |
@@ -517,7 +530,7 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, | |||
517 | * The address is aligned and the number of pages | 530 | * The address is aligned and the number of pages |
518 | * covers the full page. | 531 | * covers the full page. |
519 | */ | 532 | */ |
520 | new_pte = pfn_pte(pte_pfn(old_pte), canon_pgprot(new_prot)); | 533 | new_pte = pfn_pte(pte_pfn(old_pte), new_prot); |
521 | __set_pmd_pte(kpte, address, new_pte); | 534 | __set_pmd_pte(kpte, address, new_pte); |
522 | cpa->flags |= CPA_FLUSHTLB; | 535 | cpa->flags |= CPA_FLUSHTLB; |
523 | do_split = 0; | 536 | do_split = 0; |
@@ -561,16 +574,35 @@ int __split_large_page(pte_t *kpte, unsigned long address, pte_t *pbase) | |||
561 | #ifdef CONFIG_X86_64 | 574 | #ifdef CONFIG_X86_64 |
562 | if (level == PG_LEVEL_1G) { | 575 | if (level == PG_LEVEL_1G) { |
563 | pfninc = PMD_PAGE_SIZE >> PAGE_SHIFT; | 576 | pfninc = PMD_PAGE_SIZE >> PAGE_SHIFT; |
564 | pgprot_val(ref_prot) |= _PAGE_PSE; | 577 | /* |
578 | * Set the PSE flags only if the PRESENT flag is set | ||
579 | * otherwise pmd_present/pmd_huge will return true | ||
580 | * even on a non present pmd. | ||
581 | */ | ||
582 | if (pgprot_val(ref_prot) & _PAGE_PRESENT) | ||
583 | pgprot_val(ref_prot) |= _PAGE_PSE; | ||
584 | else | ||
585 | pgprot_val(ref_prot) &= ~_PAGE_PSE; | ||
565 | } | 586 | } |
566 | #endif | 587 | #endif |
567 | 588 | ||
568 | /* | 589 | /* |
590 | * Set the GLOBAL flags only if the PRESENT flag is set | ||
591 | * otherwise pmd/pte_present will return true even on a non | ||
592 | * present pmd/pte. The canon_pgprot will clear _PAGE_GLOBAL | ||
593 | * for the ancient hardware that doesn't support it. | ||
594 | */ | ||
595 | if (pgprot_val(ref_prot) & _PAGE_PRESENT) | ||
596 | pgprot_val(ref_prot) |= _PAGE_GLOBAL; | ||
597 | else | ||
598 | pgprot_val(ref_prot) &= ~_PAGE_GLOBAL; | ||
599 | |||
600 | /* | ||
569 | * Get the target pfn from the original entry: | 601 | * Get the target pfn from the original entry: |
570 | */ | 602 | */ |
571 | pfn = pte_pfn(*kpte); | 603 | pfn = pte_pfn(*kpte); |
572 | for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc) | 604 | for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc) |
573 | set_pte(&pbase[i], pfn_pte(pfn, ref_prot)); | 605 | set_pte(&pbase[i], pfn_pte(pfn, canon_pgprot(ref_prot))); |
574 | 606 | ||
575 | if (pfn_range_is_mapped(PFN_DOWN(__pa(address)), | 607 | if (pfn_range_is_mapped(PFN_DOWN(__pa(address)), |
576 | PFN_DOWN(__pa(address)) + 1)) | 608 | PFN_DOWN(__pa(address)) + 1)) |
@@ -685,6 +717,18 @@ repeat: | |||
685 | new_prot = static_protections(new_prot, address, pfn); | 717 | new_prot = static_protections(new_prot, address, pfn); |
686 | 718 | ||
687 | /* | 719 | /* |
720 | * Set the GLOBAL flags only if the PRESENT flag is | ||
721 | * set otherwise pte_present will return true even on | ||
722 | * a non present pte. The canon_pgprot will clear | ||
723 | * _PAGE_GLOBAL for the ancient hardware that doesn't | ||
724 | * support it. | ||
725 | */ | ||
726 | if (pgprot_val(new_prot) & _PAGE_PRESENT) | ||
727 | pgprot_val(new_prot) |= _PAGE_GLOBAL; | ||
728 | else | ||
729 | pgprot_val(new_prot) &= ~_PAGE_GLOBAL; | ||
730 | |||
731 | /* | ||
688 | * We need to keep the pfn from the existing PTE, | 732 | * We need to keep the pfn from the existing PTE, |
689 | * after all we're only going to change it's attributes | 733 | * after all we're only going to change it's attributes |
690 | * not the memory it points to | 734 | * not the memory it points to |