/*
 * 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	
 * 
 * 0x0000    PSWMASK  	y	y	n      	n    
 * 0x0001    PSWADDR  	y	y	n      	n    
 *           		 	       	
 * 0x0010    GPR0     	y       y       y       y
 * 0x0011    GPR1     	y       y       y       y
 * 0x0012    GPR2     	y       y       y       y
 * 0x0013    GPR3     	y       y       y       y
 * 0x0014    GPR4     	y       y       y       y
 * 0x0015    GPR5     	y       y       y       y
 * 0x0016    GPR6     	y       y       y       y
 * 0x0017    GPR7     	y       y       y       y
 * 0x0018    GPR8     	y       y       y       y
 * 0x0019    GPR9     	y       y       y       y
 * 0x001a    GPR10    	y       y       y       y
 * 0x001b    GPR11    	y       y       y       y
 * 0x001c    GPR12    	y       y       y       y
 * 0x001d    GPR13    	y       y       y       y
 * 0x001e    GPR14    	y       y       y       y
 * 0x001f    GPR15    	y       y       y       y
 *           		 	       	
 * 0x0020    ACR0     	y       y       n       n    
 * 0x0021    ACR1     	y       y       n       n    
 * 0x0022    ACR2     	y       y       n       n    
 * 0x0023    ACR3     	y       y       n       n    
 * 0x0024    ACR4     	y       y       n       n    
 * 0x0025    ACR5     	y       y       n       n    
 * 0x0026    ACR6     	y       y       n       n    
 * 0x0027    ACR7     	y       y       n       n    
 * 0x0028    ACR8     	y       y       n       n    
 * 0x0029    ACR9     	y       y       n       n    
 * 0x002a    ACR10    	y       y       n       n    
 * 0x002b    ACR11    	y       y       n       n    
 * 0x002c    ACR12    	y       y       n       n    
 * 0x002d    ACR13    	y       y       n       n    
 * 0x002e    ACR14    	y       y       n       n    
 * 0x002f    ACR15    	y       y       n       n    
 *           		 	       	
 * 0x0003    FPC      	y	n	n       n
 *           		 	                            
 * 0x0030    FPR0     	y       n       n       n
 * 0x0031    FPR1     	y       n       n       n
 * 0x0032    FPR2     	y       n       n       n
 * 0x0033    FPR3     	y       n       n       n
 * 0x0034    FPR4     	y       n       n       n
 * 0x0035    FPR5     	y       n       n       n
 * 0x0036    FPR6     	y       n       n       n
 * 0x0037    FPR7     	y       n       n       n
 * 0x0038    FPR8     	y       n       n       n
 * 0x0039    FPR9     	y       n       n       n
 * 0x003a    FPR10    	y       n       n       n
 * 0x003b    FPR11    	y       n       n       n
 * 0x003c    FPR12    	y       n       n       n
 * 0x003d    FPR13    	y       n       n       n
 * 0x003e    FPR14    	y       n       n       n
 * 0x003f    FPR15    	y       n       n       n
 *           			    	
 * 0x0040    CR_0     	n       y       n       n    
 * 0x0041    CR_1     	n       y       n       n    
 * 0x0042    CR_2     	n       y       n       n    
 * 0x0043    CR_3     	n       y       n       n    
 * 0x0044    CR_4     	n       y       n       n    
 * 0x0045    CR_5     	n       y       n       n    
 * 0x0046    CR_6     	n       y       n       n    
 * 0x0047    CR_7     	n       y       n       n    
 * 0x0048    CR_8     	n       y       n       n    
 * 0x0049    CR_9     	n       y       n       n    
 * 0x004a    CR_10    	n       y       n       n    
 * 0x004b    CR_11    	n       y       n       n    
 * 0x004c    CR_12    	n       y       n       n    
 * 0x004d    CR_13    	n       y       n       n    
 * 0x004e    CR_14    	n       y       n       n    
 * 0x004f    CR_15    	n       y       n       n    
 */

static void dp_save_fpu(void)
{
	dprobes.status &= ~DP_STATUS_FIRSTFPU;
	if (HAVE_HWFP) {
		save_fp_regs1(&dprobes.fpu_save_area);
	}
	else
		memset(&dprobes.fpu_save_area,0, sizeof(dprobes.fpu_save_area));

}

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

static void pushfpc(void) 
{ 
	if (dprobes.status & DP_STATUS_FIRSTFPU)
		dp_save_fpu(); 
	rpnpush(dprobes.fpu_save_area.fpc); 
}

static void pushfp(int reg)
{
	if (dprobes.status & DP_STATUS_FIRSTFPU)
		dp_save_fpu();
	rpnpush(dprobes.fpu_save_area.fprs[reg].fp.lo);
	rpnpush(dprobes.fpu_save_area.fprs[reg].fp.hi);
}


