svn commit: r295505 - head/sys/arm64/arm64

Wojciech Macek wma at FreeBSD.org
Thu Feb 11 06:50:13 UTC 2016


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;
 	}


More information about the svn-src-head mailing list