xref: /freebsd/sys/arm64/arm64/disassem.c (revision 8a1867f4)
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 
418a1867f4SWojciech Macek static const char *w_reg[] = {
428a1867f4SWojciech Macek 	"w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7",
438a1867f4SWojciech Macek 	"w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15",
448a1867f4SWojciech Macek 	"w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23",
458a1867f4SWojciech Macek 	"w24", "w25", "w26", "w27", "w28", "w29", "w30", "wSP",
468a1867f4SWojciech Macek };
478a1867f4SWojciech Macek 
488a1867f4SWojciech Macek static const char *x_reg[] = {
498a1867f4SWojciech Macek 	"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
508a1867f4SWojciech Macek 	"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
518a1867f4SWojciech Macek 	"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
528a1867f4SWojciech Macek 	"x24", "x25", "x26", "x27", "x28", "x29", "LR", "SP",
538a1867f4SWojciech Macek };
548a1867f4SWojciech Macek 
558a1867f4SWojciech Macek static const char *shift_2[] = {
568a1867f4SWojciech Macek 	"LSL", "LSR", "ASR", "RSV"
578a1867f4SWojciech Macek };
588a1867f4SWojciech Macek 
598a1867f4SWojciech Macek /*
608a1867f4SWojciech Macek  * Structure representing single token (operand) inside instruction.
618a1867f4SWojciech Macek  * name   - name of operand
628a1867f4SWojciech Macek  * pos    - position within the instruction (in bits)
638a1867f4SWojciech Macek  * len    - operand length (in bits)
648a1867f4SWojciech Macek  */
658a1867f4SWojciech Macek struct arm64_insn_token {
668a1867f4SWojciech Macek 	char name[ARM64_MAX_TOKEN_LEN];
678a1867f4SWojciech Macek 	int pos;
688a1867f4SWojciech Macek 	int len;
698a1867f4SWojciech Macek };
708a1867f4SWojciech Macek 
718a1867f4SWojciech Macek /*
728a1867f4SWojciech Macek  * Define generic types for instruction printing.
738a1867f4SWojciech Macek  */
748a1867f4SWojciech Macek enum arm64_format_type {
758a1867f4SWojciech Macek 	TYPE_01,	/* OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64
768a1867f4SWojciech Macek 			   OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64 */
778a1867f4SWojciech Macek };
788a1867f4SWojciech Macek 
798a1867f4SWojciech Macek /*
808a1867f4SWojciech Macek  * Structure representing single parsed instruction format.
818a1867f4SWojciech Macek  * name   - opcode name
828a1867f4SWojciech Macek  * format - opcode format in a human-readable way
838a1867f4SWojciech Macek  * type   - syntax type for printing
848a1867f4SWojciech Macek  * special_ops  - special options passed to a printer (if any)
858a1867f4SWojciech Macek  * mask   - bitmask for instruction matching
868a1867f4SWojciech Macek  * pattern      - pattern to look for
878a1867f4SWojciech Macek  * tokens - array of tokens (operands) inside instruction
888a1867f4SWojciech Macek  */
898a1867f4SWojciech Macek struct arm64_insn {
908a1867f4SWojciech Macek 	char* name;
918a1867f4SWojciech Macek 	char* format;
928a1867f4SWojciech Macek 	enum arm64_format_type type;
938a1867f4SWojciech Macek 	uint64_t special_ops;
948a1867f4SWojciech Macek 	uint32_t mask;
958a1867f4SWojciech Macek 	uint32_t pattern;
968a1867f4SWojciech Macek 	struct arm64_insn_token tokens[ARM64_MAX_TOKEN_CNT];
978a1867f4SWojciech Macek };
988a1867f4SWojciech Macek 
998a1867f4SWojciech Macek /*
1008a1867f4SWojciech Macek  * Specify instruction opcode format in a human-readable way. Use notation
1018a1867f4SWojciech Macek  * obtained from ARM Architecture Reference Manual for ARMv8-A.
1028a1867f4SWojciech Macek  *
1038a1867f4SWojciech Macek  * Format string description:
1048a1867f4SWojciech Macek  *  Each group must be separated by "|". Group made of 0/1 is used to
1058a1867f4SWojciech Macek  *  generate mask and pattern for instruction matching. Groups containing
1068a1867f4SWojciech Macek  *  an operand token (in format NAME(length_bits)) are used to retrieve any
1078a1867f4SWojciech Macek  *  operand data from the instruction. Names here must be meaningful
1088a1867f4SWojciech Macek  *  and match the one described in the Manual.
1098a1867f4SWojciech Macek  *
1108a1867f4SWojciech Macek  * Token description:
1118a1867f4SWojciech Macek  * SF     - "0" represents 32-bit access, "1" represents 64-bit access
1128a1867f4SWojciech Macek  * SHIFT  - type of shift (instruction dependent)
1138a1867f4SWojciech Macek  * IMM    - immediate value
1148a1867f4SWojciech Macek  * Rx     - register number
1158a1867f4SWojciech Macek  */
1168a1867f4SWojciech Macek static struct arm64_insn arm64_i[] = {
1178a1867f4SWojciech Macek     { "add", "SF(1)|0001011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", TYPE_01, 0 },
1188a1867f4SWojciech Macek     { "mov", "SF(1)|001000100000000000000|RN(5)|RD(5)", TYPE_01, 0 },
1198a1867f4SWojciech Macek     { "add", "SF(1)|0010001|SHIFT(2)|IMM(12)|RN(5)|RD(5)", TYPE_01, 0 },
1208a1867f4SWojciech Macek     { NULL, NULL }
1218a1867f4SWojciech Macek };
1228a1867f4SWojciech Macek 
1238a1867f4SWojciech Macek static void
1248a1867f4SWojciech Macek arm64_disasm_generate_masks(struct arm64_insn *tab)
1258a1867f4SWojciech Macek {
1268a1867f4SWojciech Macek 	uint32_t mask, val;
1278a1867f4SWojciech Macek 	int a, i;
1288a1867f4SWojciech Macek 	int len, ret;
1298a1867f4SWojciech Macek 	int token = 0;
1308a1867f4SWojciech Macek 	char *format;
1318a1867f4SWojciech Macek 	int error;
1328a1867f4SWojciech Macek 
1338a1867f4SWojciech Macek 	while (tab->name != NULL) {
1348a1867f4SWojciech Macek 		mask = 0;
1358a1867f4SWojciech Macek 		val = 0;
1368a1867f4SWojciech Macek 		format = tab->format;
1378a1867f4SWojciech Macek 		token = 0;
1388a1867f4SWojciech Macek 		error = 0;
1398a1867f4SWojciech Macek 
1408a1867f4SWojciech Macek 		/*
1418a1867f4SWojciech Macek 		 * For each entry analyze format strings from the
1428a1867f4SWojciech Macek 		 * left (i.e. from the MSB).
1438a1867f4SWojciech Macek 		 */
1448a1867f4SWojciech Macek 		a = (INSN_SIZE * NBBY) - 1;
1458a1867f4SWojciech Macek 		while (*format != '\0' && (a >= 0)) {
1468a1867f4SWojciech Macek 			switch(*format) {
1478a1867f4SWojciech Macek 			case '0':
1488a1867f4SWojciech Macek 				/* Bit is 0, add to mask and pattern */
1498a1867f4SWojciech Macek 				mask |= (1 << a);
1508a1867f4SWojciech Macek 				a--;
1518a1867f4SWojciech Macek 				format++;
1528a1867f4SWojciech Macek 				break;
1538a1867f4SWojciech Macek 			case '1':
1548a1867f4SWojciech Macek 				/* Bit is 1, add to mask and pattern */
1558a1867f4SWojciech Macek 				mask |= (1 << a);
1568a1867f4SWojciech Macek 				val |= (1 << a);
1578a1867f4SWojciech Macek 				a--;
1588a1867f4SWojciech Macek 				format++;
1598a1867f4SWojciech Macek 				break;
1608a1867f4SWojciech Macek 			case '|':
1618a1867f4SWojciech Macek 				/* skip */
1628a1867f4SWojciech Macek 				format++;
1638a1867f4SWojciech Macek 				break;
1648a1867f4SWojciech Macek 			default:
1658a1867f4SWojciech Macek 				/* Token found, copy the name */
1668a1867f4SWojciech Macek 				memset(tab->tokens[token].name, 0,
1678a1867f4SWojciech Macek 				    sizeof(tab->tokens[token].name));
1688a1867f4SWojciech Macek 				i = 0;
1698a1867f4SWojciech Macek 				while (*format != '(') {
1708a1867f4SWojciech Macek 					tab->tokens[token].name[i] = *format;
1718a1867f4SWojciech Macek 					i++;
1728a1867f4SWojciech Macek 					format++;
1738a1867f4SWojciech Macek 					if (i >= ARM64_MAX_TOKEN_LEN) {
1748a1867f4SWojciech Macek 						printf("ERROR: token too long in op %s\n",
1758a1867f4SWojciech Macek 						    tab->name);
1768a1867f4SWojciech Macek 						error = 1;
1778a1867f4SWojciech Macek 						break;
1788a1867f4SWojciech Macek 					}
1798a1867f4SWojciech Macek 				}
1808a1867f4SWojciech Macek 				if (error != 0)
1818a1867f4SWojciech Macek 					break;
1828a1867f4SWojciech Macek 
1838a1867f4SWojciech Macek 				/* Read the length value */
1848a1867f4SWojciech Macek 				ret = sscanf(format, "(%d)", &len);
1858a1867f4SWojciech Macek 				if (ret == 1) {
1868a1867f4SWojciech Macek 					if (token >= ARM64_MAX_TOKEN_CNT) {
1878a1867f4SWojciech Macek 						printf("ERROR: to many tokens in op %s\n",
1888a1867f4SWojciech Macek 						    tab->name);
1898a1867f4SWojciech Macek 						error = 1;
1908a1867f4SWojciech Macek 						break;
1918a1867f4SWojciech Macek 					}
1928a1867f4SWojciech Macek 
1938a1867f4SWojciech Macek 					a -= len;
1948a1867f4SWojciech Macek 					tab->tokens[token].pos = a + 1;
1958a1867f4SWojciech Macek 					tab->tokens[token].len = len;
1968a1867f4SWojciech Macek 					token++;
1978a1867f4SWojciech Macek 				}
1988a1867f4SWojciech Macek 
1998a1867f4SWojciech Macek 				/* Skip to the end of the token */
2008a1867f4SWojciech Macek 				while (*format != 0 && *format != '|')
2018a1867f4SWojciech Macek 					format++;
2028a1867f4SWojciech Macek 			}
2038a1867f4SWojciech Macek 		}
2048a1867f4SWojciech Macek 
2058a1867f4SWojciech Macek 		/* Write mask and pattern to the instruction array */
2068a1867f4SWojciech Macek 		tab->mask = mask;
2078a1867f4SWojciech Macek 		tab->pattern = val;
2088a1867f4SWojciech Macek 
2098a1867f4SWojciech Macek 		/*
2108a1867f4SWojciech Macek 		 * If we got here, format string must be parsed and "a"
2118a1867f4SWojciech Macek 		 * should point to -1. If it's not, wrong number of bits
2128a1867f4SWojciech Macek 		 * in format string. Mark this as invalid and prevent
2138a1867f4SWojciech Macek 		 * from being matched.
2148a1867f4SWojciech Macek 		 */
2158a1867f4SWojciech Macek 		if (*format != 0 || (a != -1) || (error != 0)) {
2168a1867f4SWojciech Macek 			tab->mask = 0;
2178a1867f4SWojciech Macek 			tab->pattern = 0xffffffff;
2188a1867f4SWojciech Macek 			printf("ERROR: skipping instruction op %s\n",
2198a1867f4SWojciech Macek 			    tab->name);
2208a1867f4SWojciech Macek 		}
2218a1867f4SWojciech Macek 
2228a1867f4SWojciech Macek 		tab++;
2238a1867f4SWojciech Macek 	}
2248a1867f4SWojciech Macek }
2258a1867f4SWojciech Macek 
2268a1867f4SWojciech Macek static int
2278a1867f4SWojciech Macek arm64_disasm_read_token(struct arm64_insn *insn, u_int opcode,
2288a1867f4SWojciech Macek     const char *token, int *val)
2298a1867f4SWojciech Macek {
2308a1867f4SWojciech Macek 	int i;
2318a1867f4SWojciech Macek 
2328a1867f4SWojciech Macek 	for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) {
2338a1867f4SWojciech Macek 		if (strcmp(insn->tokens[i].name, token) == 0) {
2348a1867f4SWojciech Macek 			*val = (opcode >> insn->tokens[i].pos &
2358a1867f4SWojciech Macek 			    ((1 << insn->tokens[i].len) - 1));
2368a1867f4SWojciech Macek 			return (0);
2378a1867f4SWojciech Macek 		}
2388a1867f4SWojciech Macek 	}
2398a1867f4SWojciech Macek 
2408a1867f4SWojciech Macek 	return (EINVAL);
2418a1867f4SWojciech Macek }
2428a1867f4SWojciech Macek 
2438a1867f4SWojciech Macek static const char *
2448a1867f4SWojciech Macek arm64_reg(int b64, int num)
2458a1867f4SWojciech Macek {
2468a1867f4SWojciech Macek 
2478a1867f4SWojciech Macek 	if (b64 != 0)
2488a1867f4SWojciech Macek 		return (x_reg[num]);
2498a1867f4SWojciech Macek 
2508a1867f4SWojciech Macek 	return (w_reg[num]);
2518a1867f4SWojciech Macek }
2528a1867f4SWojciech Macek 
2538a1867f4SWojciech Macek vm_offset_t
2548a1867f4SWojciech Macek disasm(const struct disasm_interface *di, vm_offset_t loc, int altfmt)
2558a1867f4SWojciech Macek {
2568a1867f4SWojciech Macek 	struct arm64_insn *i_ptr = arm64_i;
2578a1867f4SWojciech Macek 	uint32_t insn;
2588a1867f4SWojciech Macek 	int matchp;
2598a1867f4SWojciech Macek 	int ret;
2608a1867f4SWojciech Macek 	int shift, rm, rd, rn, imm, sf;
2618a1867f4SWojciech Macek 	int rm_absent;
2628a1867f4SWojciech Macek 
2638a1867f4SWojciech Macek 	/* Initialize defaults, all are 0 except SF indicating 64bit access */
2648a1867f4SWojciech Macek 	shift = rd = rm = rn = imm = 0;
2658a1867f4SWojciech Macek 	sf = 1;
2668a1867f4SWojciech Macek 
2678a1867f4SWojciech Macek 	matchp = 0;
2688a1867f4SWojciech Macek 	insn = di->di_readword(loc);
2698a1867f4SWojciech Macek 	while (i_ptr->name) {
2708a1867f4SWojciech Macek 		/* If mask is 0 then the parser was not initialized yet */
2718a1867f4SWojciech Macek 		if ((i_ptr->mask != 0) &&
2728a1867f4SWojciech Macek 		    ((insn & i_ptr->mask) ==  i_ptr->pattern)) {
2738a1867f4SWojciech Macek 			matchp = 1;
2748a1867f4SWojciech Macek 			break;
2758a1867f4SWojciech Macek 		}
2768a1867f4SWojciech Macek 		i_ptr++;
2778a1867f4SWojciech Macek 	}
2788a1867f4SWojciech Macek 	if (matchp == 0)
2798a1867f4SWojciech Macek 		goto undefined;
2808a1867f4SWojciech Macek 
2818a1867f4SWojciech Macek 	switch (i_ptr->type) {
2828a1867f4SWojciech Macek 	case TYPE_01:
2838a1867f4SWojciech Macek 		/* OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64
2848a1867f4SWojciech Macek 		   OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64 */
2858a1867f4SWojciech Macek 
2868a1867f4SWojciech Macek 		/* Mandatory tokens */
2878a1867f4SWojciech Macek 		ret = arm64_disasm_read_token(i_ptr, insn, "SF", &sf);
2888a1867f4SWojciech Macek 		ret |= arm64_disasm_read_token(i_ptr, insn, "RD", &rd);
2898a1867f4SWojciech Macek 		ret |= arm64_disasm_read_token(i_ptr, insn, "RN", &rn);
2908a1867f4SWojciech Macek 		if (ret != 0) {
2918a1867f4SWojciech Macek 			printf("ERROR: Missing mandatory token for op %s type %d\n",
2928a1867f4SWojciech Macek 			    i_ptr->name, i_ptr->type);
2938a1867f4SWojciech Macek 			goto undefined;
2948a1867f4SWojciech Macek 		}
2958a1867f4SWojciech Macek 
2968a1867f4SWojciech Macek 		/* Optional tokens */
2978a1867f4SWojciech Macek 		arm64_disasm_read_token(i_ptr, insn, "IMM", &imm);
2988a1867f4SWojciech Macek 		arm64_disasm_read_token(i_ptr, insn, "SHIFT", &shift);
2998a1867f4SWojciech Macek 		rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm);
3008a1867f4SWojciech Macek 
3018a1867f4SWojciech Macek 		di->di_printf("%s\t%s, %s", i_ptr->name, arm64_reg(sf, rd),
3028a1867f4SWojciech Macek 		    arm64_reg(sf, rn));
3038a1867f4SWojciech Macek 
3048a1867f4SWojciech Macek 		/* If RM is present use it, otherwise use immediate notation */
3058a1867f4SWojciech Macek 		if (rm_absent == 0) {
3068a1867f4SWojciech Macek 			di->di_printf(", %s", arm64_reg(sf, rm));
3078a1867f4SWojciech Macek 			if (imm != 0)
3088a1867f4SWojciech Macek 				di->di_printf(", %s #%d", shift_2[shift], imm);
3098a1867f4SWojciech Macek 		} else {
3108a1867f4SWojciech Macek 			if (imm != 0 || shift != 0)
3118a1867f4SWojciech Macek 				di->di_printf(", #0x%x", imm);
3128a1867f4SWojciech Macek 			if (shift != 0)
3138a1867f4SWojciech Macek 				di->di_printf(" LSL #12");
3148a1867f4SWojciech Macek 		}
3158a1867f4SWojciech Macek 		break;
3168a1867f4SWojciech Macek 	default:
3178a1867f4SWojciech Macek 		goto undefined;
3188a1867f4SWojciech Macek 	}
3198a1867f4SWojciech Macek 
3208a1867f4SWojciech Macek 	di->di_printf("\n");
3218a1867f4SWojciech Macek 	return(loc + INSN_SIZE);
3228a1867f4SWojciech Macek 
3238a1867f4SWojciech Macek undefined:
3248a1867f4SWojciech Macek 	di->di_printf("undefined\t%08x\n", insn);
3258a1867f4SWojciech Macek 	return(loc + INSN_SIZE);
3268a1867f4SWojciech Macek }
3278a1867f4SWojciech Macek 
3288a1867f4SWojciech Macek /* Parse format strings at the very beginning */
3298a1867f4SWojciech Macek SYSINIT(arm64_disasm_generate_masks, SI_SUB_DDB_SERVICES,
3308a1867f4SWojciech Macek     SI_ORDER_FIRST, arm64_disasm_generate_masks, arm64_i);
331