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