code:
/*	$OpenBSD: db_interface.c,v 1.20 2014/05/31 11:19:06 miod Exp $	*/
/*
 * Mach Operating System
 * Copyright (c) 1993-1991 Carnegie Mellon University
 * Copyright (c) 1991 OMRON Corporation
 * All Rights Reserved.
 *
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND
 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie the
 * rights to redistribute these changes.
 */

/*
 * m88k interface to ddb debugger
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/reboot.h>

#include <uvm/uvm_extern.h>

#include <machine/asm_macro.h>
#include <machine/cmmu.h>
#include <machine/trap.h>
#include <machine/db_machdep.h>
#include <machine/cpu.h>
#ifdef M88100
#include <machine/m88100.h>
#include <machine/m8820x.h>
#endif

#include <ddb/db_access.h>
#include <ddb/db_command.h>
#include <ddb/db_extern.h>
#include <ddb/db_interface.h>
#include <ddb/db_output.h>
#include <ddb/db_run.h>
#include <ddb/db_sym.h>

extern label_t *db_recover;
extern int frame_is_sane(db_regs_t *, int);	/* db_trace */
extern void cnpollc(int);

void	kdbprinttrap(int);

int	m88k_dmx_print(u_int, u_int, u_int, u_int);

void	m88k_db_trap(int, struct trapframe *);
void	m88k_db_print_frame(db_expr_t, int, db_expr_t, char *);
void	m88k_db_registers(db_expr_t, int, db_expr_t, char *);
void	m88k_db_where(db_expr_t, int, db_expr_t, char *);
void	m88k_db_frame_search(db_expr_t, int, db_expr_t, char *);
void	m88k_db_translate(db_expr_t, int, db_expr_t, char *);
void	m88k_db_cmmucfg(db_expr_t, int, db_expr_t, char *);

db_regs_t ddb_regs;

#ifdef MULTIPROCESSOR
#include <sys/mplock.h>
struct __mp_lock ddb_mp_lock;
cpuid_t	ddb_mp_nextcpu = (cpuid_t)-1;

void	m88k_db_cpu_cmd(db_expr_t, int, db_expr_t, char *);
#endif

/*
 * If you really feel like understanding the following procedure and
 * macros, see pages 6-22 to 6-30 (Section 6.7.3) of
 *
 * MC88100 RISC Microprocessor User's Manual Second Edition
 * (Motorola Order: MC88100UM/AD REV 1)
 *
 * and ERRATA-5 (6-23, 6-24, 6-24) of
 *
 * Errata to MC88100 User's Manual Second Edition MC88100UM/AD Rev 1
 * (Oct 2, 1990)
 * (Motorola Order: MC88100UMAD/AD)
 */

#ifdef M88100
/* macros for decoding dmt registers */

/*
 * return 1 if the printing of the next stage should be suppressed
 */
int
m88k_dmx_print(u_int t, u_int d, u_int a, u_int no)
{
	static const u_int addr_mod[16] = {
		0, 3, 2, 2, 1, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0
	};
	static const char *mode[16]  = {
		"?", ".b", ".b", ".h", ".b", "?", "?", "?",
		".b", "?", "?" , "?" , ".h" , "?", "?", ""
	};
	static const u_int mask[16] = {
		0, 0xff, 0xff00, 0xffff,
		0xff0000, 0, 0, 0,
		0xff000000, 0, 0, 0,
		0xffff0000, 0, 0, 0xffffffff
	};
	static const u_int shift[16] = {
		0,  0, 8, 0, 16, 0, 0, 0,
		24, 0, 0, 0, 16, 0, 0, 0
	};
	int reg = DMT_DREGBITS(t);

	if (ISSET(t, DMT_LOCKBAR)) {
		db_printf("xmem%s%s r%d(0x%x) <-> mem(0x%x),",
		    DMT_ENBITS(t) == 0x0f ? "" : ".bu",
		    ISSET(t, DMT_DAS) ? "" : ".usr", reg,
		    ((t >> 2 & 0xf) == 0xf) ? d : (d & 0xff), a);
		return 1;
	} else if (DMT_ENBITS(t) == 0xf) {
		/* full or double word */
		if (ISSET(t, DMT_WRITE)) {
			if (ISSET(t, DMT_DOUB1) && no == 2)
				db_printf("st.d%s -> mem(0x%x) (** restart sxip **)",
				    ISSET(t, DMT_DAS) ? "" : ".usr", a);
			else
				db_printf("st%s (0x%x) -> mem(0x%x)",
				    ISSET(t, DMT_DAS) ? "" : ".usr", d, a);
		} else {
			/* load */
			if (ISSET(t, DMT_DOUB1) && no == 2)
				db_printf("ld.d%s r%d <- mem(0x%x), r%d <- mem(0x%x)",
				    ISSET(t, DMT_DAS) ? "" : ".usr", reg, a, reg+1, a+4);
			else
				db_printf("ld%s r%d <- mem(0x%x)",
				    ISSET(t, DMT_DAS) ? "" : ".usr", reg, a);
		}
	} else {
		/* fractional word - check if load or store */
		a += addr_mod[DMT_ENBITS(t)];
		if (ISSET(t, DMT_WRITE))
			db_printf("st%s%s (0x%x) -> mem(0x%x)",
			    mode[DMT_ENBITS(t)],
			    ISSET(t, DMT_DAS) ? "" : ".usr",
			    (d & mask[DMT_ENBITS(t)]) >> shift[DMT_ENBITS(t)],
			    a);
		else
			db_printf("ld%s%s%s r%d <- mem(0x%x)",
			    mode[DMT_ENBITS(t)],
			    ISSET(t, DMT_SIGNED) ? "" : "u",
			    ISSET(t, DMT_DAS) ? "" : ".usr", reg, a);
	}
	return (0);
}
#endif	/* M88100 */

