diff options
author | Mykola Hohsadze <koliagogsadze@gmail.com> | 2023-07-24 20:49:24 +0000 |
---|---|---|
committer | Mitchell Horne <mhorne@FreeBSD.org> | 2023-07-31 12:54:01 +0000 |
commit | 193aeedc9b66d759148d51b97610ec4558569aae (patch) | |
tree | e0bf6039c3d6749a1d934e89ddc74959aeb26ecc | |
parent | c3c6cedc0ab487d168789e21a0902b28aacec26a (diff) | |
download | src-193aeedc9b66d759148d51b97610ec4558569aae.tar.gz src-193aeedc9b66d759148d51b97610ec4558569aae.zip |
arm64/disassem.c: add extended register instruction definitions
Add disassembly support for the following extended register
instructions: add, adds, sub, subs, cmp, cmn.
Reviewed by: mhorne
MFC after: 1 week
Pull Request: https://reviews.freebsd.org/D40967
(cherry picked from commit 4a07c778632bddb86a82f3e1fe144d889dae69c5)
-rw-r--r-- | sys/arm64/arm64/disassem.c | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/sys/arm64/arm64/disassem.c b/sys/arm64/arm64/disassem.c index c1e38266a2e0..a86fef1d96a6 100644 --- a/sys/arm64/arm64/disassem.c +++ b/sys/arm64/arm64/disassem.c @@ -73,6 +73,11 @@ static const char *shift_2[] = { "lsl", "lsr", "asr", "ror" }; +static const char *extend_types[] = { + "uxtb", "uxth", "uxtw", "uxtx", + "sxtb", "sxth", "sxtw", "sxtx", +}; + /* * Structure representing single token (operand) inside instruction. * name - name of operand @@ -107,6 +112,12 @@ enum arm64_format_type { /* OP <RT>, #imm SF32/64 */ TYPE_03, + + /* + * OP <RD>, <RN|SP>, <RM> {, <extend> { #<amount> } } + * OP <RN|SP>, <RM>, {, <extend> { #<amount> } } + */ + TYPE_04, }; /* @@ -260,6 +271,18 @@ static struct arm64_insn arm64_i[] = { TYPE_01, OP_SHIFT_ROR }, /* eon shifted register */ { "eor", "SF(1)|1001010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", TYPE_01, OP_SHIFT_ROR }, /* eor shifted register */ + { "add", "SF(1)|0001011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|RD(5)", + TYPE_04, OP_RD_SP }, /* add extended register */ + { "cmn", "SF(1)|0101011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|11111", + TYPE_04, 0 }, /* cmn extended register */ + { "adds", "SF(1)|0101011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|RD(5)", + TYPE_04, 0 }, /* adds extended register */ + { "sub", "SF(1)|1001011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|RD(5)", + TYPE_04, OP_RD_SP }, /* sub extended register */ + { "cmp", "SF(1)|1101011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|11111", + TYPE_04, 0 }, /* cmp extended register */ + { "subs", "SF(1)|1101011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|RD(5)", + TYPE_04, 0 }, /* subs extended register */ { NULL, NULL } }; @@ -409,6 +432,27 @@ arm64_disasm_read_token_sign_ext(struct arm64_insn *insn, u_int opcode, } static const char * +arm64_disasm_reg_extend(int sf, int option, int rd, int rn, int amount) +{ + bool is_sp, lsl_preferred_uxtw, lsl_preferred_uxtx, lsl_preferred; + + is_sp = rd == 31 || rn == 31; + lsl_preferred_uxtw = sf == 0 && option == 2; + lsl_preferred_uxtx = sf == 1 && option == 3; + lsl_preferred = is_sp && (lsl_preferred_uxtw || lsl_preferred_uxtx); + + /* + * LSL may be omitted when <amount> is 0. + * In all other cases <extend> is required. + */ + if (lsl_preferred && amount == 0) + return (NULL); + if (lsl_preferred) + return ("lsl"); + return (extend_types[option]); +} + +static const char * arm64_w_reg(int num, int wsp) { if (num == 31) @@ -432,6 +476,18 @@ arm64_reg(int b64, int num, int sp) return (arm64_w_reg(num, sp)); } +/* + * Decodes OPTION(3) to get <Xn|Wn> register or <WZR|XZR> + * for extended register instruction. + */ +static const char * +arm64_disasm_reg_width(int option, int reg) +{ + if (option == 3 || option == 7) + return (arm64_x_reg(reg, 0)); + return (arm64_w_reg(reg, 0)); +} + vm_offset_t disasm(const struct disasm_interface *di, vm_offset_t loc, int altfmt) { @@ -451,10 +507,13 @@ disasm(const struct disasm_interface *di, vm_offset_t loc, int altfmt) /* Indicate if shift type ror is supported */ bool has_shift_ror; + const char *extend; + /* Initialize defaults, all are 0 except SF indicating 64bit access */ shift = rd = rm = rn = imm = idx = option = amount = scale = 0; sign_ext = 0; sf = 1; + extend = NULL; matchp = 0; insn = di->di_readword(loc); @@ -669,6 +728,37 @@ disasm(const struct disasm_interface *di, vm_offset_t loc, int altfmt) di->di_printf("#%d", imm); break; + + case TYPE_04: + /* + * OP <RD>, <RN|SP>, <RM> {, <extend> { #<amount> } } + * OP <RN|SP>, <RM>, {, <extend> { #<amount> } } + */ + + arm64_disasm_read_token(i_ptr, insn, "RN", &rn); + arm64_disasm_read_token(i_ptr, insn, "RM", &rm); + arm64_disasm_read_token(i_ptr, insn, "OPTION", &option); + + rd_absent = arm64_disasm_read_token(i_ptr, insn, "RD", &rd); + extend = arm64_disasm_reg_extend(sf, option, rd, rn, imm); + + di->di_printf("%s\t", i_ptr->name); + + if (!rd_absent) + di->di_printf("%s, ", arm64_reg(sf, rd, rd_sp)); + + di->di_printf("%s, ", arm64_reg(sf, rn, 1)); + + if (sf != 0) + di->di_printf("%s", + arm64_disasm_reg_width(option, rm)); + else + di->di_printf("%s", arm64_w_reg(rm, 0)); + + if (extend != NULL) + di->di_printf(", %s #%d", extend, imm); + + break; default: goto undefined; } |