/*
 * IBM Dynamic Probes
 * Copyright (c) International Business Machines Corp., 2000,2001
 *
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
 */

/*
 * This is a special file, part of the dprobes interpreter that contains
 * architecture-specific functions. It is not compiled as a seperate unit.
 * It is #included into the arch-independent dprobes_in.c at a specific
 * location.
 */
#ifdef CONFIG_DUMP
#include <linux/dump.h>
#endif
#ifdef CONFIG_KDB
#include <linux/kdb.h>
#endif

#ifdef CONFIG_KDB
static inline void restore_opcode(void)
{
	if (dprobes.rec->point.probe & DP_PROBE_BREAKPOINT) {
		dprobes.reset_addr = dprobes.probe_addr;
		*((byte_t *)dprobes.reset_addr) = dprobes.opcode;
		flush_page_to_ram(dprobes.reset_addr);
	}
}

/*
 * After kdb terminates, restore the breakpoint.
 */
static inline void restore_int3(void)
{	
	if (dprobes.rec->point.probe & DP_PROBE_BREAKPOINT) {
		dprobes.reset_addr = dprobes.probe_addr;
		*((byte_t *)dprobes.reset_addr) = DP_INSTR_BREAKPOINT;
		flush_page_to_ram(dprobes.reset_addr);
	}
}
	
void call_kdb(void)
{
	unsigned long eip = dprobes.regs->eip - 1;
	if (dprobes.status & DP_KERNEL_PROBE) {
		if (dprobes.rec->point.probe & DP_PROBE_BREAKPOINT)
			dprobes.regs->eip--;
		restore_opcode();
		kdb(KDB_REASON_CALL, 0, dprobes.regs);
		restore_int3();
		if (eip == dprobes.regs->eip)
			dprobes.regs->eip++;
	}
}
#else

void call_kdb(void)
{
	printk("Dprobes: Kernel Debugger is not present.\n");
	return;
}
#endif
/*
 * Exiting the interpreter through the registered facility n.
 */
static void dp_exit_n(void)
{
	u8 facility = get_u8_oprnd();
	switch(facility) {
	case DP_EXIT_TO_SGI_KDB:
		call_kdb();	
		break;

	case DP_EXIT_TO_SGI_VMDUMP:
#ifdef CONFIG_DUMP
		if (dprobes.status & DP_KERNEL_PROBE)
			dump("Dumping due to dprobes", dprobes.regs);
#else
		printk("dprobes: Crash dump facility is not present.\n");
#endif
		break;
	case DP_EXIT_TO_CORE_DUMP:
		if (dprobes.status & DP_USER_PROBE) {
			if (do_coredump(SIGINT, dprobes.regs)) {	
				current->mm->dumpable = 1;
				printk("dprobes(%d,%d) process %s dumped core\n", dprobes.major, dprobes.minor, current->comm);
			}
			else
				printk("dprobes(%d,%d) exit to core dump failed\n", dprobes.major, dprobes.minor);
		}
		break;
	default: 
		gen_ex(EX_INVALID_OPERAND, 4, facility); 
		return;
	
	}
	dprobes.status &= ~DP_STATUS_INTERPRETER;
}

/*
 * Interpreter's version of copy_xxx_user functions. In Intel land
 * cr2 needs to be saved and restored, but in 390 land life is much 
 * easier. I think I'll just make them macros eventually.
 */
static int dp_intr_copy_from_user(void *to, void *from, int len) 
{
	int retval;
	retval = __copy_from_user(to, from, len);
	return retval;
}

static int dp_intr_copy_to_user(void *to, void *from, int len) 
{
	int retval;
	retval = __copy_to_user(to, from, len);
	return retval;
}

#define BITS_IN_UL	32

#define PROPAGATE_BIT(name, OPERATOR) \
static void name(void) \
{ \
	unsigned long index, num, pos, val; \
	index = rpnpop(); \
	if (!index || index > BITS_IN_UL) { \
		gen_ex(EX_INVALID_OPERAND, 3, index); \
		return; \
	} \
	num = rpnpop(); \
	pos = 1UL << (index - 1); \
	val = num & pos; \
	for (; pos; pos OPERATOR 1, val OPERATOR 1) \
		num &= ~pos, num |= val; \
	rpnpush(num); \
}	

