/*
 * 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.
 */

#include <linux/dprobes.h>
#include <linux/vmalloc.h>
#include <linux/serial_reg.h>

#include <asm/uaccess.h>
#include <asm/ptrace.h>
#include <asm/io.h>
#include <asm/dprobes_exclude.h>
#ifdef CONFIG_DR_ALLOC
#include <asm/dr_alloc.h>
#endif

void dprobes_asm_code_start(void) { }

/*---------------------Logging code section start -----------------------------*/
/*
 * Format strings for printing out the log.
 */
#define DP_LOG_HDR_FMT		"dprobes(%d,%d) "
#define DP_LOG_CPU_FMT		"cpu=%d "
#define DP_LOG_PROCNAME_FMT	"name=%s "
#define DP_LOG_PID_FMT		"pid=%d "
#define DP_LOG_UID_FMT		"uid=%d "
#define DP_LOG_NIP_FMT		"ip=%08lx "
#define DP_LOG_TSC_FMT		"tsc=%08lx:%08lx "
#define DP_LOG_NEWLINE_FMT	"\n"
#define DP_LOG_DATA_FMT		"%x "

static void log_stack_trace(struct dprobes_struct *dp)
{
	unsigned long i;
	byte_t *ex_log;
	unsigned char *tmp = dp->log_hdr;
	unsigned long ex_log_len = dp->ex_log_len;

	if (ex_log_len > MIN_ST_SIZE) {
		tmp += sprintf(tmp, DP_LOG_HDR_FMT, 0, 0);

#ifdef CONFIG_SMP
		tmp += sprintf(tmp, DP_LOG_CPU_FMT, current->processor);
#endif
		printk("%s ", dp->log_hdr);

		ex_log_len -= MIN_ST_SIZE;
		ex_log = dp->ex_log + MIN_ST_SIZE;
		for (i = 0; i < ex_log_len; i++)
			printk(DP_LOG_DATA_FMT, ex_log[i]);
		printk(DP_LOG_NEWLINE_FMT);
	}
}

static void fmt_log_hdr(struct dprobes_struct *dp)
{
	unsigned char *tmp = dp->log_hdr;
	unsigned long log_flags = dp->mod->pgm.flags & DP_LOG_MASK;

	tmp += sprintf(tmp, DP_LOG_HDR_FMT, dp->major, dp->minor);

#ifdef CONFIG_SMP
	tmp += sprintf(tmp, DP_LOG_CPU_FMT, current->processor);
#endif
	if (log_flags & DP_LOG_PID)
		tmp += sprintf(tmp, DP_LOG_PID_FMT, current->pid);
	if (log_flags & DP_LOG_UID)
		tmp += sprintf(tmp, DP_LOG_UID_FMT, current->uid);
	if (log_flags & DP_LOG_NIP)
		tmp += sprintf(tmp, DP_LOG_NIP_FMT, dp->nip);
	if (log_flags & DP_LOG_TSC) {
		struct timeval ts;
		do_gettimeofday(&ts);
		tmp += sprintf(tmp, DP_LOG_TSC_FMT, ts.tv_sec, ts.tv_usec);
	}
	if (log_flags & DP_LOG_PROCNAME)
		tmp += sprintf(tmp, DP_LOG_PROCNAME_FMT, current->comm);
	tmp += sprintf(tmp, DP_LOG_NEWLINE_FMT);

	tmp += sprintf(tmp, DP_LOG_HDR_FMT, dp->major, dp->minor);
	*tmp = '\0';

	return;
}

/*
 * Saves (writes) the log to the standard kernel log.
 */
static int log_to_klog(struct dprobes_struct *dp)
{
	unsigned long i;
	byte_t *log = dp->log + dp->mod->hdr.len;
	unsigned long log_len = dp->log_len - dp->mod->hdr.len;

	fmt_log_hdr(dp);
	printk("%s ", dp->log_hdr);

	for (i = 0; i < log_len; i++)
		printk(DP_LOG_DATA_FMT, log[i]);
	printk(DP_LOG_NEWLINE_FMT);

	return 0;
}	

