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