diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-18 18:51:40 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-18 18:51:40 -0500 |
commit | 0165d7f476b99eb637027c2f69c365f00260f188 (patch) | |
tree | 2369b69095695d1b974a8a2ffa2941d54de5075e | |
parent | 20ef0f1ad0035cd7acc6cbcbc018b2dcd891e274 (diff) | |
parent | f6c540cdd65d4bc4eaf4dcbf652580f09f3b0c38 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/x86/linux-2.6-x86
* git://git.kernel.org/pub/scm/linux/kernel/git/x86/linux-2.6-x86:
x86: fix lguest build failure
x86: reenable support for system without on node0
x86: CPA: avoid double checking of alias ranges
x86: CPA no alias checking for _NX
x86: zap invalid and unused pmds in early boot
x86: CPA, fix alias checks
-rw-r--r-- | arch/x86/kernel/asm-offsets_32.c | 6 | ||||
-rw-r--r-- | arch/x86/kernel/head64.c | 3 | ||||
-rw-r--r-- | arch/x86/kernel/head_64.S | 7 | ||||
-rw-r--r-- | arch/x86/mm/init_64.c | 27 | ||||
-rw-r--r-- | arch/x86/mm/numa_64.c | 5 | ||||
-rw-r--r-- | arch/x86/mm/pageattr.c | 208 | ||||
-rw-r--r-- | include/asm-x86/pgtable_64.h | 1 |
7 files changed, 154 insertions, 103 deletions
diff --git a/arch/x86/kernel/asm-offsets_32.c b/arch/x86/kernel/asm-offsets_32.c index afd84463b712..a33d53017997 100644 --- a/arch/x86/kernel/asm-offsets_32.c +++ b/arch/x86/kernel/asm-offsets_32.c | |||
@@ -20,10 +20,8 @@ | |||
20 | 20 | ||
21 | #include <xen/interface/xen.h> | 21 | #include <xen/interface/xen.h> |
22 | 22 | ||
23 | #ifdef CONFIG_LGUEST_GUEST | ||
24 | #include <linux/lguest.h> | 23 | #include <linux/lguest.h> |
25 | #include "../../../drivers/lguest/lg.h" | 24 | #include "../../../drivers/lguest/lg.h" |
26 | #endif | ||
27 | 25 | ||
28 | #define DEFINE(sym, val) \ | 26 | #define DEFINE(sym, val) \ |
29 | asm volatile("\n->" #sym " %0 " #val : : "i" (val)) | 27 | asm volatile("\n->" #sym " %0 " #val : : "i" (val)) |
@@ -134,6 +132,10 @@ void foo(void) | |||
134 | BLANK(); | 132 | BLANK(); |
135 | OFFSET(LGUEST_DATA_irq_enabled, lguest_data, irq_enabled); | 133 | OFFSET(LGUEST_DATA_irq_enabled, lguest_data, irq_enabled); |
136 | OFFSET(LGUEST_DATA_pgdir, lguest_data, pgdir); | 134 | OFFSET(LGUEST_DATA_pgdir, lguest_data, pgdir); |
135 | #endif | ||
136 | |||
137 | #ifdef CONFIG_LGUEST | ||
138 | BLANK(); | ||
137 | OFFSET(LGUEST_PAGES_host_gdt_desc, lguest_pages, state.host_gdt_desc); | 139 | OFFSET(LGUEST_PAGES_host_gdt_desc, lguest_pages, state.host_gdt_desc); |
138 | OFFSET(LGUEST_PAGES_host_idt_desc, lguest_pages, state.host_idt_desc); | 140 | OFFSET(LGUEST_PAGES_host_idt_desc, lguest_pages, state.host_idt_desc); |
139 | OFFSET(LGUEST_PAGES_host_cr3, lguest_pages, state.host_cr3); | 141 | OFFSET(LGUEST_PAGES_host_cr3, lguest_pages, state.host_cr3); |
diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index 24dbf56928d7..ad2440832de0 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c | |||
@@ -88,6 +88,9 @@ void __init x86_64_start_kernel(char * real_mode_data) | |||
88 | /* Make NULL pointers segfault */ | 88 | /* Make NULL pointers segfault */ |
89 | zap_identity_mappings(); | 89 | zap_identity_mappings(); |
90 | 90 | ||
91 | /* Cleanup the over mapped high alias */ | ||
92 | cleanup_highmap(); | ||
93 | |||
91 | for (i = 0; i < IDT_ENTRIES; i++) { | 94 | for (i = 0; i < IDT_ENTRIES; i++) { |
92 | #ifdef CONFIG_EARLY_PRINTK | 95 | #ifdef CONFIG_EARLY_PRINTK |
93 | set_intr_gate(i, &early_idt_handlers[i]); | 96 | set_intr_gate(i, &early_idt_handlers[i]); |
diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S index 09b38d539b09..53e5820d6054 100644 --- a/arch/x86/kernel/head_64.S +++ b/arch/x86/kernel/head_64.S | |||
@@ -107,8 +107,13 @@ startup_64: | |||
107 | movq %rdx, 0(%rbx, %rax, 8) | 107 | movq %rdx, 0(%rbx, %rax, 8) |
108 | ident_complete: | 108 | ident_complete: |
109 | 109 | ||
110 | /* Fixup the kernel text+data virtual addresses | 110 | /* |
111 | * Fixup the kernel text+data virtual addresses. Note that | ||
112 | * we might write invalid pmds, when the kernel is relocated | ||
113 | * cleanup_highmap() fixes this up along with the mappings | ||
114 | * beyond _end. | ||
111 | */ | 115 | */ |
116 | |||
112 | leaq level2_kernel_pgt(%rip), %rdi | 117 | leaq level2_kernel_pgt(%rip), %rdi |
113 | leaq 4096(%rdi), %r8 | 118 | leaq 4096(%rdi), %r8 |
114 | /* See if it is a valid page table entry */ | 119 | /* See if it is a valid page table entry */ |
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index a4a9cccdd4f2..bb652f5a93fb 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c | |||
@@ -171,6 +171,33 @@ set_pte_phys(unsigned long vaddr, unsigned long phys, pgprot_t prot) | |||
171 | __flush_tlb_one(vaddr); | 171 | __flush_tlb_one(vaddr); |
172 | } | 172 | } |
173 | 173 | ||
174 | /* | ||
175 | * The head.S code sets up the kernel high mapping from: | ||
176 | * __START_KERNEL_map to __START_KERNEL_map + KERNEL_TEXT_SIZE | ||
177 | * | ||
178 | * phys_addr holds the negative offset to the kernel, which is added | ||
179 | * to the compile time generated pmds. This results in invalid pmds up | ||
180 | * to the point where we hit the physaddr 0 mapping. | ||
181 | * | ||
182 | * We limit the mappings to the region from _text to _end. _end is | ||
183 | * rounded up to the 2MB boundary. This catches the invalid pmds as | ||
184 | * well, as they are located before _text: | ||
185 | */ | ||
186 | void __init cleanup_highmap(void) | ||
187 | { | ||
188 | unsigned long vaddr = __START_KERNEL_map; | ||
189 | unsigned long end = round_up((unsigned long)_end, PMD_SIZE) - 1; | ||
190 | pmd_t *pmd = level2_kernel_pgt; | ||
191 | pmd_t *last_pmd = pmd + PTRS_PER_PMD; | ||
192 | |||
193 | for (; pmd < last_pmd; pmd++, vaddr += PMD_SIZE) { | ||
194 | if (!pmd_present(*pmd)) | ||
195 | continue; | ||
196 | if (vaddr < (unsigned long) _text || vaddr > end) | ||
197 | set_pmd(pmd, __pmd(0)); | ||
198 | } | ||
199 | } | ||
200 | |||
174 | /* NOTE: this is meant to be run only at boot */ | 201 | /* NOTE: this is meant to be run only at boot */ |
175 | void __init | 202 | void __init |
176 | __set_fixmap(enum fixed_addresses idx, unsigned long phys, pgprot_t prot) | 203 | __set_fixmap(enum fixed_addresses idx, unsigned long phys, pgprot_t prot) |
diff --git a/arch/x86/mm/numa_64.c b/arch/x86/mm/numa_64.c index 1aecc658cd7d..59898fb0a4aa 100644 --- a/arch/x86/mm/numa_64.c +++ b/arch/x86/mm/numa_64.c | |||
@@ -494,11 +494,13 @@ void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn) | |||
494 | int i; | 494 | int i; |
495 | 495 | ||
496 | nodes_clear(node_possible_map); | 496 | nodes_clear(node_possible_map); |
497 | nodes_clear(node_online_map); | ||
497 | 498 | ||
498 | #ifdef CONFIG_NUMA_EMU | 499 | #ifdef CONFIG_NUMA_EMU |
499 | if (cmdline && !numa_emulation(start_pfn, end_pfn)) | 500 | if (cmdline && !numa_emulation(start_pfn, end_pfn)) |
500 | return; | 501 | return; |
501 | nodes_clear(node_possible_map); | 502 | nodes_clear(node_possible_map); |
503 | nodes_clear(node_online_map); | ||
502 | #endif | 504 | #endif |
503 | 505 | ||
504 | #ifdef CONFIG_ACPI_NUMA | 506 | #ifdef CONFIG_ACPI_NUMA |
@@ -506,6 +508,7 @@ void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn) | |||
506 | end_pfn << PAGE_SHIFT)) | 508 | end_pfn << PAGE_SHIFT)) |
507 | return; | 509 | return; |
508 | nodes_clear(node_possible_map); | 510 | nodes_clear(node_possible_map); |
511 | nodes_clear(node_online_map); | ||
509 | #endif | 512 | #endif |
510 | 513 | ||
511 | #ifdef CONFIG_K8_NUMA | 514 | #ifdef CONFIG_K8_NUMA |
@@ -513,6 +516,7 @@ void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn) | |||
513 | end_pfn<<PAGE_SHIFT)) | 516 | end_pfn<<PAGE_SHIFT)) |
514 | return; | 517 | return; |
515 | nodes_clear(node_possible_map); | 518 | nodes_clear(node_possible_map); |
519 | nodes_clear(node_online_map); | ||
516 | #endif | 520 | #endif |
517 | printk(KERN_INFO "%s\n", | 521 | printk(KERN_INFO "%s\n", |
518 | numa_off ? "NUMA turned off" : "No NUMA configuration found"); | 522 | numa_off ? "NUMA turned off" : "No NUMA configuration found"); |
@@ -524,7 +528,6 @@ void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn) | |||
524 | memnode_shift = 63; | 528 | memnode_shift = 63; |
525 | memnodemap = memnode.embedded_map; | 529 | memnodemap = memnode.embedded_map; |
526 | memnodemap[0] = 0; | 530 | memnodemap[0] = 0; |
527 | nodes_clear(node_online_map); | ||
528 | node_set_online(0); | 531 | node_set_online(0); |
529 | node_set(0, node_possible_map); | 532 | node_set(0, node_possible_map); |
530 | for (i = 0; i < NR_CPUS; i++) | 533 | for (i = 0; i < NR_CPUS; i++) |
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 4119379f80ff..3ee14996c829 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <asm/sections.h> | 16 | #include <asm/sections.h> |
17 | #include <asm/uaccess.h> | 17 | #include <asm/uaccess.h> |
18 | #include <asm/pgalloc.h> | 18 | #include <asm/pgalloc.h> |
19 | #include <asm/proto.h> | ||
19 | 20 | ||
20 | /* | 21 | /* |
21 | * The current flushing context - we pass it instead of 5 arguments: | 22 | * The current flushing context - we pass it instead of 5 arguments: |
@@ -26,8 +27,23 @@ struct cpa_data { | |||
26 | pgprot_t mask_clr; | 27 | pgprot_t mask_clr; |
27 | int numpages; | 28 | int numpages; |
28 | int flushtlb; | 29 | int flushtlb; |
30 | unsigned long pfn; | ||
29 | }; | 31 | }; |
30 | 32 | ||
33 | #ifdef CONFIG_X86_64 | ||
34 | |||
35 | static inline unsigned long highmap_start_pfn(void) | ||
36 | { | ||
37 | return __pa(_text) >> PAGE_SHIFT; | ||
38 | } | ||
39 | |||
40 | static inline unsigned long highmap_end_pfn(void) | ||
41 | { | ||
42 | return __pa(round_up((unsigned long)_end, PMD_SIZE)) >> PAGE_SHIFT; | ||
43 | } | ||
44 | |||
45 | #endif | ||
46 | |||
31 | static inline int | 47 | static inline int |
32 | within(unsigned long addr, unsigned long start, unsigned long end) | 48 | within(unsigned long addr, unsigned long start, unsigned long end) |
33 | { | 49 | { |
@@ -123,29 +139,14 @@ static void cpa_flush_range(unsigned long start, int numpages, int cache) | |||
123 | } | 139 | } |
124 | } | 140 | } |
125 | 141 | ||
126 | #define HIGH_MAP_START __START_KERNEL_map | ||
127 | #define HIGH_MAP_END (__START_KERNEL_map + KERNEL_TEXT_SIZE) | ||
128 | |||
129 | |||
130 | /* | ||
131 | * Converts a virtual address to a X86-64 highmap address | ||
132 | */ | ||
133 | static unsigned long virt_to_highmap(void *address) | ||
134 | { | ||
135 | #ifdef CONFIG_X86_64 | ||
136 | return __pa((unsigned long)address) + HIGH_MAP_START - phys_base; | ||
137 | #else | ||
138 | return (unsigned long)address; | ||
139 | #endif | ||
140 | } | ||
141 | |||
142 | /* | 142 | /* |
143 | * Certain areas of memory on x86 require very specific protection flags, | 143 | * Certain areas of memory on x86 require very specific protection flags, |
144 | * for example the BIOS area or kernel text. Callers don't always get this | 144 | * for example the BIOS area or kernel text. Callers don't always get this |
145 | * right (again, ioremap() on BIOS memory is not uncommon) so this function | 145 | * right (again, ioremap() on BIOS memory is not uncommon) so this function |
146 | * checks and fixes these known static required protection bits. | 146 | * checks and fixes these known static required protection bits. |
147 | */ | 147 | */ |
148 | static inline pgprot_t static_protections(pgprot_t prot, unsigned long address) | 148 | static inline pgprot_t static_protections(pgprot_t prot, unsigned long address, |
149 | unsigned long pfn) | ||
149 | { | 150 | { |
150 | pgprot_t forbidden = __pgprot(0); | 151 | pgprot_t forbidden = __pgprot(0); |
151 | 152 | ||
@@ -153,30 +154,23 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address) | |||
153 | * The BIOS area between 640k and 1Mb needs to be executable for | 154 | * The BIOS area between 640k and 1Mb needs to be executable for |
154 | * PCI BIOS based config access (CONFIG_PCI_GOBIOS) support. | 155 | * PCI BIOS based config access (CONFIG_PCI_GOBIOS) support. |
155 | */ | 156 | */ |
156 | if (within(__pa(address), BIOS_BEGIN, BIOS_END)) | 157 | if (within(pfn, BIOS_BEGIN >> PAGE_SHIFT, BIOS_END >> PAGE_SHIFT)) |
157 | pgprot_val(forbidden) |= _PAGE_NX; | 158 | pgprot_val(forbidden) |= _PAGE_NX; |
158 | 159 | ||
159 | /* | 160 | /* |
160 | * The kernel text needs to be executable for obvious reasons | 161 | * The kernel text needs to be executable for obvious reasons |
161 | * Does not cover __inittext since that is gone later on | 162 | * Does not cover __inittext since that is gone later on. On |
163 | * 64bit we do not enforce !NX on the low mapping | ||
162 | */ | 164 | */ |
163 | if (within(address, (unsigned long)_text, (unsigned long)_etext)) | 165 | if (within(address, (unsigned long)_text, (unsigned long)_etext)) |
164 | pgprot_val(forbidden) |= _PAGE_NX; | 166 | pgprot_val(forbidden) |= _PAGE_NX; |
165 | /* | ||
166 | * Do the same for the x86-64 high kernel mapping | ||
167 | */ | ||
168 | if (within(address, virt_to_highmap(_text), virt_to_highmap(_etext))) | ||
169 | pgprot_val(forbidden) |= _PAGE_NX; | ||
170 | 167 | ||
171 | /* The .rodata section needs to be read-only */ | ||
172 | if (within(address, (unsigned long)__start_rodata, | ||
173 | (unsigned long)__end_rodata)) | ||
174 | pgprot_val(forbidden) |= _PAGE_RW; | ||
175 | /* | 168 | /* |
176 | * Do the same for the x86-64 high kernel mapping | 169 | * The .rodata section needs to be read-only. Using the pfn |
170 | * catches all aliases. | ||
177 | */ | 171 | */ |
178 | if (within(address, virt_to_highmap(__start_rodata), | 172 | if (within(pfn, __pa((unsigned long)__start_rodata) >> PAGE_SHIFT, |
179 | virt_to_highmap(__end_rodata))) | 173 | __pa((unsigned long)__end_rodata) >> PAGE_SHIFT)) |
180 | pgprot_val(forbidden) |= _PAGE_RW; | 174 | pgprot_val(forbidden) |= _PAGE_RW; |
181 | 175 | ||
182 | prot = __pgprot(pgprot_val(prot) & ~pgprot_val(forbidden)); | 176 | prot = __pgprot(pgprot_val(prot) & ~pgprot_val(forbidden)); |
@@ -253,7 +247,7 @@ static int | |||
253 | try_preserve_large_page(pte_t *kpte, unsigned long address, | 247 | try_preserve_large_page(pte_t *kpte, unsigned long address, |
254 | struct cpa_data *cpa) | 248 | struct cpa_data *cpa) |
255 | { | 249 | { |
256 | unsigned long nextpage_addr, numpages, pmask, psize, flags, addr; | 250 | unsigned long nextpage_addr, numpages, pmask, psize, flags, addr, pfn; |
257 | pte_t new_pte, old_pte, *tmp; | 251 | pte_t new_pte, old_pte, *tmp; |
258 | pgprot_t old_prot, new_prot; | 252 | pgprot_t old_prot, new_prot; |
259 | int i, do_split = 1; | 253 | int i, do_split = 1; |
@@ -301,7 +295,15 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, | |||
301 | 295 | ||
302 | pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr); | 296 | pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr); |
303 | pgprot_val(new_prot) |= pgprot_val(cpa->mask_set); | 297 | pgprot_val(new_prot) |= pgprot_val(cpa->mask_set); |
304 | new_prot = static_protections(new_prot, address); | 298 | |
299 | /* | ||
300 | * old_pte points to the large page base address. So we need | ||
301 | * to add the offset of the virtual address: | ||
302 | */ | ||
303 | pfn = pte_pfn(old_pte) + ((address & (psize - 1)) >> PAGE_SHIFT); | ||
304 | cpa->pfn = pfn; | ||
305 | |||
306 | new_prot = static_protections(new_prot, address, pfn); | ||
305 | 307 | ||
306 | /* | 308 | /* |
307 | * We need to check the full range, whether | 309 | * We need to check the full range, whether |
@@ -309,8 +311,9 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, | |||
309 | * the pages in the range we try to preserve: | 311 | * the pages in the range we try to preserve: |
310 | */ | 312 | */ |
311 | addr = address + PAGE_SIZE; | 313 | addr = address + PAGE_SIZE; |
312 | for (i = 1; i < cpa->numpages; i++, addr += PAGE_SIZE) { | 314 | pfn++; |
313 | pgprot_t chk_prot = static_protections(new_prot, addr); | 315 | for (i = 1; i < cpa->numpages; i++, addr += PAGE_SIZE, pfn++) { |
316 | pgprot_t chk_prot = static_protections(new_prot, addr, pfn); | ||
314 | 317 | ||
315 | if (pgprot_val(chk_prot) != pgprot_val(new_prot)) | 318 | if (pgprot_val(chk_prot) != pgprot_val(new_prot)) |
316 | goto out_unlock; | 319 | goto out_unlock; |
@@ -505,46 +508,51 @@ out_unlock: | |||
505 | return 0; | 508 | return 0; |
506 | } | 509 | } |
507 | 510 | ||
508 | static int __change_page_attr(unsigned long address, struct cpa_data *cpa) | 511 | static int __change_page_attr(struct cpa_data *cpa, int primary) |
509 | { | 512 | { |
513 | unsigned long address = cpa->vaddr; | ||
510 | int do_split, err; | 514 | int do_split, err; |
511 | unsigned int level; | 515 | unsigned int level; |
512 | struct page *kpte_page; | 516 | struct page *kpte_page; |
513 | pte_t *kpte; | 517 | pte_t *kpte, old_pte; |
514 | 518 | ||
515 | repeat: | 519 | repeat: |
516 | kpte = lookup_address(address, &level); | 520 | kpte = lookup_address(address, &level); |
517 | if (!kpte) | 521 | if (!kpte) |
522 | return primary ? -EINVAL : 0; | ||
523 | |||
524 | old_pte = *kpte; | ||
525 | if (!pte_val(old_pte)) { | ||
526 | if (!primary) | ||
527 | return 0; | ||
528 | printk(KERN_WARNING "CPA: called for zero pte. " | ||
529 | "vaddr = %lx cpa->vaddr = %lx\n", address, | ||
530 | cpa->vaddr); | ||
531 | WARN_ON(1); | ||
518 | return -EINVAL; | 532 | return -EINVAL; |
533 | } | ||
519 | 534 | ||
520 | kpte_page = virt_to_page(kpte); | 535 | kpte_page = virt_to_page(kpte); |
521 | BUG_ON(PageLRU(kpte_page)); | 536 | BUG_ON(PageLRU(kpte_page)); |
522 | BUG_ON(PageCompound(kpte_page)); | 537 | BUG_ON(PageCompound(kpte_page)); |
523 | 538 | ||
524 | if (level == PG_LEVEL_4K) { | 539 | if (level == PG_LEVEL_4K) { |
525 | pte_t new_pte, old_pte = *kpte; | 540 | pte_t new_pte; |
526 | pgprot_t new_prot = pte_pgprot(old_pte); | 541 | pgprot_t new_prot = pte_pgprot(old_pte); |
527 | 542 | unsigned long pfn = pte_pfn(old_pte); | |
528 | if(!pte_val(old_pte)) { | ||
529 | printk(KERN_WARNING "CPA: called for zero pte. " | ||
530 | "vaddr = %lx cpa->vaddr = %lx\n", address, | ||
531 | cpa->vaddr); | ||
532 | WARN_ON(1); | ||
533 | return -EINVAL; | ||
534 | } | ||
535 | 543 | ||
536 | pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr); | 544 | pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr); |
537 | pgprot_val(new_prot) |= pgprot_val(cpa->mask_set); | 545 | pgprot_val(new_prot) |= pgprot_val(cpa->mask_set); |
538 | 546 | ||
539 | new_prot = static_protections(new_prot, address); | 547 | new_prot = static_protections(new_prot, address, pfn); |
540 | 548 | ||
541 | /* | 549 | /* |
542 | * We need to keep the pfn from the existing PTE, | 550 | * We need to keep the pfn from the existing PTE, |
543 | * after all we're only going to change it's attributes | 551 | * after all we're only going to change it's attributes |
544 | * not the memory it points to | 552 | * not the memory it points to |
545 | */ | 553 | */ |
546 | new_pte = pfn_pte(pte_pfn(old_pte), canon_pgprot(new_prot)); | 554 | new_pte = pfn_pte(pfn, canon_pgprot(new_prot)); |
547 | 555 | cpa->pfn = pfn; | |
548 | /* | 556 | /* |
549 | * Do we really change anything ? | 557 | * Do we really change anything ? |
550 | */ | 558 | */ |
@@ -581,67 +589,59 @@ repeat: | |||
581 | return err; | 589 | return err; |
582 | } | 590 | } |
583 | 591 | ||
584 | /** | 592 | static int __change_page_attr_set_clr(struct cpa_data *cpa, int checkalias); |
585 | * change_page_attr_addr - Change page table attributes in linear mapping | 593 | |
586 | * @address: Virtual address in linear mapping. | 594 | static int cpa_process_alias(struct cpa_data *cpa) |
587 | * @prot: New page table attribute (PAGE_*) | ||
588 | * | ||
589 | * Change page attributes of a page in the direct mapping. This is a variant | ||
590 | * of change_page_attr() that also works on memory holes that do not have | ||
591 | * mem_map entry (pfn_valid() is false). | ||
592 | * | ||
593 | * See change_page_attr() documentation for more details. | ||
594 | * | ||
595 | * Modules and drivers should use the set_memory_* APIs instead. | ||
596 | */ | ||
597 | static int change_page_attr_addr(struct cpa_data *cpa) | ||
598 | { | 595 | { |
599 | int err; | 596 | struct cpa_data alias_cpa; |
600 | unsigned long address = cpa->vaddr; | 597 | int ret = 0; |
601 | 598 | ||
602 | #ifdef CONFIG_X86_64 | 599 | if (cpa->pfn > max_pfn_mapped) |
603 | unsigned long phys_addr = __pa(address); | 600 | return 0; |
604 | 601 | ||
605 | /* | 602 | /* |
606 | * If we are inside the high mapped kernel range, then we | 603 | * No need to redo, when the primary call touched the direct |
607 | * fixup the low mapping first. __va() returns the virtual | 604 | * mapping already: |
608 | * address in the linear mapping: | ||
609 | */ | 605 | */ |
610 | if (within(address, HIGH_MAP_START, HIGH_MAP_END)) | 606 | if (!within(cpa->vaddr, PAGE_OFFSET, |
611 | address = (unsigned long) __va(phys_addr); | 607 | PAGE_OFFSET + (max_pfn_mapped << PAGE_SHIFT))) { |
612 | #endif | 608 | |
609 | alias_cpa = *cpa; | ||
610 | alias_cpa.vaddr = (unsigned long) __va(cpa->pfn << PAGE_SHIFT); | ||
613 | 611 | ||
614 | err = __change_page_attr(address, cpa); | 612 | ret = __change_page_attr_set_clr(&alias_cpa, 0); |
615 | if (err) | 613 | } |
616 | return err; | ||
617 | 614 | ||
618 | #ifdef CONFIG_X86_64 | 615 | #ifdef CONFIG_X86_64 |
616 | if (ret) | ||
617 | return ret; | ||
618 | /* | ||
619 | * No need to redo, when the primary call touched the high | ||
620 | * mapping already: | ||
621 | */ | ||
622 | if (within(cpa->vaddr, (unsigned long) _text, (unsigned long) _end)) | ||
623 | return 0; | ||
624 | |||
619 | /* | 625 | /* |
620 | * If the physical address is inside the kernel map, we need | 626 | * If the physical address is inside the kernel map, we need |
621 | * to touch the high mapped kernel as well: | 627 | * to touch the high mapped kernel as well: |
622 | */ | 628 | */ |
623 | if (within(phys_addr, 0, KERNEL_TEXT_SIZE)) { | 629 | if (!within(cpa->pfn, highmap_start_pfn(), highmap_end_pfn())) |
624 | /* | 630 | return 0; |
625 | * Calc the high mapping address. See __phys_addr() | ||
626 | * for the non obvious details. | ||
627 | * | ||
628 | * Note that NX and other required permissions are | ||
629 | * checked in static_protections(). | ||
630 | */ | ||
631 | address = phys_addr + HIGH_MAP_START - phys_base; | ||
632 | 631 | ||
633 | /* | 632 | alias_cpa = *cpa; |
634 | * Our high aliases are imprecise, because we check | 633 | alias_cpa.vaddr = |
635 | * everything between 0 and KERNEL_TEXT_SIZE, so do | 634 | (cpa->pfn << PAGE_SHIFT) + __START_KERNEL_map - phys_base; |
636 | * not propagate lookup failures back to users: | 635 | |
637 | */ | 636 | /* |
638 | __change_page_attr(address, cpa); | 637 | * The high mapping range is imprecise, so ignore the return value. |
639 | } | 638 | */ |
639 | __change_page_attr_set_clr(&alias_cpa, 0); | ||
640 | #endif | 640 | #endif |
641 | return err; | 641 | return ret; |
642 | } | 642 | } |
643 | 643 | ||
644 | static int __change_page_attr_set_clr(struct cpa_data *cpa) | 644 | static int __change_page_attr_set_clr(struct cpa_data *cpa, int checkalias) |
645 | { | 645 | { |
646 | int ret, numpages = cpa->numpages; | 646 | int ret, numpages = cpa->numpages; |
647 | 647 | ||
@@ -651,10 +651,17 @@ static int __change_page_attr_set_clr(struct cpa_data *cpa) | |||
651 | * preservation check. | 651 | * preservation check. |
652 | */ | 652 | */ |
653 | cpa->numpages = numpages; | 653 | cpa->numpages = numpages; |
654 | ret = change_page_attr_addr(cpa); | 654 | |
655 | ret = __change_page_attr(cpa, checkalias); | ||
655 | if (ret) | 656 | if (ret) |
656 | return ret; | 657 | return ret; |
657 | 658 | ||
659 | if (checkalias) { | ||
660 | ret = cpa_process_alias(cpa); | ||
661 | if (ret) | ||
662 | return ret; | ||
663 | } | ||
664 | |||
658 | /* | 665 | /* |
659 | * Adjust the number of pages with the result of the | 666 | * Adjust the number of pages with the result of the |
660 | * CPA operation. Either a large page has been | 667 | * CPA operation. Either a large page has been |
@@ -677,7 +684,7 @@ static int change_page_attr_set_clr(unsigned long addr, int numpages, | |||
677 | pgprot_t mask_set, pgprot_t mask_clr) | 684 | pgprot_t mask_set, pgprot_t mask_clr) |
678 | { | 685 | { |
679 | struct cpa_data cpa; | 686 | struct cpa_data cpa; |
680 | int ret, cache; | 687 | int ret, cache, checkalias; |
681 | 688 | ||
682 | /* | 689 | /* |
683 | * Check, if we are requested to change a not supported | 690 | * Check, if we are requested to change a not supported |
@@ -703,7 +710,10 @@ static int change_page_attr_set_clr(unsigned long addr, int numpages, | |||
703 | cpa.mask_clr = mask_clr; | 710 | cpa.mask_clr = mask_clr; |
704 | cpa.flushtlb = 0; | 711 | cpa.flushtlb = 0; |
705 | 712 | ||
706 | ret = __change_page_attr_set_clr(&cpa); | 713 | /* No alias checking for _NX bit modifications */ |
714 | checkalias = (pgprot_val(mask_set) | pgprot_val(mask_clr)) != _PAGE_NX; | ||
715 | |||
716 | ret = __change_page_attr_set_clr(&cpa, checkalias); | ||
707 | 717 | ||
708 | /* | 718 | /* |
709 | * Check whether we really changed something: | 719 | * Check whether we really changed something: |
@@ -841,7 +851,7 @@ static int __set_pages_p(struct page *page, int numpages) | |||
841 | .mask_set = __pgprot(_PAGE_PRESENT | _PAGE_RW), | 851 | .mask_set = __pgprot(_PAGE_PRESENT | _PAGE_RW), |
842 | .mask_clr = __pgprot(0)}; | 852 | .mask_clr = __pgprot(0)}; |
843 | 853 | ||
844 | return __change_page_attr_set_clr(&cpa); | 854 | return __change_page_attr_set_clr(&cpa, 1); |
845 | } | 855 | } |
846 | 856 | ||
847 | static int __set_pages_np(struct page *page, int numpages) | 857 | static int __set_pages_np(struct page *page, int numpages) |
@@ -851,7 +861,7 @@ static int __set_pages_np(struct page *page, int numpages) | |||
851 | .mask_set = __pgprot(0), | 861 | .mask_set = __pgprot(0), |
852 | .mask_clr = __pgprot(_PAGE_PRESENT | _PAGE_RW)}; | 862 | .mask_clr = __pgprot(_PAGE_PRESENT | _PAGE_RW)}; |
853 | 863 | ||
854 | return __change_page_attr_set_clr(&cpa); | 864 | return __change_page_attr_set_clr(&cpa, 1); |
855 | } | 865 | } |
856 | 866 | ||
857 | void kernel_map_pages(struct page *page, int numpages, int enable) | 867 | void kernel_map_pages(struct page *page, int numpages, int enable) |
diff --git a/include/asm-x86/pgtable_64.h b/include/asm-x86/pgtable_64.h index bd4740a60f29..7fd5e0e2361e 100644 --- a/include/asm-x86/pgtable_64.h +++ b/include/asm-x86/pgtable_64.h | |||
@@ -246,6 +246,7 @@ static inline int pud_large(pud_t pte) | |||
246 | #define __swp_entry_to_pte(x) ((pte_t) { .pte = (x).val }) | 246 | #define __swp_entry_to_pte(x) ((pte_t) { .pte = (x).val }) |
247 | 247 | ||
248 | extern int kern_addr_valid(unsigned long addr); | 248 | extern int kern_addr_valid(unsigned long addr); |
249 | extern void cleanup_highmap(void); | ||
249 | 250 | ||
250 | #define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ | 251 | #define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ |
251 | remap_pfn_range(vma, vaddr, pfn, size, prot) | 252 | remap_pfn_range(vma, vaddr, pfn, size, prot) |