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 <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> 
#<imm>} SF32/64
                           OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64 */
+       TYPE_02,        /* OP <RT>, [<RN>, #<imm>]{!}] SF32/64
+                          OP <RT>, [<RN>], #<imm>{!} SF32/64
+                          OP <RT>, <RN>, <RM> {, EXTEND AMOUNT } */
+       TYPE_03,        /* OP <RT>, #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 <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64
                   OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} 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 <RT>, [<RN>, #<imm>]{!}] SF32/64
+                  OP <RT>, [<RN>], #<imm>{!} SF32/64
+                  OP <RT>, <RN>, <RM> {, 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 <RT>, #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;
        }
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to