/*
 * The order in which the optional header elements is written out has to be the
 * same order in which the DP_HDR_* constants are defined in 
 * include/linux/dprobes.h. The formatter is expected to look for the optional
 * elements in the same order.
 */
static void fmt_log_hdr_raw(struct dprobes_struct *dp)
{
	unsigned char *tmp = dp->log;
	unsigned long mask = dp->mod->hdr.mask;

	dp->mod->hdr.major = dp->major;
	dp->mod->hdr.minor = dp->minor;
	*(struct dp_trace_hdr_struct *)tmp = dp->mod->hdr;	
	tmp += sizeof(dp->mod->hdr);

#ifdef CONFIG_SMP
	*(unsigned int *)tmp = current->processor;
	tmp += sizeof(int);
#endif
	if (mask & DP_HDR_PID) {
		*(pid_t *)tmp = current->pid;
		tmp += sizeof(pid_t);
	}
	if (mask & DP_HDR_UID) {
		*(uid_t *)tmp = current->uid;
		tmp += sizeof(uid_t);
	}
	if (mask & DP_HDR_TSC) {
		struct timeval ts;
		do_gettimeofday(&ts);
		*(struct timeval *)tmp = ts;
		tmp += sizeof(ts);
	}
	if (mask & DP_HDR_PROCNAME) {
		memcpy(tmp, current->comm, 16);
		tmp += 16;
	}
	return;
}

/*
 * dp->mod->hdr is changed here. Hence should be called only after
 * fmt_log_hdr_raw().
 */
static void fmt_st_hdr_raw(struct dprobes_struct *dp)
{
	unsigned char *tmp = dp->ex_log;
	dp->mod->hdr.mask = DP_HDR_MAJOR | DP_HDR_MINOR;
	dp->mod->hdr.major = DP_ST_MAJOR;
	dp->mod->hdr.minor = DP_ST_MINOR;
	dp->mod->hdr.len = MIN_ST_SIZE;
	*(struct dp_trace_hdr_struct *)tmp = dp->mod->hdr;	
	tmp += sizeof(dp->mod->hdr);
#ifdef CONFIG_SMP
	dp->mod->hdr.mask |= DP_HDR_CPU;
	*(unsigned int *)tmp = current->processor;
	tmp += sizeof(int);
	dp->mod->hdr.len += sizeof(int);
#endif
}

#if defined(CONFIG_TRACE) || defined(CONFIG_TRACE_MODULE)
#include <linux/trace.h>

static int log_to_ltt(struct dprobes_struct *dp)
{
	trace_raw_event(dp->mod->trace_id, dp->log_len, dp->log);	
	return 0;
}
static int log_st_to_ltt(struct dprobes_struct *dp)
{
	trace_raw_event(dp->mod->trace_id, dp->ex_log_len, dp->ex_log);
	return 0;
}
#else 
static int log_to_ltt(struct dprobes_struct *dp)
{
	printk(KERN_WARNING "dprobes: Linux Trace Toolkit not available.\n");
	return 0;
}
static int log_st_to_ltt(struct dprobes_struct *dp)
{
	printk(KERN_WARNING "dprobes: Linux Trace Toolkit not available.\n");
	return 0;
}
#endif

#ifdef CONFIG_EVLOG
#include <linux/evl_log.h>
static int log_to_evl(struct dprobes_struct *dp)
{
	/*
	 * We will need to modify this in future to create a DPROBES facility
	 * type and use dp->id instead of (major,minor) for event type. Even the
	 * priority ought to be something other than LOG_KERN.
	 */
	posix_log_write(LOG_KERN, 
			(dp->major << 16) | dp->minor, /* dp->id */
			LOG_INFO,
			(dp->log + dp->mod->hdr.len),
			(dp->log_len - dp->mod->hdr.len),
			POSIX_LOG_BINARY,
			0);
	return 0;
}
#else
static int log_to_evl(struct dprobes_struct *dp)
{
	printk(KERN_WARNING "dprobes: POSIX Event Logging not available.\n");
 	return 0;
}
#endif