void
m88k_db_print_frame(addr, have_addr, count, modif)
	db_expr_t addr;
	int have_addr;
	db_expr_t count;
	char *modif;
{
	struct trapframe *s = (struct trapframe *)addr;
	char *name;
	db_expr_t offset;
#ifdef M88100
	int suppress1 = 0, suppress2 = 0;
#endif
	int c, force = 0, help = 0;

	if (!have_addr) {
		db_printf("requires address of frame\n");
		help = 1;
	}

	while (modif && *modif) {
		switch (c = *modif++, c) {
		case 'f':
			force = 1;
			break;
		case 'h':
			help = 1;
			break;
		default:
			db_printf("unknown modifier [%c]\n", c);
			help = 1;
			break;
		}
	}

	if (help) {
		db_printf("usage: mach frame/[f] ADDRESS\n");
		db_printf("  /f force printing of insane frames.\n");
		return;
	}

	if (badaddr((vaddr_t)s, 4) ||
	    badaddr((vaddr_t)(&((db_regs_t*)s)->fpit), 4)) {
		db_printf("frame at %8p is unreadable\n", s);
		return;
	}

	if (frame_is_sane((db_regs_t *)s, 0) == 0) {
		if (force == 0)
			return;
	}

#define R(i) s->tf_r[i]
#define IPMASK(x) ((x) &  ~(3))
	db_printf("R00-05: 0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx\n",
	    R(0), R(1), R(2), R(3), R(4), R(5));
	db_printf("R06-11: 0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx\n",
	    R(6), R(7), R(8), R(9), R(10), R(11));
	db_printf("R12-17: 0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx\n",
	    R(12), R(13), R(14), R(15), R(16), R(17));
	db_printf("R18-23: 0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx\n",
	    R(18), R(19), R(20), R(21), R(22), R(23));
	db_printf("R24-29: 0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx\n",
	    R(24), R(25), R(26), R(27), R(28), R(29));
	db_printf("R30-31: 0x%08lx  0x%08lx\n", R(30), R(31));

	db_printf("%cxip: 0x%08lx ",
	    CPU_IS88110 ? 'e' : 's', s->tf_sxip & XIP_ADDR);
	db_find_xtrn_sym_and_offset((db_addr_t)IPMASK(s->tf_sxip),
	    &name, &offset);
	if (name != NULL && (u_int)offset <= db_maxoff)
		db_printf("%s+0x%08x", name, (u_int)offset);
	db_printf("\n");

	if (s->tf_snip != s->tf_sxip + 4) {
		db_printf("%cnip: 0x%08lx ",
		    CPU_IS88110 ? 'e' : 's', s->tf_snip);
		db_find_xtrn_sym_and_offset((db_addr_t)IPMASK(s->tf_snip),
		    &name, &offset);
		if (name != NULL && (u_int)offset <= db_maxoff)
			db_printf("%s+0x%08x", name, (u_int)offset);
		db_printf("\n");
	}

#ifdef M88100
	if (CPU_IS88100) {
		if (s->tf_sfip != s->tf_snip + 4) {
			db_printf("sfip: 0x%08lx ", s->tf_sfip);
			db_find_xtrn_sym_and_offset((db_addr_t)IPMASK(s->tf_sfip),
			    &name, &offset);
			if (name != NULL && (u_int)offset <= db_maxoff)
				db_printf("%s+0x%08x", name, (u_int)offset);
			db_printf("\n");
		}
	}
#endif
#ifdef M88110
	if (CPU_IS88110) {
		db_printf("fpsr: 0x%08lx fpcr: 0x%08lx fpecr: 0x%08lx\n",
			  s->tf_fpsr, s->tf_fpcr, s->tf_fpecr);
		db_printf("dsap 0x%08lx duap 0x%08lx dsr 0x%08lx dlar 0x%08lx dpar 0x%08lx\n",
			  s->tf_dsap, s->tf_duap, s->tf_dsr, s->tf_dlar, s->tf_dpar);
		db_printf("isap 0x%08lx iuap 0x%08lx isr 0x%08lx ilar 0x%08lx ipar 0x%08lx\n",
			  s->tf_isap, s->tf_iuap, s->tf_isr, s->tf_ilar, s->tf_ipar);
	}
#endif

	db_printf("epsr: 0x%08lx                current process: %p\n",
		  s->tf_epsr, curproc);
	db_printf("vector: 0x%02lx                    interrupt mask: 0x%08lx\n",
		  s->tf_vector, s->tf_mask);

	/*
	 * If the vector indicates trap, instead of an exception or
	 * interrupt, skip the check of dmt and fp regs.
	 *
	 * Interrupt and exceptions are vectored at 0-10 and 114-127.
	 */
	if (!(s->tf_vector <= 10 ||
	    (114 <= s->tf_vector && s->tf_vector <= 127))) {
		db_printf("\n");
		return;
	}

#ifdef M88100
	if (CPU_IS88100) {
		if (s->tf_vector == /*data*/3 || s->tf_dmt0 & DMT_VALID) {
			db_printf("dmt,d,a0: 0x%08lx  0x%08lx  0x%08lx ",
			    s->tf_dmt0, s->tf_dmd0, s->tf_dma0);
			db_find_xtrn_sym_and_offset((db_addr_t)s->tf_dma0,
			    &name, &offset);
			if (name != NULL && (u_int)offset <= db_maxoff)
				db_printf("%s+0x%08x", name, (u_int)offset);
			db_printf("\n          ");

			suppress1 = m88k_dmx_print(s->tf_dmt0, s->tf_dmd0,
			    s->tf_dma0, 0);
			db_printf("\n");

			if ((s->tf_dmt1 & DMT_VALID) && (!suppress1)) {
				db_printf("dmt,d,a1: 0x%08lx  0x%08lx  0x%08lx ",
				    s->tf_dmt1, s->tf_dmd1, s->tf_dma1);
				db_find_xtrn_sym_and_offset((db_addr_t)s->tf_dma1,
				    &name, &offset);
				if (name != NULL && (u_int)offset <= db_maxoff)
					db_printf("%s+0x%08x", name,
					    (u_int)offset);
				db_printf("\n          ");
				suppress2 = m88k_dmx_print(s->tf_dmt1,
				    s->tf_dmd1, s->tf_dma1, 1);
				db_printf("\n");

				if ((s->tf_dmt2 & DMT_VALID) && (!suppress2)) {
					db_printf("dmt,d,a2: 0x%08lx  0x%08lx  0x%08lx ",
						  s->tf_dmt2, s->tf_dmd2, s->tf_dma2);
					db_find_xtrn_sym_and_offset((db_addr_t)s->tf_dma2,
					    &name, &offset);
					if (name != 0 &&
					    (u_int)offset <= db_maxoff)
						db_printf("%s+0x%08x", name,
						    (u_int)offset);
					db_printf("\n          ");
					m88k_dmx_print(s->tf_dmt2, s->tf_dmd2,
					    s->tf_dma2, 2);
					db_printf("\n");
				}
			}

			db_printf("fault code %ld\n",
			    CMMU_PFSR_FAULT(s->tf_dpfsr));
		}
	}
#endif	/* M88100 */

	if (s->tf_fpecr & 255) { /* floating point error occurred */
		db_printf("fpecr: 0x%08lx fpsr: 0x%08lx fpcr: 0x%08lx\n",
		    s->tf_fpecr, s->tf_fpsr, s->tf_fpcr);
#ifdef M88100
		if (CPU_IS88100) {
			db_printf("fcr1-4: 0x%08lx  0x%08lx  0x%08lx  0x%08lx\n",
			    s->tf_fphs1, s->tf_fpls1, s->tf_fphs2, s->tf_fpls2);
			db_printf("fcr5-8: 0x%08lx  0x%08lx  0x%08lx  0x%08lx\n",
			    s->tf_fppt, s->tf_fprh, s->tf_fprl, s->tf_fpit);
		}
#endif
	}
	db_printf("\n");
}

void
m88k_db_registers(addr, have_addr, count, modif)
	db_expr_t addr;
	int have_addr;
	db_expr_t count;
	char *modif;
{
	m88k_db_print_frame((db_expr_t)DDB_REGS, TRUE, 0, modif);
}

/*
 * m88k_db_trap - field a TRACE or BPT trap
 * Note that only the tf_regs part of the frame is valid - some ddb routines
 * invoke this function with a promoted struct reg!
 */
void
m88k_db_trap(type, frame)
	int type;
	struct trapframe *frame;
{
#ifdef MULTIPROCESSOR
	struct cpu_info *ci = curcpu();
#endif

	switch(type) {
	case T_KDB_BREAK:
	case T_KDB_TRACE:
	case T_KDB_ENTRY:
		break;
	case -1:
		break;
	default:
		kdbprinttrap(type);
		if (db_recover != 0) {
			db_error("Caught exception in ddb.\n");
			/*NOTREACHED*/
		}
	}

#ifdef MULTIPROCESSOR
	ci->ci_ddb_state = CI_DDB_ENTERDDB;
	__mp_lock(&ddb_mp_lock);
	ci->ci_ddb_state = CI_DDB_INDDB;
	ddb_mp_nextcpu = (cpuid_t)-1;
	m88k_broadcast_ipi(CI_IPI_DDB);		/* pause other processors */
#endif

	ddb_regs = frame->tf_regs;

	cnpollc(TRUE);
	db_trap(type, 0);
	cnpollc(FALSE);

