xref: /freebsd/sys/arm64/arm64/disassem.c (revision 7edb7adf)
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 
325b61ad4bSMykola Hohsadze #include <sys/param.h>
338a1867f4SWojciech Macek #include <sys/systm.h>
345b61ad4bSMykola Hohsadze 
358a1867f4SWojciech Macek #include <machine/armreg.h>
365b61ad4bSMykola Hohsadze #include <machine/disassem.h>
375b61ad4bSMykola Hohsadze 
388a1867f4SWojciech Macek #include <ddb/ddb.h>
398a1867f4SWojciech Macek 
408a1867f4SWojciech Macek #define	ARM64_MAX_TOKEN_LEN	8
418a1867f4SWojciech Macek #define	ARM64_MAX_TOKEN_CNT	10
428a1867f4SWojciech Macek 
43c7fc655fSWojciech Macek #define	ARM_INSN_SIZE_OFFSET	30
44c7fc655fSWojciech Macek #define	ARM_INSN_SIZE_MASK	0x3
45c7fc655fSWojciech Macek 
46c7fc655fSWojciech Macek /* Special options for instruction printing */
47c7fc655fSWojciech Macek #define	OP_SIGN_EXT	(1UL << 0)	/* Sign-extend immediate value */
48c7fc655fSWojciech Macek #define	OP_LITERAL	(1UL << 1)	/* Use literal (memory offset) */
49c7fc655fSWojciech Macek #define	OP_MULT_4	(1UL << 2)	/* Multiply immediate by 4 */
50c7fc655fSWojciech Macek #define	OP_SF32		(1UL << 3)	/* Force 32-bit access */
51c7fc655fSWojciech Macek #define	OP_SF_INV	(1UL << 6)	/* SF is inverted (1 means 32 bit access) */
52c7fc655fSWojciech Macek 
538a1867f4SWojciech Macek static const char *w_reg[] = {
548a1867f4SWojciech Macek 	"w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7",
558a1867f4SWojciech Macek 	"w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15",
568a1867f4SWojciech Macek 	"w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23",
578a1867f4SWojciech Macek 	"w24", "w25", "w26", "w27", "w28", "w29", "w30", "wSP",
588a1867f4SWojciech Macek };
598a1867f4SWojciech Macek 
608a1867f4SWojciech Macek static const char *x_reg[] = {
618a1867f4SWojciech Macek 	"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
628a1867f4SWojciech Macek 	"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
638a1867f4SWojciech Macek 	"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
648a1867f4SWojciech Macek 	"x24", "x25", "x26", "x27", "x28", "x29", "LR", "SP",
658a1867f4SWojciech Macek };
668a1867f4SWojciech Macek 
678a1867f4SWojciech Macek static const char *shift_2[] = {
688a1867f4SWojciech Macek 	"LSL", "LSR", "ASR", "RSV"
698a1867f4SWojciech Macek };
708a1867f4SWojciech Macek 
718a1867f4SWojciech Macek /*
728a1867f4SWojciech Macek  * Structure representing single token (operand) inside instruction.
738a1867f4SWojciech Macek  * name   - name of operand
748a1867f4SWojciech Macek  * pos    - position within the instruction (in bits)
758a1867f4SWojciech Macek  * len    - operand length (in bits)
768a1867f4SWojciech Macek  */
778a1867f4SWojciech Macek struct arm64_insn_token {
788a1867f4SWojciech Macek 	char name[ARM64_MAX_TOKEN_LEN];
798a1867f4SWojciech Macek 	int pos;
808a1867f4SWojciech Macek 	int len;
818a1867f4SWojciech Macek };
828a1867f4SWojciech Macek 
838a1867f4SWojciech Macek /*
848a1867f4SWojciech Macek  * Define generic types for instruction printing.
858a1867f4SWojciech Macek  */
868a1867f4SWojciech Macek enum arm64_format_type {
875b61ad4bSMykola Hohsadze 	/*
885b61ad4bSMykola Hohsadze 	 * OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #imm} SF32/64
895b61ad4bSMykola Hohsadze 	 * OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64
905b61ad4bSMykola Hohsadze 	 */
915b61ad4bSMykola Hohsadze 	TYPE_01,
925b61ad4bSMykola Hohsadze 
935b61ad4bSMykola Hohsadze 	/*
945b61ad4bSMykola Hohsadze 	 * OP <RT>, [<RN>, #<imm>]{!} SF32/64
955b61ad4bSMykola Hohsadze 	 * OP <RT>, [<RN>], #<imm>{!} SF32/64
965b61ad4bSMykola Hohsadze 	 * OP <RT>, <RN>, <RM> {, EXTEND AMOUNT }
975b61ad4bSMykola Hohsadze 	 */
985b61ad4bSMykola Hohsadze 	TYPE_02,
995b61ad4bSMykola Hohsadze 
1005b61ad4bSMykola Hohsadze 	/* OP <RT>, #imm SF32/64 */
1015b61ad4bSMykola Hohsadze 	TYPE_03,
1028a1867f4SWojciech Macek };
1038a1867f4SWojciech Macek 
1048a1867f4SWojciech Macek /*
1058a1867f4SWojciech Macek  * Structure representing single parsed instruction format.
1068a1867f4SWojciech Macek  * name   - opcode name
1078a1867f4SWojciech Macek  * format - opcode format in a human-readable way
1088a1867f4SWojciech Macek  * type   - syntax type for printing
1098a1867f4SWojciech Macek  * special_ops  - special options passed to a printer (if any)
1108a1867f4SWojciech Macek  * mask   - bitmask for instruction matching
1118a1867f4SWojciech Macek  * pattern      - pattern to look for
1128a1867f4SWojciech Macek  * tokens - array of tokens (operands) inside instruction
1138a1867f4SWojciech Macek  */
1148a1867f4SWojciech Macek struct arm64_insn {
1158a1867f4SWojciech Macek 	char *name;
1168a1867f4SWojciech Macek 	char *format;
1178a1867f4SWojciech Macek 	enum arm64_format_type type;
1188a1867f4SWojciech Macek 	uint64_t special_ops;
1198a1867f4SWojciech Macek 	uint32_t mask;
1208a1867f4SWojciech Macek 	uint32_t pattern;
1218a1867f4SWojciech Macek 	struct arm64_insn_token tokens[ARM64_MAX_TOKEN_CNT];
1228a1867f4SWojciech Macek };
1238a1867f4SWojciech Macek 
1248a1867f4SWojciech Macek /*
1258a1867f4SWojciech Macek  * Specify instruction opcode format in a human-readable way. Use notation
1268a1867f4SWojciech Macek  * obtained from ARM Architecture Reference Manual for ARMv8-A.
1278a1867f4SWojciech Macek  *
1288a1867f4SWojciech Macek  * Format string description:
1298a1867f4SWojciech Macek  *  Each group must be separated by "|". Group made of 0/1 is used to
1308a1867f4SWojciech Macek  *  generate mask and pattern for instruction matching. Groups containing
1318a1867f4SWojciech Macek  *  an operand token (in format NAME(length_bits)) are used to retrieve any
1328a1867f4SWojciech Macek  *  operand data from the instruction. Names here must be meaningful
1338a1867f4SWojciech Macek  *  and match the one described in the Manual.
1348a1867f4SWojciech Macek  *
1358a1867f4SWojciech Macek  * Token description:
1368a1867f4SWojciech Macek  * SF     - "0" represents 32-bit access, "1" represents 64-bit access
1378a1867f4SWojciech Macek  * SHIFT  - type of shift (instruction dependent)
1388a1867f4SWojciech Macek  * IMM    - immediate value
1398a1867f4SWojciech Macek  * Rx     - register number
140c7fc655fSWojciech Macek  * OPTION - command specific options
141c7fc655fSWojciech Macek  * SCALE  - scaling of immediate value
1428a1867f4SWojciech Macek  */
1438a1867f4SWojciech Macek static struct arm64_insn arm64_i[] = {
144c7fc655fSWojciech Macek 	{ "add", "SF(1)|0001011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
145c7fc655fSWojciech Macek 	    TYPE_01, 0 },
146c7fc655fSWojciech Macek 	{ "mov", "SF(1)|001000100000000000000|RN(5)|RD(5)",
147c7fc655fSWojciech Macek 	    TYPE_01, 0 },
148c7fc655fSWojciech Macek 	{ "add", "SF(1)|0010001|SHIFT(2)|IMM(12)|RN(5)|RD(5)",
149c7fc655fSWojciech Macek 	    TYPE_01, 0 },
150c7fc655fSWojciech Macek 	{ "ldr", "1|SF(1)|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)",
151c7fc655fSWojciech Macek 	    TYPE_02, OP_SIGN_EXT },		/* ldr immediate post/pre index */
152c7fc655fSWojciech Macek 	{ "ldr", "1|SF(1)|11100101|IMM(12)|RN(5)|RT(5)",
153c7fc655fSWojciech Macek 	    TYPE_02, 0 },			/* ldr immediate unsigned */
154c7fc655fSWojciech Macek 	{ "ldr", "1|SF(1)|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
155c7fc655fSWojciech Macek 	    TYPE_02, 0 },			/* ldr register */
156c7fc655fSWojciech Macek 	{ "ldr", "0|SF(1)|011000|IMM(19)|RT(5)",
157c7fc655fSWojciech Macek 	    TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 },	/* ldr literal */
158c7fc655fSWojciech Macek 	{ "ldrb", "00|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)",
159c7fc655fSWojciech Macek 	    TYPE_02, OP_SIGN_EXT | OP_SF32 },	/* ldrb immediate post/pre index */
160c7fc655fSWojciech Macek 	{ "ldrb", "00|11100101|IMM(12)|RN(5)|RT(5)",
161c7fc655fSWojciech Macek 	    TYPE_02, OP_SF32 },			/* ldrb immediate unsigned */
162c7fc655fSWojciech Macek 	{ "ldrb", "00|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
163c7fc655fSWojciech Macek 	    TYPE_02, OP_SF32  },		/* ldrb register */
164c7fc655fSWojciech Macek 	{ "ldrh", "01|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)", TYPE_02,
165c7fc655fSWojciech Macek 	    OP_SIGN_EXT | OP_SF32 },		/* ldrh immediate post/pre index */
166c7fc655fSWojciech Macek 	{ "ldrh", "01|11100101|IMM(12)|RN(5)|RT(5)",
167c7fc655fSWojciech Macek 	    TYPE_02, OP_SF32 },			/* ldrh immediate unsigned */
168c7fc655fSWojciech Macek 	{ "ldrh", "01|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
169c7fc655fSWojciech Macek 	    TYPE_02, OP_SF32 },			/* ldrh register */
170c7fc655fSWojciech Macek 	{ "ldrsb", "001110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)",
171c7fc655fSWojciech Macek 	    TYPE_02, OP_SIGN_EXT | OP_SF_INV },	/* ldrsb immediate post/pre index */
172c7fc655fSWojciech Macek 	{ "ldrsb", "001110011|SF(1)|IMM(12)|RN(5)|RT(5)",\
173c7fc655fSWojciech Macek 	    TYPE_02, OP_SF_INV},		/* ldrsb immediate unsigned */
174c7fc655fSWojciech Macek 	{ "ldrsb", "001110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
175c7fc655fSWojciech Macek 	    TYPE_02,  OP_SF_INV },		/* ldrsb register */
176c7fc655fSWojciech Macek 	{ "ldrsh", "011110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)",
177c7fc655fSWojciech Macek 	    TYPE_02, OP_SIGN_EXT | OP_SF_INV },	/* ldrsh immediate post/pre index */
178c7fc655fSWojciech Macek 	{ "ldrsh", "011110011|SF(1)|IMM(12)|RN(5)|RT(5)",
179c7fc655fSWojciech Macek 	    TYPE_02, OP_SF_INV},		/* ldrsh immediate unsigned */
180c7fc655fSWojciech Macek 	{ "ldrsh", "011110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
181c7fc655fSWojciech Macek 	    TYPE_02, OP_SF_INV },		/* ldrsh register */
182c7fc655fSWojciech Macek 	{ "ldrsw", "10111000100|IMM(9)|OPTION(2)|RN(5)|RT(5)",
183c7fc655fSWojciech Macek 	    TYPE_02, OP_SIGN_EXT },		/* ldrsw immediate post/pre index */
184c7fc655fSWojciech Macek 	{ "ldrsw", "1011100110|IMM(12)|RN(5)|RT(5)",
185c7fc655fSWojciech Macek 	    TYPE_02, 0 },			/* ldrsw immediate unsigned */
186c7fc655fSWojciech Macek 	{ "ldrsw", "10111000101|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
187c7fc655fSWojciech Macek 	    TYPE_02, 0 },			/* ldrsw register */
188c7fc655fSWojciech Macek 	{ "ldrsw", "10011000|IMM(19)|RT(5)",
189c7fc655fSWojciech Macek 	    TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 },	/* ldr literal */
1907edb7adfSMykola Hohsadze         { "str", "1|SF(1)|111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)",
1917edb7adfSMykola Hohsadze             TYPE_02, OP_SIGN_EXT }, 		/* str immediate post/pre index */
1927edb7adfSMykola Hohsadze         { "str", "1|SF(1)|11100100|IMM(12)|RN(5)|RT(5)",
1937edb7adfSMykola Hohsadze             TYPE_02, 0 }, 		        /* str immediate unsigned */
1947edb7adfSMykola Hohsadze         { "str", "1|SF(1)|111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
1957edb7adfSMykola Hohsadze             TYPE_02, 0 }, 			/* str register */
1967edb7adfSMykola Hohsadze         { "strb", "00111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)",
1977edb7adfSMykola Hohsadze             TYPE_02, OP_SIGN_EXT | OP_SF32 },   /* strb immediate post/pre index */
1987edb7adfSMykola Hohsadze         { "strb", "0011100100|IMM(12)|RN(5)|RT(5)",
1997edb7adfSMykola Hohsadze             TYPE_02, OP_SF32 },                 /* strb immediate unsigned */
2007edb7adfSMykola Hohsadze         { "strb", "00111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
2017edb7adfSMykola Hohsadze             TYPE_02, OP_SF32 }, 		/* strb register */
2027edb7adfSMykola Hohsadze         { "strh", "01111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)",
2037edb7adfSMykola Hohsadze 	    TYPE_02, OP_SF32 | OP_SIGN_EXT }, 	/* strh immediate post/pre index */
2047edb7adfSMykola Hohsadze         { "strh", "0111100100|IMM(12)|RN(5)|RT(5)",
2057edb7adfSMykola Hohsadze 	    TYPE_02, OP_SF32 }, 		/* immediate unsigned */
2067edb7adfSMykola Hohsadze         { "strh", "01111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
2077edb7adfSMykola Hohsadze             TYPE_02, OP_SF32 }, 		/* strh register */
2088a1867f4SWojciech Macek 	{ NULL, NULL }
2098a1867f4SWojciech Macek };
2108a1867f4SWojciech Macek 
2118a1867f4SWojciech Macek static void
2128a1867f4SWojciech Macek arm64_disasm_generate_masks(struct arm64_insn *tab)
2138a1867f4SWojciech Macek {
2148a1867f4SWojciech Macek 	uint32_t mask, val;
2158a1867f4SWojciech Macek 	int a, i;
2168a1867f4SWojciech Macek 	int len, ret;
2178a1867f4SWojciech Macek 	int token = 0;
2188a1867f4SWojciech Macek 	char *format;
2198a1867f4SWojciech Macek 	int error;
2208a1867f4SWojciech Macek 
2218a1867f4SWojciech Macek 	while (tab->name != NULL) {
2228a1867f4SWojciech Macek 		mask = 0;
2238a1867f4SWojciech Macek 		val = 0;
2248a1867f4SWojciech Macek 		format = tab->format;
2258a1867f4SWojciech Macek 		token = 0;
2268a1867f4SWojciech Macek 		error = 0;
2278a1867f4SWojciech Macek 
2288a1867f4SWojciech Macek 		/*
2298a1867f4SWojciech Macek 		 * For each entry analyze format strings from the
2308a1867f4SWojciech Macek 		 * left (i.e. from the MSB).
2318a1867f4SWojciech Macek 		 */
2328a1867f4SWojciech Macek 		a = (INSN_SIZE * NBBY) - 1;
2338a1867f4SWojciech Macek 		while (*format != '\0' && (a >= 0)) {
2348a1867f4SWojciech Macek 			switch (*format) {
2358a1867f4SWojciech Macek 			case '0':
2368a1867f4SWojciech Macek 				/* Bit is 0, add to mask and pattern */
2378a1867f4SWojciech Macek 				mask |= (1 << a);
2388a1867f4SWojciech Macek 				a--;
2398a1867f4SWojciech Macek 				format++;
2408a1867f4SWojciech Macek 				break;
2418a1867f4SWojciech Macek 			case '1':
2428a1867f4SWojciech Macek 				/* Bit is 1, add to mask and pattern */
2438a1867f4SWojciech Macek 				mask |= (1 << a);
2448a1867f4SWojciech Macek 				val |= (1 << a);
2458a1867f4SWojciech Macek 				a--;
2468a1867f4SWojciech Macek 				format++;
2478a1867f4SWojciech Macek 				break;
2488a1867f4SWojciech Macek 			case '|':
2498a1867f4SWojciech Macek 				/* skip */
2508a1867f4SWojciech Macek 				format++;
2518a1867f4SWojciech Macek 				break;
2528a1867f4SWojciech Macek 			default:
2538a1867f4SWojciech Macek 				/* Token found, copy the name */
2548a1867f4SWojciech Macek 				memset(tab->tokens[token].name, 0,
2558a1867f4SWojciech Macek 				    sizeof(tab->tokens[token].name));
2568a1867f4SWojciech Macek 				i = 0;
2578a1867f4SWojciech Macek 				while (*format != '(') {
2588a1867f4SWojciech Macek 					tab->tokens[token].name[i] = *format;
2598a1867f4SWojciech Macek 					i++;
2608a1867f4SWojciech Macek 					format++;
2618a1867f4SWojciech Macek 					if (i >= ARM64_MAX_TOKEN_LEN) {
2625b61ad4bSMykola Hohsadze 						printf("ERROR: "
2635b61ad4bSMykola Hohsadze 						    "token too long in op %s\n",
2648a1867f4SWojciech Macek 						    tab->name);
2658a1867f4SWojciech Macek 						error = 1;
2668a1867f4SWojciech Macek 						break;
2678a1867f4SWojciech Macek 					}
2688a1867f4SWojciech Macek 				}
2698a1867f4SWojciech Macek 				if (error != 0)
2708a1867f4SWojciech Macek 					break;
2718a1867f4SWojciech Macek 
2728a1867f4SWojciech Macek 				/* Read the length value */
2738a1867f4SWojciech Macek 				ret = sscanf(format, "(%d)", &len);
2748a1867f4SWojciech Macek 				if (ret == 1) {
2758a1867f4SWojciech Macek 					if (token >= ARM64_MAX_TOKEN_CNT) {
2765b61ad4bSMykola Hohsadze 						printf("ERROR: "
2775b61ad4bSMykola Hohsadze 						    "too many tokens in op %s\n",
2788a1867f4SWojciech Macek 						    tab->name);
2798a1867f4SWojciech Macek 						error = 1;
2808a1867f4SWojciech Macek 						break;
2818a1867f4SWojciech Macek 					}
2828a1867f4SWojciech Macek 
2838a1867f4SWojciech Macek 					a -= len;
2848a1867f4SWojciech Macek 					tab->tokens[token].pos = a + 1;
2858a1867f4SWojciech Macek 					tab->tokens[token].len = len;
2868a1867f4SWojciech Macek 					token++;
2878a1867f4SWojciech Macek 				}
2888a1867f4SWojciech Macek 
2898a1867f4SWojciech Macek 				/* Skip to the end of the token */
2908a1867f4SWojciech Macek 				while (*format != 0 && *format != '|')
2918a1867f4SWojciech Macek 					format++;
2928a1867f4SWojciech Macek 			}
2938a1867f4SWojciech Macek 		}
2948a1867f4SWojciech Macek 
2958a1867f4SWojciech Macek 		/* Write mask and pattern to the instruction array */
2968a1867f4SWojciech Macek 		tab->mask = mask;
2978a1867f4SWojciech Macek 		tab->pattern = val;
2988a1867f4SWojciech Macek 
2998a1867f4SWojciech Macek 		/*
3008a1867f4SWojciech Macek 		 * If we got here, format string must be parsed and "a"
3018a1867f4SWojciech Macek 		 * should point to -1. If it's not, wrong number of bits
3028a1867f4SWojciech Macek 		 * in format string. Mark this as invalid and prevent
3038a1867f4SWojciech Macek 		 * from being matched.
3048a1867f4SWojciech Macek 		 */
3058a1867f4SWojciech Macek 		if (*format != 0 || (a != -1) || (error != 0)) {
3068a1867f4SWojciech Macek 			tab->mask = 0;
3078a1867f4SWojciech Macek 			tab->pattern = 0xffffffff;
3088a1867f4SWojciech Macek 			printf("ERROR: skipping instruction op %s\n",
3098a1867f4SWojciech Macek 			    tab->name);
3108a1867f4SWojciech Macek 		}
3118a1867f4SWojciech Macek 
3128a1867f4SWojciech Macek 		tab++;
3138a1867f4SWojciech Macek 	}
3148a1867f4SWojciech Macek }
3158a1867f4SWojciech Macek 
3168a1867f4SWojciech Macek static int
3178a1867f4SWojciech Macek arm64_disasm_read_token(struct arm64_insn *insn, u_int opcode,
3188a1867f4SWojciech Macek     const char *token, int *val)
3198a1867f4SWojciech Macek {
3208a1867f4SWojciech Macek 	int i;
3218a1867f4SWojciech Macek 
3228a1867f4SWojciech Macek 	for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) {
3238a1867f4SWojciech Macek 		if (strcmp(insn->tokens[i].name, token) == 0) {
3248a1867f4SWojciech Macek 			*val = (opcode >> insn->tokens[i].pos &
3258a1867f4SWojciech Macek 			    ((1 << insn->tokens[i].len) - 1));
3268a1867f4SWojciech Macek 			return (0);
3278a1867f4SWojciech Macek 		}
3288a1867f4SWojciech Macek 	}
3298a1867f4SWojciech Macek 
3308a1867f4SWojciech Macek 	return (EINVAL);
3318a1867f4SWojciech Macek }
3328a1867f4SWojciech Macek 
333c7fc655fSWojciech Macek static int
334c7fc655fSWojciech Macek arm64_disasm_read_token_sign_ext(struct arm64_insn *insn, u_int opcode,
335c7fc655fSWojciech Macek     const char *token, int *val)
336c7fc655fSWojciech Macek {
337c7fc655fSWojciech Macek 	int i;
338c7fc655fSWojciech Macek 	int msk;
339c7fc655fSWojciech Macek 
340c7fc655fSWojciech Macek 	for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) {
341c7fc655fSWojciech Macek 		if (strcmp(insn->tokens[i].name, token) == 0) {
342c7fc655fSWojciech Macek 			msk = (1 << insn->tokens[i].len) - 1;
343c7fc655fSWojciech Macek 			*val = ((opcode >> insn->tokens[i].pos) & msk);
344c7fc655fSWojciech Macek 
345c7fc655fSWojciech Macek 			/* If last bit is 1, sign-extend the value */
346c7fc655fSWojciech Macek 			if (*val & (1 << (insn->tokens[i].len - 1)))
347c7fc655fSWojciech Macek 				*val |= ~msk;
348c7fc655fSWojciech Macek 
349c7fc655fSWojciech Macek 			return (0);
350c7fc655fSWojciech Macek 		}
351c7fc655fSWojciech Macek 	}
352c7fc655fSWojciech Macek 
353c7fc655fSWojciech Macek 	return (EINVAL);
354c7fc655fSWojciech Macek }
355c7fc655fSWojciech Macek 
3568a1867f4SWojciech Macek static const char *
3578a1867f4SWojciech Macek arm64_reg(int b64, int num)
3588a1867f4SWojciech Macek {
3598a1867f4SWojciech Macek 
3608a1867f4SWojciech Macek 	if (b64 != 0)
3618a1867f4SWojciech Macek 		return (x_reg[num]);
3628a1867f4SWojciech Macek 
3638a1867f4SWojciech Macek 	return (w_reg[num]);
3648a1867f4SWojciech Macek }
3658a1867f4SWojciech Macek 
3668a1867f4SWojciech Macek vm_offset_t
3678a1867f4SWojciech Macek disasm(const struct disasm_interface *di, vm_offset_t loc, int altfmt)
3688a1867f4SWojciech Macek {
3698a1867f4SWojciech Macek 	struct arm64_insn *i_ptr = arm64_i;
3708a1867f4SWojciech Macek 	uint32_t insn;
3718a1867f4SWojciech Macek 	int matchp;
3728a1867f4SWojciech Macek 	int ret;
373c7fc655fSWojciech Macek 	int shift, rm, rt, rd, rn, imm, sf, idx, option, scale, amount;
374c7fc655fSWojciech Macek 	int sign_ext;
3758a1867f4SWojciech Macek 	int rm_absent;
376c7fc655fSWojciech Macek 	/* Indicate if immediate should be outside or inside brackets */
377c7fc655fSWojciech Macek 	int inside;
378c7fc655fSWojciech Macek 	/* Print exclamation mark if pre-incremented */
379c7fc655fSWojciech Macek 	int pre;
3808a1867f4SWojciech Macek 
3818a1867f4SWojciech Macek 	/* Initialize defaults, all are 0 except SF indicating 64bit access */
382c7fc655fSWojciech Macek 	shift = rd = rm = rn = imm = idx = option = amount = scale = 0;
383c7fc655fSWojciech Macek 	sign_ext = 0;
3848a1867f4SWojciech Macek 	sf = 1;
3858a1867f4SWojciech Macek 
3868a1867f4SWojciech Macek 	matchp = 0;
3878a1867f4SWojciech Macek 	insn = di->di_readword(loc);
3888a1867f4SWojciech Macek 	while (i_ptr->name) {
3898a1867f4SWojciech Macek 		/* If mask is 0 then the parser was not initialized yet */
3908a1867f4SWojciech Macek 		if ((i_ptr->mask != 0) &&
3918a1867f4SWojciech Macek 		    ((insn & i_ptr->mask) == i_ptr->pattern)) {
3928a1867f4SWojciech Macek 			matchp = 1;
3938a1867f4SWojciech Macek 			break;
3948a1867f4SWojciech Macek 		}
3958a1867f4SWojciech Macek 		i_ptr++;
3968a1867f4SWojciech Macek 	}
3978a1867f4SWojciech Macek 	if (matchp == 0)
3988a1867f4SWojciech Macek 		goto undefined;
3998a1867f4SWojciech Macek 
400c7fc655fSWojciech Macek 	/* Global options */
401c7fc655fSWojciech Macek 	if (i_ptr->special_ops & OP_SF32)
402c7fc655fSWojciech Macek 		sf = 0;
403c7fc655fSWojciech Macek 
404c7fc655fSWojciech Macek 	/* Global optional tokens */
405c7fc655fSWojciech Macek 	arm64_disasm_read_token(i_ptr, insn, "SF", &sf);
406c7fc655fSWojciech Macek 	if (i_ptr->special_ops & OP_SF_INV)
407c7fc655fSWojciech Macek 		sf = 1 - sf;
408c7fc655fSWojciech Macek 	if (arm64_disasm_read_token(i_ptr, insn, "SIGN", &sign_ext) == 0)
409c7fc655fSWojciech Macek 		sign_ext = 1 - sign_ext;
410c7fc655fSWojciech Macek 	if (i_ptr->special_ops & OP_SIGN_EXT)
411c7fc655fSWojciech Macek 		sign_ext = 1;
412c7fc655fSWojciech Macek 	if (sign_ext != 0)
413c7fc655fSWojciech Macek 		arm64_disasm_read_token_sign_ext(i_ptr, insn, "IMM", &imm);
414c7fc655fSWojciech Macek 	else
415c7fc655fSWojciech Macek 		arm64_disasm_read_token(i_ptr, insn, "IMM", &imm);
416c7fc655fSWojciech Macek 	if (i_ptr->special_ops & OP_MULT_4)
417c7fc655fSWojciech Macek 		imm <<= 2;
418c7fc655fSWojciech Macek 
419c7fc655fSWojciech Macek 	/* Print opcode by type */
4208a1867f4SWojciech Macek 	switch (i_ptr->type) {
4218a1867f4SWojciech Macek 	case TYPE_01:
4225b61ad4bSMykola Hohsadze 		/*
4235b61ad4bSMykola Hohsadze 		 * OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64
4245b61ad4bSMykola Hohsadze 		 * OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64
4255b61ad4bSMykola Hohsadze 		 */
4268a1867f4SWojciech Macek 
4278a1867f4SWojciech Macek 		/* Mandatory tokens */
428c7fc655fSWojciech Macek 		ret = arm64_disasm_read_token(i_ptr, insn, "RD", &rd);
4298a1867f4SWojciech Macek 		ret |= arm64_disasm_read_token(i_ptr, insn, "RN", &rn);
4308a1867f4SWojciech Macek 		if (ret != 0) {
4315b61ad4bSMykola Hohsadze 			printf("ERROR: "
4325b61ad4bSMykola Hohsadze 			    "Missing mandatory token for op %s type %d\n",
4338a1867f4SWojciech Macek 			    i_ptr->name, i_ptr->type);
4348a1867f4SWojciech Macek 			goto undefined;
4358a1867f4SWojciech Macek 		}
4368a1867f4SWojciech Macek 
4378a1867f4SWojciech Macek 		/* Optional tokens */
4388a1867f4SWojciech Macek 		arm64_disasm_read_token(i_ptr, insn, "SHIFT", &shift);
4398a1867f4SWojciech Macek 		rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm);
4408a1867f4SWojciech Macek 
4418a1867f4SWojciech Macek 		di->di_printf("%s\t%s, %s", i_ptr->name, arm64_reg(sf, rd),
4428a1867f4SWojciech Macek 		    arm64_reg(sf, rn));
4438a1867f4SWojciech Macek 
4448a1867f4SWojciech Macek 		/* If RM is present use it, otherwise use immediate notation */
4458a1867f4SWojciech Macek 		if (rm_absent == 0) {
4468a1867f4SWojciech Macek 			di->di_printf(", %s", arm64_reg(sf, rm));
4478a1867f4SWojciech Macek 			if (imm != 0)
4488a1867f4SWojciech Macek 				di->di_printf(", %s #%d", shift_2[shift], imm);
4498a1867f4SWojciech Macek 		} else {
4508a1867f4SWojciech Macek 			if (imm != 0 || shift != 0)
4518a1867f4SWojciech Macek 				di->di_printf(", #0x%x", imm);
4528a1867f4SWojciech Macek 			if (shift != 0)
4538a1867f4SWojciech Macek 				di->di_printf(" LSL #12");
4548a1867f4SWojciech Macek 		}
4558a1867f4SWojciech Macek 		break;
456c7fc655fSWojciech Macek 	case TYPE_02:
4575b61ad4bSMykola Hohsadze 		/*
4585b61ad4bSMykola Hohsadze 		 * OP <RT>, [<RN>, #<imm>]{!}] SF32/64
4595b61ad4bSMykola Hohsadze 		 * OP <RT>, [<RN>], #<imm>{!} SF32/64
4605b61ad4bSMykola Hohsadze 		 * OP <RT>, <RN>, <RM> {, EXTEND AMOUNT }
4615b61ad4bSMykola Hohsadze 		 */
462c7fc655fSWojciech Macek 
463c7fc655fSWojciech Macek 		/* Mandatory tokens */
464c7fc655fSWojciech Macek 		ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt);
465c7fc655fSWojciech Macek 		ret |= arm64_disasm_read_token(i_ptr, insn, "RN", &rn);
466c7fc655fSWojciech Macek 		if (ret != 0) {
4675b61ad4bSMykola Hohsadze 			printf("ERROR: "
4685b61ad4bSMykola Hohsadze 			    "Missing mandatory token for op %s type %d\n",
469c7fc655fSWojciech Macek 			    i_ptr->name, i_ptr->type);
470c7fc655fSWojciech Macek 			goto undefined;
471c7fc655fSWojciech Macek 		}
472c7fc655fSWojciech Macek 
473c7fc655fSWojciech Macek 		/* Optional tokens */
474c7fc655fSWojciech Macek 		arm64_disasm_read_token(i_ptr, insn, "OPTION", &option);
475c7fc655fSWojciech Macek 		arm64_disasm_read_token(i_ptr, insn, "SCALE", &scale);
476c7fc655fSWojciech Macek 		rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm);
477c7fc655fSWojciech Macek 
478c7fc655fSWojciech Macek 		if (rm_absent) {
479c7fc655fSWojciech Macek 			/*
480c7fc655fSWojciech Macek 			 * In unsigned operation, shift immediate value
481c7fc655fSWojciech Macek 			 * and reset options to default.
482c7fc655fSWojciech Macek 			 */
483c7fc655fSWojciech Macek 			if (sign_ext == 0) {
484c7fc655fSWojciech Macek 				imm = imm << ((insn >> ARM_INSN_SIZE_OFFSET) &
485c7fc655fSWojciech Macek 				    ARM_INSN_SIZE_MASK);
486c7fc655fSWojciech Macek 				option = 0;
487c7fc655fSWojciech Macek 			}
488c7fc655fSWojciech Macek 			switch (option) {
489c7fc655fSWojciech Macek 			case 0x0:
490c7fc655fSWojciech Macek 				pre = 0;
491c7fc655fSWojciech Macek 				inside = 1;
492c7fc655fSWojciech Macek 				break;
493c7fc655fSWojciech Macek 			case 0x1:
494c7fc655fSWojciech Macek 				pre = 0;
495c7fc655fSWojciech Macek 				inside = 0;
496c7fc655fSWojciech Macek 				break;
497c7fc655fSWojciech Macek 			case 0x2:
498c7fc655fSWojciech Macek 			default:
499c7fc655fSWojciech Macek 				pre = 1;
500c7fc655fSWojciech Macek 				inside = 1;
501c7fc655fSWojciech Macek 				break;
502c7fc655fSWojciech Macek 			}
503c7fc655fSWojciech Macek 
5045b61ad4bSMykola Hohsadze 			di->di_printf("%s\t%s, ", i_ptr->name,
5055b61ad4bSMykola Hohsadze 			    arm64_reg(sf, rt));
506c7fc655fSWojciech Macek 			if (inside != 0) {
507c7fc655fSWojciech Macek 				di->di_printf("[%s", arm64_reg(1, rn));
508c7fc655fSWojciech Macek 				if (imm != 0)
509c7fc655fSWojciech Macek 					di->di_printf(", #%d", imm);
510c7fc655fSWojciech Macek 				di->di_printf("]");
511c7fc655fSWojciech Macek 			} else {
512c7fc655fSWojciech Macek 				di->di_printf("[%s]", arm64_reg(1, rn));
513c7fc655fSWojciech Macek 				if (imm != 0)
514c7fc655fSWojciech Macek 					di->di_printf(", #%d", imm);
515c7fc655fSWojciech Macek 			}
516c7fc655fSWojciech Macek 			if (pre != 0)
517c7fc655fSWojciech Macek 				di->di_printf("!");
518c7fc655fSWojciech Macek 		} else {
519c7fc655fSWojciech Macek 			/* Last bit of option field determines 32/64 bit offset */
520c7fc655fSWojciech Macek 			di->di_printf("%s\t%s, [%s, %s", i_ptr->name,
521c7fc655fSWojciech Macek 			    arm64_reg(sf, rt), arm64_reg(1, rn),
522c7fc655fSWojciech Macek 			    arm64_reg(option & 1, rm));
523c7fc655fSWojciech Macek 
524cb923f03SMykola Hohsadze 			if (scale == 0)
525cb923f03SMykola Hohsadze 				amount = 0;
526cb923f03SMykola Hohsadze 			else {
527c7fc655fSWojciech Macek 				/* Calculate amount, it's op(31:30) */
528c7fc655fSWojciech Macek 				amount = (insn >> ARM_INSN_SIZE_OFFSET) &
529c7fc655fSWojciech Macek 			            ARM_INSN_SIZE_MASK;
530cb923f03SMykola Hohsadze 			}
531c7fc655fSWojciech Macek 
532c7fc655fSWojciech Macek 			switch (option) {
533c7fc655fSWojciech Macek 			case 0x2:
534c7fc655fSWojciech Macek 				di->di_printf(", uxtw #%d", amount);
535c7fc655fSWojciech Macek 				break;
536c7fc655fSWojciech Macek 			case 0x3:
537c7fc655fSWojciech Macek 				if (scale != 0)
538c7fc655fSWojciech Macek 					di->di_printf(", lsl #%d", amount);
539c7fc655fSWojciech Macek 				break;
540c7fc655fSWojciech Macek 			case 0x6:
541c7fc655fSWojciech Macek 				di->di_printf(", sxtw #%d", amount);
542c7fc655fSWojciech Macek 				break;
543c7fc655fSWojciech Macek 			case 0x7:
544cb923f03SMykola Hohsadze 				di->di_printf(", sxtx #%d", amount);
545c7fc655fSWojciech Macek 				break;
546c7fc655fSWojciech Macek 			default:
547c7fc655fSWojciech Macek 				di->di_printf(", RSVD");
548c7fc655fSWojciech Macek 				break;
549c7fc655fSWojciech Macek 			}
550c7fc655fSWojciech Macek 			di->di_printf("]");
551c7fc655fSWojciech Macek 		}
552c7fc655fSWojciech Macek 
553c7fc655fSWojciech Macek 		break;
554c7fc655fSWojciech Macek 
555c7fc655fSWojciech Macek 	case TYPE_03:
556c7fc655fSWojciech Macek 		/* OP <RT>, #imm SF32/64 */
557c7fc655fSWojciech Macek 
558c7fc655fSWojciech Macek 		/* Mandatory tokens */
559c7fc655fSWojciech Macek 		ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt);
560c7fc655fSWojciech Macek 		if (ret != 0) {
5615b61ad4bSMykola Hohsadze 			printf("ERROR: "
5625b61ad4bSMykola Hohsadze 			    "Missing mandatory token for op %s type %d\n",
563c7fc655fSWojciech Macek 			    i_ptr->name, i_ptr->type);
564c7fc655fSWojciech Macek 			goto undefined;
565c7fc655fSWojciech Macek 		}
566c7fc655fSWojciech Macek 
567c7fc655fSWojciech Macek 		di->di_printf("%s\t%s, ", i_ptr->name, arm64_reg(sf, rt));
568c7fc655fSWojciech Macek 		if (i_ptr->special_ops & OP_LITERAL)
569c7fc655fSWojciech Macek 			di->di_printf("0x%lx", loc + imm);
570c7fc655fSWojciech Macek 		else
571c7fc655fSWojciech Macek 			di->di_printf("#%d", imm);
572c7fc655fSWojciech Macek 
573c7fc655fSWojciech Macek 		break;
5748a1867f4SWojciech Macek 	default:
5758a1867f4SWojciech Macek 		goto undefined;
5768a1867f4SWojciech Macek 	}
5778a1867f4SWojciech Macek 
5788a1867f4SWojciech Macek 	di->di_printf("\n");
5798a1867f4SWojciech Macek 	return (loc + INSN_SIZE);
5808a1867f4SWojciech Macek 
5818a1867f4SWojciech Macek undefined:
5828a1867f4SWojciech Macek 	di->di_printf("undefined\t%08x\n", insn);
5838a1867f4SWojciech Macek 	return (loc + INSN_SIZE);
5848a1867f4SWojciech Macek }
5858a1867f4SWojciech Macek 
5868a1867f4SWojciech Macek /* Parse format strings at the very beginning */
5875b61ad4bSMykola Hohsadze SYSINIT(arm64_disasm_generate_masks, SI_SUB_DDB_SERVICES, SI_ORDER_FIRST,
5885b61ad4bSMykola Hohsadze     arm64_disasm_generate_masks, arm64_i);
589