aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/x86/mm/pageattr.c56
1 files changed, 30 insertions, 26 deletions
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
index 532a40bc0e7e..ec07c1873d65 100644
--- a/arch/x86/mm/pageattr.c
+++ b/arch/x86/mm/pageattr.c
@@ -305,49 +305,53 @@ repeat:
* Modules and drivers should use the set_memory_* APIs instead.
*/
+#define HIGH_MAP_START __START_KERNEL_map
+#define HIGH_MAP_END (__START_KERNEL_map + KERNEL_TEXT_SIZE)
+
static int
change_page_attr_addr(unsigned long address, pgprot_t mask_set,
- pgprot_t mask_clr)
+ pgprot_t mask_clr)
{
- int err = 0, kernel_map = 0;
- unsigned long pfn;
+ unsigned long phys_addr = __pa(address);
+ unsigned long pfn = phys_addr >> PAGE_SHIFT;
+ int err;
#ifdef CONFIG_X86_64
- if (address >= __START_KERNEL_map &&
- address < __START_KERNEL_map + KERNEL_TEXT_SIZE) {
-
- address = (unsigned long)__va(__pa((void *)address));
- kernel_map = 1;
- }
+ /*
+ * If we are inside the high mapped kernel range, then we
+ * fixup the low mapping first. __va() returns the virtual
+ * address in the linear mapping:
+ */
+ if (within(address, HIGH_MAP_START, HIGH_MAP_END))
+ address = (unsigned long) __va(phys_addr);
#endif
- pfn = __pa(address) >> PAGE_SHIFT;
-
- if (!kernel_map || 1) {
- err = __change_page_attr(address, pfn, mask_set, mask_clr);
- if (err)
- return err;
- }
+ err = __change_page_attr(address, pfn, mask_set, mask_clr);
+ if (err)
+ return err;
#ifdef CONFIG_X86_64
/*
- * Handle kernel mapping too which aliases part of
- * lowmem:
+ * If the physical address is inside the kernel map, we need
+ * to touch the high mapped kernel as well:
*/
- if (__pa(address) < KERNEL_TEXT_SIZE) {
- unsigned long addr2;
-
- addr2 = __pa(address) + __START_KERNEL_map - phys_base;
+ if (within(phys_addr, 0, KERNEL_TEXT_SIZE)) {
+ /*
+ * Calc the high mapping address. See __phys_addr()
+ * for the non obvious details.
+ */
+ address = phys_addr + HIGH_MAP_START - phys_base;
/* Make sure the kernel mappings stay executable */
pgprot_val(mask_clr) |= _PAGE_NX;
+
/*
- * Our high aliases are imprecise, so do not propagate
- * failures back to users:
+ * Our high aliases are imprecise, because we check
+ * everything between 0 and KERNEL_TEXT_SIZE, so do
+ * not propagate lookup failures back to users:
*/
- __change_page_attr(addr2, pfn, mask_set, mask_clr);
+ __change_page_attr(address, pfn, mask_set, mask_clr);
}
#endif
-
return err;
}