	frame->tf_regs = ddb_regs;

#ifdef MULTIPROCESSOR
	ci->ci_ddb_state = CI_DDB_RUNNING;
	__mp_release_all(&ddb_mp_lock);
#endif
}

extern const char *trap_type[];
extern const int trap_types;

/*
 * Print trap reason.
 */
void
kdbprinttrap(int type)
{
	printf("kernel: ");
	if (type >= trap_types || type < 0)
		printf("type %d", type);
	else
		printf("%s", trap_type[type]);
	printf(" trap\n");
}

void
Debugger()
{
	asm (ENTRY_ASM); /* entry trap */
	/* ends up at ddb_entry_trap below */
	return;
}

/*
 * When the below routine is entered interrupts should be on
 * but spl should be high
 *
 * The following routine is for breakpoint and watchpoint entry.
 */

/* breakpoint/watchpoint entry */
int
ddb_break_trap(type, eframe)
	int type;
	db_regs_t *eframe;
{
	m88k_db_trap(type, (struct trapframe *)eframe);

	if (type == T_KDB_BREAK) {
		/*
		 * back up an instruction and retry the instruction
		 * at the breakpoint address.  mc88110's exip reg
		 * already has the address of the exception instruction.
		 */
		if (CPU_IS88100)
			m88100_rewind_insn(eframe);
	}

	return 0;
}

/* enter at splhigh */
int
ddb_entry_trap(level, eframe)
	int level;
	db_regs_t *eframe;
{
	m88k_db_trap(T_KDB_ENTRY, (struct trapframe *)eframe);

	return 0;
}

/*
 * Read bytes from kernel address space for debugger.
 */
void
db_read_bytes(db_addr_t addr, size_t size, char *data)
{
	char *src;

	src = (char *)addr;

	while (size-- > 0) {
		*data++ = *src++;
	}
}

/*
 * Write bytes to kernel address space for debugger.
 */
void
db_write_bytes(db_addr_t addr, size_t size, char *data)
{
	extern pt_entry_t *pmap_pte(pmap_t, vaddr_t);
	char *dst = (char *)addr;
	vaddr_t va;
	paddr_t pa;
	pt_entry_t *pte, opte, npte;
	size_t len, olen;
	int cpu = cpu_number();

	while (size != 0) {
		va = trunc_page((vaddr_t)dst);
#ifdef M88100
		if (CPU_IS88100 && va >= BATC8_VA)
			pte = NULL;
		else
#endif
			pte = pmap_pte(pmap_kernel(), va);
		if (pte != NULL) {
			opte = *pte;
			pa = (opte & PG_FRAME) | ((vaddr_t)dst & PAGE_MASK);
		}
		len = PAGE_SIZE - ((vaddr_t)dst & PAGE_MASK);
		if (len > size)
			len = size;
		size -= olen = len;

		if (pte != NULL && (opte & PG_RO)) {
			npte = opte & ~PG_RO;
			*pte = npte;
			cmmu_tlbis(cpu, va, npte);
		}
		while (len-- != 0)
			*dst++ = *data++;
		if (pte != NULL && (opte & PG_RO)) {
			*pte = opte;
			cmmu_tlbis(cpu, va, opte);
		}
		if (pte != NULL && (opte & (CACHE_INH | CACHE_WT)) == 0) {
			cmmu_dcache_wb(cpu, pa, olen);
			cmmu_icache_inv(cpu, pa, olen);
		}
	}
}

/* display where all the cpus are stopped at */
void
m88k_db_where(addr, have_addr, count, modif)
	db_expr_t addr;
	int have_addr;
	db_expr_t count;
	char *modif;
{
	char *name;
	db_expr_t offset;
	db_addr_t l;

	l = PC_REGS(DDB_REGS); /* clear low bits */

	db_find_xtrn_sym_and_offset(l, &name, &offset);
	if (name && (u_int)offset <= db_maxoff)
		db_printf("stopped at 0x%lx  (%s+0x%lx)\n", l, name, offset);
	else
		db_printf("stopped at 0x%lx\n", l);
}

/*
 * Walk back a stack, looking for exception frames.
 * These frames are recognized by the routine frame_is_sane. Frames
 * only start with zero, so we only call frame_is_sane if the
 * current address contains zero.
 *
 * If addr is given, it is assumed to an address on the stack to be
 * searched. Otherwise, r31 of the current cpu is used.
 */
void
m88k_db_frame_search(addr, have_addr, count, modif)
	db_expr_t addr;
	int have_addr;
	db_expr_t count;
	char *modif;
{
	if (have_addr)
		addr &= ~3; /* round to word */
	else
		addr = (DDB_REGS->r[31]);

	/* walk back up stack until 8k boundry, looking for 0 */
	while (addr & ((8 * 1024) - 1)) {
		if (frame_is_sane((db_regs_t *)addr, 1) != 0)
			db_printf("frame found at 0x%lx\n", addr);
		addr += 4;
	}

	db_printf("(Walked back until 0x%lx)\n",addr);
}

#ifdef MULTIPROCESSOR

void
m88k_db_cpu_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
{
	cpuid_t cpu;
	struct cpu_info *ci;
	char state[15];

	/* switch to another processor if requested */
	if (have_addr) {
		cpu = (cpuid_t)addr;
		if (cpu >= 0 && cpu < MAX_CPUS &&
		    ISSET(m88k_cpus[cpu].ci_flags, CIF_ALIVE)) {
			ddb_mp_nextcpu = cpu;
			db_cmd_loop_done = 1;
		} else {
			db_printf("cpu%ld is not active\n", cpu);
		}
		return;
	}

	db_printf(" cpu  flags state          curproc  curpcb   depth    ipi\n");
	CPU_INFO_FOREACH(cpu, ci) {
		switch (ci->ci_ddb_state) {
		case CI_DDB_RUNNING:
			strlcpy(state, "running", sizeof state);
			break;
		case CI_DDB_ENTERDDB:
			strlcpy(state, "entering ddb", sizeof state);
			break;
		case CI_DDB_INDDB:
			strlcpy(state, "in ddb", sizeof state);
			break;
		case CI_DDB_PAUSE:
			strlcpy(state, "paused", sizeof state);
			break;
		default:
			snprintf(state, sizeof state, "unknown (%d)",
			    ci->ci_ddb_state);
			break;
		}
		db_printf("%ccpu%d   %02x  %-14s %08lx %08lx %3u %08x\n",
		    (cpu == cpu_number()) ? '*' : ' ', CPU_INFO_UNIT(ci),
		    ci->ci_flags, state, (register_t)ci->ci_curproc,
		    (register_t)ci->ci_curpcb, ci->ci_intrdepth, ci->ci_ipi);
	}
}

#endif	/* MULTIPROCESSOR */

/************************/
/* COMMAND TABLE / INIT */
/************************/

struct db_command db_machine_cmds[] = {
#ifdef MULTIPROCESSOR
	{ "ddbcpu",	m88k_db_cpu_cmd,	0,	NULL },
#endif
	{ "frame",	m88k_db_print_frame,	0,	NULL },
	{ "regs",	m88k_db_registers,	0,	NULL },
	{ "searchframe",m88k_db_frame_search,	0,	NULL },
	{ "where",	m88k_db_where,		0,	NULL },
#if defined(EXTRA_MACHDEP_COMMANDS)
	EXTRA_MACHDEP_COMMANDS
#endif
	{ NULL,		NULL,			0,	NULL }
};

void
db_machine_init()
{
	db_machine_commands_install(db_machine_cmds);
#ifdef MULTIPROCESSOR
	__mp_lock_init(&ddb_mp_lock);
#endif
}


