|  | /* | 
|  | * 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 <assert.h> | 
|  | #include <cpio.h> | 
|  | #include <error.h> | 
|  | #include <kmalloc.h> | 
|  | #include <kref.h> | 
|  | #include <pmap.h> | 
|  | #include <ros/vmm.h> | 
|  | #include <slab.h> | 
|  | #include <smp.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <umem.h> | 
|  |  | 
|  | #include <arch/vmm/vmm.h> | 
|  |  | 
|  | struct dev procdevtab; | 
|  |  | 
|  | static char *devname(void) | 
|  | { | 
|  | return procdevtab.name; | 
|  | } | 
|  |  | 
|  | enum { Qdir, | 
|  | Qtrace, | 
|  | Qtracepids, | 
|  | Qself, | 
|  | Qns, | 
|  | Qargs, | 
|  | Qctl, | 
|  | Qfd, | 
|  | Qfpregs, | 
|  | Qkregs, | 
|  | Qmaps, | 
|  | Qmem, | 
|  | Qnote, | 
|  | Qnoteid, | 
|  | Qnotepg, | 
|  | Qproc, | 
|  | Qregs, | 
|  | Quser, | 
|  | Qsegment, | 
|  | Qstatus, | 
|  | Qstrace, | 
|  | Qstrace_traceset, | 
|  | Qvmstatus, | 
|  | Qtext, | 
|  | Qwait, | 
|  | Qprofile, | 
|  | Qsyscall, | 
|  | Qcore, | 
|  | }; | 
|  |  | 
|  | enum { CMclose, | 
|  | CMclosefiles, | 
|  | CMhang, | 
|  | CMstraceme, | 
|  | CMstraceall, | 
|  | CMstrace_drop, | 
|  | }; | 
|  |  | 
|  | 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}, | 
|  | {"maps", {Qmaps}, 0, 0000}, | 
|  | {"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}, | 
|  | {"user", {Quser}, 0, 0444}, | 
|  | {"segment", {Qsegment}, 0, 0444}, | 
|  | {"status", {Qstatus}, STATSIZE, 0444}, | 
|  | {"strace", {Qstrace}, 0, 0444}, | 
|  | {"strace_traceset", {Qstrace_traceset}, 0, 0666}, | 
|  | {"vmstatus", {Qvmstatus}, 0, 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", 0}, | 
|  | {CMhang, "hang", 0},           {CMstraceme, "straceme", 0}, | 
|  | {CMstraceall, "straceall", 0}, {CMstrace_drop, "strace_drop", 2}, | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * 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 *, size_t); | 
|  | 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, devname(), 0, eve.name, 0555, dp); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | if (c->qid.path == Qdir) { | 
|  | if (s == 0) { | 
|  | strlcpy(get_cur_genbuf(), "trace", GENBUF_SZ); | 
|  | mkqid(&qid, Qtrace, -1, QTFILE); | 
|  | devdir(c, qid, get_cur_genbuf(), 0, eve.name, 0444, dp); | 
|  | return 1; | 
|  | } | 
|  | if (s == 1) { | 
|  | strlcpy(get_cur_genbuf(), "tracepids", GENBUF_SZ); | 
|  | mkqid(&qid, Qtracepids, -1, QTFILE); | 
|  | devdir(c, qid, get_cur_genbuf(), 0, eve.name, 0444, dp); | 
|  | return 1; | 
|  | } | 
|  | if (s == 2) { | 
|  | p = current; | 
|  | strlcpy(get_cur_genbuf(), "self", GENBUF_SZ); | 
|  | mkqid(&qid, (p->pid + 1) << QSHIFT, p->pid, QTDIR); | 
|  | devdir(c, qid, get_cur_genbuf(), 0, p->user.name, | 
|  | DMDIR | 0555, dp); | 
|  | return 1; | 
|  | } | 
|  | s -= 3; | 
|  | 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); | 
|  | proc_decref(p); | 
|  | return -1; | 
|  | } | 
|  | mkqid(&qid, (s + 1) << QSHIFT, pid, QTDIR); | 
|  | devdir(c, qid, get_cur_genbuf(), 0, p->user.name, DMDIR | 0555, | 
|  | dp); | 
|  | proc_decref(p); | 
|  | return 1; | 
|  | } | 
|  | if (c->qid.path == Qtrace) { | 
|  | strlcpy(get_cur_genbuf(), "trace", GENBUF_SZ); | 
|  | mkqid(&qid, Qtrace, -1, QTFILE); | 
|  | devdir(c, qid, get_cur_genbuf(), 0, eve.name, 0444, dp); | 
|  | return 1; | 
|  | } | 
|  | if (c->qid.path == Qtracepids) { | 
|  | strlcpy(get_cur_genbuf(), "tracepids", GENBUF_SZ); | 
|  | mkqid(&qid, Qtracepids, -1, QTFILE); | 
|  | devdir(c, qid, get_cur_genbuf(), 0, eve.name, 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.name, perm, dp); | 
|  | proc_decref(p); | 
|  | 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(devname(), spec); | 
|  | } | 
|  |  | 
|  | static struct walkqid *procwalk(struct chan *c, struct chan *nc, char **name, | 
|  | unsigned int nname) | 
|  | { | 
|  | return devwalk(c, nc, name, nname, 0, 0, procgen); | 
|  | } | 
|  |  | 
|  | static size_t procstat(struct chan *c, uint8_t *db, size_t 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.name, "none") != 0) | 
|  | return; | 
|  | if (iseve()) | 
|  | return; | 
|  | error(EPERM, ERROR_FIXME); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | struct bm_helper { | 
|  | struct sized_alloc *sza; | 
|  | size_t buflen; | 
|  | }; | 
|  |  | 
|  | static void get_needed_sz_cb(struct vm_region *vmr, void *arg) | 
|  | { | 
|  | struct bm_helper *bmh = (struct bm_helper *)arg; | 
|  |  | 
|  | /* ballpark estimate of a line */ | 
|  | bmh->buflen += 150; | 
|  | } | 
|  |  | 
|  | static void build_maps_cb(struct vm_region *vmr, void *arg) | 
|  | { | 
|  | struct bm_helper *bmh = (struct bm_helper *)arg; | 
|  | struct sized_alloc *sza = bmh->sza; | 
|  | size_t old_sofar; | 
|  | char path_buf[MAX_FILENAME_SZ]; | 
|  | char *path; | 
|  | unsigned long inode_nr; | 
|  |  | 
|  | if (vmr_has_file(vmr)) { | 
|  | path = foc_abs_path(vmr->__vm_foc); | 
|  | inode_nr = 0; /* TODO: do we care about this? */ | 
|  | } else { | 
|  | strlcpy(path_buf, "[heap]", sizeof(path_buf)); | 
|  | path = path_buf; | 
|  | inode_nr = 0; | 
|  | } | 
|  |  | 
|  | old_sofar = sza->sofar; | 
|  | sza_printf(sza, "%08lx-%08lx %c%c%c%c %08x %02d:%02d %d ", vmr->vm_base, | 
|  | vmr->vm_end, vmr->vm_prot & PROT_READ ? 'r' : '-', | 
|  | vmr->vm_prot & PROT_WRITE ? 'w' : '-', | 
|  | vmr->vm_prot & PROT_EXEC ? 'x' : '-', | 
|  | vmr->vm_flags & MAP_PRIVATE ? 'p' : 's', | 
|  | vmr_has_file(vmr) ? vmr->vm_foff : 0, | 
|  | vmr_has_file(vmr) ? 1 : 0, /* VFS == 1 for major */ | 
|  | 0, inode_nr); | 
|  | /* Align the filename to the 74th char, like Linux (73 chars so far) */ | 
|  | sza_printf(sza, "%*s", 73 - (sza->sofar - old_sofar), ""); | 
|  | sza_printf(sza, "%s\n", path); | 
|  | } | 
|  |  | 
|  | static struct sized_alloc *build_maps(struct proc *p) | 
|  | { | 
|  | struct bm_helper bmh[1]; | 
|  |  | 
|  | /* Try to figure out the size needed: start with extra space, then add a | 
|  | * bit for each VMR */ | 
|  | bmh->buflen = 150; | 
|  | enumerate_vmrs(p, get_needed_sz_cb, bmh); | 
|  | bmh->sza = sized_kzmalloc(bmh->buflen, MEM_WAIT); | 
|  | enumerate_vmrs(p, build_maps_cb, bmh); | 
|  | return bmh->sza; | 
|  | } | 
|  |  | 
|  | 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(ENOSYS, ERROR_FIXME); | 
|  | #if 0 | 
|  | if (omode != OREAD) | 
|  | error(EPERM, ERROR_FIXME); | 
|  | lock(&tlock); | 
|  | if (waserror()) { | 
|  | unlock(&tlock); | 
|  | nexterror(); | 
|  | } | 
|  | if (topens > 0) | 
|  | error(EFAIL, "already open"); | 
|  | topens++; | 
|  | if (tevents == NULL) { | 
|  | tevents = (Traceevent *) kzmalloc(sizeof(Traceevent) * Nevents, | 
|  | MEM_WAIT); | 
|  | if (tevents == NULL) | 
|  | error(ENOMEM, ERROR_FIXME); | 
|  | tpids = kzmalloc(Ntracedpids * 20, MEM_WAIT); | 
|  | if (tpids == NULL) { | 
|  | kfree(tpids); | 
|  | tpids = NULL; | 
|  | error(ENOMEM, ERROR_FIXME); | 
|  | } | 
|  | 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(ENOSYS, ERROR_FIXME); | 
|  | #if 0 | 
|  | if (omode != OREAD) | 
|  | error(EPERM, ERROR_FIXME); | 
|  | c->mode = openmode(omode); | 
|  | c->flag |= COPEN; | 
|  | c->offset = 0; | 
|  | return c; | 
|  | #endif | 
|  | } | 
|  | if ((p = pid2proc(SLOT(c->qid))) == NULL) | 
|  | error(ESRCH, ERROR_FIXME); | 
|  | // qlock(&p->debug); | 
|  | if (waserror()) { | 
|  | // qunlock(&p->debug); | 
|  | proc_decref(p); | 
|  | nexterror(); | 
|  | } | 
|  | pid = PID(c->qid); | 
|  | if (p->pid != pid) | 
|  | error(ESRCH, ERROR_FIXME); | 
|  |  | 
|  | omode = openmode(omode); | 
|  |  | 
|  | switch (QID(c->qid)) { | 
|  | case Qtext: | 
|  | error(ENOSYS, ERROR_FIXME); | 
|  | /* | 
|  | if (omode != OREAD) | 
|  | error(EPERM, ERROR_FIXME); | 
|  | tc = proctext(c, p); | 
|  | tc->offset = 0; | 
|  | poperror(); | 
|  | qunlock(&p->debug); | 
|  | proc_decref(p); | 
|  | cclose(c); | 
|  | return tc; | 
|  | */ | 
|  | case Qproc: | 
|  | case Qsegment: | 
|  | case Qprofile: | 
|  | case Qfd: | 
|  | if (omode != O_READ) | 
|  | error(EPERM, ERROR_FIXME); | 
|  | break; | 
|  |  | 
|  | case Qnote: | 
|  | //          if (p->privatemem) | 
|  | error(EPERM, ERROR_FIXME); | 
|  | break; | 
|  |  | 
|  | case Qmem: | 
|  | //          if (p->privatemem) | 
|  | error(EPERM, ERROR_FIXME); | 
|  | // 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 != O_READ) | 
|  | error(EPERM, ERROR_FIXME); | 
|  | c->aux = kzmalloc(sizeof(struct mntwalk), MEM_WAIT); | 
|  | break; | 
|  | case Quser: | 
|  | case Qstatus: | 
|  | case Qvmstatus: | 
|  | case Qctl: | 
|  | break; | 
|  |  | 
|  | case Qstrace: | 
|  | if (!p->strace) | 
|  | error(ENOENT, "Process does not have tracing enabled"); | 
|  | spin_lock(&p->strace->lock); | 
|  | if (p->strace->tracing) { | 
|  | spin_unlock(&p->strace->lock); | 
|  | error(EBUSY, "Process is already being traced"); | 
|  | } | 
|  | /* It's not critical that we reopen before setting tracing, but | 
|  | * it's a little cleaner (concurrent syscalls could be trying to | 
|  | * use the queue before it was reopened, and they'd throw). */ | 
|  | qreopen(p->strace->q); | 
|  | p->strace->tracing = TRUE; | 
|  | spin_unlock(&p->strace->lock); | 
|  | /* the ref we are upping is the one we put in __proc_free, which | 
|  | * is the one we got from CMstrace{on,me}.  We have a ref on p, | 
|  | * so we know we won't free until we decref the proc. */ | 
|  | kref_get(&p->strace->users, 1); | 
|  | c->aux = p->strace; | 
|  | break; | 
|  | case Qstrace_traceset: | 
|  | if (!p->strace) | 
|  | error(ENOENT, "Process does not have tracing enabled"); | 
|  | kref_get(&p->strace->users, 1); | 
|  | c->aux = p->strace; | 
|  | break; | 
|  | case Qmaps: | 
|  | c->aux = build_maps(p); | 
|  | break; | 
|  | case Qnotepg: | 
|  | error(ENOSYS, ERROR_FIXME); | 
|  | #if 0 | 
|  | nonone(p); | 
|  | pg = p->pgrp; | 
|  | if (pg == NULL) | 
|  | error(ESRCH, ERROR_FIXME); | 
|  | if (omode != OWRITE || pg->pgrpid == 1) | 
|  | error(EPERM, ERROR_FIXME); | 
|  | c->pgrpid.path = pg->pgrpid + 1; | 
|  | c->pgrpid.vers = p->noteid; | 
|  | #endif | 
|  | break; | 
|  |  | 
|  | default: | 
|  | printk("procopen %#llux\n", c->qid.path); | 
|  | error(EINVAL, ERROR_FIXME); | 
|  | } | 
|  |  | 
|  | /* 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(ESRCH, ERROR_FIXME); | 
|  |  | 
|  | tc = devopen(c, omode, 0, 0, procgen); | 
|  | poperror(); | 
|  | // qunlock(&p->debug); | 
|  | proc_decref(p); | 
|  | return tc; | 
|  | } | 
|  |  | 
|  | static size_t procwstat(struct chan *c, uint8_t *db, size_t n) | 
|  | { | 
|  | ERRSTACK(2); | 
|  | error(ENOSYS, ERROR_FIXME); | 
|  | #if 0 | 
|  | struct proc *p; | 
|  | struct dir *d; | 
|  |  | 
|  | if (c->qid.type & QTDIR) | 
|  | error(EPERM, ERROR_FIXME); | 
|  |  | 
|  | if (QID(c->qid) == Qtrace) | 
|  | return devwstat(c, db, n); | 
|  |  | 
|  | if ((p = pid2proc(SLOT(c->qid))) == NULL) | 
|  | error(ESRCH, ERROR_FIXME); | 
|  | nonone(p); | 
|  | d = NULL; | 
|  | qlock(&p->debug); | 
|  | if (waserror()) { | 
|  | qunlock(&p->debug); | 
|  | proc_decref(p); | 
|  | kfree(d); | 
|  | nexterror(); | 
|  | } | 
|  |  | 
|  | if (p->pid != PID(c->qid)) | 
|  | error(ESRCH, ERROR_FIXME); | 
|  |  | 
|  | if (strcmp(current->user.name, p->user.name) != 0 && !iseve()) | 
|  | error(EPERM, ERROR_FIXME); | 
|  |  | 
|  | d = kzmalloc(sizeof(struct dir) + n, MEM_WAIT); | 
|  | n = convM2D(db, n, &d[0], (char *)&d[1]); | 
|  | if (n == 0) | 
|  | error(ENOENT, ERROR_FIXME); | 
|  | if (!emptystr(d->uid) && strcmp(d->uid, p->user.name) != 0) { | 
|  | if (!iseve()) | 
|  | error(EPERM, ERROR_FIXME); | 
|  | else | 
|  | proc_set_username(p, d->uid); | 
|  | } | 
|  | if (d->mode != -1) | 
|  | p->procmode = d->mode & 0777; | 
|  |  | 
|  | poperror(); | 
|  | qunlock(&p->debug); | 
|  | proc_decref(p); | 
|  | 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) == Qsyscall) { | 
|  | if (c->aux) | 
|  | qclose(c->aux); | 
|  | c->aux = NULL; | 
|  | } | 
|  | if (QID(c->qid) == Qns && c->aux != 0) | 
|  | kfree(c->aux); | 
|  | if (QID(c->qid) == Qmaps && c->aux != 0) | 
|  | kfree(c->aux); | 
|  | if (QID(c->qid) == Qstrace && c->aux != 0) { | 
|  | struct strace *s = c->aux; | 
|  |  | 
|  | assert(c->flag & COPEN); /* only way aux should have been set */ | 
|  | s->tracing = FALSE; | 
|  | qhangup(s->q, NULL); | 
|  | kref_put(&s->users); | 
|  | c->aux = NULL; | 
|  | } | 
|  | if (QID(c->qid) == Qstrace_traceset && c->aux != 0) { | 
|  | struct strace *s = c->aux; | 
|  |  | 
|  | assert(c->flag & COPEN); | 
|  | kref_put(&s->users); | 
|  | c->aux = NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | 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, MEM_WAIT); | 
|  | *t = 0; | 
|  | return t; | 
|  | } | 
|  | t = kzmalloc(n, MEM_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 size_t procread(struct chan *c, void *va, size_t n, off64_t off) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | struct proc *p; | 
|  | long l, r; | 
|  | int i, j, navail, pid, rsize; | 
|  | char flag[10], *sps, *srv; | 
|  | uintptr_t offset, u; | 
|  | int tesz; | 
|  | uint8_t *rptr; | 
|  | struct mntwalk *mw; | 
|  | struct strace *s; | 
|  | struct sized_alloc *sza; | 
|  |  | 
|  | 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; | 
|  | /* Some shit in proc doesn't need to grab the reference.  For strace, we | 
|  | * already have the chan open, and all we want to do is read the queue, | 
|  | * which exists because of our kref on it. */ | 
|  | switch (QID(c->qid)) { | 
|  | case Qstrace: | 
|  | s = c->aux; | 
|  | n = qread(s->q, va, n); | 
|  | return n; | 
|  | case Qstrace_traceset: | 
|  | s = c->aux; | 
|  | return readmem(offset, va, n, s->trace_set, | 
|  | bitmap_size(MAX_SYSCALL_NR)); | 
|  | } | 
|  |  | 
|  | if ((p = pid2proc(SLOT(c->qid))) == NULL) | 
|  | error(ESRCH, "%d: no such process", SLOT(c->qid)); | 
|  | if (p->pid != PID(c->qid)) { | 
|  | proc_decref(p); | 
|  | error(ESRCH, "weird: p->pid is %d, PID(c->qid) is %d: mismatch", | 
|  | p->pid, PID(c->qid)); | 
|  | } | 
|  | switch (QID(c->qid)) { | 
|  | default: | 
|  | proc_decref(p); | 
|  | break; | 
|  | case Quser: { | 
|  | int i; | 
|  |  | 
|  | i = readstr(off, va, n, p->user.name); | 
|  | proc_decref(p); | 
|  | return i; | 
|  | } | 
|  | case Qstatus: { | 
|  | /* the old code grew the stack and was hideous. | 
|  | * status is not a high frequency operation; just malloc. */ | 
|  | char *buf = kmalloc(4096, MEM_WAIT); | 
|  | char *s = buf, *e = buf + 4096; | 
|  | int i; | 
|  |  | 
|  | s = seprintf(s, e, "%8d %-*s %-10s %6d", p->pid, | 
|  | PROC_PROGNAME_SZ, p->progname, | 
|  | procstate2str(p->state), p->ppid); | 
|  | if (p->strace) | 
|  | s = seprintf(s, e, " %d trace users %d traced procs", | 
|  | kref_refcnt(&p->strace->users), | 
|  | kref_refcnt(&p->strace->procs)); | 
|  | proc_decref(p); | 
|  | i = readstr(off, va, n, buf); | 
|  | kfree(buf); | 
|  | return i; | 
|  | } | 
|  |  | 
|  | case Qvmstatus: { | 
|  | size_t buflen = 50 * 65 + 2; | 
|  | char *buf = kmalloc(buflen, MEM_WAIT); | 
|  | int i, offset; | 
|  | offset = 0; | 
|  | offset += snprintf(buf + offset, buflen - offset, "{\n"); | 
|  | for (i = 0; i < 65; i++) { | 
|  | if (p->vmm.vmexits[i] != 0) { | 
|  | offset += | 
|  | snprintf(buf + offset, buflen - offset, | 
|  | "\"%s\":\"%lld\",\n", | 
|  | VMX_EXIT_REASON_NAMES[i], | 
|  | p->vmm.vmexits[i]); | 
|  | } | 
|  | } | 
|  | offset += snprintf(buf + offset, buflen - offset, "}\n"); | 
|  | proc_decref(p); | 
|  | n = readstr(off, va, n, buf); | 
|  | kfree(buf); | 
|  | return n; | 
|  | } | 
|  | case Qns: | 
|  | // qlock(&p->debug); | 
|  | if (waserror()) { | 
|  | // qunlock(&p->debug); | 
|  | proc_decref(p); | 
|  | nexterror(); | 
|  | } | 
|  | if (p->pgrp == NULL || p->pid != PID(c->qid)) | 
|  | error(ESRCH, ERROR_FIXME); | 
|  | mw = c->aux; | 
|  | if (mw->cddone) { | 
|  | poperror(); | 
|  | // qunlock(&p->debug); | 
|  | proc_decref(p); | 
|  | 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); | 
|  | proc_decref(p); | 
|  | return i; | 
|  | } | 
|  | int2flag(mw->cm->mflag, flag); | 
|  | if (strcmp(mw->cm->to->name->s, "#mnt") == 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); | 
|  | proc_decref(p); | 
|  | return i; | 
|  | case Qmaps: | 
|  | sza = c->aux; | 
|  | i = readstr(off, va, n, sza->buf); | 
|  | proc_decref(p); | 
|  | return i; | 
|  | } | 
|  | error(EINVAL, "QID %d did not match any QIDs for #proc", QID(c->qid)); | 
|  | 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 size_t procwrite(struct chan *c, void *va, size_t n, off64_t off) | 
|  | { | 
|  | ERRSTACK(2); | 
|  |  | 
|  | struct proc *p, *t; | 
|  | int i, id, l; | 
|  | char *args; | 
|  | uintptr_t offset = off; | 
|  | struct strace *s; | 
|  |  | 
|  | if (c->qid.type & QTDIR) | 
|  | error(EISDIR, ERROR_FIXME); | 
|  |  | 
|  | if ((p = pid2proc(SLOT(c->qid))) == NULL) | 
|  | error(ESRCH, ERROR_FIXME); | 
|  |  | 
|  | if (waserror()) { | 
|  | proc_decref(p); | 
|  | nexterror(); | 
|  | } | 
|  | if (p->pid != PID(c->qid)) | 
|  | error(ESRCH, ERROR_FIXME); | 
|  |  | 
|  | offset = off; | 
|  |  | 
|  | switch (QID(c->qid)) { | 
|  | #if 0 | 
|  | case Qargs: | 
|  | if (n == 0) | 
|  | error(EINVAL, ERROR_FIXME); | 
|  | if (n >= sizeof buf - strlen(p->text) - 1) | 
|  | error(E2BIG, ERROR_FIXME); | 
|  | l = snprintf(buf, sizeof buf, "%s [%s]", p->text, (char *)va); | 
|  | args = kzmalloc(l + 1, MEM_WAIT); | 
|  | if (args == NULL) | 
|  | error(ENOMEM, ERROR_FIXME); | 
|  | 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(EINVAL, ERROR_FIXME); | 
|  |  | 
|  | 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(ENODATA, ERROR_FIXME); | 
|  | 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; | 
|  | case Qstrace_traceset: | 
|  | s = c->aux; | 
|  | if (n + offset > bitmap_size(MAX_SYSCALL_NR)) | 
|  | error(EINVAL, | 
|  | "strace_traceset: Short write (%llu at off %llu)", | 
|  | n, offset); | 
|  | if (memcpy_from_user(current, (void *)s->trace_set + offset, va, | 
|  | n)) | 
|  | error(EFAULT, "strace_traceset: Bad addr (%p + %llu)", | 
|  | va, n); | 
|  | break; | 
|  | default: | 
|  | error(EFAIL, "unknown qid %#llux in procwrite\n", c->qid.path); | 
|  | } | 
|  | poperror(); | 
|  | proc_decref(p); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | struct dev procdevtab __devtab = { | 
|  | .name = "proc", | 
|  |  | 
|  | .reset = devreset, | 
|  | .init = procinit, | 
|  | .shutdown = devshutdown, | 
|  | .attach = procattach, | 
|  | .walk = procwalk, | 
|  | .stat = procstat, | 
|  | .open = procopen, | 
|  | .create = devcreate, | 
|  | .close = procclose, | 
|  | .read = procread, | 
|  | .bread = devbread, | 
|  | .write = procwrite, | 
|  | .bwrite = devbwrite, | 
|  | .remove = devremove, | 
|  | .wstat = procwstat, | 
|  | .power = devpower, | 
|  | .chaninfo = 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(ENOENT, ERROR_FIXME); | 
|  | if (p->state == Dead) | 
|  | error(ESRCH, ERROR_FIXME); | 
|  |  | 
|  | lock(s); | 
|  | i = s->image; | 
|  | if (i == 0) { | 
|  | unlock(s); | 
|  | error(ESRCH, ERROR_FIXME); | 
|  | } | 
|  | unlock(s); | 
|  |  | 
|  | lock(i); | 
|  | if (waserror()) { | 
|  | unlock(i); | 
|  | nexterror(); | 
|  | } | 
|  |  | 
|  | tc = i->c; | 
|  | if (tc == 0) | 
|  | error(ESRCH, ERROR_FIXME); | 
|  |  | 
|  | /* 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(ESRCH, ERROR_FIXME); | 
|  | } | 
|  |  | 
|  | if (p->pid != PID(c->qid)) { | 
|  | cclose(tc); | 
|  | error(ESRCH, ERROR_FIXME); | 
|  | } | 
|  |  | 
|  | 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(EBUSY, ERROR_FIXME); | 
|  | 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(ESRCH, ERROR_FIXME); | 
|  | } | 
|  |  | 
|  | #endif | 
|  | static void procctlcloseone(struct proc *p, int fd) | 
|  | { | 
|  | // TODO: resolve this and sys_close | 
|  | sysclose(fd); | 
|  | return; | 
|  | } | 
|  |  | 
|  | 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 strace_shutdown(struct kref *a) | 
|  | { | 
|  | struct strace *strace = container_of(a, struct strace, procs); | 
|  | static const char base_msg[] = "# Traced ~%lu syscs, Dropped %lu"; | 
|  | size_t msg_len = NUMSIZE64 * 2 + sizeof(base_msg); | 
|  | char *msg = kmalloc(msg_len, 0); | 
|  |  | 
|  | if (msg) | 
|  | snprintf(msg, msg_len, base_msg, strace->appx_nr_sysc, | 
|  | atomic_read(&strace->nr_drops)); | 
|  | qhangup(strace->q, msg); | 
|  | kfree(msg); | 
|  | } | 
|  |  | 
|  | static void strace_release(struct kref *a) | 
|  | { | 
|  | struct strace *strace = container_of(a, struct strace, users); | 
|  |  | 
|  | qfree(strace->q); | 
|  | kfree(strace); | 
|  | } | 
|  |  | 
|  | static void procctlreq(struct proc *p, char *va, size_t n) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | int8_t irq_state = 0; | 
|  | int npc, pri, core; | 
|  | struct cmdbuf *cb; | 
|  | struct cmdtab *ct; | 
|  | int64_t time; | 
|  | char *e; | 
|  | struct strace *strace; | 
|  |  | 
|  | cb = parsecmd(va, n); | 
|  | if (waserror()) { | 
|  | kfree(cb); | 
|  | nexterror(); | 
|  | } | 
|  |  | 
|  | ct = lookupcmd(cb, proccmd, ARRAY_SIZE(proccmd)); | 
|  |  | 
|  | switch (ct->index) { | 
|  | case CMstraceall: | 
|  | case CMstraceme: | 
|  | case CMstrace_drop: | 
|  | /* common allocation.  if we inherited, we might have one | 
|  | * already */ | 
|  | if (!p->strace) { | 
|  | strace = kzmalloc(sizeof(*p->strace), MEM_WAIT); | 
|  | spinlock_init(&strace->lock); | 
|  | bitmap_set(strace->trace_set, 0, MAX_SYSCALL_NR); | 
|  | strace->q = qopen(65536, Qmsg, NULL, NULL); | 
|  | /* The queue is reopened and hungup whenever we open the | 
|  | * Qstrace file.  This hangup might not be necessary, | 
|  | * but is safer. */ | 
|  | qhangup(strace->q, NULL); | 
|  | /* both of these refs are put when the proc is freed. | 
|  | * procs is for every process that has this p->strace. | 
|  | * users is procs + every user (e.g. from open()). | 
|  | * | 
|  | * it is possible to kref_put the procs kref in | 
|  | * proc_destroy, which would make strace's job easier | 
|  | * (no need to do an async wait on the child), and we | 
|  | * wouldn't need to decref p in procread(Qstrace).  But | 
|  | * the downside is that proc_destroy races | 
|  | * with us here with the kref initialization. */ | 
|  | kref_init(&strace->procs, strace_shutdown, 1); | 
|  | kref_init(&strace->users, strace_release, 1); | 
|  | if (!atomic_cas_ptr((void **)&p->strace, 0, strace)) { | 
|  | /* someone else won the race and installed | 
|  | * strace. */ | 
|  | qfree(strace->q); | 
|  | kfree(strace); | 
|  | error(EAGAIN, | 
|  | "Concurrent strace init, try again"); | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* actually do the command. */ | 
|  | switch (ct->index) { | 
|  | default: | 
|  | error(EFAIL, "Command not implemented"); | 
|  | 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 CMstraceme: | 
|  | p->strace->inherit = FALSE; | 
|  | break; | 
|  | case CMstraceall: | 
|  | p->strace->inherit = TRUE; | 
|  | break; | 
|  | case CMstrace_drop: | 
|  | if (!strcmp(cb->f[1], "on")) | 
|  | p->strace->drop_overflow = TRUE; | 
|  | else if (!strcmp(cb->f[1], "off")) | 
|  | p->strace->drop_overflow = FALSE; | 
|  | else | 
|  | error(EINVAL, "strace_drop takes on|off %s", cb->f[1]); | 
|  | 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(EINVAL, ERROR_FIXME); | 
|  |  | 
|  | 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 |