breakpoint.c 6.48 KB

/*************************************************************************
 *
 *  File: breakpoint.c
 *
 *  This file contains signal handler for SIGILL. 
 *
 *  $Header: /root/leakn64/depot/rf/sw/bbplayer/iosim/src/breakpoint.c,v 1.1.1.1 2002/05/30 05:41:20 whs Exp $
 *
 */

#include <sys/cachectl.h>
#include <siginfo.h>
#include <signal.h>
#include <ucontext.h>

#include <stdio.h>

#include "sim.h"
#include "simipc.h"

#define DIR_LOAD	DIR_SINGLE_LOAD
#define DIR_STORE	DIR_SINGLE_STORE

#define SIZE_BYTE	1
#define SIZE_HALF	2
#define SIZE_WORD	4
#define SIZE_DWORD	8

static void	breakpoint_set(minst_t *, int);
static void	breakpoint_handler(int, siginfo_t *, ucontext_t *);
static int	is_branch(minst_t);
static minst_t *branch_target(minst_t, minst_t *, gregset_t);
static int	is_loadstore(minst_t, dir_t *, sz_t *);
static ulong	loadstore_addr(minst_t, gregset_t);

static struct save {
	minst_t *addr;
	minst_t value;
} save[2];
static int savecount;

static minst_t bp = { 0x40906000 };	/* mtc0: illegal */
static const struct sigaction act = {SA_SIGINFO, breakpoint_handler, 0};

static minst_t *lo, *hi;

void
breakpoint_init(descriptor_t *dp)
{
	sigaction(SIGILL, &act, NULL);
	lo = (minst_t *)dp->addr;
	hi = (minst_t *)(dp->addr + dp->size);
	breakpoint_set((minst_t *)dp->entry, 0);
}

static void
breakpoint_set(minst_t *addr, int index)
{

	if (addr < lo || addr >= hi)
		return;
	save[index].addr = addr;
	save[index].value = *addr;
	*addr = bp;
	cacheflush(addr, sizeof(*addr), ICACHE);

	printf("breakpoint_set: save[%d]: addr=0x%08x, value=0x%08x\n",
		index, save[index].addr, save[index].value);
	printf("breakpoint_set: bp=0x%08x, *addr=0x%08x\n", bp, *addr);

	savecount = index + 1;
}

static void
breakpoint_remove(int index)
{
	*save[index].addr = save[index].value;
	cacheflush(save[index].addr, sizeof (minst_t), ICACHE);
}


static void
breakpoint_handler(int sig, siginfo_t *sip, ucontext_t *up)
{
	register int i;
	minst_t inst, bdinst;
	minst_t *pc;
	minst_t *target_pc;
	dir_t dir;
	sz_t sz;

	printf("**** BREAKPOINT HANDLER ****\n");

	/*
	 * Verify we faulted on one of our own here XXX
	 */

	for (i = 0; i < savecount; i++)
		breakpoint_remove(i);

	pc = (minst_t *)sip->si_addr;

	printf("PC=0x%08x, INST=0x%08x, PC+2=0x%08x\n", pc, *pc, pc+2);
	printf("EPC=0x%08x, INST=0x%08x\n", 
		up->uc_mcontext.gregs[CTX_EPC], 
	      *(minst_t *)(up->uc_mcontext.gregs[CTX_EPC]));
	/* up->uc_mcontext.gregs[CTX_EPC] += 4;  */

#if 0
	memory_access((long)pc, REF_INST, DIR_LOAD, SIZE_WORD);

	inst = *pc;
	if (is_branch(inst)) {

		printf(" **** is_branch ****\n");

		target_pc = branch_target(inst, pc, up->uc_mcontext.gregs);
		memory_access((long)(pc+1), REF_INST, DIR_LOAD, SIZE_WORD);
		bdinst = *(pc+1);
		if (is_loadstore(bdinst, &dir, &sz)) {
			memory_access(
			   (ulong)loadstore_addr(bdinst, up->uc_mcontext.gregs),
			   REF_DATA, dir, sz);
		}
		breakpoint_set(target_pc, 0);
		breakpoint_set(pc+2, 0);
	} else {
		if (is_loadstore(inst, &dir, &sz)) {

			printf(" **** is_loadstore ****\n");

			memory_access(
			   (ulong)loadstore_addr(inst, up->uc_mcontext.gregs),
			   REF_DATA, dir, sz);
		}
		breakpoint_set(pc+1, 0);
	}
#endif

}

