/*
 *	$Source: /afs/athena.mit.edu/contrib/watchmaker/src/ofiles/RCS/kutils.c,v $
 *	$Author: epeisach $
 *	$Header: /afs/athena.mit.edu/contrib/watchmaker/src/ofiles/RCS/kutils.c,v 1.3 91/01/23 14:23:47 epeisach Exp $
 */

#ifndef lint
static char *rcsid_kutils_c = "$Header: /afs/athena.mit.edu/contrib/watchmaker/src/ofiles/RCS/kutils.c,v 1.3 91/01/23 14:23:47 epeisach Exp $";
#endif lint

#include "./ofiles.h"

struct nlist    nl[] =
{
    {"_proc"},			/* 0 */
    {"_nproc"},			/* 1 */
    {"_Usrptmap"},		/* 2 */
    {"_usrpt"},			/* 3 */
    {0}
};

/*
 * Get user page for proc "p" from core or swap. Returns pointer to user
 * struct.
 */

struct user    *getuser (p)
struct proc    *p;
{
    static struct user	u;
    getu(p,&u,1,0);
    return(&u);
}

#if 0
getu_ultrix(mproc, uptr, sflg, kflg)
register struct proc *mproc;
register struct user *uptr;
{
	struct pte *pteaddr, apte;
	struct pte arguutl[CLSIZE], wpte[UPAGES];
	register int i;
	int ncl, size;
	long  off;

	size = sflg ? ctob(UPAGES) : sizeof (struct user);
	if ((mproc->p_flag & SLOAD) == 0) {
		if (swap < 0)
			return (0);
		(void) lseek(swap, (long)dtob(mproc->p_swaddr), 0);
		if (read(swap, (char *)user.user, size) != size) {
			fprintf(stderr, "ps: cant read u for pid %d from %s\n",
			    mproc->p_pid, swapf);
			return (0);
		}
		pcbpf = 0;
		argaddr = 0;
		return (1);
	}
	if ((mproc->p_flag & SSYS) == 0) {
		pteaddr = &Usrptmap[btokmx(mproc->p_stakbr)+mproc->p_stakpt-1];
		klseek(kmem, (long)pteaddr, 0);
		if (read(kmem, (char *)&apte, sizeof(apte)) != sizeof(apte)) {
			printf(
			"ps: cant read indir pte to get u for pid %d from %s\n",
			 mproc->p_pid, kmemf);
			return (0);
		}
		off = (long)ctob(apte.pg_pfnum) + NBPG -
			((REDZONEPAGES+1) * sizeof(struct pte));
		lseek(mem, off, 0);
		if (read(mem, (char *)arguutl, sizeof(arguutl)) != 
			sizeof(arguutl))
		{
			printf("ps: cant read page table ");
			printf("for u of pid %d from %s\n",
			    mproc->p_pid, kmemf);
			return (0);
		}
		if (arguutl[0].pg_fod == 0 && arguutl[0].pg_pfnum)
			argaddr = ctob(arguutl[0].pg_pfnum);
		else
			argaddr = 0;
	} else {
		argaddr = 0;
	}
	klseek(kmem, (long)mproc->p_addr, 0);
	if (read(kmem, (char *)wpte, sizeof(wpte)) != sizeof(wpte)) {
		printf(
		"ps: cant read indir pte to get u for pid %d from %s\n",
		    mproc->p_pid, kmemf);
		return (0);
	}
	pcbpf = wpte[0].pg_pfnum;
	ncl = (size + NBPG*CLSIZE - 1) / (NBPG*CLSIZE);
	while (--ncl >= 0) {
		i = ncl * CLSIZE;
		lseek(mem, (long)ctob(wpte[i].pg_pfnum), 0);
		if (read(mem, user.upages[i], CLSIZE*NBPG) != CLSIZE*NBPG) {
			printf("ps: cant read page %d of u of pid %d from %s\n",
			    wpte[i].pg_pfnum, mproc->p_pid, memf);
			return(0);
		}
	}
	return (1);
}
#endif

