diff options
author | Vineet Gupta <vgupta@synopsys.com> | 2013-01-30 07:16:13 -0500 |
---|---|---|
committer | Vineet Gupta <vgupta@synopsys.com> | 2013-02-15 12:46:04 -0500 |
commit | e65ab5a875d9e8ad8ff37529c2ae844699fefad1 (patch) | |
tree | 1aefe328299eaf1fbe48b27dd31c588b6e2b51b5 /arch/arc | |
parent | 44c8bb914028f34c622c30340fa21e186e4cf993 (diff) |
ARC: disassembly (needed by kprobes/kgdb/unaligned-access-emul)
In-kernel disassembler
Due Credits
* Orig written by Rajeshwar Ranga
* Consolidation/cleanups by Mischa Jonker
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
Cc: Rajeshwar Ranga <rajeshwar.ranga@gmail.com>
Cc: Mischa Jonker <mjonker@synopsys.com>
Diffstat (limited to 'arch/arc')
-rw-r--r-- | arch/arc/include/asm/disasm.h | 116 | ||||
-rw-r--r-- | arch/arc/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/arc/kernel/disasm.c | 539 |
3 files changed, 656 insertions, 1 deletions
diff --git a/arch/arc/include/asm/disasm.h b/arch/arc/include/asm/disasm.h new file mode 100644 index 000000000000..f1cce3d059a1 --- /dev/null +++ b/arch/arc/include/asm/disasm.h | |||
@@ -0,0 +1,116 @@ | |||
1 | /* | ||
2 | * several functions that help interpret ARC instructions | ||
3 | * used for unaligned accesses, kprobes and kgdb | ||
4 | * | ||
5 | * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef __ARC_DISASM_H__ | ||
13 | #define __ARC_DISASM_H__ | ||
14 | |||
15 | enum { | ||
16 | op_Bcc = 0, op_BLcc = 1, op_LD = 2, op_ST = 3, op_MAJOR_4 = 4, | ||
17 | op_MAJOR_5 = 5, op_LD_ADD = 12, op_ADD_SUB_SHIFT = 13, | ||
18 | op_ADD_MOV_CMP = 14, op_S = 15, op_LD_S = 16, op_LDB_S = 17, | ||
19 | op_LDW_S = 18, op_LDWX_S = 19, op_ST_S = 20, op_STB_S = 21, | ||
20 | op_STW_S = 22, op_Su5 = 23, op_SP = 24, op_GP = 25, | ||
21 | op_Pcl = 26, op_MOV_S = 27, op_ADD_CMP = 28, op_BR_S = 29, | ||
22 | op_B_S = 30, op_BL_S = 31 | ||
23 | }; | ||
24 | |||
25 | enum flow { | ||
26 | noflow, | ||
27 | direct_jump, | ||
28 | direct_call, | ||
29 | indirect_jump, | ||
30 | indirect_call, | ||
31 | invalid_instr | ||
32 | }; | ||
33 | |||
34 | #define IS_BIT(word, n) ((word) & (1<<n)) | ||
35 | #define BITS(word, s, e) (((word) >> (s)) & (~((-2) << ((e) - (s))))) | ||
36 | |||
37 | #define MAJOR_OPCODE(word) (BITS((word), 27, 31)) | ||
38 | #define MINOR_OPCODE(word) (BITS((word), 16, 21)) | ||
39 | #define FIELD_A(word) (BITS((word), 0, 5)) | ||
40 | #define FIELD_B(word) ((BITS((word), 12, 14)<<3) | \ | ||
41 | (BITS((word), 24, 26))) | ||
42 | #define FIELD_C(word) (BITS((word), 6, 11)) | ||
43 | #define FIELD_u6(word) FIELDC(word) | ||
44 | #define FIELD_s12(word) sign_extend(((BITS((word), 0, 5) << 6) | \ | ||
45 | BITS((word), 6, 11)), 12) | ||
46 | |||
47 | /* note that for BL/BRcc these two macro's need another AND statement to mask | ||
48 | * out bit 1 (make the result a multiple of 4) */ | ||
49 | #define FIELD_s9(word) sign_extend(((BITS(word, 15, 15) << 8) | \ | ||
50 | BITS(word, 16, 23)), 9) | ||
51 | #define FIELD_s21(word) sign_extend(((BITS(word, 6, 15) << 11) | \ | ||
52 | (BITS(word, 17, 26) << 1)), 12) | ||
53 | #define FIELD_s25(word) sign_extend(((BITS(word, 0, 3) << 21) | \ | ||
54 | (BITS(word, 6, 15) << 11) | \ | ||
55 | (BITS(word, 17, 26) << 1)), 12) | ||
56 | |||
57 | /* note: these operate on 16 bits! */ | ||
58 | #define FIELD_S_A(word) ((BITS((word), 2, 2)<<3) | BITS((word), 0, 2)) | ||
59 | #define FIELD_S_B(word) ((BITS((word), 10, 10)<<3) | \ | ||
60 | BITS((word), 8, 10)) | ||
61 | #define FIELD_S_C(word) ((BITS((word), 7, 7)<<3) | BITS((word), 5, 7)) | ||
62 | #define FIELD_S_H(word) ((BITS((word), 0, 2)<<3) | BITS((word), 5, 8)) | ||
63 | #define FIELD_S_u5(word) (BITS((word), 0, 4)) | ||
64 | #define FIELD_S_u6(word) (BITS((word), 0, 4) << 1) | ||
65 | #define FIELD_S_u7(word) (BITS((word), 0, 4) << 2) | ||
66 | #define FIELD_S_u10(word) (BITS((word), 0, 7) << 2) | ||
67 | #define FIELD_S_s7(word) sign_extend(BITS((word), 0, 5) << 1, 9) | ||
68 | #define FIELD_S_s8(word) sign_extend(BITS((word), 0, 7) << 1, 9) | ||
69 | #define FIELD_S_s9(word) sign_extend(BITS((word), 0, 8), 9) | ||
70 | #define FIELD_S_s10(word) sign_extend(BITS((word), 0, 8) << 1, 10) | ||
71 | #define FIELD_S_s11(word) sign_extend(BITS((word), 0, 8) << 2, 11) | ||
72 | #define FIELD_S_s13(word) sign_extend(BITS((word), 0, 10) << 2, 13) | ||
73 | |||
74 | #define STATUS32_L 0x00000100 | ||
75 | #define REG_LIMM 62 | ||
76 | |||
77 | struct disasm_state { | ||
78 | /* generic info */ | ||
79 | unsigned long words[2]; | ||
80 | int instr_len; | ||
81 | int major_opcode; | ||
82 | /* info for branch/jump */ | ||
83 | int is_branch; | ||
84 | int target; | ||
85 | int delay_slot; | ||
86 | enum flow flow; | ||
87 | /* info for load/store */ | ||
88 | int src1, src2, src3, dest, wb_reg; | ||
89 | int zz, aa, x, pref, di; | ||
90 | int fault, write; | ||
91 | }; | ||
92 | |||
93 | static inline int sign_extend(int value, int bits) | ||
94 | { | ||
95 | if (IS_BIT(value, (bits - 1))) | ||
96 | value |= (0xffffffff << bits); | ||
97 | |||
98 | return value; | ||
99 | } | ||
100 | |||
101 | static inline int is_short_instr(unsigned long addr) | ||
102 | { | ||
103 | uint16_t word = *((uint16_t *)addr); | ||
104 | int opcode = (word >> 11) & 0x1F; | ||
105 | return (opcode >= 0x0B); | ||
106 | } | ||
107 | |||
108 | void disasm_instr(unsigned long addr, struct disasm_state *state, | ||
109 | int userspace, struct pt_regs *regs, struct callee_regs *cregs); | ||
110 | int disasm_next_pc(unsigned long pc, struct pt_regs *regs, struct callee_regs | ||
111 | *cregs, unsigned long *fall_thru, unsigned long *target); | ||
112 | long get_reg(int reg, struct pt_regs *regs, struct callee_regs *cregs); | ||
113 | void set_reg(int reg, long val, struct pt_regs *regs, | ||
114 | struct callee_regs *cregs); | ||
115 | |||
116 | #endif /* __ARC_DISASM_H__ */ | ||
diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile index 38e4715f3349..6ae4dd4ecea0 100644 --- a/arch/arc/kernel/Makefile +++ b/arch/arc/kernel/Makefile | |||
@@ -9,7 +9,7 @@ | |||
9 | CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"' | 9 | CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"' |
10 | 10 | ||
11 | obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o entry.o process.o | 11 | obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o entry.o process.o |
12 | obj-y += signal.o traps.o sys.o troubleshoot.o stacktrace.o clk.o | 12 | obj-y += signal.o traps.o sys.o troubleshoot.o stacktrace.o disasm.o clk.o |
13 | obj-y += devtree.o | 13 | obj-y += devtree.o |
14 | 14 | ||
15 | obj-$(CONFIG_MODULES) += arcksyms.o module.o | 15 | obj-$(CONFIG_MODULES) += arcksyms.o module.o |
diff --git a/arch/arc/kernel/disasm.c b/arch/arc/kernel/disasm.c new file mode 100644 index 000000000000..254d11a4f495 --- /dev/null +++ b/arch/arc/kernel/disasm.c | |||
@@ -0,0 +1,539 @@ | |||
1 | /* | ||
2 | * several functions that help interpret ARC instructions | ||
3 | * used for unaligned accesses, kprobes and kgdb | ||
4 | * | ||
5 | * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | #include <linux/kprobes.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <asm/disasm.h> | ||
16 | #include <asm/uaccess.h> | ||
17 | |||
18 | #if defined(CONFIG_KGDB) || defined(CONFIG_MISALIGN_ACCESS) || \ | ||
19 | defined(CONFIG_KPROBES) | ||
20 | |||
21 | /* disasm_instr: Analyses instruction at addr, stores | ||
22 | * findings in *state | ||
23 | */ | ||
24 | void __kprobes disasm_instr(unsigned long addr, struct disasm_state *state, | ||
25 | int userspace, struct pt_regs *regs, struct callee_regs *cregs) | ||
26 | { | ||
27 | int fieldA = 0; | ||
28 | int fieldC = 0, fieldCisReg = 0; | ||
29 | uint16_t word1 = 0, word0 = 0; | ||
30 | int subopcode, is_linked, op_format; | ||
31 | uint16_t *ins_ptr; | ||
32 | uint16_t ins_buf[4]; | ||
33 | int bytes_not_copied = 0; | ||
34 | |||
35 | memset(state, 0, sizeof(struct disasm_state)); | ||
36 | |||
37 | /* This fetches the upper part of the 32 bit instruction | ||
38 | * in both the cases of Little Endian or Big Endian configurations. */ | ||
39 | if (userspace) { | ||
40 | bytes_not_copied = copy_from_user(ins_buf, | ||
41 | (const void __user *) addr, 8); | ||
42 | if (bytes_not_copied > 6) | ||
43 | goto fault; | ||
44 | ins_ptr = ins_buf; | ||
45 | } else { | ||
46 | ins_ptr = (uint16_t *) addr; | ||
47 | } | ||
48 | |||
49 | word1 = *((uint16_t *)addr); | ||
50 | |||
51 | state->major_opcode = (word1 >> 11) & 0x1F; | ||
52 | |||
53 | /* Check if the instruction is 32 bit or 16 bit instruction */ | ||
54 | if (state->major_opcode < 0x0B) { | ||
55 | if (bytes_not_copied > 4) | ||
56 | goto fault; | ||
57 | state->instr_len = 4; | ||
58 | word0 = *((uint16_t *)(addr+2)); | ||
59 | state->words[0] = (word1 << 16) | word0; | ||
60 | } else { | ||
61 | state->instr_len = 2; | ||
62 | state->words[0] = word1; | ||
63 | } | ||
64 | |||
65 | /* Read the second word in case of limm */ | ||
66 | word1 = *((uint16_t *)(addr + state->instr_len)); | ||
67 | word0 = *((uint16_t *)(addr + state->instr_len + 2)); | ||
68 | state->words[1] = (word1 << 16) | word0; | ||
69 | |||
70 | switch (state->major_opcode) { | ||
71 | case op_Bcc: | ||
72 | state->is_branch = 1; | ||
73 | |||
74 | /* unconditional branch s25, conditional branch s21 */ | ||
75 | fieldA = (IS_BIT(state->words[0], 16)) ? | ||
76 | FIELD_s25(state->words[0]) : | ||
77 | FIELD_s21(state->words[0]); | ||
78 | |||
79 | state->delay_slot = IS_BIT(state->words[0], 5); | ||
80 | state->target = fieldA + (addr & ~0x3); | ||
81 | state->flow = direct_jump; | ||
82 | break; | ||
83 | |||
84 | case op_BLcc: | ||
85 | if (IS_BIT(state->words[0], 16)) { | ||
86 | /* Branch and Link*/ | ||
87 | /* unconditional branch s25, conditional branch s21 */ | ||
88 | fieldA = (IS_BIT(state->words[0], 17)) ? | ||
89 | (FIELD_s25(state->words[0]) & ~0x3) : | ||
90 | FIELD_s21(state->words[0]); | ||
91 | |||
92 | state->flow = direct_call; | ||
93 | } else { | ||
94 | /*Branch On Compare */ | ||
95 | fieldA = FIELD_s9(state->words[0]) & ~0x3; | ||
96 | state->flow = direct_jump; | ||
97 | } | ||
98 | |||
99 | state->delay_slot = IS_BIT(state->words[0], 5); | ||
100 | state->target = fieldA + (addr & ~0x3); | ||
101 | state->is_branch = 1; | ||
102 | break; | ||
103 | |||
104 | case op_LD: /* LD<zz> a,[b,s9] */ | ||
105 | state->write = 0; | ||
106 | state->di = BITS(state->words[0], 11, 11); | ||
107 | if (state->di) | ||
108 | break; | ||
109 | state->x = BITS(state->words[0], 6, 6); | ||
110 | state->zz = BITS(state->words[0], 7, 8); | ||
111 | state->aa = BITS(state->words[0], 9, 10); | ||
112 | state->wb_reg = FIELD_B(state->words[0]); | ||
113 | if (state->wb_reg == REG_LIMM) { | ||
114 | state->instr_len += 4; | ||
115 | state->aa = 0; | ||
116 | state->src1 = state->words[1]; | ||
117 | } else { | ||
118 | state->src1 = get_reg(state->wb_reg, regs, cregs); | ||
119 | } | ||
120 | state->src2 = FIELD_s9(state->words[0]); | ||
121 | state->dest = FIELD_A(state->words[0]); | ||
122 | state->pref = (state->dest == REG_LIMM); | ||
123 | break; | ||
124 | |||
125 | case op_ST: | ||
126 | state->write = 1; | ||
127 | state->di = BITS(state->words[0], 5, 5); | ||
128 | if (state->di) | ||
129 | break; | ||
130 | state->aa = BITS(state->words[0], 3, 4); | ||
131 | state->zz = BITS(state->words[0], 1, 2); | ||
132 | state->src1 = FIELD_C(state->words[0]); | ||
133 | if (state->src1 == REG_LIMM) { | ||
134 | state->instr_len += 4; | ||
135 | state->src1 = state->words[1]; | ||
136 | } else { | ||
137 | state->src1 = get_reg(state->src1, regs, cregs); | ||
138 | } | ||
139 | state->wb_reg = FIELD_B(state->words[0]); | ||
140 | if (state->wb_reg == REG_LIMM) { | ||
141 | state->aa = 0; | ||
142 | state->instr_len += 4; | ||
143 | state->src2 = state->words[1]; | ||
144 | } else { | ||
145 | state->src2 = get_reg(state->wb_reg, regs, cregs); | ||
146 | } | ||
147 | state->src3 = FIELD_s9(state->words[0]); | ||
148 | break; | ||
149 | |||
150 | case op_MAJOR_4: | ||
151 | subopcode = MINOR_OPCODE(state->words[0]); | ||
152 | switch (subopcode) { | ||
153 | case 32: /* Jcc */ | ||
154 | case 33: /* Jcc.D */ | ||
155 | case 34: /* JLcc */ | ||
156 | case 35: /* JLcc.D */ | ||
157 | is_linked = 0; | ||
158 | |||
159 | if (subopcode == 33 || subopcode == 35) | ||
160 | state->delay_slot = 1; | ||
161 | |||
162 | if (subopcode == 34 || subopcode == 35) | ||
163 | is_linked = 1; | ||
164 | |||
165 | fieldCisReg = 0; | ||
166 | op_format = BITS(state->words[0], 22, 23); | ||
167 | if (op_format == 0 || ((op_format == 3) && | ||
168 | (!IS_BIT(state->words[0], 5)))) { | ||
169 | fieldC = FIELD_C(state->words[0]); | ||
170 | |||
171 | if (fieldC == REG_LIMM) { | ||
172 | fieldC = state->words[1]; | ||
173 | state->instr_len += 4; | ||
174 | } else { | ||
175 | fieldCisReg = 1; | ||
176 | } | ||
177 | } else if (op_format == 1 || ((op_format == 3) | ||
178 | && (IS_BIT(state->words[0], 5)))) { | ||
179 | fieldC = FIELD_C(state->words[0]); | ||
180 | } else { | ||
181 | /* op_format == 2 */ | ||
182 | fieldC = FIELD_s12(state->words[0]); | ||
183 | } | ||
184 | |||
185 | if (!fieldCisReg) { | ||
186 | state->target = fieldC; | ||
187 | state->flow = is_linked ? | ||
188 | direct_call : direct_jump; | ||
189 | } else { | ||
190 | state->target = get_reg(fieldC, regs, cregs); | ||
191 | state->flow = is_linked ? | ||
192 | indirect_call : indirect_jump; | ||
193 | } | ||
194 | state->is_branch = 1; | ||
195 | break; | ||
196 | |||
197 | case 40: /* LPcc */ | ||
198 | if (BITS(state->words[0], 22, 23) == 3) { | ||
199 | /* Conditional LPcc u7 */ | ||
200 | fieldC = FIELD_C(state->words[0]); | ||
201 | |||
202 | fieldC = fieldC << 1; | ||
203 | fieldC += (addr & ~0x03); | ||
204 | state->is_branch = 1; | ||
205 | state->flow = direct_jump; | ||
206 | state->target = fieldC; | ||
207 | } | ||
208 | /* For Unconditional lp, next pc is the fall through | ||
209 | * which is updated */ | ||
210 | break; | ||
211 | |||
212 | case 48 ... 55: /* LD a,[b,c] */ | ||
213 | state->di = BITS(state->words[0], 15, 15); | ||
214 | if (state->di) | ||
215 | break; | ||
216 | state->x = BITS(state->words[0], 16, 16); | ||
217 | state->zz = BITS(state->words[0], 17, 18); | ||
218 | state->aa = BITS(state->words[0], 22, 23); | ||
219 | state->wb_reg = FIELD_B(state->words[0]); | ||
220 | if (state->wb_reg == REG_LIMM) { | ||
221 | state->instr_len += 4; | ||
222 | state->src1 = state->words[1]; | ||
223 | } else { | ||
224 | state->src1 = get_reg(state->wb_reg, regs, | ||
225 | cregs); | ||
226 | } | ||
227 | state->src2 = FIELD_C(state->words[0]); | ||
228 | if (state->src2 == REG_LIMM) { | ||
229 | state->instr_len += 4; | ||
230 | state->src2 = state->words[1]; | ||
231 | } else { | ||
232 | state->src2 = get_reg(state->src2, regs, | ||
233 | cregs); | ||
234 | } | ||
235 | state->dest = FIELD_A(state->words[0]); | ||
236 | if (state->dest == REG_LIMM) | ||
237 | state->pref = 1; | ||
238 | break; | ||
239 | |||
240 | case 10: /* MOV */ | ||
241 | /* still need to check for limm to extract instr len */ | ||
242 | /* MOV is special case because it only takes 2 args */ | ||
243 | switch (BITS(state->words[0], 22, 23)) { | ||
244 | case 0: /* OP a,b,c */ | ||
245 | if (FIELD_C(state->words[0]) == REG_LIMM) | ||
246 | state->instr_len += 4; | ||
247 | break; | ||
248 | case 1: /* OP a,b,u6 */ | ||
249 | break; | ||
250 | case 2: /* OP b,b,s12 */ | ||
251 | break; | ||
252 | case 3: /* OP.cc b,b,c/u6 */ | ||
253 | if ((!IS_BIT(state->words[0], 5)) && | ||
254 | (FIELD_C(state->words[0]) == REG_LIMM)) | ||
255 | state->instr_len += 4; | ||
256 | break; | ||
257 | } | ||
258 | break; | ||
259 | |||
260 | |||
261 | default: | ||
262 | /* Not a Load, Jump or Loop instruction */ | ||
263 | /* still need to check for limm to extract instr len */ | ||
264 | switch (BITS(state->words[0], 22, 23)) { | ||
265 | case 0: /* OP a,b,c */ | ||
266 | if ((FIELD_B(state->words[0]) == REG_LIMM) || | ||
267 | (FIELD_C(state->words[0]) == REG_LIMM)) | ||
268 | state->instr_len += 4; | ||
269 | break; | ||
270 | case 1: /* OP a,b,u6 */ | ||
271 | break; | ||
272 | case 2: /* OP b,b,s12 */ | ||
273 | break; | ||
274 | case 3: /* OP.cc b,b,c/u6 */ | ||
275 | if ((!IS_BIT(state->words[0], 5)) && | ||
276 | ((FIELD_B(state->words[0]) == REG_LIMM) || | ||
277 | (FIELD_C(state->words[0]) == REG_LIMM))) | ||
278 | state->instr_len += 4; | ||
279 | break; | ||
280 | } | ||
281 | break; | ||
282 | } | ||
283 | break; | ||
284 | |||
285 | /* 16 Bit Instructions */ | ||
286 | case op_LD_ADD: /* LD_S|LDB_S|LDW_S a,[b,c] */ | ||
287 | state->zz = BITS(state->words[0], 3, 4); | ||
288 | state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, cregs); | ||
289 | state->src2 = get_reg(FIELD_S_C(state->words[0]), regs, cregs); | ||
290 | state->dest = FIELD_S_A(state->words[0]); | ||
291 | break; | ||
292 | |||
293 | case op_ADD_MOV_CMP: | ||
294 | /* check for limm, ignore mov_s h,b (== mov_s 0,b) */ | ||
295 | if ((BITS(state->words[0], 3, 4) < 3) && | ||
296 | (FIELD_S_H(state->words[0]) == REG_LIMM)) | ||
297 | state->instr_len += 4; | ||
298 | break; | ||
299 | |||
300 | case op_S: | ||
301 | subopcode = BITS(state->words[0], 5, 7); | ||
302 | switch (subopcode) { | ||
303 | case 0: /* j_s */ | ||
304 | case 1: /* j_s.d */ | ||
305 | case 2: /* jl_s */ | ||
306 | case 3: /* jl_s.d */ | ||
307 | state->target = get_reg(FIELD_S_B(state->words[0]), | ||
308 | regs, cregs); | ||
309 | state->delay_slot = subopcode & 1; | ||
310 | state->flow = (subopcode >= 2) ? | ||
311 | direct_call : indirect_jump; | ||
312 | break; | ||
313 | case 7: | ||
314 | switch (BITS(state->words[0], 8, 10)) { | ||
315 | case 4: /* jeq_s [blink] */ | ||
316 | case 5: /* jne_s [blink] */ | ||
317 | case 6: /* j_s [blink] */ | ||
318 | case 7: /* j_s.d [blink] */ | ||
319 | state->delay_slot = (subopcode == 7); | ||
320 | state->flow = indirect_jump; | ||
321 | state->target = get_reg(31, regs, cregs); | ||
322 | default: | ||
323 | break; | ||
324 | } | ||
325 | default: | ||
326 | break; | ||
327 | } | ||
328 | break; | ||
329 | |||
330 | case op_LD_S: /* LD_S c, [b, u7] */ | ||
331 | state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, cregs); | ||
332 | state->src2 = FIELD_S_u7(state->words[0]); | ||
333 | state->dest = FIELD_S_C(state->words[0]); | ||
334 | break; | ||
335 | |||
336 | case op_LDB_S: | ||
337 | case op_STB_S: | ||
338 | /* no further handling required as byte accesses should not | ||
339 | * cause an unaligned access exception */ | ||
340 | state->zz = 1; | ||
341 | break; | ||
342 | |||
343 | case op_LDWX_S: /* LDWX_S c, [b, u6] */ | ||
344 | state->x = 1; | ||
345 | /* intentional fall-through */ | ||
346 | |||
347 | case op_LDW_S: /* LDW_S c, [b, u6] */ | ||
348 | state->zz = 2; | ||
349 | state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, cregs); | ||
350 | state->src2 = FIELD_S_u6(state->words[0]); | ||
351 | state->dest = FIELD_S_C(state->words[0]); | ||
352 | break; | ||
353 | |||
354 | case op_ST_S: /* ST_S c, [b, u7] */ | ||
355 | state->write = 1; | ||
356 | state->src1 = get_reg(FIELD_S_C(state->words[0]), regs, cregs); | ||
357 | state->src2 = get_reg(FIELD_S_B(state->words[0]), regs, cregs); | ||
358 | state->src3 = FIELD_S_u7(state->words[0]); | ||
359 | break; | ||
360 | |||
361 | case op_STW_S: /* STW_S c,[b,u6] */ | ||
362 | state->write = 1; | ||
363 | state->zz = 2; | ||
364 | state->src1 = get_reg(FIELD_S_C(state->words[0]), regs, cregs); | ||
365 | state->src2 = get_reg(FIELD_S_B(state->words[0]), regs, cregs); | ||
366 | state->src3 = FIELD_S_u6(state->words[0]); | ||
367 | break; | ||
368 | |||
369 | case op_SP: /* LD_S|LDB_S b,[sp,u7], ST_S|STB_S b,[sp,u7] */ | ||
370 | /* note: we are ignoring possibility of: | ||
371 | * ADD_S, SUB_S, PUSH_S, POP_S as these should not | ||
372 | * cause unaliged exception anyway */ | ||
373 | state->write = BITS(state->words[0], 6, 6); | ||
374 | state->zz = BITS(state->words[0], 5, 5); | ||
375 | if (state->zz) | ||
376 | break; /* byte accesses should not come here */ | ||
377 | if (!state->write) { | ||
378 | state->src1 = get_reg(28, regs, cregs); | ||
379 | state->src2 = FIELD_S_u7(state->words[0]); | ||
380 | state->dest = FIELD_S_B(state->words[0]); | ||
381 | } else { | ||
382 | state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, | ||
383 | cregs); | ||
384 | state->src2 = get_reg(28, regs, cregs); | ||
385 | state->src3 = FIELD_S_u7(state->words[0]); | ||
386 | } | ||
387 | break; | ||
388 | |||
389 | case op_GP: /* LD_S|LDB_S|LDW_S r0,[gp,s11/s9/s10] */ | ||
390 | /* note: ADD_S r0, gp, s11 is ignored */ | ||
391 | state->zz = BITS(state->words[0], 9, 10); | ||
392 | state->src1 = get_reg(26, regs, cregs); | ||
393 | state->src2 = state->zz ? FIELD_S_s10(state->words[0]) : | ||
394 | FIELD_S_s11(state->words[0]); | ||
395 | state->dest = 0; | ||
396 | break; | ||
397 | |||
398 | case op_Pcl: /* LD_S b,[pcl,u10] */ | ||
399 | state->src1 = regs->ret & ~3; | ||
400 | state->src2 = FIELD_S_u10(state->words[0]); | ||
401 | state->dest = FIELD_S_B(state->words[0]); | ||
402 | break; | ||
403 | |||
404 | case op_BR_S: | ||
405 | state->target = FIELD_S_s8(state->words[0]) + (addr & ~0x03); | ||
406 | state->flow = direct_jump; | ||
407 | state->is_branch = 1; | ||
408 | break; | ||
409 | |||
410 | case op_B_S: | ||
411 | fieldA = (BITS(state->words[0], 9, 10) == 3) ? | ||
412 | FIELD_S_s7(state->words[0]) : | ||
413 | FIELD_S_s10(state->words[0]); | ||
414 | state->target = fieldA + (addr & ~0x03); | ||
415 | state->flow = direct_jump; | ||
416 | state->is_branch = 1; | ||
417 | break; | ||
418 | |||
419 | case op_BL_S: | ||
420 | state->target = FIELD_S_s13(state->words[0]) + (addr & ~0x03); | ||
421 | state->flow = direct_call; | ||
422 | state->is_branch = 1; | ||
423 | break; | ||
424 | |||
425 | default: | ||
426 | break; | ||
427 | } | ||
428 | |||
429 | if (bytes_not_copied <= (8 - state->instr_len)) | ||
430 | return; | ||
431 | |||
432 | fault: state->fault = 1; | ||
433 | } | ||
434 | |||
435 | long __kprobes get_reg(int reg, struct pt_regs *regs, | ||
436 | struct callee_regs *cregs) | ||
437 | { | ||
438 | long *p; | ||
439 | |||
440 | if (reg <= 12) { | ||
441 | p = ®s->r0; | ||
442 | return p[-reg]; | ||
443 | } | ||
444 | |||
445 | if (cregs && (reg <= 25)) { | ||
446 | p = &cregs->r13; | ||
447 | return p[13-reg]; | ||
448 | } | ||
449 | |||
450 | if (reg == 26) | ||
451 | return regs->r26; | ||
452 | if (reg == 27) | ||
453 | return regs->fp; | ||
454 | if (reg == 28) | ||
455 | return regs->sp; | ||
456 | if (reg == 31) | ||
457 | return regs->blink; | ||
458 | |||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | void __kprobes set_reg(int reg, long val, struct pt_regs *regs, | ||
463 | struct callee_regs *cregs) | ||
464 | { | ||
465 | long *p; | ||
466 | |||
467 | switch (reg) { | ||
468 | case 0 ... 12: | ||
469 | p = ®s->r0; | ||
470 | p[-reg] = val; | ||
471 | break; | ||
472 | case 13 ... 25: | ||
473 | if (cregs) { | ||
474 | p = &cregs->r13; | ||
475 | p[13-reg] = val; | ||
476 | } | ||
477 | break; | ||
478 | case 26: | ||
479 | regs->r26 = val; | ||
480 | break; | ||
481 | case 27: | ||
482 | regs->fp = val; | ||
483 | break; | ||
484 | case 28: | ||
485 | regs->sp = val; | ||
486 | break; | ||
487 | case 31: | ||
488 | regs->blink = val; | ||
489 | break; | ||
490 | default: | ||
491 | break; | ||
492 | } | ||
493 | } | ||
494 | |||
495 | /* | ||
496 | * Disassembles the insn at @pc and sets @next_pc to next PC (which could be | ||
497 | * @pc +2/4/6 (ARCompact ISA allows free intermixing of 16/32 bit insns). | ||
498 | * | ||
499 | * If @pc is a branch | ||
500 | * -@tgt_if_br is set to branch target. | ||
501 | * -If branch has delay slot, @next_pc updated with actual next PC. | ||
502 | * | ||
503 | */ | ||
504 | int __kprobes disasm_next_pc(unsigned long pc, struct pt_regs *regs, | ||
505 | struct callee_regs *cregs, | ||
506 | unsigned long *next_pc, unsigned long *tgt_if_br) | ||
507 | { | ||
508 | struct disasm_state instr; | ||
509 | |||
510 | memset(&instr, 0, sizeof(struct disasm_state)); | ||
511 | disasm_instr(pc, &instr, 0, regs, cregs); | ||
512 | |||
513 | *next_pc = pc + instr.instr_len; | ||
514 | |||
515 | /* Instruction with possible two targets branch, jump and loop */ | ||
516 | if (instr.is_branch) | ||
517 | *tgt_if_br = instr.target; | ||
518 | |||
519 | /* For the instructions with delay slots, the fall through is the | ||
520 | * instruction following the instruction in delay slot. | ||
521 | */ | ||
522 | if (instr.delay_slot) { | ||
523 | struct disasm_state instr_d; | ||
524 | |||
525 | disasm_instr(*next_pc, &instr_d, 0, regs, cregs); | ||
526 | |||
527 | *next_pc += instr_d.instr_len; | ||
528 | } | ||
529 | |||
530 | /* Zero Overhead Loop - end of the loop */ | ||
531 | if (!(regs->status32 & STATUS32_L) && (*next_pc == regs->lp_end) | ||
532 | && (regs->lp_count > 1)) { | ||
533 | *next_pc = regs->lp_start; | ||
534 | } | ||
535 | |||
536 | return instr.is_branch; | ||
537 | } | ||
538 | |||
539 | #endif /* CONFIG_KGDB || CONFIG_MISALIGN_ACCESS || CONFIG_KPROBES */ | ||