xref: /freebsd/sys/arm64/arm64/disassem.c (revision fdafd315)
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 
295b61ad4bSMykola Hohsadze #include <sys/param.h>
308a1867f4SWojciech Macek #include <sys/systm.h>
315b61ad4bSMykola Hohsadze 
328a1867f4SWojciech Macek #include <machine/armreg.h>
335b61ad4bSMykola Hohsadze #include <machine/disassem.h>
345b61ad4bSMykola Hohsadze 
358a1867f4SWojciech Macek #include <ddb/ddb.h>
368a1867f4SWojciech Macek 
378a1867f4SWojciech Macek #define	ARM64_MAX_TOKEN_LEN	8
388a1867f4SWojciech Macek #define	ARM64_MAX_TOKEN_CNT	10
398a1867f4SWojciech Macek 
40c7fc655fSWojciech Macek #define	ARM_INSN_SIZE_OFFSET	30
41c7fc655fSWojciech Macek #define	ARM_INSN_SIZE_MASK	0x3
42c7fc655fSWojciech Macek 
43c7fc655fSWojciech Macek /* Special options for instruction printing */
44c7fc655fSWojciech Macek #define	OP_SIGN_EXT	(1UL << 0)	/* Sign-extend immediate value */
45c7fc655fSWojciech Macek #define	OP_LITERAL	(1UL << 1)	/* Use literal (memory offset) */
46c7fc655fSWojciech Macek #define	OP_MULT_4	(1UL << 2)	/* Multiply immediate by 4 */
47c7fc655fSWojciech Macek #define	OP_SF32		(1UL << 3)	/* Force 32-bit access */
48c7fc655fSWojciech Macek #define	OP_SF_INV	(1UL << 6)	/* SF is inverted (1 means 32 bit access) */
499f60b8ceSMykola Hohsadze #define	OP_RD_SP	(1UL << 7)	/* Use sp for RD otherwise xzr */
509f60b8ceSMykola Hohsadze #define	OP_RT_SP	(1UL << 8)	/* Use sp for RT otherwise xzr */
519f60b8ceSMykola Hohsadze #define	OP_RN_SP	(1UL << 9)	/* Use sp for RN otherwise xzr */
529f60b8ceSMykola Hohsadze #define	OP_RM_SP	(1UL << 10)	/* Use sp for RM otherwise xzr */
539aef25d2SMykola Hohsadze #define	OP_SHIFT_ROR	(1UL << 11)	/* Use ror shift type */
54c7fc655fSWojciech Macek 
558a1867f4SWojciech Macek static const char *w_reg[] = {
568a1867f4SWojciech Macek 	"w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7",
578a1867f4SWojciech Macek 	"w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15",
588a1867f4SWojciech Macek 	"w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23",
599f60b8ceSMykola Hohsadze 	"w24", "w25", "w26", "w27", "w28", "w29", "w30"
608a1867f4SWojciech Macek };
618a1867f4SWojciech Macek 
628a1867f4SWojciech Macek static const char *x_reg[] = {
638a1867f4SWojciech Macek 	"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
648a1867f4SWojciech Macek 	"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
658a1867f4SWojciech Macek 	"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
668a852d3eSMykola Hohsadze 	"x24", "x25", "x26", "x27", "x28", "x29", "lr"
678a1867f4SWojciech Macek };
688a1867f4SWojciech Macek 
698a1867f4SWojciech Macek static const char *shift_2[] = {
709aef25d2SMykola Hohsadze 	"lsl", "lsr", "asr", "ror"
718a1867f4SWojciech Macek };
728a1867f4SWojciech Macek 
734a07c778SMykola Hohsadze static const char *extend_types[] = {
744a07c778SMykola Hohsadze 	"uxtb", "uxth", "uxtw", "uxtx",
754a07c778SMykola Hohsadze 	"sxtb", "sxth", "sxtw", "sxtx",
764a07c778SMykola Hohsadze };
774a07c778SMykola Hohsadze 
788a1867f4SWojciech Macek /*
798a1867f4SWojciech Macek  * Structure representing single token (operand) inside instruction.
808a1867f4SWojciech Macek  * name   - name of operand
818a1867f4SWojciech Macek  * pos    - position within the instruction (in bits)
828a1867f4SWojciech Macek  * len    - operand length (in bits)
838a1867f4SWojciech Macek  */
848a1867f4SWojciech Macek struct arm64_insn_token {
858a1867f4SWojciech Macek 	char name[ARM64_MAX_TOKEN_LEN];
868a1867f4SWojciech Macek 	int pos;
878a1867f4SWojciech Macek 	int len;
888a1867f4SWojciech Macek };
898a1867f4SWojciech Macek 
908a1867f4SWojciech Macek /*
918a1867f4SWojciech Macek  * Define generic types for instruction printing.
928a1867f4SWojciech Macek  */
938a1867f4SWojciech Macek enum arm64_format_type {
945b61ad4bSMykola Hohsadze 	/*
955b61ad4bSMykola Hohsadze 	 * OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #imm} SF32/64
965b61ad4bSMykola Hohsadze 	 * OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64
97ffa75b57SMykola Hohsadze 	 * OP <RD>, <RM> {, <shift> #<imm> }
98ffa75b57SMykola Hohsadze 	 * OP <RN>, <RM> {, <shift> #<imm> }
995b61ad4bSMykola Hohsadze 	 */
1005b61ad4bSMykola Hohsadze 	TYPE_01,
1015b61ad4bSMykola Hohsadze 
1025b61ad4bSMykola Hohsadze 	/*
103e57b8626SMykola Hohsadze 	 * OP <RT>, [<XN|SP>, #<simm>]!
104e57b8626SMykola Hohsadze 	 * OP <RT>, [<XN|SP>], #<simm>
105e57b8626SMykola Hohsadze 	 * OP <RT>, [<XN|SP> {, #<pimm> }]
106e57b8626SMykola Hohsadze 	 * OP <RT>, [<XN|SP>, <RM> {, EXTEND AMOUNT }]
1075b61ad4bSMykola Hohsadze 	 */
1085b61ad4bSMykola Hohsadze 	TYPE_02,
1095b61ad4bSMykola Hohsadze 
1105b61ad4bSMykola Hohsadze 	/* OP <RT>, #imm SF32/64 */
1115b61ad4bSMykola Hohsadze 	TYPE_03,
1124a07c778SMykola Hohsadze 
1134a07c778SMykola Hohsadze 	/*
1144a07c778SMykola Hohsadze 	 * OP <RD>, <RN|SP>, <RM> {, <extend> { #<amount> } }
1154a07c778SMykola Hohsadze 	 * OP <RN|SP>, <RM>, {, <extend> { #<amount> } }
1164a07c778SMykola Hohsadze 	 */
1174a07c778SMykola Hohsadze 	TYPE_04,
1188a1867f4SWojciech Macek };
1198a1867f4SWojciech Macek 
1208a1867f4SWojciech Macek /*
1218a1867f4SWojciech Macek  * Structure representing single parsed instruction format.
1228a1867f4SWojciech Macek  * name   - opcode name
1238a1867f4SWojciech Macek  * format - opcode format in a human-readable way
1248a1867f4SWojciech Macek  * type   - syntax type for printing
1258a1867f4SWojciech Macek  * special_ops  - special options passed to a printer (if any)
1268a1867f4SWojciech Macek  * mask   - bitmask for instruction matching
1278a1867f4SWojciech Macek  * pattern      - pattern to look for
1288a1867f4SWojciech Macek  * tokens - array of tokens (operands) inside instruction
1298a1867f4SWojciech Macek  */
1308a1867f4SWojciech Macek struct arm64_insn {
1318a1867f4SWojciech Macek 	char *name;
1328a1867f4SWojciech Macek 	char *format;
1338a1867f4SWojciech Macek 	enum arm64_format_type type;
1348a1867f4SWojciech Macek 	uint64_t special_ops;
1358a1867f4SWojciech Macek 	uint32_t mask;
1368a1867f4SWojciech Macek 	uint32_t pattern;
1378a1867f4SWojciech Macek 	struct arm64_insn_token tokens[ARM64_MAX_TOKEN_CNT];
1388a1867f4SWojciech Macek };
1398a1867f4SWojciech Macek 
1408a1867f4SWojciech Macek /*
1418a1867f4SWojciech Macek  * Specify instruction opcode format in a human-readable way. Use notation
1428a1867f4SWojciech Macek  * obtained from ARM Architecture Reference Manual for ARMv8-A.
1438a1867f4SWojciech Macek  *
1448a1867f4SWojciech Macek  * Format string description:
1458a1867f4SWojciech Macek  *  Each group must be separated by "|". Group made of 0/1 is used to
1468a1867f4SWojciech Macek  *  generate mask and pattern for instruction matching. Groups containing
1478a1867f4SWojciech Macek  *  an operand token (in format NAME(length_bits)) are used to retrieve any
1488a1867f4SWojciech Macek  *  operand data from the instruction. Names here must be meaningful
1498a1867f4SWojciech Macek  *  and match the one described in the Manual.
1508a1867f4SWojciech Macek  *
1518a1867f4SWojciech Macek  * Token description:
1528a1867f4SWojciech Macek  * SF     - "0" represents 32-bit access, "1" represents 64-bit access
1538a1867f4SWojciech Macek  * SHIFT  - type of shift (instruction dependent)
1548a1867f4SWojciech Macek  * IMM    - immediate value
1558a1867f4SWojciech Macek  * Rx     - register number
156c7fc655fSWojciech Macek  * OPTION - command specific options
157c7fc655fSWojciech Macek  * SCALE  - scaling of immediate value
1588a1867f4SWojciech Macek  */
1598a1867f4SWojciech Macek static struct arm64_insn arm64_i[] = {
160c7fc655fSWojciech Macek 	{ "add", "SF(1)|0001011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
1619f60b8ceSMykola Hohsadze 	    TYPE_01, 0 },			/* add shifted register */
162c7fc655fSWojciech Macek 	{ "mov", "SF(1)|001000100000000000000|RN(5)|RD(5)",
1639f60b8ceSMykola Hohsadze 	    TYPE_01, OP_RD_SP | OP_RN_SP },	/* mov (to/from sp) */
164c7fc655fSWojciech Macek 	{ "add", "SF(1)|0010001|SHIFT(2)|IMM(12)|RN(5)|RD(5)",
1659f60b8ceSMykola Hohsadze 	    TYPE_01, OP_RD_SP | OP_RN_SP },	/* add immediate */
166ffa75b57SMykola Hohsadze 	{ "cmn", "SF(1)|0101011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|11111",
167ffa75b57SMykola Hohsadze 	    TYPE_01, 0 },			/* cmn shifted register */
168ffa75b57SMykola Hohsadze 	{ "adds", "SF(1)|0101011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
169ffa75b57SMykola Hohsadze 	    TYPE_01, 0 },			/* adds shifted register */
170c7fc655fSWojciech Macek 	{ "ldr", "1|SF(1)|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)",
171e57b8626SMykola Hohsadze 	    TYPE_02, OP_SIGN_EXT },
172e57b8626SMykola Hohsadze 	    /* ldr immediate post/pre index */
173c7fc655fSWojciech Macek 	{ "ldr", "1|SF(1)|11100101|IMM(12)|RN(5)|RT(5)",
174e57b8626SMykola Hohsadze 	    TYPE_02, 0 },			/* ldr immediate unsigned */
175c7fc655fSWojciech Macek 	{ "ldr", "1|SF(1)|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
176e57b8626SMykola Hohsadze 	    TYPE_02, 0 },			/* ldr register */
177c7fc655fSWojciech Macek 	{ "ldr", "0|SF(1)|011000|IMM(19)|RT(5)",
178c7fc655fSWojciech Macek 	    TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 },	/* ldr literal */
179c7fc655fSWojciech Macek 	{ "ldrb", "00|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)",
180e57b8626SMykola Hohsadze 	    TYPE_02, OP_SIGN_EXT | OP_SF32 },
1819f60b8ceSMykola Hohsadze 	    /* ldrb immediate post/pre index */
182c7fc655fSWojciech Macek 	{ "ldrb", "00|11100101|IMM(12)|RN(5)|RT(5)",
183e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 },			/* ldrb immediate unsigned */
184c7fc655fSWojciech Macek 	{ "ldrb", "00|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
185e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 },			/* ldrb register */
186c7fc655fSWojciech Macek 	{ "ldrh", "01|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)", TYPE_02,
187e57b8626SMykola Hohsadze 	    OP_SIGN_EXT | OP_SF32 },
188e57b8626SMykola Hohsadze 	    /* ldrh immediate post/pre index */
189c7fc655fSWojciech Macek 	{ "ldrh", "01|11100101|IMM(12)|RN(5)|RT(5)",
190e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 },			/* ldrh immediate unsigned */
191c7fc655fSWojciech Macek 	{ "ldrh", "01|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
192e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 },			/* ldrh register */
193c7fc655fSWojciech Macek 	{ "ldrsb", "001110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)",
194e57b8626SMykola Hohsadze 	    TYPE_02, OP_SIGN_EXT | OP_SF_INV },
1959f60b8ceSMykola Hohsadze 	    /* ldrsb immediate post/pre index */
196c7fc655fSWojciech Macek 	{ "ldrsb", "001110011|SF(1)|IMM(12)|RN(5)|RT(5)",\
197e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF_INV },		/* ldrsb immediate unsigned */
198c7fc655fSWojciech Macek 	{ "ldrsb", "001110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
199e57b8626SMykola Hohsadze 	    TYPE_02,  OP_SF_INV },		/* ldrsb register */
200c7fc655fSWojciech Macek 	{ "ldrsh", "011110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)",
201e57b8626SMykola Hohsadze 	    TYPE_02, OP_SIGN_EXT | OP_SF_INV },
2029f60b8ceSMykola Hohsadze 	    /* ldrsh immediate post/pre index */
203c7fc655fSWojciech Macek 	{ "ldrsh", "011110011|SF(1)|IMM(12)|RN(5)|RT(5)",
204e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF_INV },		/* ldrsh immediate unsigned */
205c7fc655fSWojciech Macek 	{ "ldrsh", "011110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
206e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF_INV },		/* ldrsh register */
207c7fc655fSWojciech Macek 	{ "ldrsw", "10111000100|IMM(9)|OPTION(2)|RN(5)|RT(5)",
208e57b8626SMykola Hohsadze 	    TYPE_02, OP_SIGN_EXT },
209e57b8626SMykola Hohsadze 	    /* ldrsw immediate post/pre index */
210c7fc655fSWojciech Macek 	{ "ldrsw", "1011100110|IMM(12)|RN(5)|RT(5)",
211e57b8626SMykola Hohsadze 	    TYPE_02, 0 },			/* ldrsw immediate unsigned */
212c7fc655fSWojciech Macek 	{ "ldrsw", "10111000101|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
213e57b8626SMykola Hohsadze 	    TYPE_02, 0 },			/* ldrsw register */
214c7fc655fSWojciech Macek 	{ "ldrsw", "10011000|IMM(19)|RT(5)",
2159f60b8ceSMykola Hohsadze 	    TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 },	/* ldrsw literal */
2167edb7adfSMykola Hohsadze 	{ "str", "1|SF(1)|111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)",
217e57b8626SMykola Hohsadze 	    TYPE_02, OP_SIGN_EXT },
218e57b8626SMykola Hohsadze 	    /* str immediate post/pre index */
2197edb7adfSMykola Hohsadze 	{ "str", "1|SF(1)|11100100|IMM(12)|RN(5)|RT(5)",
220e57b8626SMykola Hohsadze 	    TYPE_02, 0 },			/* str immediate unsigned */
2217edb7adfSMykola Hohsadze 	{ "str", "1|SF(1)|111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
222e57b8626SMykola Hohsadze 	    TYPE_02, 0 },			/* str register */
2237edb7adfSMykola Hohsadze 	{ "strb", "00111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)",
224e57b8626SMykola Hohsadze 	    TYPE_02, OP_SIGN_EXT | OP_SF32 },
2259f60b8ceSMykola Hohsadze 	    /* strb immediate post/pre index */
2267edb7adfSMykola Hohsadze 	{ "strb", "0011100100|IMM(12)|RN(5)|RT(5)",
227e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 },			/* strb immediate unsigned */
2287edb7adfSMykola Hohsadze 	{ "strb", "00111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
229e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 },			/* strb register */
2307edb7adfSMykola Hohsadze 	{ "strh", "01111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)",
231e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 | OP_SIGN_EXT },
2329f60b8ceSMykola Hohsadze 	    /* strh immediate post/pre index */
2337edb7adfSMykola Hohsadze 	{ "strh", "0111100100|IMM(12)|RN(5)|RT(5)",
234e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 },
2359f60b8ceSMykola Hohsadze 	    /* strh immediate unsigned */
2367edb7adfSMykola Hohsadze 	{ "strh", "01111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
237e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 },
2389f60b8ceSMykola Hohsadze 	    /* strh register */
239ffa75b57SMykola Hohsadze 	{ "neg", "SF(1)|1001011|SHIFT(2)|0|RM(5)|IMM(6)|11111|RD(5)",
240ffa75b57SMykola Hohsadze 	    TYPE_01, 0 },			/* neg shifted register */
241ffa75b57SMykola Hohsadze 	{ "sub", "SF(1)|1001011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
242ffa75b57SMykola Hohsadze 	    TYPE_01, 0 },			/* sub shifted register */
243ffa75b57SMykola Hohsadze 	{ "cmp", "SF(1)|1101011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|11111",
244ffa75b57SMykola Hohsadze 	    TYPE_01, 0 },			/* cmp shifted register */
245ffa75b57SMykola Hohsadze 	{ "negs", "SF(1)|1101011|SHIFT(2)|0|RM(5)|IMM(6)|11111|RD(5)",
246ffa75b57SMykola Hohsadze 	    TYPE_01, 0 },			/* negs shifted register */
247ffa75b57SMykola Hohsadze 	{ "subs", "SF(1)|1101011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
248ffa75b57SMykola Hohsadze 	    TYPE_01, 0 },			/* subs shifted register */
2499aef25d2SMykola Hohsadze 	{ "mvn", "SF(1)|0101010|SHIFT(2)|1|RM(5)|IMM(6)|11111|RD(5)",
2509aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* mvn shifted register */
2519aef25d2SMykola Hohsadze 	{ "orn", "SF(1)|0101010|SHIFT(2)|1|RM(5)|IMM(6)|RN(5)|RD(5)",
2529aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* orn shifted register */
2539aef25d2SMykola Hohsadze 	{ "mov", "SF(1)|0101010000|RM(5)|000000|11111|RD(5)",
2549aef25d2SMykola Hohsadze 	    TYPE_01, 0 },			/* mov register */
2559aef25d2SMykola Hohsadze 	{ "orr", "SF(1)|0101010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
2569aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* orr shifted register */
2579aef25d2SMykola Hohsadze 	{ "and", "SF(1)|0001010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
2589aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* and shifted register */
2599aef25d2SMykola Hohsadze 	{ "tst", "SF(1)|1101010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|11111",
2609aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* tst shifted register */
2619aef25d2SMykola Hohsadze 	{ "ands", "SF(1)|1101010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
2629aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* ands shifted register */
2639aef25d2SMykola Hohsadze 	{ "bic", "SF(1)|0001010|SHIFT(2)|1|RM(5)|IMM(6)|RN(5)|RD(5)",
2649aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* bic shifted register */
2659aef25d2SMykola Hohsadze 	{ "bics", "SF(1)|1101010|SHIFT(2)|1|RM(5)|IMM(6)|RN(5)|RD(5)",
2669aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* bics shifted register */
2679aef25d2SMykola Hohsadze 	{ "eon", "SF(1)|1001010|SHIFT(2)|1|RM(5)|IMM(6)|RN(5)|RD(5)",
2689aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* eon shifted register */
2699aef25d2SMykola Hohsadze 	{ "eor", "SF(1)|1001010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
2709aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* eor shifted register */
2714a07c778SMykola Hohsadze 	{ "add", "SF(1)|0001011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|RD(5)",
2724a07c778SMykola Hohsadze 	    TYPE_04, OP_RD_SP },		/* add extended register */
2734a07c778SMykola Hohsadze 	{ "cmn", "SF(1)|0101011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|11111",
2744a07c778SMykola Hohsadze 	    TYPE_04, 0 },			/* cmn extended register */
2754a07c778SMykola Hohsadze 	{ "adds", "SF(1)|0101011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|RD(5)",
2764a07c778SMykola Hohsadze 	    TYPE_04, 0 },			/* adds extended register */
2774a07c778SMykola Hohsadze 	{ "sub", "SF(1)|1001011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|RD(5)",
2784a07c778SMykola Hohsadze 	    TYPE_04, OP_RD_SP },		/* sub extended register */
2794a07c778SMykola Hohsadze 	{ "cmp", "SF(1)|1101011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|11111",
2804a07c778SMykola Hohsadze 	    TYPE_04, 0 },			/* cmp extended register */
2814a07c778SMykola Hohsadze 	{ "subs", "SF(1)|1101011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|RD(5)",
2824a07c778SMykola Hohsadze 	    TYPE_04, 0 },			/* subs extended register */
2838a1867f4SWojciech Macek 	{ NULL, NULL }
2848a1867f4SWojciech Macek };
2858a1867f4SWojciech Macek 
2868a1867f4SWojciech Macek static void
arm64_disasm_generate_masks(struct arm64_insn * tab)2878a1867f4SWojciech Macek arm64_disasm_generate_masks(struct arm64_insn *tab)
2888a1867f4SWojciech Macek {
2898a1867f4SWojciech Macek 	uint32_t mask, val;
2908a1867f4SWojciech Macek 	int a, i;
2918a1867f4SWojciech Macek 	int len, ret;
2928a1867f4SWojciech Macek 	int token = 0;
2938a1867f4SWojciech Macek 	char *format;
2948a1867f4SWojciech Macek 	int error;
2958a1867f4SWojciech Macek 
2968a1867f4SWojciech Macek 	while (tab->name != NULL) {
2978a1867f4SWojciech Macek 		mask = 0;
2988a1867f4SWojciech Macek 		val = 0;
2998a1867f4SWojciech Macek 		format = tab->format;
3008a1867f4SWojciech Macek 		token = 0;
3018a1867f4SWojciech Macek 		error = 0;
3028a1867f4SWojciech Macek 
3038a1867f4SWojciech Macek 		/*
3048a1867f4SWojciech Macek 		 * For each entry analyze format strings from the
3058a1867f4SWojciech Macek 		 * left (i.e. from the MSB).
3068a1867f4SWojciech Macek 		 */
3078a1867f4SWojciech Macek 		a = (INSN_SIZE * NBBY) - 1;
3088a1867f4SWojciech Macek 		while (*format != '\0' && (a >= 0)) {
3098a1867f4SWojciech Macek 			switch (*format) {
3108a1867f4SWojciech Macek 			case '0':
3118a1867f4SWojciech Macek 				/* Bit is 0, add to mask and pattern */
3128a1867f4SWojciech Macek 				mask |= (1 << a);
3138a1867f4SWojciech Macek 				a--;
3148a1867f4SWojciech Macek 				format++;
3158a1867f4SWojciech Macek 				break;
3168a1867f4SWojciech Macek 			case '1':
3178a1867f4SWojciech Macek 				/* Bit is 1, add to mask and pattern */
3188a1867f4SWojciech Macek 				mask |= (1 << a);
3198a1867f4SWojciech Macek 				val |= (1 << a);
3208a1867f4SWojciech Macek 				a--;
3218a1867f4SWojciech Macek 				format++;
3228a1867f4SWojciech Macek 				break;
3238a1867f4SWojciech Macek 			case '|':
3248a1867f4SWojciech Macek 				/* skip */
3258a1867f4SWojciech Macek 				format++;
3268a1867f4SWojciech Macek 				break;
3278a1867f4SWojciech Macek 			default:
3288a1867f4SWojciech Macek 				/* Token found, copy the name */
3298a1867f4SWojciech Macek 				memset(tab->tokens[token].name, 0,
3308a1867f4SWojciech Macek 				    sizeof(tab->tokens[token].name));
3318a1867f4SWojciech Macek 				i = 0;
3328a1867f4SWojciech Macek 				while (*format != '(') {
3338a1867f4SWojciech Macek 					tab->tokens[token].name[i] = *format;
3348a1867f4SWojciech Macek 					i++;
3358a1867f4SWojciech Macek 					format++;
3368a1867f4SWojciech Macek 					if (i >= ARM64_MAX_TOKEN_LEN) {
3375b61ad4bSMykola Hohsadze 						printf("ERROR: "
3385b61ad4bSMykola Hohsadze 						    "token too long in op %s\n",
3398a1867f4SWojciech Macek 						    tab->name);
3408a1867f4SWojciech Macek 						error = 1;
3418a1867f4SWojciech Macek 						break;
3428a1867f4SWojciech Macek 					}
3438a1867f4SWojciech Macek 				}
3448a1867f4SWojciech Macek 				if (error != 0)
3458a1867f4SWojciech Macek 					break;
3468a1867f4SWojciech Macek 
3478a1867f4SWojciech Macek 				/* Read the length value */
3488a1867f4SWojciech Macek 				ret = sscanf(format, "(%d)", &len);
3498a1867f4SWojciech Macek 				if (ret == 1) {
3508a1867f4SWojciech Macek 					if (token >= ARM64_MAX_TOKEN_CNT) {
3515b61ad4bSMykola Hohsadze 						printf("ERROR: "
3525b61ad4bSMykola Hohsadze 						    "too many tokens in op %s\n",
3538a1867f4SWojciech Macek 						    tab->name);
3548a1867f4SWojciech Macek 						error = 1;
3558a1867f4SWojciech Macek 						break;
3568a1867f4SWojciech Macek 					}
3578a1867f4SWojciech Macek 
3588a1867f4SWojciech Macek 					a -= len;
3598a1867f4SWojciech Macek 					tab->tokens[token].pos = a + 1;
3608a1867f4SWojciech Macek 					tab->tokens[token].len = len;
3618a1867f4SWojciech Macek 					token++;
3628a1867f4SWojciech Macek 				}
3638a1867f4SWojciech Macek 
3648a1867f4SWojciech Macek 				/* Skip to the end of the token */
3658a1867f4SWojciech Macek 				while (*format != 0 && *format != '|')
3668a1867f4SWojciech Macek 					format++;
3678a1867f4SWojciech Macek 			}
3688a1867f4SWojciech Macek 		}
3698a1867f4SWojciech Macek 
3708a1867f4SWojciech Macek 		/* Write mask and pattern to the instruction array */
3718a1867f4SWojciech Macek 		tab->mask = mask;
3728a1867f4SWojciech Macek 		tab->pattern = val;
3738a1867f4SWojciech Macek 
3748a1867f4SWojciech Macek 		/*
3758a1867f4SWojciech Macek 		 * If we got here, format string must be parsed and "a"
3768a1867f4SWojciech Macek 		 * should point to -1. If it's not, wrong number of bits
3778a1867f4SWojciech Macek 		 * in format string. Mark this as invalid and prevent
3788a1867f4SWojciech Macek 		 * from being matched.
3798a1867f4SWojciech Macek 		 */
3808a1867f4SWojciech Macek 		if (*format != 0 || (a != -1) || (error != 0)) {
3818a1867f4SWojciech Macek 			tab->mask = 0;
3828a1867f4SWojciech Macek 			tab->pattern = 0xffffffff;
3838a1867f4SWojciech Macek 			printf("ERROR: skipping instruction op %s\n",
3848a1867f4SWojciech Macek 			    tab->name);
3858a1867f4SWojciech Macek 		}
3868a1867f4SWojciech Macek 
3878a1867f4SWojciech Macek 		tab++;
3888a1867f4SWojciech Macek 	}
3898a1867f4SWojciech Macek }
3908a1867f4SWojciech Macek 
3918a1867f4SWojciech Macek static int
arm64_disasm_read_token(struct arm64_insn * insn,u_int opcode,const char * token,int * val)3928a1867f4SWojciech Macek arm64_disasm_read_token(struct arm64_insn *insn, u_int opcode,
3938a1867f4SWojciech Macek     const char *token, int *val)
3948a1867f4SWojciech Macek {
3958a1867f4SWojciech Macek 	int i;
3968a1867f4SWojciech Macek 
3978a1867f4SWojciech Macek 	for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) {
3988a1867f4SWojciech Macek 		if (strcmp(insn->tokens[i].name, token) == 0) {
3998a1867f4SWojciech Macek 			*val = (opcode >> insn->tokens[i].pos &
4008a1867f4SWojciech Macek 			    ((1 << insn->tokens[i].len) - 1));
4018a1867f4SWojciech Macek 			return (0);
4028a1867f4SWojciech Macek 		}
4038a1867f4SWojciech Macek 	}
4048a1867f4SWojciech Macek 
4058a1867f4SWojciech Macek 	return (EINVAL);
4068a1867f4SWojciech Macek }
4078a1867f4SWojciech Macek 
408c7fc655fSWojciech Macek static int
arm64_disasm_read_token_sign_ext(struct arm64_insn * insn,u_int opcode,const char * token,int * val)409c7fc655fSWojciech Macek arm64_disasm_read_token_sign_ext(struct arm64_insn *insn, u_int opcode,
410c7fc655fSWojciech Macek     const char *token, int *val)
411c7fc655fSWojciech Macek {
412c7fc655fSWojciech Macek 	int i;
413c7fc655fSWojciech Macek 	int msk;
414c7fc655fSWojciech Macek 
415c7fc655fSWojciech Macek 	for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) {
416c7fc655fSWojciech Macek 		if (strcmp(insn->tokens[i].name, token) == 0) {
417c7fc655fSWojciech Macek 			msk = (1 << insn->tokens[i].len) - 1;
418c7fc655fSWojciech Macek 			*val = ((opcode >> insn->tokens[i].pos) & msk);
419c7fc655fSWojciech Macek 
420c7fc655fSWojciech Macek 			/* If last bit is 1, sign-extend the value */
421c7fc655fSWojciech Macek 			if (*val & (1 << (insn->tokens[i].len - 1)))
422c7fc655fSWojciech Macek 				*val |= ~msk;
423c7fc655fSWojciech Macek 
424c7fc655fSWojciech Macek 			return (0);
425c7fc655fSWojciech Macek 		}
426c7fc655fSWojciech Macek 	}
427c7fc655fSWojciech Macek 
428c7fc655fSWojciech Macek 	return (EINVAL);
429c7fc655fSWojciech Macek }
430c7fc655fSWojciech Macek 
4318a1867f4SWojciech Macek static const char *
arm64_disasm_reg_extend(int sf,int option,int rd,int rn,int amount)4324a07c778SMykola Hohsadze arm64_disasm_reg_extend(int sf, int option, int rd, int rn, int amount)
4334a07c778SMykola Hohsadze {
4344a07c778SMykola Hohsadze 	bool is_sp, lsl_preferred_uxtw, lsl_preferred_uxtx, lsl_preferred;
4354a07c778SMykola Hohsadze 
4364a07c778SMykola Hohsadze 	is_sp = rd == 31 || rn == 31;
4374a07c778SMykola Hohsadze 	lsl_preferred_uxtw = sf == 0 && option == 2;
4384a07c778SMykola Hohsadze 	lsl_preferred_uxtx = sf == 1 && option == 3;
4394a07c778SMykola Hohsadze 	lsl_preferred = is_sp && (lsl_preferred_uxtw || lsl_preferred_uxtx);
4404a07c778SMykola Hohsadze 
4414a07c778SMykola Hohsadze 	/*
4424a07c778SMykola Hohsadze 	 * LSL may be omitted when <amount> is 0.
4434a07c778SMykola Hohsadze 	 * In all other cases <extend> is required.
4444a07c778SMykola Hohsadze 	 */
4454a07c778SMykola Hohsadze 	if (lsl_preferred && amount == 0)
4464a07c778SMykola Hohsadze 		return (NULL);
4474a07c778SMykola Hohsadze 	if (lsl_preferred)
4484a07c778SMykola Hohsadze 		return ("lsl");
4494a07c778SMykola Hohsadze 	return (extend_types[option]);
4504a07c778SMykola Hohsadze }
4514a07c778SMykola Hohsadze 
4524a07c778SMykola Hohsadze static const char *
arm64_w_reg(int num,int wsp)4539f60b8ceSMykola Hohsadze arm64_w_reg(int num, int wsp)
4548a1867f4SWojciech Macek {
4559f60b8ceSMykola Hohsadze 	if (num == 31)
4569f60b8ceSMykola Hohsadze 		return (wsp != 0 ? "wsp" : "wzr");
4578a1867f4SWojciech Macek 	return (w_reg[num]);
4588a1867f4SWojciech Macek }
4598a1867f4SWojciech Macek 
4609f60b8ceSMykola Hohsadze static const char *
arm64_x_reg(int num,int sp)4619f60b8ceSMykola Hohsadze arm64_x_reg(int num, int sp)
4629f60b8ceSMykola Hohsadze {
4639f60b8ceSMykola Hohsadze 	if (num == 31)
4649f60b8ceSMykola Hohsadze 		return (sp != 0 ? "sp" : "xzr");
4659f60b8ceSMykola Hohsadze 	return (x_reg[num]);
4669f60b8ceSMykola Hohsadze }
4679f60b8ceSMykola Hohsadze 
4689f60b8ceSMykola Hohsadze static const char *
arm64_reg(int b64,int num,int sp)4699f60b8ceSMykola Hohsadze arm64_reg(int b64, int num, int sp)
4709f60b8ceSMykola Hohsadze {
4719f60b8ceSMykola Hohsadze 	if (b64 != 0)
4729f60b8ceSMykola Hohsadze 		return (arm64_x_reg(num, sp));
4739f60b8ceSMykola Hohsadze 	return (arm64_w_reg(num, sp));
4749f60b8ceSMykola Hohsadze }
4759f60b8ceSMykola Hohsadze 
4764a07c778SMykola Hohsadze /*
4774a07c778SMykola Hohsadze  * Decodes OPTION(3) to get <Xn|Wn> register or <WZR|XZR>
4784a07c778SMykola Hohsadze  * for extended register instruction.
4794a07c778SMykola Hohsadze  */
4804a07c778SMykola Hohsadze static const char *
arm64_disasm_reg_width(int option,int reg)4814a07c778SMykola Hohsadze arm64_disasm_reg_width(int option, int reg)
4824a07c778SMykola Hohsadze {
4834a07c778SMykola Hohsadze 	if (option == 3 || option == 7)
4844a07c778SMykola Hohsadze 		return (arm64_x_reg(reg, 0));
4854a07c778SMykola Hohsadze 	return (arm64_w_reg(reg, 0));
4864a07c778SMykola Hohsadze }
4874a07c778SMykola Hohsadze 
4888a1867f4SWojciech Macek vm_offset_t
disasm(const struct disasm_interface * di,vm_offset_t loc,int altfmt)4898a1867f4SWojciech Macek disasm(const struct disasm_interface *di, vm_offset_t loc, int altfmt)
4908a1867f4SWojciech Macek {
4918a1867f4SWojciech Macek 	struct arm64_insn *i_ptr = arm64_i;
4928a1867f4SWojciech Macek 	uint32_t insn;
4938a1867f4SWojciech Macek 	int matchp;
4948a1867f4SWojciech Macek 	int ret;
495c7fc655fSWojciech Macek 	int shift, rm, rt, rd, rn, imm, sf, idx, option, scale, amount;
496c7fc655fSWojciech Macek 	int sign_ext;
497ffa75b57SMykola Hohsadze 	bool rm_absent, rd_absent, rn_absent;
498c7fc655fSWojciech Macek 	/* Indicate if immediate should be outside or inside brackets */
499c7fc655fSWojciech Macek 	int inside;
500c7fc655fSWojciech Macek 	/* Print exclamation mark if pre-incremented */
501c7fc655fSWojciech Macek 	int pre;
5029f60b8ceSMykola Hohsadze 	/* Indicate if x31 register should be printed as sp or xzr */
5039f60b8ceSMykola Hohsadze 	int rm_sp, rt_sp, rd_sp, rn_sp;
5049aef25d2SMykola Hohsadze 	/* Indicate if shift type ror is supported */
5059aef25d2SMykola Hohsadze 	bool has_shift_ror;
5068a1867f4SWojciech Macek 
5074a07c778SMykola Hohsadze 	const char *extend;
5084a07c778SMykola Hohsadze 
5098a1867f4SWojciech Macek 	/* Initialize defaults, all are 0 except SF indicating 64bit access */
510c7fc655fSWojciech Macek 	shift = rd = rm = rn = imm = idx = option = amount = scale = 0;
511c7fc655fSWojciech Macek 	sign_ext = 0;
5128a1867f4SWojciech Macek 	sf = 1;
5134a07c778SMykola Hohsadze 	extend = NULL;
5148a1867f4SWojciech Macek 
5158a1867f4SWojciech Macek 	matchp = 0;
5168a1867f4SWojciech Macek 	insn = di->di_readword(loc);
5178a1867f4SWojciech Macek 	while (i_ptr->name) {
5188a1867f4SWojciech Macek 		/* If mask is 0 then the parser was not initialized yet */
5198a1867f4SWojciech Macek 		if ((i_ptr->mask != 0) &&
5208a1867f4SWojciech Macek 		    ((insn & i_ptr->mask) == i_ptr->pattern)) {
5218a1867f4SWojciech Macek 			matchp = 1;
5228a1867f4SWojciech Macek 			break;
5238a1867f4SWojciech Macek 		}
5248a1867f4SWojciech Macek 		i_ptr++;
5258a1867f4SWojciech Macek 	}
5268a1867f4SWojciech Macek 	if (matchp == 0)
5278a1867f4SWojciech Macek 		goto undefined;
5288a1867f4SWojciech Macek 
529c7fc655fSWojciech Macek 	/* Global options */
530c7fc655fSWojciech Macek 	if (i_ptr->special_ops & OP_SF32)
531c7fc655fSWojciech Macek 		sf = 0;
532c7fc655fSWojciech Macek 
533c7fc655fSWojciech Macek 	/* Global optional tokens */
534c7fc655fSWojciech Macek 	arm64_disasm_read_token(i_ptr, insn, "SF", &sf);
535c7fc655fSWojciech Macek 	if (i_ptr->special_ops & OP_SF_INV)
536c7fc655fSWojciech Macek 		sf = 1 - sf;
537c7fc655fSWojciech Macek 	if (arm64_disasm_read_token(i_ptr, insn, "SIGN", &sign_ext) == 0)
538c7fc655fSWojciech Macek 		sign_ext = 1 - sign_ext;
539c7fc655fSWojciech Macek 	if (i_ptr->special_ops & OP_SIGN_EXT)
540c7fc655fSWojciech Macek 		sign_ext = 1;
541c7fc655fSWojciech Macek 	if (sign_ext != 0)
542c7fc655fSWojciech Macek 		arm64_disasm_read_token_sign_ext(i_ptr, insn, "IMM", &imm);
543c7fc655fSWojciech Macek 	else
544c7fc655fSWojciech Macek 		arm64_disasm_read_token(i_ptr, insn, "IMM", &imm);
545c7fc655fSWojciech Macek 	if (i_ptr->special_ops & OP_MULT_4)
546c7fc655fSWojciech Macek 		imm <<= 2;
547c7fc655fSWojciech Macek 
5489f60b8ceSMykola Hohsadze 	rm_sp = i_ptr->special_ops & OP_RM_SP;
5499f60b8ceSMykola Hohsadze 	rt_sp = i_ptr->special_ops & OP_RT_SP;
5509f60b8ceSMykola Hohsadze 	rd_sp = i_ptr->special_ops & OP_RD_SP;
5519f60b8ceSMykola Hohsadze 	rn_sp = i_ptr->special_ops & OP_RN_SP;
5529f60b8ceSMykola Hohsadze 
5539aef25d2SMykola Hohsadze 	has_shift_ror = i_ptr->special_ops & OP_SHIFT_ROR;
5549aef25d2SMykola Hohsadze 
555c7fc655fSWojciech Macek 	/* Print opcode by type */
5568a1867f4SWojciech Macek 	switch (i_ptr->type) {
5578a1867f4SWojciech Macek 	case TYPE_01:
5585b61ad4bSMykola Hohsadze 		/*
5595b61ad4bSMykola Hohsadze 		 * OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64
5605b61ad4bSMykola Hohsadze 		 * OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64
561ffa75b57SMykola Hohsadze 		 * OP <RD>, <RM> {, <shift> #<imm> }
562ffa75b57SMykola Hohsadze 		 * OP <RN>, <RM> {, <shift> #<imm> }
5635b61ad4bSMykola Hohsadze 		 */
5648a1867f4SWojciech Macek 
565ffa75b57SMykola Hohsadze 		rd_absent = arm64_disasm_read_token(i_ptr, insn, "RD", &rd);
566ffa75b57SMykola Hohsadze 		rn_absent = arm64_disasm_read_token(i_ptr, insn, "RN", &rn);
5678a1867f4SWojciech Macek 		rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm);
568ffa75b57SMykola Hohsadze 		arm64_disasm_read_token(i_ptr, insn, "SHIFT", &shift);
5698a1867f4SWojciech Macek 
5709aef25d2SMykola Hohsadze 		/*
5719aef25d2SMykola Hohsadze 		 * if shift type is RESERVED for shifted register instruction,
5729aef25d2SMykola Hohsadze 		 * print undefined
5739aef25d2SMykola Hohsadze 		 */
5749aef25d2SMykola Hohsadze 		if (shift == 3 && !has_shift_ror)
5759aef25d2SMykola Hohsadze 			goto undefined;
5769aef25d2SMykola Hohsadze 
577ffa75b57SMykola Hohsadze 		di->di_printf("%s\t", i_ptr->name);
578ffa75b57SMykola Hohsadze 
579ffa75b57SMykola Hohsadze 		/*
580ffa75b57SMykola Hohsadze 		 * If RD and RN are present, we will display the following
581ffa75b57SMykola Hohsadze 		 * patterns:
582ffa75b57SMykola Hohsadze 		 * - OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64
583ffa75b57SMykola Hohsadze 		 * - OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64
584ffa75b57SMykola Hohsadze 		 * Otherwise if only RD is present:
585ffa75b57SMykola Hohsadze 		 * - OP <RD>, <RM> {, <shift> #<imm> }
586ffa75b57SMykola Hohsadze 		 * Otherwise if only RN is present:
587ffa75b57SMykola Hohsadze 		 * - OP <RN>, <RM> {, <shift> #<imm> }
588ffa75b57SMykola Hohsadze 		 */
589ffa75b57SMykola Hohsadze 		if (!rd_absent && !rn_absent)
590ffa75b57SMykola Hohsadze 			di->di_printf("%s, %s", arm64_reg(sf, rd, rd_sp),
591ffa75b57SMykola Hohsadze 			    arm64_reg(sf, rn, rn_sp));
592ffa75b57SMykola Hohsadze 		else if (!rd_absent)
593ffa75b57SMykola Hohsadze 			di->di_printf("%s", arm64_reg(sf, rd, rd_sp));
594ffa75b57SMykola Hohsadze 		else
595ffa75b57SMykola Hohsadze 			di->di_printf("%s", arm64_reg(sf, rn, rn_sp));
5968a1867f4SWojciech Macek 
5978a1867f4SWojciech Macek 		/* If RM is present use it, otherwise use immediate notation */
598ffa75b57SMykola Hohsadze 		if (!rm_absent) {
5999f60b8ceSMykola Hohsadze 			di->di_printf(", %s", arm64_reg(sf, rm, rm_sp));
6008a1867f4SWojciech Macek 			if (imm != 0)
6018a1867f4SWojciech Macek 				di->di_printf(", %s #%d", shift_2[shift], imm);
6028a1867f4SWojciech Macek 		} else {
6038a1867f4SWojciech Macek 			if (imm != 0 || shift != 0)
6048a1867f4SWojciech Macek 				di->di_printf(", #0x%x", imm);
6058a1867f4SWojciech Macek 			if (shift != 0)
6068a852d3eSMykola Hohsadze 				di->di_printf(" lsl #12");
6078a1867f4SWojciech Macek 		}
6088a1867f4SWojciech Macek 		break;
609c7fc655fSWojciech Macek 	case TYPE_02:
6105b61ad4bSMykola Hohsadze 		/*
611e57b8626SMykola Hohsadze 		 * OP <RT>, [<XN|SP>, #<simm>]!
612e57b8626SMykola Hohsadze 		 * OP <RT>, [<XN|SP>], #<simm>
613e57b8626SMykola Hohsadze 		 * OP <RT>, [<XN|SP> {, #<pimm> }]
614e57b8626SMykola Hohsadze 		 * OP <RT>, [<XN|SP>, <RM> {, EXTEND AMOUNT }]
6155b61ad4bSMykola Hohsadze 		 */
616c7fc655fSWojciech Macek 
617c7fc655fSWojciech Macek 		/* Mandatory tokens */
618c7fc655fSWojciech Macek 		ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt);
619c7fc655fSWojciech Macek 		ret |= arm64_disasm_read_token(i_ptr, insn, "RN", &rn);
620c7fc655fSWojciech Macek 		if (ret != 0) {
6215b61ad4bSMykola Hohsadze 			printf("ERROR: "
6225b61ad4bSMykola Hohsadze 			    "Missing mandatory token for op %s type %d\n",
623c7fc655fSWojciech Macek 			    i_ptr->name, i_ptr->type);
624c7fc655fSWojciech Macek 			goto undefined;
625c7fc655fSWojciech Macek 		}
626c7fc655fSWojciech Macek 
627c7fc655fSWojciech Macek 		/* Optional tokens */
628c7fc655fSWojciech Macek 		arm64_disasm_read_token(i_ptr, insn, "OPTION", &option);
629c7fc655fSWojciech Macek 		arm64_disasm_read_token(i_ptr, insn, "SCALE", &scale);
630c7fc655fSWojciech Macek 		rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm);
631c7fc655fSWojciech Macek 
632c7fc655fSWojciech Macek 		if (rm_absent) {
633c7fc655fSWojciech Macek 			/*
634c7fc655fSWojciech Macek 			 * In unsigned operation, shift immediate value
635c7fc655fSWojciech Macek 			 * and reset options to default.
636c7fc655fSWojciech Macek 			 */
637c7fc655fSWojciech Macek 			if (sign_ext == 0) {
638c7fc655fSWojciech Macek 				imm = imm << ((insn >> ARM_INSN_SIZE_OFFSET) &
639c7fc655fSWojciech Macek 				    ARM_INSN_SIZE_MASK);
640c7fc655fSWojciech Macek 				option = 0;
641c7fc655fSWojciech Macek 			}
642c7fc655fSWojciech Macek 			switch (option) {
643c7fc655fSWojciech Macek 			case 0x0:
644c7fc655fSWojciech Macek 				pre = 0;
645c7fc655fSWojciech Macek 				inside = 1;
646c7fc655fSWojciech Macek 				break;
647c7fc655fSWojciech Macek 			case 0x1:
648c7fc655fSWojciech Macek 				pre = 0;
649c7fc655fSWojciech Macek 				inside = 0;
650c7fc655fSWojciech Macek 				break;
651c7fc655fSWojciech Macek 			case 0x2:
652c7fc655fSWojciech Macek 			default:
653c7fc655fSWojciech Macek 				pre = 1;
654c7fc655fSWojciech Macek 				inside = 1;
655c7fc655fSWojciech Macek 				break;
656c7fc655fSWojciech Macek 			}
657c7fc655fSWojciech Macek 
6585b61ad4bSMykola Hohsadze 			di->di_printf("%s\t%s, ", i_ptr->name,
6599f60b8ceSMykola Hohsadze 			    arm64_reg(sf, rt, rt_sp));
660c7fc655fSWojciech Macek 			if (inside != 0) {
661e57b8626SMykola Hohsadze 				di->di_printf("[%s", arm64_x_reg(rn, 1));
662c7fc655fSWojciech Macek 				if (imm != 0)
663c7fc655fSWojciech Macek 					di->di_printf(", #%d", imm);
664c7fc655fSWojciech Macek 				di->di_printf("]");
665c7fc655fSWojciech Macek 			} else {
666e57b8626SMykola Hohsadze 				di->di_printf("[%s]", arm64_x_reg(rn, 1));
667c7fc655fSWojciech Macek 				if (imm != 0)
668c7fc655fSWojciech Macek 					di->di_printf(", #%d", imm);
669c7fc655fSWojciech Macek 			}
670c7fc655fSWojciech Macek 			if (pre != 0)
671c7fc655fSWojciech Macek 				di->di_printf("!");
672c7fc655fSWojciech Macek 		} else {
673c7fc655fSWojciech Macek 			/* Last bit of option field determines 32/64 bit offset */
674c7fc655fSWojciech Macek 			di->di_printf("%s\t%s, [%s, %s", i_ptr->name,
675e57b8626SMykola Hohsadze 			    arm64_reg(sf, rt, rt_sp), arm64_x_reg(rn, 1),
6769f60b8ceSMykola Hohsadze 			    arm64_reg(option & 1, rm, rm_sp));
677c7fc655fSWojciech Macek 
678cb923f03SMykola Hohsadze 			if (scale == 0)
679cb923f03SMykola Hohsadze 				amount = 0;
680cb923f03SMykola Hohsadze 			else {
681c7fc655fSWojciech Macek 				/* Calculate amount, it's op(31:30) */
682c7fc655fSWojciech Macek 				amount = (insn >> ARM_INSN_SIZE_OFFSET) &
683c7fc655fSWojciech Macek 			            ARM_INSN_SIZE_MASK;
684cb923f03SMykola Hohsadze 			}
685c7fc655fSWojciech Macek 
686c7fc655fSWojciech Macek 			switch (option) {
687c7fc655fSWojciech Macek 			case 0x2:
688c7fc655fSWojciech Macek 				di->di_printf(", uxtw #%d", amount);
689c7fc655fSWojciech Macek 				break;
690c7fc655fSWojciech Macek 			case 0x3:
691c7fc655fSWojciech Macek 				if (scale != 0)
692c7fc655fSWojciech Macek 					di->di_printf(", lsl #%d", amount);
693c7fc655fSWojciech Macek 				break;
694c7fc655fSWojciech Macek 			case 0x6:
695c7fc655fSWojciech Macek 				di->di_printf(", sxtw #%d", amount);
696c7fc655fSWojciech Macek 				break;
697c7fc655fSWojciech Macek 			case 0x7:
698cb923f03SMykola Hohsadze 				di->di_printf(", sxtx #%d", amount);
699c7fc655fSWojciech Macek 				break;
700c7fc655fSWojciech Macek 			default:
7018a852d3eSMykola Hohsadze 				di->di_printf(", rsv");
702c7fc655fSWojciech Macek 				break;
703c7fc655fSWojciech Macek 			}
704c7fc655fSWojciech Macek 			di->di_printf("]");
705c7fc655fSWojciech Macek 		}
706c7fc655fSWojciech Macek 
707c7fc655fSWojciech Macek 		break;
708c7fc655fSWojciech Macek 
709c7fc655fSWojciech Macek 	case TYPE_03:
710c7fc655fSWojciech Macek 		/* OP <RT>, #imm SF32/64 */
711c7fc655fSWojciech Macek 
712c7fc655fSWojciech Macek 		/* Mandatory tokens */
713c7fc655fSWojciech Macek 		ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt);
714c7fc655fSWojciech Macek 		if (ret != 0) {
7155b61ad4bSMykola Hohsadze 			printf("ERROR: "
7165b61ad4bSMykola Hohsadze 			    "Missing mandatory token for op %s type %d\n",
717c7fc655fSWojciech Macek 			    i_ptr->name, i_ptr->type);
718c7fc655fSWojciech Macek 			goto undefined;
719c7fc655fSWojciech Macek 		}
720c7fc655fSWojciech Macek 
7219f60b8ceSMykola Hohsadze 		di->di_printf("%s\t%s, ", i_ptr->name, arm64_reg(sf, rt, rt_sp));
722c7fc655fSWojciech Macek 		if (i_ptr->special_ops & OP_LITERAL)
723c7fc655fSWojciech Macek 			di->di_printf("0x%lx", loc + imm);
724c7fc655fSWojciech Macek 		else
725c7fc655fSWojciech Macek 			di->di_printf("#%d", imm);
726c7fc655fSWojciech Macek 
727c7fc655fSWojciech Macek 		break;
7284a07c778SMykola Hohsadze 
7294a07c778SMykola Hohsadze 	case TYPE_04:
7304a07c778SMykola Hohsadze 		/*
7314a07c778SMykola Hohsadze 		 * OP <RD>, <RN|SP>, <RM> {, <extend> { #<amount> } }
7324a07c778SMykola Hohsadze 		 * OP <RN|SP>, <RM>, {, <extend> { #<amount> } }
7334a07c778SMykola Hohsadze 		 */
7344a07c778SMykola Hohsadze 
7354a07c778SMykola Hohsadze 		arm64_disasm_read_token(i_ptr, insn, "RN", &rn);
7364a07c778SMykola Hohsadze 		arm64_disasm_read_token(i_ptr, insn, "RM", &rm);
7374a07c778SMykola Hohsadze 		arm64_disasm_read_token(i_ptr, insn, "OPTION", &option);
7384a07c778SMykola Hohsadze 
7394a07c778SMykola Hohsadze 		rd_absent = arm64_disasm_read_token(i_ptr, insn, "RD", &rd);
7404a07c778SMykola Hohsadze 		extend = arm64_disasm_reg_extend(sf, option, rd, rn, imm);
7414a07c778SMykola Hohsadze 
7424a07c778SMykola Hohsadze 		di->di_printf("%s\t", i_ptr->name);
7434a07c778SMykola Hohsadze 
7444a07c778SMykola Hohsadze 		if (!rd_absent)
7454a07c778SMykola Hohsadze 			di->di_printf("%s, ", arm64_reg(sf, rd, rd_sp));
7464a07c778SMykola Hohsadze 
7474a07c778SMykola Hohsadze 		di->di_printf("%s, ", arm64_reg(sf, rn, 1));
7484a07c778SMykola Hohsadze 
7494a07c778SMykola Hohsadze 		if (sf != 0)
7504a07c778SMykola Hohsadze 			di->di_printf("%s",
7514a07c778SMykola Hohsadze 			    arm64_disasm_reg_width(option, rm));
7524a07c778SMykola Hohsadze 		else
7534a07c778SMykola Hohsadze 			di->di_printf("%s", arm64_w_reg(rm, 0));
7544a07c778SMykola Hohsadze 
7554a07c778SMykola Hohsadze 		if (extend != NULL)
7564a07c778SMykola Hohsadze 			di->di_printf(", %s #%d", extend, imm);
7574a07c778SMykola Hohsadze 
7584a07c778SMykola Hohsadze 		break;
7598a1867f4SWojciech Macek 	default:
7608a1867f4SWojciech Macek 		goto undefined;
7618a1867f4SWojciech Macek 	}
7628a1867f4SWojciech Macek 
7638a1867f4SWojciech Macek 	di->di_printf("\n");
7648a1867f4SWojciech Macek 	return (loc + INSN_SIZE);
7658a1867f4SWojciech Macek 
7668a1867f4SWojciech Macek undefined:
7678a1867f4SWojciech Macek 	di->di_printf("undefined\t%08x\n", insn);
7688a1867f4SWojciech Macek 	return (loc + INSN_SIZE);
7698a1867f4SWojciech Macek }
7708a1867f4SWojciech Macek 
7718a1867f4SWojciech Macek /* Parse format strings at the very beginning */
7725b61ad4bSMykola Hohsadze SYSINIT(arm64_disasm_generate_masks, SI_SUB_DDB_SERVICES, SI_ORDER_FIRST,
7735b61ad4bSMykola Hohsadze     arm64_disasm_generate_masks, arm64_i);
774