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 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 */ 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 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 */ 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 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 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 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 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 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