Provide implementation for the arch-dependent functions that are called by the 
main check
function of objtool.
The ORC unwinder is not yet supported by the arm64 architecture so we only 
provide a dummy
interface for now.
The decoding of the instruction is split into classes and subclasses as 
described into
the Instruction Encoding in the ArmV8.5 Architecture Reference Manual.

Signed-off-by: Raphael Gault <[email protected]>
---
 tools/objtool/arch/arm64/Build                |    6 +
 tools/objtool/arch/arm64/bit_operations.c     |   65 +
 tools/objtool/arch/arm64/decode.c             | 2843 +++++++++++++++++
 .../objtool/arch/arm64/include/arch_special.h |   44 +
 .../arch/arm64/include/asm/orc_types.h        |  109 +
 .../arch/arm64/include/bit_operations.h       |   22 +
 tools/objtool/arch/arm64/include/cfi.h        |   76 +
 .../objtool/arch/arm64/include/insn_decode.h  |  219 ++
 tools/objtool/arch/arm64/orc_gen.c            |   40 +
 9 files changed, 3424 insertions(+)
 create mode 100644 tools/objtool/arch/arm64/Build
 create mode 100644 tools/objtool/arch/arm64/bit_operations.c
 create mode 100644 tools/objtool/arch/arm64/decode.c
 create mode 100644 tools/objtool/arch/arm64/include/arch_special.h
 create mode 100644 tools/objtool/arch/arm64/include/asm/orc_types.h
 create mode 100644 tools/objtool/arch/arm64/include/bit_operations.h
 create mode 100644 tools/objtool/arch/arm64/include/cfi.h
 create mode 100644 tools/objtool/arch/arm64/include/insn_decode.h
 create mode 100644 tools/objtool/arch/arm64/orc_gen.c

