]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/arm64/disassem.c
Update the Arm Optimized Routine library to v23.01
[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
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);