code:
/*	$OpenBSD: db_sstep.c,v 1.7 2010/11/27 19:57:23 miod Exp $	*/
/*
 * Mach Operating System
 * Copyright (c) 1993-1991 Carnegie Mellon University
 * Copyright (c) 1991 OMRON Corporation
 * All Rights Reserved.
 *
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND
 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie the
 * rights to redistribute these changes.
 */

#include <sys/param.h>
#include <sys/systm.h>

#include <machine/db_machdep.h>

#include <ddb/db_access.h>	/* db_get_value() */
#include <ddb/db_break.h>	/* db_breakpoint_t */
#include <ddb/db_run.h>

/*
 * Support routines for software single step.
 *
 * Author: Daniel Stodolsky (danner@cs.cmu.edu)
 *
 */

/*
 * We can not use the MI ddb SOFTWARE_SSTEP facility, since the 88110 will use
 * hardware single stepping.
 * Moreover, our software single stepping implementation is tailor-made for the
 * 88100 and faster than the MI code.
 */

#ifdef M88100

boolean_t	inst_branch_or_call(u_int);
db_addr_t	branch_taken(u_int, db_addr_t, db_regs_t *);

db_breakpoint_t db_not_taken_bkpt = 0;
db_breakpoint_t db_taken_bkpt = 0;

/*
 * Returns TRUE is the instruction a branch, jump or call instruction
 * (br, bb0, bb1, bcnd, jmp, bsr, jsr)
 */
boolean_t
inst_branch_or_call(u_int ins)
{
	/* check high five bits */
	switch (ins >> (32 - 5)) {
	case 0x18: /* br */
	case 0x19: /* bsr */
	case 0x1a: /* bb0 */
	case 0x1b: /* bb1 */
	case 0x1d: /* bcnd */
		return (TRUE);
	case 0x1e: /* could be jmp or jsr */
		if ((ins & 0xfffff3e0) == 0xf400c000)
			return (TRUE);
	}

	return (FALSE);
}

/*
 * branch_taken(instruction, program counter, regs)
 *
 * instruction will be a control flow instruction location at address pc.
 * Branch taken is supposed to return the address to which the instruction
 * would jump if the branch is taken.
 */
db_addr_t
branch_taken(u_int inst, db_addr_t pc, db_regs_t *regs)
{
	u_int regno;

	/*
	 * Quick check of the instruction. Note that we know we are only
	 * invoked if inst_branch_or_call() returns TRUE, so we do not
	 * need to repeat the jmp and jsr stricter checks here.
	 */
	switch (inst >> (32 - 5)) {
	case 0x18: /* br */
	case 0x19: /* bsr */
		/* signed 26 bit pc relative displacement, shift left two bits */
		inst = (inst & 0x03ffffff) << 2;
		/* check if sign extension is needed */
		if (inst & 0x08000000)
			inst |= 0xf0000000;
		return (pc + inst);

	case 0x1a: /* bb0 */
	case 0x1b: /* bb1 */
	case 0x1d: /* bcnd */
		/* signed 16 bit pc relative displacement, shift left two bits */
		inst = (inst & 0x0000ffff) << 2;
		/* check if sign extension is needed */
		if (inst & 0x00020000)
			inst |= 0xfffc0000;
		return (pc + inst);

	default: /* jmp or jsr */
		regno = inst & 0x1f;
		return (regno == 0 ? 0 : regs->r[regno]);
	}
}

#endif	/* M88100 */

void
db_set_single_step(db_regs_t *regs)
{
#ifdef M88110
	if (CPU_IS88110) {
		/*
		 * On the 88110, we can use the hardware tracing facility...
		 */
		regs->epsr |= PSR_TRACE | PSR_SER;
	}
#endif
#ifdef M88100
	if (CPU_IS88100) {
		/*
		 * ... while the 88100 will use two breakpoints.
		 */
		db_addr_t pc = PC_REGS(regs);
		db_addr_t brpc;
		u_int inst;

		/*
		 * User was stopped at pc, e.g. the instruction
		 * at pc was not executed.
		 */
		db_read_bytes(pc, sizeof(inst), (caddr_t)&inst);

		/*
		 * Find if this instruction may cause a branch, and set up a
		 * breakpoint at the branch location.
		 */
		if (inst_branch_or_call(inst)) {
			brpc = branch_taken(inst, pc, regs);

			/* self-branches are hopeless */
			if (brpc != pc && brpc != 0)
				db_taken_bkpt = db_set_temp_breakpoint(brpc);
		}

		db_not_taken_bkpt = db_set_temp_breakpoint(pc + 4);
	}
#endif
}

void
db_clear_single_step(regs)
	db_regs_t *regs;
{
#ifdef M88110
	if (CPU_IS88110) {
		regs->epsr &= ~(PSR_TRACE | PSR_SER);
	}
#endif
#ifdef M88100
	if (CPU_IS88100) {
		if (db_taken_bkpt != 0) {
			db_delete_temp_breakpoint(db_taken_bkpt);
			db_taken_bkpt = 0;
		}
		if (db_not_taken_bkpt != 0) {
			db_delete_temp_breakpoint(db_not_taken_bkpt);
			db_not_taken_bkpt = 0;
		}
	}
#endif
}

license: unknown


code:
/*	$OpenBSD: db_trace.c,v 1.13 2014/07/13 12:11:01 jasper Exp $	*/
/*
 * Mach Operating System
 * Copyright (c) 1993-1991 Carnegie Mellon University
 * Copyright (c) 1991 OMRON Corporation
 * All Rights Reserved.
 *
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND
 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie the
 * rights to redistribute these changes.
 */

#include <sys/param.h>
#include <sys/systm.h>

#include <machine/cpu.h>
#include <machine/db_machdep.h>

#include <ddb/db_variables.h>	/* db_variable, DB_VAR_GET, etc.  */
#include <ddb/db_output.h>	/* db_printf                      */
#include <ddb/db_sym.h>		/* DB_STGY_PROC, etc.             */
#include <ddb/db_command.h>	/* db_recover                     */
#include <ddb/db_access.h>
#include <ddb/db_interface.h>

#ifdef DEBUG
#define	DPRINTF(stmt) printf stmt
#else
#define	DPRINTF(stmt) do { } while (0)
#endif

static inline
u_int br_dest(vaddr_t addr, u_int inst)
{
	inst = (inst & 0x03ffffff) << 2;
	/* check if sign extension is needed */
	if (inst & 0x08000000)
		inst |= 0xf0000000;
	return (addr + inst);
}

int frame_is_sane(db_regs_t *regs, int);
const char *m88k_exception_name(u_int vector);
u_int db_trace_get_val(vaddr_t addr, u_int *ptr);

/*
 * Some macros to tell if the given text is the instruction.
 */
#define JMPN_R1(I)		((I) == 0xf400c401)	/* jmp.n r1 */
#define JMP_R1(I)		((I) == 0xf400c001)	/* jmp r1 */

/* gets the IMM16 value from an instruction */
#define IMM16VAL(I)		((I) & 0x0000ffff)

/* subu r31, r31, IMM */
#define SUBU_R31_R31_IMM(I)	(((I) & 0xffff0000) == 0x67ff0000U)

/* st r30, r31, IMM */
#define ST_R30_R31_IMM(I)	(((I) & 0xffff0000) == 0x27df0000U)

extern label_t *db_recover;

/*
 * m88k trace/register state interface for ddb.
 */

