xref: /freebsd/sys/arm64/arm64/disassem.c (revision c7fc655f)
18a1867f4SWojciech Macek /*-
28a1867f4SWojciech Macek  * Copyright (c) 2016 Cavium
38a1867f4SWojciech Macek  * All rights reserved.
48a1867f4SWojciech Macek  *
58a1867f4SWojciech Macek  * This software was developed by Semihalf.
68a1867f4SWojciech Macek  *
78a1867f4SWojciech Macek  * Redistribution and use in source and binary forms, with or without
88a1867f4SWojciech Macek  * modification, are permitted provided that the following conditions
98a1867f4SWojciech Macek  * are met:
108a1867f4SWojciech Macek  * 1. Redistributions of source code must retain the above copyright
118a1867f4SWojciech Macek  *    notice, this list of conditions and the following disclaimer.
128a1867f4SWojciech Macek  * 2. Redistributions in binary form must reproduce the above copyright
138a1867f4SWojciech Macek  *    notice, this list of conditions and the following disclaimer in the
148a1867f4SWojciech Macek  *    documentation and/or other materials provided with the distribution.
158a1867f4SWojciech Macek  *
168a1867f4SWojciech Macek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
178a1867f4SWojciech Macek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
188a1867f4SWojciech Macek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
198a1867f4SWojciech Macek  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
208a1867f4SWojciech Macek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
218a1867f4SWojciech Macek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
228a1867f4SWojciech Macek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
238a1867f4SWojciech Macek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
248a1867f4SWojciech Macek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
258a1867f4SWojciech Macek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
268a1867f4SWojciech Macek  * SUCH DAMAGE.
278a1867f4SWojciech Macek  */
288a1867f4SWojciech Macek 
298a1867f4SWojciech Macek #include <sys/cdefs.h>
308a1867f4SWojciech Macek __FBSDID("$FreeBSD$");
318a1867f4SWojciech Macek #include <sys/param.h>
328a1867f4SWojciech Macek 
338a1867f4SWojciech Macek #include <sys/systm.h>
348a1867f4SWojciech Macek #include <machine/disassem.h>
358a1867f4SWojciech Macek #include <machine/armreg.h>
368a1867f4SWojciech Macek #include <ddb/ddb.h>
378a1867f4SWojciech Macek 
388a1867f4SWojciech Macek #define	ARM64_MAX_TOKEN_LEN	8
398a1867f4SWojciech Macek #define	ARM64_MAX_TOKEN_CNT	10
408a1867f4SWojciech Macek 
41c7fc655fSWojciech Macek #define	ARM_INSN_SIZE_OFFSET	30
42c7fc655fSWojciech Macek #define	ARM_INSN_SIZE_MASK	0x3
43c7fc655fSWojciech Macek 
44c7fc655fSWojciech Macek /* Special options for instruction printing */
45c7fc655fSWojciech Macek #define	OP_SIGN_EXT	(1UL << 0)	/* Sign-extend immediate value */
46c7fc655fSWojciech Macek #define	OP_LITERAL	(1UL << 1)	/* Use literal (memory offset) */
47c7fc655fSWojciech Macek #define	OP_MULT_4	(1UL << 2)	/* Multiply immediate by 4 */
48c7fc655fSWojciech Macek #define	OP_SF32		(1UL << 3)	/* Force 32-bit access */
49c7fc655fSWojciech Macek #define	OP_SF_INV	(1UL << 6)	/* SF is inverted (1 means 32 bit access) */
50c7fc655fSWojciech Macek 
518a1867f4SWojciech Macek static const char *w_reg[] = {
528a1867f4SWojciech Macek 	"w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7",
538a1867f4SWojciech Macek 	"w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15",
548a1867f4SWojciech Macek 	"w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23",
558a1867f4SWojciech Macek 	"w24", "w25", "w26", "w27", "w28", "w29", "w30", "wSP",
568a1867f4SWojciech Macek };
578a1867f4SWojciech Macek 
588a1867f4SWojciech Macek static const char *x_reg[] = {
598a1867f4SWojciech Macek 	"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
608a1867f4SWojciech Macek 	"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
618a1867f4SWojciech Macek 	"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
628a1867f4SWojciech Macek 	"x24", "x25", "x26", "x27", "x28", "x29", "LR", "SP",
638a1867f4SWojciech Macek };
648a1867f4SWojciech Macek 
658a1867f4SWojciech Macek static const char *shift_2[] = {
668a1867f4SWojciech Macek 	"LSL", "LSR", "ASR", "RSV"
678a1867f4SWojciech Macek };
688a1867f4SWojciech Macek 
698a1867f4SWojciech Macek /*
708a1867f4SWojciech Macek  * Structure representing single token (operand) inside instruction.
718a1867f4SWojciech Macek  * name   - name of operand
728a1867f4SWojciech Macek  * pos    - position within the instruction (in bits)
738a1867f4SWojciech Macek  * len    - operand length (in bits)
748a1867f4SWojciech Macek  */
758a1867f4SWojciech Macek struct arm64_insn_token {
768a1867f4SWojciech Macek 	char name[ARM64_MAX_TOKEN_LEN];
778a1867f4SWojciech Macek 	int pos;
788a1867f4SWojciech Macek 	int len;
798a1867f4SWojciech Macek };
808a1867f4SWojciech Macek 
818a1867f4SWojciech Macek /*
828a1867f4SWojciech Macek  * Define generic types for instruction printing.
838a1867f4SWojciech Macek  */
848a1867f4SWojciech Macek enum arm64_format_type {
858a1867f4SWojciech Macek 	TYPE_01,	/* OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64
868a1867f4SWojciech Macek 			   OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64 */
87c7fc655fSWojciech Macek 	TYPE_02,	/* OP <RT>, [<RN>, #<imm>]{!}] SF32/64
88c7fc655fSWojciech Macek 			   OP <RT>, [<RN>], #<imm>{!} SF32/64
89c7fc655fSWojciech Macek 			   OP <RT>, <RN>, <RM> {, EXTEND AMOUNT } */
90c7fc655fSWojciech Macek 	TYPE_03,	/* OP <RT>, #imm SF32/64 */
918a1867f4SWojciech Macek };
928a1867f4SWojciech Macek 
938a1867f4SWojciech Macek /*
948a1867f4SWojciech Macek  * Structure representing single parsed instruction format.
958a1867f4SWojciech Macek  * name   - opcode name
968a1867f4SWojciech Macek  * format - opcode format in a human-readable way
978a1867f4SWojciech Macek  * type   - syntax type for printing
988a1867f4SWojciech Macek  * special_ops  - special options passed to a printer (if any)
998a1867f4SWojciech Macek  * mask   - bitmask for instruction matching
1008a1867f4SWojciech Macek  * pattern      - pattern to look for
1018a1867f4SWojciech Macek  * tokens - array of tokens (operands) inside instruction
1028a1867f4SWojciech Macek  */
1038a1867f4SWojciech Macek struct arm64_insn {
1048a1867f4SWojciech Macek 	char* name;
1058a1867f4SWojciech Macek 	char* format;
1068a1867f4SWojciech Macek 	enum arm64_format_type type;
1078a1867f4SWojciech Macek 	uint64_t special_ops;
1088a1867f4SWojciech Macek 	uint32_t mask;
1098a1867f4SWojciech Macek 	uint32_t pattern;
1108a1867f4SWojciech Macek 	struct arm64_insn_token tokens[ARM64_MAX_TOKEN_CNT];
1118a1867f4SWojciech Macek };
1128a1867f4SWojciech Macek 
1138a1867f4SWojciech Macek /*
1148a1867f4SWojciech Macek  * Specify instruction opcode format in a human-readable way. Use notation
1158a1867f4SWojciech Macek  * obtained from ARM Architecture Reference Manual for ARMv8-A.
1168a1867f4SWojciech Macek  *
1178a1867f4SWojciech Macek  * Format string description:
1188a1867f4SWojciech Macek  *  Each group must be separated by "|". Group made of 0/1 is used to
1198a1867f4SWojciech Macek  *  generate mask and pattern for instruction matching. Groups containing
1208a1867f4SWojciech Macek  *  an operand token (in format NAME(length_bits)) are used to retrieve any
1218a1867f4SWojciech Macek  *  operand data from the instruction. Names here must be meaningful
1228a1867f4SWojciech Macek  *  and match the one described in the Manual.
1238a1867f4SWojciech Macek  *
1248a1867f4SWojciech Macek  * Token description:
1258a1867f4SWojciech Macek  * SF     - "0" represents 32-bit access, "1" represents 64-bit access
1268a1867f4SWojciech Macek  * SHIFT  - type of shift (instruction dependent)
1278a1867f4SWojciech Macek  * IMM    - immediate value
1288a1867f4SWojciech Macek  * Rx     - register number
129c7fc655fSWojciech Macek  * OPTION - command specific options
130c7fc655fSWojciech Macek  * SCALE  - scaling of immediate value
1318a1867f4SWojciech Macek  */
1328a1867f4SWojciech Macek static struct arm64_insn arm64_i[] = {
133c7fc655fSWojciech Macek 	{ "add", "SF(1)|0001011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
134c7fc655fSWojciech Macek 	    TYPE_01, 0 },
135c7fc655fSWojciech Macek 	{ "mov", "SF(1)|001000100000000000000|RN(5)|RD(5)",
136c7fc655fSWojciech Macek 	    TYPE_01, 0 },
137c7fc655fSWojciech Macek 	{ "add", "SF(1)|0010001|SHIFT(2)|IMM(12)|RN(5)|RD(5)",
138c7fc655fSWojciech Macek 	    TYPE_01, 0 },
139c7fc655fSWojciech Macek 	{ "ldr", "1|SF(1)|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)",
140c7fc655fSWojciech Macek 	    TYPE_02, OP_SIGN_EXT },		/* ldr immediate post/pre index */
141c7fc655fSWojciech Macek 	{ "ldr", "1|SF(1)|11100101|IMM(12)|RN(5)|RT(5)",
142c7fc655fSWojciech Macek 	    TYPE_02, 0 },			/* ldr immediate unsigned */
143c7fc655fSWojciech Macek 	{ "ldr", "1|SF(1)|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
144c7fc655fSWojciech Macek 	    TYPE_02, 0 },			/* ldr register */
145c7fc655fSWojciech Macek 	{ "ldr", "0|SF(1)|011000|IMM(19)|RT(5)",
146c7fc655fSWojciech Macek 	    TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 },	/* ldr literal */
147c7fc655fSWojciech Macek 	{ "ldrb", "00|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)",
148c7fc655fSWojciech Macek 	    TYPE_02, OP_SIGN_EXT | OP_SF32 },	/* ldrb immediate post/pre index */
149c7fc655fSWojciech Macek 	{ "ldrb", "00|11100101|IMM(12)|RN(5)|RT(5)",
150c7fc655fSWojciech Macek 	    TYPE_02, OP_SF32 },			/* ldrb immediate unsigned */
151c7fc655fSWojciech Macek 	{ "ldrb", "00|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
152c7fc655fSWojciech Macek 	    TYPE_02, OP_SF32  },		/* ldrb register */
153c7fc655fSWojciech Macek 	{ "ldrh", "01|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)", TYPE_02,
154c7fc655fSWojciech Macek 	    OP_SIGN_EXT | OP_SF32 },		/* ldrh immediate post/pre index */
155c7fc655fSWojciech Macek 	{ "ldrh", "01|11100101|IMM(12)|RN(5)|RT(5)",
156c7fc655fSWojciech Macek 	    TYPE_02, OP_SF32 },			/* ldrh immediate unsigned */
157c7fc655fSWojciech Macek 	{ "ldrh", "01|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
158c7fc655fSWojciech Macek 	    TYPE_02, OP_SF32 },			/* ldrh register */
159c7fc655fSWojciech Macek 	{ "ldrsb", "001110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)",
160c7fc655fSWojciech Macek 	    TYPE_02, OP_SIGN_EXT | OP_SF_INV },	/* ldrsb immediate post/pre index */
161c7fc655fSWojciech Macek 	{ "ldrsb", "001110011|SF(1)|IMM(12)|RN(5)|RT(5)",\
162c7fc655fSWojciech Macek 	    TYPE_02, OP_SF_INV},		/* ldrsb immediate unsigned */
163c7fc655fSWojciech Macek 	{ "ldrsb", "001110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
164c7fc655fSWojciech Macek 	    TYPE_02,  OP_SF_INV },		/* ldrsb register */
165c7fc655fSWojciech Macek 	{ "ldrsh", "011110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)",
166c7fc655fSWojciech Macek 	    TYPE_02, OP_SIGN_EXT | OP_SF_INV },	/* ldrsh immediate post/pre index */
167c7fc655fSWojciech Macek 	{ "ldrsh", "011110011|SF(1)|IMM(12)|RN(5)|RT(5)",
168c7fc655fSWojciech Macek 	    TYPE_02, OP_SF_INV},		/* ldrsh immediate unsigned */
169c7fc655fSWojciech Macek 	{ "ldrsh", "011110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
170c7fc655fSWojciech Macek 	    TYPE_02, OP_SF_INV },		/* ldrsh register */
171c7fc655fSWojciech Macek 	{ "ldrsw", "10111000100|IMM(9)|OPTION(2)|RN(5)|RT(5)",
172c7fc655fSWojciech Macek 	    TYPE_02, OP_SIGN_EXT },		/* ldrsw immediate post/pre index */
173c7fc655fSWojciech Macek 	{ "ldrsw", "1011100110|IMM(12)|RN(5)|RT(5)",
174c7fc655fSWojciech Macek 	    TYPE_02, 0 },			/* ldrsw immediate unsigned */
175c7fc655fSWojciech Macek 	{ "ldrsw", "10111000101|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
176c7fc655fSWojciech Macek 	    TYPE_02, 0 },			/* ldrsw register */
177c7fc655fSWojciech Macek 	{ "ldrsw", "10011000|IMM(19)|RT(5)",
178c7fc655fSWojciech Macek 	    TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 },	/* ldr literal */
1798a1867f4SWojciech Macek 	{ NULL, NULL }
1808a1867f4SWojciech Macek };
1818a1867f4SWojciech Macek 
1828a1867f4SWojciech Macek static void
1838a1867f4SWojciech Macek arm64_disasm_generate_masks(struct arm64_insn *tab)
1848a1867f4SWojciech Macek {
1858a1867f4SWojciech Macek 	uint32_t mask, val;
1868a1867f4SWojciech Macek 	int a, i;
1878a1867f4SWojciech Macek 	int len, ret;
1888a1867f4SWojciech Macek 	int token = 0;
1898a1867f4SWojciech Macek 	char *format;
1908a1867f4SWojciech Macek 	int error;
1918a1867f4SWojciech Macek 
1928a1867f4SWojciech Macek 	while (tab->name != NULL) {
1938a1867f4SWojciech Macek 		mask = 0;
1948a1867f4SWojciech Macek 		val = 0;
1958a1867f4SWojciech Macek 		format = tab->format;
1968a1867f4SWojciech Macek 		token = 0;
1978a1867f4SWojciech Macek 		error = 0;
1988a1867f4SWojciech Macek 
1998a1867f4SWojciech Macek 		/*
2008a1867f4SWojciech Macek 		 * For each entry analyze format strings from the
2018a1867f4SWojciech Macek 		 * left (i.e. from the MSB).
2028a1867f4SWojciech Macek 		 */
2038a1867f4SWojciech Macek 		a = (INSN_SIZE * NBBY) - 1;
2048a1867f4SWojciech Macek 		while (*format != '\0' && (a >= 0)) {
2058a1867f4SWojciech Macek 			switch(*format) {
2068a1867f4SWojciech Macek 			case '0':
2078a1867f4SWojciech Macek 				/* Bit is 0, add to mask and pattern */
2088a1867f4SWojciech Macek 				mask |= (1 << a);
2098a1867f4SWojciech Macek 				a--;
2108a1867f4SWojciech Macek 				format++;
2118a1867f4SWojciech Macek 				break;
2128a1867f4SWojciech Macek 			case '1':
2138a1867f4SWojciech Macek 				/* Bit is 1, add to mask and pattern */
2148a1867f4SWojciech Macek 				mask |= (1 << a);
2158a1867f4SWojciech Macek 				val |= (1 << a);
2168a1867f4SWojciech Macek 				a--;
2178a1867f4SWojciech Macek 				format++;
2188a1867f4SWojciech Macek 				break;
2198a1867f4SWojciech Macek 			case '|':
2208a1867f4SWojciech Macek 				/* skip */
2218a1867f4SWojciech Macek 				format++;
2228a1867f4SWojciech Macek 				break;
2238a1867f4SWojciech Macek 			default:
2248a1867f4SWojciech Macek 				/* Token found, copy the name */
2258a1867f4SWojciech Macek 				memset(tab->tokens[token].name, 0,
2268a1867f4SWojciech Macek 				    sizeof(tab->tokens[token].name));
2278a1867f4SWojciech Macek 				i = 0;
2288a1867f4SWojciech Macek 				while (*format != '(') {
2298a1867f4SWojciech Macek 					tab->tokens[token].name[i] = *format;
2308a1867f4SWojciech Macek 					i++;
2318a1867f4SWojciech Macek 					format++;
2328a1867f4SWojciech Macek 					if (i >= ARM64_MAX_TOKEN_LEN) {
2338a1867f4SWojciech Macek 						printf("ERROR: token too long in op %s\n",
2348a1867f4SWojciech Macek 						    tab->name);
2358a1867f4SWojciech Macek 						error = 1;
2368a1867f4SWojciech Macek 						break;
2378a1867f4SWojciech Macek 					}
2388a1867f4SWojciech Macek 				}
2398a1867f4SWojciech Macek 				if (error != 0)
2408a1867f4SWojciech Macek 					break;
2418a1867f4SWojciech Macek 
2428a1867f4SWojciech Macek 				/* Read the length value */
2438a1867f4SWojciech Macek 				ret = sscanf(format, "(%d)", &len);
2448a1867f4SWojciech Macek 				if (ret == 1) {
2458a1867f4SWojciech Macek 					if (token >= ARM64_MAX_TOKEN_CNT) {
2468a1867f4SWojciech Macek 						printf("ERROR: to many tokens in op %s\n",
2478a1867f4SWojciech Macek 						    tab->name);
2488a1867f4SWojciech Macek 						error = 1;
2498a1867f4SWojciech Macek 						break;
2508a1867f4SWojciech Macek 					}
2518a1867f4SWojciech Macek 
2528a1867f4SWojciech Macek 					a -= len;
2538a1867f4SWojciech Macek 					tab->tokens[token].pos = a + 1;
2548a1867f4SWojciech Macek 					tab->tokens[token].len = len;
2558a1867f4SWojciech Macek 					token++;
2568a1867f4SWojciech Macek 				}
2578a1867f4SWojciech Macek 
2588a1867f4SWojciech Macek 				/* Skip to the end of the token */
2598a1867f4SWojciech Macek 				while (*format != 0 && *format != '|')
2608a1867f4SWojciech Macek 					format++;
2618a1867f4SWojciech Macek 			}
2628a1867f4SWojciech Macek 		}
2638a1867f4SWojciech Macek 
2648a1867f4SWojciech Macek 		/* Write mask and pattern to the instruction array */
2658a1867f4SWojciech Macek 		tab->mask = mask;
2668a1867f4SWojciech Macek 		tab->pattern = val;
2678a1867f4SWojciech Macek 
2688a1867f4SWojciech Macek 		/*
2698a1867f4SWojciech Macek 		 * If we got here, format string must be parsed and "a"
2708a1867f4SWojciech Macek 		 * should point to -1. If it's not, wrong number of bits
2718a1867f4SWojciech Macek 		 * in format string. Mark this as invalid and prevent
2728a1867f4SWojciech Macek 		 * from being matched.
2738a1867f4SWojciech Macek 		 */
2748a1867f4SWojciech Macek 		if (*format != 0 || (a != -1) || (error != 0)) {
2758a1867f4SWojciech Macek 			tab->mask = 0;
2768a1867f4SWojciech Macek 			tab->pattern = 0xffffffff;
2778a1867f4SWojciech Macek 			printf("ERROR: skipping instruction op %s\n",
2788a1867f4SWojciech Macek 			    tab->name);
2798a1867f4SWojciech Macek 		}
2808a1867f4SWojciech Macek 
2818a1867f4SWojciech Macek 		tab++;
2828a1867f4SWojciech Macek 	}
2838a1867f4SWojciech Macek }
2848a1867f4SWojciech Macek 
2858a1867f4SWojciech Macek static int
2868a1867f4SWojciech Macek arm64_disasm_read_token(struct arm64_insn *insn, u_int opcode,
2878a1867f4SWojciech Macek     const char *token, int *val)
2888a1867f4SWojciech Macek {
2898a1867f4SWojciech Macek 	int i;
2908a1867f4SWojciech Macek 
2918a1867f4SWojciech Macek 	for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) {
2928a1867f4SWojciech Macek 		if (strcmp(insn->tokens[i].name, token) == 0) {
2938a1867f4SWojciech Macek 			*val = (opcode >> insn->tokens[i].pos &
2948a1867f4SWojciech Macek 			    ((1 << insn->tokens[i].len) - 1));
2958a1867f4SWojciech Macek 			return (0);
2968a1867f4SWojciech Macek 		}
2978a1867f4SWojciech Macek 	}
2988a1867f4SWojciech Macek 
2998a1867f4SWojciech Macek 	return (EINVAL);
3008a1867f4SWojciech Macek }
3018a1867f4SWojciech Macek 
302c7fc655fSWojciech Macek static int
303c7fc655fSWojciech Macek arm64_disasm_read_token_sign_ext(struct arm64_insn *insn, u_int opcode,
304c7fc655fSWojciech Macek     const char *token, int *val)
305c7fc655fSWojciech Macek {
306c7fc655fSWojciech Macek 	int i;
307c7fc655fSWojciech Macek 	int msk;
308c7fc655fSWojciech Macek 
309c7fc655fSWojciech Macek 	for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) {
310c7fc655fSWojciech Macek 		if (strcmp(insn->tokens[i].name, token) == 0) {
311c7fc655fSWojciech Macek 			msk = (1 << insn->tokens[i].len) - 1;
312c7fc655fSWojciech Macek 			*val = ((opcode >> insn->tokens[i].pos) & msk);
313c7fc655fSWojciech Macek 
314c7fc655fSWojciech Macek 			/* If last bit is 1, sign-extend the value */
315c7fc655fSWojciech Macek 			if (*val & (1 << (insn->tokens[i].len - 1)))
316c7fc655fSWojciech Macek 				*val |= ~msk;
317c7fc655fSWojciech Macek 
318c7fc655fSWojciech Macek 			return (0);
319c7fc655fSWojciech Macek 		}
320c7fc655fSWojciech Macek 	}
321c7fc655fSWojciech Macek 
322c7fc655fSWojciech Macek 	return (EINVAL);
323c7fc655fSWojciech Macek }
324c7fc655fSWojciech Macek 
3258a1867f4SWojciech Macek static const char *
3268a1867f4SWojciech Macek arm64_reg(int b64, int num)
3278a1867f4SWojciech Macek {
3288a1867f4SWojciech Macek 
3298a1867f4SWojciech Macek 	if (b64 != 0)
3308a1867f4SWojciech Macek 		return (x_reg[num]);
3318a1867f4SWojciech Macek 
3328a1867f4SWojciech Macek 	return (w_reg[num]);
3338a1867f4SWojciech Macek }
3348a1867f4SWojciech Macek 
3358a1867f4SWojciech Macek vm_offset_t
3368a1867f4SWojciech Macek disasm(const struct disasm_interface *di, vm_offset_t loc, int altfmt)
3378a1867f4SWojciech Macek {
3388a1867f4SWojciech Macek 	struct arm64_insn *i_ptr = arm64_i;
3398a1867f4SWojciech Macek 	uint32_t insn;
3408a1867f4SWojciech Macek 	int matchp;
3418a1867f4SWojciech Macek 	int ret;
342c7fc655fSWojciech Macek 	int shift, rm, rt, rd, rn, imm, sf, idx, option, scale, amount;
343c7fc655fSWojciech Macek 	int sign_ext;
3448a1867f4SWojciech Macek 	int rm_absent;
345c7fc655fSWojciech Macek 	/* Indicate if immediate should be outside or inside brackets */
346c7fc655fSWojciech Macek 	int inside;
347c7fc655fSWojciech Macek 	/* Print exclamation mark if pre-incremented */
348c7fc655fSWojciech Macek 	int pre;
3498a1867f4SWojciech Macek 
3508a1867f4SWojciech Macek 	/* Initialize defaults, all are 0 except SF indicating 64bit access */
351c7fc655fSWojciech Macek 	shift = rd = rm = rn = imm = idx = option = amount = scale = 0;
352c7fc655fSWojciech Macek 	sign_ext = 0;
3538a1867f4SWojciech Macek 	sf = 1;
3548a1867f4SWojciech Macek 
3558a1867f4SWojciech Macek 	matchp = 0;
3568a1867f4SWojciech Macek 	insn = di->di_readword(loc);
3578a1867f4SWojciech Macek 	while (i_ptr->name) {
3588a1867f4SWojciech Macek 		/* If mask is 0 then the parser was not initialized yet */
3598a1867f4SWojciech Macek 		if ((i_ptr->mask != 0) &&
3608a1867f4SWojciech Macek 		    ((insn & i_ptr->mask) ==  i_ptr->pattern)) {
3618a1867f4SWojciech Macek 			matchp = 1;
3628a1867f4SWojciech Macek 			break;
3638a1867f4SWojciech Macek 		}
3648a1867f4SWojciech Macek 		i_ptr++;
3658a1867f4SWojciech Macek 	}
3668a1867f4SWojciech Macek 	if (matchp == 0)
3678a1867f4SWojciech Macek 		goto undefined;
3688a1867f4SWojciech Macek 
369c7fc655fSWojciech Macek 	/* Global options */
370c7fc655fSWojciech Macek 	if (i_ptr->special_ops & OP_SF32)
371c7fc655fSWojciech Macek 		sf = 0;
372c7fc655fSWojciech Macek 
373c7fc655fSWojciech Macek 	/* Global optional tokens */
374c7fc655fSWojciech Macek 	arm64_disasm_read_token(i_ptr, insn, "SF", &sf);
375c7fc655fSWojciech Macek 	if (i_ptr->special_ops & OP_SF_INV)
376c7fc655fSWojciech Macek 		sf = 1 - sf;
377c7fc655fSWojciech Macek 	if (arm64_disasm_read_token(i_ptr, insn, "SIGN", &sign_ext) == 0)
378c7fc655fSWojciech Macek 		sign_ext = 1 - sign_ext;
379c7fc655fSWojciech Macek 	if (i_ptr->special_ops & OP_SIGN_EXT)
380c7fc655fSWojciech Macek 		sign_ext = 1;
381c7fc655fSWojciech Macek 	if (sign_ext != 0)
382c7fc655fSWojciech Macek 		arm64_disasm_read_token_sign_ext(i_ptr, insn, "IMM", &imm);
383c7fc655fSWojciech Macek 	else
384c7fc655fSWojciech Macek 		arm64_disasm_read_token(i_ptr, insn, "IMM", &imm);
385c7fc655fSWojciech Macek 	if (i_ptr->special_ops & OP_MULT_4)
386c7fc655fSWojciech Macek 		imm <<= 2;
387c7fc655fSWojciech Macek 
388c7fc655fSWojciech Macek 	/* Print opcode by type */
3898a1867f4SWojciech Macek 	switch (i_ptr->type) {
3908a1867f4SWojciech Macek 	case TYPE_01:
3918a1867f4SWojciech Macek 		/* OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64
3928a1867f4SWojciech Macek 		   OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64 */
3938a1867f4SWojciech Macek 
3948a1867f4SWojciech Macek 		/* Mandatory tokens */
395c7fc655fSWojciech Macek 		ret = arm64_disasm_read_token(i_ptr, insn, "RD", &rd);
3968a1867f4SWojciech Macek 		ret |= arm64_disasm_read_token(i_ptr, insn, "RN", &rn);
3978a1867f4SWojciech Macek 		if (ret != 0) {
3988a1867f4SWojciech Macek 			printf("ERROR: Missing mandatory token for op %s type %d\n",
3998a1867f4SWojciech Macek 			    i_ptr->name, i_ptr->type);
4008a1867f4SWojciech Macek 			goto undefined;
4018a1867f4SWojciech Macek 		}
4028a1867f4SWojciech Macek 
4038a1867f4SWojciech Macek 		/* Optional tokens */
4048a1867f4SWojciech Macek 		arm64_disasm_read_token(i_ptr, insn, "SHIFT", &shift);
4058a1867f4SWojciech Macek 		rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm);
4068a1867f4SWojciech Macek 
4078a1867f4SWojciech Macek 		di->di_printf("%s\t%s, %s", i_ptr->name, arm64_reg(sf, rd),
4088a1867f4SWojciech Macek 		    arm64_reg(sf, rn));
4098a1867f4SWojciech Macek 
4108a1867f4SWojciech Macek 		/* If RM is present use it, otherwise use immediate notation */
4118a1867f4SWojciech Macek 		if (rm_absent == 0) {
4128a1867f4SWojciech Macek 			di->di_printf(", %s", arm64_reg(sf, rm));
4138a1867f4SWojciech Macek 			if (imm != 0)
4148a1867f4SWojciech Macek 				di->di_printf(", %s #%d", shift_2[shift], imm);
4158a1867f4SWojciech Macek 		} else {
4168a1867f4SWojciech Macek 			if (imm != 0 || shift != 0)
4178a1867f4SWojciech Macek 				di->di_printf(", #0x%x", imm);
4188a1867f4SWojciech Macek 			if (shift != 0)
4198a1867f4SWojciech Macek 				di->di_printf(" LSL #12");
4208a1867f4SWojciech Macek 		}
4218a1867f4SWojciech Macek 		break;
422c7fc655fSWojciech Macek 	case TYPE_02:
423c7fc655fSWojciech Macek 		/* OP <RT>, [<RN>, #<imm>]{!}] SF32/64
424c7fc655fSWojciech Macek 		   OP <RT>, [<RN>], #<imm>{!} SF32/64
425c7fc655fSWojciech Macek 		   OP <RT>, <RN>, <RM> {, EXTEND AMOUNT } */
426c7fc655fSWojciech Macek 
427c7fc655fSWojciech Macek 		/* Mandatory tokens */
428c7fc655fSWojciech Macek 		ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt);
429c7fc655fSWojciech Macek 		ret |= arm64_disasm_read_token(i_ptr, insn, "RN", &rn);
430c7fc655fSWojciech Macek 		if (ret != 0) {
431c7fc655fSWojciech Macek 			printf("ERROR: Missing mandatory token for op %s type %d\n",
432c7fc655fSWojciech Macek 			    i_ptr->name, i_ptr->type);
433c7fc655fSWojciech Macek 			goto undefined;
434c7fc655fSWojciech Macek 		}
435c7fc655fSWojciech Macek 
436c7fc655fSWojciech Macek 		/* Optional tokens */
437c7fc655fSWojciech Macek 		arm64_disasm_read_token(i_ptr, insn, "OPTION", &option);
438c7fc655fSWojciech Macek 		arm64_disasm_read_token(i_ptr, insn, "SCALE", &scale);
439c7fc655fSWojciech Macek 		rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm);
440c7fc655fSWojciech Macek 
441c7fc655fSWojciech Macek 		if (rm_absent) {
442c7fc655fSWojciech Macek 			/*
443c7fc655fSWojciech Macek 			 * In unsigned operation, shift immediate value
444c7fc655fSWojciech Macek 			 * and reset options to default.
445c7fc655fSWojciech Macek 			 */
446c7fc655fSWojciech Macek 			if (sign_ext == 0) {
447c7fc655fSWojciech Macek 				imm = imm << ((insn >> ARM_INSN_SIZE_OFFSET) &
448c7fc655fSWojciech Macek 				    ARM_INSN_SIZE_MASK);
449c7fc655fSWojciech Macek 				option = 0;
450c7fc655fSWojciech Macek 			}
451c7fc655fSWojciech Macek 			switch (option) {
452c7fc655fSWojciech Macek 			case 0x0:
453c7fc655fSWojciech Macek 				pre = 0;
454c7fc655fSWojciech Macek 				inside = 1;
455c7fc655fSWojciech Macek 				break;
456c7fc655fSWojciech Macek 			case 0x1:
457c7fc655fSWojciech Macek 				pre = 0;
458c7fc655fSWojciech Macek 				inside = 0;
459c7fc655fSWojciech Macek 				break;
460c7fc655fSWojciech Macek 			case 0x2:
461c7fc655fSWojciech Macek 			default:
462c7fc655fSWojciech Macek 				pre = 1;
463c7fc655fSWojciech Macek 				inside = 1;
464c7fc655fSWojciech Macek 				break;
465c7fc655fSWojciech Macek 			}
466c7fc655fSWojciech Macek 
467c7fc655fSWojciech Macek 			di->di_printf("%s\t%s, ", i_ptr->name, arm64_reg(sf, rt));
468c7fc655fSWojciech Macek 			if (inside != 0) {
469c7fc655fSWojciech Macek 				di->di_printf("[%s", arm64_reg(1, rn));
470c7fc655fSWojciech Macek 				if (imm != 0)
471c7fc655fSWojciech Macek 					di->di_printf(", #%d", imm);
472c7fc655fSWojciech Macek 				di->di_printf("]");
473c7fc655fSWojciech Macek 			} else {
474c7fc655fSWojciech Macek 				di->di_printf("[%s]", arm64_reg(1, rn));
475c7fc655fSWojciech Macek 				if (imm != 0)
476c7fc655fSWojciech Macek 					di->di_printf(", #%d", imm);
477c7fc655fSWojciech Macek 			}
478c7fc655fSWojciech Macek 			if (pre != 0)
479c7fc655fSWojciech Macek 				di->di_printf("!");
480c7fc655fSWojciech Macek 		} else {
481c7fc655fSWojciech Macek 			/* Last bit of option field determines 32/64 bit offset */
482c7fc655fSWojciech Macek 			di->di_printf("%s\t%s, [%s, %s", i_ptr->name,
483c7fc655fSWojciech Macek 			    arm64_reg(sf, rt), arm64_reg(1, rn),
484c7fc655fSWojciech Macek 			    arm64_reg(option & 1, rm));
485c7fc655fSWojciech Macek 
486c7fc655fSWojciech Macek 			/* Calculate amount, it's op(31:30) */
487c7fc655fSWojciech Macek 			amount = (insn >> ARM_INSN_SIZE_OFFSET) &
488c7fc655fSWojciech Macek 			    ARM_INSN_SIZE_MASK;
489c7fc655fSWojciech Macek 
490c7fc655fSWojciech Macek 			switch (option) {
491c7fc655fSWojciech Macek 			case 0x2:
492c7fc655fSWojciech Macek 				di->di_printf(", uxtw #%d", amount);
493c7fc655fSWojciech Macek 				break;
494c7fc655fSWojciech Macek 			case 0x3:
495c7fc655fSWojciech Macek 				if (scale != 0)
496c7fc655fSWojciech Macek 					di->di_printf(", lsl #%d", amount);
497c7fc655fSWojciech Macek 				break;
498c7fc655fSWojciech Macek 			case 0x6:
499c7fc655fSWojciech Macek 				di->di_printf(", sxtw #%d", amount);
500c7fc655fSWojciech Macek 				break;
501c7fc655fSWojciech Macek 			case 0x7:
502c7fc655fSWojciech Macek 				di->di_printf(", sxts #%d", amount);
503c7fc655fSWojciech Macek 				break;
504c7fc655fSWojciech Macek 			default:
505c7fc655fSWojciech Macek 				di->di_printf(", RSVD");
506c7fc655fSWojciech Macek 				break;
507c7fc655fSWojciech Macek 			}
508c7fc655fSWojciech Macek 			di->di_printf("]");
509c7fc655fSWojciech Macek 		}
510c7fc655fSWojciech Macek 
511c7fc655fSWojciech Macek 		break;
512c7fc655fSWojciech Macek 
513c7fc655fSWojciech Macek 	case TYPE_03:
514c7fc655fSWojciech Macek 		/* OP <RT>, #imm SF32/64 */
515c7fc655fSWojciech Macek 
516c7fc655fSWojciech Macek 		/* Mandatory tokens */
517c7fc655fSWojciech Macek 		ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt);
518c7fc655fSWojciech Macek 		if (ret != 0) {
519c7fc655fSWojciech Macek 			printf("ERROR: Missing mandatory token for op %s type %d\n",
520c7fc655fSWojciech Macek 			    i_ptr->name, i_ptr->type);
521c7fc655fSWojciech Macek 			goto undefined;
522c7fc655fSWojciech Macek 		}
523c7fc655fSWojciech Macek 
524c7fc655fSWojciech Macek 		di->di_printf("%s\t%s, ", i_ptr->name, arm64_reg(sf, rt));
525c7fc655fSWojciech Macek 		if (i_ptr->special_ops & OP_LITERAL)
526c7fc655fSWojciech Macek 			di->di_printf("0x%lx", loc + imm);
527c7fc655fSWojciech Macek 		else
528c7fc655fSWojciech Macek 			di->di_printf("#%d", imm);
529c7fc655fSWojciech Macek 
530c7fc655fSWojciech Macek 		break;
5318a1867f4SWojciech Macek 	default:
5328a1867f4SWojciech Macek 		goto undefined;
5338a1867f4SWojciech Macek 	}
5348a1867f4SWojciech Macek 
5358a1867f4SWojciech Macek 	di->di_printf("\n");
5368a1867f4SWojciech Macek 	return(loc + INSN_SIZE);
5378a1867f4SWojciech Macek 
5388a1867f4SWojciech Macek undefined:
5398a1867f4SWojciech Macek 	di->di_printf("undefined\t%08x\n", insn);
5408a1867f4SWojciech Macek 	return(loc + INSN_SIZE);
5418a1867f4SWojciech Macek }
5428a1867f4SWojciech Macek 
5438a1867f4SWojciech Macek /* Parse format strings at the very beginning */
5448a1867f4SWojciech Macek SYSINIT(arm64_disasm_generate_masks, SI_SUB_DDB_SERVICES,
5458a1867f4SWojciech Macek     SI_ORDER_FIRST, arm64_disasm_generate_masks, arm64_i);
546