/*
 * Push all floating point regs.
 */
static void pushafp(void)
{
	unsigned short i;
	if (dprobes.status & DP_STATUS_FIRSTFPU)
		dp_save_fpu();

	for (i=0; i<NUM_FPRS; i++) {
		rpnpush(dprobes.fpu_save_area.fprs[i].fp.hi);
		rpnpush(dprobes.fpu_save_area.fprs[i].fp.lo);
	}

}


/*	
 * Push specified control registers
 */
static void pushcr(int reg)
{ 
	if (!(dprobes.status & DP_STATUS_FIRSTCR)) {
		__ctl_store(dprobes.crs, 0, 15);
		dprobes.status |= DP_STATUS_FIRSTCR;
	}
   	
	rpnpush(dprobes.crs[reg]);
}

/*
 * Current register main push function.
 */
static void push_cpuid(void)
{
	rpnpush(S390_lowcore.cpu_data.cpu_id.version);
	rpnpush(S390_lowcore.cpu_data.cpu_id.ident  );
	rpnpush(S390_lowcore.cpu_data.cpu_id.machine);
}
	
/*
 * push msr. 
 */
static void pushr(void)
{
	struct dprobes_struct *dp = &dprobes;
	unsigned short index = get_u16_oprnd();
	unsigned long val;
	switch(index) {

		case DP_PSWMASK: val = dp->regs->psw.mask; break;
		case DP_PSWADDR: val = dp->regs->psw.addr; break;

		case DP_GPR0:  val = dp->regs->gprs[0]; break;
		case DP_GPR1:  val = dp->regs->gprs[1]; break;
		/* I think this is right */
		case DP_GPR2:  val = dp->regs->orig_gpr2; break;
		case DP_GPR3:  val = dp->regs->gprs[3]; break;
		case DP_GPR4:  val = dp->regs->gprs[4]; break;
		case DP_GPR5:  val = dp->regs->gprs[5]; break;
		case DP_GPR6:  val = dp->regs->gprs[6]; break;
		case DP_GPR7:  val = dp->regs->gprs[7]; break;
		case DP_GPR8:  val = dp->regs->gprs[8]; break;
		case DP_GPR9:  val = dp->regs->gprs[9]; break;
		case DP_GPR10: val = dp->regs->gprs[10]; break;
		case DP_GPR11: val = dp->regs->gprs[11]; break;
		case DP_GPR12: val = dp->regs->gprs[12]; break;
		case DP_GPR13: val = dp->regs->gprs[13]; break;
		case DP_GPR14: val = dp->regs->gprs[14]; break;
		case DP_GPR15: val = dp->regs->gprs[15]; break;

		case DP_ACR0:  val = dp->regs->acrs[0]; break; 
		case DP_ACR1:  val = dp->regs->acrs[1]; break; 
		case DP_ACR2:  val = dp->regs->acrs[2]; break; 
		case DP_ACR3:  val = dp->regs->acrs[3]; break; 
		case DP_ACR4:  val = dp->regs->acrs[4]; break; 
		case DP_ACR5:  val = dp->regs->acrs[5]; break; 
		case DP_ACR6:  val = dp->regs->acrs[6]; break; 
		case DP_ACR7:  val = dp->regs->acrs[7]; break; 
		case DP_ACR8:  val = dp->regs->acrs[8]; break; 
		case DP_ACR9:  val = dp->regs->acrs[9]; break; 
		case DP_ACR10: val = dp->regs->acrs[1]; break; 
		case DP_ACR11: val = dp->regs->acrs[11]; break;
		case DP_ACR12: val = dp->regs->acrs[12]; break;
		case DP_ACR13: val = dp->regs->acrs[13]; break;
		case DP_ACR14: val = dp->regs->acrs[14]; break;
		case DP_ACR15: val = dp->regs->acrs[15]; break;
		
		/* hmmm, where should these really come from? */
		case DP_CR_0:  pushcr(0); return; 
		case DP_CR_1:  pushcr(1); return; 
		case DP_CR_2:  pushcr(2); return; 
		case DP_CR_3:  pushcr(3); return; 
		case DP_CR_4:  pushcr(4); return; 
		case DP_CR_5:  pushcr(5); return; 
		case DP_CR_6:  pushcr(6); return; 
		case DP_CR_7:  pushcr(7); return; 
		case DP_CR_8:  pushcr(8); return; 
		case DP_CR_9:  pushcr(9); return; 
		case DP_CR_10: pushcr(10); return;
		case DP_CR_11: pushcr(11); return;
		case DP_CR_12: pushcr(12); return;
		case DP_CR_13: pushcr(13); return;
		case DP_CR_14: pushcr(14); return;
		case DP_CR_15: pushcr(15); return;

		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_PSWMASK: val = dp->uregs->psw.mask; break;
		case DP_PSWADDR: val = dp->uregs->psw.addr; break;
	
		case DP_GPR0:  val = dp->uregs->gprs[0]; break;
		case DP_GPR1:  val = dp->uregs->gprs[1]; break;
		case DP_GPR2:  val = dp->uregs->gprs[2]; break;
		case DP_GPR3:  val = dp->uregs->gprs[3]; break;
		case DP_GPR4:  val = dp->uregs->gprs[4]; break;
		case DP_GPR5:  val = dp->uregs->gprs[5]; break;
		case DP_GPR6:  val = dp->uregs->gprs[6]; break;
		case DP_GPR7:  val = dp->uregs->gprs[7]; break;
		case DP_GPR8:  val = dp->uregs->gprs[8]; break;
		case DP_GPR9:  val = dp->uregs->gprs[9]; break;
		case DP_GPR10: val = dp->uregs->gprs[10]; break;
		case DP_GPR11: val = dp->uregs->gprs[11]; break;
		case DP_GPR12: val = dp->uregs->gprs[12]; break;
		case DP_GPR13: val = dp->uregs->gprs[13]; break;
		case DP_GPR14: val = dp->uregs->gprs[14]; break;
		case DP_GPR15: val = dp->uregs->gprs[15]; break;
	
		case DP_ACR0:  val = dp->uregs->acrs[0]; break; 
		case DP_ACR1:  val = dp->uregs->acrs[1]; break; 
		case DP_ACR2:  val = dp->uregs->acrs[2]; break; 
		case DP_ACR3:  val = dp->uregs->acrs[3]; break; 
		case DP_ACR4:  val = dp->uregs->acrs[4]; break; 
		case DP_ACR5:  val = dp->uregs->acrs[5]; break; 
		case DP_ACR6:  val = dp->uregs->acrs[6]; break; 
		case DP_ACR7:  val = dp->uregs->acrs[7]; break; 
		case DP_ACR8:  val = dp->uregs->acrs[8]; break; 
		case DP_ACR9:  val = dp->uregs->acrs[9]; break; 
		case DP_ACR10: val = dp->uregs->acrs[1]; break; 
		case DP_ACR11: val = dp->uregs->acrs[11]; break;
		case DP_ACR12: val = dp->uregs->acrs[12]; break;
		case DP_ACR13: val = dp->uregs->acrs[13]; break;
		case DP_ACR14: val = dp->uregs->acrs[14]; break;
		case DP_ACR15: val = dp->uregs->acrs[15]; 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;
	
		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_GPR0:  dp->regs->gprs[0]  = val; break;
		case DP_GPR1:  dp->regs->gprs[1]  = val; break;
		case DP_GPR2:  dp->regs->gprs[2]  = val; break;
		case DP_GPR3:  dp->regs->gprs[3]  = val; break;
		case DP_GPR4:  dp->regs->gprs[4]  = val; break;
		case DP_GPR5:  dp->regs->gprs[5]  = val; break;
		case DP_GPR6:  dp->regs->gprs[6]  = val; break;
		case DP_GPR7:  dp->regs->gprs[7]  = val; break;
		case DP_GPR8:  dp->regs->gprs[8]  = val; break;
		case DP_GPR9:  dp->regs->gprs[9]  = val; break;
		case DP_GPR10: dp->regs->gprs[10] = val; break;
		case DP_GPR11: dp->regs->gprs[11] = val; break;
		case DP_GPR12: dp->regs->gprs[12] = val; break;
		case DP_GPR13: dp->regs->gprs[13] = val; break;
		case DP_GPR14: dp->regs->gprs[14] = val; break;
		case DP_GPR15: dp->regs->gprs[15] = 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_GPR0:  dp->uregs->gprs[0]  = val; break;
		case DP_GPR1:  dp->uregs->gprs[1]  = val; break;
		case DP_GPR2:  dp->uregs->gprs[2]  = val; break;
		case DP_GPR3:  dp->uregs->gprs[3]  = val; break;
		case DP_GPR4:  dp->uregs->gprs[4]  = val; break;
		case DP_GPR5:  dp->uregs->gprs[5]  = val; break;
		case DP_GPR6:  dp->uregs->gprs[6]  = val; break;
		case DP_GPR7:  dp->uregs->gprs[7]  = val; break;
		case DP_GPR8:  dp->uregs->gprs[8]  = val; break;
		case DP_GPR9:  dp->uregs->gprs[9]  = val; break;
		case DP_GPR10: dp->uregs->gprs[10] = val; break;
		case DP_GPR11: dp->uregs->gprs[11] = val; break;
		case DP_GPR12: dp->uregs->gprs[12] = val; break;
		case DP_GPR13: dp->uregs->gprs[13] = val; break;
		case DP_GPR14: dp->uregs->gprs[14] = val; break;
		case DP_GPR15: dp->uregs->gprs[15] = 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;
}