/* lifted from mips */
static int
db_setf_regs(struct db_variable      *vp,
	db_expr_t		*valuep,
	int			op)		/* read/write */
{
	int   *regp = (int *) ((char *) DDB_REGS + (int) (vp->valuep));

	if (op == DB_VAR_GET)
		*valuep = *regp;
	else if (op == DB_VAR_SET)
		*regp = *valuep;

	return (0); /* silence warning */
}

#define N(s, x)  {s, (long *)&(((db_regs_t *) 0)->x), db_setf_regs}

struct db_variable db_regs[] = {
	N("r1", r[1]),     N("r2", r[2]),    N("r3", r[3]),    N("r4", r[4]),
	N("r5", r[5]),     N("r6", r[6]),    N("r7", r[7]),    N("r8", r[8]),
	N("r9", r[9]),     N("r10", r[10]),  N("r11", r[11]),  N("r12", r[12]),
	N("r13", r[13]),   N("r14", r[14]),  N("r15", r[15]),  N("r16", r[16]),
	N("r17", r[17]),   N("r18", r[18]),  N("r19", r[19]),  N("r20", r[20]),
	N("r21", r[21]),   N("r22", r[22]),  N("r23", r[23]),  N("r24", r[24]),
	N("r25", r[25]),   N("r26", r[26]),  N("r27", r[27]),  N("r28", r[28]),
	N("r29", r[29]),   N("r30", r[30]),  N("r31", r[31]),  N("epsr", epsr),
	N("sxip", sxip),   N("snip", snip),  N("sfip", sfip),  N("ssbr", ssbr),
	N("dmt0", dmt0),   N("dmd0", dmd0),  N("dma0", dma0),  N("dmt1", dmt1),
	N("dmd1", dmd1),   N("dma1", dma1),  N("dmt2", dmt2),  N("dmd2", dmd2),
	N("dma2", dma2),   N("fpecr", fpecr),N("fphs1", fphs1),N("fpls1", fpls1),
	N("fphs2", fphs2), N("fpls2", fpls2),N("fppt", fppt),  N("fprh", fprh),
	N("fprl", fprl),   N("fpit", fpit),  N("fpsr", fpsr),  N("fpcr", fpcr),
};
#undef N

struct db_variable *db_eregs = db_regs + nitems(db_regs);

#define TRASHES    0x001	/* clobbers instruction field D */
#define STORE      0x002	/* does a store to S1+IMM16 */
#define LOAD       0x004	/* does a load from S1+IMM16 */
#define DOUBLE     0x008	/* double-register */
#define FLOW_CTRL  0x010	/* flow-control instruction */
#define DELAYED    0x020	/* delayed flow control */
#define JSR	   0x040	/* flow-control is a jsr[.n] */
#define BSR	   0x080	/* flow-control is a bsr[.n] */

/*
 * Given a word of instruction text, return some flags about that
 * instruction (flags defined above).
 */
static u_int
m88k_instruction_info(u_int32_t instruction)
{
	static const struct {
		u_int32_t mask, value;
		u_int flags;
	} *ptr, control[] = {
		/* runs in the same order as 2nd Ed 88100 manual Table 3-14 */
		{ 0xf0000000U, 0x00000000U, /* xmem */     TRASHES | STORE | LOAD},
		{ 0xec000000U, 0x00000000U, /* ld.d */     TRASHES | LOAD | DOUBLE},
		{ 0xe0000000U, 0x00000000U, /* load */     TRASHES | LOAD},
		{ 0xfc000000U, 0x20000000U, /* st.d */     STORE | DOUBLE},
		{ 0xf0000000U, 0x20000000U, /* store */    STORE},
		{ 0xc0000000U, 0x40000000U, /* arith */    TRASHES},
		{ 0xfc004000U, 0x80004000U, /* ld cr */    TRASHES},
		{ 0xfc004000U, 0x80000000U, /* st cr */    0},
		{ 0xfc008060U, 0x84000000U, /* f */        TRASHES},
		{ 0xfc008060U, 0x84000020U, /* f.d */      TRASHES | DOUBLE},
		{ 0xfc000000U, 0xcc000000U, /* bsr.n */    FLOW_CTRL | DELAYED | BSR},
		{ 0xfc000000U, 0xc8000000U, /* bsr */      FLOW_CTRL | BSR},
		{ 0xe4000000U, 0xc4000000U, /* br/bb.n */  FLOW_CTRL | DELAYED},
		{ 0xe4000000U, 0xc0000000U, /* br/bb */    FLOW_CTRL},
		{ 0xfc000000U, 0xec000000U, /* bcnd.n */   FLOW_CTRL | DELAYED},
		{ 0xfc000000U, 0xe8000000U, /* bcnd */     FLOW_CTRL},
		{ 0xfc00c000U, 0xf0008000U, /* bits */     TRASHES},
		{ 0xfc00c000U, 0xf000c000U, /* trap */     0},
		{ 0xfc00f0e0U, 0xf4002000U, /* st */       0},
		{ 0xfc00cce0U, 0xf4000000U, /* ld.d */     TRASHES | DOUBLE},
		{ 0xfc00c0e0U, 0xf4000000U, /* ld */       TRASHES},
		{ 0xfc00c0e0U, 0xf4004000U, /* arith */    TRASHES},
		{ 0xfc00c3e0U, 0xf4008000U, /* bits */     TRASHES},
		{ 0xfc00ffe0U, 0xf400cc00U, /* jsr.n */    FLOW_CTRL | DELAYED | JSR},
		{ 0xfc00ffe0U, 0xf400c800U, /* jsr */      FLOW_CTRL | JSR},
		{ 0xfc00ffe0U, 0xf400c400U, /* jmp.n */    FLOW_CTRL | DELAYED},
		{ 0xfc00ffe0U, 0xf400c000U, /* jmp */      FLOW_CTRL},
		{ 0xfc00fbe0U, 0xf400e800U, /* ff */       TRASHES},
		{ 0xfc00ffe0U, 0xf400f800U, /* tbnd */     0},
		{ 0xfc00ffe0U, 0xf400fc00U, /* rte */      FLOW_CTRL},
		{ 0xfc000000U, 0xf8000000U, /* tbnd */     0},
	};

	for (ptr = &control[0]; ptr < &control[nitems(control)]; ptr++)
		if ((instruction & ptr->mask) == ptr->value)
			return ptr->flags;
	return 0;
}

static int
hex_value_needs_0x(u_int value)
{
	int c;
	int have_a_hex_digit = 0;

	if (value <= 9)
		return (0);

	while (value != 0) {
		c = value & 0xf;
		value >>= 4;
		if (c > 9)
			have_a_hex_digit = 1;
	}
	if (have_a_hex_digit == 0)	/* has no letter, thus needs 0x */
		return (1);
	if (c > 9)		/* starts with a letter, thus needs 0x */
		return (1);
	return (0);
}

/*
 * returns
 *   1 if regs seems to be a reasonable kernel exception frame.
 *   2 if regs seems to be a reasonable user exception frame
 * 	(in the current task).
 *   0 if this looks like neither.
 */
