summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/mm/pageattr.c26
1 files changed, 18 insertions, 8 deletions
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
index 6a9a77a403c9..e14e95ea7338 100644
--- a/arch/x86/mm/pageattr.c
+++ b/arch/x86/mm/pageattr.c
@@ -516,7 +516,7 @@ static inline void check_conflict(int warnlvl, pgprot_t prot, pgprotval_t val,
516 */ 516 */
517static inline pgprot_t static_protections(pgprot_t prot, unsigned long start, 517static inline pgprot_t static_protections(pgprot_t prot, unsigned long start,
518 unsigned long pfn, unsigned long npg, 518 unsigned long pfn, unsigned long npg,
519 int warnlvl) 519 unsigned long lpsize, int warnlvl)
520{ 520{
521 pgprotval_t forbidden, res; 521 pgprotval_t forbidden, res;
522 unsigned long end; 522 unsigned long end;
@@ -535,9 +535,17 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long start,
535 check_conflict(warnlvl, prot, res, start, end, pfn, "Text NX"); 535 check_conflict(warnlvl, prot, res, start, end, pfn, "Text NX");
536 forbidden = res; 536 forbidden = res;
537 537
538 res = protect_kernel_text_ro(start, end); 538 /*
539 check_conflict(warnlvl, prot, res, start, end, pfn, "Text RO"); 539 * Special case to preserve a large page. If the change spawns the
540 forbidden |= res; 540 * full large page mapping then there is no point to split it
541 * up. Happens with ftrace and is going to be removed once ftrace
542 * switched to text_poke().
543 */
544 if (lpsize != (npg * PAGE_SIZE) || (start & (lpsize - 1))) {
545 res = protect_kernel_text_ro(start, end);
546 check_conflict(warnlvl, prot, res, start, end, pfn, "Text RO");
547 forbidden |= res;
548 }
541 549
542 /* Check the PFN directly */ 550 /* Check the PFN directly */
543 res = protect_pci_bios(pfn, pfn + npg - 1); 551 res = protect_pci_bios(pfn, pfn + npg - 1);
@@ -819,7 +827,7 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address,
819 * extra conditional required here. 827 * extra conditional required here.
820 */ 828 */
821 chk_prot = static_protections(old_prot, lpaddr, old_pfn, numpages, 829 chk_prot = static_protections(old_prot, lpaddr, old_pfn, numpages,
822 CPA_CONFLICT); 830 psize, CPA_CONFLICT);
823 831
824 if (WARN_ON_ONCE(pgprot_val(chk_prot) != pgprot_val(old_prot))) { 832 if (WARN_ON_ONCE(pgprot_val(chk_prot) != pgprot_val(old_prot))) {
825 /* 833 /*
@@ -855,7 +863,7 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address,
855 * protection requirement in the large page. 863 * protection requirement in the large page.
856 */ 864 */
857 new_prot = static_protections(req_prot, lpaddr, old_pfn, numpages, 865 new_prot = static_protections(req_prot, lpaddr, old_pfn, numpages,
858 CPA_DETECT); 866 psize, CPA_DETECT);
859 867
860 /* 868 /*
861 * If there is a conflict, split the large page. 869 * If there is a conflict, split the large page.
@@ -906,7 +914,8 @@ static void split_set_pte(struct cpa_data *cpa, pte_t *pte, unsigned long pfn,
906 if (!cpa->force_static_prot) 914 if (!cpa->force_static_prot)
907 goto set; 915 goto set;
908 916
909 prot = static_protections(ref_prot, address, pfn, npg, CPA_PROTECT); 917 /* Hand in lpsize = 0 to enforce the protection mechanism */
918 prot = static_protections(ref_prot, address, pfn, npg, 0, CPA_PROTECT);
910 919
911 if (pgprot_val(prot) == pgprot_val(ref_prot)) 920 if (pgprot_val(prot) == pgprot_val(ref_prot))
912 goto set; 921 goto set;
@@ -1503,7 +1512,8 @@ repeat:
1503 pgprot_val(new_prot) |= pgprot_val(cpa->mask_set); 1512 pgprot_val(new_prot) |= pgprot_val(cpa->mask_set);
1504 1513
1505 cpa_inc_4k_install(); 1514 cpa_inc_4k_install();
1506 new_prot = static_protections(new_prot, address, pfn, 1, 1515 /* Hand in lpsize = 0 to enforce the protection mechanism */
1516 new_prot = static_protections(new_prot, address, pfn, 1, 0,
1507 CPA_PROTECT); 1517 CPA_PROTECT);
1508 1518
1509 new_prot = pgprot_clear_protnone_bits(new_prot); 1519 new_prot = pgprot_clear_protnone_bits(new_prot);