PROPAGATE_BIT(pbl, <<=)
PROPAGATE_BIT(pbr, >>=)

#define PROPAGATE_BIT_IMM(name, OPERATOR) \
static void name(void) \
{ \
	unsigned long index, num, pos, val; \
	index = (unsigned long)get_u8_oprnd(); \
	if (!index || index > BITS_IN_UL) { \
		dprobes.status &= ~DP_STATUS_INTERPRETER; \
		return; \
	} \
	num = rpnpop(); \
	pos = 1UL << (index - 1); \
	val = num & pos; \
	for (; pos; pos OPERATOR 1, val OPERATOR 1) \
		num &= ~pos, num |= val; \
	rpnpush(num); \
}

PROPAGATE_BIT_IMM(pbl_i, <<=)
PROPAGATE_BIT_IMM(pbr_i, >>=)



static void pzl(void) 
{ 
	unsigned long index, num, pos;
	index = rpnpop(); 
	if (!index || index > BITS_IN_UL) {
		gen_ex(EX_INVALID_OPERAND, 3, index); \
		return;
	}
	num = rpnpop();
	pos = 1UL << index;
	for (; pos; pos <<= 1)
		num &= ~pos;
	rpnpush(num);
}
	
static void pzl_i(void) 
{ 
	unsigned long index, num, pos;
	index = (unsigned long)get_u8_oprnd();
	if (!index || index > BITS_IN_UL) {
		gen_ex(EX_INVALID_OPERAND, 3, index);
		return;
	}
	num = rpnpop();
	pos = 1UL << index;
	for (; pos; pos <<= 1)
		num &= ~pos;
	rpnpush(num);
}

static void pzr(void)
{
	unsigned long index, num, pos;
	index = rpnpop();
	if (!index || index > BITS_IN_UL) {
		gen_ex(EX_INVALID_OPERAND, 3, index);
		return;
	}
	if (index == 1)
		return;
	num = rpnpop();
	pos = 1UL << (index - 2);
	for (; pos; pos >>= 1)
		num &= ~pos;
	rpnpush(num);
}

static void pzr_i(void)
{
	unsigned long index, num, pos;
	index = (unsigned long)get_u8_oprnd();
	if (!index || index > BITS_IN_UL) {
		gen_ex(EX_INVALID_OPERAND, 3, index);
		return;
	}
	if (index == 1)
		return;
	num = rpnpop();
	pos = 1UL << (index - 2);
	for (; pos; pos >>= 1)
		num &= ~pos;
	rpnpush(num);
}

static void ror_i(void)
{
	byte_t count = get_u8_oprnd();
	dprobes.rpn_stack[dprobes.rpn_tos] = 
		(dprobes.rpn_stack[dprobes.rpn_tos] >> count) | 
		(dprobes.rpn_stack[dprobes.rpn_tos] << (BITS_IN_UL - count));

}

static void rol_i(void)
{
	byte_t count = get_u8_oprnd();
	dprobes.rpn_stack[dprobes.rpn_tos] = 
		(dprobes.rpn_stack[dprobes.rpn_tos] << count) | 
		(dprobes.rpn_stack[dprobes.rpn_tos] >> (BITS_IN_UL - count));
}

static void ror(void)
{
	unsigned long oprnd = rpnpop();
	unsigned long count = rpnpop();
	oprnd = (oprnd >> count) | (oprnd << (BITS_IN_UL - count));
	rpnpush(oprnd);
}

static void rol(void)
{
	unsigned long oprnd = rpnpop();
	unsigned long count = rpnpop();
	oprnd = (oprnd << count) | (oprnd >> (BITS_IN_UL - count));
	rpnpush(oprnd);
}

/*
 * Register push and pop, current and user registers.
 */