int
frame_is_sane(db_regs_t *regs, int quiet)
{
	/* no good if we can't read the whole frame */
	if (badaddr((vaddr_t)regs, 4) || badaddr((vaddr_t)&regs->fpit, 4)) {
		if (quiet == 0)
			db_printf("[WARNING: frame at %p : unreadable]\n", regs);
		return 0;
	}

	/* r0 must be 0 (obviously) */
	if (regs->r[0] != 0) {
		if (quiet == 0)
			db_printf("[WARNING: frame at %p : r[0] != 0]\n", regs);
		return 0;
	}

	/* stack sanity ... r31 must be nonzero, and must be word aligned */
	if (regs->r[31] == 0 || (regs->r[31] & 3) != 0) {
		if (quiet == 0)
			db_printf("[WARNING: frame at %p : r[31] == 0 or not word aligned]\n", regs);
		return 0;
	}

#ifdef M88100
	if (CPU_IS88100) {
		/* sxip is reasonable */
#if 0
		if ((regs->sxip & XIP_E) != 0)
			goto out;
#endif
		/* snip is reasonable */
		if ((regs->snip & ~NIP_ADDR) != NIP_V)
			goto out;
		/* sfip is reasonable */
		if ((regs->sfip & ~FIP_ADDR) != FIP_V)
			goto out;
	}
#endif

	/* epsr sanity */
	if (regs->epsr & PSR_BO)
		goto out;

	return ((regs->epsr & PSR_MODE) ? 1 : 2);

out:
	if (quiet == 0)
		db_printf("[WARNING: not an exception frame?]\n");
	return (0);
}

const char *
m88k_exception_name(u_int vector)
{
	switch (vector) {
	default:
	case   0: return "Reset";
	case   1: return "Interrupt";
	case   2: return "Instruction Access Exception";
	case   3: return "Data Access Exception";
	case   4: return "Misaligned Access Exception";
	case   5: return "Unimplemented Opcode Exception";
	case   6: return "Privilege Violation";
	case   7: return "Bounds Check";
	case   8: return "Integer Divide Exception";
	case   9: return "Integer Overflow Exception";
	case  10: return "Error Exception";
	case  11: return "Non Maskable Interrupt";
	case 114: return "FPU precise";
	case 115: return "FPU imprecise";
	case DDB_ENTRY_BKPT_NO:
		return "ddb break";
	case DDB_ENTRY_TRACE_NO:
		return "ddb trace";
	case DDB_ENTRY_TRAP_NO:
		return "ddb trap";
	case 451: return "Syscall";
	}
}

/*
 * Read a word at address addr.
 * Return 1 if was able to read, 0 otherwise.
 */
u_int
db_trace_get_val(vaddr_t addr, u_int *ptr)
{
	label_t db_jmpbuf;
	label_t *prev = db_recover;

	if (setjmp((db_recover = &db_jmpbuf)) != 0) {
		db_recover = prev;
		return 0;
	} else {
		db_read_bytes(addr, 4, (char *)ptr);
		db_recover = prev;
		return 1;
	}
}

#define	FIRST_CALLPRESERVED_REG	14
#define	LAST_CALLPRESERVED_REG	29
#define	FIRST_ARG_REG		2
#define	LAST_ARG_REG		9
#define	RETURN_VAL_REG		1

static u_int global_saved_list = 0x0; /* one bit per register */
static u_int local_saved_list  = 0x0; /* one bit per register */
static u_int trashed_list      = 0x0; /* one bit per register */
static u_int saved_reg[32];		 /* one value per register */

#define	reg_bit(reg)	1 << (reg)

static void
save_reg(int reg, u_int value)
{
	reg &= 0x1f;
	if (trashed_list & reg_bit(reg))
		return;	/* don't save trashed registers */

	saved_reg[reg] = value;
	global_saved_list |= reg_bit(reg);
	local_saved_list |= reg_bit(reg);
}

#define	mark_reg_trashed(reg)	trashed_list |= reg_bit((reg) & 0x1f)

#define	have_global_reg(reg)	(global_saved_list & reg_bit(reg))
#define	have_local_reg(reg)	(local_saved_list & reg_bit(reg))

#define	clear_local_saved_regs()	local_saved_list = trashed_list = 0
#define	clear_global_saved_regs()	local_saved_list = global_saved_list = 0

#define	saved_reg_value(reg)	saved_reg[(reg)]

/*
 * Show any arguments that we might have been able to determine.
 */
static void
print_args(void)
{
	int reg, last_arg;

	/* find the highest argument register saved */
	for (last_arg = LAST_ARG_REG; last_arg >= FIRST_ARG_REG; last_arg--)
		if (have_local_reg(last_arg))
			break;
	if (last_arg < FIRST_ARG_REG)
		return;	/* none were saved */

	db_printf("(");

	/* print each one, up to the highest */
	for (reg = FIRST_ARG_REG; /*nothing */; reg++) {
		if (!have_local_reg(reg))
			db_printf("?");
		else {
			u_int value = saved_reg_value(reg);
			db_printf("%s%x", hex_value_needs_0x(value) ?
				  "0x" : "", value);
		}
		if (reg == last_arg)
			break;
		else
			db_printf(", ");
	}
	db_printf(")");
}

#define JUMP_SOURCE_IS_BAD		0
#define JUMP_SOURCE_IS_OK		1
#define JUMP_SOURCE_IS_UNLIKELY		2

/*
 * Give an address to where we return, and an address to where we'd jumped,
 * Decided if it all makes sense.
 *
 * Gcc sometimes optimized something like
 *	if (condition)
 *		func1();
 *	else
 *		OtherStuff...
 * to
 *	bcnd	!condition, mark
 *	bsr.n	func1
 *	 or	r1, r0, mark2
 *    mark:
 *	OtherStuff...
 *    mark2:
 *
 * So RETURN_TO will be mark2, even though we really did branch via
 * 'bsr.n func1', so this makes it difficult to be certain about being
 * wrong.
 */
static int
is_jump_source_ok(vaddr_t return_to, vaddr_t jump_to)
{
	u_int flags;
	uint32_t instruction;

	/*
	 * Delayed branches are the most common... look two instructions before
	 * where we were going to return to to see if it's a delayed branch.
	 */
	if (!db_trace_get_val(return_to - 8, &instruction))
		return JUMP_SOURCE_IS_BAD;

	flags = m88k_instruction_info(instruction);
	if ((flags & (FLOW_CTRL | DELAYED)) == (FLOW_CTRL | DELAYED) &&
	    (flags & (JSR | BSR)) != 0) {
		if ((flags & JSR) != 0)
			return JUMP_SOURCE_IS_OK; /* have to assume it's correct */
		/* calculate the offset */
		if (br_dest(return_to - 8, instruction) == jump_to)
			return JUMP_SOURCE_IS_OK; /* exactamundo! */
		else
			return JUMP_SOURCE_IS_UNLIKELY;	/* seems wrong */
	}

	/*
	 * Try again, looking for a non-delayed jump one instruction back.
	 */
	if (!db_trace_get_val(return_to - 4, &instruction))
		return JUMP_SOURCE_IS_BAD;

	flags = m88k_instruction_info(instruction);
	if ((flags & (FLOW_CTRL | DELAYED)) == FLOW_CTRL &&
	    (flags & (JSR | BSR)) != 0) {
		if ((flags & JSR) != 0)
			return JUMP_SOURCE_IS_OK; /* have to assume it's correct */
		/* calculate the offset */
		if (br_dest(return_to - 4, instruction) == jump_to)
			return JUMP_SOURCE_IS_OK; /* exactamundo! */
		else
			return JUMP_SOURCE_IS_UNLIKELY;	/* seems wrong */
	}

	return JUMP_SOURCE_IS_UNLIKELY;
}

static int next_address_likely_wrong = 0;

/* How much slop we expect in the stack trace */
#define FRAME_PLAY 8

/*
 *  Stack decode -
 *	vaddr_t addr;    program counter
 *	vaddr_t *stack; IN/OUT stack pointer
 *
 * 	given an address within a function and a stack pointer,
 *	try to find the function from which this one was called
 *	and the stack pointer for that function.
 *
 *	The return value is zero if we get confused or
 *	we determine that the return address has not yet
 *	been saved (early in the function prologue). Otherwise
 *	the return value is the address from which this function
 *	was called.
 *
 *	Note that even is zero is returned (the second case) the
 *	stack pointer can be adjusted.
 */
