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