aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390
diff options
context:
space:
mode:
authorHeiko Carstens <heiko.carstens@de.ibm.com>2013-09-11 17:24:14 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-09-11 18:58:52 -0400
commit63c40436a1afc837f3ace6b5a39c547bc91c20bc (patch)
tree0e32113be82ec272d56c1278e1ca87cbf6ac88ba /arch/s390
parentaf96397de8600232effbff43dc8b4ca20ddc02b1 (diff)
s390/kprobes: add support for pc-relative long displacement instructions
With the general-instruction extension facility (z10) a couple of instructions with a pc-relative long displacement were introduced. The kprobes support for these instructions however was never implemented. In result, if anybody ever put a probe on any of these instructions the result would have been random behaviour after the instruction got executed within the insn slot. So lets add the missing handling for these instructions. Since all of the new instructions have 32 bit signed displacement the easiest solution is to allocate an insn slot that is within the same 2GB area like the original instruction and patch the displacement field. Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Reviewed-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: Martin Schwidefsky <schwidefsky@de.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/s390')
-rw-r--r--arch/s390/include/asm/kprobes.h4
-rw-r--r--arch/s390/kernel/kprobes.c144
2 files changed, 140 insertions, 8 deletions
diff --git a/arch/s390/include/asm/kprobes.h b/arch/s390/include/asm/kprobes.h
index dcf6948a875c..4176dfe0fba1 100644
--- a/arch/s390/include/asm/kprobes.h
+++ b/arch/s390/include/asm/kprobes.h
@@ -31,6 +31,8 @@
31#include <linux/ptrace.h> 31#include <linux/ptrace.h>
32#include <linux/percpu.h> 32#include <linux/percpu.h>
33 33
34#define __ARCH_WANT_KPROBES_INSN_SLOT
35
34struct pt_regs; 36struct pt_regs;
35struct kprobe; 37struct kprobe;
36 38
@@ -57,7 +59,7 @@ typedef u16 kprobe_opcode_t;
57/* Architecture specific copy of original instruction */ 59/* Architecture specific copy of original instruction */
58struct arch_specific_insn { 60struct arch_specific_insn {
59 /* copy of original instruction */ 61 /* copy of original instruction */
60 kprobe_opcode_t insn[MAX_INSN_SIZE]; 62 kprobe_opcode_t *insn;
61}; 63};
62 64
63struct prev_kprobe { 65struct prev_kprobe {
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c
index adbbe7f1cb0d..0ce9fb245034 100644
--- a/arch/s390/kernel/kprobes.c
+++ b/arch/s390/kernel/kprobes.c
@@ -37,6 +37,26 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
37 37
38struct kretprobe_blackpoint kretprobe_blacklist[] = { }; 38struct kretprobe_blackpoint kretprobe_blacklist[] = { };
39 39
40DEFINE_INSN_CACHE_OPS(dmainsn);
41
42static void *alloc_dmainsn_page(void)
43{
44 return (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
45}
46
47static void free_dmainsn_page(void *page)
48{
49 free_page((unsigned long)page);
50}
51
52struct kprobe_insn_cache kprobe_dmainsn_slots = {
53 .mutex = __MUTEX_INITIALIZER(kprobe_dmainsn_slots.mutex),
54 .alloc = alloc_dmainsn_page,
55 .free = free_dmainsn_page,
56 .pages = LIST_HEAD_INIT(kprobe_dmainsn_slots.pages),
57 .insn_size = MAX_INSN_SIZE,
58};
59
40static int __kprobes is_prohibited_opcode(kprobe_opcode_t *insn) 60static int __kprobes is_prohibited_opcode(kprobe_opcode_t *insn)
41{ 61{
42 switch (insn[0] >> 8) { 62 switch (insn[0] >> 8) {
@@ -100,9 +120,8 @@ static int __kprobes get_fixup_type(kprobe_opcode_t *insn)
100 fixup |= FIXUP_RETURN_REGISTER; 120 fixup |= FIXUP_RETURN_REGISTER;
101 break; 121 break;
102 case 0xc0: 122 case 0xc0:
103 if ((insn[0] & 0x0f) == 0x00 || /* larl */ 123 if ((insn[0] & 0x0f) == 0x05) /* brasl */
104 (insn[0] & 0x0f) == 0x05) /* brasl */ 124 fixup |= FIXUP_RETURN_REGISTER;
105 fixup |= FIXUP_RETURN_REGISTER;
106 break; 125 break;
107 case 0xeb: 126 case 0xeb:
108 switch (insn[2] & 0xff) { 127 switch (insn[2] & 0xff) {
@@ -134,18 +153,128 @@ static int __kprobes get_fixup_type(kprobe_opcode_t *insn)
134 return fixup; 153 return fixup;
135} 154}
136 155
156static int __kprobes is_insn_relative_long(kprobe_opcode_t *insn)
157{
158 /* Check if we have a RIL-b or RIL-c format instruction which
159 * we need to modify in order to avoid instruction emulation. */
160 switch (insn[0] >> 8) {
161 case 0xc0:
162 if ((insn[0] & 0x0f) == 0x00) /* larl */
163 return true;
164 break;
165 case 0xc4:
166 switch (insn[0] & 0x0f) {
167 case 0x02: /* llhrl */
168 case 0x04: /* lghrl */
169 case 0x05: /* lhrl */
170 case 0x06: /* llghrl */
171 case 0x07: /* sthrl */
172 case 0x08: /* lgrl */
173 case 0x0b: /* stgrl */
174 case 0x0c: /* lgfrl */
175 case 0x0d: /* lrl */
176 case 0x0e: /* llgfrl */
177 case 0x0f: /* strl */
178 return true;
179 }
180 break;
181 case 0xc6:
182 switch (insn[0] & 0x0f) {
183 case 0x00: /* exrl */
184 case 0x02: /* pfdrl */
185 case 0x04: /* cghrl */
186 case 0x05: /* chrl */
187 case 0x06: /* clghrl */
188 case 0x07: /* clhrl */
189 case 0x08: /* cgrl */
190 case 0x0a: /* clgrl */
191 case 0x0c: /* cgfrl */
192 case 0x0d: /* crl */
193 case 0x0e: /* clgfrl */
194 case 0x0f: /* clrl */
195 return true;
196 }
197 break;
198 }
199 return false;
200}
201
202static void __kprobes copy_instruction(struct kprobe *p)
203{
204 s64 disp, new_disp;
205 u64 addr, new_addr;
206
207 memcpy(p->ainsn.insn, p->addr, ((p->opcode >> 14) + 3) & -2);
208 if (!is_insn_relative_long(p->ainsn.insn))
209 return;
210 /*
211 * For pc-relative instructions in RIL-b or RIL-c format patch the
212 * RI2 displacement field. We have already made sure that the insn
213 * slot for the patched instruction is within the same 2GB area
214 * as the original instruction (either kernel image or module area).
215 * Therefore the new displacement will always fit.
216 */
217 disp = *(s32 *)&p->ainsn.insn[1];
218 addr = (u64)(unsigned long)p->addr;
219 new_addr = (u64)(unsigned long)p->ainsn.insn;
220 new_disp = ((addr + (disp * 2)) - new_addr) / 2;
221 *(s32 *)&p->ainsn.insn[1] = new_disp;
222}
223
224static inline int is_kernel_addr(void *addr)
225{
226 return addr < (void *)_end;
227}
228
229static inline int is_module_addr(void *addr)
230{
231#ifdef CONFIG_64BIT
232 BUILD_BUG_ON(MODULES_LEN > (1UL << 31));
233 if (addr < (void *)MODULES_VADDR)
234 return 0;
235 if (addr > (void *)MODULES_END)
236 return 0;
237#endif
238 return 1;
239}
240
241static int __kprobes s390_get_insn_slot(struct kprobe *p)
242{
243 /*
244 * Get an insn slot that is within the same 2GB area like the original
245 * instruction. That way instructions with a 32bit signed displacement
246 * field can be patched and executed within the insn slot.
247 */
248 p->ainsn.insn = NULL;
249 if (is_kernel_addr(p->addr))
250 p->ainsn.insn = get_dmainsn_slot();
251 if (is_module_addr(p->addr))
252 p->ainsn.insn = get_insn_slot();
253 return p->ainsn.insn ? 0 : -ENOMEM;
254}
255
256static void __kprobes s390_free_insn_slot(struct kprobe *p)
257{
258 if (!p->ainsn.insn)
259 return;
260 if (is_kernel_addr(p->addr))
261 free_dmainsn_slot(p->ainsn.insn, 0);
262 else
263 free_insn_slot(p->ainsn.insn, 0);
264 p->ainsn.insn = NULL;
265}
266
137int __kprobes arch_prepare_kprobe(struct kprobe *p) 267int __kprobes arch_prepare_kprobe(struct kprobe *p)
138{ 268{
139 if ((unsigned long) p->addr & 0x01) 269 if ((unsigned long) p->addr & 0x01)
140 return -EINVAL; 270 return -EINVAL;
141
142 /* Make sure the probe isn't going on a difficult instruction */ 271 /* Make sure the probe isn't going on a difficult instruction */
143 if (is_prohibited_opcode(p->addr)) 272 if (is_prohibited_opcode(p->addr))
144 return -EINVAL; 273 return -EINVAL;
145 274 if (s390_get_insn_slot(p))
275 return -ENOMEM;
146 p->opcode = *p->addr; 276 p->opcode = *p->addr;
147 memcpy(p->ainsn.insn, p->addr, ((p->opcode >> 14) + 3) & -2); 277 copy_instruction(p);
148
149 return 0; 278 return 0;
150} 279}
151 280
@@ -186,6 +315,7 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p)
186 315
187void __kprobes arch_remove_kprobe(struct kprobe *p) 316void __kprobes arch_remove_kprobe(struct kprobe *p)
188{ 317{
318 s390_free_insn_slot(p);
189} 319}
190 320
191static void __kprobes enable_singlestep(struct kprobe_ctlblk *kcb, 321static void __kprobes enable_singlestep(struct kprobe_ctlblk *kcb,