/*
 * y -- allowed and implemented, n -- not allowed and not implemented.
 *
 * Index	Register	push u	push r	pop u	pop r	
 * 01		NIP		y	y	n	n
 * 02		MSR             y	y	n	n
 *   		               
 * 03		CTR             y	y	n	n
 * 04		LNK             y	y	n	n
 * 05		XER             y	y	n	n
 * 06		CCR             y	y	n	n
 * 07		MQ              y	y	n	n
 * 08		DAR             y	y	n	n
 * 09		DSISR           y	y	n	n
 * 0A		FPC             y	y	n	n
 * 0B		CPUID		y	y	n	n
 *   		                 	
 * 10		R0 		y	y	y	y
 * 11		R1 		y	y	y	y
 * 12		R2 		y	y	y	y
 * 13		R3 		y	y	y	y
 * 14		R4 		y	y	y	y
 * 15		R5 		y	y	y	y
 * 16		R6 		y	y	y	y
 * 17		R7 		y	y	y	y
 * 18		R8 		y	y	y	y
 * 19		R9 		y	y	y	y
 * 1a		R10		y	y	y	y
 * 1b		R11		y	y	y	y
 * 1c		R12		y	y	y	y
 * 1d		R13		y	y	y	y
 * 1e		R14		y	y	y	y
 * 1f		R15		y	y	y	y
 * 20		R16		y	y	y	y
 * 21		R17		y	y	y	y
 * 22		R18		y	y	y	y
 * 23           R19		y	y	y	y
 * 24		R20		y	y	y	y
 * 25		R21		y	y	y	y
 * 26		R22		y	y	y	y
 * 27		R23		y	y	y	y
 * 28		R24		y	y	y	y
 * 29		R25		y	y	y	y
 * 2a		R26		y	y	y	y
 * 2b		R27		y	y	y	y
 * 2c		R28		y	y	y	y
 * 2d		R29		y	y	y	y
 * 2e		R30		y	y	y	y
 * 2f		R31		y	y	y	y
 *   		   
 * 30		FPR0		y	n	n	n
 * 31		FPR1		y	n	n	n
 * 32		FPR2		y	n	n	n
 * 33		FPR3		y	n	n	n
 * 34		FPR4		y	n	n	n
 * 35		FPR5		y	n	n	n
 * 36		FPR6		y	n	n	n
 * 37		FPR7		y	n	n	n
 * 38		FPR8		y	n	n	n
 * 39		FPR9		y	n	n	n
 * 3a		FPR10		y	n	n	n
 * 3b		FPR11		y	n	n	n
 * 3c		FPR12		y	n	n	n
 * 3d		FPR13		y	n	n	n
 * 3e		FPR14		y	n	n	n
 * 3f		FPR15		y	n	n	n
 * 40		FPR16		y	n	n	n
 * 41		FPR17		y	n	n	n
 * 42		FPR18		y	n	n	n
 * 43		FPR19		y	n	n	n
 * 44		FPR20		y	n	n	n
 * 45		FPR21		y	n	n	n
 * 46		FPR22		y	n	n	n
 * 47		FPR23		y	n	n	n
 * 48		FPR24		y	n	n	n
 * 49		FPR25		y	n	n	n
 * 4a		FPR26		y	n	n	n
 * 4b		FPR27		y	n	n	n
 * 4c		FPR28		y	n	n	n
 * 4d		FPR29		y	n	n	n
 * 4e		FPR30		y	n	n	n
 * 4f		FPR31		y	n	n	n
 */

/*
 * Define Floating Point Control Registers' push functions.
 */

static void pushfpc(void) 
{ 
	rpnpush(current->thread.fpscr); 
}

/* run in fear of pointer abuse */
static void pushfp(int reg)
{
	unsigned long *d;
	d = (unsigned long *)&current->thread.fpr[reg];
	rpnpush(d[0]);

}



/*
 * Push the pvr as cpuid.
 */
static void push_cpuid(void)
{
	rpnpush(mfspr(SPRN_PVR));
}
	
/*
 * push kernel registers
 */
