/*
 *$Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident "$Header: stack.c 1.14 89/08/07 $"

/*
 * $Log:	stack.c,v $
 */

#include "crash.h"

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/dir.h> 
#include <sys/resource.h>
#include <signal.h>
#include <sys/user.h>
#include <sys/timer.h>
#include <sys/proc.h>
#include <sys/vm.h>
#include <sys/pte.h>
#include <sys/engine.h>
#include <sys/plocal.h>
#include <sys/reg.h>
#include <sys/trap.h>
#include <string.h>
#define	INKERNEL
#include <sys/var.h>

static	int	fp_length = 0;
static	int	*fp_tail = 0;
static	int	fp_offset = 0;

static	int	*kfp = 0;
static	int	usekfp = 0;
static	int	disreg = 0;

extern int xdebug;

stack()
{
	register char *arg;
	register struct proc *p;
	int eng, n, didstack;
	struct var v;

	eng = didstack = disreg = usekfp = 0;
	if ((arg=token()) == NULL) { /* "s" */
stackproc:
		if (live) {
			readv(search("v"), &v, sizeof v);
			procmax = v.ve_proc;
		}
		for (p = proc; p < procmax; p++) {
			fp_tail = 0;
			fp_length = 0;
			proctrace(p);
		}
		return;
	}
	while (arg != NULL) {
		/* display registers and fp if "-r" */
		if (strcmp(arg, "-r") == 0) {
			disreg = 1;
			arg = token();
			continue;
		}
		/* user selected kfp if "-f" */
		if (strcmp(arg, "-f") == 0) {
			usekfp = 1;
			arg = token();
			continue;
		}
		if (strcmp(arg, "-e") == 0) {
			eng = !eng;
			arg = token();
			if (arg == NULL)
				for (eng=0; eng < Nengine; eng++) {
					fp_tail = 0;
					fp_length = 0;
					didstack = 1;
					engtrace(eng);
				}
			continue;
		} else if (eng) {
			n = atoi(arg);
			if ( err_atoi ) {
				printf("'%s', %s\n", arg, err_atoi);
			} else {
				fp_tail = 0;
				fp_length = 0;
				didstack = 1;
				engtrace(n);
			}
		} else {
			fp_tail = 0;
			fp_length = 0;
			didstack = 1;
			proctrace(getproc(arg));
		}
		arg = token();
	}
	if (!didstack && (disreg || usekfp))
		goto stackproc;
}

static
Dmpstk()
{
	register char *arg;

	if ((arg = token()) == NULL) {
		printf("dump what stack?\n");
		return;
	}

	dmpstk(getproc(arg));
}

static
engtrace(n)
{
	struct engine *e, *v;
	struct ppriv_pages ppriv_pages;	/* physical address of local stuff */
	register struct priv_map *pm;
	register i,j;

	if (n < 0  || n >= Nengine) {
		printf("engine %d? (0..%d are valid)\n", n, Nengine-1);
		return;
	}
	e = &l_engine[n];
	v = &v_engine[n];
	if (live) 
		readv(v, e, sizeof (struct engine));
	j = readp(e->e_local, &ppriv_pages, sizeof ppriv_pages);
	if (j != sizeof ppriv_pages) {
		printf("engtrace: readp error (%d != %d)\n", j, sizeof ppriv_pages);
		return;
	}
	pm = (struct priv_map *)ppriv_pages.pp_pmap;
	for (j=0; j < UPAGES; j++) {
		i = readp(PTETOPHYS(pm->pm_uarea[j]), (char *) ppriv_pages.pp_uarea[j], NBPG);
		if (i != NBPG) {
			printf("engtrace: readp error (%d != %d)\n", i, NBPG);
			return;
		}
	}
#if CRASH_DEBUG
	if (xdebug > 2) {
		for (j=0; j < (UPAGES*NBPG)/sizeof (int); j++) {
			if ((j & (8-1)) == 0 || j==0) printf("%s%08x", j ? "\n" : NULL, VA_UBLOCK + j * sizeof (int));
			printf(" %08x", ((int *)ppriv_pages.pp_uarea)[j]);
		}
		printf("\n");
	}
#endif
	printf("\n*** Engine %d ***\n", n);
	utrace(ppriv_pages.pp_uarea, 0, 1);
}

