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 /* 77 * Structure representing single token (operand) inside instruction. 78 * name - name of operand 79 * pos - position within the instruction (in bits) 80 * len - operand length (in bits) 81 */ 82 struct arm64_insn_token { 83 char name[ARM64_MAX_TOKEN_LEN]; 84 int pos; 85 int len; 86 }; 87 88 /* 89 * Define generic types for instruction printing. 90 */ 91 enum arm64_format_type { 92 /* 93 * OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #imm} SF32/64 94 * OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64 95 * OP <RD>, <RM> {, <shift> #<imm> } 96 * OP <RN>, <RM> {, <shift> #<imm> } 97 */ 98 TYPE_01, 99 100 /* 101 * OP <RT>, [<RN>, #<imm>]{!} SF32/64 102 * OP <RT>, [<RN>], #<imm>{!} SF32/64 103 * OP <RT>, <RN>, <RM> {, EXTEND AMOUNT } 104 */ 105 TYPE_02, 106 107 /* OP <RT>, #imm SF32/64 */ 108 TYPE_03, 109 }; 110 111 /* 112 * Structure representing single parsed instruction format. 113 * name - opcode name 114 * format - opcode format in a human-readable way 115 * type - syntax type for printing 116 * special_ops - special options passed to a printer (if any) 117 * mask - bitmask for instruction matching 118 * pattern - pattern to look for 119 * tokens - array of tokens (operands) inside instruction 120 */ 121 struct arm64_insn { 122 char *name; 123 char *format; 124 enum arm64_format_type type; 125 uint64_t special_ops; 126 uint32_t mask; 127 uint32_t pattern; 128 struct arm64_insn_token tokens[ARM64_MAX_TOKEN_CNT]; 129 }; 130 131 /* 132 * Specify instruction opcode format in a human-readable way. Use notation 133 * obtained from ARM Architecture Reference Manual for ARMv8-A. 134 * 135 * Format string description: 136 * Each group must be separated by "|". Group made of 0/1 is used to 137 * generate mask and pattern for instruction matching. Groups containing 138 * an operand token (in format NAME(length_bits)) are used to retrieve any 139 * operand data from the instruction. Names here must be meaningful 140 * and match the one described in the Manual. 141 * 142 * Token description: 143 * SF - "0" represents 32-bit access, "1" represents 64-bit access 144 * SHIFT - type of shift (instruction dependent) 145 * IMM - immediate value 146 * Rx - register number 147 * OPTION - command specific options 148 * SCALE - scaling of immediate value 149 */ 150 static struct arm64_insn arm64_i[] = { 151 { "add", "SF(1)|0001011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", 152 TYPE_01, 0 }, /* add shifted register */ 153 { "mov", "SF(1)|001000100000000000000|RN(5)|RD(5)", 154 TYPE_01, OP_RD_SP | OP_RN_SP }, /* mov (to/from sp) */ 155 { "add", "SF(1)|0010001|SHIFT(2)|IMM(12)|RN(5)|RD(5)", 156 TYPE_01, OP_RD_SP | OP_RN_SP }, /* add immediate */ 157 { "cmn", "SF(1)|0101011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|11111", 158 TYPE_01, 0 }, /* cmn shifted register */ 159 { "adds", "SF(1)|0101011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", 160 TYPE_01, 0 }, /* adds shifted register */ 161 { "ldr", "1|SF(1)|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)", 162 TYPE_02, OP_SIGN_EXT | OP_RN_SP }, /* ldr immediate post/pre index */ 163 { "ldr", "1|SF(1)|11100101|IMM(12)|RN(5)|RT(5)", 164 TYPE_02, OP_RN_SP }, /* ldr immediate unsigned */ 165 { "ldr", "1|SF(1)|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 166 TYPE_02, OP_RN_SP }, /* ldr register */ 167 { "ldr", "0|SF(1)|011000|IMM(19)|RT(5)", 168 TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 }, /* ldr literal */ 169 { "ldrb", "00|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)", 170 TYPE_02, OP_SIGN_EXT | OP_SF32 | OP_RN_SP }, 171 /* ldrb immediate post/pre index */ 172 { "ldrb", "00|11100101|IMM(12)|RN(5)|RT(5)", 173 TYPE_02, OP_SF32 | OP_RN_SP }, /* ldrb immediate unsigned */ 174 { "ldrb", "00|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 175 TYPE_02, OP_SF32 | OP_RN_SP }, /* ldrb register */ 176 { "ldrh", "01|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)", TYPE_02, 177 OP_SIGN_EXT | OP_SF32 | OP_RN_SP }, /* ldrh immediate post/pre index */ 178 { "ldrh", "01|11100101|IMM(12)|RN(5)|RT(5)", 179 TYPE_02, OP_SF32 | OP_RN_SP }, /* ldrh immediate unsigned */ 180 { "ldrh", "01|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 181 TYPE_02, OP_SF32 | OP_RN_SP }, /* ldrh register */ 182 { "ldrsb", "001110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)", 183 TYPE_02, OP_SIGN_EXT | OP_SF_INV | OP_RN_SP }, 184 /* ldrsb immediate post/pre index */ 185 { "ldrsb", "001110011|SF(1)|IMM(12)|RN(5)|RT(5)",\ 186 TYPE_02, OP_SF_INV | OP_RN_SP }, /* ldrsb immediate unsigned */ 187 { "ldrsb", "001110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 188 TYPE_02, OP_SF_INV | OP_RN_SP }, /* ldrsb register */ 189 { "ldrsh", "011110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)", 190 TYPE_02, OP_SIGN_EXT | OP_SF_INV | OP_RN_SP }, 191 /* ldrsh immediate post/pre index */ 192 { "ldrsh", "011110011|SF(1)|IMM(12)|RN(5)|RT(5)", 193 TYPE_02, OP_SF_INV | OP_RN_SP }, /* ldrsh immediate unsigned */ 194 { "ldrsh", "011110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 195 TYPE_02, OP_SF_INV | OP_RN_SP }, /* ldrsh register */ 196 { "ldrsw", "10111000100|IMM(9)|OPTION(2)|RN(5)|RT(5)", 197 TYPE_02, OP_SIGN_EXT | OP_RN_SP }, /* ldrsw immediate post/pre index */ 198 { "ldrsw", "1011100110|IMM(12)|RN(5)|RT(5)", 199 TYPE_02, OP_RN_SP }, /* ldrsw immediate unsigned */ 200 { "ldrsw", "10111000101|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 201 TYPE_02, OP_RN_SP }, /* ldrsw register */ 202 { "ldrsw", "10011000|IMM(19)|RT(5)", 203 TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 }, /* ldrsw literal */ 204 { "str", "1|SF(1)|111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)", 205 TYPE_02, OP_SIGN_EXT | OP_RN_SP }, /* str immediate post/pre index */ 206 { "str", "1|SF(1)|11100100|IMM(12)|RN(5)|RT(5)", 207 TYPE_02, OP_RN_SP }, /* str immediate unsigned */ 208 { "str", "1|SF(1)|111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 209 TYPE_02, OP_RN_SP }, /* str register */ 210 { "strb", "00111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)", 211 TYPE_02, OP_SIGN_EXT | OP_SF32 | OP_RN_SP }, 212 /* strb immediate post/pre index */ 213 { "strb", "0011100100|IMM(12)|RN(5)|RT(5)", 214 TYPE_02, OP_SF32 | OP_RN_SP }, /* strb immediate unsigned */ 215 { "strb", "00111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 216 TYPE_02, OP_SF32 | OP_RN_SP }, /* strb register */ 217 { "strh", "01111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)", 218 TYPE_02, OP_SF32 | OP_SIGN_EXT | OP_RN_SP }, 219 /* strh immediate post/pre index */ 220 { "strh", "0111100100|IMM(12)|RN(5)|RT(5)", 221 TYPE_02, OP_SF32 | OP_RN_SP }, 222 /* strh immediate unsigned */ 223 { "strh", "01111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", 224 TYPE_02, OP_SF32 | OP_RN_SP }, 225 /* strh register */ 226 { "neg", "SF(1)|1001011|SHIFT(2)|0|RM(5)|IMM(6)|11111|RD(5)", 227 TYPE_01, 0 }, /* neg shifted register */ 228 { "sub", "SF(1)|1001011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", 229 TYPE_01, 0 }, /* sub shifted register */ 230 { "cmp", "SF(1)|1101011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|11111", 231 TYPE_01, 0 }, /* cmp shifted register */ 232 { "negs", "SF(1)|1101011|SHIFT(2)|0|RM(5)|IMM(6)|11111|RD(5)", 233 TYPE_01, 0 }, /* negs shifted register */ 234 { "subs", "SF(1)|1101011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", 235 TYPE_01, 0 }, /* subs shifted register */ 236 { "mvn", "SF(1)|0101010|SHIFT(2)|1|RM(5)|IMM(6)|11111|RD(5)", 237 TYPE_01, OP_SHIFT_ROR }, /* mvn shifted register */ 238 { "orn", "SF(1)|0101010|SHIFT(2)|1|RM(5)|IMM(6)|RN(5)|RD(5)", 239 TYPE_01, OP_SHIFT_ROR }, /* orn shifted register */ 240 { "mov", "SF(1)|0101010000|RM(5)|000000|11111|RD(5)", 241 TYPE_01, 0 }, /* mov register */ 242 { "orr", "SF(1)|0101010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", 243 TYPE_01, OP_SHIFT_ROR }, /* orr shifted register */ 244 { "and", "SF(1)|0001010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", 245 TYPE_01, OP_SHIFT_ROR }, /* and shifted register */ 246 { "tst", "SF(1)|1101010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|11111", 247 TYPE_01, OP_SHIFT_ROR }, /* tst shifted register */ 248 { "ands", "SF(1)|1101010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", 249 TYPE_01, OP_SHIFT_ROR }, /* ands shifted register */ 250 { "bic", "SF(1)|0001010|SHIFT(2)|1|RM(5)|IMM(6)|RN(5)|RD(5)", 251 TYPE_01, OP_SHIFT_ROR }, /* bic shifted register */ 252 { "bics", "SF(1)|1101010|SHIFT(2)|1|RM(5)|IMM(6)|RN(5)|RD(5)", 253 TYPE_01, OP_SHIFT_ROR }, /* bics shifted register */ 254 { "eon", "SF(1)|1001010|SHIFT(2)|1|RM(5)|IMM(6)|RN(5)|RD(5)", 255 TYPE_01, OP_SHIFT_ROR }, /* eon shifted register */ 256 { "eor", "SF(1)|1001010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", 257 TYPE_01, OP_SHIFT_ROR }, /* eor shifted register */ 258 { NULL, NULL } 259 }; 260 261 static void 262 arm64_disasm_generate_masks(struct arm64_insn *tab) 263 { 264 uint32_t mask, val; 265 int a, i; 266 int len, ret; 267 int token = 0; 268 char *format; 269 int error; 270 271 while (tab->name != NULL) { 272 mask = 0; 273 val = 0; 274 format = tab->format; 275 token = 0; 276 error = 0; 277 278 /* 279 * For each entry analyze format strings from the 280 * left (i.e. from the MSB). 281 */ 282 a = (INSN_SIZE * NBBY) - 1; 283 while (*format != '\0' && (a >= 0)) { 284 switch (*format) { 285 case '0': 286 /* Bit is 0, add to mask and pattern */ 287 mask |= (1 << a); 288 a--; 289 format++; 290 break; 291 case '1': 292 /* Bit is 1, add to mask and pattern */ 293 mask |= (1 << a); 294 val |= (1 << a); 295 a--; 296 format++; 297 break; 298 case '|': 299 /* skip */ 300 format++; 301 break; 302 default: 303 /* Token found, copy the name */ 304 memset(tab->tokens[token].name, 0, 305 sizeof(tab->tokens[token].name)); 306 i = 0; 307 while (*format != '(') { 308 tab->tokens[token].name[i] = *format; 309 i++; 310 format++; 311 if (i >= ARM64_MAX_TOKEN_LEN) { 312 printf("ERROR: " 313 "token too long in op %s\n", 314 tab->name); 315 error = 1; 316 break; 317 } 318 } 319 if (error != 0) 320 break; 321 322 /* Read the length value */ 323 ret = sscanf(format, "(%d)", &len); 324 if (ret == 1) { 325 if (token >= ARM64_MAX_TOKEN_CNT) { 326 printf("ERROR: " 327 "too many tokens in op %s\n", 328 tab->name); 329 error = 1; 330 break; 331 } 332 333 a -= len; 334 tab->tokens[token].pos = a + 1; 335 tab->tokens[token].len = len; 336 token++; 337 } 338 339 /* Skip to the end of the token */ 340 while (*format != 0 && *format != '|') 341 format++; 342 } 343 } 344 345 /* Write mask and pattern to the instruction array */ 346 tab->mask = mask; 347 tab->pattern = val; 348 349 /* 350 * If we got here, format string must be parsed and "a" 351 * should point to -1. If it's not, wrong number of bits 352 * in format string. Mark this as invalid and prevent 353 * from being matched. 354 */ 355 if (*format != 0 || (a != -1) || (error != 0)) { 356 tab->mask = 0; 357 tab->pattern = 0xffffffff; 358 printf("ERROR: skipping instruction op %s\n", 359 tab->name); 360 } 361 362 tab++; 363 } 364 } 365 366 static int 367 arm64_disasm_read_token(struct arm64_insn *insn, u_int opcode, 368 const char *token, int *val) 369 { 370 int i; 371 372 for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) { 373 if (strcmp(insn->tokens[i].name, token) == 0) { 374 *val = (opcode >> insn->tokens[i].pos & 375 ((1 << insn->tokens[i].len) - 1)); 376 return (0); 377 } 378 } 379 380 return (EINVAL); 381 } 382 383 static int 384 arm64_disasm_read_token_sign_ext(struct arm64_insn *insn, u_int opcode, 385 const char *token, int *val) 386 { 387 int i; 388 int msk; 389 390 for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) { 391 if (strcmp(insn->tokens[i].name, token) == 0) { 392 msk = (1 << insn->tokens[i].len) - 1; 393 *val = ((opcode >> insn->tokens[i].pos) & msk); 394 395 /* If last bit is 1, sign-extend the value */ 396 if (*val & (1 << (insn->tokens[i].len - 1))) 397 *val |= ~msk; 398 399 return (0); 400 } 401 } 402 403 return (EINVAL); 404 } 405 406 static const char * 407 arm64_w_reg(int num, int wsp) 408 { 409 if (num == 31) 410 return (wsp != 0 ? "wsp" : "wzr"); 411 return (w_reg[num]); 412 } 413 414 static const char * 415 arm64_x_reg(int num, int sp) 416 { 417 if (num == 31) 418 return (sp != 0 ? "sp" : "xzr"); 419 return (x_reg[num]); 420 } 421 422 static const char * 423 arm64_reg(int b64, int num, int sp) 424 { 425 if (b64 != 0) 426 return (arm64_x_reg(num, sp)); 427 return (arm64_w_reg(num, sp)); 428 } 429 430 vm_offset_t 431 disasm(const struct disasm_interface *di, vm_offset_t loc, int altfmt) 432 { 433 struct arm64_insn *i_ptr = arm64_i; 434 uint32_t insn; 435 int matchp; 436 int ret; 437 int shift, rm, rt, rd, rn, imm, sf, idx, option, scale, amount; 438 int sign_ext; 439 bool rm_absent, rd_absent, rn_absent; 440 /* Indicate if immediate should be outside or inside brackets */ 441 int inside; 442 /* Print exclamation mark if pre-incremented */ 443 int pre; 444 /* Indicate if x31 register should be printed as sp or xzr */ 445 int rm_sp, rt_sp, rd_sp, rn_sp; 446 /* Indicate if shift type ror is supported */ 447 bool has_shift_ror; 448 449 /* Initialize defaults, all are 0 except SF indicating 64bit access */ 450 shift = rd = rm = rn = imm = idx = option = amount = scale = 0; 451 sign_ext = 0; 452 sf = 1; 453 454 matchp = 0; 455 insn = di->di_readword(loc); 456 while (i_ptr->name) { 457 /* If mask is 0 then the parser was not initialized yet */ 458 if ((i_ptr->mask != 0) && 459 ((insn & i_ptr->mask) == i_ptr->pattern)) { 460 matchp = 1; 461 break; 462 } 463 i_ptr++; 464 } 465 if (matchp == 0) 466 goto undefined; 467 468 /* Global options */ 469 if (i_ptr->special_ops & OP_SF32) 470 sf = 0; 471 472 /* Global optional tokens */ 473 arm64_disasm_read_token(i_ptr, insn, "SF", &sf); 474 if (i_ptr->special_ops & OP_SF_INV) 475 sf = 1 - sf; 476 if (arm64_disasm_read_token(i_ptr, insn, "SIGN", &sign_ext) == 0) 477 sign_ext = 1 - sign_ext; 478 if (i_ptr->special_ops & OP_SIGN_EXT) 479 sign_ext = 1; 480 if (sign_ext != 0) 481 arm64_disasm_read_token_sign_ext(i_ptr, insn, "IMM", &imm); 482 else 483 arm64_disasm_read_token(i_ptr, insn, "IMM", &imm); 484 if (i_ptr->special_ops & OP_MULT_4) 485 imm <<= 2; 486 487 rm_sp = i_ptr->special_ops & OP_RM_SP; 488 rt_sp = i_ptr->special_ops & OP_RT_SP; 489 rd_sp = i_ptr->special_ops & OP_RD_SP; 490 rn_sp = i_ptr->special_ops & OP_RN_SP; 491 492 has_shift_ror = i_ptr->special_ops & OP_SHIFT_ROR; 493 494 /* Print opcode by type */ 495 switch (i_ptr->type) { 496 case TYPE_01: 497 /* 498 * OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64 499 * OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64 500 * OP <RD>, <RM> {, <shift> #<imm> } 501 * OP <RN>, <RM> {, <shift> #<imm> } 502 */ 503 504 rd_absent = arm64_disasm_read_token(i_ptr, insn, "RD", &rd); 505 rn_absent = arm64_disasm_read_token(i_ptr, insn, "RN", &rn); 506 rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm); 507 arm64_disasm_read_token(i_ptr, insn, "SHIFT", &shift); 508 509 /* 510 * if shift type is RESERVED for shifted register instruction, 511 * print undefined 512 */ 513 if (shift == 3 && !has_shift_ror) 514 goto undefined; 515 516 di->di_printf("%s\t", i_ptr->name); 517 518 /* 519 * If RD and RN are present, we will display the following 520 * patterns: 521 * - OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64 522 * - OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64 523 * Otherwise if only RD is present: 524 * - OP <RD>, <RM> {, <shift> #<imm> } 525 * Otherwise if only RN is present: 526 * - OP <RN>, <RM> {, <shift> #<imm> } 527 */ 528 if (!rd_absent && !rn_absent) 529 di->di_printf("%s, %s", arm64_reg(sf, rd, rd_sp), 530 arm64_reg(sf, rn, rn_sp)); 531 else if (!rd_absent) 532 di->di_printf("%s", arm64_reg(sf, rd, rd_sp)); 533 else 534 di->di_printf("%s", arm64_reg(sf, rn, rn_sp)); 535 536 /* If RM is present use it, otherwise use immediate notation */ 537 if (!rm_absent) { 538 di->di_printf(", %s", arm64_reg(sf, rm, rm_sp)); 539 if (imm != 0) 540 di->di_printf(", %s #%d", shift_2[shift], imm); 541 } else { 542 if (imm != 0 || shift != 0) 543 di->di_printf(", #0x%x", imm); 544 if (shift != 0) 545 di->di_printf(" lsl #12"); 546 } 547 break; 548 case TYPE_02: 549 /* 550 * OP <RT>, [<RN>, #<imm>]{!}] SF32/64 551 * OP <RT>, [<RN>], #<imm>{!} SF32/64 552 * OP <RT>, <RN>, <RM> {, EXTEND AMOUNT } 553 */ 554 555 /* Mandatory tokens */ 556 ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt); 557 ret |= arm64_disasm_read_token(i_ptr, insn, "RN", &rn); 558 if (ret != 0) { 559 printf("ERROR: " 560 "Missing mandatory token for op %s type %d\n", 561 i_ptr->name, i_ptr->type); 562 goto undefined; 563 } 564 565 /* Optional tokens */ 566 arm64_disasm_read_token(i_ptr, insn, "OPTION", &option); 567 arm64_disasm_read_token(i_ptr, insn, "SCALE", &scale); 568 rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm); 569 570 if (rm_absent) { 571 /* 572 * In unsigned operation, shift immediate value 573 * and reset options to default. 574 */ 575 if (sign_ext == 0) { 576 imm = imm << ((insn >> ARM_INSN_SIZE_OFFSET) & 577 ARM_INSN_SIZE_MASK); 578 option = 0; 579 } 580 switch (option) { 581 case 0x0: 582 pre = 0; 583 inside = 1; 584 break; 585 case 0x1: 586 pre = 0; 587 inside = 0; 588 break; 589 case 0x2: 590 default: 591 pre = 1; 592 inside = 1; 593 break; 594 } 595 596 di->di_printf("%s\t%s, ", i_ptr->name, 597 arm64_reg(sf, rt, rt_sp)); 598 if (inside != 0) { 599 di->di_printf("[%s", arm64_reg(1, rn, rn_sp)); 600 if (imm != 0) 601 di->di_printf(", #%d", imm); 602 di->di_printf("]"); 603 } else { 604 di->di_printf("[%s]", arm64_reg(1, rn, rn_sp)); 605 if (imm != 0) 606 di->di_printf(", #%d", imm); 607 } 608 if (pre != 0) 609 di->di_printf("!"); 610 } else { 611 /* Last bit of option field determines 32/64 bit offset */ 612 di->di_printf("%s\t%s, [%s, %s", i_ptr->name, 613 arm64_reg(sf, rt, rt_sp), arm64_reg(1, rn, rn_sp), 614 arm64_reg(option & 1, rm, rm_sp)); 615 616 if (scale == 0) 617 amount = 0; 618 else { 619 /* Calculate amount, it's op(31:30) */ 620 amount = (insn >> ARM_INSN_SIZE_OFFSET) & 621 ARM_INSN_SIZE_MASK; 622 } 623 624 switch (option) { 625 case 0x2: 626 di->di_printf(", uxtw #%d", amount); 627 break; 628 case 0x3: 629 if (scale != 0) 630 di->di_printf(", lsl #%d", amount); 631 break; 632 case 0x6: 633 di->di_printf(", sxtw #%d", amount); 634 break; 635 case 0x7: 636 di->di_printf(", sxtx #%d", amount); 637 break; 638 default: 639 di->di_printf(", rsv"); 640 break; 641 } 642 di->di_printf("]"); 643 } 644 645 break; 646 647 case TYPE_03: 648 /* OP <RT>, #imm SF32/64 */ 649 650 /* Mandatory tokens */ 651 ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt); 652 if (ret != 0) { 653 printf("ERROR: " 654 "Missing mandatory token for op %s type %d\n", 655 i_ptr->name, i_ptr->type); 656 goto undefined; 657 } 658 659 di->di_printf("%s\t%s, ", i_ptr->name, arm64_reg(sf, rt, rt_sp)); 660 if (i_ptr->special_ops & OP_LITERAL) 661 di->di_printf("0x%lx", loc + imm); 662 else 663 di->di_printf("#%d", imm); 664 665 break; 666 default: 667 goto undefined; 668 } 669 670 di->di_printf("\n"); 671 return (loc + INSN_SIZE); 672 673 undefined: 674 di->di_printf("undefined\t%08x\n", insn); 675 return (loc + INSN_SIZE); 676 } 677 678 /* Parse format strings at the very beginning */ 679 SYSINIT(arm64_disasm_generate_masks, SI_SUB_DDB_SERVICES, SI_ORDER_FIRST, 680 arm64_disasm_generate_masks, arm64_i); 681