From 3425cfe54a46512a2ef35e5254c2d32e14ace69a Mon Sep 17 00:00:00 2001
From: aroulin <andy.roulin@epfl.ch>
Date: Thu, 6 Aug 2015 13:55:56 +0200
Subject: [PATCH] Disassembler: ARMv6K REX instructions

---
 src/core/arm/disassembler/arm_disasm.cpp | 92 ++++++++++++++++++++++--
 src/core/arm/disassembler/arm_disasm.h   | 11 +++
 2 files changed, 97 insertions(+), 6 deletions(-)

diff --git a/src/core/arm/disassembler/arm_disasm.cpp b/src/core/arm/disassembler/arm_disasm.cpp
index bb87043a4..8eec64d48 100644
--- a/src/core/arm/disassembler/arm_disasm.cpp
+++ b/src/core/arm/disassembler/arm_disasm.cpp
@@ -38,6 +38,7 @@ static const char *opcode_names[] = {
     "blx",
     "bx",
     "cdp",
+    "clrex",
     "clz",
     "cmn",
     "cmp",
@@ -47,6 +48,10 @@ static const char *opcode_names[] = {
     "ldr",
     "ldrb",
     "ldrbt",
+    "ldrex",
+    "ldrexb",
+    "ldrexd",
+    "ldrexh",
     "ldrh",
     "ldrsb",
     "ldrsh",
@@ -73,6 +78,10 @@ static const char *opcode_names[] = {
     "str",
     "strb",
     "strbt",
+    "strex",
+    "strexb",
+    "strexd",
+    "strexh",
     "strh",
     "strt",
     "sub",
@@ -178,6 +187,8 @@ std::string ARM_Disasm::Disassemble(uint32_t addr, uint32_t insn)
             return DisassembleBX(insn);
         case OP_CDP:
             return "cdp";
+        case OP_CLREX:
+            return "clrex";
         case OP_CLZ:
             return DisassembleCLZ(insn);
         case OP_LDC:
@@ -194,6 +205,15 @@ std::string ARM_Disasm::Disassemble(uint32_t addr, uint32_t insn)
         case OP_STRBT:
         case OP_STRT:
             return DisassembleMem(insn);
+        case OP_LDREX:
+        case OP_LDREXB:
+        case OP_LDREXD:
+        case OP_LDREXH:
+        case OP_STREX:
+        case OP_STREXB:
+        case OP_STREXD:
+        case OP_STREXH:
+            return DisassembleREX(opcode, insn);
         case OP_LDRH:
         case OP_LDRSB:
         case OP_LDRSH:
@@ -687,6 +707,36 @@ std::string ARM_Disasm::DisassemblePLD(uint32_t insn)
     }
 }
 
+std::string ARM_Disasm::DisassembleREX(Opcode opcode, uint32_t insn) {
+    uint32_t rn = BITS(insn, 16, 19);
+    uint32_t rd = BITS(insn, 12, 15);
+    uint32_t rt = BITS(insn, 0, 3);
+    uint32_t cond = BITS(insn, 28, 31);
+
+    switch (opcode) {
+        case OP_STREX:
+        case OP_STREXB:
+        case OP_STREXH:
+            return Common::StringFromFormat("%s%s\tr%d, r%d, [r%d]", opcode_names[opcode],
+                                            cond_to_str(cond), rd, rt, rn);
+        case OP_STREXD:
+            return Common::StringFromFormat("%s%s\tr%d, r%d, r%d, [r%d]", opcode_names[opcode],
+                                            cond_to_str(cond), rd, rt, rt + 1, rn);
+
+        // for LDREX instructions, rd corresponds to Rt from reference manual
+        case OP_LDREX:
+        case OP_LDREXB:
+        case OP_LDREXH:
+            return Common::StringFromFormat("%s%s\tr%d, [r%d]", opcode_names[opcode],
+                                            cond_to_str(cond), rd, rn);
+        case OP_LDREXD:
+            return Common::StringFromFormat("%s%s\tr%d, r%d, [r%d]", opcode_names[opcode],
+                                            cond_to_str(cond), rd, rd + 1, rn);
+        default:
+            return opcode_names[OP_UNDEFINED];
+    }
+}
+
 std::string ARM_Disasm::DisassembleSWI(uint32_t insn)
 {
     uint8_t cond = (insn >> 28) & 0xf;
@@ -739,12 +789,9 @@ Opcode ARM_Disasm::Decode00(uint32_t insn) {
         }
         uint32_t bits7_4 = (insn >> 4) & 0xf;
         if (bits7_4 == 0x9) {
-            if ((insn & 0x0ff00ff0) == 0x01000090) {
-                // Swp instruction
-                uint8_t bit22 = (insn >> 22) & 0x1;
-                if (bit22)
-                    return OP_SWPB;
-                return OP_SWP;
+            uint32_t bit24 = BIT(insn, 24);
+            if (bit24) {
+                return DecodeSyncPrimitive(insn);
             }
             // One of the multiply instructions
             return DecodeMUL(insn);
@@ -778,6 +825,10 @@ Opcode ARM_Disasm::Decode01(uint32_t insn) {
         // Pre-load
         return OP_PLD;
     }
+    if (insn == 0xf57ff01f) {
+        // Clear-Exclusive
+        return OP_CLREX;
+    }
     if (is_load) {
         if (is_byte) {
             // Load byte
@@ -868,6 +919,35 @@ Opcode ARM_Disasm::Decode11(uint32_t insn) {
     return OP_MCR;
 }
 
+Opcode ARM_Disasm::DecodeSyncPrimitive(uint32_t insn) {
+    uint32_t op = BITS(insn, 20, 23);
+    uint32_t bit22 = BIT(insn, 22);
+    switch (op) {
+        case 0x0:
+            if (bit22)
+                return OP_SWPB;
+            return OP_SWP;
+        case 0x8:
+            return OP_STREX;
+        case 0x9:
+            return OP_LDREX;
+        case 0xA:
+            return OP_STREXD;
+        case 0xB:
+            return OP_LDREXD;
+        case 0xC:
+            return OP_STREXB;
+        case 0xD:
+            return OP_LDREXB;
+        case 0xE:
+            return OP_STREXH;
+        case 0xF:
+            return OP_LDREXH;
+        default:
+            return OP_UNDEFINED;
+    }
+}
+
 Opcode ARM_Disasm::DecodeMUL(uint32_t insn) {
     uint8_t bit24 = (insn >> 24) & 0x1;
     if (bit24 != 0) {
diff --git a/src/core/arm/disassembler/arm_disasm.h b/src/core/arm/disassembler/arm_disasm.h
index a4e4adf2f..d04fd21eb 100644
--- a/src/core/arm/disassembler/arm_disasm.h
+++ b/src/core/arm/disassembler/arm_disasm.h
@@ -20,6 +20,7 @@ enum Opcode {
     OP_BLX,
     OP_BX,
     OP_CDP,
+    OP_CLREX,
     OP_CLZ,
     OP_CMN,
     OP_CMP,
@@ -29,6 +30,10 @@ enum Opcode {
     OP_LDR,
     OP_LDRB,
     OP_LDRBT,
+    OP_LDREX,
+    OP_LDREXB,
+    OP_LDREXD,
+    OP_LDREXH,
     OP_LDRH,
     OP_LDRSB,
     OP_LDRSH,
@@ -55,6 +60,10 @@ enum Opcode {
     OP_STR,
     OP_STRB,
     OP_STRBT,
+    OP_STREX,
+    OP_STREXB,
+    OP_STREXD,
+    OP_STREXH,
     OP_STRH,
     OP_STRT,
     OP_SUB,
@@ -122,6 +131,7 @@ class ARM_Disasm {
   static Opcode Decode01(uint32_t insn);
   static Opcode Decode10(uint32_t insn);
   static Opcode Decode11(uint32_t insn);
+  static Opcode DecodeSyncPrimitive(uint32_t insn);
   static Opcode DecodeMUL(uint32_t insn);
   static Opcode DecodeMSRImmAndHints(uint32_t insn);
   static Opcode DecodeLDRH(uint32_t insn);
@@ -143,6 +153,7 @@ class ARM_Disasm {
   static std::string DisassembleMSR(uint32_t insn);
   static std::string DisassembleNoOperands(Opcode opcode, uint32_t insn);
   static std::string DisassemblePLD(uint32_t insn);
+  static std::string DisassembleREX(Opcode opcode, uint32_t insn);
   static std::string DisassembleSWI(uint32_t insn);
   static std::string DisassembleSWP(Opcode opcode, uint32_t insn);
 };