static
proctrace(pp)
	struct proc *pp;	/* virtual address of proc entry */
{
	register char *ret;
	struct	proc	p;
	int	use_u_sp;

	if (pp == NULL)
		return;
	ret = getuarea(pp);
	if (ret == NULL) return 0;
	if (ret == (char *) -1) {
		printf("bad read of Uarea\n"); 
		return -1;
	}
	if (ret == (char *) SWAPPED) { 
		printf("process is swapped\n"); 
		return -1; 
	}
#if CRASH_DEBUG
	if (xdebug > 2) {
		register int j;

		for (j=0; j < (UPAGES*NBPG)/sizeof (int); j++) {
			if ((j & (8-1)) == 0 || j==0) printf("%s%08x", j ? "\n" : NULL, VA_UBLOCK + j * sizeof (int));
			printf(" %08x", ((int *)ret)[j]);
		}
		printf("\n");
	}
#endif
	/*
	 * If process anything but running on a processor, 
	 * find frame pointer from u.u_sp.
	 */

	printf("\n*** Proc slot %d ***\n", pp - proc);

	if (readv(pp, &p, sizeof (struct proc)) == sizeof (struct proc)
	&& (p.p_stat && p.p_stat != SONPROC))
		use_u_sp = 1;
	else
		use_u_sp = 0;
	utrace(ret, use_u_sp, 0);
}

static
dmpstk(pp)
	struct proc *pp;
{
	register char *ret;
	register int j;

	if (pp == NULL)
		return;

	ret = getuarea(pp);
	if (ret == (char *) -1) {
		printf("bad read of Uarea\n");
		return -1;
	}

	if (ret == (char *) SWAPPED) {
		printf("process is swapped\n");
		return -1;
	}

	printf("*** Proc slot %d stack dump ***\n", pp - proc);

	for (j=0; j < (UPAGES*NBPG)/sizeof (int); j++) {
		if ((j & (8-1)) == 0 || j==0) printf("%s%08x", j ? "\n" : NULL, VA_UAREA + j * sizeof (int));
		printf(" %08x", ((int *)ret)[j]);
	}
	printf("\n");
}

static
utrace(ub, use_u_sp, idle)
	register char *ub;		/* pointer to U-block */
	int	use_u_sp;		/* derive fp from u_sp ?? */
	int	idle;			/* running on idle stack */
{
	register  int *sp;
	int *fp, *guessfp;
	int *top, *bot;
	struct user *u;

	u = UBTOUSER(ub);
	fp_offset = (VA_UBLOCK - (int)ub);
	sp = (int *)(u->u_sp - fp_offset);

	top = (int *)(VA_UAREA - fp_offset);
	bot = (int *)((int)top + sizeof(struct user) - (UPAGES*NBPG));

	if (xdebug) 
printf("(bottom %#x, top %#x)\n", (int)bot+fp_offset, (int)top+fp_offset-4);
	if (usekfp && (kfp != (int *)0)) {
		guessfp = (int *)((int)kfp - (int)fp_offset);
	 } else if (usekfp && (kfp == 0)) {
		printf("bad kfp\n");
		return;
	} else if (use_u_sp) {
		/*
		 * Stack frame when process is sleeping or stopped is
		 * u_sp -> EBP, ret PC.  386/implementation specific.
		 */
		guessfp = sp;
	} else 
		guessfp = (int *) (scanfp(top, bot, idle) - fp_offset);

#if CRASH_DEBUG
	if (xdebug > 1) 
		printf(">> utrace: fp_offset %#x, sp %#x, fp %#x, guessfp %#x, top %#x, bot %#x\n", 
			fp_offset, sp, fp, guessfp, top, bot);
#endif

	/* bad u_sp and scanfp failed */
	if ((int)guessfp+fp_offset == 0) {
		printf("no reasonable trace possible\n");
		return;
	}
	printstacktrace(guessfp, "unknown", top, bot);
}

