diff options
-rw-r--r-- | arch/i386/kernel/alternative.c | 40 | ||||
-rw-r--r-- | arch/i386/kernel/kprobes.c | 9 | ||||
-rw-r--r-- | arch/i386/kernel/paravirt.c | 17 | ||||
-rw-r--r-- | arch/i386/mm/init.c | 14 | ||||
-rw-r--r-- | arch/x86_64/kernel/kprobes.c | 10 | ||||
-rw-r--r-- | arch/x86_64/mm/init.c | 10 | ||||
-rw-r--r-- | arch/x86_64/mm/pageattr.c | 2 | ||||
-rw-r--r-- | include/asm-i386/alternative.h | 2 | ||||
-rw-r--r-- | include/asm-x86_64/alternative.h | 2 | ||||
-rw-r--r-- | include/asm-x86_64/pgtable.h | 2 |
10 files changed, 63 insertions, 45 deletions
diff --git a/arch/i386/kernel/alternative.c b/arch/i386/kernel/alternative.c index 0695be538de5..206ea2ca63cc 100644 --- a/arch/i386/kernel/alternative.c +++ b/arch/i386/kernel/alternative.c | |||
@@ -2,8 +2,12 @@ | |||
2 | #include <linux/sched.h> | 2 | #include <linux/sched.h> |
3 | #include <linux/spinlock.h> | 3 | #include <linux/spinlock.h> |
4 | #include <linux/list.h> | 4 | #include <linux/list.h> |
5 | #include <linux/kprobes.h> | ||
6 | #include <linux/mm.h> | ||
7 | #include <linux/vmalloc.h> | ||
5 | #include <asm/alternative.h> | 8 | #include <asm/alternative.h> |
6 | #include <asm/sections.h> | 9 | #include <asm/sections.h> |
10 | #include <asm/pgtable.h> | ||
7 | 11 | ||
8 | #ifdef CONFIG_HOTPLUG_CPU | 12 | #ifdef CONFIG_HOTPLUG_CPU |
9 | static int smp_alt_once; | 13 | static int smp_alt_once; |
@@ -150,7 +154,7 @@ static void nop_out(void *insns, unsigned int len) | |||
150 | unsigned int noplen = len; | 154 | unsigned int noplen = len; |
151 | if (noplen > ASM_NOP_MAX) | 155 | if (noplen > ASM_NOP_MAX) |
152 | noplen = ASM_NOP_MAX; | 156 | noplen = ASM_NOP_MAX; |
153 | memcpy(insns, noptable[noplen], noplen); | 157 | text_poke(insns, noptable[noplen], noplen); |
154 | insns += noplen; | 158 | insns += noplen; |
155 | len -= noplen; | 159 | len -= noplen; |
156 | } | 160 | } |
@@ -202,7 +206,7 @@ static void alternatives_smp_lock(u8 **start, u8 **end, u8 *text, u8 *text_end) | |||
202 | continue; | 206 | continue; |
203 | if (*ptr > text_end) | 207 | if (*ptr > text_end) |
204 | continue; | 208 | continue; |
205 | **ptr = 0xf0; /* lock prefix */ | 209 | text_poke(*ptr, ((unsigned char []){0xf0}), 1); /* add lock prefix */ |
206 | }; | 210 | }; |
207 | } | 211 | } |
208 | 212 | ||
@@ -360,10 +364,6 @@ void apply_paravirt(struct paravirt_patch_site *start, | |||
360 | /* Pad the rest with nops */ | 364 | /* Pad the rest with nops */ |
361 | nop_out(p->instr + used, p->len - used); | 365 | nop_out(p->instr + used, p->len - used); |
362 | } | 366 | } |
363 | |||
364 | /* Sync to be conservative, in case we patched following | ||
365 | * instructions */ | ||
366 | sync_core(); | ||
367 | } | 367 | } |
368 | extern struct paravirt_patch_site __start_parainstructions[], | 368 | extern struct paravirt_patch_site __start_parainstructions[], |
369 | __stop_parainstructions[]; | 369 | __stop_parainstructions[]; |
@@ -406,3 +406,31 @@ void __init alternative_instructions(void) | |||
406 | apply_paravirt(__parainstructions, __parainstructions_end); | 406 | apply_paravirt(__parainstructions, __parainstructions_end); |
407 | local_irq_restore(flags); | 407 | local_irq_restore(flags); |
408 | } | 408 | } |
409 | |||
410 | /* | ||
411 | * Warning: | ||
412 | * When you use this code to patch more than one byte of an instruction | ||
413 | * you need to make sure that other CPUs cannot execute this code in parallel. | ||
414 | * Also no thread must be currently preempted in the middle of these instructions. | ||
415 | * And on the local CPU you need to be protected again NMI or MCE handlers | ||
416 | * seeing an inconsistent instruction while you patch. | ||
417 | */ | ||
418 | void __kprobes text_poke(void *oaddr, unsigned char *opcode, int len) | ||
419 | { | ||
420 | u8 *addr = oaddr; | ||
421 | if (!pte_write(*lookup_address((unsigned long)addr))) { | ||
422 | struct page *p[2] = { virt_to_page(addr), virt_to_page(addr+PAGE_SIZE) }; | ||
423 | addr = vmap(p, 2, VM_MAP, PAGE_KERNEL); | ||
424 | if (!addr) | ||
425 | return; | ||
426 | addr += ((unsigned long)oaddr) % PAGE_SIZE; | ||
427 | } | ||
428 | memcpy(addr, opcode, len); | ||
429 | sync_core(); | ||
430 | /* Not strictly needed, but can speed CPU recovery up. Ignore cross cacheline | ||
431 | case. */ | ||
432 | if (cpu_has_clflush) | ||
433 | asm("clflush (%0) " :: "r" (oaddr) : "memory"); | ||
434 | if (addr != oaddr) | ||
435 | vunmap(addr); | ||
436 | } | ||
diff --git a/arch/i386/kernel/kprobes.c b/arch/i386/kernel/kprobes.c index dde828a333c3..448a50b1324c 100644 --- a/arch/i386/kernel/kprobes.c +++ b/arch/i386/kernel/kprobes.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <asm/cacheflush.h> | 35 | #include <asm/cacheflush.h> |
36 | #include <asm/desc.h> | 36 | #include <asm/desc.h> |
37 | #include <asm/uaccess.h> | 37 | #include <asm/uaccess.h> |
38 | #include <asm/alternative.h> | ||
38 | 39 | ||
39 | void jprobe_return_end(void); | 40 | void jprobe_return_end(void); |
40 | 41 | ||
@@ -169,16 +170,12 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) | |||
169 | 170 | ||
170 | void __kprobes arch_arm_kprobe(struct kprobe *p) | 171 | void __kprobes arch_arm_kprobe(struct kprobe *p) |
171 | { | 172 | { |
172 | *p->addr = BREAKPOINT_INSTRUCTION; | 173 | text_poke(p->addr, ((unsigned char []){BREAKPOINT_INSTRUCTION}), 1); |
173 | flush_icache_range((unsigned long) p->addr, | ||
174 | (unsigned long) p->addr + sizeof(kprobe_opcode_t)); | ||
175 | } | 174 | } |
176 | 175 | ||
177 | void __kprobes arch_disarm_kprobe(struct kprobe *p) | 176 | void __kprobes arch_disarm_kprobe(struct kprobe *p) |
178 | { | 177 | { |
179 | *p->addr = p->opcode; | 178 | text_poke(p->addr, &p->opcode, 1); |
180 | flush_icache_range((unsigned long) p->addr, | ||
181 | (unsigned long) p->addr + sizeof(kprobe_opcode_t)); | ||
182 | } | 179 | } |
183 | 180 | ||
184 | void __kprobes arch_remove_kprobe(struct kprobe *p) | 181 | void __kprobes arch_remove_kprobe(struct kprobe *p) |
diff --git a/arch/i386/kernel/paravirt.c b/arch/i386/kernel/paravirt.c index 53f07a8275e3..79c167fcaee9 100644 --- a/arch/i386/kernel/paravirt.c +++ b/arch/i386/kernel/paravirt.c | |||
@@ -124,20 +124,28 @@ unsigned paravirt_patch_ignore(unsigned len) | |||
124 | return len; | 124 | return len; |
125 | } | 125 | } |
126 | 126 | ||
127 | struct branch { | ||
128 | unsigned char opcode; | ||
129 | u32 delta; | ||
130 | } __attribute__((packed)); | ||
131 | |||
127 | unsigned paravirt_patch_call(void *target, u16 tgt_clobbers, | 132 | unsigned paravirt_patch_call(void *target, u16 tgt_clobbers, |
128 | void *site, u16 site_clobbers, | 133 | void *site, u16 site_clobbers, |
129 | unsigned len) | 134 | unsigned len) |
130 | { | 135 | { |
131 | unsigned char *call = site; | 136 | unsigned char *call = site; |
132 | unsigned long delta = (unsigned long)target - (unsigned long)(call+5); | 137 | unsigned long delta = (unsigned long)target - (unsigned long)(call+5); |
138 | struct branch b; | ||
133 | 139 | ||
134 | if (tgt_clobbers & ~site_clobbers) | 140 | if (tgt_clobbers & ~site_clobbers) |
135 | return len; /* target would clobber too much for this site */ | 141 | return len; /* target would clobber too much for this site */ |
136 | if (len < 5) | 142 | if (len < 5) |
137 | return len; /* call too long for patch site */ | 143 | return len; /* call too long for patch site */ |
138 | 144 | ||
139 | *call++ = 0xe8; /* call */ | 145 | b.opcode = 0xe8; /* call */ |
140 | *(unsigned long *)call = delta; | 146 | b.delta = delta; |
147 | BUILD_BUG_ON(sizeof(b) != 5); | ||
148 | text_poke(call, (unsigned char *)&b, 5); | ||
141 | 149 | ||
142 | return 5; | 150 | return 5; |
143 | } | 151 | } |
@@ -150,8 +158,9 @@ unsigned paravirt_patch_jmp(void *target, void *site, unsigned len) | |||
150 | if (len < 5) | 158 | if (len < 5) |
151 | return len; /* call too long for patch site */ | 159 | return len; /* call too long for patch site */ |
152 | 160 | ||
153 | *jmp++ = 0xe9; /* jmp */ | 161 | b.opcode = 0xe9; /* jmp */ |
154 | *(unsigned long *)jmp = delta; | 162 | b.delta = delta; |
163 | text_poke(call, (unsigned char *)&b, 5); | ||
155 | 164 | ||
156 | return 5; | 165 | return 5; |
157 | } | 166 | } |
diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index e1a9a805c445..c3b9905af2d5 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c | |||
@@ -800,17 +800,9 @@ void mark_rodata_ro(void) | |||
800 | unsigned long start = PFN_ALIGN(_text); | 800 | unsigned long start = PFN_ALIGN(_text); |
801 | unsigned long size = PFN_ALIGN(_etext) - start; | 801 | unsigned long size = PFN_ALIGN(_etext) - start; |
802 | 802 | ||
803 | #ifndef CONFIG_KPROBES | 803 | change_page_attr(virt_to_page(start), |
804 | #ifdef CONFIG_HOTPLUG_CPU | 804 | size >> PAGE_SHIFT, PAGE_KERNEL_RX); |
805 | /* It must still be possible to apply SMP alternatives. */ | 805 | printk("Write protecting the kernel text: %luk\n", size >> 10); |
806 | if (num_possible_cpus() <= 1) | ||
807 | #endif | ||
808 | { | ||
809 | change_page_attr(virt_to_page(start), | ||
810 | size >> PAGE_SHIFT, PAGE_KERNEL_RX); | ||
811 | printk("Write protecting the kernel text: %luk\n", size >> 10); | ||
812 | } | ||
813 | #endif | ||
814 | start += size; | 806 | start += size; |
815 | size = (unsigned long)__end_rodata - start; | 807 | size = (unsigned long)__end_rodata - start; |
816 | change_page_attr(virt_to_page(start), | 808 | change_page_attr(virt_to_page(start), |
diff --git a/arch/x86_64/kernel/kprobes.c b/arch/x86_64/kernel/kprobes.c index d4a0d0ac9935..a30e004682e2 100644 --- a/arch/x86_64/kernel/kprobes.c +++ b/arch/x86_64/kernel/kprobes.c | |||
@@ -39,9 +39,9 @@ | |||
39 | #include <linux/module.h> | 39 | #include <linux/module.h> |
40 | #include <linux/kdebug.h> | 40 | #include <linux/kdebug.h> |
41 | 41 | ||
42 | #include <asm/cacheflush.h> | ||
43 | #include <asm/pgtable.h> | 42 | #include <asm/pgtable.h> |
44 | #include <asm/uaccess.h> | 43 | #include <asm/uaccess.h> |
44 | #include <asm/alternative.h> | ||
45 | 45 | ||
46 | void jprobe_return_end(void); | 46 | void jprobe_return_end(void); |
47 | static void __kprobes arch_copy_kprobe(struct kprobe *p); | 47 | static void __kprobes arch_copy_kprobe(struct kprobe *p); |
@@ -209,16 +209,12 @@ static void __kprobes arch_copy_kprobe(struct kprobe *p) | |||
209 | 209 | ||
210 | void __kprobes arch_arm_kprobe(struct kprobe *p) | 210 | void __kprobes arch_arm_kprobe(struct kprobe *p) |
211 | { | 211 | { |
212 | *p->addr = BREAKPOINT_INSTRUCTION; | 212 | text_poke(p->addr, ((unsigned char []){BREAKPOINT_INSTRUCTION}), 1); |
213 | flush_icache_range((unsigned long) p->addr, | ||
214 | (unsigned long) p->addr + sizeof(kprobe_opcode_t)); | ||
215 | } | 213 | } |
216 | 214 | ||
217 | void __kprobes arch_disarm_kprobe(struct kprobe *p) | 215 | void __kprobes arch_disarm_kprobe(struct kprobe *p) |
218 | { | 216 | { |
219 | *p->addr = p->opcode; | 217 | text_poke(p->addr, &p->opcode, 1); |
220 | flush_icache_range((unsigned long) p->addr, | ||
221 | (unsigned long) p->addr + sizeof(kprobe_opcode_t)); | ||
222 | } | 218 | } |
223 | 219 | ||
224 | void __kprobes arch_remove_kprobe(struct kprobe *p) | 220 | void __kprobes arch_remove_kprobe(struct kprobe *p) |
diff --git a/arch/x86_64/mm/init.c b/arch/x86_64/mm/init.c index 2044fa961c07..314e12b2209f 100644 --- a/arch/x86_64/mm/init.c +++ b/arch/x86_64/mm/init.c | |||
@@ -600,16 +600,6 @@ void mark_rodata_ro(void) | |||
600 | { | 600 | { |
601 | unsigned long start = (unsigned long)_stext, end; | 601 | unsigned long start = (unsigned long)_stext, end; |
602 | 602 | ||
603 | #ifdef CONFIG_HOTPLUG_CPU | ||
604 | /* It must still be possible to apply SMP alternatives. */ | ||
605 | if (num_possible_cpus() > 1) | ||
606 | start = (unsigned long)_etext; | ||
607 | #endif | ||
608 | |||
609 | #ifdef CONFIG_KPROBES | ||
610 | start = (unsigned long)__start_rodata; | ||
611 | #endif | ||
612 | |||
613 | end = (unsigned long)__end_rodata; | 603 | end = (unsigned long)__end_rodata; |
614 | start = (start + PAGE_SIZE - 1) & PAGE_MASK; | 604 | start = (start + PAGE_SIZE - 1) & PAGE_MASK; |
615 | end &= PAGE_MASK; | 605 | end &= PAGE_MASK; |
diff --git a/arch/x86_64/mm/pageattr.c b/arch/x86_64/mm/pageattr.c index 36377b6b8efe..7e161c698af4 100644 --- a/arch/x86_64/mm/pageattr.c +++ b/arch/x86_64/mm/pageattr.c | |||
@@ -13,7 +13,7 @@ | |||
13 | #include <asm/tlbflush.h> | 13 | #include <asm/tlbflush.h> |
14 | #include <asm/io.h> | 14 | #include <asm/io.h> |
15 | 15 | ||
16 | static inline pte_t *lookup_address(unsigned long address) | 16 | pte_t *lookup_address(unsigned long address) |
17 | { | 17 | { |
18 | pgd_t *pgd = pgd_offset_k(address); | 18 | pgd_t *pgd = pgd_offset_k(address); |
19 | pud_t *pud; | 19 | pud_t *pud; |
diff --git a/include/asm-i386/alternative.h b/include/asm-i386/alternative.h index eb7da5402bfa..bda6c810c0f4 100644 --- a/include/asm-i386/alternative.h +++ b/include/asm-i386/alternative.h | |||
@@ -149,4 +149,6 @@ apply_paravirt(struct paravirt_patch_site *start, | |||
149 | #define __parainstructions_end NULL | 149 | #define __parainstructions_end NULL |
150 | #endif | 150 | #endif |
151 | 151 | ||
152 | extern void text_poke(void *addr, unsigned char *opcode, int len); | ||
153 | |||
152 | #endif /* _I386_ALTERNATIVE_H */ | 154 | #endif /* _I386_ALTERNATIVE_H */ |
diff --git a/include/asm-x86_64/alternative.h b/include/asm-x86_64/alternative.h index eea7aecfac78..ab161e810151 100644 --- a/include/asm-x86_64/alternative.h +++ b/include/asm-x86_64/alternative.h | |||
@@ -154,4 +154,6 @@ apply_paravirt(struct paravirt_patch *start, struct paravirt_patch *end) | |||
154 | #define __parainstructions_end NULL | 154 | #define __parainstructions_end NULL |
155 | #endif | 155 | #endif |
156 | 156 | ||
157 | extern void text_poke(void *addr, unsigned char *opcode, int len); | ||
158 | |||
157 | #endif /* _X86_64_ALTERNATIVE_H */ | 159 | #endif /* _X86_64_ALTERNATIVE_H */ |
diff --git a/include/asm-x86_64/pgtable.h b/include/asm-x86_64/pgtable.h index 60cff1e4f7a3..c9d8764c89d1 100644 --- a/include/asm-x86_64/pgtable.h +++ b/include/asm-x86_64/pgtable.h | |||
@@ -403,6 +403,8 @@ extern struct list_head pgd_list; | |||
403 | 403 | ||
404 | extern int kern_addr_valid(unsigned long addr); | 404 | extern int kern_addr_valid(unsigned long addr); |
405 | 405 | ||
406 | pte_t *lookup_address(unsigned long addr); | ||
407 | |||
406 | #define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ | 408 | #define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ |
407 | remap_pfn_range(vma, vaddr, pfn, size, prot) | 409 | remap_pfn_range(vma, vaddr, pfn, size, prot) |
408 | 410 | ||