aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/i386/kernel/alternative.c40
-rw-r--r--arch/i386/kernel/kprobes.c9
-rw-r--r--arch/i386/kernel/paravirt.c17
-rw-r--r--arch/i386/mm/init.c14
-rw-r--r--arch/x86_64/kernel/kprobes.c10
-rw-r--r--arch/x86_64/mm/init.c10
-rw-r--r--arch/x86_64/mm/pageattr.c2
-rw-r--r--include/asm-i386/alternative.h2
-rw-r--r--include/asm-x86_64/alternative.h2
-rw-r--r--include/asm-x86_64/pgtable.h2
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
9static int smp_alt_once; 13static 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}
368extern struct paravirt_patch_site __start_parainstructions[], 368extern 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 */
418void __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
39void jprobe_return_end(void); 40void jprobe_return_end(void);
40 41
@@ -169,16 +170,12 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
169 170
170void __kprobes arch_arm_kprobe(struct kprobe *p) 171void __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
177void __kprobes arch_disarm_kprobe(struct kprobe *p) 176void __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
184void __kprobes arch_remove_kprobe(struct kprobe *p) 181void __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
127struct branch {
128 unsigned char opcode;
129 u32 delta;
130} __attribute__((packed));
131
127unsigned paravirt_patch_call(void *target, u16 tgt_clobbers, 132unsigned 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
46void jprobe_return_end(void); 46void jprobe_return_end(void);
47static void __kprobes arch_copy_kprobe(struct kprobe *p); 47static void __kprobes arch_copy_kprobe(struct kprobe *p);
@@ -209,16 +209,12 @@ static void __kprobes arch_copy_kprobe(struct kprobe *p)
209 209
210void __kprobes arch_arm_kprobe(struct kprobe *p) 210void __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
217void __kprobes arch_disarm_kprobe(struct kprobe *p) 215void __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
224void __kprobes arch_remove_kprobe(struct kprobe *p) 220void __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
16static inline pte_t *lookup_address(unsigned long address) 16pte_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
152extern 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
157extern 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
404extern int kern_addr_valid(unsigned long addr); 404extern int kern_addr_valid(unsigned long addr);
405 405
406pte_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