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