static inline void save_log(struct dprobes_struct *dp)
{
	switch(dp->mod->pgm.flags & DP_LOG_TARGET_MASK) {
	
	case DP_LOG_TARGET_LTT:
		fmt_log_hdr_raw(dp);
		log_to_ltt(dp);
		if (dp->ex_log_len > MIN_ST_SIZE) {
			fmt_st_hdr_raw(dp);
			log_st_to_ltt(dp);
		}
		break;
	case DP_LOG_TARGET_EVL:
		log_to_evl(dp);
		break;
	default:
		log_to_klog(dp);
		log_stack_trace(dp);
		break;
	}
	return;
}

/*--------------------- Logging code section end -----------------------------*/

static inline int is_fpu_instn(byte_t *addr)
{
	return 0;
}


/*
* Here we know for sure that the addr is valid and is paged in.
* Hence used with no error checks.
*/
static inline pte_t *get_one_pte_nocheck(struct mm_struct *mm, unsigned long addr)
{
	return pte_offset(pmd_offset(pgd_offset(mm, addr), addr), addr);
}
/*
* page_address() gives the real address of the page in memory. In VM
* you cat d <page struct *>.40 and the last entry is the real addr
*/
static inline void make_alias(unsigned long addr)
{
	register pte_t * pte;
	register struct page *dpage;


	pte = get_one_pte_nocheck(current->mm, addr);
	dpage = pte_page(__pte(pte_val(*pte)));
	dprobes.alias_addr = page_address(dpage);

	/* do we need this? or a global flush or nothing? */
	/* __flush_tlb_one(dprobes.alias_addr); */
	return;
}


/*
* Restores the original instruction at the current probe location.
*/
static inline void restore_original(struct dprobes_struct *dp)
{

	if (dp->reset_addr)
		goto restore;
	if (dp->status & DP_KERNEL_PROBE) {
		dp->reset_addr = dp->probe_addr;
	} else {
		make_alias(dp->probe_addr);
		dp->reset_addr = (unsigned long) dp->alias_addr + (dp->probe_addr & ~PAGE_MASK);
	}
	restore:
	*((__u32 *)dp->reset_addr) = dp->opcode;
	flush_icache_range((unsigned long)dp->reset_addr, (unsigned long)dp->reset_addr + sizeof(unsigned long));
	return;
}

/*
 * Does necessary setup for single stepping.
 */
static inline void setup_ss(struct dprobes_struct *dp, struct pt_regs *regs)
{

	/* Save off the MSR so we can put it back clean */
	dp->msr = regs->msr;
	/* turn off interrupts, turn on single step */
	regs->msr |= (MSR_SE | MSR_ILE | MSR_EE | MSR_ME);
	/* rewind the instruction pointer to where we stopped */
	instruction_pointer(regs) = dp->probe_addr;

	dp->status |= DP_STATUS_SS;

	/* moved this down here to tighten down the window when the original opcode is visible */
	restore_original(dp);                    

	return;
}


/*
 * Exits to the selected facility after the original instruction is
 * successfully executed.
 */
static inline void exit_probe(struct dprobes_struct *dp)
{
	dp->rec->count++;
	if (dp->rec->count >= dp->rec->point.maxhits) {
		dp->rec->status &= ~DP_REC_STATUS_ACTIVE;
	       	dp->rec->status |= DP_REC_STATUS_DISABLED;
		if (dp->rec->point.probe == DP_PROBE_BREAKPOINT)
			restore_original(dp);
	}
	if (dp->status & DP_STATUS_ABORT)
		return;
	save_log(dp);
	return;
}

/*
* Called after single-stepping, successfully or not, to reinstate the probe.
*/
static inline void reset_probe(struct dprobes_struct *dp)
{
	*((__u32 *)dp->reset_addr) = DP_INSTR_BREAKPOINT;
	flush_icache_range((unsigned long)dp->reset_addr, (unsigned long)dp->reset_addr + sizeof(unsigned long));
	return;
}

