| 1 | |
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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
|
| 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 | }
|