Merge git://git.infradead.org/mtd-2.6
[pandora-kernel.git] / arch / tile / kernel / backtrace.c
index 1b0a410..d3c41c1 100644 (file)
@@ -19,9 +19,6 @@
 
 #include <arch/chip.h>
 
-#if TILE_CHIP < 10
-
-
 #include <asm/opcode-tile.h>
 
 
 #define TREG_LR 55
 
 
+#if TILE_CHIP >= 10
+#define tile_bundle_bits tilegx_bundle_bits
+#define TILE_MAX_INSTRUCTIONS_PER_BUNDLE TILEGX_MAX_INSTRUCTIONS_PER_BUNDLE
+#define TILE_BUNDLE_ALIGNMENT_IN_BYTES TILEGX_BUNDLE_ALIGNMENT_IN_BYTES
+#define tile_decoded_instruction tilegx_decoded_instruction
+#define tile_mnemonic tilegx_mnemonic
+#define parse_insn_tile parse_insn_tilegx
+#define TILE_OPC_IRET TILEGX_OPC_IRET
+#define TILE_OPC_ADDI TILEGX_OPC_ADDI
+#define TILE_OPC_ADDLI TILEGX_OPC_ADDLI
+#define TILE_OPC_INFO TILEGX_OPC_INFO
+#define TILE_OPC_INFOL TILEGX_OPC_INFOL
+#define TILE_OPC_JRP TILEGX_OPC_JRP
+#define TILE_OPC_MOVE TILEGX_OPC_MOVE
+#define OPCODE_STORE TILEGX_OPC_ST
+typedef long long bt_int_reg_t;
+#else
+#define OPCODE_STORE TILE_OPC_SW
+typedef int bt_int_reg_t;
+#endif
+
 /** A decoded bundle used for backtracer analysis. */
-typedef struct {
+struct BacktraceBundle {
        tile_bundle_bits bits;
        int num_insns;
        struct tile_decoded_instruction
        insns[TILE_MAX_INSTRUCTIONS_PER_BUNDLE];
-} BacktraceBundle;
+};
 
 
 /* This implementation only makes sense for native tools. */
 /** Default function to read memory. */
