1 // SPDX-License-Identifier: GPL-2.0
2 #include <string.h>
3 #include <linux/compiler.h>
4 #include <linux/kernel.h>
5 #include "../annotate-data.h"
6 #include "../debug.h"
7 #include "../disasm.h"
8
9 #define PPC_OP(op) (((op) >> 26) & 0x3F)
10 #define PPC_21_30(R) (((R) >> 1) & 0x3ff)
11 #define PPC_22_30(R) (((R) >> 1) & 0x1ff)
12
13 #define MINUS_EXT_XO_FORM 234
14 #define SUB_EXT_XO_FORM 232
15 #define ADD_ZERO_EXT_XO_FORM 202
16 #define SUB_ZERO_EXT_XO_FORM 200
17
arithmetic__scnprintf(const struct ins * ins,char * bf,size_t size,struct ins_operands * ops,int max_ins_name)18 static int arithmetic__scnprintf(const struct ins *ins, char *bf, size_t size,
19 struct ins_operands *ops, int max_ins_name)
20 {
21 return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name,
22 ops->raw);
23 }
24
25 /*
26 * Sets the fields: multi_regs and "mem_ref".
27 * "mem_ref" is set for ops->source which is later used to
28 * fill the objdump->memory_ref-char field. This ops is currently
29 * used by powerpc and since binary instruction code is used to
30 * extract opcode, regs and offset, no other parsing is needed here.
31 *
32 * Dont set multi regs for 4 cases since it has only one operand
33 * for source:
34 * - Add to Minus One Extended XO-form ( Ex: addme, addmeo )
35 * - Subtract From Minus One Extended XO-form ( Ex: subfme )
36 * - Add to Zero Extended XO-form ( Ex: addze, addzeo )
37 * - Subtract From Zero Extended XO-form ( Ex: subfze )
38 */
arithmetic__parse(const struct arch * arch __maybe_unused,struct ins_operands * ops,struct map_symbol * ms __maybe_unused,struct disasm_line * dl)39 static int arithmetic__parse(const struct arch *arch __maybe_unused, struct ins_operands *ops,
40 struct map_symbol *ms __maybe_unused, struct disasm_line *dl)
41 {
42 int opcode = PPC_OP(dl->raw.raw_insn);
43
44 ops->source.mem_ref = false;
45 if (opcode == 31) {
46 if ((opcode != MINUS_EXT_XO_FORM) && (opcode != SUB_EXT_XO_FORM) &&
47 (opcode != ADD_ZERO_EXT_XO_FORM) && (opcode != SUB_ZERO_EXT_XO_FORM))
48 ops->source.multi_regs = true;
49 }
50
51 ops->target.mem_ref = false;
52 ops->target.multi_regs = false;
53
54 return 0;
55 }
56
57 static const struct ins_ops arithmetic_ops = {
58 .parse = arithmetic__parse,
59 .scnprintf = arithmetic__scnprintf,
60 };
61
load_store__scnprintf(const struct ins * ins,char * bf,size_t size,struct ins_operands * ops,int max_ins_name)62 static int load_store__scnprintf(const struct ins *ins, char *bf, size_t size,
63 struct ins_operands *ops, int max_ins_name)
64 {
65 return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name,
66 ops->raw);
67 }
68
69 /*
70 * Sets the fields: multi_regs and "mem_ref".
71 * "mem_ref" is set for ops->source which is later used to
72 * fill the objdump->memory_ref-char field. This ops is currently
73 * used by powerpc and since binary instruction code is used to
74 * extract opcode, regs and offset, no other parsing is needed here
75 */
load_store__parse(const struct arch * arch __maybe_unused,struct ins_operands * ops,struct map_symbol * ms __maybe_unused,struct disasm_line * dl __maybe_unused)76 static int load_store__parse(const struct arch *arch __maybe_unused, struct ins_operands *ops,
77 struct map_symbol *ms __maybe_unused, struct disasm_line *dl __maybe_unused)
78 {
79 ops->source.mem_ref = true;
80 ops->source.multi_regs = false;
81 /* opcode 31 is of X form */
82 if (PPC_OP(dl->raw.raw_insn) == 31)
83 ops->source.multi_regs = true;
84
85 ops->target.mem_ref = false;
86 ops->target.multi_regs = false;
87
88 return 0;
89 }
90
91 static const struct ins_ops load_store_ops = {
92 .parse = load_store__parse,
93 .scnprintf = load_store__scnprintf,
94 };
95
powerpc__associate_instruction_ops(struct arch * arch,const char * name)96 static const struct ins_ops *powerpc__associate_instruction_ops(struct arch *arch, const char *name)
97 {
98 int i;
99 const struct ins_ops *ops;
100
101 /*
102 * - Interested only if instruction starts with 'b'.
103 * - Few start with 'b', but aren't branch instructions.
104 */
105 if (name[0] != 'b' ||
106 !strncmp(name, "bcd", 3) ||
107 !strncmp(name, "brinc", 5) ||
108 !strncmp(name, "bper", 4))
109 return NULL;
110
111 ops = &jump_ops;
112
113 i = strlen(name) - 1;
114 if (i < 0)
115 return NULL;
116
117 /* ignore optional hints at the end of the instructions */
118 if (name[i] == '+' || name[i] == '-')
119 i--;
120
121 if (name[i] == 'l' || (name[i] == 'a' && name[i-1] == 'l')) {
122 /*
123 * if the instruction ends up with 'l' or 'la', then
124 * those are considered 'calls' since they update LR.
125 * ... except for 'bnl' which is branch if not less than
126 * and the absolute form of the same.
127 */
128 if (strcmp(name, "bnl") && strcmp(name, "bnl+") &&
129 strcmp(name, "bnl-") && strcmp(name, "bnla") &&
130 strcmp(name, "bnla+") && strcmp(name, "bnla-"))
131 ops = &call_ops;
132 }
133 if (name[i] == 'r' && name[i-1] == 'l')
134 /*
135 * instructions ending with 'lr' are considered to be
136 * return instructions
137 */
138 ops = &ret_ops;
139
140 arch__associate_ins_ops(arch, name, ops);
141 return ops;
142 }
143
144 struct insn_offset {
145 const char *name;
146 int value;
147 };
148
149 /*
150 * There are memory instructions with opcode 31 which are
151 * of X Form, Example:
152 * ldx RT,RA,RB
153 * ______________________________________
154 * | 31 | RT | RA | RB | 21 |/|
155 * --------------------------------------
156 * 0 6 11 16 21 30 31
157 *
158 * But all instructions with opcode 31 are not memory.
159 * Example: add RT,RA,RB
160 *
161 * Use bits 21 to 30 to check memory insns with 31 as opcode.
162 * In ins_array below, for ldx instruction:
163 * name => OP_31_XOP_LDX
164 * value => 21
165 */
166
167 static struct insn_offset ins_array[] = {
168 { .name = "OP_31_XOP_LXSIWZX", .value = 12, },
169 { .name = "OP_31_XOP_LWARX", .value = 20, },
170 { .name = "OP_31_XOP_LDX", .value = 21, },
171 { .name = "OP_31_XOP_LWZX", .value = 23, },
172 { .name = "OP_31_XOP_LDUX", .value = 53, },
173 { .name = "OP_31_XOP_LWZUX", .value = 55, },
174 { .name = "OP_31_XOP_LXSIWAX", .value = 76, },
175 { .name = "OP_31_XOP_LDARX", .value = 84, },
176 { .name = "OP_31_XOP_LBZX", .value = 87, },
177 { .name = "OP_31_XOP_LVX", .value = 103, },
178 { .name = "OP_31_XOP_LBZUX", .value = 119, },
179 { .name = "OP_31_XOP_STXSIWX", .value = 140, },
180 { .name = "OP_31_XOP_STDX", .value = 149, },
181 { .name = "OP_31_XOP_STWX", .value = 151, },
182 { .name = "OP_31_XOP_STDUX", .value = 181, },
183 { .name = "OP_31_XOP_STWUX", .value = 183, },
184 { .name = "OP_31_XOP_STBX", .value = 215, },
185 { .name = "OP_31_XOP_STVX", .value = 231, },
186 { .name = "OP_31_XOP_STBUX", .value = 247, },
187 { .name = "OP_31_XOP_LHZX", .value = 279, },
188 { .name = "OP_31_XOP_LHZUX", .value = 311, },
189 { .name = "OP_31_XOP_LXVDSX", .value = 332, },
190 { .name = "OP_31_XOP_LWAX", .value = 341, },
191 { .name = "OP_31_XOP_LHAX", .value = 343, },
192 { .name = "OP_31_XOP_LWAUX", .value = 373, },
193 { .name = "OP_31_XOP_LHAUX", .value = 375, },
194 { .name = "OP_31_XOP_STHX", .value = 407, },
195 { .name = "OP_31_XOP_STHUX", .value = 439, },
196 { .name = "OP_31_XOP_LXSSPX", .value = 524, },
197 { .name = "OP_31_XOP_LDBRX", .value = 532, },
198 { .name = "OP_31_XOP_LSWX", .value = 533, },
199 { .name = "OP_31_XOP_LWBRX", .value = 534, },
200 { .name = "OP_31_XOP_LFSUX", .value = 567, },
201 { .name = "OP_31_XOP_LXSDX", .value = 588, },
202 { .name = "OP_31_XOP_LSWI", .value = 597, },
203 { .name = "OP_31_XOP_LFDX", .value = 599, },
204 { .name = "OP_31_XOP_LFDUX", .value = 631, },
205 { .name = "OP_31_XOP_STXSSPX", .value = 652, },
206 { .name = "OP_31_XOP_STDBRX", .value = 660, },
207 { .name = "OP_31_XOP_STXWX", .value = 661, },
208 { .name = "OP_31_XOP_STWBRX", .value = 662, },
209 { .name = "OP_31_XOP_STFSX", .value = 663, },
210 { .name = "OP_31_XOP_STFSUX", .value = 695, },
211 { .name = "OP_31_XOP_STXSDX", .value = 716, },
212 { .name = "OP_31_XOP_STSWI", .value = 725, },
213 { .name = "OP_31_XOP_STFDX", .value = 727, },
214 { .name = "OP_31_XOP_STFDUX", .value = 759, },
215 { .name = "OP_31_XOP_LXVW4X", .value = 780, },
216 { .name = "OP_31_XOP_LHBRX", .value = 790, },
217 { .name = "OP_31_XOP_LXVD2X", .value = 844, },
218 { .name = "OP_31_XOP_LFIWAX", .value = 855, },
219 { .name = "OP_31_XOP_LFIWZX", .value = 887, },
220 { .name = "OP_31_XOP_STXVW4X", .value = 908, },
221 { .name = "OP_31_XOP_STHBRX", .value = 918, },
222 { .name = "OP_31_XOP_STXVD2X", .value = 972, },
223 { .name = "OP_31_XOP_STFIWX", .value = 983, },
224 };
225
226 /*
227 * Arithmetic instructions which are having opcode as 31.
228 * These instructions are tracked to save the register state
229 * changes. Example:
230 *
231 * lwz r10,264(r3)
232 * add r31, r3, r3
233 * lwz r9, 0(r31)
234 *
235 * Here instruction tracking needs to identify the "add"
236 * instruction and save data type of r3 to r31. If a sample
237 * is hit at next "lwz r9, 0(r31)", by this instruction tracking,
238 * data type of r31 can be resolved.
239 */
240 static struct insn_offset arithmetic_ins_op_31[] = {
241 { .name = "SUB_CARRY_XO_FORM", .value = 8, },
242 { .name = "MUL_HDW_XO_FORM1", .value = 9, },
243 { .name = "ADD_CARRY_XO_FORM", .value = 10, },
244 { .name = "MUL_HW_XO_FORM1", .value = 11, },
245 { .name = "SUB_XO_FORM", .value = 40, },
246 { .name = "MUL_HDW_XO_FORM", .value = 73, },
247 { .name = "MUL_HW_XO_FORM", .value = 75, },
248 { .name = "SUB_EXT_XO_FORM", .value = 136, },
249 { .name = "ADD_EXT_XO_FORM", .value = 138, },
250 { .name = "SUB_ZERO_EXT_XO_FORM", .value = 200, },
251 { .name = "ADD_ZERO_EXT_XO_FORM", .value = 202, },
252 { .name = "SUB_EXT_XO_FORM2", .value = 232, },
253 { .name = "MUL_DW_XO_FORM", .value = 233, },
254 { .name = "ADD_EXT_XO_FORM2", .value = 234, },
255 { .name = "MUL_W_XO_FORM", .value = 235, },
256 { .name = "ADD_XO_FORM", .value = 266, },
257 { .name = "DIV_DW_XO_FORM1", .value = 457, },
258 { .name = "DIV_W_XO_FORM1", .value = 459, },
259 { .name = "DIV_DW_XO_FORM", .value = 489, },
260 { .name = "DIV_W_XO_FORM", .value = 491, },
261 };
262
263 static struct insn_offset arithmetic_two_ops[] = {
264 { .name = "mulli", .value = 7, },
265 { .name = "subfic", .value = 8, },
266 { .name = "addic", .value = 12, },
267 { .name = "addic.", .value = 13, },
268 { .name = "addi", .value = 14, },
269 { .name = "addis", .value = 15, },
270 };
271
cmp_offset(const void * a,const void * b)272 static int cmp_offset(const void *a, const void *b)
273 {
274 const struct insn_offset *val1 = a;
275 const struct insn_offset *val2 = b;
276
277 return (val1->value - val2->value);
278 }
279
check_ppc_insn(struct disasm_line * dl)280 const struct ins_ops *check_ppc_insn(struct disasm_line *dl)
281 {
282 int raw_insn = dl->raw.raw_insn;
283 int opcode = PPC_OP(raw_insn);
284 int mem_insn_31 = PPC_21_30(raw_insn);
285 struct insn_offset *ret;
286 struct insn_offset mem_insns_31_opcode = {
287 "OP_31_INSN",
288 mem_insn_31
289 };
290 char name_insn[32];
291
292 /*
293 * Instructions with opcode 32 to 63 are memory
294 * instructions in powerpc
295 */
296 if ((opcode & 0x20)) {
297 /*
298 * Set name in case of raw instruction to
299 * opcode to be used in insn-stat
300 */
301 if (!strlen(dl->ins.name)) {
302 sprintf(name_insn, "%d", opcode);
303 dl->ins.name = strdup(name_insn);
304 }
305 return &load_store_ops;
306 } else if (opcode == 31) {
307 /* Check for memory instructions with opcode 31 */
308 ret = bsearch(&mem_insns_31_opcode, ins_array, ARRAY_SIZE(ins_array), sizeof(ins_array[0]), cmp_offset);
309 if (ret) {
310 if (!strlen(dl->ins.name))
311 dl->ins.name = strdup(ret->name);
312 return &load_store_ops;
313 } else {
314 mem_insns_31_opcode.value = PPC_22_30(raw_insn);
315 ret = bsearch(&mem_insns_31_opcode, arithmetic_ins_op_31, ARRAY_SIZE(arithmetic_ins_op_31),
316 sizeof(arithmetic_ins_op_31[0]), cmp_offset);
317 if (ret != NULL)
318 return &arithmetic_ops;
319 /* Bits 21 to 30 has value 444 for "mr" insn ie, OR X form */
320 if (PPC_21_30(raw_insn) == 444)
321 return &arithmetic_ops;
322 }
323 } else {
324 mem_insns_31_opcode.value = opcode;
325 ret = bsearch(&mem_insns_31_opcode, arithmetic_two_ops, ARRAY_SIZE(arithmetic_two_ops),
326 sizeof(arithmetic_two_ops[0]), cmp_offset);
327 if (ret != NULL)
328 return &arithmetic_ops;
329 }
330
331 return NULL;
332 }
333
334 /*
335 * Instruction tracking function to track register state moves.
336 * Example sequence:
337 * ld r10,264(r3)
338 * mr r31,r3
339 * <<after some sequence>
340 * ld r9,312(r31)
341 *
342 * Previous instruction sequence shows that register state of r3
343 * is moved to r31. update_insn_state_powerpc tracks these state
344 * changes
345 */
346 #ifdef HAVE_LIBDW_SUPPORT
update_insn_state_powerpc(struct type_state * state,struct data_loc_info * dloc,Dwarf_Die * cu_die __maybe_unused,struct disasm_line * dl)347 static void update_insn_state_powerpc(struct type_state *state,
348 struct data_loc_info *dloc, Dwarf_Die * cu_die __maybe_unused,
349 struct disasm_line *dl)
350 {
351 struct annotated_insn_loc loc;
352 struct annotated_op_loc *src = &loc.ops[INSN_OP_SOURCE];
353 struct annotated_op_loc *dst = &loc.ops[INSN_OP_TARGET];
354 struct type_state_reg *tsr;
355 u32 insn_offset = dl->al.offset;
356
357 if (annotate_get_insn_location(dloc->arch, dl, &loc) < 0)
358 return;
359
360 /*
361 * Value 444 for bits 21:30 is for "mr"
362 * instruction. "mr" is extended OR. So set the
363 * source and destination reg correctly
364 */
365 if (PPC_21_30(dl->raw.raw_insn) == 444) {
366 int src_reg = src->reg1;
367
368 src->reg1 = dst->reg1;
369 dst->reg1 = src_reg;
370 }
371
372 if (!has_reg_type(state, dst->reg1))
373 return;
374
375 tsr = &state->regs[dst->reg1];
376
377 if (!has_reg_type(state, src->reg1) ||
378 !state->regs[src->reg1].ok) {
379 tsr->ok = false;
380 return;
381 }
382
383 tsr->type = state->regs[src->reg1].type;
384 tsr->kind = state->regs[src->reg1].kind;
385 tsr->ok = true;
386
387 pr_debug_dtp("mov [%x] reg%d -> reg%d",
388 insn_offset, src->reg1, dst->reg1);
389 pr_debug_type_name(&tsr->type, tsr->kind);
390 }
391 #endif /* HAVE_LIBDW_SUPPORT */
392
arch__new_powerpc(const struct e_machine_and_e_flags * id,const char * cpuid __maybe_unused)393 const struct arch *arch__new_powerpc(const struct e_machine_and_e_flags *id,
394 const char *cpuid __maybe_unused)
395 {
396 struct arch *arch = zalloc(sizeof(*arch));
397
398 if (!arch)
399 return NULL;
400
401 arch->name = "powerpc";
402 arch->id = *id;
403 arch->objdump.comment_char = '#';
404 annotate_opts.show_asm_raw = true;
405 arch->associate_instruction_ops = powerpc__associate_instruction_ops;
406 #ifdef HAVE_LIBDW_SUPPORT
407 arch->update_insn_state = update_insn_state_powerpc;
408 #endif
409 return arch;
410 }
411