1 /*- 2 * Copyright (c) 2016 Cavium 3 * All rights reserved. 4 * 5 * This software was developed by Semihalf. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 35 #include <machine/armreg.h> 36 #include <machine/disassem.h> 37 38 #include <ddb/ddb.h> 39 40 #define ARM64_MAX_TOKEN_LEN 8 41 #define ARM64_MAX_TOKEN_CNT 10 42 43 #define ARM_INSN_SIZE_OFFSET 30 44 #define ARM_INSN_SIZE_MASK 0x3 45 46 /* Special options for instruction printing */ 47 #define OP_SIGN_EXT (1UL << 0) /* Sign-extend immediate value */ 48 #define OP_LITERAL (1UL << 1) /* Use literal (memory offset) */ 49 #define OP_MULT_4 (1UL << 2) /* Multiply immediate by 4 */ 50 #define OP_SF32 (1UL << 3) /* Force 32-bit access */ 51 #define OP_SF_INV (1UL << 6) /* SF is inverted (1 means 32 bit access) */ 52 #define OP_RD_SP (1UL << 7) /* Use sp for RD otherwise xzr */ 53 #define OP_RT_SP (1UL << 8) /* Use sp for RT otherwise xzr */ 54 #define OP_RN_SP (1UL << 9) /* Use sp for RN otherwise xzr */ 55 #define OP_RM_SP (1UL << 10) /* Use sp for RM otherwise xzr */ 56 #define OP_SHIFT_ROR (1UL << 11) /* Use ror shift type */ 57 58 static const char *w_reg[] = { 59 "w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7", 60 "w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15", 61 "w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23", 62 "w24", "w25", "w26", "w27", "w28", "w29", "w30" 63 }; 64 65 static const char *x_reg[] = { 66 "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", 67 "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", 68 "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", 69 "x24", "x25", "x26", "x27", "x28", "x29", "lr" 70 }; 71 72 static const char *shift_2[] = { 73 "lsl", "lsr", "asr", "ror" 74 }; 75 76 static const char *extend_types[] = { 77 "uxtb", "uxth", "uxtw", "uxtx", 78 "sxtb", "sxth", "sxtw", "sxtx", 79 }; 80 81 /* 82 * Structure representing single token (operand) inside instruction. 83 * name - name of operand 84 * pos - position within the instruction (in bits) 85 * len - operand length (in bits) 86 */ 87 struct arm64_insn_token { 88 char name[ARM64_MAX_TOKEN_LEN]; 89 int pos; 90 int len; 91 }; 92 93 /* 94 * Define generic types for instruction printing. 95 */ 96 enum arm64_format_type { 97 /* 98 * OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #imm} SF32/64 99 * OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64 100 * OP <RD>, <RM> {, <shift> #<imm> } 101 * OP <RN>, <RM> {, <shift> #<imm> } 102 */ 103 TYPE_01, 104 105 /* 106 * OP <RT>, [<XN|SP>, #<simm>]! 107 * OP <RT>, [<XN|SP>], #<simm> 108 * OP <RT>, [<XN|SP> {, #<pimm> }] 109 * OP <RT>, [<XN|SP>, <RM> {, EXTEND AMOUNT }] 110 */ 111 TYPE_02, 112 113 /* OP <RT>, #imm SF32/64 */ 114 TYPE_03, 115 116 /* 117 * OP <RD>, <RN|SP>, <RM> {, <extend> { #<amount> } } 118 * OP <RN|SP>, <RM>, {, <extend> { #<amount> } } 119 */ 120 TYPE_04, 121 }; 122 123 /* 124 * Structure representing single parsed instruction format. 125 * name - opcode name 126 * format - opcode format in a human-readable way 127 * type - syntax type for printing 128 * special_ops - special options passed to a printer (if any) 129 * mask - bitmask for instruction matching 130 * pattern - pattern to look for 131 * tokens - array of tokens (operands) inside instruction 132 */ 133 struct arm64_insn { 134 char *name; 135 char *format; 136 enum arm64_format_type type; 137 uint64_t special_ops; 138 uint32_t mask; 139 uint32_t pattern; 140 struct arm64_insn_token tokens[ARM64_MAX_TOKEN_CNT]; 141 }; 142 143 /* 144 * Specify instruction opcode format in a human-readable way. Use notation 145 * obtained from ARM Architecture Reference Manual for ARMv8-A. 146 * 147 * Format string description: 148 * Each group must be separated by "|". Group made of 0/1 is used to 149 * generate mask and pattern for instruction matching. Groups containing 150 * an operand token (in format NAME(length_bits)) are used to retrieve any 151 * operand data from the instruction. Names here must be meaningful 152 * and match the one described in the Manual. 153 * 154 * Token description: 155 * SF - "0" represents 32-bit access, "1" represents 64-bit access 156 * SHIFT - type of shift (instruction dependent) 157 * IMM - immediate value 158 * Rx - register number 159 * OPTION - command specific options 160 * SCALE - scaling of immediate value 161 */ 162 static struct arm64_insn arm64_i[] = { 163 { "add", "SF(1)|0001011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", 164 TYPE_01, 0 }, /* add shifted register */ 165 { "mov", "SF(1)|001000100000000000000|RN(5)|RD(5)", 166 TYPE_01, OP_RD_SP | OP_RN_SP }, /* mov (to/from sp) */ 167 { "add", "SF(1)|0010001|SHIFT(2)|IMM(12)|RN(5)|RD(5)", 168 TYPE_01, OP_RD_SP | OP_RN_SP }, /* add immediate */ 169 { "cmn", "SF(1)|0101011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|11111", 170 TYPE_01, 0 }, /* cmn shifted register */ 171 { "adds", "SF(1)|0101011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", 172 TYPE_01, 0 }, /* adds shifted register */ 173 { "ldr", "1|SF(1)|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)", 174 TYPE_02, OP_SIGN_EXT }, 175 /* ldr immediate post/pre index */ 176 { "ldr", "1|SF(1)|11100101|IMM(12)|RN(5)|RT(5)", 177 TYPE_02, 0 }, /* ldr immediate unsigned */ 178 { "ldr", "1|SF(1)|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 179 TYPE_02, 0 }, /* ldr register */ 180 { "ldr", "0|SF(1)|011000|IMM(19)|RT(5)", 181 TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 }, /* ldr literal */ 182 { "ldrb", "00|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)", 183 TYPE_02, OP_SIGN_EXT | OP_SF32 }, 184 /* ldrb immediate post/pre index */ 185 { "ldrb", "00|11100101|IMM(12)|RN(5)|RT(5)", 186 TYPE_02, OP_SF32 }, /* ldrb immediate unsigned */ 187 { "ldrb", "00|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 188 TYPE_02, OP_SF32 }, /* ldrb register */ 189 { "ldrh", "01|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)", TYPE_02, 190 OP_SIGN_EXT | OP_SF32 }, 191 /* ldrh immediate post/pre index */ 192 { "ldrh", "01|11100101|IMM(12)|RN(5)|RT(5)", 193 TYPE_02, OP_SF32 }, /* ldrh immediate unsigned */ 194 { "ldrh", "01|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 195 TYPE_02, OP_SF32 }, /* ldrh register */ 196 { "ldrsb", "001110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)", 197 TYPE_02, OP_SIGN_EXT | OP_SF_INV }, 198 /* ldrsb immediate post/pre index */ 199 { "ldrsb", "001110011|SF(1)|IMM(12)|RN(5)|RT(5)",\ 200 TYPE_02, OP_SF_INV }, /* ldrsb immediate unsigned */ 201 { "ldrsb", "001110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 202 TYPE_02, OP_SF_INV }, /* ldrsb register */ 203 { "ldrsh", "011110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)", 204 TYPE_02, OP_SIGN_EXT | OP_SF_INV }, 205 /* ldrsh immediate post/pre index */ 206 { "ldrsh", "011110011|SF(1)|IMM(12)|RN(5)|RT(5)", 207 TYPE_02, OP_SF_INV }, /* ldrsh immediate unsigned */ 208 { "ldrsh", "011110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 209 TYPE_02, OP_SF_INV }, /* ldrsh register */ 210 { "ldrsw", "10111000100|IMM(9)|OPTION(2)|RN(5)|RT(5)", 211 TYPE_02, OP_SIGN_EXT }, 212 /* ldrsw immediate post/pre index */ 213 { "ldrsw", "1011100110|IMM(12)|RN(5)|RT(5)", 214 TYPE_02, 0 }, /* ldrsw immediate unsigned */ 215 { "ldrsw", "10111000101|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 216 TYPE_02, 0 }, /* ldrsw register */ 217 { "ldrsw", "10011000|IMM(19)|RT(5)", 218 TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 }, /* ldrsw literal */ 219 { "str", "1|SF(1)|111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)", 220 TYPE_02, OP_SIGN_EXT }, 221 /* str immediate post/pre index */ 222 { "str", "1|SF(1)|11100100|IMM(12)|RN(5)|RT(5)", 223 TYPE_02, 0 }, /* str immediate unsigned */ 224 { "str", "1|SF(1)|111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 225 TYPE_02, 0 }, /* str register */ 226 { "strb", "00111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)", 227 TYPE_02, OP_SIGN_EXT | OP_SF32 }, 228 /* strb immediate post/pre index */ 229 { "strb", "0011100100|IMM(12)|RN(5)|RT(5)", 230 TYPE_02, OP_SF32 }, /* strb immediate unsigned */ 231 { "strb", "00111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 232 TYPE_02, OP_SF32 }, /* strb register */ 233 { "strh", "01111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)", 234 TYPE_02, OP_SF32 | OP_SIGN_EXT }, 235 /* strh immediate post/pre index */ 236 { "strh", "0111100100|IMM(12)|RN(5)|RT(5)", 237 TYPE_02, OP_SF32 }, 238 /* strh immediate unsigned */ 239 { "strh", "01111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 240 TYPE_02, OP_SF32 }, 241 /* strh register */ 242 { "neg", "SF(1)|1001011|SHIFT(2)|0|RM(5)|IMM(6)|11111|RD(5)", 243 TYPE_01, 0 }, /* neg shifted register */ 244 { "sub", "SF(1)|1001011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", 245 TYPE_01, 0 }, /* sub shifted register */ 246 { "cmp", "SF(1)|1101011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|11111", 247 TYPE_01, 0 }, /* cmp shifted register */ 248 { "negs", "SF(1)|1101011|SHIFT(2)|0|RM(5)|IMM(6)|11111|RD(5)", 249 TYPE_01, 0 }, /* negs shifted register */ 250 { "subs", "SF(1)|1101011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", 251 TYPE_01, 0 }, /* subs shifted register */ 252 { "mvn", "SF(1)|0101010|SHIFT(2)|1|RM(5)|IMM(6)|11111|RD(5)", 253 TYPE_01, OP_SHIFT_ROR }, /* mvn shifted register */ 254 { "orn", "SF(1)|0101010|SHIFT(2)|1|RM(5)|IMM(6)|RN(5)|RD(5)", 255 TYPE_01, OP_SHIFT_ROR }, /* orn shifted register */ 256 { "mov", "SF(1)|0101010000|RM(5)|000000|11111|RD(5)", 257 TYPE_01, 0 }, /* mov register */ 258 { "orr", "SF(1)|0101010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", 259 TYPE_01, OP_SHIFT_ROR }, /* orr shifted register */ 260 { "and", "SF(1)|0001010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", 261 TYPE_01, OP_SHIFT_ROR }, /* and shifted register */ 262 { "tst", "SF(1)|1101010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|11111", 263 TYPE_01, OP_SHIFT_ROR }, /* tst shifted register */ 264 { "ands", "SF(1)|1101010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", 265 TYPE_01, OP_SHIFT_ROR }, /* ands shifted register */ 266 { "bic", "SF(1)|0001010|SHIFT(2)|1|RM(5)|IMM(6)|RN(5)|RD(5)", 267 TYPE_01, OP_SHIFT_ROR }, /* bic shifted register */ 268 { "bics", "SF(1)|1101010|SHIFT(2)|1|RM(5)|IMM(6)|RN(5)|RD(5)", 269 TYPE_01, OP_SHIFT_ROR }, /* bics shifted register */ 270 { "eon", "SF(1)|1001010|SHIFT(2)|1|RM(5)|IMM(6)|RN(5)|RD(5)", 271 TYPE_01, OP_SHIFT_ROR }, /* eon shifted register */ 272 { "eor", "SF(1)|1001010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", 273 TYPE_01, OP_SHIFT_ROR }, /* eor shifted register */ 274 { "add", "SF(1)|0001011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|RD(5)", 275 TYPE_04, OP_RD_SP }, /* add extended register */ 276 { "cmn", "SF(1)|0101011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|11111", 277 TYPE_04, 0 }, /* cmn extended register */ 278 { "adds", "SF(1)|0101011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|RD(5)", 279 TYPE_04, 0 }, /* adds extended register */ 280 { "sub", "SF(1)|1001011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|RD(5)", 281 TYPE_04, OP_RD_SP }, /* sub extended register */ 282 { "cmp", "SF(1)|1101011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|11111", 283 TYPE_04, 0 }, /* cmp extended register */ 284 { "subs", "SF(1)|1101011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|RD(5)", 285 TYPE_04, 0 }, /* subs extended register */ 286 { NULL, NULL } 287 }; 288 289 static void 290 arm64_disasm_generate_masks(struct arm64_insn *tab) 291 { 292 uint32_t mask, val; 293 int a, i; 294 int len, ret; 295 int token = 0; 296 char *format; 297 int error; 298 299 while (tab->name != NULL) { 300 mask = 0; 301 val = 0; 302 format = tab->format; 303 token = 0; 304 error = 0; 305 306 /* 307 * For each entry analyze format strings from the 308 * left (i.e. from the MSB). 309 */ 310 a = (INSN_SIZE * NBBY) - 1; 311 while (*format != '\0' && (a >= 0)) { 312 switch (*format) { 313 case '0': 314 /* Bit is 0, add to mask and pattern */ 315 mask |= (1 << a); 316 a--; 317 format++; 318 break; 319 case '1': 320 /* Bit is 1, add to mask and pattern */ 321 mask |= (1 << a); 322 val |= (1 << a); 323 a--; 324 format++; 325 break; 326 case '|': 327 /* skip */ 328 format++; 329 break; 330 default: 331 /* Token found, copy the name */ 332 memset(tab->tokens[token].name, 0, 333 sizeof(tab->tokens[token].name)); 334 i = 0; 335 while (*format != '(') { 336 tab->tokens[token].name[i] = *format; 337 i++; 338 format++; 339 if (i >= ARM64_MAX_TOKEN_LEN) { 340 printf("ERROR: " 341 "token too long in op %s\n", 342 tab->name); 343 error = 1; 344 break; 345 } 346 } 347 if (error != 0) 348 break; 349 350 /* Read the length value */ 351 ret = sscanf(format, "(%d)", &len); 352 if (ret == 1) { 353 if (token >= ARM64_MAX_TOKEN_CNT) { 354 printf("ERROR: " 355 "too many tokens in op %s\n", 356 tab->name); 357 error = 1; 358 break; 359 } 360 361 a -= len; 362 tab->tokens[token].pos = a + 1; 363 tab->tokens[token].len = len; 364 token++; 365 } 366 367 /* Skip to the end of the token */ 368 while (*format != 0 && *format != '|') 369 format++; 370 } 371 } 372 373 /* Write mask and pattern to the instruction array */ 374 tab->mask = mask; 375 tab->pattern = val; 376 377 /* 378 * If we got here, format string must be parsed and "a" 379 * should point to -1. If it's not, wrong number of bits 380 * in format string. Mark this as invalid and prevent 381 * from being matched. 382 */ 383 if (*format != 0 || (a != -1) || (error != 0)) { 384 tab->mask = 0; 385 tab->pattern = 0xffffffff; 386 printf("ERROR: skipping instruction op %s\n", 387 tab->name); 388 } 389 390 tab++; 391 } 392 } 393 394 static int 395 arm64_disasm_read_token(struct arm64_insn *insn, u_int opcode, 396 const char *token, int *val) 397 { 398 int i; 399 400 for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) { 401 if (strcmp(insn->tokens[i].name, token) == 0) { 402 *val = (opcode >> insn->tokens[i].pos & 403 ((1 << insn->tokens[i].len) - 1)); 404 return (0); 405 } 406 } 407 408 return (EINVAL); 409 } 410 411 static int 412 arm64_disasm_read_token_sign_ext(struct arm64_insn *insn, u_int opcode, 413 const char *token, int *val) 414 { 415 int i; 416 int msk; 417 418 for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) { 419 if (strcmp(insn->tokens[i].name, token) == 0) { 420 msk = (1 << insn->tokens[i].len) - 1; 421 *val = ((opcode >> insn->tokens[i].pos) & msk); 422 423 /* If last bit is 1, sign-extend the value */ 424 if (*val & (1 << (insn->tokens[i].len - 1))) 425 *val |= ~msk; 426 427 return (0); 428 } 429 } 430 431 return (EINVAL); 432 } 433 434 static const char * 435 arm64_disasm_reg_extend(int sf, int option, int rd, int rn, int amount) 436 { 437 bool is_sp, lsl_preferred_uxtw, lsl_preferred_uxtx, lsl_preferred; 438 439 is_sp = rd == 31 || rn == 31; 440 lsl_preferred_uxtw = sf == 0 && option == 2; 441 lsl_preferred_uxtx = sf == 1 && option == 3; 442 lsl_preferred = is_sp && (lsl_preferred_uxtw || lsl_preferred_uxtx); 443 444 /* 445 * LSL may be omitted when <amount> is 0. 446 * In all other cases <extend> is required. 447 */ 448 if (lsl_preferred && amount == 0) 449 return (NULL); 450 if (lsl_preferred) 451 return ("lsl"); 452 return (extend_types[option]); 453 } 454 455 static const char * 456 arm64_w_reg(int num, int wsp) 457 { 458 if (num == 31) 459 return (wsp != 0 ? "wsp" : "wzr"); 460 return (w_reg[num]); 461 } 462 463 static const char * 464 arm64_x_reg(int num, int sp) 465 { 466 if (num == 31) 467 return (sp != 0 ? "sp" : "xzr"); 468 return (x_reg[num]); 469 } 470 471 static const char * 472 arm64_reg(int b64, int num, int sp) 473 { 474 if (b64 != 0) 475 return (arm64_x_reg(num, sp)); 476 return (arm64_w_reg(num, sp)); 477 } 478 479 /* 480 * Decodes OPTION(3) to get <Xn|Wn> register or <WZR|XZR> 481 * for extended register instruction. 482 */ 483 static const char * 484 arm64_disasm_reg_width(int option, int reg) 485 { 486 if (option == 3 || option == 7) 487 return (arm64_x_reg(reg, 0)); 488 return (arm64_w_reg(reg, 0)); 489 } 490 491 vm_offset_t 492 disasm(const struct disasm_interface *di, vm_offset_t loc, int altfmt) 493 { 494 struct arm64_insn *i_ptr = arm64_i; 495 uint32_t insn; 496 int matchp; 497 int ret; 498 int shift, rm, rt, rd, rn, imm, sf, idx, option, scale, amount; 499 int sign_ext; 500 bool rm_absent, rd_absent, rn_absent; 501 /* Indicate if immediate should be outside or inside brackets */ 502 int inside; 503 /* Print exclamation mark if pre-incremented */ 504 int pre; 505 /* Indicate if x31 register should be printed as sp or xzr */ 506 int rm_sp, rt_sp, rd_sp, rn_sp; 507 /* Indicate if shift type ror is supported */ 508 bool has_shift_ror; 509 510 const char *extend; 511 512 /* Initialize defaults, all are 0 except SF indicating 64bit access */ 513 shift = rd = rm = rn = imm = idx = option = amount = scale = 0; 514 sign_ext = 0; 515 sf = 1; 516 extend = NULL; 517 518 matchp = 0; 519 insn = di->di_readword(loc); 520 while (i_ptr->name) { 521 /* If mask is 0 then the parser was not initialized yet */ 522 if ((i_ptr->mask != 0) && 523 ((insn & i_ptr->mask) == i_ptr->pattern)) { 524 matchp = 1; 525 break; 526 } 527 i_ptr++; 528 } 529 if (matchp == 0) 530 goto undefined; 531 532 /* Global options */ 533 if (i_ptr->special_ops & OP_SF32) 534 sf = 0; 535 536 /* Global optional tokens */ 537 arm64_disasm_read_token(i_ptr, insn, "SF", &sf); 538 if (i_ptr->special_ops & OP_SF_INV) 539 sf = 1 - sf; 540 if (arm64_disasm_read_token(i_ptr, insn, "SIGN", &sign_ext) == 0) 541 sign_ext = 1 - sign_ext; 542 if (i_ptr->special_ops & OP_SIGN_EXT) 543 sign_ext = 1; 544 if (sign_ext != 0) 545 arm64_disasm_read_token_sign_ext(i_ptr, insn, "IMM", &imm); 546 else 547 arm64_disasm_read_token(i_ptr, insn, "IMM", &imm); 548 if (i_ptr->special_ops & OP_MULT_4) 549 imm <<= 2; 550 551 rm_sp = i_ptr->special_ops & OP_RM_SP; 552 rt_sp = i_ptr->special_ops & OP_RT_SP; 553 rd_sp = i_ptr->special_ops & OP_RD_SP; 554 rn_sp = i_ptr->special_ops & OP_RN_SP; 555 556 has_shift_ror = i_ptr->special_ops & OP_SHIFT_ROR; 557 558 /* Print opcode by type */ 559 switch (i_ptr->type) { 560 case TYPE_01: 561 /* 562 * OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64 563 * OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64 564 * OP <RD>, <RM> {, <shift> #<imm> } 565 * OP <RN>, <RM> {, <shift> #<imm> } 566 */ 567 568 rd_absent = arm64_disasm_read_token(i_ptr, insn, "RD", &rd); 569 rn_absent = arm64_disasm_read_token(i_ptr, insn, "RN", &rn); 570 rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm); 571 arm64_disasm_read_token(i_ptr, insn, "SHIFT", &shift); 572 573 /* 574 * if shift type is RESERVED for shifted register instruction, 575 * print undefined 576 */ 577 if (shift == 3 && !has_shift_ror) 578 goto undefined; 579 580 di->di_printf("%s\t", i_ptr->name); 581 582 /* 583 * If RD and RN are present, we will display the following 584 * patterns: 585 * - OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64 586 * - OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64 587 * Otherwise if only RD is present: 588 * - OP <RD>, <RM> {, <shift> #<imm> } 589 * Otherwise if only RN is present: 590 * - OP <RN>, <RM> {, <shift> #<imm> } 591 */ 592 if (!rd_absent && !rn_absent) 593 di->di_printf("%s, %s", arm64_reg(sf, rd, rd_sp), 594 arm64_reg(sf, rn, rn_sp)); 595 else if (!rd_absent) 596 di->di_printf("%s", arm64_reg(sf, rd, rd_sp)); 597 else 598 di->di_printf("%s", arm64_reg(sf, rn, rn_sp)); 599 600 /* If RM is present use it, otherwise use immediate notation */ 601 if (!rm_absent) { 602 di->di_printf(", %s", arm64_reg(sf, rm, rm_sp)); 603 if (imm != 0) 604 di->di_printf(", %s #%d", shift_2[shift], imm); 605 } else { 606 if (imm != 0 || shift != 0) 607 di->di_printf(", #0x%x", imm); 608 if (shift != 0) 609 di->di_printf(" lsl #12"); 610 } 611 break; 612 case TYPE_02: 613 /* 614 * OP <RT>, [<XN|SP>, #<simm>]! 615 * OP <RT>, [<XN|SP>], #<simm> 616 * OP <RT>, [<XN|SP> {, #<pimm> }] 617 * OP <RT>, [<XN|SP>, <RM> {, EXTEND AMOUNT }] 618 */ 619 620 /* Mandatory tokens */ 621 ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt); 622 ret |= arm64_disasm_read_token(i_ptr, insn, "RN", &rn); 623 if (ret != 0) { 624 printf("ERROR: " 625 "Missing mandatory token for op %s type %d\n", 626 i_ptr->name, i_ptr->type); 627 goto undefined; 628 } 629 630 /* Optional tokens */ 631 arm64_disasm_read_token(i_ptr, insn, "OPTION", &option); 632 arm64_disasm_read_token(i_ptr, insn, "SCALE", &scale); 633 rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm); 634 635 if (rm_absent) { 636 /* 637 * In unsigned operation, shift immediate value 638 * and reset options to default. 639 */ 640 if (sign_ext == 0) { 641 imm = imm << ((insn >> ARM_INSN_SIZE_OFFSET) & 642 ARM_INSN_SIZE_MASK); 643 option = 0; 644 } 645 switch (option) { 646 case 0x0: 647 pre = 0; 648 inside = 1; 649 break; 650 case 0x1: 651 pre = 0; 652 inside = 0; 653 break; 654 case 0x2: 655 default: 656 pre = 1; 657 inside = 1; 658 break; 659 } 660 661 di->di_printf("%s\t%s, ", i_ptr->name, 662 arm64_reg(sf, rt, rt_sp)); 663 if (inside != 0) { 664 di->di_printf("[%s", arm64_x_reg(rn, 1)); 665 if (imm != 0) 666 di->di_printf(", #%d", imm); 667 di->di_printf("]"); 668 } else { 669 di->di_printf("[%s]", arm64_x_reg(rn, 1)); 670 if (imm != 0) 671 di->di_printf(", #%d", imm); 672 } 673 if (pre != 0) 674 di->di_printf("!"); 675 } else { 676 /* Last bit of option field determines 32/64 bit offset */ 677 di->di_printf("%s\t%s, [%s, %s", i_ptr->name, 678 arm64_reg(sf, rt, rt_sp), arm64_x_reg(rn, 1), 679 arm64_reg(option & 1, rm, rm_sp)); 680 681 if (scale == 0) 682 amount = 0; 683 else { 684 /* Calculate amount, it's op(31:30) */ 685 amount = (insn >> ARM_INSN_SIZE_OFFSET) & 686 ARM_INSN_SIZE_MASK; 687 } 688 689 switch (option) { 690 case 0x2: 691 di->di_printf(", uxtw #%d", amount); 692 break; 693 case 0x3: 694 if (scale != 0) 695 di->di_printf(", lsl #%d", amount); 696 break; 697 case 0x6: 698 di->di_printf(", sxtw #%d", amount); 699 break; 700 case 0x7: 701 di->di_printf(", sxtx #%d", amount); 702 break; 703 default: 704 di->di_printf(", rsv"); 705 break; 706 } 707 di->di_printf("]"); 708 } 709 710 break; 711 712 case TYPE_03: 713 /* OP <RT>, #imm SF32/64 */ 714 715 /* Mandatory tokens */ 716 ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt); 717 if (ret != 0) { 718 printf("ERROR: " 719 "Missing mandatory token for op %s type %d\n", 720 i_ptr->name, i_ptr->type); 721 goto undefined; 722 } 723 724 di->di_printf("%s\t%s, ", i_ptr->name, arm64_reg(sf, rt, rt_sp)); 725 if (i_ptr->special_ops & OP_LITERAL) 726 di->di_printf("0x%lx", loc + imm); 727 else 728 di->di_printf("#%d", imm); 729 730 break; 731 732 case TYPE_04: 733 /* 734 * OP <RD>, <RN|SP>, <RM> {, <extend> { #<amount> } } 735 * OP <RN|SP>, <RM>, {, <extend> { #<amount> } } 736 */ 737 738 arm64_disasm_read_token(i_ptr, insn, "RN", &rn); 739 arm64_disasm_read_token(i_ptr, insn, "RM", &rm); 740 arm64_disasm_read_token(i_ptr, insn, "OPTION", &option); 741 742 rd_absent = arm64_disasm_read_token(i_ptr, insn, "RD", &rd); 743 extend = arm64_disasm_reg_extend(sf, option, rd, rn, imm); 744 745 di->di_printf("%s\t", i_ptr->name); 746 747 if (!rd_absent) 748 di->di_printf("%s, ", arm64_reg(sf, rd, rd_sp)); 749 750 di->di_printf("%s, ", arm64_reg(sf, rn, 1)); 751 752 if (sf != 0) 753 di->di_printf("%s", 754 arm64_disasm_reg_width(option, rm)); 755 else 756 di->di_printf("%s", arm64_w_reg(rm, 0)); 757 758 if (extend != NULL) 759 di->di_printf(", %s #%d", extend, imm); 760 761 break; 762 default: 763 goto undefined; 764 } 765 766 di->di_printf("\n"); 767 return (loc + INSN_SIZE); 768 769 undefined: 770 di->di_printf("undefined\t%08x\n", insn); 771 return (loc + INSN_SIZE); 772 } 773 774 /* Parse format strings at the very beginning */ 775 SYSINIT(arm64_disasm_generate_masks, SI_SUB_DDB_SERVICES, SI_ORDER_FIRST, 776 arm64_disasm_generate_masks, arm64_i); 777