/*
 * Get symbols from the kernel.
 */

getsyms ()
{
    register        i;

    if (nlist (UNIX, nl))
	fail (UNIX);
    
    for (i = 0; i < (sizeof (nl) / sizeof (nl[0])) - 1; i++)
	if (nl[i].n_value == 0)
	{
	    fprintff (stderr, "%s: can't find kernel symbol for %s.\n",
		      progname, nl[i].n_name);
	    exit (1);
	}

    eseek (kmem, (long) nl[X_PROC].n_value, 0, "procbase");
    eread (kmem, (char *) &procbase, sizeof (procbase), "procbase");

    eseek (kmem, (long) nl[X_NPROC].n_value, 0, "nproc");
    eread (kmem, (char *) &nproc, sizeof (nproc), "nproc");

    /* variables used by <vmmac.h> */

    Usrptma = (struct pte *) nl[X_USRPTMA].n_value;
    usrpt = (struct pte *) nl[X_USRPT].n_value;
}

/*
 * Read proc table entry "n" into buffer "p".
 */
procslot (n, p)
int             n;
struct proc    *p;
{
    eseek (kmem, procbase + (long) (n * sizeof (struct proc)), 0, "proc");
    eread (kmem, (char *) p, sizeof (struct proc), "proc");
}


/*
 * Read with error checking.
 */
eread (fd, p, size, s)
int             fd;
char           *p;
int             size;
char           *s;
{
    char            buf[100];
    
    errno = 0;
    if (read (fd, p, size) != size)
    {
	sprintff (buf, "read error for %s", s);
	fail (buf);
    }
}

/*
 * Seek with error checking.
 */

eseek (fd, off, whence, s)
int             fd;
long            off;
int             whence;
char           *s;
{
    long            lseek ();
    long            ret;
    char            buf[100];

    errno = 0;
    if ((ret = lseek (fd, off, whence)) != off)
    {
	sprintff (buf, "seek for %s failed, wanted %d, got %d",
		  s, off, ret);
	fail (buf);
    }
}

/*-------------------------------CUT HERE-------------------------------------*/
int usersize = sizeof(struct user);
#define Usrptmap Usrptma

/*
 * this code isolates the system dependancies (or most of them) 
 * in getting the 'u' area from the kernel memory or swap area
 * and also obtains the argument list (and possibly the environent)
 * from the user process.
 *
 * this code is (currently) included in ps and w.
 * YET TO BE DONE:
 * 1. isolate the symbols needed by this code into this module 
 * 	and do a merge before calling 'nlist' to obtain them.
 * 2. put into a separate file (getu.c?)
 * 3. put the object code into a library (-lps?) for general availability.
 *
 * the #ifndef FILE is so that the #includes aren't done twice (for now)
 * but are present so that later this can be separated into getu.c
 */

#ifndef FILE
#include <stdio.h>

#include <sys/param.h>
#include <sys/tty.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <machine/pte.h>
#include <sys/vm.h>
#include <sys/text.h>
#include <sys/stat.h>
#include <sys/mbuf.h>
#ifdef ibm032
#include <machine/fp.h>
#endif
#endif NFILE
#ifdef mips
#include <machine/vmparam.h>
#endif

#ifdef vax
#define clear(x) 	((int)x & 0x7fffffff)
#endif
#ifdef mips
#define clear(x) 	((int)x & 0x7fffffff)
#endif
#ifdef ibm032
#define clear(x) 	((int)x & 0x0fffffff)
#endif

/*
 * external input variables (should be parameters but this was a lot
 * easier to do.
 */
extern int swap;
extern int kmem;
extern int mem;
char	*kmemf, *memf, *swapf, *nlistf;
int	argaddr;
int	pcbpf;
extern struct	pte *Usrptmap, *usrpt;
int	dflg;			/* debug flag */
int	kflg;			/* if reading from kernel */
char	*savestr();
extern int	nproc, ntext;
extern int	dmmin, dmmax;
extern int	nswap;