static int
is_branch(minst_t inst)
{
	switch (inst.j_format.opcode) {
	case spec_op:
		switch (inst.r_format.func) {
		case jr_op:
		case jalr_op:
			return(1);
		}
		return(0);

	case bcond_op:
		switch (inst.i_format.rt) {
		case bltz_op:
		case bgez_op:
		case bltzal_op:
		case bgezal_op:
		case bltzl_op:
		case bgezl_op:
		case bltzall_op:
		case bgezall_op:
			return(1);
		}
		return(0);

	case j_op:
	case jal_op:
	case beq_op:
	case bne_op:
	case blez_op:
	case bgtz_op:
	case beql_op:
	case bnel_op:
	case blezl_op:
	case bgtzl_op:
		return(1);

	case cop0_op:
	case cop1_op:
	case cop2_op:
	case cop3_op:
		switch (inst.r_format.rs) {
		case bc_op:
			return(1);
		}
		return(0);
	}
	return(0);
}

/*
 * branch_target - return address where branch will go
 */
static minst_t *
branch_target(minst_t inst, minst_t *pc, gregset_t gr)
{
	register short simmediate;

	switch (inst.j_format.opcode) {
	case spec_op:
		switch (inst.r_format.func) {
		case jr_op:
		case jalr_op:
			return((minst_t *)gr[inst.r_format.rs]);
		}
		break;

	case bcond_op:
		switch (inst.i_format.rt) {
		case bltz_op:
		case bgez_op:
		case bltzal_op:
		case bgezal_op:
		case bltzl_op:
		case bgezl_op:
		case bltzall_op:
		case bgezall_op:
			/*
			 * assign to temp since compiler currently
			 * doesn't handle signed bit fields
			 */
			simmediate = inst.i_format.simmediate;
			return((minst_t *)((long)pc+4+(simmediate<<2)));
		}
		break;

	case j_op:
	case jal_op:
		return((minst_t *)((((long)pc+4)&~((1<<28)-1)) | (inst.j_format.target<<2)));

	case beq_op:
	case bne_op:
	case blez_op:
	case bgtz_op:
	case beql_op:
	case bnel_op:
	case blezl_op:
	case bgtzl_op:
		/*
		 * assign to temp since compiler currently
		 * doesn't handle signed bit fields
		 */
		simmediate = inst.i_format.simmediate;
		return((minst_t *)((long)pc+4+(simmediate<<2)));

	case cop0_op:
	case cop1_op:
	case cop2_op:
	case cop3_op:
		switch (inst.r_format.rs) {
		case bc_op:
			/*
			 * kludge around compiler deficiency
			 */
			simmediate = inst.i_format.simmediate;
			return((minst_t *)((long)pc+4+(simmediate<<2)));
		}
		break;
	}
	return pc;
}

static int
is_loadstore(minst_t inst, dir_t *dirp, sz_t *szp)
{
	switch(inst.i_format.opcode) {
	case lb_op: case lbu_op:
		*dirp=DIR_LOAD; *szp = SIZE_BYTE; return(1);
	case lh_op: case lhu_op:
		*dirp=DIR_LOAD; *szp = SIZE_HALF; return(1);
	case lw_op: case lwu_op: case ll_op: case lwc1_op:
		*dirp=DIR_LOAD; *szp = SIZE_WORD; return(1);
#ifdef JEFF
	case lwl_op: case lwr_op:
		abort();
#endif
	case ld_op: case lld_op: case ldc1_op:
		*dirp=DIR_LOAD; *szp = SIZE_DWORD; return(1);
#ifdef JEFF
	case ldl_op:
	case ldr_op:
#endif
	case sb_op:
		*dirp=DIR_STORE; *szp = SIZE_BYTE; return(1);
	case sh_op:
		*dirp=DIR_STORE; *szp = SIZE_HALF; return(1);
	case sw_op: case sc_op: case swc1_op:
		*dirp=DIR_STORE; *szp = SIZE_WORD; return(1);
#ifdef JEFF
	case swl_op:
	case swr_op:
#endif
	case sd_op: case scd_op: case sdc1_op:
		*dirp=DIR_STORE; *szp = SIZE_DWORD; return(1);
#ifdef JEFF
	case sdl_op:
	case sdr_op:
#endif
		return(1);
	}
	return(0);
}

/*
 * ldst_addr - compute data address instruction at EF_EPC would reference
 * 2 versions - layered-up to allow caller to avoid
 * assumptions that the outer layer makes.
 */
static ulong
loadstore_addr(minst_t inst, gregset_t gr)
{
	ulong base;

	base = (inst.i_format.rs == 0) ? 0 : gr[inst.i_format.rs];
	return (base + inst.i_format.simmediate);
}