/*
 * Called after a probe is completely handled to release locks etc.
 */
static inline void done_probe(struct dprobes_struct *dp)
{
	dp->status &= ~DP_STATUS_DONE;
	spin_unlock(&dp->rec->lock);
	dp->sem--;
	return;
}
/*
 * Resets the Single-Step Trace Enable
 * turn interrupts back on for the user process
 */
static inline void reset_se(struct dprobes_struct *dp, struct pt_regs *regs)
{
	/* reset msr to what it was when we stopped, will keep SE etc intact */
	regs->msr = dp->msr;
}
/*
 * Handles the case when the single stepping has failed. For eg. does
 * necessary cleanup if the single-stepped instruction faults.
 */
static inline void ss_failed(struct dprobes_struct *dp, struct pt_regs *regs)
{
	reset_probe(dp);
	reset_se(dp,regs);
	done_probe(dp);
	return;
}

/*
 * Handles the case when the single stepping has failed and we still want
 * to save the log as in the case of ss'ing causing a bounds exception
 * which does not mean the instruction has failed to execute.
 */
static inline void ss_failed_save_log(struct dprobes_struct *dp, struct pt_regs *regs)
{
	reset_probe(dp);
	reset_se(dp,regs);
	exit_probe(dp);
	done_probe(dp);
	return;
}


/*
 * Does post-processing after the original instruction is successfully
 * single stepped.
 */
static inline void ss_done(struct dprobes_struct *dp, struct pt_regs *regs)
{
	reset_probe(dp);
	reset_se(dp,regs);
	exit_probe(dp);
	done_probe(dp);
	return;
}


/*
 * Locate the vma, and search by the vm_file's inode + offset rather
 * than by this page so that this works for COWed pages too.
 *
 * We'll need to hold the mm lock here when finding user mode probes.
 * No deadlock considerations here as it is used only for user space probes.
 */
static inline struct dp_record_struct * find_record_at(unsigned long addr, int ptype, struct pt_regs * regs)
{
	if (user_mode(regs)) {
		struct mm_struct *mm = current->mm; 
		struct vm_area_struct *vma;
		unsigned long offset;
		struct inode *inode;

		if (ptype == DP_PROBE_WATCHPOINT)
			return find_rec(NULL, addr);

		spin_lock(&mm->page_table_lock); 
		vma = find_vma(mm, addr);
		offset= addr - vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT);

		if (!vma->vm_file) {
			spin_unlock(&mm->page_table_lock);
			return NULL;
		}
		inode = vma->vm_file->f_dentry->d_inode;
		spin_unlock(&mm->page_table_lock);
		return find_rec(inode, offset);
	}
	else {
	 	return find_rec(NULL, addr);
	}
}



/*	
 * Writing the breakpoint instruction should be the last thing done
 * in this function to ensure that all the structures that
 * are needed to locate the probe are in place before a probe can
 * be hit.
 */
static inline int insert_bp(byte_t *addr, struct dp_record_struct *rec,
	struct dp_module_struct *m, struct page * page)
{
	if (excluded((unsigned long)addr)) {
		rec->status |= DP_REC_STATUS_EXCLUDED;
		return -1;
	}
	if (!(m->pgm.flags & DP_MODTYPE_USER) &&
		!(((unsigned long)addr) >= m->base && 
		((unsigned long)addr) < (m->end + m->base))) {
		rec->status |= DP_REC_STATUS_INVALID_OFFSET;
		return -1;
	} 
	if (*((__u32 *) addr) != rec->point.opcode)  {
		if (m->pgm.flags & DP_DONT_VERIFY_OPCODES) {
			rec->point.opcode = *((__u32 *) addr);
		} else {
			if (!(m->pgm.flags & DP_MODTYPE_USER) || 
			    !IS_COW_PAGE(page, m->inode)) {
				rec->point.actual_opcode = *((__u32 *) addr);
				rec->status |= DP_REC_STATUS_MISMATCH;
			}
			return -1;
		}
	}
/*	if (!MACHINE_HAS_IEEE && is_fpu_instn(addr)) {
		rec->status |= DP_REC_STATUS_EXCLUDED_OPCODE;
		return -1;
	} */