#ifdef ibm032
#ifdef BSD4_3
#define REDSIZE CLSIZE*2		/* red zone size plus reserved page */
#else
#define REDSIZE CLSIZE			/* red zone size */
#endif BSD4_3
struct userx
{
	char userfill[UPAGES*NBPG-sizeof (struct user)];
	struct user user;
};

union {
	struct	userx userx;
	char	upages[UPAGES][NBPG];
} user;
#define U	user.userx.user
char *kernel_stack_top = &user.upages[0][0];
char *kernel_stack_base = (char *) &U;

#else
#define REDSIZE 0		/* red zone size */

union {
	struct	user user;
	char	upages[UPAGES][NBPG];
} user;
#define U	user.user
char *kernel_stack_base = &user.upages[UPAGES][0];
char *kernel_stack_top = (char *) U.u_stack;
#endif	ibm032

/*
 * read in the u area in a (pretty much) machine independent fasion.
 * mproc is pointer to (incore) proc structure 
 * uptr is where to copy the result to
 * sflg != 0	read the kernel stack as well as the u structure
 * kflg != 0	post mortem mode
 */
getu(mproc, uptr, sflg, kflg)
	register struct proc *mproc;
	register struct user *uptr;
{
	struct pte *pteaddr, apte;
/* on ibm032 the arrangement is:
 * 1. top of user stack
 * 2. red zone
 * 3. kernel stack 
 * 4. kernel stack and u area
 * 
 * the u area is at the very end of P1.
 */
	struct pte arguutl[UPAGES+CLSIZE +REDSIZE];
	struct pte wpte[UPAGES];
	register int i;
	int ncl, size;

#ifdef ibm032
	size = ctob(UPAGES);
#else
	size = sflg ? ctob(UPAGES) : sizeof (struct user);
#endif
	if (dflg > 2)
		printf("getu(%x,%x,%d,%d)\n",mproc,uptr,sflg,kflg);
	if ((mproc->p_flag & SLOAD) == 0) {
		if (swap < 0) {
			bzero((char *) uptr, sizeof (struct user));
			return (1);	/* pretend we got it (for -k case) */
		}
		(void) lseek(swap, (long)dtob(mproc->p_swaddr), 0);
		if (read(swap, (char *)user.upages, size) != size) {
			fprintf(stderr, "getu: cant read u for pid %d from %s\n",
			    mproc->p_pid, swapf);
			return (0);
		}
		pcbpf = 0;
		argaddr = 0;
#ifdef ibm032
		if ((i = usersize - sizeof (struct user)) > 0)
			bcopy(((char *) &U) - i, (char *) uptr, sizeof (struct user));	/* fake the location of the u structure */
		else
#endif ibm032
		*uptr = U;   /* added 8-9-85 for consistency */
		return (1);
	}
#ifndef ultrix
	if (kflg)
		mproc->p_p0br = (struct pte *)clear(mproc->p_p0br);
	pteaddr = &Usrptmap[btokmx(mproc->p_p0br) + mproc->p_szpt - 1];
#else
	pteaddr = &Usrptmap[btokmx(mproc->p_stakbr)+mproc->p_stakpt-1];
#endif
	klseek(kmem, (long)pteaddr, 0);
	if (klread(kmem, (char *)&apte, sizeof(apte)) != sizeof(apte)) {
		printf("getu: cant read indir pte to get u for pid %d from %s\n",
		    mproc->p_pid, swapf);
		return (0);
	}
/* locate the last entry in the page table array which will include 
 * the entry for the u area 
 */

#ifndef ultrix
	klseek(mem,
	    (long)ctob(apte.pg_pfnum+1) - (UPAGES+CLSIZE+REDSIZE) * sizeof (struct pte),
		0);
#else
	klseek(mem,
	       (long)ctob(apte.pg_pfnum) + NBPG - ((REDZONEPAGES+1) * sizeof(struct pte)), 0);
#endif
	if (klread(mem, (char *)arguutl, sizeof(arguutl)) != sizeof(arguutl)) {
		printf("getu: cant read page table for u of pid %d from %s\n",
		    mproc->p_pid, kmemf);
		return (0);
	}
	if (arguutl[0].pg_fod == 0 && arguutl[0].pg_pfnum)
		argaddr = ctob(arguutl[0].pg_pfnum);
	else
		argaddr = 0;
#ifdef ultrix
	klseek(kmem, (long)mproc->p_addr, 0);
	if (read(kmem, (char *)wpte, sizeof(wpte)) != sizeof(wpte)) {
		printf(
		"ps: cant read indir pte to get u for pid %d from %s\n",
		    mproc->p_pid, kmemf);
		return (0);
	}
#endif
	pcbpf = arguutl[CLSIZE+REDSIZE].pg_pfnum;
	ncl = (size + NBPG*CLSIZE - 1) / (NBPG*CLSIZE);
	while (--ncl >= 0) {
		long addr;
		i = ncl * CLSIZE;
#ifdef ultrix
		addr = (long)ctob(wpte[i].pg_pfnum);
#else
		addr = (long)ctob(arguutl[CLSIZE+REDSIZE+i].pg_pfnum);
#endif
		if (addr == 0) {
			bzero((char *) uptr, sizeof (struct user));
			return(1);	/* faked for swapper */
		}
		klseek(mem, addr, 0);
		if (klread(mem, user.upages[i], CLSIZE*NBPG) != CLSIZE*NBPG) {
			printf("getu: cant read page %d of u of pid %d from %s\n",
			    arguutl[CLSIZE+REDSIZE+i].pg_pfnum, mproc->p_pid, memf);
			return(0);
		}
	}
#ifdef ibm032
	if ((i = usersize - sizeof (struct user)) > 0)
		bcopy(((char *) &U) - i, (char *) uptr, sizeof (struct user));	/* fake the location of the u structure */
	else
#endif ibm032
	*uptr = U;	/* return the structure */
	if (dflg > 3)
		prhex("user struct",
			(char *) uptr,dflg > 4 ? sizeof (struct user) : 100 );
	return (1);
}

