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