]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/arm64/disassem.c
Merge llvm-project release/16.x llvmorg-16.0.5-0-g185b81e034ba
[FreeBSD/FreeBSD.git] / sys / arm64 / arm64 / disassem.c
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);