|
@@ -472,6 +472,19 @@ try_preserve_large_page(pte_t *kpte, unsigned long address,
|
|
|
pgprot_val(req_prot) &= ~pgprot_val(cpa->mask_clr);
|
|
|
pgprot_val(req_prot) |= pgprot_val(cpa->mask_set);
|
|
|
|
|
|
+ /*
|
|
|
+ * Set the PSE and GLOBAL flags only if the PRESENT flag is
|
|
|
+ * set otherwise pmd_present/pmd_huge will return true even on
|
|
|
+ * a non present pmd. The canon_pgprot will clear _PAGE_GLOBAL
|
|
|
+ * for the ancient hardware that doesn't support it.
|
|
|
+ */
|
|
|
+ if (pgprot_val(new_prot) & _PAGE_PRESENT)
|
|
|
+ pgprot_val(new_prot) |= _PAGE_PSE | _PAGE_GLOBAL;
|
|
|
+ else
|
|
|
+ pgprot_val(new_prot) &= ~(_PAGE_PSE | _PAGE_GLOBAL);
|
|
|
+
|
|
|
+ new_prot = canon_pgprot(new_prot);
|
|
|
+
|
|
|
/*
|
|
|
* old_pte points to the large page base address. So we need
|
|
|
* to add the offset of the virtual address:
|
|
@@ -517,7 +530,7 @@ try_preserve_large_page(pte_t *kpte, unsigned long address,
|
|
|
* The address is aligned and the number of pages
|
|
|
* covers the full page.
|
|
|
*/
|
|
|
- new_pte = pfn_pte(pte_pfn(old_pte), canon_pgprot(new_prot));
|
|
|
+ new_pte = pfn_pte(pte_pfn(old_pte), new_prot);
|
|
|
__set_pmd_pte(kpte, address, new_pte);
|
|
|
cpa->flags |= CPA_FLUSHTLB;
|
|
|
do_split = 0;
|
|
@@ -561,16 +574,35 @@ int __split_large_page(pte_t *kpte, unsigned long address, pte_t *pbase)
|
|
|
#ifdef CONFIG_X86_64
|
|
|
if (level == PG_LEVEL_1G) {
|
|
|
pfninc = PMD_PAGE_SIZE >> PAGE_SHIFT;
|
|
|
- pgprot_val(ref_prot) |= _PAGE_PSE;
|
|
|
+ /*
|
|
|
+ * Set the PSE flags only if the PRESENT flag is set
|
|
|
+ * otherwise pmd_present/pmd_huge will return true
|
|
|
+ * even on a non present pmd.
|
|
|
+ */
|
|
|
+ if (pgprot_val(ref_prot) & _PAGE_PRESENT)
|
|
|
+ pgprot_val(ref_prot) |= _PAGE_PSE;
|
|
|
+ else
|
|
|
+ pgprot_val(ref_prot) &= ~_PAGE_PSE;
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
+ /*
|
|
|
+ * Set the GLOBAL flags only if the PRESENT flag is set
|
|
|
+ * otherwise pmd/pte_present will return true even on a non
|
|
|
+ * present pmd/pte. The canon_pgprot will clear _PAGE_GLOBAL
|
|
|
+ * for the ancient hardware that doesn't support it.
|
|
|
+ */
|
|
|
+ if (pgprot_val(ref_prot) & _PAGE_PRESENT)
|
|
|
+ pgprot_val(ref_prot) |= _PAGE_GLOBAL;
|
|
|
+ else
|
|
|
+ pgprot_val(ref_prot) &= ~_PAGE_GLOBAL;
|
|
|
+
|
|
|
/*
|
|
|
* Get the target pfn from the original entry:
|
|
|
*/
|
|
|
pfn = pte_pfn(*kpte);
|
|
|
for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc)
|
|
|
- set_pte(&pbase[i], pfn_pte(pfn, ref_prot));
|
|
|
+ set_pte(&pbase[i], pfn_pte(pfn, canon_pgprot(ref_prot)));
|
|
|
|
|
|
if (pfn_range_is_mapped(PFN_DOWN(__pa(address)),
|
|
|
PFN_DOWN(__pa(address)) + 1))
|
|
@@ -684,6 +716,18 @@ repeat:
|
|
|
|
|
|
new_prot = static_protections(new_prot, address, pfn);
|
|
|
|
|
|
+ /*
|
|
|
+ * Set the GLOBAL flags only if the PRESENT flag is
|
|
|
+ * set otherwise pte_present will return true even on
|
|
|
+ * a non present pte. The canon_pgprot will clear
|
|
|
+ * _PAGE_GLOBAL for the ancient hardware that doesn't
|
|
|
+ * support it.
|
|
|
+ */
|
|
|
+ if (pgprot_val(new_prot) & _PAGE_PRESENT)
|
|
|
+ pgprot_val(new_prot) |= _PAGE_GLOBAL;
|
|
|
+ else
|
|
|
+ pgprot_val(new_prot) &= ~_PAGE_GLOBAL;
|
|
|
+
|
|
|
/*
|
|
|
* We need to keep the pfn from the existing PTE,
|
|
|
* after all we're only going to change it's attributes
|