aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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,