	rec->status |= DP_REC_STATUS_ACTIVE;
	add_rec_to_hash_queue(rec, m->inode, rec->point.offset);
	*((__u32 *) addr) = DP_INSTR_BREAKPOINT;
	flush_icache_range((unsigned long)addr, (unsigned long)addr + sizeof(unsigned long));
	return 0;
}


/*
 * Silently, permanently remove a reentered probe. As this _must_ have 
 * been in kernel. No need for setting up an alias.
 hmmm, although this shouldn't hit for user mode probes, it is.
 * Ignore the reentered watchpoint. No permanent removal.
 */
static inline int remove_reentered(struct dp_record_struct *rec, struct pt_regs * regs, unsigned long addr)
{
	unsigned short probe = rec->point.probe & DP_PROBE_MASK;
	
	if (probe == DP_PROBE_BREAKPOINT) {
		*(__u32 *)addr = rec->point.opcode;
		flush_icache_range((unsigned long)addr, (unsigned long)addr + sizeof(unsigned long));
		regs->nip = addr;   
		rec->status &= ~DP_REC_STATUS_ACTIVE;
		rec->status |= DP_REC_STATUS_REMOVED;
	} 
	
	/* FIXME saving for watchpoints. */
	/* else {
		
		set_rf(rec, regs);
	} */

	spin_unlock(&rec->lock); /* release the per-record spinlock */
	return 1;
}

/* 
 * For breakpoint probes,
 * If we recurse into illegal_op on the same probe, then we put the original
 * instruction back and let it run effectively ignoring second probe 
 * hit. Note that unlike recursion due to a different probe, here we
 * don't permanently remove this probe: no change in record status
 * no locking it (we already have the rec->lock.
 *
 * For watchpoint probes,
 * If we recurse into trap1 on same probe, we just (setting RF for
 * instruction breakpoint) return, effectively ignoring the second probe hit.
 */
static inline int reentered_on_same_probe(int ptype, unsigned long addr, struct pt_regs *regs, unsigned int condition)
{
	struct dprobes_struct *dp = &dprobes;
	
	if (ptype == DP_PROBE_BREAKPOINT) {
		*(__u32 *)addr = dp->opcode;
		flush_icache_range((unsigned long)addr, (unsigned long)addr + sizeof(unsigned long));
		regs->nip = addr;   
	}
	/* FIXME saving for watchpoints. */
/*	else {
		if (dr_trap_type(condition) == DP_WATCHTYPE_EXECUTE)
			regs->eflags |= EF_RF;
	}
*/	
	return 1;
}