diff --git a/tools/objtool/arch/arm64/Build b/tools/objtool/arch/arm64/Build
new file mode 100644
index 000000000000..deac494a30d3
--- /dev/null
+++ b/tools/objtool/arch/arm64/Build
@@ -0,0 +1,6 @@
+objtool-y += decode.o
+objtool-y += orc_gen.o
+objtool-y += bit_operations.o
+
+
+CFLAGS_decode.o += -I$(OUTPUT)arch/arm64/lib
diff --git a/tools/objtool/arch/arm64/bit_operations.c 
b/tools/objtool/arch/arm64/bit_operations.c
new file mode 100644
index 000000000000..02429ede1519
--- /dev/null
+++ b/tools/objtool/arch/arm64/bit_operations.c
@@ -0,0 +1,65 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "bit_operations.h"
+
+#include "../../warn.h"
+
+u64 replicate(u64 x, int size, int n)
+{
+       u64 ret = 0;
+       while (n >= 0) {
+               ret = (ret | x) << size;
+               n--;
+       }
+       return ret | x;
+}
+
+u64 ror(u64 x, int size, int shift)
+{
+       int m = shift % size;
+       if (shift == 0)
+               return x;
+       return ZERO_EXTEND((x >> m) | (x << (size - m)), size);
+}
+
+int highest_set_bit(u32 x)
+{
+       int i;
+       for (i = 31; i >= 0; i--, x <<= 1)
+               if (x & 0x80000000)
+                       return i;
+       return 0;
+}
+
+/* imms and immr are both 6 bit long */
+__uint128_t decode_bit_masks(unsigned char N, unsigned char imms,
+                       unsigned char immr, bool immediate)
+{
+       u64 tmask, wmask;
+       u32 diff, S, R, esize, welem, telem;
+       unsigned char levels = 0, len = 0;
+
+       len = highest_set_bit((N << 6) | ((~imms) & ONES(6)));
+       levels = ZERO_EXTEND(ONES(len), 6);
+
+       if (immediate && ((imms & levels) == levels)) {
+               WARN("unknown instruction");
+               return -1;
+       }
+
+       S = imms & levels;
+       R = immr & levels;
+       diff = ZERO_EXTEND(S - R, 6);
+
+       esize = 1 << len;
+       diff = diff & ONES(len);
+
+       welem = ZERO_EXTEND(ONES(S + 1), esize);
+       telem = ZERO_EXTEND(ONES(diff + 1), esize);
+
+       wmask = replicate(ror(welem, esize, R), esize, 64 / esize);
+       tmask = replicate(telem, esize, 64 / esize);
+
+       return ((__uint128_t)wmask << 64) | tmask;
+
+}
diff --git a/tools/objtool/arch/arm64/decode.c 
b/tools/objtool/arch/arm64/decode.c
new file mode 100644
index 000000000000..0feb3ae3af5d
--- /dev/null
+++ b/tools/objtool/arch/arm64/decode.c
@@ -0,0 +1,2843 @@
+/*
+ * Copyright (C) 2019 Raphael Gault <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "insn_decode.h"
+#include "cfi.h"
+#include "bit_operations.h"
+
+#include "../../check.h"
+#include "../../arch.h"
+#include "../../elf.h"
+#include "../../warn.h"
+
+
+/*static int (*arm_decode_class)(u32 instr, unsigned int *len, unsigned char 
*type,
+                               unsigned long *immediate, struct stack_op 
*op);*/
+static arm_decode_class aarch64_insn_class_decode_table[] = {
+       [INSN_RESERVED]                 = arm_decode_reserved,
+       [INSN_UNKNOWN]                  = arm_decode_unknown,
+       [INSN_SVE_ENC]                  = arm_decode_sve_encoding,
+       [INSN_UNALLOC]                  = arm_decode_unknown,
+       [INSN_LD_ST_4]                  = arm_decode_ld_st,
+       [INSN_DP_REG_5]                 = arm_decode_dp_reg,
+       [INSN_LD_ST_6]                  = arm_decode_ld_st,
+       [INSN_DP_SIMD_7]                = arm_decode_dp_simd,
+       [0b1000 ... INSN_DP_IMM]        = arm_decode_dp_imm,
+       [0b1010 ... INSN_BRANCH]        = arm_decode_br_sys,
+       [INSN_LD_ST_C]                  = arm_decode_ld_st,
+       [INSN_DP_REG_D]                 = arm_decode_dp_reg,
+       [INSN_LD_ST_E]                  = arm_decode_ld_st,
+       [INSN_DP_SIMD_F]                = arm_decode_dp_simd,
+};
+
+static arm_decode_class aarch64_insn_dp_imm_decode_table[] = {
+       [0 ... INSN_PCREL]      = arm_decode_pcrel,
+       [INSN_ADD_SUB]          = arm_decode_add_sub,
+       [INSN_ADD_TAG]          = arm_decode_add_sub_tags,
+       [INSN_LOGICAL]          = arm_decode_logical,
+       [INSN_MOVE_WIDE]        = arm_decode_move_wide,
+       [INSN_BITFIELD]         = arm_decode_bitfield,
+       [INSN_EXTRACT]          = arm_decode_extract,
+};
+
+bool arch_callee_saved_reg(unsigned char reg)
+{
+       switch(reg){
+               case CFI_R19:
+               case CFI_R20:
+               case CFI_R21:
+               case CFI_R22:
+               case CFI_R23:
+               case CFI_R24:
+               case CFI_R25:
+               case CFI_R26:
+               case CFI_R27:
+               case CFI_R28:
+               case CFI_FP:
+               case CFI_R30:
+                       return true;
+               default:
+                       return false;
+       }
+}
+
+
+void arch_initial_func_cfi_state(struct cfi_state *state)
+{
+       int i;
+
+       for (i = 0; i < CFI_NUM_REGS; i++) {
+               state->regs[i].base = CFI_UNDEFINED;
+               state->regs[i].offset = 0;
+       }
+
+       /* initial CFA (call frame address) */
+       state->cfa.base = CFI_CFA;
+       state->cfa.offset = 0;
+
+       /* initial RA (return address) */
+       state->regs[CFI_LR].base = CFI_CFA;
+       state->regs[CFI_LR].offset = -8;
+
+
+}
+
+unsigned long arch_compute_rela_sym_offset(int addend)
+{
+       return addend;
+}
+
+static int is_arm64(struct elf *elf)
+{
+       switch(elf->ehdr.e_machine){
+               case EM_AARCH64: //0xB7
+                       return 1;
+               default:
+                       WARN("unexpected ELF machine type %x", 
elf->ehdr.e_machine);
+                       return 0;
+       }
+}
+
+/*
+ * Arm A64 Instruction set' decode groups (based on op0 bits[28:25]):
+ * Ob0000 - Reserved
+ * 0b0001/0b001x - Unallocated
+ * 0b100x - Data Processing -- Immediate
+ * 0b101x - Branch, Exception Gen., System Instructions.
+ * 0bx1x0 - Loads and Stores
+ * 0bx101 - Data Processing -- Registers
+ * 0bx111 - Data Processing -- Scalar Floating-Points, Advanced SIMD
+ */
+
+int arch_decode_instruction(struct elf *elf, struct section *sec,
+                       unsigned long offset, unsigned int maxlen,
+                       unsigned int *len, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       int arm64 = 0, ret = 0;
+       u32 insn = 0;
+
+       *len = 4;
+       *immediate = 0;
+
+       op->dest.type = 0;
+       op->dest.reg = 0;
+       op->dest.offset = 0;
+       op->src.type = 0;
+       op->src.reg = 0;
+       op->src.offset = 0;
+
+       //test architucture (make sure it is arm64)
+       arm64 = is_arm64(elf);
+       if (arm64 != 1)
+               return -1;
+
+       //retrieve instruction (from sec->data->offset)
+       insn = *(u32*)(sec->data->d_buf + offset);
+
+       //dispatch according to encoding classes
+       ret = aarch64_insn_class_decode_table[(insn >> 25) & 0xf](insn, type,
+                                                       immediate, op);
+       /*
+       * For instruction that do operations on multiple registers at a time,
+       * like store/load of pairs of registers, we decompose the instruction
+       * into several individual instruction to be able to track the state.
+       * This is useful for PUSH/POP kind of scenarios.
+       * We thus set the len at 0 so that we re-decode the same instruction
+       * again until we have gone to every steps.
+       */
+       if (ret == INSN_COMPOSED) {
+               *len = 0;
+               ret = 0;
+       }
+       return ret;
+}
+
+int arm_decode_unknown(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       *type = 0;
+       return 0;
+}
+
+int arm_decode_dp_simd(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       *type = INSN_OTHER;
+       return 0;
+}
+
+int arm_decode_reserved(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       *immediate = instr & ONES(16);
+       *type = INSN_BUG;
+       return 0;
+}
+
+int arm_decode_dp_imm(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       return aarch64_insn_dp_imm_decode_table[(instr >> 23) & 0x7](instr,
+                                                       type, immediate, op);
+
+}
+
+int arm_decode_pcrel(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char rd = 0, page = 0;
+       u32 immhi = 0, immlo = 0;
+
+       page = EXTRACT_BIT(instr, 31);
+       rd = instr & 0x1F;
+       immhi = (instr >> 5) & ONES(19);
+       immlo = (instr >> 29) & ONES(2);
+
+       *immediate = SIGN_EXTEND((immhi << 2) | immlo, 21);
+
+       if (page)
+               *immediate = SIGN_EXTEND(*immediate << 12, 33);
+
+       *type = INSN_OTHER;
+       op->src.reg = ADR_SOURCE;
+       op->dest.reg = rd;
+
+       return 0;
+}
+
+int arm_decode_add_sub(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned long imm12 = 0, imm = 0;
+       unsigned char sf = 0, sh = 0, S = 0, op_bit = 0;
+       unsigned char rn = 0, rd = 0;
+
+       S = EXTRACT_BIT(instr, 29);
+       op_bit = EXTRACT_BIT(instr, 30);
+       sf = EXTRACT_BIT(instr, 31);
+       sh = EXTRACT_BIT(instr, 22);
+       rd = instr & ONES(5);
+       rn = (instr >> 5) & ONES(5);
+       imm12 = (instr >> 10) & ONES(12);
+       imm = ZERO_EXTEND(imm12 << (sh * 12), (sf + 1) * 32);
+
+       *type = INSN_OTHER;
+
+       if ((!S && rd == CFI_SP) || rn == CFI_SP) {
+               *type = INSN_STACK;
+               op->dest.type = OP_DEST_REG;
+               op->dest.offset = 0;
+               op->dest.reg = rd;
+               op->src.type = imm12 ? OP_SRC_ADD: OP_SRC_REG;
+               op->src.offset = op_bit ? -1 * imm : imm;
+               op->src.reg = rn;
+       }
+       return 0;
+}
+
+int arm_decode_add_sub_tags(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char decode_field = 0, rn = 0, rd = 0, uimm6 = 0;
+
+       decode_field = (instr >> 29) & ONES(3);
+       rd = instr & ONES(5);
+       rn = (instr >> 5) & ONES(5);
+       uimm6 = (instr >> 16) & ONES(6);
+
+       *immediate = uimm6;
+       *type = INSN_OTHER;
+
+#define ADDG_DECODE    4
+#define SUBG_DECODE    5
+       if (decode_field != ADDG_DECODE && decode_field != SUBG_DECODE)
+               return arm_decode_unknown(instr, type, immediate, op);
+
+#undef ADDG_DECODE
+#undef SUBG_DECODE
+       op->dest.type = OP_DEST_REG;
+       op->dest.offset = 0;
+       op->dest.reg = rd;
+       op->src.type = OP_SRC_ADD;
+       op->src.offset = 0;
+       op->src.reg = rn;
+
+       if (rd == CFI_SP)
+               *type = INSN_STACK;
+
+       return 0;
+}
+
+int arm_decode_logical(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char sf = 0, opc = 0, N = 0;
+       unsigned char imms = 0, immr = 0, rn = 0, rd = 0;
+
+       rd = instr & ONES(5);
+       rn = (instr >> 5) & ONES(5);
+
+       imms = (instr >> 10) & ONES(6);
+       immr = (instr >> 16) & ONES(6);
+
+       N = EXTRACT_BIT(instr, 22);
+       opc = (instr >> 29) & ONES(2);
+       sf = EXTRACT_BIT(instr, 31);
+
+
+       if (N == 1 && sf == 0)
+               return arm_decode_unknown(instr, type, immediate, op);
+
+       *type = INSN_OTHER;
+       *immediate = (decode_bit_masks(N, imms, immr, true) >> 64);
+#define ANDS_DECODE    0b11
+       if (opc == ANDS_DECODE)
+               return 0;
+#undef ANDS_DECODE
+       if (rd == CFI_SP) {
+               *type = INSN_STACK;
+               op->dest.type = OP_DEST_REG;
+               op->dest.offset = 0;
+               op->dest.reg = CFI_SP;
+
+               op->src.type = OP_SRC_AND;
+               op->src.offset = 0;
+               op->src.reg = rn;
+       }
+
+       return 0;
+}
+
+int arm_decode_move_wide(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       u32 imm16 = 0;
+       unsigned char hw = 0, opc = 0, sf = 0;
+
+       sf = EXTRACT_BIT(instr, 31);
+       opc = (instr >> 29) & ONES(2);
+       hw = (instr >> 21) & ONES(2);
+       imm16 = (instr >> 5) & ONES(16);
+
+       if ((sf == 0 && (hw & 0x2)) || opc == 0x1)
+               return arm_decode_unknown(instr, type, immediate, op);
+
+       *type = INSN_OTHER;
+       *immediate = imm16;
+
+       return 0;
+}
+
+
+int arm_decode_bitfield(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char sf = 0, opc = 0, N = 0;
+
+       sf = EXTRACT_BIT(instr, 31);
+       opc = (instr >> 29) & ONES(2);
+       N = EXTRACT_BIT(instr, 22);
+
+       if ((opc == 0x3) || (sf != N))
+               return arm_decode_unknown(instr, type, immediate, op);
+
+       *type = INSN_OTHER;
+
+       return 0;
+}
+
+int arm_decode_extract(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char sf = 0, op21 = 0, N = 0, o0 = 0;
+       unsigned char imms = 0;
+       unsigned char decode_field = 0;
+
+       sf = EXTRACT_BIT(instr, 31);
+       op21 = (instr >> 29) & ONES(2);
+       N = EXTRACT_BIT(instr, 22);
+       o0 = EXTRACT_BIT(instr, 21);
+       imms = (instr >> 10) & ONES(6);
+
+       decode_field = (sf << 4) | (op21 << 2) | (N << 1) | o0;
+       *type = INSN_OTHER;
+       *immediate = imms;
+
+       if ((decode_field == 0 && !EXTRACT_BIT(imms, 5))
+               || decode_field == 0b10010)
+               return 0;
+
+       return arm_decode_unknown(instr, type, immediate, op);
+}
+
+static struct aarch64_insn_decoder br_sys_decoder[] = {
+       {
+               .mask = 0b1111000000000000000000,
+               .value = 0b0100000000000000000000,
+               .decode_func = arm_decode_br_cond_imm,
+       },
+       {
+               .mask = 0b1111100000000000000000,
+               .value = 0b1100000000000000000000,
+               .decode_func = arm_decode_except_gen,
+       },
+       {
+               .mask = 0b1111111111111111111111,
+               .value = 0b1100100000011001011111,
+               .decode_func = arm_decode_hints,
+       },
+       {
+               .mask = 0b1111111111111111100000,
+               .value = 0b1100100000011001100000,
+               .decode_func = arm_decode_barriers,
+       },
+       {
+               .mask = 0b1111111111000111100000,
+               .value = 0b1100100000000010000000,
+               .decode_func = arm_decode_pstate,
+       },
+       {
+               .mask = 0b1111111011000000000000,
+               .value = 0b1100100001000000000000,
+               .decode_func = arm_decode_system_insn,
+       },
+       {
+               .mask = 0b1111111010000000000000,
+               .value = 0b1100100010000000000000,
+               .decode_func = arm_decode_system_regs,
+       },
+       {
+               .mask = 0b1111000000000000000000,
+               .value = 0b1101000000000000000000,
+               .decode_func = arm_decode_br_uncond_reg,
+       },
+       {
+               .mask = 0b0110000000000000000000,
+               .value = 0b0000000000000000000000,
+               .decode_func = arm_decode_br_uncond_imm,
+       },
+       {
+               .mask = 0b0111000000000000000000,
+               .value = 0b0010000000000000000000,
+               .decode_func = arm_decode_br_comp_imm,
+       },
+       {
+               .mask = 0b0111000000000000000000,
+               .value = 0b0011000000000000000000,
+               .decode_func = arm_decode_br_tst_imm,
+       },
+
+};
+
+int arm_decode_br_sys(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       u32 decode_field = 0, op1 = 0;
+       unsigned char op0 = 0, op2 = 0;
+       int i = 0;
+
+       op0 = (instr >> 29) & ONES(3);
+       op1 = (instr >> 12) & ONES(14);
+       op2 = instr & ONES(5);
+
+       decode_field = op0;
+       decode_field = (decode_field << 19) | (op1 << 5) | op2;
+
+       for (i = 0; i < ARRAY_SIZE(br_sys_decoder); i++) {
+               if ((decode_field & br_sys_decoder[i].mask) == 
br_sys_decoder[i].value) {
+                       return br_sys_decoder[i].decode_func(instr,
+                                                       type, immediate, op);
+               }
+       }
+
+       return arm_decode_unknown(instr, type, immediate, op);
+}
+
+
+int arm_decode_br_cond_imm(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char o0 = 0, o1 = 0;
+       u32 imm19;
+
+       o0 = EXTRACT_BIT(instr, 4);
+       o1 = EXTRACT_BIT(instr, 24);
+       imm19 = (instr >> 5) & ONES(19);
+
+       *immediate = SIGN_EXTEND(imm19 << 2, 19);
+
+       if ((o1 << 1) | o0)
+               return arm_decode_unknown(instr, type, immediate, op);
+
+       *type = INSN_JUMP_CONDITIONAL;
+
+       return 0;
+
+}
+
+static struct aarch64_insn_decoder except_gen_decoder[] = {
+       {
+               .mask = 0b00000100,
+               .value = 0b00000100,
+       },
+       {
+               .mask = 0b00001000,
+               .value = 0b00001000,
+       },
+       {
+               .mask = 0b00010000,
+               .value = 0b00010000,
+       },
+       {
+               .mask = 0b11111111,
+               .value = 0b00000000,
+       },
+       {
+               .mask = 0b11111101,
+               .value = 0b00100001,
+       },
+       {
+               .mask = 0b11111110,
+               .value = 0b00100010,
+       },
+       {
+               .mask = 0b11111101,
+               .value = 0b01000001,
+       },
+       {
+               .mask = 0b11111110,
+               .value = 0b01000010,
+       },
+       {
+               .mask = 0b11111111,
+               .value = 0b01100001,
+       },
+       {
+               .mask = 0b11111110,
+               .value = 0b01100010,
+       },
+       {
+               .mask = 0b11111111,
+               .value = 0b10000000,
+       },
+       {
+               .mask = 0b11111111,
+               .value = 0b10100000,
+       },
+       {
+               .mask = 0b11111100,
+               .value = 0b11000000,
+       },
+       {
+               .mask = 0b11111111,
+               .value = 0b11100001,
+       },
+       {
+               .mask = 0b11111110,
+               .value = 0b11100010,
+       },
+};
+
+int arm_decode_except_gen(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       u32 imm16 = 0;
+       unsigned char opc = 0, op2 = 0, LL = 0, decode_field = 0;
+       int i = 0;
+
+       imm16 = (instr >> 5) & ONES(16);
+       opc = (instr >> 21) & ONES(3);
+       op2 = (instr >> 2) & ONES(3);
+       LL = instr & ONES(2);
+       decode_field = (opc << 5) | (op2 << 2) | LL;
+
+       for (i = 0; i < ARRAY_SIZE(except_gen_decoder); i++) {
+               if ((decode_field & except_gen_decoder[i].mask)
+                               == except_gen_decoder[i].value) {
+                       return arm_decode_unknown(instr, type, immediate, op);
+               }
+       }
+
+#define INSN_SVC       0b00000001
+#define INSN_HVC       0b00000010
+#define INSN_SMC       0b00000011
+#define INSN_BRK       0b00100000
+#define INSN_HLT       0b01000000
+#define INSN_DCPS1     0b10100001
+#define INSN_DCPS2     0b10100010
+#define INSN_DCPS3     0b10100011
+
+       switch(decode_field){
+               case INSN_SVC:
+               case INSN_HVC:
+               case INSN_SMC:
+                       *immediate = imm16;
+                       *type = INSN_CONTEXT_SWITCH;
+                       return 0;
+               case INSN_BRK:
+                       if (imm16 == 0x800)
+                               *type = INSN_BUG;
+                       else if (imm16 == 0x100 || imm16 >= 0x900)
+                               *type = INSN_CONTEXT_SWITCH;
+                       else
+                               *type = INSN_OTHER;
+                       return 0;
+               case INSN_HLT:
+               case INSN_DCPS1:
+               case INSN_DCPS2:
+               case INSN_DCPS3:
+                       *immediate = imm16;
+                       *type = INSN_OTHER;
+                       return 0;
+               default:
+                       return arm_decode_unknown(instr, type, immediate, op);
+       }
+
+#undef INSN_SVC
+#undef INSN_HVC
+#undef INSN_SMC
+#undef INSN_BRK
+#undef INSN_HLT
+#undef INSN_DCPS1
+#undef INSN_DCPS2
+#undef INSN_DCPS3
+}
+
+int arm_decode_hints(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       *type = INSN_NOP;
+       return 0;
+}
+
+int arm_decode_barriers(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       /* TODO:check unallocated */
+       *type = INSN_OTHER;
+       return 0;
+}
+
+int arm_decode_pstate(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       /* TODO:check unallocated */
+       *type = INSN_OTHER;
+       return 0;
+}
+
+int arm_decode_system_insn(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       /* TODO:check unallocated */
+       *type = INSN_OTHER;
+       return 0;
+}
+
+int arm_decode_system_regs(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       /* TODO:check unallocated */
+       *type = INSN_OTHER;
+       return 0;
+}
+
+
+static struct aarch64_insn_decoder ret_decoder[] = {
+       /*
+        * RET, RETAA, RETAB
+        */
+       {
+               .mask = 0b1111111111111110000011111,
+               .value = 0b0010111110000000000000000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b1111111111111111111111111,
+               .value = 0b0010111110000101111111111,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b1111111111111111111111111,
+               .value = 0b0010111110000111111111111,
+               .decode_func = NULL,
+       },
+};
+
+static struct aarch64_insn_decoder br_decoder[] = {
+       /*
+        * BR, BRAA, BRAAZ, BRAB, BRABZ
+        */
+       {
+               .mask = 0b1111111111111110000011111,
+               .value = 0b0000111110000000000000000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b1111111111111110000011111,
+               .value = 0b0000111110000100000011111,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b1111111111111110000011111,
+               .value = 0b0000111110000110000011111,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b1111111111111110000000000,
+               .value = 0b1000111110000100000000000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b1111111111111110000000000,
+               .value = 0b1000111110000110000000000,
+               .decode_func = NULL,
+       },
+};
+
+#define INSN_DRPS_FIELD                0b0101111110000001111100000
+#define INSN_DRPS_MASK         0b1111111111111111111111111
+
+static struct aarch64_insn_decoder ct_sw_decoder[] = {
+       /*
+        * ERET, ERETAA, ERETAB
+        */
+       {
+               .mask = INSN_DRPS_MASK,
+               .value = 0b0100111110000001111100000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = INSN_DRPS_MASK,
+               .value = 0b0100111110000101111111111,
+               .decode_func = NULL,
+       },
+       {
+               .mask = INSN_DRPS_MASK,
+               .value = 0b0100111110000111111111111,
+               .decode_func = NULL,
+       },
+};
+
+
+static struct aarch64_insn_decoder call_decoder[] = {
+       /*
+        * BLR, BLRAA, BLRAAZ, BLRAB, BLRABZ
+        */
+       {
+               .mask = 0b1111111111111110000011111,
+               .value =  0b0001111110000000000000000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b1111111111111110000011111,
+               .value = 0b0001111110000100000011111,
+               .decode_func = NULL,
+       },
+       {
+               0b1111111111111110000011111,
+               0b0001111110000110000011111,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b1111111111111110000000000,
+               .value = 0b1001111110000100000000000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b1111111111111110000000000,
+               .value = 0b1001111110000110000000000,
+               .decode_func = NULL,
+       },
+};
+
+int arm_decode_br_uncond_reg(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+
+       u32 decode_field = 0;
+       int i = 0;
+
+       decode_field = instr & ONES(25);
+       *type = 0;
+       for (i = 0; i < ARRAY_SIZE(br_decoder); i++) {
+               if ((decode_field & br_decoder[i].mask) == br_decoder[i].value)
+                       *type = INSN_JUMP_DYNAMIC;
+       }
+       for (i = 0; i < ARRAY_SIZE(call_decoder); i++) {
+               if ((decode_field & call_decoder[i].value) == 
call_decoder[i].value)
+                       *type = INSN_CALL_DYNAMIC;
+       }
+       for (i = 0; i < ARRAY_SIZE(ret_decoder); i++) {
+               if ((decode_field & ret_decoder[i].mask) == 
ret_decoder[i].value)
+                       *type = INSN_RETURN;
+       }
+       for (i = 0; i < ARRAY_SIZE(ct_sw_decoder); i++) {
+               if ((decode_field & ct_sw_decoder[i].mask) == 
ct_sw_decoder[i].value)
+                       *type = INSN_CONTEXT_SWITCH;
+       }
+       if ((decode_field & INSN_DRPS_MASK) == INSN_DRPS_FIELD)
+               *type = INSN_OTHER;
+       if (*type == 0)
+               return arm_decode_unknown(instr, type, immediate, op);
+       return 0;
+}
+#undef INSN_DRPS_FIELD
+#undef INSN_DRPS_MASK
+
+int arm_decode_br_uncond_imm(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char decode_field = 0;
+       u32 imm26 = 0;
+
+       decode_field = EXTRACT_BIT(instr, 31);
+       imm26 = instr & ONES(26);
+
+       *immediate = SIGN_EXTEND(imm26 << 2, 28);
+       if (decode_field == 0)
+               *type = INSN_JUMP_UNCONDITIONAL;
+       else
+               *type = INSN_CALL;
+
+       return 0;
+
+}
+
+int arm_decode_br_comp_imm(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       u32 imm19 = (instr >> 5) & ONES(19);
+
+       *immediate = SIGN_EXTEND(imm19 << 2, 21);
+       *type = INSN_JUMP_CONDITIONAL;
+       return 0;
+}
+
+int arm_decode_br_tst_imm(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       u32 imm14 = (instr >> 5) & ONES(14);
+
+       *immediate = SIGN_EXTEND(imm14 << 2, 16);
+       *type = INSN_JUMP_CONDITIONAL;
+       return 0;
+}
+
+static struct aarch64_insn_decoder ld_st_decoder[] = {
+       {
+               .mask = 0b101111111111100,
+               .value = 0b000010000000000,
+               .decode_func = arm_decode_adv_simd_mult,
+       },
+       {
+               .mask = 0b101111110000000,
+               .value = 0b000010100000000,
+               .decode_func = arm_decode_adv_simd_mult_post,
+       },
+       {
+               .mask = 0b101111101111100,
+               .value = 0b000011000000000,
+               .decode_func = arm_decode_adv_simd_single,
+       },
+       {
+               .mask = 0b101111100000000,
+               .value = 0b000011100000000,
+               .decode_func = arm_decode_adv_simd_single_post,
+       },
+       {
+               .mask = 0b111111010000000,
+               .value = 0b110101010000000,
+               .decode_func = arm_decode_ld_st_mem_tags,
+       },
+       {
+               .mask = 0b001111000000000,
+               .value = 0b000000000000000,
+               .decode_func = arm_decode_ld_st_exclusive,
+       },
+       {
+               .mask = 0b001111010000011,
+               .value = 0b000101000000000,
+               .decode_func = arm_decode_ldapr_stlr_unsc_imm,
+       },
+       {
+               .mask = 0b001101000000000,
+               .value = 0b000100000000000,
+               .decode_func = arm_decode_ld_regs_literal,
+       },
+       {
+               .mask = 0b001101100000000,
+               .value = 0b001000000000000,
+               .decode_func = arm_decode_ld_st_noalloc_pair_off,
+       },
+       {
+               .mask = 0b001101100000000,
+               .value = 0b001000100000000,
+               .decode_func = arm_decode_ld_st_regs_pair_post,
+       },
+       {
+               .mask = 0b001101100000000,
+               .value = 0b001001000000000,
+               .decode_func = arm_decode_ld_st_regs_pair_off,
+       },
+       {
+               .mask = 0b001101100000000,
+               .value = 0b001001100000000,
+               .decode_func = arm_decode_ld_st_regs_pair_pre,
+       },
+       {
+               .mask = 0b001101010000011,
+               .value = 0b001100000000000,
+               .decode_func = arm_decode_ld_st_regs_unsc_imm,
+       },
+       {
+               .mask = 0b001101010000011,
+               .value = 0b001100000000001,
+               .decode_func = arm_decode_ld_st_imm_post,
+       },
+       {
+               .mask = 0b001101010000011,
+               .value = 0b001100000000010,
+               .decode_func = arm_decode_ld_st_imm_unpriv,
+       },
+       {
+               .mask = 0b001101010000011,
+               .value = 0b001100000000011,
+               .decode_func = arm_decode_ld_st_imm_pre,
+       },
+       {
+               .mask = 0b001101010000011,
+               .value = 0b001100010000000,
+               .decode_func = arm_decode_atomic,
+       },
+       {
+               .mask = 0b001101010000011,
+               .value = 0b001100010000010,
+               .decode_func = arm_decode_ld_st_regs_off,
+       },
+       {
+               .mask = 0b001101010000001,
+               .value = 0b001100010000001,
+               .decode_func = arm_decode_ld_st_regs_pac,
+       },
+       {
+               .mask = 0b001101000000000,
+               .value = 0b001101000000000,
+               .decode_func = arm_decode_ld_st_regs_unsigned,
+       },
+};
+
+int arm_decode_ld_st(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       u32 decode_field = 0;
+       int i = 0;
+       unsigned char op0 = 0, op1 = 0, op2 = 0, op3 = 0, op4 = 0;
+
+       op0 = (instr >> 28) & ONES(4);
+       op1 = EXTRACT_BIT(instr, 26);
+       op2 = (instr >> 23) & ONES(2);
+       op3 = (instr >> 16) & ONES(6);
+       op4 = (instr >> 10) & ONES(2);
+       decode_field = (op0 << 3) | (op1 << 2) | op2;
+       decode_field = (decode_field << 8) | (op3 << 2) | op4;
+
+       for (i = 0; i < ARRAY_SIZE(ld_st_decoder); i++) {
+               if ((decode_field & ld_st_decoder[i].mask) == 
ld_st_decoder[i].value) {
+                       return ld_st_decoder[i].decode_func(instr, type, 
immediate, op);
+               }
+       }
+       return arm_decode_unknown(instr, type, immediate, op);
+}
+
+static int adv_simd_mult_fields[] = {
+       0b00000,
+       0b00010,
+       0b00100,
+       0b00110,
+       0b00111,
+       0b01000,
+       0b01010,
+       0b10000,
+       0b10010,
+       0b10100,
+       0b10110,
+       0b10111,
+       0b11000,
+       0b11010,
+};
+
+int arm_decode_adv_simd_mult(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char L = 0, opcode = 0, rn = 0, rt = 0;
+       unsigned char decode_field = 0;
+       int i = 0;
+
+       L = EXTRACT_BIT(instr, 22);
+       opcode = (instr >> 12) & ONES(4);
+
+       decode_field = (L << 4) | opcode;
+       rn = (instr >> 5) & ONES(5);
+       rt = instr & ONES(5);
+       *type = INSN_OTHER;
+
+
+       for (i = 0; i < ARRAY_SIZE(adv_simd_mult_fields); i++) {
+               if ((decode_field & 0b11111) == adv_simd_mult_fields[i]) {
+                       if (rn != 31)
+                               return 0;
+                       *type = INSN_STACK;
+               }
+       }
+       if (*type != INSN_STACK)
+               return arm_decode_unknown(instr, type, immediate, op);
+
+       if (!L) {
+               op->dest.type = OP_DEST_REG_INDIRECT;
+               op->dest.reg = CFI_SP;
+               op->dest.offset = 0;
+               op->src.type = OP_SRC_REG;
+               op->src.reg = rt;
+               op->src.offset = 0;
+       }
+       else {
+               op->src.type = OP_SRC_REG_INDIRECT;
+               op->src.reg = CFI_SP;
+               op->src.offset = 0;
+               op->dest.type = OP_SRC_REG;
+               op->dest.reg = rt;
+               op->dest.offset = 0;
+       }
+
+       return 0;
+}
+
+int arm_decode_adv_simd_mult_post(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       /* same opcode as for the no offset variant */
+       unsigned char rm = 0;
+       int ret = 0;
+       rm = (instr >> 16) & ONES(5);
+
+       ret = arm_decode_adv_simd_mult(instr, type, immediate, op);
+
+       /*
+       * This is actually irrelevent if the offset is given by a register
+       * however there is no way to know the offset value from the encoding
+       * in such a case.
+       */
+       if (op->dest.type == OP_DEST_REG_INDIRECT)
+               op->dest.offset = rm;
+       if (op->src.type == OP_SRC_REG_INDIRECT)
+               op->src.offset = rm;
+       return ret;
+}
+
+
+static struct aarch64_insn_decoder simd_single_decoder[] = {
+       {
+               .mask = 0b11111000,
+               .value = 0b00000000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111000,
+               .value = 0b00001000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111001,
+               .value = 0b00010000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111001,
+               .value = 0b00011000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111011,
+               .value = 0b00100000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111111,
+               .value = 0b00100001,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111011,
+               .value = 0b00101000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111111,
+               .value = 0b00101001,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111000,
+               .value = 0b01000000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111000,
+               .value = 0b01001000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111001,
+               .value = 0b01010000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111001,
+               .value = 0b01011000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111011,
+               .value = 0b01100000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111111,
+               .value = 0b01100001,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111011,
+               .value = 0b01101000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111111,
+               .value = 0b01101001,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111000,
+               .value = 0b10000000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111000,
+               .value = 0b10001000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111001,
+               .value = 0b10010000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111001,
+               .value = 0b10011000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111011,
+               .value = 0b10100000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111111,
+               .value = 0b10100001,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111011,
+               .value = 0b10101000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111111,
+               .value = 0b10101001,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111100,
+               .value = 0b10110000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111100,
+               .value = 0b10111000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11000000,
+               .value = 0b11111000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111000,
+               .value = 0b11001000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111001,
+               .value = 0b11010000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111001,
+               .value = 0b11011000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111011,
+               .value = 0b11100000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111111,
+               .value = 0b11100001,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111011,
+               .value = 0b11101000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111111,
+               .value = 0b11101001,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111100,
+               .value = 0b11110000,
+               .decode_func = NULL,
+       },
+       {
+               .mask = 0b11111100,
+               .value = 0b11111000,
+               .decode_func = NULL,
+       },
+};
+
+int arm_decode_adv_simd_single(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char L = 0, R = 0, S = 0, opcode = 0, size = 0;
+       unsigned char rn = 0, rt = 0, dfield = 0;
+       int i = 0;
+
+       L = EXTRACT_BIT(instr, 22);
+       R = EXTRACT_BIT(instr, 21);
+       S = EXTRACT_BIT(instr, 12);
+       opcode = (instr >> 13) & ONES(3);
+       size = (instr >> 10) & ONES(2);
+
+       dfield = (L << 7) | (R << 6) | (opcode << 3) | (S << 2) | size;
+
+       *type = INSN_OTHER;
+       rn = (instr << 5) & ONES(5);
+
+       for (i = 0; i < ARRAY_SIZE(simd_single_decoder); i++) {
+               if ((dfield & simd_single_decoder[i].mask) == 
simd_single_decoder[i].value) {
+                       if (rn != CFI_SP)
+                               return 0;
+                       *type = INSN_STACK;
+               }
+       }
+
+       if (*type == INSN_OTHER)
+               return arm_decode_unknown(instr, type, immediate, op);
+
+       rt = instr & ONES(5);
+       if (!L) {
+               op->dest.type = OP_DEST_REG_INDIRECT;
+               op->dest.reg = CFI_SP;
+               op->dest.offset = 0;
+               op->src.type = OP_SRC_REG;
+               op->src.reg = rt;
+               op->src.offset = 0;
+       }
+       else {
+               op->src.type = OP_SRC_REG_INDIRECT;
+               op->src.reg = CFI_SP;
+               op->src.offset = 0;
+               op->dest.type = OP_DEST_REG;
+               op->dest.reg = rt;
+               op->dest.offset = 0;
+       }
+       return 0;
+}
+
+int arm_decode_adv_simd_single_post(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       /* same opcode as for the no offset variant */
+       unsigned char rm = 0;
+       int ret = 0;
+       rm = (instr >> 16) & ONES(5);
+
+       ret = arm_decode_adv_simd_single(instr, type, immediate, op);
+
+       /*
+       * This is actually irrelevent if the offset is given by a register
+       * however there is no way to know the offset value from the encoding
+       * in such a case.
+       */
+       if (op->dest.type == OP_DEST_REG_INDIRECT)
+               op->dest.offset = rm;
+       if (op->src.type == OP_SRC_REG_INDIRECT)
+               op->src.offset = rm;
+       return ret;
+}
+
+int arm_decode_ld_st_mem_tags(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       u32 imm9 = 0;
+       unsigned char opc = 0, op2 = 0, rn = 0, rt = 0, decode_field = 0;
+
+       imm9 = (instr >> 12) & ONES(9);
+       opc = (instr >> 22) & ONES(2);
+       op2 = (instr >> 10) & ONES(2);
+       rn = (instr >> 5) & ONES(5);
+       rt = instr & ONES(6);
+
+       decode_field = (opc << 2) | op2;
+
+       if (decode_field == 0x0
+               || (decode_field == 0x8 && imm9 != 0)
+               || (decode_field == 0xC && imm9 != 0)) {
+               return arm_decode_unknown(instr, type, immediate, op);
+       }
+
+       if (rn != CFI_SP) {
+               *type = INSN_OTHER;
+               return 0;
+       }
+       *type = INSN_STACK;
+       *immediate = imm9;
+
+       /*
+       * Offset should normally be shifted to the
+       * left of LOG2_TAG_GRANULE
+       */
+       switch (decode_field) {
+               case 1:
+               case 5:
+               case 9:
+               case 13:
+                       /* post index */
+               case 3:
+               case 7:
+               case 8:
+               case 11:
+               case 15:
+                       /* pre index */
+                       op->dest.reg = CFI_SP;
+                       op->dest.type = OP_DEST_PUSH;
+                       op->dest.offset = SIGN_EXTEND(imm9, 9);
+                       op->src.reg = rt;
+                       op->src.type = OP_SRC_REG;
+                       op->src.offset = 0;
+                       return 0;
+               case 2:
+               case 6:
+               case 10:
+               case 14:
+                       /* store */
+                       op->dest.reg = CFI_SP;
+                       op->dest.type = OP_DEST_REG_INDIRECT;
+                       op->dest.offset = SIGN_EXTEND(imm9, 9);
+                       op->src.reg = rt;
+                       op->src.type = OP_SRC_REG;
+                       op->src.offset = 0;
+                       return 0;
+               case 4:
+               case 12:
+                       /* load */
+                       op->src.reg = CFI_SP;
+                       op->src.type = OP_SRC_REG_INDIRECT;
+                       op->src.offset = SIGN_EXTEND(imm9, 9);
+                       op->dest.reg = rt;
+                       op->dest.type = OP_DEST_REG;
+                       op->dest.offset = 0;
+                       return 0;
+       }
+
+       return -1;
+
+}
+
+#define ST_EXCL_UNALLOC_1 0b001010
+#define ST_EXCL_UNALLOC_2 0b000010
+
+#define LDXRB          0b000100
+#define LDAXRB         0b000101
+#define LDLARB         0b001100
+#define LDARB          0b001101
+#define LDXRH          0b010100
+#define LDAXRH         0b010101
+#define LDLARH         0b011100
+#define LDARH          0b011101
+#define LDXR           0b100100
+#define LDAXR          0b100101
+#define LDXP           0b100110
+#define LDAXP          0b100111
+#define LDLAR          0b101100
+#define LDAR           0b101101
+#define LDXR_64                0b110100
+#define LDAXR_64       0b110101
+#define LDXP_64                0b110110
+#define LDAXP_64       0b110111
+#define LDLAR_64       0b111100
+#define LDAR_64                0b111101
+
+#define LD_EXCL_NUMBER 20
+
+static int ld_excl_masks[] = {
+       LDXRB,
+       LDAXRB,
+       LDLARB,
+       LDARB,
+       LDXRH,
+       LDAXRH,
+       LDLARH,
+       LDARH,
+       LDXR,
+       LDAXR,
+       LDXP,
+       LDAXP,
+       LDLAR,
+       LDAR,
+       LDXR_64,
+       LDAXR_64,
+       LDXP_64,
+       LDAXP_64,
+       LDLAR_64,
+       LDAR_64,
+};
+
+int arm_decode_ld_st_exclusive(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char size = 0, o2 = 0, L = 0, o1 = 0, o0 = 0;
+       unsigned char rt = 0, rt2 = 0, rn = 0;
+       unsigned char decode_field = 0;
+       int i = 0;
+
+       size = (instr >> 30) & ONES(2);
+       o2 = EXTRACT_BIT(instr, 23);
+       L = EXTRACT_BIT(instr, 22);
+       o1 = EXTRACT_BIT(instr, 21);
+       o0 = EXTRACT_BIT(instr, 15);
+
+       rt2 = (instr >> 10) & ONES(5);
+       rn = (instr >> 5) & ONES(5);
+       rt = instr & ONES(5);
+
+       decode_field = (size << 4) | (o2 << 3) | (L << 2) | (o1 << 1) | o0;
+
+       if ((decode_field & ST_EXCL_UNALLOC_1) == ST_EXCL_UNALLOC_1
+               || (decode_field & 0b101010) == ST_EXCL_UNALLOC_2){
+               if (rt2 != 31) {
+                       return arm_decode_unknown(instr, type, immediate, op);
+               }
+       }
+
+       if (rn != 31) {
+               *type = INSN_OTHER;
+               return 0;
+       }
+
+       *type = INSN_STACK;
+       for (i = 0; i < LD_EXCL_NUMBER; i++) {
+               if ((decode_field & 0b111111) == ld_excl_masks[i]) {
+                       op->src.type = OP_SRC_REG_INDIRECT;
+                       op->src.reg = CFI_SP;
+                       op->src.offset = 0;
+                       op->dest.type = OP_DEST_REG;
+                       op->dest.reg = rt;
+                       op->dest.offset = 0;
+                       return 0;
+               }
+       }
+
+       op->dest.type = OP_DEST_REG_INDIRECT;
+       op->dest.reg = CFI_SP;
+       op->dest.offset = 0;
+       op->src.type = OP_SRC_REG;
+       op->src.reg = rt;
+       op->src.offset = 0;
+
+       return 0;
+}
+#undef ST_EXCL_UNALLOC_1
+#undef ST_EXCL_UNALLOC_2
+
+#undef LD_EXCL_NUMBER
+
+#undef LDXRB
+#undef LDAXRB
+#undef LDLARB
+#undef LDARB
+#undef LDXRH
+#undef LDAXRH
+#undef LDLARH
+#undef LDARH
+#undef LDXR
+#undef LDAXR
+#undef LDXP
+#undef LDAXP
+#undef LDLAR
+#undef LDAR
+#undef LDXR_64
+#undef LDAXR_64
+#undef LDXP_64
+#undef LDAXP_64
+#undef LDLAR_64
+#undef LDAR_64
+
+
+int arm_decode_ldapr_stlr_unsc_imm(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       u32 imm9 = 0;
+       unsigned char size = 0, opc = 0, rn = 0, rt = 0, decode_field = 0;
+
+       imm9 = (instr >> 12) & ONES(9);
+       size = (instr >> 30) & ONES(2);
+       opc = (instr >> 22) & ONES(2);
+       rn = (instr >> 5) & ONES(5);
+       rt = instr & ONES(5);
+
+       decode_field = (size << 2) | opc;
+       if (decode_field == 0xB
+               || decode_field == 0xE
+               || decode_field == 0xF) {
+               return arm_decode_unknown(instr, type, immediate, op);
+       }
+
+       if (rn != 31) {
+               *type = INSN_OTHER;
+               return 0;
+       }
+       *type = INSN_STACK;
+       *immediate = imm9;
+       switch (decode_field) {
+               case 1:
+               case 2:
+               case 3:
+               case 5:
+               case 6:
+               case 7:
+               case 9:
+               case 10:
+               case 13:
+                       /* load */
+                       op->src.type = OP_SRC_REG_INDIRECT;
+                       op->src.reg = CFI_SP;
+                       op->src.offset = SIGN_EXTEND(imm9, 9);
+                       op->dest.type = OP_DEST_REG;
+                       op->dest.reg = rt;
+                       op->dest.offset = 0;
+                       break;
+               default:
+                       /* store */
+                       op->dest.type = OP_SRC_REG_INDIRECT;
+                       op->dest.reg = CFI_SP;
+                       op->dest.offset = SIGN_EXTEND(imm9, 9);
+                       op->src.type = OP_SRC_REG;
+                       op->src.reg = rt;
+                       op->src.offset = 0;
+                       break;
+       }
+
+       return 0;
+}
+
+int arm_decode_ld_regs_literal(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char opc = 0, V = 0;
+       opc = (instr >> 30) & ONES(2);
+       V = EXTRACT_BIT(instr, 26);
+
+       if (((opc << 1) | V) == 0x7)
+               return arm_decode_unknown(instr, type, immediate, op);
+
+       *type = INSN_OTHER;
+       return 0;
+}
+
+int arm_decode_ld_st_noalloc_pair_off(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+
+       unsigned char opc = 0, V = 0, L = 0;
+       unsigned char decode_field = 0;
+
+       opc = (instr >> 30) & ONES(2);
+       V = EXTRACT_BIT(instr, 26);
+       L = EXTRACT_BIT(instr, 22);
+
+       decode_field = (opc << 2) | (V << 1) | L;
+
+       if (decode_field == 0x4 || decode_field == 0x5
+               || decode_field >= 12) {
+               return arm_decode_unknown(instr, type, immediate, op);
+       }
+       return arm_decode_ld_st_regs_pair_off(instr, type, immediate, op);
+}
+
+/*
+ * We use this to decompose the load/store of pairs
+ * into two distinct instructions so that we can track
+ * the update of the stack as if several push/pop were
+ * done consecutively
+ */
+int arm_decode_ld_st_regs_pair_off(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char opc = 0, V = 0, L = 0, bit = 0;
+       unsigned char imm7 = 0, rt2 = 0, rt = 0, rn = 0;
+       unsigned char decode_field = 0;
+       int scale = 0;
+
+       static struct insn_decode_state state = {
+               .is_composed_insn = false,
+               .current_reg_num = 0,
+               .insn_regs_num = 2,
+               .insn_type = 0,
+               .immediate = 0,
+               .curr_offset = 0,
+               .op = {
+                       .dest = {
+                               .type = 0,
+                               .reg = 0,
+                               .offset = 0
+                       },
+                       .src = {
+                               .type = 0,
+                               .reg = 0,
+                               .offset = 0
+                       },
+               },
+               .regs = {0, 0},
+       };
+
+       if (state.is_composed_insn) {
+               *op = state.op;
+               if (op->dest.type == OP_DEST_REG_INDIRECT) {
+                       op->src.reg = state.regs[state.insn_regs_num
+                                               - (++state.current_reg_num)];
+                       op->dest.offset = state.curr_offset;
+               }
+               else {
+                       op->dest.reg = state.regs[state.current_reg_num++];
+                       op->src.offset = state.curr_offset;
+               }
+
+               *type = state.insn_type;
+               *immediate = state.immediate / state.insn_regs_num;
+               if (state.current_reg_num >= state.insn_regs_num) {
+                       state.is_composed_insn = false;
+                       return 0;
+               }
+               return INSN_COMPOSED;
+       }
+
+       opc = (instr >> 30) & ONES(2);
+       V = EXTRACT_BIT(instr, 26);
+       L = EXTRACT_BIT(instr, 22);
+       imm7 = (instr >> 15) & ONES(7);
+       rt2 = (instr >> 10) & ONES(5);
+       rn = (instr >> 5) & ONES(5);
+       rt = instr & ONES(5);
+       bit = EXTRACT_BIT(opc, 1);
+       scale = 2 + bit;
+
+       decode_field = (opc << 2) | (V << 1) | L;
+
+       if (decode_field >= 0xC)
+               return arm_decode_unknown(instr, type, immediate, op);
+
+       *immediate = (SIGN_EXTEND(imm7, 7)) << scale;
+
+       if (rn != CFI_SP) {
+               *type = INSN_OTHER;
+               return 0;
+       }
+
+       *type = INSN_STACK;
+
+       state.is_composed_insn = true;
+       state.current_reg_num = 1;
+       state.insn_regs_num = 2;
+       state.insn_type = *type;
+       state.immediate = *immediate;
+       state.curr_offset = 0;
+       state.regs[0] = rt;
+       state.regs[1] = rt2;
+
+       switch (decode_field) {
+               case 1:
+               case 3:
+               case 5:
+               case 7:
+               case 9:
+               case 11:
+                       /* load */
+                       op->src.type = OP_SRC_REG_INDIRECT;
+                       op->src.reg = CFI_SP;
+                       op->src.offset = 0;
+                       state.curr_offset = 8;
+                       op->dest.type = OP_DEST_REG;
+                       op->dest.reg = state.regs[0];
+                       op->dest.offset = 0;
+                       break;
+               default:
+                       op->dest.type = OP_DEST_REG_INDIRECT;
+                       op->dest.reg = CFI_SP;
+                       op->dest.offset = 8;
+                       state.curr_offset = 0;
+                       op->src.type = OP_SRC_REG;
+                       op->src.reg = state.regs[1];
+                       op->src.offset = 0;
+                       /* store */
+       }
+       state.op = *op;
+       return INSN_COMPOSED;
+}
+
+int arm_decode_ld_st_regs_pair_post(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       int ret = 0;
+
+       ret = arm_decode_ld_st_regs_pair_off(instr, type, immediate, op);
+       if (ret < 0 || *type == INSN_OTHER)
+               return ret;
+       if (op->dest.type == OP_DEST_REG_INDIRECT) {
+               op->dest.type = OP_DEST_PUSH;
+               op->dest.reg = CFI_SP;
+       }
+
+       if (op->src.type == OP_SRC_REG_INDIRECT) {
+               op->src.type = OP_SRC_POP;
+               op->src.reg = CFI_SP;
+       }
+
+       return ret;
+}
+
+int arm_decode_ld_st_regs_pair_pre(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       return arm_decode_ld_st_regs_pair_post(instr, type, immediate, op);
+}
+
+int arm_decode_ld_st_regs_unsc_imm(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       u32 imm9 = 0;
+       unsigned char size = 0, V = 0, opc = 0, rn = 0, rt = 0;
+       unsigned char decode_field = 0;
+
+       size = (instr >> 30) & ONES(2);
+       V = EXTRACT_BIT(instr, 26);
+       opc = (instr >> 22) & ONES(2);
+
+       imm9 = (instr >> 12) & ONES(9);
+       rn = (instr >> 5) & ONES(5);
+       rt = instr & ONES(5);
+
+       decode_field = (size << 2) | (V << 2) | opc;
+
+       switch (decode_field) {
+               case 0b01110:
+               case 0b01111:
+               case 0b11110:
+               case 0b11111:
+               case 0b10011:
+               case 0b11011:
+               case 0b10110:
+               case 0b10111:
+                       return arm_decode_unknown(instr, type, immediate, op);
+               case 26:
+                       /* prefetch */
+                       *type = INSN_OTHER;
+                       return 0;
+               case 1:
+               case 2:
+               case 3:
+               case 5:
+               case 7:
+               case 9:
+               case 10:
+               case 11:
+               case 13:
+               case 17:
+               case 18:
+               case 21:
+               case 25:
+               case 29:
+                       /* load */
+                       if (rn != CFI_SP) {
+                               *type = INSN_OTHER;
+                               return 0;
+                       }
+                       op->src.type = OP_SRC_REG_INDIRECT;
+                       op->src.reg = CFI_SP;
+                       op->src.offset = SIGN_EXTEND(imm9, 9);
+                       op->dest.type = OP_DEST_REG;
+                       op->dest.reg = rt;
+                       op->dest.offset = 0;
+                       break;
+               default:
+                       if (rn != CFI_SP) {
+                               *type = INSN_OTHER;
+                               return 0;
+                       }
+                       op->dest.type = OP_DEST_REG_INDIRECT;
+                       op->dest.reg = CFI_SP;
+                       op->dest.offset = SIGN_EXTEND(imm9, 9);
+                       op->src.type = OP_DEST_REG;
+                       op->src.reg = rt;
+                       op->src.offset = 0;
+                       break;
+       }
+
+       *type = INSN_STACK;
+       return 0;
+}
+
+static struct aarch64_insn_decoder ld_unsig_unalloc_decoder[] = {
+       {
+               .mask = 0b01110,
+               .value = 0b01110,
+       },
+       {
+               .mask = 0b10111,
+               .value = 0b10011,
+       },
+       {
+               .mask = 0b10110,
+               .value = 0b10110,
+       },
+};
+
+int arm_decode_ld_st_regs_unsigned(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char size = 0, V = 0, opc = 0, rn = 0, rt = 0;
+       unsigned char decode_field = 0;
+       u32 imm12 = 0;
+       int i = 0;
+
+       size = (instr >> 30) & ONES(2);
+       V = EXTRACT_BIT(instr, 26);
+       opc = (instr >> 22) & ONES(2);
+
+       decode_field = (size << 3) | (V << 2) | opc;
+       for (i = 0; i < ARRAY_SIZE(ld_unsig_unalloc_decoder); i++) {
+               if ((decode_field & ld_unsig_unalloc_decoder[i].mask)
+                               == ld_unsig_unalloc_decoder[i].value) {
+                       return arm_decode_unknown(instr, type,
+                                               immediate, op);
+               }
+       }
+
+       imm12 = (instr >> 10) & ONES(12);
+       rn = (instr >> 5) & ONES(5);
+       rt = instr & ONES(5);
+
+       if (rn != CFI_SP || decode_field == 26) {
+               *type = INSN_OTHER;
+               return 0;
+       }
+
+       *type = INSN_STACK;
+
+       switch (decode_field) {
+               case 1:
+               case 2:
+               case 3:
+               case 5:
+               case 7:
+               case 9:
+               case 10:
+               case 11:
+               case 13:
+               case 17:
+               case 18:
+               case 21:
+               case 25:
+                       /* load */
+                       op->src.type = OP_SRC_REG_INDIRECT;
+                       op->src.reg = CFI_SP;
+                       op->src.offset = imm12;
+                       op->dest.type = OP_DEST_REG;
+                       op->dest.reg = rt;
+                       op->dest.offset = 0;
+                       break;
+               default: /* store */
+                       op->dest.type = OP_DEST_REG_INDIRECT;
+                       op->dest.reg = CFI_SP;
+                       op->dest.offset = imm12;
+                       op->src.type = OP_DEST_REG;
+                       op->src.reg = rt;
+                       op->src.offset = 0;
+       }
+
+       return 0;
+
+}
+
+int arm_decode_ld_st_imm_post(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char size = 0, V = 0, opc = 0;
+       unsigned char decode_field = 0;
+       u32 imm9 = 0;
+       int ret = 0;
+
+       size = (instr >> 30) & ONES(2);
+       V = EXTRACT_BIT(instr, 26);
+       opc = (instr >> 22) & ONES(2);
+
+       imm9 = (instr >> 12) & ONES(9);
+
+       decode_field = (size << 2) | (V << 2) | opc;
+
+       if (decode_field == 0b11010)
+               return arm_decode_unknown(instr, type, immediate, op);
+
+       ret = arm_decode_ld_st_regs_unsigned(instr, type, immediate, op);
+       if (ret < 0 || *type == INSN_OTHER)
+               return ret;
+
+       if (op->dest.type == OP_DEST_REG_INDIRECT) {
+               op->dest.type = OP_DEST_PUSH;
+               op->dest.reg = CFI_SP;
+               op->dest.offset = SIGN_EXTEND(imm9, 9);
+       }
+
+       if (op->src.type == OP_SRC_REG_INDIRECT) {
+               op->src.type = OP_SRC_POP;
+               op->src.reg = CFI_SP;
+               op->src.offset = SIGN_EXTEND(imm9, 9);
+       }
+
+       return 0;
+}
+
+int arm_decode_ld_st_imm_pre(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       return arm_decode_ld_st_imm_post(instr, type, immediate, op);
+}
+
+#define LD_UNPR_UNALLOC_1 0b10011
+#define LD_UNPR_UNALLOC_2 0b11010
+int arm_decode_ld_st_imm_unpriv(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char size = 0, V = 0, opc = 0, rn = 0, rt = 0;
+       unsigned char decode_field = 0;
+       u32 imm9 = 0;
+
+       size = (instr >> 30) & ONES(2);
+       V = EXTRACT_BIT(instr, 26);
+       opc = (instr >> 22) & ONES(2);
+
+       imm9 = (instr >> 12) & ONES(9);
+
+       decode_field = (size << 3) | (V << 2) | opc;
+       if (V == 1
+               || (decode_field & 0b10111) == LD_UNPR_UNALLOC_1
+               || (decode_field & 0b11111) == LD_UNPR_UNALLOC_2) {
+               return arm_decode_unknown(instr, type, immediate, op);
+       }
+#undef LD_UNPR_UNALLOC_1
+#undef LD_UNPR_UNALLOC_2
+
+       if (rn != CFI_SP) {
+               *type = INSN_OTHER;
+               return 0;
+       }
+       *type = INSN_STACK;
+
+       switch(decode_field) {
+               case 1:
+               case 2:
+               case 3:
+               case 9:
+               case 10:
+               case 11:
+               case 17:
+               case 18:
+               case 25:
+                       /* load */
+                       op->src.type = OP_SRC_REG_INDIRECT;
+                       op->src.reg = CFI_SP;
+                       op->src.offset = SIGN_EXTEND(imm9, 9);
+                       op->dest.type = OP_DEST_REG;
+                       op->dest.reg = rt;
+                       op->dest.offset = 0;
+                       break;
+               default:
+                       /* store */
+                       op->dest.type = OP_DEST_REG_INDIRECT;
+                       op->dest.reg = CFI_SP;
+                       op->dest.offset = SIGN_EXTEND(imm9, 9);
+                       op->src.type = OP_DEST_REG;
+                       op->src.reg = rt;
+                       op->src.offset = 0;
+                       break;
+       }
+       return 0;
+
+}
+
+static struct aarch64_insn_decoder atom_unallocs_decoder[] = {
+       {
+               .mask = 0b1001111,
+               .value = 0b0001001,
+       },
+       {
+               .mask = 0b1001110,
+               .value = 0b0001010,
+       },
+       {
+               .mask = 0b1001111,
+               .value = 0b0001101,
+       },
+       {
+               .mask = 0b1001110,
+               .value = 0b0001110,
+       },
+       {
+               .mask = 0b1101111,
+               .value = 0b0001100,
+       },
+       {
+               .mask = 0b1111111,
+               .value = 0b0111100,
+       },
+       {
+               .mask = 0b1000000,
+               .value = 0b1000000,
+       },
+};
+
+int arm_decode_atomic(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char V = 0, A = 0, R = 0, o3 = 0, opc = 0;
+       unsigned char rn = 0, rt = 0;
+       unsigned char decode_field = 0;
+       int i = 0;
+
+       V = EXTRACT_BIT(instr, 26);
+       A = EXTRACT_BIT(instr, 23);
+       R = EXTRACT_BIT(instr, 22);
+       o3 = EXTRACT_BIT(instr, 15);
+       opc = (instr >> 12) & ONES(3);
+
+       decode_field = (V << 6) | (A << 5) | (R << 4) | (o3 << 3) | opc;
+
+       for (i = 0; i < ARRAY_SIZE(atom_unallocs_decoder); i++) {
+               if ((decode_field & atom_unallocs_decoder[i].mask)
+                               == atom_unallocs_decoder[i].value) {
+                       return arm_decode_unknown(instr,
+                                               type, immediate, op);
+               }
+       }
+
+       rn = (instr >> 5) & ONES(5);
+       rt = instr & ONES(5);
+
+       if (rn != CFI_SP) {
+               *type = INSN_OTHER;
+               return 0;
+       }
+       *type = INSN_STACK;
+       op->src.reg = CFI_SP;
+       op->src.type = OP_DEST_REG_INDIRECT;
+       op->src.offset = 0;
+       op->dest.type = OP_DEST_REG;
+       op->dest.reg = rt;
+       op->dest.offset = 0;
+
+       return 0;
+
+}
+
+int arm_decode_ld_st_regs_off(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char size = 0, V = 0, opc = 0, option = 0;
+       unsigned char rm = 0, rn = 0, rt = 0;
+       unsigned char decode_field = 0;
+
+       size = (instr >> 30) & ONES(2);
+       V = EXTRACT_BIT(instr, 26);
+       opc = (instr >> 22) & ONES(2);
+       option = (instr >> 13) & ONES(3);
+
+#define LD_ROFF_UNALLOC_1      0b01110
+#define LD_ROFF_UNALLOC_2      0b10110
+#define LD_ROFF_UNALLOC_3      0b10011
+       decode_field = (size << 3) | (V << 2) | opc;
+       if (!EXTRACT_BIT(option, 1)
+               || (decode_field & LD_ROFF_UNALLOC_1) == LD_ROFF_UNALLOC_1
+               || (decode_field & LD_ROFF_UNALLOC_2) == LD_ROFF_UNALLOC_2
+               || (decode_field & 0b10111) == LD_ROFF_UNALLOC_3) {
+               return arm_decode_unknown(instr, type, immediate, op);
+       }
+#undef LD_ROFF_UNALLOC_1
+#undef LD_ROFF_UNALLOC_2
+#undef LD_ROFF_UNALLOC_3
+
+       rn = (instr >> 5) & ONES(5);
+
+#define LD_ROFF_PRFM   0b11010
+       if (rn != CFI_SP || decode_field == LD_ROFF_PRFM) {
+               *type = INSN_OTHER;
+               return 0;
+       }
+#undef LD_ROFF_PRFM
+
+       rt = instr & ONES(5);
+       rm = (instr >> 16) & ONES(5);
+
+       switch (decode_field & ONES(3)) {
+               case 0b001:
+               case 0b010:
+               case 0b011:
+               case 0b101:
+               case 0b111:
+                       /* load */
+                       op->src.type = OP_SRC_REG_INDIRECT;
+                       op->src.reg = CFI_SP;
+                       op->src.offset = rm;
+                       op->dest.type = OP_DEST_REG;
+                       op->dest.reg = rt;
+                       op->dest.offset = 0;
+                       break;
+               default:
+                       /* store */
+                       op->dest.type = OP_DEST_REG_INDIRECT;
+                       op->dest.reg = CFI_SP;
+                       op->dest.offset = rm;
+                       op->src.type = OP_DEST_REG;
+                       op->src.reg = rt;
+                       op->src.offset = 0;
+                       break;
+
+       }
+
+       return 0;
+}
+
+int arm_decode_ld_st_regs_pac(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char size = 0, V = 0, W = 0, S = 0;
+       unsigned char rn = 0, rt = 0;
+       u32 imm9 = 0, s10 = 0;
+
+       size = (instr >> 30) & ONES(2);
+       V = EXTRACT_BIT(instr, 26);
+       W = EXTRACT_BIT(instr, 11);
+
+       if (size != 3 || V == 1) {
+               return arm_decode_unknown(instr, type, immediate, op);
+       }
+
+       rn = (instr >> 5) & ONES(5);
+
+       if (rn != CFI_SP) {
+               *type = INSN_OTHER;
+               return 0;
+       }
+
+       S = EXTRACT_BIT(instr, 22);
+       s10 = (S << 9) | imm9;
+
+       op->dest.reg = rt;
+       op->dest.type = OP_DEST_REG;
+       op->dest.offset = 0;
+       op->src.offset = (SIGN_EXTEND(s10, 9) << 3);
+       if (W) { /* pre-indexed/writeback */
+               op->src.type = OP_SRC_POP;
+               op->src.reg = CFI_SP;
+       }
+       else {
+               op->src.type = OP_SRC_REG_INDIRECT;
+               op->src.reg = CFI_SP;
+       }
+
+       return 0;
+}
+
+
+static struct aarch64_insn_decoder dp_reg_decoder[] = {
+       {
+               .mask = 0b111111000000,
+               .value = 0b010110000000,
+               .decode_func = arm_decode_dp_reg_2src,
+       },
+       {
+               .mask = 0b111111000000,
+               .value = 0b110110000000,
+               .decode_func = arm_decode_dp_reg_1src,
+       },
+       {
+               .mask = 0b011000000000,
+               .value = 0b000000000000,
+               .decode_func = arm_decode_dp_reg_logi,
+       },
+       {
+               .mask = 0b011001000000,
+               .value = 0b001000000000,
+               .decode_func = arm_decode_dp_reg_adds,
+       },
+       {
+               .mask = 0b011001000000,
+               .value = 0b001001000000,
+               .decode_func = arm_decode_dp_reg_adde,
+       },
+       {
+               .mask = 0b011111111111,
+               .value = 0b010000000000,
+               .decode_func = arm_decode_dp_reg_addc,
+       },
+       {
+               .mask = 0b011111011111,
+               .value = 0b010000000001,
+               .decode_func = arm_decode_dp_reg_rota,
+       },
+       {
+               .mask = 0b011111001111,
+               .value = 0b010000000010,
+               .decode_func = arm_decode_dp_reg_eval,
+       },
+       {
+               .mask = 0b011111000010,
+               .value = 0b010010000000,
+               .decode_func = arm_decode_dp_reg_cmpr,
+       },
+       {
+               .mask = 0b011111000010,
+               .value = 0b010010000010,
+               .decode_func = arm_decode_dp_reg_cmpi,
+       },
+       {
+               .mask = 0b011111000000,
+               .value = 0b010100000000,
+               .decode_func = arm_decode_dp_reg_csel,
+       },
+       {
+               .mask = 0b011000000000,
+               .value = 0b011000000000,
+               .decode_func = arm_decode_dp_reg_3src,
+       },
+};
+
+int arm_decode_dp_reg(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char op0 = 0, op1 = 0, op2 = 0, op3 = 0;
+       u32 decode_field = 0;
+       int i = 0;
+
+       op0 = EXTRACT_BIT(instr, 30);
+       op1 = EXTRACT_BIT(instr, 28);
+       op2 = (instr >> 21) & ONES(4);
+       op3 = (instr >> 10) & ONES(6);
+       decode_field = (op0 << 5) | (op1 << 4) | op2;
+       decode_field = (decode_field << 6) | op3;
+
+       for (i = 0; i < ARRAY_SIZE(dp_reg_decoder); i++) {
+               if ((decode_field & dp_reg_decoder[i].mask)
+                               == dp_reg_decoder[i].value) {
+                       return dp_reg_decoder[i].decode_func(instr, type,
+                                                       immediate, op);
+               }
+       }
+       return arm_decode_unknown(instr, type, immediate, op);
+}
+
+static struct aarch64_insn_decoder dp_reg_2src_decoder[] = {
+       {
+               .mask = 0b00111111,
+               .value = 0b00000001,
+       },
+       {
+               .mask = 0b00111000,
+               .value = 0b00011000,
+       },
+       {
+               .mask = 0b00100000,
+               .value = 0b00100000,
+       },
+       {
+               .mask = 0b01111111,
+               .value = 0b00000101,
+       },
+       {
+               .mask = 0b01111100,
+               .value = 0b00001100,
+       },
+       {
+               .mask = 0b01111110,
+               .value = 0b01000010,
+       },
+       {
+               .mask = 0b01111100,
+               .value = 0b01000100,
+       },
+       {
+               .mask = 0b01111000,
+               .value = 0b01001000,
+       },
+       {
+               .mask = 0b01110000,
+               .value = 0b01010000,
+       },
+       {
+               .mask = 0b10111111,
+               .value = 0b00000000,
+       },
+       {
+               .mask = 0b11111111,
+               .value = 0b00000100,
+       },
+       {
+               .mask = 0b11111110,
+               .value = 0b00000110,
+       },
+       {
+               .mask = 0b11111011,
+               .value = 0b00010011,
+       },
+       {
+               .mask = 0b11111001,
+               .value = 0b10010000,
+       },
+       {
+               .mask = 0b11111010,
+               .value = 0b10010000,
+       },
+};
+
+static int dp_reg_2src_stack_fields[] = {
+       0b10000000,
+       0b10000100,
+       0b10000101,
+       0b10001100,
+       0b11000000,
+};
+
+int arm_decode_dp_reg_2src(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char sf = 0, S = 0, opcode = 0, rn = 0, rd = 0;
+       unsigned char decode_field = 0;
+       int i = 0;
+
+       sf = EXTRACT_BIT(instr, 31);
+       S = EXTRACT_BIT(instr, 29);
+       opcode = (instr >> 10) & ONES(6);
+
+       decode_field = (sf << 7) | (S << 6) | opcode;
+
+       for (i = 0; i < ARRAY_SIZE(dp_reg_2src_decoder); i++) {
+               if ((decode_field & dp_reg_2src_decoder[i].mask)
+                               == dp_reg_2src_decoder[i].value) {
+                       return arm_decode_unknown(\
+                                       instr, type, immediate, op);
+               }
+       }
+
+       *type = 0;
+       for (i = 0; i < ARRAY_SIZE(dp_reg_2src_stack_fields); i++) {
+               if (opcode == dp_reg_2src_stack_fields[i]) {
+                       *type = INSN_OTHER;
+                       break;
+               }
+       }
+       if (*type == 0) {
+               *type = INSN_OTHER;
+               return 0;
+       }
+
+       rn = (instr >> 5) & ONES(5);
+       rd = instr & ONES(5);
+
+#define IRG_OPCODE     0b10000100
+       if ((rn != CFI_SP && opcode != IRG_OPCODE)
+               || (opcode == IRG_OPCODE && rd != CFI_SP
+                       && rn != CFI_SP)) {
+               *type = INSN_OTHER;
+               return 0;
+       }
+#undef IRG_OPCODE
+
+       *type = INSN_STACK;
+       op->dest.reg = rd;
+       op->dest.type = OP_DEST_REG;
+       op->dest.offset = 0;
+
+       op->src.reg = rn;
+       op->src.type = OP_DEST_REG;
+       op->src.offset = 0;
+
+       return 0;
+}
+
+
+static struct aarch64_insn_decoder dp_reg_1src_decoder[] = {
+       {
+               .mask = 0b0000000001000,
+               .value = 0b0000000001000,
+       },
+       {
+               .mask = 0b0000000010000,
+               .value = 0b0000000010000,
+       },
+       {
+               .mask = 0b0000000100000,
+               .value = 0b0000000100000,
+       },
+       {
+               .mask = 0b0000001000000,
+               .value = 0b0000001000000,
+       },
+       {
+               .mask = 0b0000010000000,
+               .value = 0b0000010000000,
+       },
+       {
+               .mask = 0b0000100000000,
+               .value = 0b0000100000000,
+       },
+       {
+               .mask = 0b0001000000000,
+               .value = 0b0001000000000,
+       },
+       {
+               .mask = 0b0010000000000,
+               .value = 0b0010000000000,
+       },
+       {
+               .mask = 0b0111111111110,
+               .value = 0b0000000000110,
+       },
+       {
+               .mask = 0b0100000000000,
+               .value = 0b0100000000000,
+       },
+};
+
+int arm_decode_dp_reg_1src(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char sf = 0, S = 0, opcode2 = 0, opcode = 0;
+       u32 decode_field = 0;
+       int i = 0;
+
+       sf = EXTRACT_BIT(instr, 31);
+       S = EXTRACT_BIT(instr, 29);
+       opcode2 = (instr >> 16) & ONES(5);
+       opcode = (instr >> 10) & ONES(6);
+
+       decode_field = (sf << 6) | (S << 5) | opcode2;
+       decode_field = (decode_field << 6) | opcode;
+
+       for (i = 0; i < ARRAY_SIZE(dp_reg_1src_decoder); i++) {
+               if ((decode_field & dp_reg_1src_decoder[i].mask) ==
+                               dp_reg_1src_decoder[i].value) {
+                       return arm_decode_unknown(instr, type, immediate, op);
+               }
+       }
+       *type = INSN_OTHER;
+       return 0;
+}
+
+int arm_decode_dp_reg_logi(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char sf = 0, imm6 = 0;
+
+       sf = EXTRACT_BIT(instr, 31);
+       imm6 = (instr >> 10) & ONES(6);
+
+       if (imm6 >= 0b100000 && !sf)
+               return arm_decode_unknown(instr, type, immediate, op);
+
+       *type = INSN_OTHER;
+       return 0;
+}
+
+int arm_decode_dp_reg_adds(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char sf = 0, shift = 0, imm6 = 0;
+
+       sf = EXTRACT_BIT(instr, 31);
+       shift = (instr >> 22) & ONES(2);
+       imm6 = (instr >> 10) & ONES(6);
+
+       if ((imm6 >= 0b100000 && !sf) || shift == 0b11)
+               return arm_decode_unknown(instr, type, immediate, op);
+
+       *type = INSN_OTHER;
+       return 0;
+}
+
+int arm_decode_dp_reg_adde(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+
+       unsigned char S = 0, opt = 0, imm3 = 0, rn = 0, rd = 0;
+
+       S = EXTRACT_BIT(instr, 29);
+       opt = (instr >> 22) & ONES(2);
+       imm3 = (instr >> 10) & ONES(3);
+       rn = (instr >> 5) & ONES(5);
+       rd = instr & ONES(5);
+
+       if (opt != 0 || imm3 >= 0b101)
+               return arm_decode_unknown(instr, type, immediate, op);
+
+       if (rd == CFI_SP && S == 0) {
+               *type = INSN_STACK;
+               op->dest.reg = CFI_SP;
+               op->dest.type = OP_DEST_REG;
+               op->src.type = OP_SRC_ADD;
+               op->src.reg = rn;
+
+               return 0;
+       }
+       *type = INSN_OTHER;
+       return 0;
+}
+
+
+int arm_decode_dp_reg_addc(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       *type = INSN_OTHER;
+       return 0;
+}
+
+int arm_decode_dp_reg_rota(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char sf = 0, S = 0, op_bit = 0, o2 = 0;
+       unsigned char decode_field = 0;
+
+       sf = EXTRACT_BIT(instr, 31);
+       op_bit = EXTRACT_BIT(instr, 30);
+       S = EXTRACT_BIT(instr, 29);
+       o2 = EXTRACT_BIT(instr, 4);
+
+       decode_field = (sf << 3) | (op_bit << 2) | (S << 1) | o2;
+
+       if (decode_field != 0b1010)
+               return arm_decode_unknown(instr, type, immediate, op);
+
+       *type = INSN_OTHER;
+       return 0;
+}
+
+
+int arm_decode_dp_reg_eval(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char sf = 0, S = 0, op_bit = 0, o3 = 0, sz = 0;
+       unsigned char opcode2 = 0, mask = 0;
+       u32 decode_field = 0;
+
+       sf = EXTRACT_BIT(instr, 31);
+       op_bit = EXTRACT_BIT(instr, 30);
+       S = EXTRACT_BIT(instr, 29);
+       sz = EXTRACT_BIT(instr, 14);
+       o3 = EXTRACT_BIT(instr, 4);
+
+       opcode2 = (instr >> 15) & ONES(6);
+       mask = instr & ONES(4);
+
+       decode_field = (sf << 2) | (op_bit << 1) | S;
+       decode_field = (decode_field << 12) | (opcode2 << 6) | (sz << 5);
+       decode_field |= (o3 << 4) | mask;
+
+#define DP_EVAL_SETF_1 0b001000000001101
+#define DP_EVAL_SETF_2 0b001000000101101
+
+       if (decode_field != DP_EVAL_SETF_1
+               && decode_field != DP_EVAL_SETF_2) {
+               return arm_decode_unknown(instr, type, immediate, op);
+       }
+
+       *type = INSN_OTHER;
+       return 0;
+#undef DP_EVAL_SETF_1
+#undef DP_EVAL_SETF_2
+}
+
+int arm_decode_dp_reg_cmpr(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char S = 0, o2 = 0, o3 = 0;
+
+       S = EXTRACT_BIT(instr, 29);
+       o2 = EXTRACT_BIT(instr, 10);
+       o3 = EXTRACT_BIT(instr, 4);
+
+       if (!S || o2 || o3)
+               return arm_decode_unknown(instr, type, immediate, op);
+
+       *type = INSN_OTHER;
+       return 0;
+}
+
+int arm_decode_dp_reg_csel(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char S = 0, op2 = 0;
+
+       S = EXTRACT_BIT(instr, 29);
+       op2 = (instr >> 10) & ONES(2);
+
+       if (S || op2 >= 0b10)
+               return arm_decode_unknown(instr, type, immediate, op);
+
+       *type = INSN_OTHER;
+       return 0;
+}
+
+int arm_decode_dp_reg_cmpi(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       return arm_decode_dp_reg_cmpr(instr, type, immediate, op);
+}
+
+static int dp_reg_3src_fields[] = {
+};
+
+static struct aarch64_insn_decoder dp_reg_3src_decoder[] = {
+       {
+               .mask = 0b0111111,
+               .value = 0b0000101,
+       },
+       {
+               .mask = 0b0111110,
+               .value = 0b0000110,
+       },
+       {
+               .mask = 0b0111110,
+               .value = 0b0001000,
+       },
+       {
+               .mask = 0b0111111,
+               .value = 0b0001101,
+       },
+       {
+               .mask = 0b0111110,
+               .value = 0b0001110,
+       },
+       {
+               .mask = 0b0110000,
+               .value = 0b0010000,
+       },
+       {
+               .mask = 0b0100000,
+               .value = 0b0100000,
+       },
+       {
+               .mask = 0b1111111,
+               .value = 0b0000010,
+       },
+       {
+               .mask = 0b1111111,
+               .value = 0b0000011,
+       },
+       {
+               .mask = 0b1111111,
+               .value = 0b0000100,
+       },
+       {
+               .mask = 0b1111111,
+               .value = 0b0001010,
+       },
+       {
+               .mask = 0b1111111,
+               .value = 0b0001011,
+       },
+       {
+               .mask = 0b1111111,
+               .value = 0b0001100,
+       },
+};
+
+int arm_decode_dp_reg_3src(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       unsigned char sf = 0, op54 = 0, op31 = 0, o0 = 0;
+       unsigned char decode_field = 0;
+       int i = 0;
+
+       sf = EXTRACT_BIT(instr, 31);
+       op54 = (instr >> 29) & ONES(2);
+       op31 = (instr >> 21) & ONES(3);
+       o0 = EXTRACT_BIT(instr, 15);
+
+       decode_field = (sf << 6) | (op54 << 4) | (op31 << 1) | o0;
+
+       for (i = 0; i < ARRAY_SIZE(dp_reg_3src_fields); i++) {
+               if ((decode_field & dp_reg_3src_decoder[i].mask)
+                               == dp_reg_3src_decoder[i].value) {
+                       return arm_decode_unknown(instr, type, immediate, op);
+               }
+       }
+
+       *type = INSN_OTHER;
+       return 0;
+
+}
+unsigned long arch_compute_jump_destination(struct instruction *insn)
+{
+       return insn->offset + insn->immediate;
+}
+
+static struct aarch64_insn_decoder sve_enc_decoder[] = {
+       {
+               .mask = 0b1111010000111000,
+               .value = 0b0000010000011000,
+       },
+       {
+               .mask = 0b1111110000111000,
+               .value = 0b0001110000000000,
+       },
+       {
+               .mask = 0b1111010000110000,
+               .value = 0b0011010000010000,
+       },
+       {
+               .mask = 0b1111011100111000,
+               .value = 0b0011010100101000,
+       },
+       {
+               .mask = 0b1111011000110000,
+               .value = 0b0011011000100000,
+       },
+       {
+               .mask = 0b1111010000100000,
+               .value = 0b0100000000100000,
+       },
+       {
+               .mask = 0b1111000000000000,
+               .value = 0b0101000000000000,
+       },
+       {
+               .mask = 0b1111011111111000,
+               .value = 0b0110000000101000,
+       },
+       {
+               .mask = 0b1111011111110000,
+               .value = 0b0110000000110000,
+       },
+       {
+               .mask = 0b1111011111100000,
+               .value = 0b0110000001100000,
+       },
+       {
+               .mask = 0b1111011110100000,
+               .value = 0b0110000010100000,
+       },
+       {
+               .mask = 0b1111011100100000,
+               .value = 0b0110000100100000,
+       },
+       {
+               .mask = 0b1111011000100000,
+               .value = 0b0110001000100000,
+       },
+       {
+               .mask = 0b1111010000110110,
+               .value = 0b0110010000000010,
+       },
+       {
+               .mask = 0b1111010000111111,
+               .value = 0b0110010000001001,
+       },
+       {
+               .mask = 0b1111010000111100,
+               .value = 0b0110010000001100,
+       },
+       {
+               .mask = 0b1111010000110000,
+               .value = 0b0110010000010000,
+       },
+       {
+               .mask = 0b1111010000100000,
+               .value = 0b0110010000100000,
+       },
+       {
+               .mask = 0b1111011100111100,
+               .value = 0b0111000100001000,
+       },
+};
+
+/*
+ * Since these instructions are optional (not present on all arm processors)
+ * we consider that they will never be used to save/restore stack frame.
+ */
+int arm_decode_sve_encoding(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op)
+{
+       int i = 0;
+       unsigned char op0 = 0, op1 = 0, op2 = 0, op3 = 0;
+       u32 decode_field = 0;
+
+       op0 = (instr >> 29) & ONES(3);
+       op1 = (instr >> 23) & ONES(2);
+       op2 = (instr >> 17) & ONES(5);
+       op3 = (instr >> 10) & ONES(6);
+
+       decode_field = (op0 << 2) | op1;
+       decode_field = (decode_field << 5) | op2;
+       decode_field = (decode_field << 6) | op3;
+
+       for (i = 0; i < ARRAY_SIZE(sve_enc_decoder); i++) {
+               if ((decode_field & sve_enc_decoder[i].mask)
+                       == sve_enc_decoder[i].value)
+                       return arm_decode_unknown(instr, type, immediate, op);
+       }
+
+       *type = INSN_OTHER;
+
+       return 0;
+}
diff --git a/tools/objtool/arch/arm64/include/arch_special.h 
b/tools/objtool/arch/arm64/include/arch_special.h
new file mode 100644
index 000000000000..54bcce4c58c0
--- /dev/null
+++ b/tools/objtool/arch/arm64/include/arch_special.h
@@ -0,0 +1,44 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define EX_ENTRY_SIZE          8
+#define EX_ORIG_OFFSET         0
+#define EX_NEW_OFFSET          4
+
+#define JUMP_ENTRY_SIZE                16
+#define JUMP_ORIG_OFFSET       0
+#define JUMP_NEW_OFFSET                4
+
+#define ALT_ENTRY_SIZE         12
+#define ALT_ORIG_OFFSET                0
+#define ALT_NEW_OFFSET         4
+#define ALT_FEATURE_OFFSET     8
+#define ALT_ORIG_LEN_OFFSET    10
+#define ALT_NEW_LEN_OFFSET     11
+
+/*
+ * On arm64 the .altinstr_replacement is not always marked
+ * as containing executable instruction. But we still want
+ * to process it so we ignore the SHF_EXEC flag
+ */
+#define IGNORE_SHF_EXEC_FLAG   1
+
+/*
+ * The jump table detection is not the same on arm64 so for
+ * now we just detect if it is a dynamic jump (br <Xn> insn)
+ */
+#define JUMP_DYNAMIC_IS_SWITCH_TABLE   1
+
+#define X86_FEATURE_POPCNT (4*32+23)
diff --git a/tools/objtool/arch/arm64/include/asm/orc_types.h 
b/tools/objtool/arch/arm64/include/asm/orc_types.h
new file mode 100644
index 000000000000..46f516dd80ce
--- /dev/null
+++ b/tools/objtool/arch/arm64/include/asm/orc_types.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 Josh Poimboeuf <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _ORC_TYPES_H
+#define _ORC_TYPES_H
+
+#include <linux/types.h>
+#include <linux/compiler.h>
+
+/*
+ * The ORC_REG_* registers are base registers which are used to find other
+ * registers on the stack.
+ *
+ * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the
+ * address of the previous frame: the caller's SP before it called the current
+ * function.
+ *
+ * ORC_REG_UNDEFINED means the corresponding register's value didn't change in
+ * the current frame.
+ *
+ * The most commonly used base registers are SP and BP -- which the previous SP
+ * is usually based on -- and PREV_SP and UNDEFINED -- which the previous BP is
+ * usually based on.
+ *
+ * The rest of the base registers are needed for special cases like entry code
+ * and GCC realigned stacks.
+ */
+#define ORC_REG_UNDEFINED              0
+#define ORC_REG_PREV_SP                        1
+#define ORC_REG_DX                     2
+#define ORC_REG_DI                     3
+#define ORC_REG_BP                     4
+#define ORC_REG_SP                     5
+#define ORC_REG_R10                    6
+#define ORC_REG_R13                    7
+#define ORC_REG_BP_INDIRECT            8
+#define ORC_REG_SP_INDIRECT            9
+#define ORC_REG_MAX                    15
+
+/*
+ * ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the
+ * caller's SP right before it made the call).  Used for all callable
+ * functions, i.e. all C code and all callable asm functions.
+ *
+ * ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points
+ * to a fully populated pt_regs from a syscall, interrupt, or exception.
+ *
+ * ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset
+ * points to the iret return frame.
+ *
+ * The UNWIND_HINT macros are used only for the unwind_hint struct.  They
+ * aren't used in struct orc_entry due to size and complexity constraints.
+ * Objtool converts them to real types when it converts the hints to orc
+ * entries.
+ */
+#define ORC_TYPE_CALL                  0
+#define ORC_TYPE_REGS                  1
+#define ORC_TYPE_REGS_IRET             2
+#define UNWIND_HINT_TYPE_SAVE          3
+#define UNWIND_HINT_TYPE_RESTORE       4
+
+#ifndef __ASSEMBLY__
+/*
+ * This struct is more or less a vastly simplified version of the DWARF Call
+ * Frame Information standard.  It contains only the necessary parts of DWARF
+ * CFI, simplified for ease of access by the in-kernel unwinder.  It tells the
+ * unwinder how to find the previous SP and BP (and sometimes entry regs) on
+ * the stack for a given code address.  Each instance of the struct corresponds
+ * to one or more code locations.
+ */
+struct orc_entry {
+       s16             sp_offset;
+       s16             bp_offset;
+       unsigned        sp_reg:4;
+       unsigned        bp_reg:4;
+       unsigned        type:2;
+       unsigned        end:1;
+} __packed;
+
+/*
+ * This struct is used by asm and inline asm code to manually annotate the
+ * location of registers on the stack for the ORC unwinder.
+ *
+ * Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*.
+ */
+struct unwind_hint {
+       u32             ip;
+       s16             sp_offset;
+       u8              sp_reg;
+       u8              type;
+       u8              end;
+};
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ORC_TYPES_H */
diff --git a/tools/objtool/arch/arm64/include/bit_operations.h 
b/tools/objtool/arch/arm64/include/bit_operations.h
new file mode 100644
index 000000000000..bdfa9d183995
--- /dev/null
+++ b/tools/objtool/arch/arm64/include/bit_operations.h
@@ -0,0 +1,22 @@
+#ifndef _BIT_OPERATIONS_H
+#define _BIT_OPERATIONS_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <linux/types.h>
+
+#define ONES(N)                (((__uint128_t)1 << (N)) - 1)
+#define ZERO_EXTEND(X, N)      ((X) & ONES(N))
+#define EXTRACT_BIT(X, N)      (((X) >> (N)) & ONES(1))
+#define SIGN_EXTEND(X, N)      ((((unsigned long) -1 + (EXTRACT_BIT(X, N - 1) 
^ 1)) << N) | X)
+
+u64 replicate(u64 x, int size, int n);
+
+u64 ror(u64 x, int size, int shift);
+
+int highest_set_bit(u32 x);
+
+__uint128_t decode_bit_masks(unsigned char N, unsigned char imms,
+                       unsigned char immr, bool immediate);
+
+#endif /* _BIT_OPERATIONS_H */
diff --git a/tools/objtool/arch/arm64/include/cfi.h 
b/tools/objtool/arch/arm64/include/cfi.h
new file mode 100644
index 000000000000..8084b95543b8
--- /dev/null
+++ b/tools/objtool/arch/arm64/include/cfi.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015-2017 Josh Poimboeuf <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _OBJTOOL_CFI_H
+#define _OBJTOOL_CFI_H
+
+#define CFI_UNDEFINED          -1
+#define CFI_CFA                        -2
+#define CFI_SP_INDIRECT                -3
+#define CFI_BP_INDIRECT                -4
+
+#define CFI_R0                 0
+#define CFI_R1                 1
+#define CFI_R2                 2
+#define CFI_R3                 3
+#define CFI_R4                 4
+#define CFI_R5                 5
+#define CFI_R6                 6
+#define CFI_R7                 7
+#define CFI_R8                 8
+#define CFI_R9                 9
+#define CFI_R10                        10
+#define CFI_R11                        11
+#define CFI_R12                        12
+#define CFI_R13                        13
+#define CFI_R14                        14
+#define CFI_R15                        15
+#define CFI_R16                        16
+#define CFI_R17                        17
+#define CFI_R18                        18
+#define CFI_R19                        19
+#define CFI_R20                        20
+#define CFI_R21                        21
+#define CFI_R22                        22
+#define CFI_R23                        23
+#define CFI_R24                        24
+#define CFI_R25                        25
+#define CFI_R26                        26
+#define CFI_R27                        27
+#define CFI_R28                        28
+#define CFI_R29                        29
+#define CFI_FP                 CFI_R29
+#define CFI_BP                 CFI_FP
+#define CFI_R30                        30
+#define CFI_LR                 CFI_R30
+#define CFI_SP                 31
+
+
+
+#define CFI_NUM_REGS           32
+
+struct cfi_reg {
+       int base;
+       int offset;
+};
+
+struct cfi_state {
+       struct cfi_reg cfa;
+       struct cfi_reg regs[CFI_NUM_REGS];
+};
+
+#endif /* _OBJTOOL_CFI_H */
diff --git a/tools/objtool/arch/arm64/include/insn_decode.h 
b/tools/objtool/arch/arm64/include/insn_decode.h
new file mode 100644
index 000000000000..b4229cbe3ae1
--- /dev/null
+++ b/tools/objtool/arch/arm64/include/insn_decode.h
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2019 Raphael Gault <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _ARM_INSN_DECODE_H
+#define _ARM_INSN_DECODE_H
+
+#include "../../../arch.h"
+
+#define INSN_RESERVED  0b0000
+#define INSN_UNKNOWN   0b0001
+#define INSN_SVE_ENC   0b0010
+#define INSN_UNALLOC   0b0011
+#define INSN_DP_IMM    0b1001  //0x100x
+#define INSN_BRANCH    0b1011  //0x101x
+#define INSN_LD_ST_4   0b0100  //0bx1x0
+#define INSN_LD_ST_6   0b0110  //0bx1x0
+#define INSN_LD_ST_C   0b1100  //0bx1x0
+#define INSN_LD_ST_E   0b1110  //0bx1x0
+#define INSN_DP_REG_5  0b0101  //0bx101
+#define INSN_DP_REG_D  0b1101  //0bx101
+#define INSN_DP_SIMD_7 0b0111  //0bx111
+#define INSN_DP_SIMD_F 0b1111  //0bx111
+
+#define INSN_PCREL     0b001   //0b00x
+#define INSN_ADD_SUB   0b010
+#define INSN_ADD_TAG   0b011
+#define INSN_LOGICAL   0b100
+#define INSN_MOVE_WIDE 0b101
+#define INSN_BITFIELD  0b110
+#define INSN_EXTRACT   0b111
+
+#define INSN_BR_UNCOND_IMM_L   0b0001
+#define INSN_CP_BR_IMM_L       0b0010
+#define INSN_TST_BR_IMM_L      0b0011
+#define INSN_BR_COND_IMM       0b0100
+#define INSN_BR_UNKNOWN_IMM    0b0111
+#define INSN_BR_UNCOND_IMM_H   0b1001
+#define INSN_CP_BR_IMM_H       0b1010
+#define INSN_TST_BR_IMM_H      0b1011
+#define INSN_BR_SYS_NO_IMM     0b1101
+
+#define INSN_OP1_HINTS         0b01000000110010
+#define INSN_OP1_BARRIERS      0b01000000110011
+
+#define COMPOSED_INSN_REGS_NUM 2
+#define INSN_COMPOSED  1
+
+#define ADR_SOURCE     -1
+
+
+struct insn_decode_state {
+       bool is_composed_insn;
+       unsigned char current_reg_num;
+       unsigned char insn_regs_num;
+       unsigned char insn_type;
+       unsigned long immediate;
+       unsigned long curr_offset;
+       struct stack_op op;
+       unsigned char regs[COMPOSED_INSN_REGS_NUM];
+};
+
+typedef int (*arm_decode_class)(u32 instr, unsigned char *type,
+                               unsigned long *immediate, struct stack_op *op);
+
+struct aarch64_insn_decoder {
+       u32 mask;
+       u32 value;
+       arm_decode_class decode_func;
+};
+
+/* arm64 instruction classes */
+int arm_decode_reserved(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_sve_encoding(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_dp_imm(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_dp_reg(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_br_sys(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_ld_st(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_dp_simd(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_unknown(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+
+
+/* arm64 data processing -- immediate subclasses */
+int arm_decode_pcrel(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_add_sub(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_add_sub_tags(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_logical(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_move_wide(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_bitfield(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_extract(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+
+/* arm64 branch, exception generation, system insn subclasses */
+int arm_decode_br_uncond_imm(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_br_comp_imm(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_br_tst_imm(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_br_cond_imm(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+#if 0
+int arm_decode_br_sys_no_imm(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+#endif
+
+int arm_decode_br_uncond_reg(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+
+int arm_decode_br_reg(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_except_gen(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_hints(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_barriers(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_pstate(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_system_insn(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_system_regs(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+
+/* arm64 load/store instructions */
+int arm_decode_adv_simd_mult(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_adv_simd_mult_post(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_adv_simd_single(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_adv_simd_single_post(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_ld_st_mem_tags(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_ldapr_stlr_unsc_imm(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_ld_regs_literal(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_ld_st_noalloc_pair_off(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_ld_st_regs_pair_post(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_ld_st_regs_pair_off(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_ld_st_regs_pair_pre(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_ld_st_regs_unsc_imm(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_ld_st_imm_post(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_ld_st_imm_unpriv(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_ld_st_imm_pre(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_atomic(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_ld_st_regs_off(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_ld_st_regs_pac(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_ld_st_regs_unsigned(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+
+int arm_decode_ld_st_exclusive(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+
+/* arm64 data processing -- registers instructions */
+int arm_decode_dp_reg_1src(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_dp_reg_2src(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_dp_reg_3src(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_dp_reg_adde(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_dp_reg_cmpi(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_dp_reg_eval(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_dp_reg_cmpr(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_dp_reg_rota(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_dp_reg_csel(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_dp_reg_addc(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_dp_reg_adds(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+int arm_decode_dp_reg_logi(u32 instr, unsigned char *type,
+                       unsigned long *immediate, struct stack_op *op);
+#endif /* _ARM_INSN_DECODE_H */
diff --git a/tools/objtool/arch/arm64/orc_gen.c 
b/tools/objtool/arch/arm64/orc_gen.c
new file mode 100644
index 000000000000..81383d34a743
--- /dev/null
+++ b/tools/objtool/arch/arm64/orc_gen.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 Josh Poimboeuf <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../orc.h"
+#include "../../check.h"
+#include "../../warn.h"
+
+int arch_create_orc(struct objtool_file *file)
+{
+       WARN("arm64 architecture does not yet support orc");
+       return -1;
+}
+
+int arch_create_orc_sections(struct objtool_file *file)
+{
+       WARN("arm64 architecture does not yet support orc");
+       return -1;
+}
+
+int arch_orc_read_unwind_hints(struct objtool_file *file)
+{
+       return 0;
+}
-- 
2.17.1

Reply via email to