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