int dp_trap(struct pt_regs * regs, int probe_type, unsigned int condition, unsigned long addr)
{
	struct dprobes_struct *dp = &dprobes;
	struct dp_record_struct *rec;
	__u32 *opcode; /* for recently removed probe opcode check */

	if (dp->sem && dp->probe_addr == addr) {
		return reentered_on_same_probe(probe_type, addr, regs, condition);
	} 

	rec = find_record_at(addr, probe_type, regs);
	if (!rec)
		goto no_probe;
	/*
	 * We found a probe at the addr, but first check if it is reentrant
	 * before running it.
	 * Note that find_record_at returns with rec->lock held.
	 */
	if (dp->sem) {
		return remove_reentered(rec, regs, addr);
	}
	
	dp->sem++;

	/*
	 * valid probe, handle it.
	 */
	dp->rec = rec;
	dp->opcode = rec->point.opcode;
	dp->probe_addr = addr;
	dp->reset_addr = 0; /* alias not yet created */

	if (rec->status & (DP_REC_STATUS_DISABLED | DP_REC_STATUS_REMOVED)) {
#ifdef DEBUG_DPROBES
		printk("disabled probe fired !!!\n");
#endif
		goto remove_it;
       	}

	dp->regs = regs;
	dp->mod = rec->mod;
	dp->major = rec->point.major;
	dp->minor = rec->point.minor;
	dp->nip = regs->nip;
	
	/* Check probem state bit for user mode */
	if (user_mode(regs)) {
		dp->status |= DP_USER_PROBE;
		dp->uregs = regs;
		// make sure we are running when we fall out
		current->need_resched = 0;
	} else {
		dp->status |= DP_KERNEL_PROBE;
		dp->uregs = (struct pt_regs * )((unsigned long)(current) + 2 * PAGE_SIZE - sizeof(struct pt_regs));
	}

	dp->status |= DP_STATUS_FIRSTFPU;
  
	/* rec->count > 0 for passcount */
	if (rec->status & DP_REC_STATUS_ACTIVE && rec->count >= 0) {
		dp_interpreter(); 
		if (rec->status & (DP_REC_STATUS_DISABLED | DP_REC_STATUS_REMOVED)) {
			if (rec->status & DP_REC_STATUS_ACTIVE)
				goto remove_it;
			else
				goto removed;
		}
	} else {
		dp->status |= DP_STATUS_ABORT;
	}
	
	if (probe_type == DP_PROBE_BREAKPOINT) {
		setup_ss(dp, regs);
	} else {
		exit_probe(dp);
		done_probe(dp);
/*		saving next line for watchpoints
		set_rf(rec, regs);                */		
	}
	
	return 1;

remove_it:
	rec->status &= ~DP_REC_STATUS_ACTIVE;
	rec->status |= DP_REC_STATUS_DISABLED;
	if (probe_type == DP_PROBE_BREAKPOINT)
		restore_original(dp);
	else
		printk(KERN_ERR "DProbes: delete_watchpoint(watchpoints are not yet implemented)\n"); 
		/* delete_watchpoint(condition, regs, rec); */

removed:
	if (probe_type == DP_PROBE_BREAKPOINT)				 
        regs->nip  = addr; 
	done_probe(dp);
	return 1;

no_probe:
	/*
 	 * If the trapping location does not have the illegal instruction, 
	 * we can probably consider that this was a probe location 
	 * that was recently removed. Tell illegal_op() that we handled 
	 * it so that the process will not get a SIGILL.
	 */
	if (probe_type == DP_PROBE_BREAKPOINT) {

		/* had to rework this, caused pointer deref oops
		   for user mode probes in original form */
		if (user_mode(regs)) 
            get_user(*((__u32 *) opcode), (__u32 *)addr);
		else
			*((__u32 *)opcode) = *((__u32 *)addr);

		if (*((__u32 *)opcode) != DP_INSTR_BREAKPOINT) {
			regs->nip = addr;   
			return 1;
		}
	}
#ifdef CONFIG_DR_ALLOC
	/*
	 * If the local or global enable bit in dr7 is not set for
	 * the debug register which caused the trap, we assume that
	 * this trap is due to a DProbes watchpoint which was recently
	 * removed. Tell do_debug that we handled it so that the
	 * process will not get a SIGTRAP.
	 */
	else {
		int dbno;
		unsigned long dr7;
		dbno = dr_trap(condition);
		if (dbno != -1) {
			dr7 = read_dr(7);
			if (!(dr7 & (3UL<<dbno*2))) {
					if (dr_trap_type(condition) == DP_WATCHTYPE_EXECUTE)
						regs->eflags |= EF_RF;
				return 1;
			}
		}
	}	
#endif	

	return 0;
}


/*
 * Return values 0 means that this trap1 is NOT handled by us. Any other
 * return value indicated otherwise.
 */
