diff options
Diffstat (limited to 'arch/x86/mm/pageattr.c')
-rw-r--r-- | arch/x86/mm/pageattr.c | 56 |
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: | |||
305 | * Modules and drivers should use the set_memory_* APIs instead. | 305 | * Modules and drivers should use the set_memory_* APIs instead. |
306 | */ | 306 | */ |
307 | 307 | ||
308 | #define HIGH_MAP_START __START_KERNEL_map | ||
309 | #define HIGH_MAP_END (__START_KERNEL_map + KERNEL_TEXT_SIZE) | ||
310 | |||
308 | static int | 311 | static int |
309 | change_page_attr_addr(unsigned long address, pgprot_t mask_set, | 312 | change_page_attr_addr(unsigned long address, pgprot_t mask_set, |
310 | pgprot_t mask_clr) | 313 | pgprot_t mask_clr) |
311 | { | 314 | { |
312 | int err = 0, kernel_map = 0; | 315 | unsigned long phys_addr = __pa(address); |
313 | unsigned long pfn; | 316 | unsigned long pfn = phys_addr >> PAGE_SHIFT; |
317 | int err; | ||
314 | 318 | ||
315 | #ifdef CONFIG_X86_64 | 319 | #ifdef CONFIG_X86_64 |
316 | if (address >= __START_KERNEL_map && | 320 | /* |
317 | address < __START_KERNEL_map + KERNEL_TEXT_SIZE) { | 321 | * If we are inside the high mapped kernel range, then we |
318 | 322 | * fixup the low mapping first. __va() returns the virtual | |
319 | address = (unsigned long)__va(__pa((void *)address)); | 323 | * address in the linear mapping: |
320 | kernel_map = 1; | 324 | */ |
321 | } | 325 | if (within(address, HIGH_MAP_START, HIGH_MAP_END)) |
326 | address = (unsigned long) __va(phys_addr); | ||
322 | #endif | 327 | #endif |
323 | 328 | ||
324 | pfn = __pa(address) >> PAGE_SHIFT; | 329 | err = __change_page_attr(address, pfn, mask_set, mask_clr); |
325 | 330 | if (err) | |
326 | if (!kernel_map || 1) { | 331 | return err; |
327 | err = __change_page_attr(address, pfn, mask_set, mask_clr); | ||
328 | if (err) | ||
329 | return err; | ||
330 | } | ||
331 | 332 | ||
332 | #ifdef CONFIG_X86_64 | 333 | #ifdef CONFIG_X86_64 |
333 | /* | 334 | /* |
334 | * Handle kernel mapping too which aliases part of | 335 | * If the physical address is inside the kernel map, we need |
335 | * lowmem: | 336 | * to touch the high mapped kernel as well: |
336 | */ | 337 | */ |
337 | if (__pa(address) < KERNEL_TEXT_SIZE) { | 338 | if (within(phys_addr, 0, KERNEL_TEXT_SIZE)) { |
338 | unsigned long addr2; | 339 | /* |
339 | 340 | * Calc the high mapping address. See __phys_addr() | |
340 | addr2 = __pa(address) + __START_KERNEL_map - phys_base; | 341 | * for the non obvious details. |
342 | */ | ||
343 | address = phys_addr + HIGH_MAP_START - phys_base; | ||
341 | /* Make sure the kernel mappings stay executable */ | 344 | /* Make sure the kernel mappings stay executable */ |
342 | pgprot_val(mask_clr) |= _PAGE_NX; | 345 | pgprot_val(mask_clr) |= _PAGE_NX; |
346 | |||
343 | /* | 347 | /* |
344 | * Our high aliases are imprecise, so do not propagate | 348 | * Our high aliases are imprecise, because we check |
345 | * failures back to users: | 349 | * everything between 0 and KERNEL_TEXT_SIZE, so do |
350 | * not propagate lookup failures back to users: | ||
346 | */ | 351 | */ |
347 | __change_page_attr(addr2, pfn, mask_set, mask_clr); | 352 | __change_page_attr(address, pfn, mask_set, mask_clr); |
348 | } | 353 | } |
349 | #endif | 354 | #endif |
350 | |||
351 | return err; | 355 | return err; |
352 | } | 356 | } |
353 | 357 | ||