static
scanfp(top, bot, idle)
	int *bot, *top;
	int	idle;
{
	/*
	 * ok lets use some heurestics for a good start.
	 * there 3 main types of calling mechanism
	 * interrupts faults and exceptions
	 * system calls from user mode will always be from user text priv 3
	 * and hence the first byte on the stack is 23 in this case
	 * when called from the idle stack if in debug mode then information
	 * from the previous stack may be available.
	 */
	if (idle) {
		if ((top[-2] == 0) && (top[-0x12] == 0xfffffff8))
			top -= 1;
		else
			top -= 0x11;	/* idle stack starts 0x40 byte down */
	}
	if (*(top-1) == 0x23) {	/* USER_SS */
		if (xdebug) 
			printf("*** onproc %s in a system call or trap\n",
							live?"is":"was");
		fpsearch(top-10, bot, 0);
		return (int) fp_tail;
	}
	fpsearch(top, bot, 0);
	return (int) fp_tail;
}

static
fpsearch(top, bot, depth)
	int *top, *bot;
{
	register int *i, *me;

	if (depth > (UPAGES*NBPG)/sizeof (int *)) {
		printf("fpsearch: recursion detected\n");
		return;
	}
	if (depth == 0) { /* top level */
		for (i=top-1; i >= top-48; i--) {
			me = (int *)(*i - fp_offset);
			if (me >= bot && me <= top)
				fpsearch(me, bot, depth+1);
		}
	} else {	/* looking for links */
		for (i=top-1; i >= bot; i--) {
			me = (int *)(*i - fp_offset);
			if (me == top) { /* found a link */
				if (depth > fp_length) {
					fp_length++;
					fp_tail = (int *)((int)i + fp_offset);
				}
				/* the divine recurse */
				fpsearch(i, bot, depth+1);
			}
		}
	}
}

/*
 * getregs()
 *	Look at at call frame for a given procedure, and determine
 *	how many (if any) register variables, and return their values.
 *
 * Assumes register order is %edi, %esi, %ebx.
 *
 * Returns # register variables, and values in regs[].
 */

/*
 * proc_entry[]:	Binary for "pushl %ebp; movl %esp, %ebp"
 * subl_byte_esp[]:	Binary for "subl $n, %esp" where n fits in a byte.
 * subl_long_esp[]:	Binary for "subl $n, %esp" where n fits in a long.
 * push_regs[]:		Binary for "pushl %edi; pushl %esi; pushl %ebx"
 */

static	char	*regname[] = { "edi", "esi", "ebx" };

static	unsigned char	proc_entry[] = { 0x55, 0x8b, 0xec };
static	unsigned char	subl_byte_esp[] = { 0x83, 0xec };
static	unsigned char	subl_long_esp[] = { 0x81, 0xec };
static	unsigned char	push_regs[] = { 0x57, 0x56, 0x53 };

static
getregs(sym, fp, regs)
	char	*sym;		/* entry symbol */
	int	*fp;		/* adjusted frame pointer for "sym" call */
	int	*regs;		/* array of registers to fill out */
{
	register unsigned char *ip;
	register int	i;
	unsigned int	val;
	unsigned char	instr[12];

	if (!lookbysym(sym, &val)
	||  readv(val, instr, sizeof(instr)) != sizeof(instr))
		return(0);

	/*
	 * If not standard procedure entry, assume no register variables.
	 */

	if (memcmp(instr, proc_entry, sizeof(proc_entry)) != 0)
		return(0);

	/*
	 * Skip a "subl $n, %esp" instruction (allocates locals before
	 * push registers), if one exists.
	 */

	ip = &instr[sizeof(proc_entry)];
	if (memcmp(ip, subl_byte_esp, sizeof(subl_byte_esp)) == 0) {
		ip += sizeof(subl_byte_esp);	/* skip to immediate */
		fp -= (*ip / 4);		/* adjust fp from immediate */
		ip += 1;			/* skip single byte immediate */
	} else if (memcmp(ip, subl_long_esp, sizeof(subl_long_esp)) == 0) {
		ip += sizeof(subl_long_esp);	/* skip to immediate */
		fp -= (*(long *)ip / 4);	/* adjust fp from immediate */
		ip += 4;			/* skip 4-byte immediate */
	}

	/*
	 * The next bytes of code push the registers (if any).
	 */

	for (i = 0; i < sizeof(push_regs); i++) {
		if (*ip++ != push_regs[i])
			break;
		regs[i] = *--fp;
	}

	return(i);
}