int dp_trap1(struct pt_regs * regs)
{
	register struct dprobes_struct *dp = &dprobes;

	if (!(dp->status & DP_STATUS_SS))
		goto no_probe;

	/* ss_success */
	ss_done(dp, regs);
	/*
	 * if somebody else is singlestepping across a probe point, eflags
	 * will have TF set, in which case, continue the remaining processing
	 * of do_debug, as if this is not a probe hit. 
	 *** FIXME is this necessary here? ***
	if (regs->msr & MSR_SR) 
		goto no_probe;
	 *** as a replacement for the IA-32 ***   
	if (regs->eflags & EF_TF)
		goto no_probe;
	 */

	return 1;

no_probe:
	return 0;
}

/*
 *
 * Return values 0 means that this fault is NOT handled by us. Any other
 * return value indicated otherwise.
 */
int dp_gpf(struct pt_regs * regs)
{
	register struct dprobes_struct *dp = &dprobes;
	unsigned long fixup;

	if (dp->status & DP_STATUS_INTERPRETER) {
		dp->status |= DP_STATUS_GPF;
		/* Are we prepared to handle this kernel fault?  */
		if ((fixup = search_exception_table(regs->nip)) != 0) {
			regs->nip = fixup;
			return 1;
		}

		/*
		 * Major problem. We are in the interpreter, got a page fault
		 * and don't have a local exception handler. Let the default
		 * fault handler take effect, which will oops.
		 */
		return 0;
	}
	else if (dp->status & DP_STATUS_SS) {
		if (!dp->rec->point.logonfault) {
			ss_failed(dp, regs);
		}
		else 
			ss_failed_save_log(dp, regs);
	}
	return 0;
}

/*
 * Return values 0 means that this fault is NOT handled by us. Any other
 * return value indicated otherwise.
 */
int dp_pf(struct pt_regs * regs)
{
	register struct dprobes_struct *dp = &dprobes;
	unsigned long fixup;

	if (dp->status & DP_STATUS_INTERPRETER) {
		dp->status |= DP_STATUS_PF;
		/* Are we prepared to handle this kernel fault?  */
		if ((fixup = search_exception_table(regs->nip)) != 0) {
			regs->nip = fixup;
			return 1;
		}

		/*
		 * Major problem. We are in the interpreter, got a page fault
		 * and don't have a local exception handler. Let the default
		 * fault handler take effect, which will oops.
		 */
		return 0;
	}
	else if (dp->status & DP_STATUS_SS) {
		if (!dp->rec->point.logonfault) {
			ss_failed(dp, regs);
		}
		else 
			ss_failed_save_log(dp, regs);
	}

	return 0;
}

/*
 * If any other fault occurs while we are single-stepping, we need to
 * reinstate the probe. If any fault occurs, we still want to 
 * save the log.
 */
int dp_handle_fault(struct pt_regs * regs)
{
	register struct dprobes_struct *dp = &dprobes;

	if (dp->status & DP_STATUS_SS) {
		if (!dp->rec->point.logonfault) {
			ss_failed(dp, regs);
		}
		else 
			ss_failed_save_log(dp, regs);
	}
	return 0;
}


int __insert_probe(byte_t *addr, struct dp_record_struct *rec, 
	struct dp_module_struct *m, struct page * page)
{
	if (rec->point.probe & DP_PROBE_BREAKPOINT) {
		return insert_bp(addr, rec, m, page);
	}
	else {
		printk(KERN_ERR "DProbes: __insert_probe(watchpoints are not yet implemented)\n"); 
		return 0;
	}
}

static inline void remove_bp(byte_t *addr, struct dp_record_struct *rec)
{
	if (*((__u32 *) addr) == DP_INSTR_BREAKPOINT) {
		*((__u32 *) addr) = rec->point.opcode;
		flush_icache_range((unsigned long)addr, (unsigned long)addr + sizeof(unsigned long));
	}
}

int __remove_probe(byte_t * addr, struct dp_record_struct *rec)
{
	if (rec->point.probe & DP_PROBE_BREAKPOINT){
		remove_bp(addr, rec);
	}
	else 
		printk(KERN_ERR "DProbes: __remove_probe(watchpoints are not yet implemented)\n"); 
	return 0;
}

void dprobes_asm_code_end(void) { }

