|  | /* | 
|  | * This file is part of the UCB release of Plan 9. It is subject to the license | 
|  | * terms in the LICENSE file found in the top-level directory of this | 
|  | * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
|  | * part of the UCB release of Plan 9, including this file, may be copied, | 
|  | * modified, propagated, or distributed except according to the terms contained | 
|  | * in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | //#define DEBUG | 
|  | /* proc on plan 9 has lots of capabilities, some of which we might | 
|  | * want for akaros: | 
|  | * debug control | 
|  | * event tracing | 
|  | * process control (no need for signal system call, etc.) | 
|  | * textual status | 
|  | * rather than excise code that won't work, I'm bracketing it with | 
|  | * #if 0 until we know we don't want it | 
|  | */ | 
|  | #include <vfs.h> | 
|  | #include <kfs.h> | 
|  | #include <slab.h> | 
|  | #include <kmalloc.h> | 
|  | #include <kref.h> | 
|  | #include <string.h> | 
|  | #include <stdio.h> | 
|  | #include <assert.h> | 
|  | #include <error.h> | 
|  | #include <cpio.h> | 
|  | #include <pmap.h> | 
|  | #include <smp.h> | 
|  | #include <arch/vmm/vmm.h> | 
|  |  | 
|  | enum { | 
|  | Qdir, | 
|  | Qtrace, | 
|  | Qtracepids, | 
|  | Qns, | 
|  | Qargs, | 
|  | Qctl, | 
|  | Qfd, | 
|  | Qfpregs, | 
|  | Qkregs, | 
|  | Qmem, | 
|  | Qnote, | 
|  | Qnoteid, | 
|  | Qnotepg, | 
|  | Qproc, | 
|  | Qregs, | 
|  | Qsegment, | 
|  | Qstatus, | 
|  | Qtext, | 
|  | Qwait, | 
|  | Qprofile, | 
|  | Qsyscall, | 
|  | Qcore, | 
|  | }; | 
|  |  | 
|  | enum { | 
|  | CMclose, | 
|  | CMclosefiles, | 
|  | CMfixedpri, | 
|  | CMhang, | 
|  | CMkill, | 
|  | CMnohang, | 
|  | CMnoswap, | 
|  | CMpri, | 
|  | CMprivate, | 
|  | CMprofile, | 
|  | CMstart, | 
|  | CMstartstop, | 
|  | CMstartsyscall, | 
|  | CMstop, | 
|  | CMwaitstop, | 
|  | CMwired, | 
|  | CMtrace, | 
|  | CMcore, | 
|  | CMvminit, | 
|  | CMvmstart, | 
|  | CMvmkill, | 
|  | }; | 
|  |  | 
|  | enum { | 
|  | Nevents = 0x4000, | 
|  | Emask = Nevents - 1, | 
|  | Ntracedpids = 1024, | 
|  | STATSIZE = 8 + 1 + 10 + 1 + 6 + 2, | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * Status, fd, and ns are left fully readable (0444) because of their use in debugging, | 
|  | * particularly on shared servers. | 
|  | * Arguably, ns and fd shouldn't be readable; if you'd prefer, change them to 0000 | 
|  | */ | 
|  | struct dirtab procdir[] = { | 
|  | {"args", {Qargs}, 0, 0660}, | 
|  | {"ctl", {Qctl}, 0, 0660}, | 
|  | {"fd", {Qfd}, 0, 0444}, | 
|  | {"fpregs", {Qfpregs}, 0, 0000}, | 
|  | //  {"kregs",   {Qkregs},   sizeof(Ureg),       0600}, | 
|  | {"mem", {Qmem}, 0, 0000}, | 
|  | {"note", {Qnote}, 0, 0000}, | 
|  | {"noteid", {Qnoteid}, 0, 0664}, | 
|  | {"notepg", {Qnotepg}, 0, 0000}, | 
|  | {"ns", {Qns}, 0, 0444}, | 
|  | {"proc", {Qproc}, 0, 0400}, | 
|  | //  {"regs",        {Qregs},    sizeof(Ureg),       0000}, | 
|  | {"segment", {Qsegment}, 0, 0444}, | 
|  | {"status", {Qstatus}, STATSIZE, 0444}, | 
|  | {"text", {Qtext}, 0, 0000}, | 
|  | {"wait", {Qwait}, 0, 0400}, | 
|  | {"profile", {Qprofile}, 0, 0400}, | 
|  | {"syscall", {Qsyscall}, 0, 0400}, | 
|  | {"core", {Qcore}, 0, 0444}, | 
|  | }; | 
|  |  | 
|  | static | 
|  | struct cmdtab proccmd[] = { | 
|  | {CMclose, "close", 2}, | 
|  | {CMclosefiles, "closefiles", 1}, | 
|  | {CMfixedpri, "fixedpri", 2}, | 
|  | {CMhang, "hang", 1}, | 
|  | {CMnohang, "nohang", 1}, | 
|  | {CMnoswap, "noswap", 1}, | 
|  | {CMkill, "kill", 1}, | 
|  | {CMpri, "pri", 2}, | 
|  | {CMprivate, "private", 1}, | 
|  | {CMprofile, "profile", 1}, | 
|  | {CMstart, "start", 1}, | 
|  | {CMstartstop, "startstop", 1}, | 
|  | {CMstartsyscall, "startsyscall", 1}, | 
|  | {CMstop, "stop", 1}, | 
|  | {CMwaitstop, "waitstop", 1}, | 
|  | {CMwired, "wired", 2}, | 
|  | {CMtrace, "trace", 0}, | 
|  | {CMcore, "core", 2}, | 
|  | {CMcore, "core", 2}, | 
|  | {CMcore, "core", 2}, | 
|  | {CMvminit, "vminit", 0}, | 
|  | {CMvmstart, "vmstart", 0}, | 
|  | {CMvmkill, "vmkill", 0}, | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * struct qids are, in path: | 
|  | *	 5 bits of file type (qids above) (old comment said 4 here) | 
|  | *	23 bits of process slot number + 1 (pid + 1 is stored) | 
|  | *	     in vers, | 
|  | *	32 bits of pid, for consistency checking | 
|  | * If notepg, c->pgrpid.path is pgrp slot, .vers is noteid. | 
|  | */ | 
|  | #define	QSHIFT	5	/* location in qid of proc slot # */ | 
|  | #define	SLOTBITS 23	/* number of bits in the slot */ | 
|  | #define	QIDMASK	((1<<QSHIFT)-1) | 
|  | #define	SLOTMASK	(((1<<SLOTBITS)-1) << QSHIFT) | 
|  |  | 
|  | #define QID(q)		((((uint32_t)(q).path)&QIDMASK)>>0) | 
|  | #define SLOT(q)		(((((uint32_t)(q).path)&SLOTMASK)>>QSHIFT)-1) | 
|  | #define PID(q)		((q).vers) | 
|  | #define NOTEID(q)	((q).vers) | 
|  |  | 
|  | static void procctlreq(struct proc *, char *, int); | 
|  | static int procctlmemio(struct proc *, uintptr_t, int, void *, int); | 
|  | //static struct chan*   proctext(struct chan*, struct proc*); | 
|  | //static Segment* txt2data(struct proc*, Segment*); | 
|  | //static int    procstopped(void*); | 
|  | static void mntscan(struct mntwalk *, struct proc *); | 
|  |  | 
|  | //static Traceevent *tevents; | 
|  | static char *tpids, *tpidsc, *tpidse; | 
|  | static spinlock_t tlock; | 
|  | static int topens; | 
|  | static int tproduced, tconsumed; | 
|  | //static void notrace(struct proc*, int, int64_t); | 
|  |  | 
|  | //void (*proctrace)(struct proc*, int, int64_t) = notrace; | 
|  |  | 
|  | #if 0 | 
|  | static void profclock(Ureg * ur, Timer *) | 
|  | { | 
|  | Tos *tos; | 
|  |  | 
|  | if (up == NULL || current->state != Running) | 
|  | return; | 
|  |  | 
|  | /* user profiling clock */ | 
|  | if (userureg(ur)) { | 
|  | tos = (Tos *) (USTKTOP - sizeof(Tos)); | 
|  | tos->clock += TK2MS(1); | 
|  | segclock(userpc(ur)); | 
|  | } | 
|  | } | 
|  | #endif | 
|  | static int | 
|  | procgen(struct chan *c, char *name, struct dirtab *tab, int unused, int s, | 
|  | struct dir *dp) | 
|  | { | 
|  | struct qid qid; | 
|  | struct proc *p; | 
|  | char *ename; | 
|  |  | 
|  | int pid; | 
|  | uint32_t path, perm, len; | 
|  | if (s == DEVDOTDOT) { | 
|  | mkqid(&qid, Qdir, 0, QTDIR); | 
|  | devdir(c, qid, "#p", 0, eve, 0555, dp); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | if (c->qid.path == Qdir) { | 
|  | if (s == 0) { | 
|  | strncpy(get_cur_genbuf(), "trace", GENBUF_SZ); | 
|  | mkqid(&qid, Qtrace, -1, QTFILE); | 
|  | devdir(c, qid, get_cur_genbuf(), 0, eve, 0444, dp); | 
|  | return 1; | 
|  | } | 
|  | if (s == 1) { | 
|  | strncpy(get_cur_genbuf(), "tracepids", GENBUF_SZ); | 
|  | mkqid(&qid, Qtracepids, -1, QTFILE); | 
|  | devdir(c, qid, get_cur_genbuf(), 0, eve, 0444, dp); | 
|  | return 1; | 
|  | } | 
|  | s -= 2; | 
|  | if (name != NULL) { | 
|  | /* ignore s and use name to find pid */ | 
|  | pid = strtol(name, &ename, 10); | 
|  | if (pid <= 0 || ename[0] != '\0') | 
|  | return -1; | 
|  | p = pid2proc(pid); | 
|  | if (!p) | 
|  | return -1; | 
|  | /* Need to update s, so that it's the correct 'index' for our proc | 
|  | * (aka, the pid).  We use s later when making the qid. */ | 
|  | s = pid; | 
|  | } else { | 
|  | /* This is a shitty iterator, and the list isn't guaranteed to give | 
|  | * you the same ordering twice in a row. (procs come and go). */ | 
|  | p = pid_nth(s); | 
|  | if (!p) | 
|  | return -1; | 
|  | pid = p->pid; | 
|  | } | 
|  |  | 
|  | snprintf(get_cur_genbuf(), GENBUF_SZ, "%u", pid); | 
|  | /* | 
|  | * String comparison is done in devwalk so | 
|  | * name must match its formatted pid. | 
|  | */ | 
|  | if (name != NULL && strcmp(name, get_cur_genbuf()) != 0) { | 
|  | printk("pid-name mismatch, name: %s, pid %d\n", name, pid); | 
|  | kref_put(&p->p_kref); | 
|  | return -1; | 
|  | } | 
|  | mkqid(&qid, (s + 1) << QSHIFT, pid, QTDIR); | 
|  | devdir(c, qid, get_cur_genbuf(), 0, p->user, DMDIR | 0555, dp); | 
|  | kref_put(&p->p_kref); | 
|  | return 1; | 
|  | } | 
|  | if (c->qid.path == Qtrace) { | 
|  | strncpy(get_cur_genbuf(), "trace", GENBUF_SZ); | 
|  | mkqid(&qid, Qtrace, -1, QTFILE); | 
|  | devdir(c, qid, get_cur_genbuf(), 0, eve, 0444, dp); | 
|  | return 1; | 
|  | } | 
|  | if (c->qid.path == Qtracepids) { | 
|  | strncpy(get_cur_genbuf(), "tracepids", GENBUF_SZ); | 
|  | mkqid(&qid, Qtracepids, -1, QTFILE); | 
|  | devdir(c, qid, get_cur_genbuf(), 0, eve, 0444, dp); | 
|  | return 1; | 
|  | } | 
|  | if (s >= ARRAY_SIZE(procdir)) | 
|  | return -1; | 
|  | if (tab) | 
|  | panic("procgen"); | 
|  |  | 
|  | tab = &procdir[s]; | 
|  | /* path is everything other than the QID part.  Not sure from the orig code | 
|  | * if they wanted just the pid part (SLOTMASK) or everything above QID */ | 
|  | path = c->qid.path & ~QIDMASK;	/* slot component */ | 
|  | if ((p = pid2proc(SLOT(c->qid))) == NULL) | 
|  | return -1; | 
|  | perm = 0444 | tab->perm; | 
|  | #if 0 | 
|  | if (perm == 0) | 
|  | perm = p->procmode; | 
|  | else	/* just copy read bits */ | 
|  | perm |= p->procmode & 0444; | 
|  | #endif | 
|  |  | 
|  | len = tab->length; | 
|  | #if 0 | 
|  | switch (QID(c->qid)) { | 
|  | case Qwait: | 
|  | len = p->nwait;	/* incorrect size, but >0 means there's something to read */ | 
|  | break; | 
|  | case Qprofile: | 
|  | q = p->seg[TSEG]; | 
|  | if (q && q->profile) { | 
|  | len = (q->top - q->base) >> LRESPROF; | 
|  | len *= sizeof(*q->profile); | 
|  | } | 
|  | break; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | mkqid(&qid, path | tab->qid.path, c->qid.vers, QTFILE); | 
|  | devdir(c, qid, tab->name, len, p->user, perm, dp); | 
|  | kref_put(&p->p_kref); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | #if 0 | 
|  | static void notrace(struct proc *, Tevent, int64_t) | 
|  | { | 
|  | } | 
|  |  | 
|  | static spinlock_t tlck = SPINLOCK_INITIALIZER_IRQSAVE; | 
|  |  | 
|  | static void _proctrace(struct proc *p, Tevent etype, int64_t ts) | 
|  | { | 
|  | Traceevent *te; | 
|  | int tp; | 
|  |  | 
|  | ilock(&tlck); | 
|  | if (p->trace == 0 || topens == 0 || tproduced - tconsumed >= Nevents) { | 
|  | iunlock(&tlck); | 
|  | return; | 
|  | } | 
|  | tp = tproduced++; | 
|  | iunlock(&tlck); | 
|  |  | 
|  | te = &tevents[tp & Emask]; | 
|  | te->pid = p->pid; | 
|  | te->etype = etype; | 
|  | if (ts == 0) | 
|  | te->time = todget(NULL); | 
|  | else | 
|  | te->time = ts; | 
|  | te->core = m->machno; | 
|  | } | 
|  |  | 
|  | void proctracepid(struct proc *p) | 
|  | { | 
|  | if (p->trace == 1 && proctrace != notrace) { | 
|  | p->trace = 2; | 
|  | ilock(&tlck); | 
|  | tpidsc = seprint(tpidsc, tpidse, "%d %s\n", p->pid, p->text); | 
|  | iunlock(&tlck); | 
|  | } | 
|  | } | 
|  |  | 
|  | #endif | 
|  | static void procinit(void) | 
|  | { | 
|  | #if 0 | 
|  | if (conf.nproc >= (SLOTMASK >> QSHIFT) - 1) | 
|  | printd("warning: too many procs for devproc\n"); | 
|  | addclock0link((void (*)(void))profclock, 113);	/* Relative prime to HZ */ | 
|  | #endif | 
|  | } | 
|  |  | 
|  | static struct chan *procattach(char *spec) | 
|  | { | 
|  | return devattach('p', spec); | 
|  | } | 
|  |  | 
|  | static struct walkqid *procwalk(struct chan *c, struct chan *nc, char **name, | 
|  | int nname) | 
|  | { | 
|  | return devwalk(c, nc, name, nname, 0, 0, procgen); | 
|  | } | 
|  |  | 
|  | static int procstat(struct chan *c, uint8_t * db, int n) | 
|  | { | 
|  | return devstat(c, db, n, 0, 0, procgen); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  none can't read or write state on other | 
|  | *  processes.  This is to contain access of | 
|  | *  servers running as none should they be | 
|  | *  subverted by, for example, a stack attack. | 
|  | */ | 
|  | static void nonone(struct proc *p) | 
|  | { | 
|  | return; | 
|  | #if 0 | 
|  | if (p == up) | 
|  | return; | 
|  | if (strcmp(current->user, "none") != 0) | 
|  | return; | 
|  | if (iseve()) | 
|  | return; | 
|  | error(Eperm); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | static struct chan *procopen(struct chan *c, int omode) | 
|  | { | 
|  | ERRSTACK(2); | 
|  | struct proc *p; | 
|  | struct pgrp *pg; | 
|  | struct chan *tc; | 
|  | int pid; | 
|  |  | 
|  | if (c->qid.type & QTDIR) | 
|  | return devopen(c, omode, 0, 0, procgen); | 
|  |  | 
|  | if (QID(c->qid) == Qtrace) { | 
|  | error("proc: Qtrace: not yet"); | 
|  | #if 0 | 
|  | if (omode != OREAD) | 
|  | error(Eperm); | 
|  | lock(&tlock); | 
|  | if (waserror()) { | 
|  | unlock(&tlock); | 
|  | nexterror(); | 
|  | } | 
|  | if (topens > 0) | 
|  | error("already open"); | 
|  | topens++; | 
|  | if (tevents == NULL) { | 
|  | tevents = (Traceevent *) kzmalloc(sizeof(Traceevent) * Nevents, | 
|  | KMALLOC_WAIT); | 
|  | if (tevents == NULL) | 
|  | error(Enomem); | 
|  | tpids = kzmalloc(Ntracedpids * 20, KMALLOC_WAIT); | 
|  | if (tpids == NULL) { | 
|  | kfree(tpids); | 
|  | tpids = NULL; | 
|  | error(Enomem); | 
|  | } | 
|  | tpidsc = tpids; | 
|  | tpidse = tpids + Ntracedpids * 20; | 
|  | *tpidsc = 0; | 
|  | tproduced = tconsumed = 0; | 
|  | } | 
|  | proctrace = _proctrace; | 
|  | poperror(); | 
|  | unlock(&tlock); | 
|  |  | 
|  | c->mode = openmode(omode); | 
|  | c->flag |= COPEN; | 
|  | c->offset = 0; | 
|  | return c; | 
|  | #endif | 
|  | } | 
|  | if (QID(c->qid) == Qtracepids) { | 
|  | error("Proc: Qtracepids: not yet"); | 
|  | #if 0 | 
|  | if (omode != OREAD) | 
|  | error(Eperm); | 
|  | c->mode = openmode(omode); | 
|  | c->flag |= COPEN; | 
|  | c->offset = 0; | 
|  | return c; | 
|  | #endif | 
|  | } | 
|  | if ((p = pid2proc(SLOT(c->qid))) == NULL) | 
|  | error(Eprocdied); | 
|  | //qlock(&p->debug); | 
|  | if (waserror()) { | 
|  | //qunlock(&p->debug); | 
|  | kref_put(&p->p_kref); | 
|  | nexterror(); | 
|  | } | 
|  | pid = PID(c->qid); | 
|  | if (p->pid != pid) | 
|  | error(Eprocdied); | 
|  |  | 
|  | omode = openmode(omode); | 
|  |  | 
|  | switch (QID(c->qid)) { | 
|  | case Qtext: | 
|  | error("notyet"); | 
|  | /* | 
|  | if (omode != OREAD) | 
|  | error(Eperm); | 
|  | tc = proctext(c, p); | 
|  | tc->offset = 0; | 
|  | poperror(); | 
|  | qunlock(&p->debug); | 
|  | kref_put(&p->p_kref); | 
|  | cclose(c); | 
|  | return tc; | 
|  | */ | 
|  | case Qproc: | 
|  | case Qsegment: | 
|  | case Qprofile: | 
|  | case Qfd: | 
|  | if (omode != OREAD) | 
|  | error(Eperm); | 
|  | break; | 
|  |  | 
|  | case Qnote: | 
|  | //          if (p->privatemem) | 
|  | error(Eperm); | 
|  | break; | 
|  |  | 
|  | case Qmem: | 
|  | //          if (p->privatemem) | 
|  | error(Eperm); | 
|  | //nonone(p); | 
|  | break; | 
|  |  | 
|  | case Qargs: | 
|  | case Qnoteid: | 
|  | case Qwait: | 
|  | case Qregs: | 
|  | case Qfpregs: | 
|  | case Qkregs: | 
|  | case Qsyscall: | 
|  | case Qcore: | 
|  | nonone(p); | 
|  | break; | 
|  |  | 
|  | case Qns: | 
|  | if (omode != OREAD) | 
|  | error(Eperm); | 
|  | c->aux = kzmalloc(sizeof(struct mntwalk), KMALLOC_WAIT); | 
|  | break; | 
|  | case Qstatus: | 
|  | case Qctl: | 
|  | break; | 
|  | case Qnotepg: | 
|  | error("not yet"); | 
|  | #if 0 | 
|  | nonone(p); | 
|  | pg = p->pgrp; | 
|  | if (pg == NULL) | 
|  | error(Eprocdied); | 
|  | if (omode != OWRITE || pg->pgrpid == 1) | 
|  | error(Eperm); | 
|  | c->pgrpid.path = pg->pgrpid + 1; | 
|  | c->pgrpid.vers = p->noteid; | 
|  | #endif | 
|  | break; | 
|  |  | 
|  | default: | 
|  | poperror(); | 
|  | //qunlock(&p->debug); | 
|  | kref_put(&p->p_kref); | 
|  | printk("procopen %#llux\n", c->qid.path); | 
|  | error(Egreg); | 
|  | } | 
|  |  | 
|  | /* Affix pid to qid */ | 
|  | //  if (p->state != Dead) | 
|  | c->qid.vers = p->pid; | 
|  | /* make sure the process slot didn't get reallocated while we were playing */ | 
|  | //coherence(); | 
|  | /* TODO: think about what we really want here.  In akaros, we wouldn't have | 
|  | * our pid changed like that. */ | 
|  | if (p->pid != pid) | 
|  | error(Eprocdied); | 
|  |  | 
|  | tc = devopen(c, omode, 0, 0, procgen); | 
|  | poperror(); | 
|  | //qunlock(&p->debug); | 
|  | kref_put(&p->p_kref); | 
|  | return tc; | 
|  | } | 
|  |  | 
|  | static int procwstat(struct chan *c, uint8_t * db, int n) | 
|  | { | 
|  | ERRSTACK(2); | 
|  | error("procwwstat: not yet"); | 
|  | #if 0 | 
|  | struct proc *p; | 
|  | struct dir *d; | 
|  |  | 
|  | if (c->qid.type & QTDIR) | 
|  | error(Eperm); | 
|  |  | 
|  | if (QID(c->qid) == Qtrace) | 
|  | return devwstat(c, db, n); | 
|  |  | 
|  | if ((p = pid2proc(SLOT(c->qid))) == NULL) | 
|  | error(Eprocdied); | 
|  | nonone(p); | 
|  | d = NULL; | 
|  | qlock(&p->debug); | 
|  | if (waserror()) { | 
|  | qunlock(&p->debug); | 
|  | kref_put(&p->p_kref); | 
|  | kfree(d); | 
|  | nexterror(); | 
|  | } | 
|  |  | 
|  | if (p->pid != PID(c->qid)) | 
|  | error(Eprocdied); | 
|  |  | 
|  | if (strcmp(current->user, p->user) != 0 && strcmp(current->user, eve) != 0) | 
|  | error(Eperm); | 
|  |  | 
|  | d = kzmalloc(sizeof(struct dir) + n, KMALLOC_WAIT); | 
|  | n = convM2D(db, n, &d[0], (char *)&d[1]); | 
|  | if (n == 0) | 
|  | error(Eshortstat); | 
|  | if (!emptystr(d->uid) && strcmp(d->uid, p->user) != 0) { | 
|  | if (strcmp(current->user, eve) != 0) | 
|  | error(Eperm); | 
|  | else | 
|  | kstrdup(&p->user, d->uid); | 
|  | } | 
|  | if (d->mode != ~0UL) | 
|  | p->procmode = d->mode & 0777; | 
|  |  | 
|  | poperror(); | 
|  | qunlock(&p->debug); | 
|  | kref_put(&p->p_kref); | 
|  | kfree(d); | 
|  |  | 
|  | return n; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #if 0 | 
|  | static long procoffset(long offset, char *va, int *np) | 
|  | { | 
|  | if (offset > 0) { | 
|  | offset -= *np; | 
|  | if (offset < 0) { | 
|  | memmove(va, va + *np + offset, -offset); | 
|  | *np = -offset; | 
|  | } else | 
|  | *np = 0; | 
|  | } | 
|  | return offset; | 
|  | } | 
|  |  | 
|  | static int procqidwidth(struct chan *c) | 
|  | { | 
|  | char buf[32]; | 
|  |  | 
|  | return sprint(buf, "%lu", c->qid.vers); | 
|  | } | 
|  |  | 
|  | int procfdprint(struct chan *c, int fd, int w, char *s, int ns) | 
|  | { | 
|  | int n; | 
|  |  | 
|  | if (w == 0) | 
|  | w = procqidwidth(c); | 
|  | n = snprint(s, ns, | 
|  | "%3d %.2s %C %4ud (%.16llux %*lud %.2ux) %5ld %8lld %s\n", fd, | 
|  | &"r w rw"[(c->mode & 3) << 1], c->dev->dc, c->devno, | 
|  | c->qid.path, w, c->qid.vers, c->qid.type, c->iounit, c->offset, | 
|  | c->name->s); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | static int procfds(struct proc *p, char *va, int count, long offset) | 
|  | { | 
|  | ERRSTACK(2); | 
|  | struct fgrp *f; | 
|  | struct chan *c; | 
|  | char buf[256]; | 
|  | int n, i, w, ww; | 
|  | char *a; | 
|  |  | 
|  | /* print to buf to avoid holding fgrp lock while writing to user space */ | 
|  | if (count > sizeof buf) | 
|  | count = sizeof buf; | 
|  | a = buf; | 
|  |  | 
|  | qlock(&p->debug); | 
|  | f = p->fgrp; | 
|  | if (f == NULL) { | 
|  | qunlock(&p->debug); | 
|  | return 0; | 
|  | } | 
|  | lock(f); | 
|  | if (waserror()) { | 
|  | unlock(f); | 
|  | qunlock(&p->debug); | 
|  | nexterror(); | 
|  | } | 
|  |  | 
|  | n = readstr(0, a, count, p->dot->name->s); | 
|  | n += snprint(a + n, count - n, "\n"); | 
|  | offset = procoffset(offset, a, &n); | 
|  | /* compute width of qid.path */ | 
|  | w = 0; | 
|  | for (i = 0; i <= f->maxfd; i++) { | 
|  | c = f->fd[i]; | 
|  | if (c == NULL) | 
|  | continue; | 
|  | ww = procqidwidth(c); | 
|  | if (ww > w) | 
|  | w = ww; | 
|  | } | 
|  | for (i = 0; i <= f->maxfd; i++) { | 
|  | c = f->fd[i]; | 
|  | if (c == NULL) | 
|  | continue; | 
|  | n += procfdprint(c, i, w, a + n, count - n); | 
|  | offset = procoffset(offset, a, &n); | 
|  | } | 
|  | poperror(); | 
|  | unlock(f); | 
|  | qunlock(&p->debug); | 
|  |  | 
|  | /* copy result to user space, now that locks are released */ | 
|  | memmove(va, buf, n); | 
|  |  | 
|  | return n; | 
|  | } | 
|  | #endif | 
|  | static void procclose(struct chan *c) | 
|  | { | 
|  | if (QID(c->qid) == Qtrace) { | 
|  | spin_lock(&tlock); | 
|  | if (topens > 0) | 
|  | topens--; | 
|  | /* ?? | 
|  | if(topens == 0) | 
|  | proctrace = notrace; | 
|  | */ | 
|  | spin_unlock(&tlock); | 
|  | } | 
|  | if (QID(c->qid) == Qns && c->aux != 0) | 
|  | kfree(c->aux); | 
|  | } | 
|  |  | 
|  | void int2flag(int flag, char *s) | 
|  | { | 
|  | if (flag == 0) { | 
|  | *s = '\0'; | 
|  | return; | 
|  | } | 
|  | *s++ = '-'; | 
|  | if (flag & MAFTER) | 
|  | *s++ = 'a'; | 
|  | if (flag & MBEFORE) | 
|  | *s++ = 'b'; | 
|  | if (flag & MCREATE) | 
|  | *s++ = 'c'; | 
|  | if (flag & MCACHE) | 
|  | *s++ = 'C'; | 
|  | *s = '\0'; | 
|  | } | 
|  |  | 
|  | #if 0 | 
|  | static char *argcpy(char *s, char *p) | 
|  | { | 
|  | char *t, *tp, *te; | 
|  | int n; | 
|  |  | 
|  | n = p - s; | 
|  | if (n > 128) | 
|  | n = 128; | 
|  | if (n <= 0) { | 
|  | t = kzmalloc(1, KMALLOC_WAIT); | 
|  | *t = 0; | 
|  | return t; | 
|  | } | 
|  | t = kzmalloc(n, KMALLOC_WAIT); | 
|  | tp = t; | 
|  | te = t + n; | 
|  |  | 
|  | while (tp + 1 < te) { | 
|  | for (p--; p > s && p[-1] != 0; p--) ; | 
|  | tp = seprint(tp, te, "%q ", p); | 
|  | if (p == s) | 
|  | break; | 
|  | } | 
|  | if (*tp == ' ') | 
|  | *tp = 0; | 
|  | return t; | 
|  | } | 
|  |  | 
|  | static int procargs(struct proc *p, char *buf, int nbuf) | 
|  | { | 
|  | char *s; | 
|  |  | 
|  | if (p->setargs == 0) { | 
|  | s = argcpy(p->args, p->args + p->nargs); | 
|  | kfree(p->args); | 
|  | p->nargs = strlen(s); | 
|  | p->args = s; | 
|  | p->setargs = 1; | 
|  | } | 
|  | return snprint(buf, nbuf, "%s", p->args); | 
|  | } | 
|  |  | 
|  | static int eventsavailable(void *) | 
|  | { | 
|  | return tproduced > tconsumed; | 
|  | } | 
|  | #endif | 
|  | static long procread(struct chan *c, void *va, long n, int64_t off) | 
|  | { | 
|  | ERRSTACK(5); | 
|  | struct proc *p; | 
|  | long l, r; | 
|  | int i, j, navail, pid, rsize; | 
|  | char flag[10], *sps, *srv, statbuf[512]; | 
|  | uintptr_t offset, u; | 
|  | int tesz; | 
|  | uint8_t *rptr; | 
|  | struct mntwalk *mw; | 
|  |  | 
|  | if (c->qid.type & QTDIR) { | 
|  | int nn; | 
|  | printd("procread: dir\n"); | 
|  | nn = devdirread(c, va, n, 0, 0, procgen); | 
|  | printd("procread: %d\n", nn); | 
|  | return nn; | 
|  | } | 
|  |  | 
|  | offset = off; | 
|  | #if 0 | 
|  | if (QID(c->qid) == Qtrace) { | 
|  | if (!eventsavailable(NULL)) | 
|  | return 0; | 
|  |  | 
|  | rptr = va; | 
|  | tesz = BIT32SZ + BIT32SZ + BIT64SZ + BIT32SZ; | 
|  | navail = tproduced - tconsumed; | 
|  | if (navail > n / tesz) | 
|  | navail = n / tesz; | 
|  | while (navail > 0) { | 
|  | PBIT32(rptr, tevents[tconsumed & Emask].pid); | 
|  | rptr += BIT32SZ; | 
|  | PBIT32(rptr, tevents[tconsumed & Emask].etype); | 
|  | rptr += BIT32SZ; | 
|  | PBIT64(rptr, tevents[tconsumed & Emask].time); | 
|  | rptr += BIT64SZ; | 
|  | PBIT32(rptr, tevents[tconsumed & Emask].core); | 
|  | rptr += BIT32SZ; | 
|  | tconsumed++; | 
|  | navail--; | 
|  | } | 
|  | return rptr - (uint8_t *) va; | 
|  | } | 
|  |  | 
|  | if (QID(c->qid) == Qtracepids) | 
|  | if (tpids == NULL) | 
|  | return 0; | 
|  | else | 
|  | return readstr(off, va, n, tpids); | 
|  | #endif | 
|  | if ((p = pid2proc(SLOT(c->qid))) == NULL) | 
|  | error(Eprocdied); | 
|  | if (p->pid != PID(c->qid)) { | 
|  | kref_put(&p->p_kref); | 
|  | error(Eprocdied); | 
|  | } | 
|  | switch (QID(c->qid)) { | 
|  | default: | 
|  | kref_put(&p->p_kref); | 
|  | break; | 
|  | #if 0 | 
|  | #warning check refcnting in here | 
|  | case Qargs: | 
|  | qlock(&p->debug); | 
|  | j = procargs(p, current->genbuf, sizeof current->genbuf); | 
|  | qunlock(&p->debug); | 
|  | kref_put(&p->p_kref); | 
|  | if (offset >= j) | 
|  | return 0; | 
|  | if (offset + n > j) | 
|  | n = j - offset; | 
|  | memmove(va, ¤t->genbuf[offset], n); | 
|  | return n; | 
|  |  | 
|  | case Qsyscall: | 
|  | if (p->syscalltrace == NULL) | 
|  | return 0; | 
|  | return readstr(offset, va, n, p->syscalltrace); | 
|  |  | 
|  | case Qcore: | 
|  | i = 0; | 
|  | ac = p->ac; | 
|  | wired = p->wired; | 
|  | if (ac != NULL) | 
|  | i = ac->machno; | 
|  | else if (wired != NULL) | 
|  | i = wired->machno; | 
|  | snprint(statbuf, sizeof statbuf, "%d\n", i); | 
|  | return readstr(offset, va, n, statbuf); | 
|  |  | 
|  | case Qmem: | 
|  | if (offset < KZERO | 
|  | || (offset >= USTKTOP - USTKSIZE && offset < USTKTOP)) { | 
|  | r = procctlmemio(p, offset, n, va, 1); | 
|  | kref_put(&p->p_kref); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | if (!iseve()) { | 
|  | kref_put(&p->p_kref); | 
|  | error(Eperm); | 
|  | } | 
|  |  | 
|  | /* validate kernel addresses */ | 
|  | if (offset < PTR2UINT(end)) { | 
|  | if (offset + n > PTR2UINT(end)) | 
|  | n = PTR2UINT(end) - offset; | 
|  | memmove(va, UINT2PTR(offset), n); | 
|  | kref_put(&p->p_kref); | 
|  | return n; | 
|  | } | 
|  | for (i = 0; i < nelem(conf.mem); i++) { | 
|  | cm = &conf.mem[i]; | 
|  | /* klimit-1 because klimit might be zero! */ | 
|  | if (cm->kbase <= offset && offset <= cm->klimit - 1) { | 
|  | if (offset + n >= cm->klimit - 1) | 
|  | n = cm->klimit - offset; | 
|  | memmove(va, UINT2PTR(offset), n); | 
|  | kref_put(&p->p_kref); | 
|  | return n; | 
|  | } | 
|  | } | 
|  | kref_put(&p->p_kref); | 
|  | error(Ebadarg); | 
|  |  | 
|  | case Qprofile: | 
|  | s = p->seg[TSEG]; | 
|  | if (s == 0 || s->profile == 0) | 
|  | error("profile is off"); | 
|  | i = (s->top - s->base) >> LRESPROF; | 
|  | i *= sizeof(*s->profile); | 
|  | if (offset >= i) { | 
|  | kref_put(&p->p_kref); | 
|  | return 0; | 
|  | } | 
|  | if (offset + n > i) | 
|  | n = i - offset; | 
|  | memmove(va, ((char *)s->profile) + offset, n); | 
|  | kref_put(&p->p_kref); | 
|  | return n; | 
|  |  | 
|  | case Qnote: | 
|  | qlock(&p->debug); | 
|  | if (waserror()) { | 
|  | qunlock(&p->debug); | 
|  | kref_put(&p->p_kref); | 
|  | nexterror(); | 
|  | } | 
|  | if (p->pid != PID(c->qid)) | 
|  | error(Eprocdied); | 
|  | if (n < 1)	/* must accept at least the '\0' */ | 
|  | error(Etoosmall); | 
|  | if (p->nnote == 0) | 
|  | n = 0; | 
|  | else { | 
|  | i = strlen(p->note[0].msg) + 1; | 
|  | if (i > n) | 
|  | i = n; | 
|  | rptr = va; | 
|  | memmove(rptr, p->note[0].msg, i); | 
|  | rptr[i - 1] = '\0'; | 
|  | p->nnote--; | 
|  | memmove(p->note, p->note + 1, p->nnote * sizeof(Note)); | 
|  | n = i; | 
|  | } | 
|  | if (p->nnote == 0) | 
|  | p->notepending = 0; | 
|  | poperror(); | 
|  | qunlock(&p->debug); | 
|  | kref_put(&p->p_kref); | 
|  | return n; | 
|  |  | 
|  | case Qproc: | 
|  | if (offset >= sizeof(struct proc)) { | 
|  | kref_put(&p->p_kref); | 
|  | return 0; | 
|  | } | 
|  | if (offset + n > sizeof(struct proc)) | 
|  | n = sizeof(struct proc) - offset; | 
|  | memmove(va, ((char *)p) + offset, n); | 
|  | kref_put(&p->p_kref); | 
|  | return n; | 
|  |  | 
|  | case Qregs: | 
|  | rptr = (uint8_t *) p->dbgreg; | 
|  | rsize = sizeof(Ureg); | 
|  | regread: | 
|  | if (rptr == 0) { | 
|  | kref_put(&p->p_kref); | 
|  | error(Enoreg); | 
|  | } | 
|  | if (offset >= rsize) { | 
|  | kref_put(&p->p_kref); | 
|  | return 0; | 
|  | } | 
|  | if (offset + n > rsize) | 
|  | n = rsize - offset; | 
|  | memmove(va, rptr + offset, n); | 
|  | kref_put(&p->p_kref); | 
|  | return n; | 
|  |  | 
|  | case Qkregs: | 
|  | memset(&kur, 0, sizeof(Ureg)); | 
|  | setkernur(&kur, p); | 
|  | rptr = (uint8_t *) & kur; | 
|  | rsize = sizeof(Ureg); | 
|  | goto regread; | 
|  |  | 
|  | case Qfpregs: | 
|  | r = fpudevprocio(p, va, n, offset, 0); | 
|  | kref_put(&p->p_kref); | 
|  | return r; | 
|  |  | 
|  | case Qstatus: | 
|  | if (offset >= STATSIZE) { | 
|  | kref_put(&p->p_kref); | 
|  | return 0; | 
|  | } | 
|  | if (offset + n > STATSIZE) | 
|  | n = STATSIZE - offset; | 
|  |  | 
|  | sps = p->psstate; | 
|  | if (sps == 0) | 
|  | sps = statename[p->state]; | 
|  | memset(statbuf, ' ', sizeof statbuf); | 
|  | j = 2 * KNAMELEN + 12; | 
|  | snprint(statbuf, j + 1, "%-*.*s%-*.*s%-12.11s", | 
|  | KNAMELEN, KNAMELEN - 1, p->text, | 
|  | KNAMELEN, KNAMELEN - 1, p->user, sps); | 
|  |  | 
|  | for (i = 0; i < 6; i++) { | 
|  | l = p->time[i]; | 
|  | if (i == TReal) | 
|  | l = sys->ticks - l; | 
|  | l = TK2MS(l); | 
|  | readnum(0, statbuf + j + NUMSIZE * i, NUMSIZE, l, NUMSIZE); | 
|  | } | 
|  | /* ignore stack, which is mostly non-existent */ | 
|  | u = 0; | 
|  | for (i = 1; i < NSEG; i++) { | 
|  | s = p->seg[i]; | 
|  | if (s) | 
|  | u += s->top - s->base; | 
|  | } | 
|  | readnum(0, statbuf + j + NUMSIZE * 6, NUMSIZE, u >> 10u, NUMSIZE);	/* wrong size */ | 
|  | readnum(0, statbuf + j + NUMSIZE * 7, NUMSIZE, p->basepri, NUMSIZE); | 
|  | readnum(0, statbuf + j + NUMSIZE * 8, NUMSIZE, p->priority, | 
|  | NUMSIZE); | 
|  |  | 
|  | /* | 
|  | * NIX: added # of traps, syscalls, and iccs | 
|  | */ | 
|  | readnum(0, statbuf + j + NUMSIZE * 9, NUMSIZE, p->ntrap, NUMSIZE); | 
|  | readnum(0, statbuf + j + NUMSIZE * 10, NUMSIZE, p->nintr, NUMSIZE); | 
|  | readnum(0, statbuf + j + NUMSIZE * 11, NUMSIZE, p->nsyscall, | 
|  | NUMSIZE); | 
|  | readnum(0, statbuf + j + NUMSIZE * 12, NUMSIZE, p->nicc, NUMSIZE); | 
|  | readnum(0, statbuf + j + NUMSIZE * 13, NUMSIZE, p->nactrap, | 
|  | NUMSIZE); | 
|  | readnum(0, statbuf + j + NUMSIZE * 14, NUMSIZE, p->nacsyscall, | 
|  | NUMSIZE); | 
|  | memmove(va, statbuf + offset, n); | 
|  | kref_put(&p->p_kref); | 
|  | return n; | 
|  |  | 
|  | case Qsegment: | 
|  | j = 0; | 
|  | for (i = 0; i < NSEG; i++) { | 
|  | sg = p->seg[i]; | 
|  | if (sg == 0) | 
|  | continue; | 
|  | j += sprint(statbuf + j, "%-6s %c%c %p %p %4d\n", | 
|  | sname[sg->type & SG_TYPE], | 
|  | sg->type & SG_RONLY ? 'R' : ' ', | 
|  | sg->profile ? 'P' : ' ', | 
|  | sg->base, sg->top, sg->ref); | 
|  | } | 
|  | kref_put(&p->p_kref); | 
|  | if (offset >= j) | 
|  | return 0; | 
|  | if (offset + n > j) | 
|  | n = j - offset; | 
|  | if (n == 0 && offset == 0) | 
|  | exhausted("segments"); | 
|  | memmove(va, &statbuf[offset], n); | 
|  | return n; | 
|  |  | 
|  | case Qwait: | 
|  | if (!canqlock(&p->qwaitr)) { | 
|  | kref_put(&p->p_kref); | 
|  | error(Einuse); | 
|  | } | 
|  |  | 
|  | if (waserror()) { | 
|  | qunlock(&p->qwaitr); | 
|  | kref_put(&p->p_kref); | 
|  | nexterror(); | 
|  | } | 
|  |  | 
|  | lock(&p->exl); | 
|  | if (up == p && p->nchild == 0 && p->waitq == 0) { | 
|  | unlock(&p->exl); | 
|  | error(Enochild); | 
|  | } | 
|  | pid = p->pid; | 
|  | while (p->waitq == 0) { | 
|  | unlock(&p->exl); | 
|  | rendez_sleep(&p->waitr, haswaitq, p); | 
|  | if (p->pid != pid) | 
|  | error(Eprocdied); | 
|  | lock(&p->exl); | 
|  | } | 
|  | wq = p->waitq; | 
|  | p->waitq = wq->next; | 
|  | p->nwait--; | 
|  | unlock(&p->exl); | 
|  |  | 
|  | poperror(); | 
|  | qunlock(&p->qwaitr); | 
|  | kref_put(&p->p_kref); | 
|  | n = snprint(va, n, "%d %lu %lud %lud %q", | 
|  | wq->w.pid, | 
|  | wq->w.time[TUser], wq->w.time[TSys], wq->w.time[TReal], | 
|  | wq->w.msg); | 
|  | kfree(wq); | 
|  | return n; | 
|  | #endif | 
|  | case Qstatus:{ | 
|  | /* the extra 2 is paranoia */ | 
|  | char buf[8 + 1 + PROC_PROGNAME_SZ + 1 + 10 + 1 + 6 + 2]; | 
|  | snprintf(buf, sizeof(buf), | 
|  | "%8d %-*s %-10s %6d", p->pid, PROC_PROGNAME_SZ, | 
|  | p->progname, procstate2str(p->state), | 
|  | p->ppid); | 
|  | kref_put(&p->p_kref); | 
|  | return readstr(off, va, n, buf); | 
|  | } | 
|  |  | 
|  | case Qns: | 
|  | //qlock(&p->debug); | 
|  | if (waserror()) { | 
|  | //qunlock(&p->debug); | 
|  | kref_put(&p->p_kref); | 
|  | nexterror(); | 
|  | } | 
|  | if (p->pgrp == NULL || p->pid != PID(c->qid)) | 
|  | error(Eprocdied); | 
|  | mw = c->aux; | 
|  | if (mw->cddone) { | 
|  | poperror(); | 
|  | //qunlock(&p->debug); | 
|  | kref_put(&p->p_kref); | 
|  | return 0; | 
|  | } | 
|  | mntscan(mw, p); | 
|  | if (mw->mh == 0) { | 
|  | mw->cddone = 1; | 
|  | i = snprintf(va, n, "cd %s\n", p->dot->name->s); | 
|  | poperror(); | 
|  | //qunlock(&p->debug); | 
|  | kref_put(&p->p_kref); | 
|  | return i; | 
|  | } | 
|  | int2flag(mw->cm->mflag, flag); | 
|  | if (strcmp(mw->cm->to->name->s, "#M") == 0) { | 
|  | srv = srvname(mw->cm->to->mchan); | 
|  | i = snprintf(va, n, "mount %s %s %s %s\n", flag, | 
|  | srv == NULL ? mw->cm->to->mchan->name->s : srv, | 
|  | mw->mh->from->name->s, | 
|  | mw->cm->spec ? mw->cm->spec : ""); | 
|  | kfree(srv); | 
|  | } else | 
|  | i = snprintf(va, n, "bind %s %s %s\n", flag, | 
|  | mw->cm->to->name->s, mw->mh->from->name->s); | 
|  | poperror(); | 
|  | //qunlock(&p->debug); | 
|  | kref_put(&p->p_kref); | 
|  | return i; | 
|  | #if 0 | 
|  | case Qnoteid: | 
|  | r = readnum(offset, va, n, p->noteid, NUMSIZE); | 
|  | kref_put(&p->p_kref); | 
|  | return r; | 
|  | case Qfd: | 
|  | r = procfds(p, va, n, offset); | 
|  | kref_put(&p->p_kref); | 
|  | return r; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | error(Egreg); | 
|  | return 0;	/* not reached */ | 
|  | } | 
|  |  | 
|  | static void mntscan(struct mntwalk *mw, struct proc *p) | 
|  | { | 
|  | struct pgrp *pg; | 
|  | struct mount *t; | 
|  | struct mhead *f; | 
|  | int best, i, last, nxt; | 
|  |  | 
|  | pg = p->pgrp; | 
|  | rlock(&pg->ns); | 
|  |  | 
|  | nxt = 0; | 
|  | best = (int)(~0U >> 1);	/* largest 2's complement int */ | 
|  |  | 
|  | last = 0; | 
|  | if (mw->mh) | 
|  | last = mw->cm->mountid; | 
|  |  | 
|  | for (i = 0; i < MNTHASH; i++) { | 
|  | for (f = pg->mnthash[i]; f; f = f->hash) { | 
|  | for (t = f->mount; t; t = t->next) { | 
|  | if (mw->mh == 0 || (t->mountid > last && t->mountid < best)) { | 
|  | mw->cm = t; | 
|  | mw->mh = f; | 
|  | best = mw->cm->mountid; | 
|  | nxt = 1; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | if (nxt == 0) | 
|  | mw->mh = 0; | 
|  |  | 
|  | runlock(&pg->ns); | 
|  | } | 
|  |  | 
|  | static long procwrite(struct chan *c, void *va, long n, int64_t off) | 
|  | { | 
|  | ERRSTACK(2); | 
|  |  | 
|  | struct proc *p, *t; | 
|  | int i, id, l; | 
|  | char *args; | 
|  | uintptr_t offset; | 
|  |  | 
|  | if (c->qid.type & QTDIR) | 
|  | error(Eisdir); | 
|  |  | 
|  | if ((p = pid2proc(SLOT(c->qid))) == NULL) | 
|  | error(Eprocdied); | 
|  |  | 
|  | if (waserror()) { | 
|  | kref_put(&p->p_kref); | 
|  | nexterror(); | 
|  | } | 
|  | if (p->pid != PID(c->qid)) | 
|  | error(Eprocdied); | 
|  |  | 
|  | offset = off; | 
|  |  | 
|  | switch (QID(c->qid)) { | 
|  | #if 0 | 
|  | case Qargs: | 
|  | if (n == 0) | 
|  | error(Eshort); | 
|  | if (n >= sizeof buf - strlen(p->text) - 1) | 
|  | error(Etoobig); | 
|  | l = snprintf(buf, sizeof buf, "%s [%s]", p->text, (char *)va); | 
|  | args = kzmalloc(l + 1, KMALLOC_WAIT); | 
|  | if (args == NULL) | 
|  | error(Enomem); | 
|  | memmove(args, buf, l); | 
|  | args[l] = 0; | 
|  | kfree(p->args); | 
|  | p->nargs = l; | 
|  | p->args = args; | 
|  | p->setargs = 1; | 
|  | break; | 
|  |  | 
|  | case Qmem: | 
|  | if (p->state != Stopped) | 
|  | error(Ebadctl); | 
|  |  | 
|  | n = procctlmemio(p, offset, n, va, 0); | 
|  | break; | 
|  |  | 
|  | case Qregs: | 
|  | if (offset >= sizeof(Ureg)) | 
|  | n = 0; | 
|  | else if (offset + n > sizeof(Ureg)) | 
|  | n = sizeof(Ureg) - offset; | 
|  | if (p->dbgreg == 0) | 
|  | error(Enoreg); | 
|  | setregisters(p->dbgreg, (char *)(p->dbgreg) + offset, va, n); | 
|  | break; | 
|  |  | 
|  | case Qfpregs: | 
|  | n = fpudevprocio(p, va, n, offset, 1); | 
|  | break; | 
|  | #endif | 
|  | case Qctl: | 
|  | procctlreq(p, va, n); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | poperror(); | 
|  | kref_put(&p->p_kref); | 
|  | error("unknown qid %#llux in procwrite\n", c->qid.path); | 
|  | } | 
|  | poperror(); | 
|  | kref_put(&p->p_kref); | 
|  | return n; | 
|  |  | 
|  | } | 
|  |  | 
|  | struct dev procdevtab __devtab = { | 
|  | 'p', | 
|  | "proc", | 
|  |  | 
|  | devreset, | 
|  | procinit, | 
|  | devshutdown, | 
|  | procattach, | 
|  | procwalk, | 
|  | procstat, | 
|  | procopen, | 
|  | devcreate, | 
|  | procclose, | 
|  | procread, | 
|  | devbread, | 
|  | procwrite, | 
|  | devbwrite, | 
|  | devremove, | 
|  | procwstat, | 
|  | devpower, | 
|  | devchaninfo, | 
|  | }; | 
|  |  | 
|  | #if 0 | 
|  | static struct chan *proctext(struct chan *c, struct proc *p) | 
|  | { | 
|  | ERRSTACK(2); | 
|  | struct chan *tc; | 
|  | Image *i; | 
|  | Segment *s; | 
|  |  | 
|  | s = p->seg[TSEG]; | 
|  | if (s == 0) | 
|  | error(Enonexist); | 
|  | if (p->state == Dead) | 
|  | error(Eprocdied); | 
|  |  | 
|  | lock(s); | 
|  | i = s->image; | 
|  | if (i == 0) { | 
|  | unlock(s); | 
|  | error(Eprocdied); | 
|  | } | 
|  | unlock(s); | 
|  |  | 
|  | lock(i); | 
|  | if (waserror()) { | 
|  | unlock(i); | 
|  | nexterror(); | 
|  | } | 
|  |  | 
|  | tc = i->c; | 
|  | if (tc == 0) | 
|  | error(Eprocdied); | 
|  |  | 
|  | /* TODO: what do you want here?  you can't get a kref and have the new val | 
|  | * be 1.  Here is the old code: if (kref_get(&tc->ref, 1) == 1 || ... ) */ | 
|  | if (kref_refcnt(&tc->ref, 1) == 1 || (tc->flag & COPEN) == 0 | 
|  | || tc->mode != OREAD) { | 
|  | cclose(tc); | 
|  | error(Eprocdied); | 
|  | } | 
|  |  | 
|  | if (p->pid != PID(c->qid)) { | 
|  | cclose(tc); | 
|  | error(Eprocdied); | 
|  | } | 
|  |  | 
|  | poperror(); | 
|  | unlock(i); | 
|  |  | 
|  | return tc; | 
|  | } | 
|  |  | 
|  | /* TODO: this will fail at compile time, since we don't have a proc-wide rendez, | 
|  | * among other things, and we'll need to rewrite this for akaros */ | 
|  | void procstopwait(struct proc *p, int ctl) | 
|  | { | 
|  | ERRSTACK(2); | 
|  | int pid; | 
|  |  | 
|  | if (p->pdbg) | 
|  | error(Einuse); | 
|  | if (procstopped(p) || p->state == Broken) | 
|  | return; | 
|  |  | 
|  | if (ctl != 0) | 
|  | p->procctl = ctl; | 
|  | p->pdbg = up; | 
|  | pid = p->pid; | 
|  | qunlock(&p->debug); | 
|  | current->psstate = "Stopwait"; | 
|  | if (waserror()) { | 
|  | p->pdbg = 0; | 
|  | qlock(&p->debug); | 
|  | nexterror(); | 
|  | } | 
|  | rendez_sleep(¤t->sleep, procstopped, p); | 
|  | poperror(); | 
|  | qlock(&p->debug); | 
|  | if (p->pid != pid) | 
|  | error(Eprocdied); | 
|  | } | 
|  |  | 
|  | #endif | 
|  | static void procctlcloseone(struct proc *p, int fd) | 
|  | { | 
|  | // TODO: resolve this and sys_close | 
|  | struct file *file = get_file_from_fd(&p->open_files, fd); | 
|  | int retval = 0; | 
|  | printd("%s %d\n", __func__, fd); | 
|  | /* VFS */ | 
|  | if (file) { | 
|  | put_file_from_fd(&p->open_files, fd); | 
|  | kref_put(&file->f_kref);	/* Drop the ref from get_file */ | 
|  | return; | 
|  | } | 
|  | /* 9ns, should also handle errors (bad FD, etc) */ | 
|  | retval = sysclose(fd); | 
|  | return; | 
|  |  | 
|  | //sys_close(p, fd); | 
|  | } | 
|  |  | 
|  | void procctlclosefiles(struct proc *p, int all, int fd) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if (all) | 
|  | for (i = 0; i < NR_FILE_DESC_MAX; i++) | 
|  | procctlcloseone(p, i); | 
|  | else | 
|  | procctlcloseone(p, fd); | 
|  | } | 
|  |  | 
|  | static void procctlreq(struct proc *p, char *va, int n) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | int8_t irq_state = 0; | 
|  | int npc, pri, core; | 
|  | struct cmdbuf *cb; | 
|  | struct cmdtab *ct; | 
|  | int64_t time; | 
|  | char *e; | 
|  |  | 
|  | cb = parsecmd(va, n); | 
|  | if (waserror()) { | 
|  | kfree(cb); | 
|  | nexterror(); | 
|  | } | 
|  |  | 
|  | ct = lookupcmd(cb, proccmd, ARRAY_SIZE(proccmd)); | 
|  |  | 
|  | switch (ct->index) { | 
|  | case CMvmstart: | 
|  | case CMvmkill: | 
|  | default: | 
|  | error("nope\n"); | 
|  | break; | 
|  | case CMtrace: | 
|  | systrace_trace_pid(p); | 
|  | break; | 
|  | case CMclose: | 
|  | procctlclosefiles(p, 0, atoi(cb->f[1])); | 
|  | break; | 
|  | case CMclosefiles: | 
|  | procctlclosefiles(p, 1, 0); | 
|  | break; | 
|  | #if 0 | 
|  | we may want this.Let us pause a proc.case CMhang:p->hang = 1; | 
|  | break; | 
|  | #endif | 
|  | case CMkill: | 
|  | p = pid2proc(strtol(cb->f[1], 0, 0)); | 
|  | if (!p) | 
|  | error("No such proc\n"); | 
|  |  | 
|  | enable_irqsave(&irq_state); | 
|  | proc_destroy(p); | 
|  | disable_irqsave(&irq_state); | 
|  | proc_decref(p); | 
|  | /* this is a little ghetto. it's not fully free yet, but we are also | 
|  | * slowing it down by messing with it, esp with the busy waiting on a | 
|  | * hyperthreaded core. */ | 
|  | spin_on(p->env_cr3); | 
|  | break; | 
|  | case CMvminit: | 
|  | break; | 
|  | } | 
|  | poperror(); | 
|  | kfree(cb); | 
|  | } | 
|  |  | 
|  | #if 0 | 
|  | static int procstopped(void *a) | 
|  | { | 
|  | struct proc *p = a; | 
|  | return p->state == Stopped; | 
|  | } | 
|  |  | 
|  | static int | 
|  | procctlmemio(struct proc *p, uintptr_t offset, int n, void *va, int read) | 
|  | { | 
|  | KMap *k; | 
|  | Pte *pte; | 
|  | Page *pg; | 
|  | Segment *s; | 
|  | uintptr_t soff, l;			/* hmmmm */ | 
|  | uint8_t *b; | 
|  | uintmem pgsz; | 
|  |  | 
|  | for (;;) { | 
|  | s = seg(p, offset, 1); | 
|  | if (s == 0) | 
|  | error(Ebadarg); | 
|  |  | 
|  | if (offset + n >= s->top) | 
|  | n = s->top - offset; | 
|  |  | 
|  | if (!read && (s->type & SG_TYPE) == SG_TEXT) | 
|  | s = txt2data(p, s); | 
|  |  | 
|  | s->steal++; | 
|  | soff = offset - s->base; | 
|  | if (waserror()) { | 
|  | s->steal--; | 
|  | nexterror(); | 
|  | } | 
|  | if (fixfault(s, offset, read, 0, s->color) == 0) | 
|  | break; | 
|  | poperror(); | 
|  | s->steal--; | 
|  | } | 
|  | poperror(); | 
|  | pte = s->map[soff / PTEMAPMEM]; | 
|  | if (pte == 0) | 
|  | panic("procctlmemio"); | 
|  | pgsz = m->pgsz[s->pgszi]; | 
|  | pg = pte->pages[(soff & (PTEMAPMEM - 1)) / pgsz]; | 
|  | if (pagedout(pg)) | 
|  | panic("procctlmemio1"); | 
|  |  | 
|  | l = pgsz - (offset & (pgsz - 1)); | 
|  | if (n > l) | 
|  | n = l; | 
|  |  | 
|  | k = kmap(pg); | 
|  | if (waserror()) { | 
|  | s->steal--; | 
|  | kunmap(k); | 
|  | nexterror(); | 
|  | } | 
|  | b = (uint8_t *) VA(k); | 
|  | b += offset & (pgsz - 1); | 
|  | if (read == 1) | 
|  | memmove(va, b, n);	/* This can fault */ | 
|  | else | 
|  | memmove(b, va, n); | 
|  | poperror(); | 
|  | kunmap(k); | 
|  |  | 
|  | /* Ensure the process sees text page changes */ | 
|  | if (s->flushme) | 
|  | memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl)); | 
|  |  | 
|  | s->steal--; | 
|  |  | 
|  | if (read == 0) | 
|  | p->newtlb = 1; | 
|  |  | 
|  | return n; | 
|  | } | 
|  |  | 
|  | static Segment *txt2data(struct proc *p, Segment * s) | 
|  | { | 
|  | int i; | 
|  | Segment *ps; | 
|  |  | 
|  | ps = newseg(SG_DATA, s->base, s->size); | 
|  | ps->image = s->image; | 
|  | kref_get(&ps->image->ref, 1); | 
|  | ps->fstart = s->fstart; | 
|  | ps->flen = s->flen; | 
|  | ps->flushme = 1; | 
|  |  | 
|  | qlock(&p->seglock); | 
|  | for (i = 0; i < NSEG; i++) | 
|  | if (p->seg[i] == s) | 
|  | break; | 
|  | if (i == NSEG) | 
|  | panic("segment gone"); | 
|  |  | 
|  | qunlock(&s->lk); | 
|  | putseg(s); | 
|  | qlock(&ps->lk); | 
|  | p->seg[i] = ps; | 
|  | qunlock(&p->seglock); | 
|  |  | 
|  | return ps; | 
|  | } | 
|  |  | 
|  | Segment *data2txt(Segment * s) | 
|  | { | 
|  | Segment *ps; | 
|  |  | 
|  | ps = newseg(SG_TEXT, s->base, s->size); | 
|  | ps->image = s->image; | 
|  | kref_get(&ps->image->ref, 1); | 
|  | ps->fstart = s->fstart; | 
|  | ps->flen = s->flen; | 
|  | ps->flushme = 1; | 
|  |  | 
|  | return ps; | 
|  | } | 
|  | #endif |