|
@@ -298,9 +298,11 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address,
|
|
|
|
|
|
/*
|
|
|
* The .rodata section needs to be read-only. Using the pfn
|
|
|
- * catches all aliases.
|
|
|
+ * catches all aliases. This also includes __ro_after_init,
|
|
|
+ * so do not enforce until kernel_set_to_readonly is true.
|
|
|
*/
|
|
|
- if (within(pfn, __pa_symbol(__start_rodata) >> PAGE_SHIFT,
|
|
|
+ if (kernel_set_to_readonly &&
|
|
|
+ within(pfn, __pa_symbol(__start_rodata) >> PAGE_SHIFT,
|
|
|
__pa_symbol(__end_rodata) >> PAGE_SHIFT))
|
|
|
pgprot_val(forbidden) |= _PAGE_RW;
|
|
|
|
|
@@ -512,6 +514,23 @@ static void __set_pmd_pte(pte_t *kpte, unsigned long address, pte_t pte)
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
+static pgprot_t pgprot_clear_protnone_bits(pgprot_t prot)
|
|
|
+{
|
|
|
+ /*
|
|
|
+ * _PAGE_GLOBAL means "global page" for present PTEs.
|
|
|
+ * But, it is also used to indicate _PAGE_PROTNONE
|
|
|
+ * for non-present PTEs.
|
|
|
+ *
|
|
|
+ * This ensures that a _PAGE_GLOBAL PTE going from
|
|
|
+ * present to non-present is not confused as
|
|
|
+ * _PAGE_PROTNONE.
|
|
|
+ */
|
|
|
+ if (!(pgprot_val(prot) & _PAGE_PRESENT))
|
|
|
+ pgprot_val(prot) &= ~_PAGE_GLOBAL;
|
|
|
+
|
|
|
+ return prot;
|
|
|
+}
|
|
|
+
|
|
|
static int
|
|
|
try_preserve_large_page(pte_t *kpte, unsigned long address,
|
|
|
struct cpa_data *cpa)
|
|
@@ -566,6 +585,7 @@ try_preserve_large_page(pte_t *kpte, unsigned long address,
|
|
|
* up accordingly.
|
|
|
*/
|
|
|
old_pte = *kpte;
|
|
|
+ /* Clear PSE (aka _PAGE_PAT) and move PAT bit to correct position */
|
|
|
req_prot = pgprot_large_2_4k(old_prot);
|
|
|
|
|
|
pgprot_val(req_prot) &= ~pgprot_val(cpa->mask_clr);
|
|
@@ -577,19 +597,9 @@ try_preserve_large_page(pte_t *kpte, unsigned long address,
|
|
|
* different bit positions in the two formats.
|
|
|
*/
|
|
|
req_prot = pgprot_4k_2_large(req_prot);
|
|
|
-
|
|
|
- /*
|
|
|
- * 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.
|
|
|
- */
|
|
|
+ req_prot = pgprot_clear_protnone_bits(req_prot);
|
|
|
if (pgprot_val(req_prot) & _PAGE_PRESENT)
|
|
|
- pgprot_val(req_prot) |= _PAGE_PSE | _PAGE_GLOBAL;
|
|
|
- else
|
|
|
- pgprot_val(req_prot) &= ~(_PAGE_PSE | _PAGE_GLOBAL);
|
|
|
-
|
|
|
- req_prot = canon_pgprot(req_prot);
|
|
|
+ pgprot_val(req_prot) |= _PAGE_PSE;
|
|
|
|
|
|
/*
|
|
|
* old_pfn points to the large page base pfn. So we need
|
|
@@ -674,8 +684,12 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
|
|
|
switch (level) {
|
|
|
case PG_LEVEL_2M:
|
|
|
ref_prot = pmd_pgprot(*(pmd_t *)kpte);
|
|
|
- /* clear PSE and promote PAT bit to correct position */
|
|
|
+ /*
|
|
|
+ * Clear PSE (aka _PAGE_PAT) and move
|
|
|
+ * PAT bit to correct position.
|
|
|
+ */
|
|
|
ref_prot = pgprot_large_2_4k(ref_prot);
|
|
|
+
|
|
|
ref_pfn = pmd_pfn(*(pmd_t *)kpte);
|
|
|
break;
|
|
|
|
|
@@ -698,23 +712,14 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * 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;
|
|
|
+ ref_prot = pgprot_clear_protnone_bits(ref_prot);
|
|
|
|
|
|
/*
|
|
|
* Get the target pfn from the original entry:
|
|
|
*/
|
|
|
pfn = ref_pfn;
|
|
|
for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc)
|
|
|
- set_pte(&pbase[i], pfn_pte(pfn, canon_pgprot(ref_prot)));
|
|
|
+ set_pte(&pbase[i], pfn_pte(pfn, ref_prot));
|
|
|
|
|
|
if (virt_addr_valid(address)) {
|
|
|
unsigned long pfn = PFN_DOWN(__pa(address));
|
|
@@ -930,19 +935,7 @@ static void populate_pte(struct cpa_data *cpa,
|
|
|
|
|
|
pte = pte_offset_kernel(pmd, start);
|
|
|
|
|
|
- /*
|
|
|
- * 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(pgprot) & _PAGE_PRESENT)
|
|
|
- pgprot_val(pgprot) |= _PAGE_GLOBAL;
|
|
|
- else
|
|
|
- pgprot_val(pgprot) &= ~_PAGE_GLOBAL;
|
|
|
-
|
|
|
- pgprot = canon_pgprot(pgprot);
|
|
|
+ pgprot = pgprot_clear_protnone_bits(pgprot);
|
|
|
|
|
|
while (num_pages-- && start < end) {
|
|
|
set_pte(pte, pfn_pte(cpa->pfn, pgprot));
|
|
@@ -1234,24 +1227,14 @@ 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;
|
|
|
+ new_prot = pgprot_clear_protnone_bits(new_prot);
|
|
|
|
|
|
/*
|
|
|
* We need to keep the pfn from the existing PTE,
|
|
|
* after all we're only going to change it's attributes
|
|
|
* not the memory it points to
|
|
|
*/
|
|
|
- new_pte = pfn_pte(pfn, canon_pgprot(new_prot));
|
|
|
+ new_pte = pfn_pte(pfn, new_prot);
|
|
|
cpa->pfn = pfn;
|
|
|
/*
|
|
|
* Do we really change anything ?
|
|
@@ -1428,11 +1411,11 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages,
|
|
|
memset(&cpa, 0, sizeof(cpa));
|
|
|
|
|
|
/*
|
|
|
- * Check, if we are requested to change a not supported
|
|
|
- * feature:
|
|
|
+ * Check, if we are requested to set a not supported
|
|
|
+ * feature. Clearing non-supported features is OK.
|
|
|
*/
|
|
|
mask_set = canon_pgprot(mask_set);
|
|
|
- mask_clr = canon_pgprot(mask_clr);
|
|
|
+
|
|
|
if (!pgprot_val(mask_set) && !pgprot_val(mask_clr) && !force_split)
|
|
|
return 0;
|
|
|
|
|
@@ -1775,6 +1758,12 @@ int set_memory_4k(unsigned long addr, int numpages)
|
|
|
__pgprot(0), 1, 0, NULL);
|
|
|
}
|
|
|
|
|
|
+int set_memory_nonglobal(unsigned long addr, int numpages)
|
|
|
+{
|
|
|
+ return change_page_attr_clear(&addr, numpages,
|
|
|
+ __pgprot(_PAGE_GLOBAL), 0);
|
|
|
+}
|
|
|
+
|
|
|
static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc)
|
|
|
{
|
|
|
struct cpa_data cpa;
|