xref: /freebsd/sys/arm64/arm64/disassem.c (revision f3065e767def62d9b593dd7528c0eb121a7e1439)
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 	 */
95 	TYPE_01,
96 
97 	/*
98 	 * OP <RT>, [<RN>, #<imm>]{!} SF32/64
99 	 * OP <RT>, [<RN>], #<imm>{!} SF32/64
100 	 * OP <RT>, <RN>, <RM> {, EXTEND AMOUNT }
101 	 */
102 	TYPE_02,
103 
104 	/* OP <RT>, #imm SF32/64 */
105 	TYPE_03,
106 };
107 
108 /*
109  * Structure representing single parsed instruction format.
110  * name   - opcode name
111  * format - opcode format in a human-readable way
112  * type   - syntax type for printing
113  * special_ops  - special options passed to a printer (if any)
114  * mask   - bitmask for instruction matching
115  * pattern      - pattern to look for
116  * tokens - array of tokens (operands) inside instruction
117  */
118 struct arm64_insn {
119 	char *name;
120 	char *format;
121 	enum arm64_format_type type;
122 	uint64_t special_ops;
123 	uint32_t mask;
124 	uint32_t pattern;
125 	struct arm64_insn_token tokens[ARM64_MAX_TOKEN_CNT];
126 };
127 
128 /*
129  * Specify instruction opcode format in a human-readable way. Use notation
130  * obtained from ARM Architecture Reference Manual for ARMv8-A.
131  *
132  * Format string description:
133  *  Each group must be separated by "|". Group made of 0/1 is used to
134  *  generate mask and pattern for instruction matching. Groups containing
135  *  an operand token (in format NAME(length_bits)) are used to retrieve any
136  *  operand data from the instruction. Names here must be meaningful
137  *  and match the one described in the Manual.
138  *
139  * Token description:
140  * SF     - "0" represents 32-bit access, "1" represents 64-bit access
141  * SHIFT  - type of shift (instruction dependent)
142  * IMM    - immediate value
143  * Rx     - register number
144  * OPTION - command specific options
145  * SCALE  - scaling of immediate value
146  */
147 static struct arm64_insn arm64_i[] = {
148 	{ "add", "SF(1)|0001011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
149 	    TYPE_01, 0 },			/* add shifted register */
150 	{ "mov", "SF(1)|001000100000000000000|RN(5)|RD(5)",
151 	    TYPE_01, OP_RD_SP | OP_RN_SP },	/* mov (to/from sp) */
152 	{ "add", "SF(1)|0010001|SHIFT(2)|IMM(12)|RN(5)|RD(5)",
153 	    TYPE_01, OP_RD_SP | OP_RN_SP },	/* add immediate */
154 	{ "ldr", "1|SF(1)|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)",
155 	    TYPE_02, OP_SIGN_EXT | OP_RN_SP },	/* ldr immediate post/pre index */
156 	{ "ldr", "1|SF(1)|11100101|IMM(12)|RN(5)|RT(5)",
157 	    TYPE_02, OP_RN_SP },		/* ldr immediate unsigned */
158 	{ "ldr", "1|SF(1)|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
159 	    TYPE_02, OP_RN_SP },		/* ldr register */
160 	{ "ldr", "0|SF(1)|011000|IMM(19)|RT(5)",
161 	    TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 },	/* ldr literal */
162 	{ "ldrb", "00|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)",
163 	    TYPE_02, OP_SIGN_EXT | OP_SF32 | OP_RN_SP },
164 	    /* ldrb immediate post/pre index */
165 	{ "ldrb", "00|11100101|IMM(12)|RN(5)|RT(5)",
166 	    TYPE_02, OP_SF32 | OP_RN_SP },	/* ldrb immediate unsigned */
167 	{ "ldrb", "00|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
168 	    TYPE_02, OP_SF32 | OP_RN_SP },	/* ldrb register */
169 	{ "ldrh", "01|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)", TYPE_02,
170 	    OP_SIGN_EXT | OP_SF32 | OP_RN_SP },	/* ldrh immediate post/pre index */
171 	{ "ldrh", "01|11100101|IMM(12)|RN(5)|RT(5)",
172 	    TYPE_02, OP_SF32 | OP_RN_SP },	/* ldrh immediate unsigned */
173 	{ "ldrh", "01|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
174 	    TYPE_02, OP_SF32 | OP_RN_SP },	/* ldrh register */
175 	{ "ldrsb", "001110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)",
176 	    TYPE_02, OP_SIGN_EXT | OP_SF_INV | OP_RN_SP },
177 	    /* ldrsb immediate post/pre index */
178 	{ "ldrsb", "001110011|SF(1)|IMM(12)|RN(5)|RT(5)",\
179 	    TYPE_02, OP_SF_INV | OP_RN_SP },	/* ldrsb immediate unsigned */
180 	{ "ldrsb", "001110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
181 	    TYPE_02,  OP_SF_INV | OP_RN_SP },	/* ldrsb register */
182 	{ "ldrsh", "011110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)",
183 	    TYPE_02, OP_SIGN_EXT | OP_SF_INV | OP_RN_SP },
184 	    /* ldrsh immediate post/pre index */
185 	{ "ldrsh", "011110011|SF(1)|IMM(12)|RN(5)|RT(5)",
186 	    TYPE_02, OP_SF_INV | OP_RN_SP },	/* ldrsh immediate unsigned */
187 	{ "ldrsh", "011110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
188 	    TYPE_02, OP_SF_INV | OP_RN_SP },	/* ldrsh register */
189 	{ "ldrsw", "10111000100|IMM(9)|OPTION(2)|RN(5)|RT(5)",
190 	    TYPE_02, OP_SIGN_EXT | OP_RN_SP },	/* ldrsw immediate post/pre index */
191 	{ "ldrsw", "1011100110|IMM(12)|RN(5)|RT(5)",
192 	    TYPE_02, OP_RN_SP },		/* ldrsw immediate unsigned */
193 	{ "ldrsw", "10111000101|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
194 	    TYPE_02, OP_RN_SP },		/* ldrsw register */
195 	{ "ldrsw", "10011000|IMM(19)|RT(5)",
196 	    TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 },	/* ldrsw literal */
197 	{ "str", "1|SF(1)|111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)",
198 	    TYPE_02, OP_SIGN_EXT | OP_RN_SP }, 	/* str immediate post/pre index */
199 	{ "str", "1|SF(1)|11100100|IMM(12)|RN(5)|RT(5)",
200 	    TYPE_02, OP_RN_SP },		/* str immediate unsigned */
201 	{ "str", "1|SF(1)|111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
202 	    TYPE_02, OP_RN_SP },		/* str register */
203 	{ "strb", "00111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)",
204 	    TYPE_02, OP_SIGN_EXT | OP_SF32 | OP_RN_SP },
205 	    /* strb immediate post/pre index */
206 	{ "strb", "0011100100|IMM(12)|RN(5)|RT(5)",
207 	    TYPE_02, OP_SF32 | OP_RN_SP },	/* strb immediate unsigned */
208 	{ "strb", "00111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
209 	    TYPE_02, OP_SF32 | OP_RN_SP },	/* strb register */
210 	{ "strh", "01111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)",
211 	    TYPE_02, OP_SF32 | OP_SIGN_EXT | OP_RN_SP },
212 	    /* strh immediate post/pre index */
213 	{ "strh", "0111100100|IMM(12)|RN(5)|RT(5)",
214 	    TYPE_02, OP_SF32 | OP_RN_SP },
215 	    /* strh immediate unsigned */
216 	{ "strh", "01111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
217 	    TYPE_02, OP_SF32 | OP_RN_SP },
218 	    /* strh register */
219 	{ NULL, NULL }
220 };
221 
222 static void
223 arm64_disasm_generate_masks(struct arm64_insn *tab)
224 {
225 	uint32_t mask, val;
226 	int a, i;
227 	int len, ret;
228 	int token = 0;
229 	char *format;
230 	int error;
231 
232 	while (tab->name != NULL) {
233 		mask = 0;
234 		val = 0;
235 		format = tab->format;
236 		token = 0;
237 		error = 0;
238 
239 		/*
240 		 * For each entry analyze format strings from the
241 		 * left (i.e. from the MSB).
242 		 */
243 		a = (INSN_SIZE * NBBY) - 1;
244 		while (*format != '\0' && (a >= 0)) {
245 			switch (*format) {
246 			case '0':
247 				/* Bit is 0, add to mask and pattern */
248 				mask |= (1 << a);
249 				a--;
250 				format++;
251 				break;
252 			case '1':
253 				/* Bit is 1, add to mask and pattern */
254 				mask |= (1 << a);
255 				val |= (1 << a);
256 				a--;
257 				format++;
258 				break;
259 			case '|':
260 				/* skip */
261 				format++;
262 				break;
263 			default:
264 				/* Token found, copy the name */
265 				memset(tab->tokens[token].name, 0,
266 				    sizeof(tab->tokens[token].name));
267 				i = 0;
268 				while (*format != '(') {
269 					tab->tokens[token].name[i] = *format;
270 					i++;
271 					format++;
272 					if (i >= ARM64_MAX_TOKEN_LEN) {
273 						printf("ERROR: "
274 						    "token too long in op %s\n",
275 						    tab->name);
276 						error = 1;
277 						break;
278 					}
279 				}
280 				if (error != 0)
281 					break;
282 
283 				/* Read the length value */
284 				ret = sscanf(format, "(%d)", &len);
285 				if (ret == 1) {
286 					if (token >= ARM64_MAX_TOKEN_CNT) {
287 						printf("ERROR: "
288 						    "too many tokens in op %s\n",
289 						    tab->name);
290 						error = 1;
291 						break;
292 					}
293 
294 					a -= len;
295 					tab->tokens[token].pos = a + 1;
296 					tab->tokens[token].len = len;
297 					token++;
298 				}
299 
300 				/* Skip to the end of the token */
301 				while (*format != 0 && *format != '|')
302 					format++;
303 			}
304 		}
305 
306 		/* Write mask and pattern to the instruction array */
307 		tab->mask = mask;
308 		tab->pattern = val;
309 
310 		/*
311 		 * If we got here, format string must be parsed and "a"
312 		 * should point to -1. If it's not, wrong number of bits
313 		 * in format string. Mark this as invalid and prevent
314 		 * from being matched.
315 		 */
316 		if (*format != 0 || (a != -1) || (error != 0)) {
317 			tab->mask = 0;
318 			tab->pattern = 0xffffffff;
319 			printf("ERROR: skipping instruction op %s\n",
320 			    tab->name);
321 		}
322 
323 		tab++;
324 	}
325 }
326 
327 static int
328 arm64_disasm_read_token(struct arm64_insn *insn, u_int opcode,
329     const char *token, int *val)
330 {
331 	int i;
332 
333 	for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) {
334 		if (strcmp(insn->tokens[i].name, token) == 0) {
335 			*val = (opcode >> insn->tokens[i].pos &
336 			    ((1 << insn->tokens[i].len) - 1));
337 			return (0);
338 		}
339 	}
340 
341 	return (EINVAL);
342 }
343 
344 static int
345 arm64_disasm_read_token_sign_ext(struct arm64_insn *insn, u_int opcode,
346     const char *token, int *val)
347 {
348 	int i;
349 	int msk;
350 
351 	for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) {
352 		if (strcmp(insn->tokens[i].name, token) == 0) {
353 			msk = (1 << insn->tokens[i].len) - 1;
354 			*val = ((opcode >> insn->tokens[i].pos) & msk);
355 
356 			/* If last bit is 1, sign-extend the value */
357 			if (*val & (1 << (insn->tokens[i].len - 1)))
358 				*val |= ~msk;
359 
360 			return (0);
361 		}
362 	}
363 
364 	return (EINVAL);
365 }
366 
367 static const char *
368 arm64_w_reg(int num, int wsp)
369 {
370 	if (num == 31)
371 		return (wsp != 0 ? "wsp" : "wzr");
372 	return (w_reg[num]);
373 }
374 
375 static const char *
376 arm64_x_reg(int num, int sp)
377 {
378 	if (num == 31)
379 		return (sp != 0 ? "sp" : "xzr");
380 	return (x_reg[num]);
381 }
382 
383 static const char *
384 arm64_reg(int b64, int num, int sp)
385 {
386 	if (b64 != 0)
387 		return (arm64_x_reg(num, sp));
388 	return (arm64_w_reg(num, sp));
389 }
390 
391 vm_offset_t
392 disasm(const struct disasm_interface *di, vm_offset_t loc, int altfmt)
393 {
394 	struct arm64_insn *i_ptr = arm64_i;
395 	uint32_t insn;
396 	int matchp;
397 	int ret;
398 	int shift, rm, rt, rd, rn, imm, sf, idx, option, scale, amount;
399 	int sign_ext;
400 	int rm_absent;
401 	/* Indicate if immediate should be outside or inside brackets */
402 	int inside;
403 	/* Print exclamation mark if pre-incremented */
404 	int pre;
405 	/* Indicate if x31 register should be printed as sp or xzr */
406 	int rm_sp, rt_sp, rd_sp, rn_sp;
407 
408 	/* Initialize defaults, all are 0 except SF indicating 64bit access */
409 	shift = rd = rm = rn = imm = idx = option = amount = scale = 0;
410 	sign_ext = 0;
411 	sf = 1;
412 
413 	matchp = 0;
414 	insn = di->di_readword(loc);
415 	while (i_ptr->name) {
416 		/* If mask is 0 then the parser was not initialized yet */
417 		if ((i_ptr->mask != 0) &&
418 		    ((insn & i_ptr->mask) == i_ptr->pattern)) {
419 			matchp = 1;
420 			break;
421 		}
422 		i_ptr++;
423 	}
424 	if (matchp == 0)
425 		goto undefined;
426 
427 	/* Global options */
428 	if (i_ptr->special_ops & OP_SF32)
429 		sf = 0;
430 
431 	/* Global optional tokens */
432 	arm64_disasm_read_token(i_ptr, insn, "SF", &sf);
433 	if (i_ptr->special_ops & OP_SF_INV)
434 		sf = 1 - sf;
435 	if (arm64_disasm_read_token(i_ptr, insn, "SIGN", &sign_ext) == 0)
436 		sign_ext = 1 - sign_ext;
437 	if (i_ptr->special_ops & OP_SIGN_EXT)
438 		sign_ext = 1;
439 	if (sign_ext != 0)
440 		arm64_disasm_read_token_sign_ext(i_ptr, insn, "IMM", &imm);
441 	else
442 		arm64_disasm_read_token(i_ptr, insn, "IMM", &imm);
443 	if (i_ptr->special_ops & OP_MULT_4)
444 		imm <<= 2;
445 
446 	rm_sp = i_ptr->special_ops & OP_RM_SP;
447 	rt_sp = i_ptr->special_ops & OP_RT_SP;
448 	rd_sp = i_ptr->special_ops & OP_RD_SP;
449 	rn_sp = i_ptr->special_ops & OP_RN_SP;
450 
451 	/* Print opcode by type */
452 	switch (i_ptr->type) {
453 	case TYPE_01:
454 		/*
455 		 * OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64
456 		 * OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64
457 		 */
458 
459 		/* Mandatory tokens */
460 		ret = arm64_disasm_read_token(i_ptr, insn, "RD", &rd);
461 		ret |= arm64_disasm_read_token(i_ptr, insn, "RN", &rn);
462 		if (ret != 0) {
463 			printf("ERROR: "
464 			    "Missing mandatory token for op %s type %d\n",
465 			    i_ptr->name, i_ptr->type);
466 			goto undefined;
467 		}
468 
469 		/* Optional tokens */
470 		arm64_disasm_read_token(i_ptr, insn, "SHIFT", &shift);
471 		rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm);
472 
473 		di->di_printf("%s\t%s, %s", i_ptr->name,
474 		    arm64_reg(sf, rd, rd_sp), arm64_reg(sf, rn, rn_sp));
475 
476 		/* If RM is present use it, otherwise use immediate notation */
477 		if (rm_absent == 0) {
478 			di->di_printf(", %s", arm64_reg(sf, rm, rm_sp));
479 			if (imm != 0)
480 				di->di_printf(", %s #%d", shift_2[shift], imm);
481 		} else {
482 			if (imm != 0 || shift != 0)
483 				di->di_printf(", #0x%x", imm);
484 			if (shift != 0)
485 				di->di_printf(" LSL #12");
486 		}
487 		break;
488 	case TYPE_02:
489 		/*
490 		 * OP <RT>, [<RN>, #<imm>]{!}] SF32/64
491 		 * OP <RT>, [<RN>], #<imm>{!} SF32/64
492 		 * OP <RT>, <RN>, <RM> {, EXTEND AMOUNT }
493 		 */
494 
495 		/* Mandatory tokens */
496 		ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt);
497 		ret |= arm64_disasm_read_token(i_ptr, insn, "RN", &rn);
498 		if (ret != 0) {
499 			printf("ERROR: "
500 			    "Missing mandatory token for op %s type %d\n",
501 			    i_ptr->name, i_ptr->type);
502 			goto undefined;
503 		}
504 
505 		/* Optional tokens */
506 		arm64_disasm_read_token(i_ptr, insn, "OPTION", &option);
507 		arm64_disasm_read_token(i_ptr, insn, "SCALE", &scale);
508 		rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm);
509 
510 		if (rm_absent) {
511 			/*
512 			 * In unsigned operation, shift immediate value
513 			 * and reset options to default.
514 			 */
515 			if (sign_ext == 0) {
516 				imm = imm << ((insn >> ARM_INSN_SIZE_OFFSET) &
517 				    ARM_INSN_SIZE_MASK);
518 				option = 0;
519 			}
520 			switch (option) {
521 			case 0x0:
522 				pre = 0;
523 				inside = 1;
524 				break;
525 			case 0x1:
526 				pre = 0;
527 				inside = 0;
528 				break;
529 			case 0x2:
530 			default:
531 				pre = 1;
532 				inside = 1;
533 				break;
534 			}
535 
536 			di->di_printf("%s\t%s, ", i_ptr->name,
537 			    arm64_reg(sf, rt, rt_sp));
538 			if (inside != 0) {
539 				di->di_printf("[%s", arm64_reg(1, rn, rn_sp));
540 				if (imm != 0)
541 					di->di_printf(", #%d", imm);
542 				di->di_printf("]");
543 			} else {
544 				di->di_printf("[%s]", arm64_reg(1, rn, rn_sp));
545 				if (imm != 0)
546 					di->di_printf(", #%d", imm);
547 			}
548 			if (pre != 0)
549 				di->di_printf("!");
550 		} else {
551 			/* Last bit of option field determines 32/64 bit offset */
552 			di->di_printf("%s\t%s, [%s, %s", i_ptr->name,
553 			    arm64_reg(sf, rt, rt_sp), arm64_reg(1, rn, rn_sp),
554 			    arm64_reg(option & 1, rm, rm_sp));
555 
556 			if (scale == 0)
557 				amount = 0;
558 			else {
559 				/* Calculate amount, it's op(31:30) */
560 				amount = (insn >> ARM_INSN_SIZE_OFFSET) &
561 			            ARM_INSN_SIZE_MASK;
562 			}
563 
564 			switch (option) {
565 			case 0x2:
566 				di->di_printf(", uxtw #%d", amount);
567 				break;
568 			case 0x3:
569 				if (scale != 0)
570 					di->di_printf(", lsl #%d", amount);
571 				break;
572 			case 0x6:
573 				di->di_printf(", sxtw #%d", amount);
574 				break;
575 			case 0x7:
576 				di->di_printf(", sxtx #%d", amount);
577 				break;
578 			default:
579 				di->di_printf(", RSVD");
580 				break;
581 			}
582 			di->di_printf("]");
583 		}
584 
585 		break;
586 
587 	case TYPE_03:
588 		/* OP <RT>, #imm SF32/64 */
589 
590 		/* Mandatory tokens */
591 		ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt);
592 		if (ret != 0) {
593 			printf("ERROR: "
594 			    "Missing mandatory token for op %s type %d\n",
595 			    i_ptr->name, i_ptr->type);
596 			goto undefined;
597 		}
598 
599 		di->di_printf("%s\t%s, ", i_ptr->name, arm64_reg(sf, rt, rt_sp));
600 		if (i_ptr->special_ops & OP_LITERAL)
601 			di->di_printf("0x%lx", loc + imm);
602 		else
603 			di->di_printf("#%d", imm);
604 
605 		break;
606 	default:
607 		goto undefined;
608 	}
609 
610 	di->di_printf("\n");
611 	return (loc + INSN_SIZE);
612 
613 undefined:
614 	di->di_printf("undefined\t%08x\n", insn);
615 	return (loc + INSN_SIZE);
616 }
617 
618 /* Parse format strings at the very beginning */
619 SYSINIT(arm64_disasm_generate_masks, SI_SUB_DDB_SERVICES, SI_ORDER_FIRST,
620     arm64_disasm_generate_masks, arm64_i);
621