xref: /freebsd/sys/arm64/arm64/disassem.c (revision bdd1243d)
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);
681