316 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <linux/compiler.h>3 4static 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', then32 * those are considered 'calls' since they update LR.33 * ... except for 'bnl' which is branch if not less than34 * 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 be44 * return instructions45 */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 56struct insn_offset {57 const char *name;58 int value;59};60 61/*62 * There are memory instructions with opcode 31 which are63 * of X Form, Example:64 * ldx RT,RA,RB65 * ______________________________________66 * | 31 | RT | RA | RB | 21 |/|67 * --------------------------------------68 * 0 6 11 16 21 30 3169 *70 * But all instructions with opcode 31 are not memory.71 * Example: add RT,RA,RB72 *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_LDX76 * value => 2177 */78 79static 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 state141 * changes. Example:142 *143 * lwz r10,264(r3)144 * add r31, r3, r3145 * 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 sample149 * is hit at next "lwz r9, 0(r31)", by this instruction tracking,150 * data type of r31 can be resolved.151 */152static 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 175static 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 184static 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 192static 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_31201 };202 char name_insn[32];203 204 /*205 * Instructions with opcode 32 to 63 are memory206 * instructions in powerpc207 */208 if ((opcode & 0x20)) {209 /*210 * Set name in case of raw instruction to211 * opcode to be used in insn-stat212 */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,r3251 * <<after some sequence>252 * ld r9,312(r31)253 *254 * Previous instruction sequence shows that register state of r3255 * is moved to r31. update_insn_state_powerpc tracks these state256 * changes257 */258#ifdef HAVE_DWARF_SUPPORT259static 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 the275 * source and destination reg correctly276 */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_DWARF_SUPPORT */304 305static 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 }313 314 return 0;315}316