static void pushr(void)
{
	struct dprobes_struct *dp = &dprobes;
	unsigned short index = get_u16_oprnd();
	unsigned long val;
	switch(index) {

		case DP_NIP:   val = dp->regs->nip; break;

		case DP_MSR:   val = dp->regs->msr; break; 
		case DP_CTR:   val = dp->regs->ctr; break; 
		case DP_LNK:   val = dp->regs->link; break; 
		case DP_XER:   val = dp->regs->xer; break; 
		case DP_CCR:   val = dp->regs->ccr; break; 
		case DP_DAR:   val = dp->regs->dar; break; 
		case DP_DSISR: val = dp->regs->dsisr; break; 

		case DP_R0:  val = dp->regs->gpr[0]; break;
		case DP_R1:  val = dp->regs->gpr[1]; break;
		case DP_R2:  val = dp->regs->gpr[2]; break;
		case DP_R3:  val = dp->regs->gpr[3]; break;
		case DP_R4:  val = dp->regs->gpr[4]; break;
		case DP_R5:  val = dp->regs->gpr[5]; break;
		case DP_R6:  val = dp->regs->gpr[6]; break;
		case DP_R7:  val = dp->regs->gpr[7]; break;
		case DP_R8:  val = dp->regs->gpr[8]; break;
		case DP_R9:  val = dp->regs->gpr[9]; break;
		case DP_R10: val = dp->regs->gpr[10]; break;
		case DP_R11: val = dp->regs->gpr[11]; break;
		case DP_R12: val = dp->regs->gpr[12]; break;
		case DP_R13: val = dp->regs->gpr[13]; break;
		case DP_R14: val = dp->regs->gpr[14]; break;
		case DP_R15: val = dp->regs->gpr[15]; break;
		case DP_R16: val = dp->regs->gpr[16]; break;
		case DP_R17: val = dp->regs->gpr[17]; break;
		case DP_R18: val = dp->regs->gpr[18]; break;
		case DP_R19: val = dp->regs->gpr[19]; break;
		case DP_R20: val = dp->regs->gpr[20]; break;
		case DP_R21: val = dp->regs->gpr[21]; break;
		case DP_R22: val = dp->regs->gpr[22]; break;
		case DP_R23: val = dp->regs->gpr[23]; break;
		case DP_R24: val = dp->regs->gpr[24]; break;
		case DP_R25: val = dp->regs->gpr[25]; break;
		case DP_R26: val = dp->regs->gpr[26]; break;
		case DP_R27: val = dp->regs->gpr[27]; break;
		case DP_R28: val = dp->regs->gpr[28]; break;
		case DP_R29: val = dp->regs->gpr[29]; break;
		case DP_R30: val = dp->regs->gpr[30]; break;
		case DP_R31: val = dp->regs->gpr[31]; break;

		case DP_CPUID:	push_cpuid(); return;

		default:	val = 0; break;
	}
	rpnpush(val);
	return;
}

/*
 * User register main push function.
 */
