diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2011-01-05 06:47:19 -0500 |
---|---|---|
committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2011-01-05 06:47:23 -0500 |
commit | ba640a591574036ab22cd32b47897340b0605342 (patch) | |
tree | 271783643d438caa0c41a5dbc68e712a7a4c5484 /arch/s390/kernel/kprobes.c | |
parent | 5a8b589f8a35b2c69d1819e3365825e4385a844c (diff) |
[S390] kprobes: instruction fixup
Determine instruction fixup details in resume_execution, no need to do
it beforehand. Remove fixup, ilen and reg from arch_specific_insn.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/kprobes.c')
-rw-r--r-- | arch/s390/kernel/kprobes.c | 130 |
1 files changed, 57 insertions, 73 deletions
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index 1e75ec523577..fcbc25836879 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c | |||
@@ -37,29 +37,9 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); | |||
37 | 37 | ||
38 | struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}}; | 38 | struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}}; |
39 | 39 | ||
40 | int __kprobes arch_prepare_kprobe(struct kprobe *p) | 40 | static int __kprobes is_prohibited_opcode(kprobe_opcode_t *insn) |
41 | { | 41 | { |
42 | /* Make sure the probe isn't going on a difficult instruction */ | 42 | switch (insn[0] >> 8) { |
43 | if (is_prohibited_opcode((kprobe_opcode_t *) p->addr)) | ||
44 | return -EINVAL; | ||
45 | |||
46 | if ((unsigned long)p->addr & 0x01) | ||
47 | return -EINVAL; | ||
48 | |||
49 | /* Use the get_insn_slot() facility for correctness */ | ||
50 | if (!(p->ainsn.insn = get_insn_slot())) | ||
51 | return -ENOMEM; | ||
52 | |||
53 | memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); | ||
54 | |||
55 | get_instruction_type(&p->ainsn); | ||
56 | p->opcode = *p->addr; | ||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | int __kprobes is_prohibited_opcode(kprobe_opcode_t *instruction) | ||
61 | { | ||
62 | switch (*(__u8 *) instruction) { | ||
63 | case 0x0c: /* bassm */ | 43 | case 0x0c: /* bassm */ |
64 | case 0x0b: /* bsm */ | 44 | case 0x0b: /* bsm */ |
65 | case 0x83: /* diag */ | 45 | case 0x83: /* diag */ |
@@ -68,7 +48,7 @@ int __kprobes is_prohibited_opcode(kprobe_opcode_t *instruction) | |||
68 | case 0xad: /* stosm */ | 48 | case 0xad: /* stosm */ |
69 | return -EINVAL; | 49 | return -EINVAL; |
70 | } | 50 | } |
71 | switch (*(__u16 *) instruction) { | 51 | switch (insn[0]) { |
72 | case 0x0101: /* pr */ | 52 | case 0x0101: /* pr */ |
73 | case 0xb25a: /* bsa */ | 53 | case 0xb25a: /* bsa */ |
74 | case 0xb240: /* bakr */ | 54 | case 0xb240: /* bakr */ |
@@ -81,80 +61,79 @@ int __kprobes is_prohibited_opcode(kprobe_opcode_t *instruction) | |||
81 | return 0; | 61 | return 0; |
82 | } | 62 | } |
83 | 63 | ||
84 | void __kprobes get_instruction_type(struct arch_specific_insn *ainsn) | 64 | static int __kprobes get_fixup_type(kprobe_opcode_t *insn) |
85 | { | 65 | { |
86 | /* default fixup method */ | 66 | /* default fixup method */ |
87 | ainsn->fixup = FIXUP_PSW_NORMAL; | 67 | int fixup = FIXUP_PSW_NORMAL; |
88 | |||
89 | /* save r1 operand */ | ||
90 | ainsn->reg = (*ainsn->insn & 0xf0) >> 4; | ||
91 | |||
92 | /* save the instruction length (pop 5-5) in bytes */ | ||
93 | switch (*(__u8 *) (ainsn->insn) >> 6) { | ||
94 | case 0: | ||
95 | ainsn->ilen = 2; | ||
96 | break; | ||
97 | case 1: | ||
98 | case 2: | ||
99 | ainsn->ilen = 4; | ||
100 | break; | ||
101 | case 3: | ||
102 | ainsn->ilen = 6; | ||
103 | break; | ||
104 | } | ||
105 | 68 | ||
106 | switch (*(__u8 *) ainsn->insn) { | 69 | switch (insn[0] >> 8) { |
107 | case 0x05: /* balr */ | 70 | case 0x05: /* balr */ |
108 | case 0x0d: /* basr */ | 71 | case 0x0d: /* basr */ |
109 | ainsn->fixup = FIXUP_RETURN_REGISTER; | 72 | fixup = FIXUP_RETURN_REGISTER; |
110 | /* if r2 = 0, no branch will be taken */ | 73 | /* if r2 = 0, no branch will be taken */ |
111 | if ((*ainsn->insn & 0x0f) == 0) | 74 | if ((insn[0] & 0x0f) == 0) |
112 | ainsn->fixup |= FIXUP_BRANCH_NOT_TAKEN; | 75 | fixup |= FIXUP_BRANCH_NOT_TAKEN; |
113 | break; | 76 | break; |
114 | case 0x06: /* bctr */ | 77 | case 0x06: /* bctr */ |
115 | case 0x07: /* bcr */ | 78 | case 0x07: /* bcr */ |
116 | ainsn->fixup = FIXUP_BRANCH_NOT_TAKEN; | 79 | fixup = FIXUP_BRANCH_NOT_TAKEN; |
117 | break; | 80 | break; |
118 | case 0x45: /* bal */ | 81 | case 0x45: /* bal */ |
119 | case 0x4d: /* bas */ | 82 | case 0x4d: /* bas */ |
120 | ainsn->fixup = FIXUP_RETURN_REGISTER; | 83 | fixup = FIXUP_RETURN_REGISTER; |
121 | break; | 84 | break; |
122 | case 0x47: /* bc */ | 85 | case 0x47: /* bc */ |
123 | case 0x46: /* bct */ | 86 | case 0x46: /* bct */ |
124 | case 0x86: /* bxh */ | 87 | case 0x86: /* bxh */ |
125 | case 0x87: /* bxle */ | 88 | case 0x87: /* bxle */ |
126 | ainsn->fixup = FIXUP_BRANCH_NOT_TAKEN; | 89 | fixup = FIXUP_BRANCH_NOT_TAKEN; |
127 | break; | 90 | break; |
128 | case 0x82: /* lpsw */ | 91 | case 0x82: /* lpsw */ |
129 | ainsn->fixup = FIXUP_NOT_REQUIRED; | 92 | fixup = FIXUP_NOT_REQUIRED; |
130 | break; | 93 | break; |
131 | case 0xb2: /* lpswe */ | 94 | case 0xb2: /* lpswe */ |
132 | if (*(((__u8 *) ainsn->insn) + 1) == 0xb2) { | 95 | if ((insn[0] & 0xff) == 0xb2) |
133 | ainsn->fixup = FIXUP_NOT_REQUIRED; | 96 | fixup = FIXUP_NOT_REQUIRED; |
134 | } | ||
135 | break; | 97 | break; |
136 | case 0xa7: /* bras */ | 98 | case 0xa7: /* bras */ |
137 | if ((*ainsn->insn & 0x0f) == 0x05) { | 99 | if ((insn[0] & 0x0f) == 0x05) |
138 | ainsn->fixup |= FIXUP_RETURN_REGISTER; | 100 | fixup |= FIXUP_RETURN_REGISTER; |
139 | } | ||
140 | break; | 101 | break; |
141 | case 0xc0: | 102 | case 0xc0: |
142 | if ((*ainsn->insn & 0x0f) == 0x00 /* larl */ | 103 | if ((insn[0] & 0x0f) == 0x00 || /* larl */ |
143 | || (*ainsn->insn & 0x0f) == 0x05) /* brasl */ | 104 | (insn[0] & 0x0f) == 0x05) /* brasl */ |
144 | ainsn->fixup |= FIXUP_RETURN_REGISTER; | 105 | fixup |= FIXUP_RETURN_REGISTER; |
145 | break; | 106 | break; |
146 | case 0xeb: | 107 | case 0xeb: |
147 | if (*(((__u8 *) ainsn->insn) + 5 ) == 0x44 || /* bxhg */ | 108 | if ((insn[2] & 0xff) == 0x44 || /* bxhg */ |
148 | *(((__u8 *) ainsn->insn) + 5) == 0x45) {/* bxleg */ | 109 | (insn[2] & 0xff) == 0x45) /* bxleg */ |
149 | ainsn->fixup = FIXUP_BRANCH_NOT_TAKEN; | 110 | fixup = FIXUP_BRANCH_NOT_TAKEN; |
150 | } | ||
151 | break; | 111 | break; |
152 | case 0xe3: /* bctg */ | 112 | case 0xe3: /* bctg */ |
153 | if (*(((__u8 *) ainsn->insn) + 5) == 0x46) { | 113 | if ((insn[2] & 0xff) == 0x46) |
154 | ainsn->fixup = FIXUP_BRANCH_NOT_TAKEN; | 114 | fixup = FIXUP_BRANCH_NOT_TAKEN; |
155 | } | ||
156 | break; | 115 | break; |
157 | } | 116 | } |
117 | return fixup; | ||
118 | } | ||
119 | |||
120 | int __kprobes arch_prepare_kprobe(struct kprobe *p) | ||
121 | { | ||
122 | if ((unsigned long) p->addr & 0x01) | ||
123 | return -EINVAL; | ||
124 | |||
125 | /* Make sure the probe isn't going on a difficult instruction */ | ||
126 | if (is_prohibited_opcode((kprobe_opcode_t *) p->addr)) | ||
127 | return -EINVAL; | ||
128 | |||
129 | /* Use the get_insn_slot() facility for correctness */ | ||
130 | if (!(p->ainsn.insn = get_insn_slot())) | ||
131 | return -ENOMEM; | ||
132 | |||
133 | p->opcode = *p->addr; | ||
134 | memcpy(p->ainsn.insn, p->addr, ((p->opcode >> 14) + 3) & -2); | ||
135 | |||
136 | return 0; | ||
158 | } | 137 | } |
159 | 138 | ||
160 | struct ins_replace_args { | 139 | struct ins_replace_args { |
@@ -444,17 +423,22 @@ static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs) | |||
444 | { | 423 | { |
445 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | 424 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); |
446 | unsigned long ip = regs->psw.addr & PSW_ADDR_INSN; | 425 | unsigned long ip = regs->psw.addr & PSW_ADDR_INSN; |
426 | int fixup = get_fixup_type(p->ainsn.insn); | ||
447 | 427 | ||
448 | if (p->ainsn.fixup & FIXUP_PSW_NORMAL) | 428 | if (fixup & FIXUP_PSW_NORMAL) |
449 | ip += (unsigned long) p->addr - (unsigned long) p->ainsn.insn; | 429 | ip += (unsigned long) p->addr - (unsigned long) p->ainsn.insn; |
450 | 430 | ||
451 | if (p->ainsn.fixup & FIXUP_BRANCH_NOT_TAKEN) | 431 | if (fixup & FIXUP_BRANCH_NOT_TAKEN) { |
452 | if (ip - (unsigned long) p->ainsn.insn == p->ainsn.ilen) | 432 | int ilen = ((p->ainsn.insn[0] >> 14) + 3) & -2; |
453 | ip = (unsigned long) p->addr + p->ainsn.ilen; | 433 | if (ip - (unsigned long) p->ainsn.insn == ilen) |
434 | ip = (unsigned long) p->addr + ilen; | ||
435 | } | ||
454 | 436 | ||
455 | if (p->ainsn.fixup & FIXUP_RETURN_REGISTER) | 437 | if (fixup & FIXUP_RETURN_REGISTER) { |
456 | regs->gprs[p->ainsn.reg] += (unsigned long) p->addr - | 438 | int reg = (p->ainsn.insn[0] & 0xf0) >> 4; |
457 | (unsigned long) p->ainsn.insn; | 439 | regs->gprs[reg] += (unsigned long) p->addr - |
440 | (unsigned long) p->ainsn.insn; | ||
441 | } | ||
458 | 442 | ||
459 | disable_singlestep(kcb, regs, ip); | 443 | disable_singlestep(kcb, regs, ip); |
460 | } | 444 | } |