Index: head/sys/arm64/arm64/disassem.c =================================================================== --- head/sys/arm64/arm64/disassem.c +++ head/sys/arm64/arm64/disassem.c @@ -38,6 +38,16 @@ #define ARM64_MAX_TOKEN_LEN 8 #define ARM64_MAX_TOKEN_CNT 10 +#define ARM_INSN_SIZE_OFFSET 30 +#define ARM_INSN_SIZE_MASK 0x3 + +/* Special options for instruction printing */ +#define OP_SIGN_EXT (1UL << 0) /* Sign-extend immediate value */ +#define OP_LITERAL (1UL << 1) /* Use literal (memory offset) */ +#define OP_MULT_4 (1UL << 2) /* Multiply immediate by 4 */ +#define OP_SF32 (1UL << 3) /* Force 32-bit access */ +#define OP_SF_INV (1UL << 6) /* SF is inverted (1 means 32 bit access) */ + static const char *w_reg[] = { "w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7", "w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15", @@ -74,6 +84,10 @@ enum arm64_format_type { TYPE_01, /* OP , , {, #} SF32/64 OP , , #{, } SF32/64 */ + TYPE_02, /* OP , [, #]{!}] SF32/64 + OP , [], #{!} SF32/64 + OP , , {, EXTEND AMOUNT } */ + TYPE_03, /* OP , #imm SF32/64 */ }; /* @@ -112,12 +126,57 @@ * SHIFT - type of shift (instruction dependent) * IMM - immediate value * Rx - register number + * OPTION - command specific options + * SCALE - scaling of immediate value */ static struct arm64_insn arm64_i[] = { - { "add", "SF(1)|0001011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", TYPE_01, 0 }, - { "mov", "SF(1)|001000100000000000000|RN(5)|RD(5)", TYPE_01, 0 }, - { "add", "SF(1)|0010001|SHIFT(2)|IMM(12)|RN(5)|RD(5)", TYPE_01, 0 }, - { NULL, NULL } + { "add", "SF(1)|0001011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", + TYPE_01, 0 }, + { "mov", "SF(1)|001000100000000000000|RN(5)|RD(5)", + TYPE_01, 0 }, + { "add", "SF(1)|0010001|SHIFT(2)|IMM(12)|RN(5)|RD(5)", + TYPE_01, 0 }, + { "ldr", "1|SF(1)|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)", + TYPE_02, OP_SIGN_EXT }, /* ldr immediate post/pre index */ + { "ldr", "1|SF(1)|11100101|IMM(12)|RN(5)|RT(5)", + TYPE_02, 0 }, /* ldr immediate unsigned */ + { "ldr", "1|SF(1)|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", + TYPE_02, 0 }, /* ldr register */ + { "ldr", "0|SF(1)|011000|IMM(19)|RT(5)", + TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 }, /* ldr literal */ + { "ldrb", "00|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)", + TYPE_02, OP_SIGN_EXT | OP_SF32 }, /* ldrb immediate post/pre index */ + { "ldrb", "00|11100101|IMM(12)|RN(5)|RT(5)", + TYPE_02, OP_SF32 }, /* ldrb immediate unsigned */ + { "ldrb", "00|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", + TYPE_02, OP_SF32 }, /* ldrb register */ + { "ldrh", "01|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)", TYPE_02, + OP_SIGN_EXT | OP_SF32 }, /* ldrh immediate post/pre index */ + { "ldrh", "01|11100101|IMM(12)|RN(5)|RT(5)", + TYPE_02, OP_SF32 }, /* ldrh immediate unsigned */ + { "ldrh", "01|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", + TYPE_02, OP_SF32 }, /* ldrh register */ + { "ldrsb", "001110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)", + TYPE_02, OP_SIGN_EXT | OP_SF_INV }, /* ldrsb immediate post/pre index */ + { "ldrsb", "001110011|SF(1)|IMM(12)|RN(5)|RT(5)",\ + TYPE_02, OP_SF_INV}, /* ldrsb immediate unsigned */ + { "ldrsb", "001110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", + TYPE_02, OP_SF_INV }, /* ldrsb register */ + { "ldrsh", "011110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)", + TYPE_02, OP_SIGN_EXT | OP_SF_INV }, /* ldrsh immediate post/pre index */ + { "ldrsh", "011110011|SF(1)|IMM(12)|RN(5)|RT(5)", + TYPE_02, OP_SF_INV}, /* ldrsh immediate unsigned */ + { "ldrsh", "011110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", + TYPE_02, OP_SF_INV }, /* ldrsh register */ + { "ldrsw", "10111000100|IMM(9)|OPTION(2)|RN(5)|RT(5)", + TYPE_02, OP_SIGN_EXT }, /* ldrsw immediate post/pre index */ + { "ldrsw", "1011100110|IMM(12)|RN(5)|RT(5)", + TYPE_02, 0 }, /* ldrsw immediate unsigned */ + { "ldrsw", "10111000101|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)", + TYPE_02, 0 }, /* ldrsw register */ + { "ldrsw", "10011000|IMM(19)|RT(5)", + TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 }, /* ldr literal */ + { NULL, NULL } }; static void @@ -240,6 +299,29 @@ return (EINVAL); } +static int +arm64_disasm_read_token_sign_ext(struct arm64_insn *insn, u_int opcode, + const char *token, int *val) +{ + int i; + int msk; + + for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) { + if (strcmp(insn->tokens[i].name, token) == 0) { + msk = (1 << insn->tokens[i].len) - 1; + *val = ((opcode >> insn->tokens[i].pos) & msk); + + /* If last bit is 1, sign-extend the value */ + if (*val & (1 << (insn->tokens[i].len - 1))) + *val |= ~msk; + + return (0); + } + } + + return (EINVAL); +} + static const char * arm64_reg(int b64, int num) { @@ -257,11 +339,17 @@ uint32_t insn; int matchp; int ret; - int shift, rm, rd, rn, imm, sf; + int shift, rm, rt, rd, rn, imm, sf, idx, option, scale, amount; + int sign_ext; int rm_absent; + /* Indicate if immediate should be outside or inside brackets */ + int inside; + /* Print exclamation mark if pre-incremented */ + int pre; /* Initialize defaults, all are 0 except SF indicating 64bit access */ - shift = rd = rm = rn = imm = 0; + shift = rd = rm = rn = imm = idx = option = amount = scale = 0; + sign_ext = 0; sf = 1; matchp = 0; @@ -278,14 +366,33 @@ if (matchp == 0) goto undefined; + /* Global options */ + if (i_ptr->special_ops & OP_SF32) + sf = 0; + + /* Global optional tokens */ + arm64_disasm_read_token(i_ptr, insn, "SF", &sf); + if (i_ptr->special_ops & OP_SF_INV) + sf = 1 - sf; + if (arm64_disasm_read_token(i_ptr, insn, "SIGN", &sign_ext) == 0) + sign_ext = 1 - sign_ext; + if (i_ptr->special_ops & OP_SIGN_EXT) + sign_ext = 1; + if (sign_ext != 0) + arm64_disasm_read_token_sign_ext(i_ptr, insn, "IMM", &imm); + else + arm64_disasm_read_token(i_ptr, insn, "IMM", &imm); + if (i_ptr->special_ops & OP_MULT_4) + imm <<= 2; + + /* Print opcode by type */ switch (i_ptr->type) { case TYPE_01: /* OP , , {, #} SF32/64 OP , , #{, } SF32/64 */ /* Mandatory tokens */ - ret = arm64_disasm_read_token(i_ptr, insn, "SF", &sf); - ret |= arm64_disasm_read_token(i_ptr, insn, "RD", &rd); + ret = arm64_disasm_read_token(i_ptr, insn, "RD", &rd); ret |= arm64_disasm_read_token(i_ptr, insn, "RN", &rn); if (ret != 0) { printf("ERROR: Missing mandatory token for op %s type %d\n", @@ -294,7 +401,6 @@ } /* Optional tokens */ - arm64_disasm_read_token(i_ptr, insn, "IMM", &imm); arm64_disasm_read_token(i_ptr, insn, "SHIFT", &shift); rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm); @@ -313,6 +419,115 @@ di->di_printf(" LSL #12"); } break; + case TYPE_02: + /* OP , [, #]{!}] SF32/64 + OP , [], #{!} SF32/64 + OP , , {, EXTEND AMOUNT } */ + + /* Mandatory tokens */ + ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt); + ret |= arm64_disasm_read_token(i_ptr, insn, "RN", &rn); + if (ret != 0) { + printf("ERROR: Missing mandatory token for op %s type %d\n", + i_ptr->name, i_ptr->type); + goto undefined; + } + + /* Optional tokens */ + arm64_disasm_read_token(i_ptr, insn, "OPTION", &option); + arm64_disasm_read_token(i_ptr, insn, "SCALE", &scale); + rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm); + + if (rm_absent) { + /* + * In unsigned operation, shift immediate value + * and reset options to default. + */ + if (sign_ext == 0) { + imm = imm << ((insn >> ARM_INSN_SIZE_OFFSET) & + ARM_INSN_SIZE_MASK); + option = 0; + } + switch (option) { + case 0x0: + pre = 0; + inside = 1; + break; + case 0x1: + pre = 0; + inside = 0; + break; + case 0x2: + default: + pre = 1; + inside = 1; + break; + } + + di->di_printf("%s\t%s, ", i_ptr->name, arm64_reg(sf, rt)); + if (inside != 0) { + di->di_printf("[%s", arm64_reg(1, rn)); + if (imm != 0) + di->di_printf(", #%d", imm); + di->di_printf("]"); + } else { + di->di_printf("[%s]", arm64_reg(1, rn)); + if (imm != 0) + di->di_printf(", #%d", imm); + } + if (pre != 0) + di->di_printf("!"); + } else { + /* Last bit of option field determines 32/64 bit offset */ + di->di_printf("%s\t%s, [%s, %s", i_ptr->name, + arm64_reg(sf, rt), arm64_reg(1, rn), + arm64_reg(option & 1, rm)); + + /* Calculate amount, it's op(31:30) */ + amount = (insn >> ARM_INSN_SIZE_OFFSET) & + ARM_INSN_SIZE_MASK; + + switch (option) { + case 0x2: + di->di_printf(", uxtw #%d", amount); + break; + case 0x3: + if (scale != 0) + di->di_printf(", lsl #%d", amount); + break; + case 0x6: + di->di_printf(", sxtw #%d", amount); + break; + case 0x7: + di->di_printf(", sxts #%d", amount); + break; + default: + di->di_printf(", RSVD"); + break; + } + di->di_printf("]"); + } + + break; + + case TYPE_03: + /* OP , #imm SF32/64 */ + + /* Mandatory tokens */ + ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt); + if (ret != 0) { + printf("ERROR: Missing mandatory token for op %s type %d\n", + i_ptr->name, i_ptr->type); + goto undefined; + } + + di->di_printf("%s\t%s, ", i_ptr->name, arm64_reg(sf, rt)); + if (i_ptr->special_ops & OP_LITERAL) + di->di_printf("0x%lx", loc + imm); + else + di->di_printf("#%d", imm); + + break; default: goto undefined; }