| /* The fake debug assert instructions | 
 |  * | 
 |  * Copyright 2010 Analog Devices Inc. | 
 |  * | 
 |  * Licensed under the GPL-2 or later | 
 |  */ | 
 |  | 
 | #include <linux/types.h> | 
 | #include <linux/kernel.h> | 
 | #include <linux/ptrace.h> | 
 |  | 
 | const char * const greg_names[] = { | 
 | 	"R0",    "R1",      "R2",     "R3",    "R4",    "R5",    "R6",     "R7", | 
 | 	"P0",    "P1",      "P2",     "P3",    "P4",    "P5",    "SP",     "FP", | 
 | 	"I0",    "I1",      "I2",     "I3",    "M0",    "M1",    "M2",     "M3", | 
 | 	"B0",    "B1",      "B2",     "B3",    "L0",    "L1",    "L2",     "L3", | 
 | 	"A0.X",  "A0.W",    "A1.X",   "A1.W",  "<res>", "<res>", "ASTAT",  "RETS", | 
 | 	"<res>", "<res>",   "<res>",  "<res>", "<res>", "<res>", "<res>",  "<res>", | 
 | 	"LC0",   "LT0",     "LB0",    "LC1",   "LT1",   "LB1",   "CYCLES", "CYCLES2", | 
 | 	"USP",   "SEQSTAT", "SYSCFG", "RETI",  "RETX",  "RETN",  "RETE",   "EMUDAT", | 
 | }; | 
 |  | 
 | static const char *get_allreg_name(int grp, int reg) | 
 | { | 
 | 	return greg_names[(grp << 3) | reg]; | 
 | } | 
 |  | 
 | /* | 
 |  * Unfortunately, the pt_regs structure is not laid out the same way as the | 
 |  * hardware register file, so we need to do some fix ups. | 
 |  * | 
 |  * CYCLES is not stored in the pt_regs structure - so, we just read it from | 
 |  * the hardware. | 
 |  * | 
 |  * Don't support: | 
 |  *  - All reserved registers | 
 |  *  - All in group 7 are (supervisors only) | 
 |  */ | 
 |  | 
 | static bool fix_up_reg(struct pt_regs *fp, long *value, int grp, int reg) | 
 | { | 
 | 	long *val = &fp->r0; | 
 | 	unsigned long tmp; | 
 |  | 
 | 	/* Only do Dregs and Pregs for now */ | 
 | 	if (grp == 5 || | 
 | 	   (grp == 4 && (reg == 4 || reg == 5)) || | 
 | 	   (grp == 7)) | 
 | 		return false; | 
 |  | 
 | 	if (grp == 0 || (grp == 1 && reg < 6)) | 
 | 		val -= (reg + 8 * grp); | 
 | 	else if (grp == 1 && reg == 6) | 
 | 		val = &fp->usp; | 
 | 	else if (grp == 1 && reg == 7) | 
 | 		val = &fp->fp; | 
 | 	else if (grp == 2) { | 
 | 		val = &fp->i0; | 
 | 		val -= reg; | 
 | 	} else if (grp == 3 && reg >= 4) { | 
 | 		val = &fp->l0; | 
 | 		val -= (reg - 4); | 
 | 	} else if (grp == 3 && reg < 4) { | 
 | 		val = &fp->b0; | 
 | 		val -= reg; | 
 | 	} else if (grp == 4 && reg < 4) { | 
 | 		val = &fp->a0x; | 
 | 		val -= reg; | 
 | 	} else if (grp == 4 && reg == 6) | 
 | 		val = &fp->astat; | 
 | 	else if (grp == 4 && reg == 7) | 
 | 		val = &fp->rets; | 
 | 	else if (grp == 6 && reg < 6) { | 
 | 		val = &fp->lc0; | 
 | 		val -= reg; | 
 | 	} else if (grp == 6 && reg == 6) { | 
 | 		__asm__ __volatile__("%0 = cycles;\n" : "=d"(tmp)); | 
 | 		val = &tmp; | 
 | 	} else if (grp == 6 && reg == 7) { | 
 | 		__asm__ __volatile__("%0 = cycles2;\n" : "=d"(tmp)); | 
 | 		val = &tmp; | 
 | 	} | 
 |  | 
 | 	*value = *val; | 
 | 	return true; | 
 |  | 
 | } | 
 |  | 
 | #define PseudoDbg_Assert_opcode         0xf0000000 | 
 | #define PseudoDbg_Assert_expected_bits  0 | 
 | #define PseudoDbg_Assert_expected_mask  0xffff | 
 | #define PseudoDbg_Assert_regtest_bits   16 | 
 | #define PseudoDbg_Assert_regtest_mask   0x7 | 
 | #define PseudoDbg_Assert_grp_bits       19 | 
 | #define PseudoDbg_Assert_grp_mask       0x7 | 
 | #define PseudoDbg_Assert_dbgop_bits     22 | 
 | #define PseudoDbg_Assert_dbgop_mask     0x3 | 
 | #define PseudoDbg_Assert_dontcare_bits  24 | 
 | #define PseudoDbg_Assert_dontcare_mask  0x7 | 
 | #define PseudoDbg_Assert_code_bits      27 | 
 | #define PseudoDbg_Assert_code_mask      0x1f | 
 |  | 
 | /* | 
 |  * DBGA - debug assert | 
 |  */ | 
 | bool execute_pseudodbg_assert(struct pt_regs *fp, unsigned int opcode) | 
 | { | 
 | 	int expected = ((opcode >> PseudoDbg_Assert_expected_bits) & PseudoDbg_Assert_expected_mask); | 
 | 	int dbgop    = ((opcode >> (PseudoDbg_Assert_dbgop_bits)) & PseudoDbg_Assert_dbgop_mask); | 
 | 	int grp      = ((opcode >> (PseudoDbg_Assert_grp_bits)) & PseudoDbg_Assert_grp_mask); | 
 | 	int regtest  = ((opcode >> (PseudoDbg_Assert_regtest_bits)) & PseudoDbg_Assert_regtest_mask); | 
 | 	long value; | 
 |  | 
 | 	if ((opcode & 0xFF000000) != PseudoDbg_Assert_opcode) | 
 | 		return false; | 
 |  | 
 | 	if (!fix_up_reg(fp, &value, grp, regtest)) | 
 | 		return false; | 
 |  | 
 | 	if (dbgop == 0 || dbgop == 2) { | 
 | 		/* DBGA ( regs_lo , uimm16 ) */ | 
 | 		/* DBGAL ( regs , uimm16 ) */ | 
 | 		if (expected != (value & 0xFFFF)) { | 
 | 			pr_notice("DBGA (%s.L,0x%x) failure, got 0x%x\n", | 
 | 				get_allreg_name(grp, regtest), | 
 | 				expected, (unsigned int)(value & 0xFFFF)); | 
 | 			return false; | 
 | 		} | 
 |  | 
 | 	} else if (dbgop == 1 || dbgop == 3) { | 
 | 		/* DBGA ( regs_hi , uimm16 ) */ | 
 | 		/* DBGAH ( regs , uimm16 ) */ | 
 | 		if (expected != ((value >> 16) & 0xFFFF)) { | 
 | 			pr_notice("DBGA (%s.H,0x%x) failure, got 0x%x\n", | 
 | 				get_allreg_name(grp, regtest), | 
 | 				expected, (unsigned int)((value >> 16) & 0xFFFF)); | 
 | 			return false; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	fp->pc += 4; | 
 | 	return true; | 
 | } | 
 |  | 
 | #define PseudoDbg_opcode        0xf8000000 | 
 | #define PseudoDbg_reg_bits      0 | 
 | #define PseudoDbg_reg_mask      0x7 | 
 | #define PseudoDbg_grp_bits      3 | 
 | #define PseudoDbg_grp_mask      0x7 | 
 | #define PseudoDbg_fn_bits       6 | 
 | #define PseudoDbg_fn_mask       0x3 | 
 | #define PseudoDbg_code_bits     8 | 
 | #define PseudoDbg_code_mask     0xff | 
 |  | 
 | /* | 
 |  * DBG - debug (dump a register value out) | 
 |  */ | 
 | bool execute_pseudodbg(struct pt_regs *fp, unsigned int opcode) | 
 | { | 
 | 	int grp, fn, reg; | 
 | 	long value, value1; | 
 |  | 
 | 	if ((opcode & 0xFF000000) != PseudoDbg_opcode) | 
 | 		return false; | 
 |  | 
 | 	opcode >>= 16; | 
 | 	grp = ((opcode >> PseudoDbg_grp_bits) & PseudoDbg_reg_mask); | 
 | 	fn  = ((opcode >> PseudoDbg_fn_bits)  & PseudoDbg_fn_mask); | 
 | 	reg = ((opcode >> PseudoDbg_reg_bits) & PseudoDbg_reg_mask); | 
 |  | 
 | 	if (fn == 3 && (reg == 0 || reg == 1)) { | 
 | 		if (!fix_up_reg(fp, &value, 4, 2 * reg)) | 
 | 			return false; | 
 | 		if (!fix_up_reg(fp, &value1, 4, 2 * reg + 1)) | 
 | 			return false; | 
 |  | 
 | 		pr_notice("DBG A%i = %02lx%08lx\n", reg, value & 0xFF, value1); | 
 | 		fp->pc += 2; | 
 | 		return true; | 
 |  | 
 | 	} else if (fn == 0) { | 
 | 		if (!fix_up_reg(fp, &value, grp, reg)) | 
 | 			return false; | 
 |  | 
 | 		pr_notice("DBG %s = %08lx\n", get_allreg_name(grp, reg), value); | 
 | 		fp->pc += 2; | 
 | 		return true; | 
 | 	} | 
 |  | 
 | 	return false; | 
 | } |