From owner-svn-src-head@freebsd.org Thu Feb 11 06:50:12 2016 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id E6670AA59DB; Thu, 11 Feb 2016 06:50:12 +0000 (UTC) (envelope-from wma@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id A49C31EAE; Thu, 11 Feb 2016 06:50:12 +0000 (UTC) (envelope-from wma@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u1B6oBdR060542; Thu, 11 Feb 2016 06:50:11 GMT (envelope-from wma@FreeBSD.org) Received: (from wma@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u1B6oBEn060541; Thu, 11 Feb 2016 06:50:11 GMT (envelope-from wma@FreeBSD.org) Message-Id: <201602110650.u1B6oBEn060541@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: wma set sender to wma@FreeBSD.org using -f From: Wojciech Macek Date: Thu, 11 Feb 2016 06:50:11 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r295505 - head/sys/arm64/arm64 X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 11 Feb 2016 06:50:13 -0000 Author: wma Date: Thu Feb 11 06:50:11 2016 New Revision: 295505 URL: https://svnweb.freebsd.org/changeset/base/295505 Log: ARM64 disassembler: support for LDR instructions Implemented disassembly for a whole bunch of various ldr instructions. Obtained from: Semihalf Sponsored by: Cavium Approved by: cognet (mentor) Reviewed by: zbb Differential revision: https://reviews.freebsd.org/D5217 Modified: head/sys/arm64/arm64/disassem.c Modified: head/sys/arm64/arm64/disassem.c ============================================================================== --- head/sys/arm64/arm64/disassem.c Thu Feb 11 06:24:34 2016 (r295504) +++ head/sys/arm64/arm64/disassem.c Thu Feb 11 06:50:11 2016 (r295505) @@ -38,6 +38,16 @@ __FBSDID("$FreeBSD$"); #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 @@ struct arm64_insn_token { 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 @@ struct arm64_insn { * 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 @@ arm64_disasm_read_token(struct arm64_ins 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 @@ disasm(const struct disasm_interface *di 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 @@ disasm(const struct disasm_interface *di 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 @@ disasm(const struct disasm_interface *di } /* 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 @@ disasm(const struct disasm_interface *di 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; }