static void pushu(void)
{
	struct dprobes_struct *dp = &dprobes;
	unsigned short index = get_u16_oprnd();
	unsigned long val;
	switch(index) {

		case DP_NIP:   val = dp->uregs->nip; break;
	
		case DP_MSR:   val = dp->uregs->msr; break; 
		case DP_CTR:   val = dp->uregs->ctr; break; 
		case DP_LNK:   val = dp->uregs->link; break; 
		case DP_XER:   val = dp->uregs->xer; break; 
		case DP_CCR:   val = dp->uregs->ccr; break; 
		case DP_DAR:   val = dp->uregs->dar; break; 
		case DP_DSISR: val = dp->uregs->dsisr; break; 
	
		case DP_R0:  val = dp->uregs->gpr[0]; break;
		case DP_R1:  val = dp->uregs->gpr[1]; break;
		case DP_R2:  val = dp->uregs->gpr[2]; break;
		case DP_R3:  val = dp->uregs->gpr[3]; break;
		case DP_R4:  val = dp->uregs->gpr[4]; break;
		case DP_R5:  val = dp->uregs->gpr[5]; break;
		case DP_R6:  val = dp->uregs->gpr[6]; break;
		case DP_R7:  val = dp->uregs->gpr[7]; break;
		case DP_R8:  val = dp->uregs->gpr[8]; break;
		case DP_R9:  val = dp->uregs->gpr[9]; break;
		case DP_R10: val = dp->uregs->gpr[10]; break;
		case DP_R11: val = dp->uregs->gpr[11]; break;
		case DP_R12: val = dp->uregs->gpr[12]; break;
		case DP_R13: val = dp->uregs->gpr[13]; break;
		case DP_R14: val = dp->uregs->gpr[14]; break;
		case DP_R15: val = dp->uregs->gpr[15]; break;
		case DP_R16: val = dp->uregs->gpr[16]; break;
		case DP_R17: val = dp->uregs->gpr[17]; break;
		case DP_R18: val = dp->uregs->gpr[18]; break;
		case DP_R19: val = dp->uregs->gpr[19]; break;
		case DP_R20: val = dp->uregs->gpr[20]; break;
		case DP_R21: val = dp->uregs->gpr[21]; break;
		case DP_R22: val = dp->uregs->gpr[22]; break;
		case DP_R23: val = dp->uregs->gpr[23]; break;
		case DP_R24: val = dp->uregs->gpr[24]; break;
		case DP_R25: val = dp->uregs->gpr[25]; break;
		case DP_R26: val = dp->uregs->gpr[26]; break;
		case DP_R27: val = dp->uregs->gpr[27]; break;
		case DP_R28: val = dp->uregs->gpr[28]; break;
		case DP_R29: val = dp->uregs->gpr[29]; break;
		case DP_R30: val = dp->uregs->gpr[30]; break;
		case DP_R31: val = dp->uregs->gpr[31]; break;
							   
		case DP_FPC:  pushfpc(); return;
	
		case DP_FPR0:  pushfp(0); return;
		case DP_FPR1:  pushfp(1); return;
		case DP_FPR2:  pushfp(2); return;
		case DP_FPR3:  pushfp(3); return;
		case DP_FPR4:  pushfp(4); return;
		case DP_FPR5:  pushfp(5); return;
		case DP_FPR6:  pushfp(6); return;
		case DP_FPR7:  pushfp(7); return;
		case DP_FPR8:  pushfp(8); return;
		case DP_FPR9:  pushfp(9); return;
		case DP_FPR10: pushfp(10); return;
		case DP_FPR11: pushfp(11); return;
		case DP_FPR12: pushfp(12); return;
		case DP_FPR13: pushfp(13); return;
		case DP_FPR14: pushfp(14); return;
		case DP_FPR15: pushfp(15); return;
		case DP_FPR16: pushfp(16); return;
		case DP_FPR17: pushfp(17); return;
		case DP_FPR18: pushfp(18); return;
		case DP_FPR19: pushfp(19); return;
		case DP_FPR20: pushfp(20); return;
		case DP_FPR21: pushfp(21); return;
		case DP_FPR22: pushfp(22); return;
		case DP_FPR23: pushfp(23); return;
		case DP_FPR24: pushfp(24); return;
		case DP_FPR25: pushfp(25); return;
		case DP_FPR26: pushfp(26); return;
		case DP_FPR27: pushfp(27); return;
		case DP_FPR28: pushfp(28); return;
		case DP_FPR29: pushfp(29); return;
		case DP_FPR30: pushfp(30); return;
		case DP_FPR31: pushfp(31); return;
	
		default: 	val = 0; break;
	}
	rpnpush(val);
	return;
}

/*
 * Current register main pop function.
 */