static vaddr_t
stack_decode(db_addr_t addr, vaddr_t *stack, int (*pr)(const char *, ...))
{
	db_sym_t proc;
	db_expr_t offset_from_proc;
	uint instructions_to_search;
	db_addr_t check_addr;
	db_addr_t function_addr;    /* start of function */
	uint32_t r31;
	uint32_t inst;
	vaddr_t ret_addr;	    /* address to which we return */
	u_int tried_to_save_r1 = 0;
	vaddr_t str30_addr = 0;
	vaddr_t last_subu_addr = 0;

	/* get what we hope will be the db_sym_t for the function name */
	proc = db_search_symbol(addr, DB_STGY_PROC, &offset_from_proc);
	if (offset_from_proc == addr) /* i.e. no symbol found */
		proc = DB_SYM_NULL;

	/*
	 * Try and find the start of this function, and its stack usage.
	 * If we do not have symbols available, we will need to
	 * look back in memory for a prologue pattern.
	 */
	if (proc != DB_SYM_NULL) {
		char *names = NULL;
		db_symbol_values(proc, &names, &function_addr);
		if (names == NULL)
			return 0;
	} else {
		/*
		 * Unable to find symbol. Search back looking for a function
		 * prolog.
		 *
		 * This is a difficult game because of the compiler
		 * optimizations. However, we can rely upon the first two
		 * instructions of the function being:
		 *	subu	r31, r31, imm16
		 *	st	r30, r31, imm16
		 * unless the function did not need a frame, in which case
		 * the store of r30 is missing...
		 */
		instructions_to_search = 400;
		for (check_addr = addr; instructions_to_search-- != 0;
		    check_addr -= 4) {
			if (!db_trace_get_val(check_addr, &inst))
				break;

			if (ST_R30_R31_IMM(inst)) {
				DPRINTF(("{st r30 found at %p}\n",
				    check_addr));
				str30_addr = (vaddr_t)check_addr;
			} else if (SUBU_R31_R31_IMM(inst)) {
				DPRINTF(("{subu r31 found at %p}\n",
				    check_addr));
				if (str30_addr == (vaddr_t)check_addr + 4)
					break;	/* success */
				else
					last_subu_addr = (vaddr_t)check_addr;
			}

			/*
			 * if we come across a [jmp r1] or [jmp.n r1] assume
			 * we have hit the previous functions epilogue and
			 * stop our search.
			 * Since we know we would have hit the "subu r31, r31"
			 * if it was right in front of us, we know this doesn't
			 * have one so we just return failure....
			 */
			if (JMP_R1(inst) || JMPN_R1(inst)) {
				if (last_subu_addr != 0) {
					check_addr = last_subu_addr;
					break;
				} else
					return 0;
			}
		}
		if (instructions_to_search == 0)
			return 0; /* bummer, couldn't find it */
		function_addr = check_addr;
	}

	DPRINTF(("{start of function at %p}\n", function_addr));
	/*
	 * We now know the start of the function (function_addr).
	 * If we're stopped right there, or if it's not a
	 *		subu r31, r31, imm16
	 * then we're done.
	 */
	if (addr == function_addr)
		return 0;
	if (!db_trace_get_val(function_addr, &inst))
		return 0;
	if (!SUBU_R31_R31_IMM(inst))
		return 0;

 	/*
	 * Unfortunately for us, functions with variable number of
	 * arguments will further alter r31 in va_start(), through the
	 * use of __builtin_alloca(). Since panic() is one such
	 * function, we need to take this into account to be able to
	 * backtrack further.
	 *
	 * So we need to look for further addu/subu r31 sequences after the
	 * prologue.
	 *
	 * Of course, control flow may cause execution to NOT pass
	 * through the __builtin_alloca() expansion...
	 */
	DPRINTF(("{orig r31 is %p}\n", *stack));
	*stack += IMM16VAL(inst);

	if (function_addr == (vaddr_t)&panic) /* XXX others? */ {
		check_addr = function_addr + 4;
		instructions_to_search = (addr - check_addr) / 4;
		while (instructions_to_search-- != 0) {
			if (!db_trace_get_val(check_addr, &inst))
				break;
			if (SUBU_R31_R31_IMM(inst)) {
				DPRINTF(("{variadic subu r31 found at %p}\n",
				    check_addr));
				*stack += IMM16VAL(inst);
			}
			check_addr += 4;
		}
	}

	/*
	 * Search from the beginning of the function (function_addr) to where
	 * we are in the function (addr) looking to see what kind of registers
	 * have been saved on the stack.
	 *
	 * We'll stop looking before we get to addr if we hit a branch.
	 */
	clear_local_saved_regs();
	check_addr = function_addr;
	r31 = *stack;
	DPRINTF(("{entry r31 would be %p}\n", r31));
	for (instructions_to_search = (addr - check_addr) / sizeof(uint32_t);
	    instructions_to_search-- != 0; check_addr += 4) {
		uint32_t s1, d;
		uint flags;

		/* read the instruction */
		if (!db_trace_get_val(check_addr, &inst))
			break;

		if (SUBU_R31_R31_IMM(inst)) {
			r31 -= IMM16VAL(inst);
			DPRINTF(("{adjust r31 to %p at %p}\n",
			    r31, check_addr));
		}

		/* find out the particulars about this instruction */
		flags = m88k_instruction_info(inst);

		/* split the instruction in its diatic components anyway */
		s1 = (inst >> 16) & 0x1f;
		d = (inst >> 21) & 0x1f;

		/* if a store to something off the stack pointer, note the value */
		if ((flags & STORE) && s1 == 31 /*stack pointer*/) {
			u_int value;
			if (!have_local_reg(d)) {
				if (d == 1)
					tried_to_save_r1 = r31 + IMM16VAL(inst);
				DPRINTF(("{r%d saved at %p to %p}\n",
				    d, check_addr, r31 + IMM16VAL(inst)));
				if (db_trace_get_val(r31 + IMM16VAL(inst),
				    &value))
					save_reg(d, value);
			}
			if ((flags & DOUBLE) && !have_local_reg(d + 1)) {
				if (d == 0)
					tried_to_save_r1 = r31 +
					    IMM16VAL(inst) + 4;
				DPRINTF(("{r%d saved at %p to %p}\n",
				    d + 1, check_addr, r31 + IMM16VAL(inst)));
				if (db_trace_get_val(r31 + IMM16VAL(inst) + 4,
				    &value))
					save_reg(d + 1, value);
			}
		}

		/* if an inst that kills D (and maybe D+1), note that */
		if (flags & TRASHES) {
			mark_reg_trashed(d);
			if (flags & DOUBLE)
				mark_reg_trashed(d + 1);
		}

		/* if a flow control instruction, stop now (or next if delayed) */
		if ((flags & FLOW_CTRL) && instructions_to_search != 0)
			instructions_to_search = (flags & DELAYED) ? 1 : 0;
	}

	/*
	 * If we didn't save r1 at some point, we're hosed.
	 */
	if (!have_local_reg(1)) {
		if (tried_to_save_r1 != 0) {
			(*pr)("    <return value of next fcn unreadable in %08x>\n",
				  tried_to_save_r1);
		}
		return 0;
	}

	ret_addr = saved_reg_value(1);

	if (ret_addr != 0) {
		switch (is_jump_source_ok(ret_addr, function_addr)) {
		case JUMP_SOURCE_IS_OK:
			break; /* excellent */

		case JUMP_SOURCE_IS_BAD:
			return 0; /* bummer */

		case JUMP_SOURCE_IS_UNLIKELY:
			next_address_likely_wrong = 1;
			break;
		}
	}

	return ret_addr;
}

