Robin Getz | 6ce3e9c | 2010-03-16 14:40:17 +0000 | [diff] [blame] | 1 | /* The fake debug assert instructions |
| 2 | * |
| 3 | * Copyright 2010 Analog Devices Inc. |
| 4 | * |
| 5 | * Licensed under the GPL-2 or later |
| 6 | */ |
| 7 | |
| 8 | #include <linux/types.h> |
| 9 | #include <linux/kernel.h> |
| 10 | #include <linux/ptrace.h> |
| 11 | |
Robin Getz | 5a132f7 | 2010-03-29 02:04:45 +0000 | [diff] [blame] | 12 | const char * const greg_names[] = { |
| 13 | "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", |
| 14 | "P0", "P1", "P2", "P3", "P4", "P5", "SP", "FP", |
| 15 | "I0", "I1", "I2", "I3", "M0", "M1", "M2", "M3", |
| 16 | "B0", "B1", "B2", "B3", "L0", "L1", "L2", "L3", |
| 17 | "A0.X", "A0.W", "A1.X", "A1.W", "<res>", "<res>", "ASTAT", "RETS", |
| 18 | "<res>", "<res>", "<res>", "<res>", "<res>", "<res>", "<res>", "<res>", |
| 19 | "LC0", "LT0", "LB0", "LC1", "LT1", "LB1", "CYCLES", "CYCLES2", |
| 20 | "USP", "SEQSTAT", "SYSCFG", "RETI", "RETX", "RETN", "RETE", "EMUDAT", |
| 21 | }; |
| 22 | |
| 23 | static const char *get_allreg_name(int grp, int reg) |
| 24 | { |
| 25 | return greg_names[(grp << 3) | reg]; |
| 26 | } |
| 27 | |
Robin Getz | dc89d97 | 2010-03-28 12:50:53 +0000 | [diff] [blame] | 28 | /* |
| 29 | * Unfortunately, the pt_regs structure is not laid out the same way as the |
| 30 | * hardware register file, so we need to do some fix ups. |
Robin Getz | 5a132f7 | 2010-03-29 02:04:45 +0000 | [diff] [blame] | 31 | * |
| 32 | * CYCLES is not stored in the pt_regs structure - so, we just read it from |
| 33 | * the hardware. |
| 34 | * |
| 35 | * Don't support: |
| 36 | * - All reserved registers |
| 37 | * - All in group 7 are (supervisors only) |
Robin Getz | dc89d97 | 2010-03-28 12:50:53 +0000 | [diff] [blame] | 38 | */ |
Robin Getz | 5a132f7 | 2010-03-29 02:04:45 +0000 | [diff] [blame] | 39 | |
Robin Getz | dc89d97 | 2010-03-28 12:50:53 +0000 | [diff] [blame] | 40 | static bool fix_up_reg(struct pt_regs *fp, long *value, int grp, int reg) |
| 41 | { |
| 42 | long *val = &fp->r0; |
Robin Getz | 5a132f7 | 2010-03-29 02:04:45 +0000 | [diff] [blame] | 43 | unsigned long tmp; |
Robin Getz | dc89d97 | 2010-03-28 12:50:53 +0000 | [diff] [blame] | 44 | |
| 45 | /* Only do Dregs and Pregs for now */ |
Robin Getz | 5a132f7 | 2010-03-29 02:04:45 +0000 | [diff] [blame] | 46 | if (grp == 5 || |
| 47 | (grp == 4 && (reg == 4 || reg == 5)) || |
| 48 | (grp == 7)) |
Robin Getz | dc89d97 | 2010-03-28 12:50:53 +0000 | [diff] [blame] | 49 | return false; |
| 50 | |
| 51 | if (grp == 0 || (grp == 1 && reg < 6)) |
| 52 | val -= (reg + 8 * grp); |
| 53 | else if (grp == 1 && reg == 6) |
| 54 | val = &fp->usp; |
| 55 | else if (grp == 1 && reg == 7) |
| 56 | val = &fp->fp; |
Robin Getz | 5a132f7 | 2010-03-29 02:04:45 +0000 | [diff] [blame] | 57 | else if (grp == 2) { |
| 58 | val = &fp->i0; |
| 59 | val -= reg; |
| 60 | } else if (grp == 3 && reg >= 4) { |
| 61 | val = &fp->l0; |
| 62 | val -= (reg - 4); |
| 63 | } else if (grp == 3 && reg < 4) { |
| 64 | val = &fp->b0; |
| 65 | val -= reg; |
| 66 | } else if (grp == 4 && reg < 4) { |
| 67 | val = &fp->a0x; |
| 68 | val -= reg; |
| 69 | } else if (grp == 4 && reg == 6) |
| 70 | val = &fp->astat; |
| 71 | else if (grp == 4 && reg == 7) |
| 72 | val = &fp->rets; |
| 73 | else if (grp == 6 && reg < 6) { |
| 74 | val = &fp->lc0; |
| 75 | val -= reg; |
| 76 | } else if (grp == 6 && reg == 6) { |
| 77 | __asm__ __volatile__("%0 = cycles;\n" : "=d"(tmp)); |
| 78 | val = &tmp; |
| 79 | } else if (grp == 6 && reg == 7) { |
| 80 | __asm__ __volatile__("%0 = cycles2;\n" : "=d"(tmp)); |
| 81 | val = &tmp; |
| 82 | } |
Robin Getz | dc89d97 | 2010-03-28 12:50:53 +0000 | [diff] [blame] | 83 | |
| 84 | *value = *val; |
| 85 | return true; |
| 86 | |
| 87 | } |
| 88 | |
Robin Getz | 6ce3e9c | 2010-03-16 14:40:17 +0000 | [diff] [blame] | 89 | #define PseudoDbg_Assert_opcode 0xf0000000 |
| 90 | #define PseudoDbg_Assert_expected_bits 0 |
| 91 | #define PseudoDbg_Assert_expected_mask 0xffff |
| 92 | #define PseudoDbg_Assert_regtest_bits 16 |
| 93 | #define PseudoDbg_Assert_regtest_mask 0x7 |
| 94 | #define PseudoDbg_Assert_grp_bits 19 |
| 95 | #define PseudoDbg_Assert_grp_mask 0x7 |
| 96 | #define PseudoDbg_Assert_dbgop_bits 22 |
| 97 | #define PseudoDbg_Assert_dbgop_mask 0x3 |
| 98 | #define PseudoDbg_Assert_dontcare_bits 24 |
| 99 | #define PseudoDbg_Assert_dontcare_mask 0x7 |
| 100 | #define PseudoDbg_Assert_code_bits 27 |
| 101 | #define PseudoDbg_Assert_code_mask 0x1f |
| 102 | |
Robin Getz | dc89d97 | 2010-03-28 12:50:53 +0000 | [diff] [blame] | 103 | /* |
| 104 | * DBGA - debug assert |
| 105 | */ |
Robin Getz | 6ce3e9c | 2010-03-16 14:40:17 +0000 | [diff] [blame] | 106 | bool execute_pseudodbg_assert(struct pt_regs *fp, unsigned int opcode) |
| 107 | { |
| 108 | int expected = ((opcode >> PseudoDbg_Assert_expected_bits) & PseudoDbg_Assert_expected_mask); |
| 109 | int dbgop = ((opcode >> (PseudoDbg_Assert_dbgop_bits)) & PseudoDbg_Assert_dbgop_mask); |
| 110 | int grp = ((opcode >> (PseudoDbg_Assert_grp_bits)) & PseudoDbg_Assert_grp_mask); |
| 111 | int regtest = ((opcode >> (PseudoDbg_Assert_regtest_bits)) & PseudoDbg_Assert_regtest_mask); |
Robin Getz | dc89d97 | 2010-03-28 12:50:53 +0000 | [diff] [blame] | 112 | long value; |
Robin Getz | 6ce3e9c | 2010-03-16 14:40:17 +0000 | [diff] [blame] | 113 | |
| 114 | if ((opcode & 0xFF000000) != PseudoDbg_Assert_opcode) |
| 115 | return false; |
| 116 | |
Robin Getz | dc89d97 | 2010-03-28 12:50:53 +0000 | [diff] [blame] | 117 | if (!fix_up_reg(fp, &value, grp, regtest)) |
Robin Getz | 6ce3e9c | 2010-03-16 14:40:17 +0000 | [diff] [blame] | 118 | return false; |
| 119 | |
Robin Getz | 6ce3e9c | 2010-03-16 14:40:17 +0000 | [diff] [blame] | 120 | if (dbgop == 0 || dbgop == 2) { |
| 121 | /* DBGA ( regs_lo , uimm16 ) */ |
| 122 | /* DBGAL ( regs , uimm16 ) */ |
Robin Getz | dc89d97 | 2010-03-28 12:50:53 +0000 | [diff] [blame] | 123 | if (expected != (value & 0xFFFF)) { |
Robin Getz | 5a132f7 | 2010-03-29 02:04:45 +0000 | [diff] [blame] | 124 | pr_notice("DBGA (%s.L,0x%x) failure, got 0x%x\n", |
| 125 | get_allreg_name(grp, regtest), |
| 126 | expected, (unsigned int)(value & 0xFFFF)); |
Robin Getz | 6ce3e9c | 2010-03-16 14:40:17 +0000 | [diff] [blame] | 127 | return false; |
| 128 | } |
| 129 | |
| 130 | } else if (dbgop == 1 || dbgop == 3) { |
| 131 | /* DBGA ( regs_hi , uimm16 ) */ |
| 132 | /* DBGAH ( regs , uimm16 ) */ |
Robin Getz | dc89d97 | 2010-03-28 12:50:53 +0000 | [diff] [blame] | 133 | if (expected != ((value >> 16) & 0xFFFF)) { |
Robin Getz | 5a132f7 | 2010-03-29 02:04:45 +0000 | [diff] [blame] | 134 | pr_notice("DBGA (%s.H,0x%x) failure, got 0x%x\n", |
| 135 | get_allreg_name(grp, regtest), |
| 136 | expected, (unsigned int)((value >> 16) & 0xFFFF)); |
Robin Getz | 6ce3e9c | 2010-03-16 14:40:17 +0000 | [diff] [blame] | 137 | return false; |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | fp->pc += 4; |
| 142 | return true; |
| 143 | } |
Robin Getz | dc89d97 | 2010-03-28 12:50:53 +0000 | [diff] [blame] | 144 | |
| 145 | #define PseudoDbg_opcode 0xf8000000 |
| 146 | #define PseudoDbg_reg_bits 0 |
| 147 | #define PseudoDbg_reg_mask 0x7 |
| 148 | #define PseudoDbg_grp_bits 3 |
| 149 | #define PseudoDbg_grp_mask 0x7 |
| 150 | #define PseudoDbg_fn_bits 6 |
| 151 | #define PseudoDbg_fn_mask 0x3 |
| 152 | #define PseudoDbg_code_bits 8 |
| 153 | #define PseudoDbg_code_mask 0xff |
| 154 | |
| 155 | /* |
| 156 | * DBG - debug (dump a register value out) |
| 157 | */ |
| 158 | bool execute_pseudodbg(struct pt_regs *fp, unsigned int opcode) |
| 159 | { |
| 160 | int grp, fn, reg; |
Robin Getz | a6d9dbf | 2010-03-29 04:30:40 +0000 | [diff] [blame] | 161 | long value, value1; |
Robin Getz | dc89d97 | 2010-03-28 12:50:53 +0000 | [diff] [blame] | 162 | |
| 163 | if ((opcode & 0xFF000000) != PseudoDbg_opcode) |
| 164 | return false; |
| 165 | |
| 166 | opcode >>= 16; |
| 167 | grp = ((opcode >> PseudoDbg_grp_bits) & PseudoDbg_reg_mask); |
| 168 | fn = ((opcode >> PseudoDbg_fn_bits) & PseudoDbg_fn_mask); |
| 169 | reg = ((opcode >> PseudoDbg_reg_bits) & PseudoDbg_reg_mask); |
| 170 | |
Robin Getz | a6d9dbf | 2010-03-29 04:30:40 +0000 | [diff] [blame] | 171 | if (fn == 3 && (reg == 0 || reg == 1)) { |
| 172 | if (!fix_up_reg(fp, &value, 4, 2 * reg)) |
| 173 | return false; |
| 174 | if (!fix_up_reg(fp, &value1, 4, 2 * reg + 1)) |
| 175 | return false; |
Robin Getz | dc89d97 | 2010-03-28 12:50:53 +0000 | [diff] [blame] | 176 | |
Robin Getz | a6d9dbf | 2010-03-29 04:30:40 +0000 | [diff] [blame] | 177 | pr_notice("DBG A%i = %02lx%08lx\n", reg, value & 0xFF, value1); |
| 178 | fp->pc += 2; |
| 179 | return true; |
Robin Getz | dc89d97 | 2010-03-28 12:50:53 +0000 | [diff] [blame] | 180 | |
Robin Getz | a6d9dbf | 2010-03-29 04:30:40 +0000 | [diff] [blame] | 181 | } else if (fn == 0) { |
| 182 | if (!fix_up_reg(fp, &value, grp, reg)) |
| 183 | return false; |
| 184 | |
| 185 | pr_notice("DBG %s = %08lx\n", get_allreg_name(grp, reg), value); |
| 186 | fp->pc += 2; |
| 187 | return true; |
| 188 | } |
| 189 | |
| 190 | return false; |
Robin Getz | dc89d97 | 2010-03-28 12:50:53 +0000 | [diff] [blame] | 191 | } |