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