/*
 * printstacktrace()
 *	Print a stack trace, optionally with register variables.
 *
 * This could be augmented with more smarts about syscalls and locore
 * entries.
 */

static
printstacktrace(fp, whence, top, bot)
	register int *fp;		/* frame pointer of current frame */
	char	*whence;		/* from where we was called */
	int	*top, *bot;		/* range of stack values */
{
	int *newfp = (int *)(fp[0]);	/* frame pointer of next frame */
	int *oldpc = (int *)(fp[1]);	/* pc this frame returns to */
	int *oldap = (int *)(&fp[2]);	/* point to first argument */
	int  nargs = 0;			/* number of arguments passed */
	int prev_instr;
	char buft[100];
	unsigned char offset[sizeof(int)];
	int i;
	int locore = 0;

	/*
	 * Get the instruction following call (at ret pc).
	 */

	readv(oldpc, &prev_instr, sizeof prev_instr);

	/*
	 * determine number of arguments from:
	 *	stack pointer subtract instruction 
	 *	or pop into register
	 */

#define POP_ECX           0x59  /* opcode for 386 'popl %ecx' instruction */
#define ADDLIMM8        0xc483  /* opcode for 386 'addl $imm8' instruction */
#define ADDLIMM32       0xc481  /* opcode for 386 'addl $imm32' instruction */

	if ((int)oldpc < Etext) {
		if ((prev_instr & 0xff) == POP_ECX) {
			nargs = 1;
        	} else if ((prev_instr & 0xffff) == ADDLIMM8) {
			nargs = ((prev_instr >> 16) & 0xff) / 4;
		} else if ((prev_instr & 0xffff) == ADDLIMM32) {
			readv(oldpc+2, offset, sizeof(offset));
			nargs = (offset[0] >> 24) | (offset[1] >>  8) | 
			        (offset[2] <<  8) | (offset[3] << 24);
		}
	}

	if (nargs > 6)		  /* sanity */
		nargs = 6;

	/*
	 * Figure procedure we're in from data below on the stack (eg, whence
	 * argument), and set up `whence' for next iteration (from "oldpc").
	 * Print arguments.
	 */

	strcpy(buft, whence);
	if (strrchr(buft, '+'))
		*(char *)strrchr(buft, '+') = '\0';
	printf("%s(", buft);
	if (strcmp(buft,"trap") == 0) {
		nargs=14;
		locore = 1;
	} else if (strcmp(buft,"syscall") == 0) {
		nargs=13;
		locore = 1;
	} else if (strcmp(buft,"devcommon") == 0) {
		nargs=5;
		locore = 1;
	} else if (strcmp(buft,"bin0int") == 0) {
		nargs=5;
		locore = 1;
	}
	while (nargs) {
		printf("%#x", *oldap++);
		if( --nargs )
			printf(", ");
	}
	if ((long)oldpc > Etext) {
		printf(") from unknown (%#x)\n", oldpc);
		whence = "unknown";
	} else {
		printf(") ");
		if (xdebug) printf("[fp=%#x] ", (int)fp+fp_offset);
		whence = addr_str(oldpc);
		if (locore)
			printf("\n");
		else {
			printf("from %s\n", whence);
		}
	}

	/*
	 * If in a "cmn_err" frame, dump registers at time of panic.
	 * Else if looking at registers, dump them from current frame.
	 */

	if (xdebug && strcmp("trap", buft) == 0) {
		if (fp[12] == KERNEL_CS) {
			printf("\n*** trap type0x%x from kernel eip=0x%x\n\n", 
					fp[2], fp[11] );
		} else {
			printf("\n*** trap type0x%x from user eip=0x%x\n\n", 
					fp[2], fp[11] );
		}
	} else if (strcmp("cmn_err", buft) == 0) {
		/*
		 * If cmn_err() entry changes, so must this.
		 */
	
		int	*eax;
		int	e;
		e = entry_args();
		eax = fp - e;
		printf("\t[eax=%x ebx=%x ecx=%x edx=%x esi=%x edi=%x] fp=%x\n",
				eax[EAX], eax[EBX], eax[ECX],
				eax[EDX], eax[ESI], eax[EDI],
				(int)fp + fp_offset);
	} else if (disreg) {
		int	regs[3];
		int	nregs;
		if (nregs = getregs(buft, fp, regs)) {
			printf("\t[");
			for (i = 0; i < nregs; i++) {
				if (i != 0) printf(" ");
				printf("%s=%#x", regname[i], regs[i]);
			}
			printf("] fp = %#x\n",(unsigned int)fp+fp_offset);
		}
	}

	if ((int)fp == ((int)top - 15*4)) {
		printf("from \"%s\", user pc %#x\n",
			user_cmd((struct user *)top), fp[10]);
		return;
	}

	newfp = (int *)((int)newfp - fp_offset);
	if (newfp <= top && newfp > fp && newfp >= bot)
		printstacktrace(newfp, whence, top, bot);
	else if (((int)top - (int)newfp) > 15*4)
		printf("unable to continue  [newfp=%#x]\n", (int)newfp+fp_offset);
}

