]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/arm64/disassem.c
libarchive: merge security fix from vendor branch
[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 #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
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
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
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 *
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 *
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 *
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 *
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 *
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
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);