-static bool
-bt_read_memory(void *result, VirtualAddress addr, size_t size, void *extra)
+static bool bt_read_memory(void *result, VirtualAddress addr,
+                          unsigned int size, void *extra)
 {
        /* FIXME: this should do some horrible signal stuff to catch
         * SEGV cleanly and fail.
@@ -58,11 +76,11 @@ bt_read_memory(void *result, VirtualAddress addr, size_t size, void *extra)
  * has the specified mnemonic, and whose first 'num_operands_to_match'
  * operands exactly match those in 'operand_values'.
  */
-static const struct tile_decoded_instruction*
-find_matching_insn(const BacktraceBundle *bundle,
-                  tile_mnemonic mnemonic,
-                  const int *operand_values,
-                  int num_operands_to_match)
+static const struct tile_decoded_instruction *find_matching_insn(
+       const struct BacktraceBundle *bundle,
+       tile_mnemonic mnemonic,
+       const int *operand_values,
+       int num_operands_to_match)
 {
        int i, j;
        bool match;
@@ -90,8 +108,7 @@ find_matching_insn(const BacktraceBundle *bundle,
 }
 
 /** Does this bundle contain an 'iret' instruction? */
-static inline bool
-bt_has_iret(const BacktraceBundle *bundle)
+static inline bool bt_has_iret(const struct BacktraceBundle *bundle)
 {
        return find_matching_insn(bundle, TILE_OPC_IRET, NULL, 0) != NULL;
 }
@@ -99,8 +116,7 @@ bt_has_iret(const BacktraceBundle *bundle)
 /** Does this bundle contain an 'addi sp, sp, OFFSET' or
  * 'addli sp, sp, OFFSET' instruction, and if so, what is OFFSET?
  */
-static bool
-bt_has_addi_sp(const BacktraceBundle *bundle, int *adjust)
+static bool bt_has_addi_sp(const struct BacktraceBundle *bundle, int *adjust)
 {
        static const int vals[2] = { TREG_SP, TREG_SP };
 
@@ -108,6 +124,12 @@ bt_has_addi_sp(const BacktraceBundle *bundle, int *adjust)
                find_matching_insn(bundle, TILE_OPC_ADDI, vals, 2);
        if (insn == NULL)
                insn = find_matching_insn(bundle, TILE_OPC_ADDLI, vals, 2);
+#if TILE_CHIP >= 10
+       if (insn == NULL)
+               insn = find_matching_insn(bundle, TILEGX_OPC_ADDXLI, vals, 2);
+       if (insn == NULL)
+               insn = find_matching_insn(bundle, TILEGX_OPC_ADDXI, vals, 2);
+#endif
        if (insn == NULL)
                return false;
 
@@ -120,8 +142,7 @@ bt_has_addi_sp(const BacktraceBundle *bundle, int *adjust)
  * as an unsigned value by this code since that's what the caller wants.
  * Returns the number of info ops found.
  */
-static int
-bt_get_info_ops(const BacktraceBundle *bundle,
+static int bt_get_info_ops(const struct BacktraceBundle *bundle,
                int operands[MAX_INFO_OPS_PER_BUNDLE])
 {
        int num_ops = 0;
@@ -143,8 +164,7 @@ bt_get_info_ops(const BacktraceBundle *bundle,
 /** Does this bundle contain a jrp instruction, and if so, to which
  * register is it jumping?
  */
-static bool
-bt_has_jrp(const BacktraceBundle *bundle, int *target_reg)
+static bool bt_has_jrp(const struct BacktraceBundle *bundle, int *target_reg)
 {
        const struct tile_decoded_instruction *insn =
                find_matching_insn(bundle, TILE_OPC_JRP, NULL, 0);
@@ -156,8 +176,7 @@ bt_has_jrp(const BacktraceBundle *bundle, int *target_reg)
 }
 
 /** Does this bundle modify the specified register in any way? */
-static bool
-bt_modifies_reg(const BacktraceBundle *bundle, int reg)
+static bool bt_modifies_reg(const struct BacktraceBundle *bundle, int reg)
 {
        int i, j;
        for (i = 0; i < bundle->num_insns; i++) {
@@ -177,43 +196,77 @@ bt_modifies_reg(const BacktraceBundle *bundle, int reg)
 }
 
 /** Does this bundle modify sp? */
-static inline bool
-bt_modifies_sp(const BacktraceBundle *bundle)
+static inline bool bt_modifies_sp(const struct BacktraceBundle *bundle)
 {
        return bt_modifies_reg(bundle, TREG_SP);
 }
 
 /** Does this bundle modify lr? */
-static inline bool
-bt_modifies_lr(const BacktraceBundle *bundle)
+static inline bool bt_modifies_lr(const struct BacktraceBundle *bundle)
 {
        return bt_modifies_reg(bundle, TREG_LR);
 }
 
 /** Does this bundle contain the instruction 'move fp, sp'? */
-static inline bool
-bt_has_move_r52_sp(const BacktraceBundle *bundle)
+static inline bool bt_has_move_r52_sp(const struct BacktraceBundle *bundle)
 {
        static const int vals[2] = { 52, TREG_SP };
        return find_matching_insn(bundle, TILE_OPC_MOVE, vals, 2) != NULL;
 }
 
-/** Does this bundle contain the instruction 'sw sp, lr'? */
-static inline bool
-bt_has_sw_sp_lr(const BacktraceBundle *bundle)
+/** Does this bundle contain a store of lr to sp? */
+static inline bool bt_has_sw_sp_lr(const struct BacktraceBundle *bundle)
 {
        static const int vals[2] = { TREG_SP, TREG_LR };
-       return find_matching_insn(bundle, TILE_OPC_SW, vals, 2) != NULL;
+       return find_matching_insn(bundle, OPCODE_STORE, vals, 2) != NULL;
+}
+
+#if TILE_CHIP >= 10
+/** Track moveli values placed into registers. */
+static inline void bt_update_moveli(const struct BacktraceBundle *bundle,
+                                   int moveli_args[])
+{
+       int i;
+       for (i = 0; i < bundle->num_insns; i++) {
+               const struct tile_decoded_instruction *insn =
+                       &bundle->insns[i];
+
+               if (insn->opcode->mnemonic == TILEGX_OPC_MOVELI) {
+                       int reg = insn->operand_values[0];
+                       moveli_args[reg] = insn->operand_values[1];
+               }
+       }
+}
+
+/** Does this bundle contain an 'add sp, sp, reg' instruction
+ * from a register that we saw a moveli into, and if so, what
+ * is the value in the register?
+ */
+static bool bt_has_add_sp(const struct BacktraceBundle *bundle, int *adjust,
+                         int moveli_args[])
+{
+       static const int vals[2] = { TREG_SP, TREG_SP };
+
+       const struct tile_decoded_instruction *insn =
+               find_matching_insn(bundle, TILEGX_OPC_ADDX, vals, 2);
+       if (insn) {
+               int reg = insn->operand_values[2];
+               if (moveli_args[reg]) {
+                       *adjust = moveli_args[reg];
+                       return true;
+               }
+       }
+       return false;
 }
+#endif
 
 /** Locates the caller's PC and SP for a program starting at the
  * given address.
  */
-static void
-find_caller_pc_and_caller_sp(CallerLocation *location,
-                            const VirtualAddress start_pc,
-                            BacktraceMemoryReader read_memory_func,
-                            void *read_memory_func_extra)
+static void find_caller_pc_and_caller_sp(CallerLocation *location,
+                                        const VirtualAddress start_pc,
+                                        BacktraceMemoryReader read_memory_func,
+                                        void *read_memory_func_extra)
 {
        /* Have we explicitly decided what the sp is,
         * rather than just the default?
@@ -237,6 +290,11 @@ find_caller_pc_and_caller_sp(CallerLocation *location,
        int next_bundle = 0;
        VirtualAddress pc;
 
+#if TILE_CHIP >= 10
+       /* Naively try to track moveli values to support addx for -m32. */
+       int moveli_args[TILEGX_NUM_REGISTERS] = { 0 };
+#endif
+
        /* Default to assuming that the caller's sp is the current sp.
         * This is necessary to handle the case where we start backtracing
         * right at the end of the epilog.
@@ -253,7 +311,7 @@ find_caller_pc_and_caller_sp(CallerLocation *location,
 
        for (pc = start_pc;; pc += sizeof(tile_bundle_bits)) {
 
-               BacktraceBundle bundle;
+               struct BacktraceBundle bundle;
                int num_info_ops, info_operands[MAX_INFO_OPS_PER_BUNDLE];
                int one_ago, jrp_reg;
                bool has_jrp;
@@ -390,7 +448,11 @@ find_caller_pc_and_caller_sp(CallerLocation *location,
 
                if (!sp_determined) {
                        int adjust;
-                       if (bt_has_addi_sp(&bundle, &adjust)) {
+                       if (bt_has_addi_sp(&bundle, &adjust)
+#if TILE_CHIP >= 10
+                           || bt_has_add_sp(&bundle, &adjust, moveli_args)
+#endif
+                               ) {
                                location->sp_location = SP_LOC_OFFSET;
 
                                if (adjust <= 0) {
@@ -437,6 +499,11 @@ find_caller_pc_and_caller_sp(CallerLocation *location,
                                        sp_determined = true;
                                }
                        }
+
+#if TILE_CHIP >= 10
+                       /* Track moveli arguments for -m32 mode. */
+                       bt_update_moveli(&bundle, moveli_args);
+#endif
                }
 
                if (bt_has_iret(&bundle)) {
@@ -475,12 +542,11 @@ find_caller_pc_and_caller_sp(CallerLocation *location,
        }
 }
 
-void
-backtrace_init(BacktraceIterator *state,
-              BacktraceMemoryReader read_memory_func,
-              void *read_memory_func_extra,
-              VirtualAddress pc, VirtualAddress lr,
-              VirtualAddress sp, VirtualAddress r52)
+void backtrace_init(BacktraceIterator *state,
+                   BacktraceMemoryReader read_memory_func,
+                   void *read_memory_func_extra,
+                   VirtualAddress pc, VirtualAddress lr,
+                   VirtualAddress sp, VirtualAddress r52)
 {
        CallerLocation location;
        VirtualAddress fp, initial_frame_caller_pc;
@@ -513,11 +579,10 @@ backtrace_init(BacktraceIterator *state,
                break;
        }
 
-       /* The frame pointer should theoretically be aligned mod 8. If
-        * it's not even aligned mod 4 then something terrible happened
-        * and we should mark it as invalid.
+       /* If the frame pointer is not aligned to the basic word size
+        * something terrible happened and we should mark it as invalid.
         */
-       if (fp % 4 != 0)
+       if (fp % sizeof(bt_int_reg_t) != 0)
                fp = -1;
 
        /* -1 means "don't know initial_frame_caller_pc". */
@@ -558,10 +623,16 @@ backtrace_init(BacktraceIterator *state,
        state->read_memory_func_extra = read_memory_func_extra;
 }
 
-bool
-backtrace_next(BacktraceIterator *state)
+/* Handle the case where the register holds more bits than the VA. */
+static bool valid_addr_reg(bt_int_reg_t reg)
 {
-       VirtualAddress next_fp, next_pc, next_frame[2];
+       return ((VirtualAddress)reg == reg);
+}
+
+bool backtrace_next(BacktraceIterator *state)
+{
+       VirtualAddress next_fp, next_pc;
+       bt_int_reg_t next_frame[2];
 
        if (state->fp == -1) {
                /* No parent frame. */
@@ -575,11 +646,9 @@ backtrace_next(BacktraceIterator *state)
        }
 
        next_fp = next_frame[1];
-       if (next_fp % 4 != 0) {
-               /* Caller's frame pointer is suspect, so give up.
-                * Technically it should be aligned mod 8, but we will
-                * be forgiving here.
-                */
+       if (!valid_addr_reg(next_frame[1]) ||
+           next_fp % sizeof(bt_int_reg_t) != 0) {
+               /* Caller's frame pointer is suspect, so give up. */
                return false;
        }
 
@@ -597,7 +666,7 @@ backtrace_next(BacktraceIterator *state)
        } else {
                /* Get the caller PC from the frame linkage area. */
                next_pc = next_frame[0];
-               if (next_pc == 0 ||
+               if (!valid_addr_reg(next_frame[0]) || next_pc == 0 ||
                    next_pc % TILE_BUNDLE_ALIGNMENT_IN_BYTES != 0) {
                        /* The PC is suspect, so give up. */
                        return false;
@@ -611,24 +680,3 @@ backtrace_next(BacktraceIterator *state)
 
        return true;
 }
-
-#else /* TILE_CHIP < 10 */
-
-void
-backtrace_init(BacktraceIterator *state,
-              BacktraceMemoryReader read_memory_func,
-              void *read_memory_func_extra,
-              VirtualAddress pc, VirtualAddress lr,
-              VirtualAddress sp, VirtualAddress r52)
-{
-       state->pc = pc;
-       state->sp = sp;
-       state->fp = -1;
-       state->initial_frame_caller_pc = -1;
-       state->read_memory_func = read_memory_func;
-       state->read_memory_func_extra = read_memory_func_extra;
-}
-
-bool backtrace_next(BacktraceIterator *state) { return false; }
-
-#endif /* TILE_CHIP < 10 */