static void popr(void)
{
	struct dprobes_struct *dp = &dprobes;
	unsigned short index = get_u16_oprnd();
	unsigned long val = rpnpop();
	switch(index) {
	
	case DP_R0:  dp->regs->gpr[0] = val; break;
	case DP_R1:  dp->regs->gpr[1] = val; break;
	case DP_R2:  dp->regs->gpr[2] = val; break;
	case DP_R3:  dp->regs->gpr[3] = val; break;
	case DP_R4:  dp->regs->gpr[4] = val; break;
	case DP_R5:  dp->regs->gpr[5] = val; break;
	case DP_R6:  dp->regs->gpr[6] = val; break;
	case DP_R7:  dp->regs->gpr[7] = val; break;
	case DP_R8:  dp->regs->gpr[8] = val; break;
	case DP_R9:  dp->regs->gpr[9] = val; break;
	case DP_R10: dp->regs->gpr[10] = val; break;
	case DP_R11: dp->regs->gpr[11] = val; break;
	case DP_R12: dp->regs->gpr[12] = val; break;
	case DP_R13: dp->regs->gpr[13] = val; break;
	case DP_R14: dp->regs->gpr[14] = val; break;
	case DP_R15: dp->regs->gpr[15] = val; break;
	case DP_R16: dp->regs->gpr[16] = val; break;
	case DP_R17: dp->regs->gpr[17] = val; break;
	case DP_R18: dp->regs->gpr[18] = val; break;
	case DP_R19: dp->regs->gpr[19] = val; break;
	case DP_R20: dp->regs->gpr[20] = val; break;
	case DP_R21: dp->regs->gpr[21] = val; break;
	case DP_R22: dp->regs->gpr[22] = val; break;
	case DP_R23: dp->regs->gpr[23] = val; break;
	case DP_R24: dp->regs->gpr[24] = val; break;
	case DP_R25: dp->regs->gpr[25] = val; break;
	case DP_R26: dp->regs->gpr[26] = val; break;
	case DP_R27: dp->regs->gpr[27] = val; break;
	case DP_R28: dp->regs->gpr[28] = val; break;
	case DP_R29: dp->regs->gpr[29] = val; break;
	case DP_R30: dp->regs->gpr[30] = val; break;
	case DP_R31: dp->regs->gpr[31] = val; break;
		
	default:
	} 
}

/*
 * User register main pop function.
 */
static void popu(void)
{
	struct dprobes_struct *dp = &dprobes;
	unsigned short index = get_u16_oprnd();
	unsigned long val = rpnpop();
	switch(index) {
	case DP_R0:  dp->uregs->gpr[0] = val; break;
	case DP_R1:  dp->uregs->gpr[1] = val; break;
	case DP_R2:  dp->uregs->gpr[2] = val; break;
	case DP_R3:  dp->uregs->gpr[3] = val; break;
	case DP_R4:  dp->uregs->gpr[4] = val; break;
	case DP_R5:  dp->uregs->gpr[5] = val; break;
	case DP_R6:  dp->uregs->gpr[6] = val; break;
	case DP_R7:  dp->uregs->gpr[7] = val; break;
	case DP_R8:  dp->uregs->gpr[8] = val; break;
	case DP_R9:  dp->uregs->gpr[9] = val; break;
	case DP_R10: dp->uregs->gpr[10] = val; break;
	case DP_R11: dp->uregs->gpr[11] = val; break;
	case DP_R12: dp->uregs->gpr[12] = val; break;
	case DP_R13: dp->uregs->gpr[13] = val; break;
	case DP_R14: dp->uregs->gpr[14] = val; break;
	case DP_R15: dp->uregs->gpr[15] = val; break;
	case DP_R16: dp->uregs->gpr[16] = val; break;
	case DP_R17: dp->uregs->gpr[17] = val; break;
	case DP_R18: dp->uregs->gpr[18] = val; break;
	case DP_R19: dp->uregs->gpr[19] = val; break;
	case DP_R20: dp->uregs->gpr[20] = val; break;
	case DP_R21: dp->uregs->gpr[21] = val; break;
	case DP_R22: dp->uregs->gpr[22] = val; break;
	case DP_R23: dp->uregs->gpr[23] = val; break;
	case DP_R24: dp->uregs->gpr[24] = val; break;
	case DP_R25: dp->uregs->gpr[25] = val; break;
	case DP_R26: dp->uregs->gpr[26] = val; break;
	case DP_R27: dp->uregs->gpr[27] = val; break;
	case DP_R28: dp->uregs->gpr[28] = val; break;
	case DP_R29: dp->uregs->gpr[29] = val; break;
	case DP_R30: dp->uregs->gpr[30] = val; break;
	case DP_R31: dp->uregs->gpr[31] = val; break;
	
		default:
	} 
}

/*
 * Entry point for the dprobes interpreter(Probe handler).
 */
static void dp_asm_interpreter(byte_t rpn_instr)
{
	struct dprobes_struct *dp = &dprobes;

		switch(rpn_instr) {

			/* case DP_SEG2LIN:	seg2lin(); break; */

			default: gen_ex(EX_INVALID_OPCODE, *(dp->rpn_ip - 1),
			(unsigned long)(dp->rpn_ip -dp->rpn_code - 1)); break;		}
	return;
}

