diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/ia64/kernel/kprobes.c | 191 |
1 files changed, 121 insertions, 70 deletions
diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c index 98bef04d9484..5d5344681555 100644 --- a/arch/ia64/kernel/kprobes.c +++ b/arch/ia64/kernel/kprobes.c | |||
@@ -81,90 +81,141 @@ static enum instruction_type bundle_encoding[32][3] = { | |||
81 | { u, u, u }, /* 1F */ | 81 | { u, u, u }, /* 1F */ |
82 | }; | 82 | }; |
83 | 83 | ||
84 | int arch_prepare_kprobe(struct kprobe *p) | 84 | /* |
85 | * In this function we check to see if the instruction | ||
86 | * is IP relative instruction and update the kprobe | ||
87 | * inst flag accordingly | ||
88 | */ | ||
89 | static void update_kprobe_inst_flag(uint template, uint slot, uint major_opcode, | ||
90 | unsigned long kprobe_inst, struct kprobe *p) | ||
85 | { | 91 | { |
86 | unsigned long addr = (unsigned long) p->addr; | ||
87 | unsigned long *bundle_addr = (unsigned long *)(addr & ~0xFULL); | ||
88 | unsigned long slot = addr & 0xf; | ||
89 | unsigned long template; | ||
90 | unsigned long major_opcode = 0; | ||
91 | unsigned long lx_type_inst = 0; | ||
92 | unsigned long kprobe_inst = 0; | ||
93 | bundle_t *bundle = &p->ainsn.insn.bundle; | ||
94 | |||
95 | memcpy(&p->opcode.bundle, bundle_addr, sizeof(bundle_t)); | ||
96 | memcpy(&p->ainsn.insn.bundle, bundle_addr, sizeof(bundle_t)); | ||
97 | |||
98 | p->ainsn.inst_flag = 0; | 92 | p->ainsn.inst_flag = 0; |
99 | p->ainsn.target_br_reg = 0; | 93 | p->ainsn.target_br_reg = 0; |
100 | 94 | ||
101 | template = bundle->quad0.template; | 95 | if (bundle_encoding[template][slot] == B) { |
96 | switch (major_opcode) { | ||
97 | case INDIRECT_CALL_OPCODE: | ||
98 | p->ainsn.inst_flag |= INST_FLAG_FIX_BRANCH_REG; | ||
99 | p->ainsn.target_br_reg = ((kprobe_inst >> 6) & 0x7); | ||
100 | break; | ||
101 | case IP_RELATIVE_PREDICT_OPCODE: | ||
102 | case IP_RELATIVE_BRANCH_OPCODE: | ||
103 | p->ainsn.inst_flag |= INST_FLAG_FIX_RELATIVE_IP_ADDR; | ||
104 | break; | ||
105 | case IP_RELATIVE_CALL_OPCODE: | ||
106 | p->ainsn.inst_flag |= INST_FLAG_FIX_RELATIVE_IP_ADDR; | ||
107 | p->ainsn.inst_flag |= INST_FLAG_FIX_BRANCH_REG; | ||
108 | p->ainsn.target_br_reg = ((kprobe_inst >> 6) & 0x7); | ||
109 | break; | ||
110 | } | ||
111 | } else if (bundle_encoding[template][slot] == X) { | ||
112 | switch (major_opcode) { | ||
113 | case LONG_CALL_OPCODE: | ||
114 | p->ainsn.inst_flag |= INST_FLAG_FIX_BRANCH_REG; | ||
115 | p->ainsn.target_br_reg = ((kprobe_inst >> 6) & 0x7); | ||
116 | break; | ||
117 | } | ||
118 | } | ||
119 | return; | ||
120 | } | ||
102 | 121 | ||
103 | if (((bundle_encoding[template][1] == L) && slot > 1) || (slot > 2)) { | 122 | /* |
104 | printk(KERN_WARNING "Attempting to insert unaligned kprobe at 0x%lx\n", | 123 | * In this function we override the bundle with |
105 | addr); | 124 | * the break instruction at the given slot. |
106 | return -EINVAL; | 125 | */ |
126 | static void prepare_break_inst(uint template, uint slot, uint major_opcode, | ||
127 | unsigned long kprobe_inst, struct kprobe *p) | ||
128 | { | ||
129 | unsigned long break_inst = BREAK_INST; | ||
130 | bundle_t *bundle = &p->ainsn.insn.bundle; | ||
131 | |||
132 | /* | ||
133 | * Copy the original kprobe_inst qualifying predicate(qp) | ||
134 | * to the break instruction | ||
135 | */ | ||
136 | break_inst |= (0x3f & kprobe_inst); | ||
137 | |||
138 | switch (slot) { | ||
139 | case 0: | ||
140 | bundle->quad0.slot0 = break_inst; | ||
141 | break; | ||
142 | case 1: | ||
143 | bundle->quad0.slot1_p0 = break_inst; | ||
144 | bundle->quad1.slot1_p1 = break_inst >> (64-46); | ||
145 | break; | ||
146 | case 2: | ||
147 | bundle->quad1.slot2 = break_inst; | ||
148 | break; | ||
107 | } | 149 | } |
108 | 150 | ||
109 | if (slot == 1 && bundle_encoding[template][1] == L) { | 151 | /* |
110 | lx_type_inst = 1; | 152 | * Update the instruction flag, so that we can |
111 | slot = 2; | 153 | * emulate the instruction properly after we |
112 | } | 154 | * single step on original instruction |
155 | */ | ||
156 | update_kprobe_inst_flag(template, slot, major_opcode, kprobe_inst, p); | ||
157 | } | ||
158 | |||
159 | static inline void get_kprobe_inst(bundle_t *bundle, uint slot, | ||
160 | unsigned long *kprobe_inst, uint *major_opcode) | ||
161 | { | ||
162 | unsigned long kprobe_inst_p0, kprobe_inst_p1; | ||
163 | unsigned int template; | ||
164 | |||
165 | template = bundle->quad0.template; | ||
113 | 166 | ||
114 | switch (slot) { | 167 | switch (slot) { |
115 | case 0: | 168 | case 0: |
116 | major_opcode = (bundle->quad0.slot0 >> SLOT0_OPCODE_SHIFT); | 169 | *major_opcode = (bundle->quad0.slot0 >> SLOT0_OPCODE_SHIFT); |
117 | kprobe_inst = bundle->quad0.slot0; | 170 | *kprobe_inst = bundle->quad0.slot0; |
118 | bundle->quad0.slot0 = BREAK_INST | (0x3f & kprobe_inst); | ||
119 | break; | 171 | break; |
120 | case 1: | 172 | case 1: |
121 | major_opcode = (bundle->quad1.slot1_p1 >> SLOT1_p1_OPCODE_SHIFT); | 173 | *major_opcode = (bundle->quad1.slot1_p1 >> SLOT1_p1_OPCODE_SHIFT); |
122 | kprobe_inst = (bundle->quad0.slot1_p0 | | 174 | kprobe_inst_p0 = bundle->quad0.slot1_p0; |
123 | (bundle->quad1.slot1_p1 << (64-46))); | 175 | kprobe_inst_p1 = bundle->quad1.slot1_p1; |
124 | bundle->quad0.slot1_p0 = BREAK_INST | (0x3f & kprobe_inst); | 176 | *kprobe_inst = kprobe_inst_p0 | (kprobe_inst_p1 << (64-46)); |
125 | bundle->quad1.slot1_p1 = (BREAK_INST >> (64-46)); | ||
126 | break; | 177 | break; |
127 | case 2: | 178 | case 2: |
128 | major_opcode = (bundle->quad1.slot2 >> SLOT2_OPCODE_SHIFT); | 179 | *major_opcode = (bundle->quad1.slot2 >> SLOT2_OPCODE_SHIFT); |
129 | kprobe_inst = bundle->quad1.slot2; | 180 | *kprobe_inst = bundle->quad1.slot2; |
130 | bundle->quad1.slot2 = BREAK_INST | (0x3f & kprobe_inst); | ||
131 | break; | 181 | break; |
132 | } | 182 | } |
183 | } | ||
133 | 184 | ||
134 | /* | 185 | static int valid_kprobe_addr(int template, int slot, unsigned long addr) |
135 | * Look for IP relative Branches, IP relative call or | 186 | { |
136 | * IP relative predicate instructions | 187 | if ((slot > 2) || ((bundle_encoding[template][1] == L) && slot > 1)) { |
137 | */ | 188 | printk(KERN_WARNING "Attempting to insert unaligned kprobe at 0x%lx\n", |
138 | if (bundle_encoding[template][slot] == B) { | 189 | addr); |
139 | switch (major_opcode) { | 190 | return -EINVAL; |
140 | case INDIRECT_CALL_OPCODE: | ||
141 | p->ainsn.inst_flag |= INST_FLAG_FIX_BRANCH_REG; | ||
142 | p->ainsn.target_br_reg = ((kprobe_inst >> 6) & 0x7); | ||
143 | break; | ||
144 | case IP_RELATIVE_PREDICT_OPCODE: | ||
145 | case IP_RELATIVE_BRANCH_OPCODE: | ||
146 | p->ainsn.inst_flag |= INST_FLAG_FIX_RELATIVE_IP_ADDR; | ||
147 | break; | ||
148 | case IP_RELATIVE_CALL_OPCODE: | ||
149 | p->ainsn.inst_flag |= INST_FLAG_FIX_RELATIVE_IP_ADDR; | ||
150 | p->ainsn.inst_flag |= INST_FLAG_FIX_BRANCH_REG; | ||
151 | p->ainsn.target_br_reg = ((kprobe_inst >> 6) & 0x7); | ||
152 | break; | ||
153 | default: | ||
154 | /* Do nothing */ | ||
155 | break; | ||
156 | } | ||
157 | } else if (lx_type_inst) { | ||
158 | switch (major_opcode) { | ||
159 | case LONG_CALL_OPCODE: | ||
160 | p->ainsn.inst_flag |= INST_FLAG_FIX_BRANCH_REG; | ||
161 | p->ainsn.target_br_reg = ((kprobe_inst >> 6) & 0x7); | ||
162 | break; | ||
163 | default: | ||
164 | /* Do nothing */ | ||
165 | break; | ||
166 | } | ||
167 | } | 191 | } |
192 | return 0; | ||
193 | } | ||
194 | |||
195 | int arch_prepare_kprobe(struct kprobe *p) | ||
196 | { | ||
197 | unsigned long addr = (unsigned long) p->addr; | ||
198 | unsigned long *kprobe_addr = (unsigned long *)(addr & ~0xFULL); | ||
199 | unsigned long kprobe_inst=0; | ||
200 | unsigned int slot = addr & 0xf, template, major_opcode = 0; | ||
201 | bundle_t *bundle = &p->ainsn.insn.bundle; | ||
202 | |||
203 | memcpy(&p->opcode.bundle, kprobe_addr, sizeof(bundle_t)); | ||
204 | memcpy(&p->ainsn.insn.bundle, kprobe_addr, sizeof(bundle_t)); | ||
205 | |||
206 | template = bundle->quad0.template; | ||
207 | |||
208 | if(valid_kprobe_addr(template, slot, addr)) | ||
209 | return -EINVAL; | ||
210 | |||
211 | /* Move to slot 2, if bundle is MLX type and kprobe slot is 1 */ | ||
212 | if (slot == 1 && bundle_encoding[template][1] == L) | ||
213 | slot++; | ||
214 | |||
215 | /* Get kprobe_inst and major_opcode from the bundle */ | ||
216 | get_kprobe_inst(bundle, slot, &kprobe_inst, &major_opcode); | ||
217 | |||
218 | prepare_break_inst(template, slot, major_opcode, kprobe_inst, p); | ||
168 | 219 | ||
169 | return 0; | 220 | return 0; |
170 | } | 221 | } |
@@ -260,7 +311,7 @@ static void resume_execution(struct kprobe *p, struct pt_regs *regs) | |||
260 | if (regs->cr_iip == bundle_addr) { | 311 | if (regs->cr_iip == bundle_addr) { |
261 | regs->cr_iip = resume_addr; | 312 | regs->cr_iip = resume_addr; |
262 | } | 313 | } |
263 | } | 314 | } |
264 | 315 | ||
265 | turn_ss_off: | 316 | turn_ss_off: |
266 | /* Turn off Single Step bit */ | 317 | /* Turn off Single Step bit */ |