Kfp()
{
	register char *arg;

	if ((arg = token()) == NULL)
		printf("kfp = %08#x\n", kfp);
	else 
		kfp = (int *)atoi(arg);
}

get_locore()
{
	extern int end_locore, start_locore;

	start_locore = search("bin1int");
	end_locore = search("sys_copy_err");
	if (end_locore == 0 || start_locore == 0)
		printf("Warning: locore symbols out of date\n");
}

/*
 * entry_args -- calculate entry args for cmn_err.
 *
 * This routine computes the value that must be subtracted from the frame
 * pointer to find out where the "pushal" instruction placed all the registers.
 * It returns the number of WORDS so frame pointer must be adjusted by 4 times
 * the return value.
 *
 * Expected instruction sequence must not be changed, but the exact value
 * used for local argument storage may change.  It is because this value
 * for local storage changes that the value cannot be hard coded.
 */

entry_args()
{
	char *m;
	unsigned addr;
	int length;
	int i, n;

	if (!lookbysym("cmn_err", &addr)) {
		printf("\nUnable to locate routine cmn_err\n");
err:
		printf("\nWARNING: entry_args failed to calculate register location\n");
		printf("Using constant value to locate stored registers.\n");
		printf("Register values may be incorrect.\n");
		return(14);
	}

	/* expect to see "pushl  %ebp" as first instruction. */

	m = (char *) dis386(addr, &length);
	if (length == 0)
		goto err;
	addr += length;
	if (strncmp(m, "pushl", 5) != 0) {
		printf("\nDid not find \"pushl\" as first instruction of cmn_err.\n");
		goto err;
	}

	/* expect to see "movl  %esp, %ebp" */

	m = (char *) dis386(addr, &length);
	if (length == 0)
		goto err;
	addr += length;
	if (strncmp(m, "movl", 4) != 0) {
		printf("\nDid not find \"movl\" as second instruction of cmn_err.\n");
		goto err;
	}

	/*
	 * Now expect to see "subl  $0x28,%esp" but the value may not
	 * be 0x28.  We want to pick up this value.  It is some number
	 * of bytes, which we expect to be divisible by 4.  We want to
	 * divide by 4 to calculate the number of words.
	 */

	m = (char *) dis386(addr, &length);
	if (length == 0)
		goto err;
	addr += length;
	if (strncmp(m, "subl", 4) != 0) {
		printf("\nDid not find \"movl\" as third instruction of cmn_err.\n");
		goto err;
	}

	if ( (sscanf(m, "%*[^$]$0x%x,%%esp", &i) != 1) &&	
	     (sscanf(m, "%*[^$]$x%x,%%esp", &i) != 1)    ) {
		printf("\nCould not decode stack adjust value in cmn_err.\n");
		goto err;
	}
	
	n = i / 4;
	if ((n * 4) != i) {
		printf("Stack adjust value in cmn_err not multiple of 4.\n");
		goto err;
	}

	/*
	 * Now increment return value for each register pushed on stack
	 * with "pushl" instruction.
	 */

	for (;;) {
		m = (char *) dis386(addr, &length);
		if (strncmp(m, "pushal", 6) == 0) {
			return(n + 1);
		} else if (strncmp(m, "pushl", 5) == 0) {
			if (length == 0)
				goto err;
			n++;
			addr += length;
		} else {
			printf("Unable to locate \"pushal\" instruction in cmn_err.\n");
			goto err;
		}
	}
}