static void
db_stack_trace_cmd2(db_regs_t *regs, int (*pr)(const char *, ...))
{
	vaddr_t stack;
	u_int depth=1;
	vaddr_t where;
	u_int ft;
	u_int pair[2];
	int i;

	/*
	 * Frame_is_sane returns:
	 *   1 if regs seems to be a reasonable kernel exception frame.
	 *   2 if regs seems to be a reasonable user exception frame
	 *      (in the current task).
	 *   0 if this looks like neither.
	 */
	if ((ft = frame_is_sane(regs, 1)) == 0) {
		(*pr)("Register frame 0x%x is suspicious; skipping trace\n", regs);
		return;
	}

	/* if user space and no user space trace specified, puke */
	if (ft == 2)
		return;

	/* fetch address */
	where = PC_REGS(regs);
	stack = regs->r[31];
	(*pr)("stack base = 0x%x\n", stack);
	(*pr)("(0) "); /* depth of trace */
	db_printsym(where, DB_STGY_PROC, pr);
	clear_global_saved_regs();

	/* see if this routine had a stack frame */
	if ((where = stack_decode(where, &stack, pr)) == 0) {
		where = regs->r[1];
		(*pr)("(stackless)");
	} else
		print_args();
	(*pr)("\n");

	do {
		/*
		 * If requested, show preserved registers at the time
		 * the next-shown call was made. Only registers known to have
		 * changed from the last exception frame are shown, as others
		 * can be gotten at by looking at the exception frame.
		 */
		(*pr)("(%d)%c", depth++, next_address_likely_wrong ? '?' : ' ');
		next_address_likely_wrong = 0;

		db_printsym(where, DB_STGY_PROC, pr);
		where = stack_decode(where, &stack, pr);
		print_args();
		(*pr)("\n");
	} while (where);

	/* try to trace back over trap/exception */

	stack &= ~7; /* double word aligned */
	/* take last top of stack, and try to find an exception frame near it */

	i = FRAME_PLAY;
	while (i) {
		/*
		 * On the stack, a pointer to the exception frame is written
		 * in two adjacent words. In the case of a fault from the kernel,
		 * this should point to the frame right above them:
		 *
		 * Exception Frame Top
		 * ..
		 * Exception Frame Bottom  <-- frame addr
		 * frame addr
		 * frame addr		<-- stack pointer
		 *
		 * In the case of a fault from user mode, the top of stack
		 * will just have the address of the frame
		 * replicated twice.
		 *
		 * frame addr		<-- top of stack
		 * frame addr
		 *
		 * Here we are just looking for kernel exception frames.
		 */

		if (badaddr((vaddr_t)stack, 4) ||
		    badaddr((vaddr_t)(stack + 4), 4))
			break;

		db_read_bytes((vaddr_t)stack, 2 * sizeof(int), (char *)pair);

		/* the pairs should match and equal stack+8 */
		if (pair[0] == pair[1]) {
			if (pair[0] != stack+8) {
#if 0
				if (!badaddr((vaddr_t)pair[0], 4) &&
				    pair[0] != 0)
					(*pr)("stack_trace:found pair 0x%x but != to stack+8\n",
					    pair[0]);
#endif
			} else if (frame_is_sane((db_regs_t*)pair[0], 1) != 0) {
				struct trapframe *frame =
				    (struct trapframe *)pair[0];

				(*pr)("-------------- %s [EF: 0x%x] -------------\n",
				    m88k_exception_name(frame->tf_vector),
				    frame);
				db_stack_trace_cmd2(&frame->tf_regs, pr);
				return;
			}
		}
		stack += 8;
		i--;
	}
}

/*
 * stack trace - needs a pointer to a m88k saved state.
 *
 * If argument f is given, the stack pointer of each call frame is
 * printed.
 */
void
db_stack_trace_print(db_expr_t addr,
		   int have_addr,
		   db_expr_t count,
		   char *modif,
		   int (*pr)(const char *, ...))
{
	enum {
		Default, Stack, Frame
	} style = Default;
	db_regs_t frame;
	db_regs_t *regs;
	union {
		db_regs_t *frame;
		db_expr_t num;
	} arg;

	arg.num = addr;

	while (modif && *modif) {
		switch (*modif++) {
		case 's': style = Stack  ; break;
		case 'f': style = Frame  ; break;
		default:
			(*pr)("unknown trace modifier [%c]\n", modif[-1]);
			/*FALLTHROUGH*/
		case 'h':
			(*pr)("usage: trace/[MODIFIER]  [ARG]\n");
			(*pr)("  s = ARG is a stack pointer\n");
			(*pr)("  f = ARG is a frame pointer\n");
			return;
		}
	}

	if (!have_addr && style != Default) {
		(*pr)("expecting argument with /s or /f\n");
		return;
	}
	if (have_addr && style == Default)
		style = Frame;

	switch (style) {
	case Default:
		regs = DDB_REGS;
		break;
	case Frame:
		regs = arg.frame;
		break;
	case Stack:
	    {
		u_int val1, val2, sxip;
		u_int ptr;
		bzero((void *)&frame, sizeof(frame));
#define REASONABLE_FRAME_DISTANCE 2048

		/*
		 * We've got to find the top of a stack frame so we can get both
		 * a PC and and real SP.
		 */
		for (ptr = arg.num;/**/; ptr += 4) {
			/* Read a word from the named stack */
			if (db_trace_get_val(ptr, &val1) == 0) {
				(*pr)("can't read from %x, aborting.\n", ptr);
				return;
			}

			/*
			 * See if it's a frame pointer.... if so it will be larger than
			 * the address it was taken from (i.e. point back up the stack)
			 * and we'll be able to read where it points.
			 */
			if (val1 <= ptr ||
			    (val1 & 3)  ||
			    val1 > (ptr + REASONABLE_FRAME_DISTANCE))
				continue;

			/* peek at the next word to see if it could be a return address */
			if (db_trace_get_val(ptr, &sxip) == 0) {
				(*pr)("can't read from %x, aborting.\n", ptr);
				return;
			}
			if (sxip == 0 || !db_trace_get_val(sxip, &val2))
				continue;

			if (db_trace_get_val(val1, &val2) == 0) {
				(*pr)("can't read from %x, aborting.\n", val1);
				continue;
			}

			/*
			 * The value we've just read will be either
			 * another frame pointer, or the start of
			 * another exception frame.
			 */
			if (val2 == 0x12345678 &&
			    db_trace_get_val(val1 - 4, &val2) &&
			    val2 == val1 &&
			    db_trace_get_val(val1 - 8, &val2) &&
			    val2 == val1) {
				/* we've found a frame, so the stack
				   must have been good */
				(*pr)("%x looks like a frame, accepting %x\n",val1,ptr);
				break;
			}

			if (val2 > val1 && (val2 & 3) == 0) {
				/* well, looks close enough to be another frame pointer */
				(*pr)("*%x = %x looks like a stack frame pointer, accepting %x\n", val1, val2, ptr);
				break;
			}
		}
		frame.r[31] = ptr;
		frame.epsr = 0x800003f0U;
#ifdef M88100
		if (CPU_IS88100) {
			frame.sxip = sxip | XIP_V;
			frame.snip = frame.sxip + 4;
			frame.sfip = frame.snip + 4;
		}
#endif
		(*pr)("[r31=%x, %sxip=%x]\n", frame.r[31],
		    CPU_IS88110 ? "e" : "s", frame.sxip);
		regs = &frame;
	    }
		break;
	}
	db_stack_trace_cmd2(regs, pr);
}