klseek(fd, loc, off)
	int fd;
	long loc;
	int off;
{

	if (kflg)
		loc = clear(loc);
	if (dflg > 1)
		printf("klseek(%s,%x,%x)\n",fd == kmem ? "kmem" : "mem",loc,off);
	(void) lseek(fd, (long)loc, off);
}

klread(fd, buff, length) char *buff;
{
	register int n = read(fd,buff,length);
	register int i, j = (dflg-2) << 4;

	if (dflg > 1)
		printf("klread(%s,%x,%x) (%x) ",fd == kmem ? "kmem" : "mem",buff,length,n);
	for (i=0; i<n && i < j ; ++i)
		printf(" %02x",buff[i]);
	if (dflg > 1)
		printf("\n");
	return(n);
}

prhex(msg,str,length)
register char *msg, *str; register int length;
{
	printf("%s @%x (%d bytes)\n",msg,str,length);
	while (--length >= 0)
		printf(" %02x",*str++);
	printf("\n");
}

#ifdef notdef
char *
getcmd(mproc,cflg,eflg)
	register struct proc *mproc;
	int cflg;
	int eflg;
{
	char cmdbuf[CLSIZE*NBPG];
	union {
		char	argc[CLSIZE*NBPG];
		int	argi[CLSIZE*NBPG/sizeof (int)];
	} argspac;
	register char *cp;
	register int *ip;
	char c;
	int nbad;
	struct dblock db;
	char *file;
	char *limit;

	if (mproc->p_stat == SZOMB || mproc->p_flag&(SSYS|SWEXIT))
		return ("");
	if (cflg) {
		(void) strncpy(cmdbuf, u.u_comm, sizeof (u.u_comm));
		if (dflg)
			printf("cmdbuf1=%s\n",cmdbuf);
		return (savestr(cmdbuf));
	}
	if ((mproc->p_flag & SLOAD) == 0 || argaddr == 0) {
		if (swap < 0)
			goto retucomm;
#if !defined(ibm032) || !defined(BSD4_3)
		vstodb(0, CLSIZE, &u.u_smap, &db, 1);
#else
		vstodb(CLSIZE, CLSIZE, &u.u_smap, &db, 1);
#endif
		(void) lseek(swap, (long)dtob(db.db_base), 0);
		if (read(swap, (char *)&argspac, sizeof(argspac))
		    != sizeof(argspac))
			goto bad;
		file = swapf;
	} else {
		klseek(mem, (long)argaddr, 0);
		if (klread(mem, (char *)&argspac, sizeof (argspac))
		    != sizeof (argspac))
			goto bad;
		file = memf;
	}
	if (dflg > 3)
		prhex("argspac",(char *) &argspac,
			dflg > 8 ? sizeof argspac :  (2 << dflg));
	ip = &argspac.argi[CLSIZE*NBPG/sizeof (int)];
#if defined(ibm032) && defined(NFL) && !defined(BSD4_3)
	if (dflg>3)
		printf("sizeof fp_mach = %d\n",sizeof (struct fp_mach));
	ip -= sizeof (struct fp_mach) / sizeof (int);
#endif ibm032
	limit = (char *) ip;
	ip -= 2;		/* last arg word and .long 0 */
	while (*--ip)
		if (ip == argspac.argi)
			goto retucomm;
	*(char *)ip = ' ';
	ip++;
	nbad = 0;
	for (cp = (char *)ip; cp < limit; cp++) {
		c = *cp & 0177;
		if (c == 0)
			*cp = ' ';
		else if (c < ' ' || c > 0176) {
			if (++nbad >= 5*(eflg+1)) {
				*cp++ = ' ';
				break;
			}
			*cp = '?';
		} else if (eflg == 0 && c == '=') {
			while (*--cp != ' ')
				if (cp <= (char *)ip)
					break;
			break;
		}
	}
	*cp = 0;
	while (*--cp == ' ')
		*cp = 0;
	cp = (char *)ip;
	(void) strncpy(cmdbuf, cp, &argspac.argc[CLSIZE*NBPG] - cp);
	if (cp[0] == '-' || cp[0] == '?' || cp[0] <= ' ') {
		(void) strcat(cmdbuf, " (");
		(void) strncat(cmdbuf, u.u_comm, sizeof(u.u_comm));
		(void) strcat(cmdbuf, ")");
	}
	if (dflg)
		printf("cmdbuf2=%s\n",cmdbuf);
	return (savestr(cmdbuf));

bad:
	fprintf(stderr, "getcmd: error locating command name for pid %d from %s\n",
	    mproc->p_pid, file);
retucomm:
	(void) strcpy(cmdbuf, " (");
	(void) strncat(cmdbuf, u.u_comm, sizeof (u.u_comm));
	(void) strcat(cmdbuf, ")");
	if (dflg)
		printf("cmdbuf3=%s\n",cmdbuf);
	return (savestr(cmdbuf));
}

/*
 * Given a base/size pair in virtual swap area,
 * return a physical base/size pair which is the
 * (largest) initial, physically contiguous block.
 */
vstodb(vsbase, vssize, dmp, dbp, rev)
	register int vsbase;
	int vssize;
	struct dmap *dmp;
	register struct dblock *dbp;
{
	register int blk = dmmin;
	register swblk_t *ip = dmp->dm_map;

	vsbase = ctod(vsbase);
	vssize = ctod(vssize);
	if (vsbase < 0 || vsbase + vssize > dmp->dm_size)
		panic("vstodb");
	while (vsbase >= blk) {
		vsbase -= blk;
		if (blk < dmmax)
			blk *= 2;
		ip++;
	}
	if (*ip <= 0 || *ip + blk > nswap)
		panic("vstodb *ip");
	dbp->db_size = min(vssize, blk - vsbase);
	dbp->db_base = *ip + (rev ? blk - (vsbase + dbp->db_size) : vsbase);
}
#endif notdef
