86f323290c
always make sure that there are child processes we can wait for before sleeping. put pwait() sleep into a loop and recheck. this is not strictly neccesary but prevents accidents if there are spurious wakeups or a bug.
1613 lines
28 KiB
C
1613 lines
28 KiB
C
#include "u.h"
|
|
#include <trace.h>
|
|
#include "tos.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "../port/error.h"
|
|
#include "ureg.h"
|
|
#include "edf.h"
|
|
|
|
enum
|
|
{
|
|
Qdir,
|
|
Qtrace,
|
|
Qargs,
|
|
Qctl,
|
|
Qfd,
|
|
Qfpregs,
|
|
Qkregs,
|
|
Qmem,
|
|
Qnote,
|
|
Qnoteid,
|
|
Qnotepg,
|
|
Qns,
|
|
Qppid,
|
|
Qproc,
|
|
Qregs,
|
|
Qsegment,
|
|
Qstatus,
|
|
Qtext,
|
|
Qwait,
|
|
Qprofile,
|
|
Qsyscall,
|
|
};
|
|
|
|
enum
|
|
{
|
|
CMclose,
|
|
CMclosefiles,
|
|
CMfixedpri,
|
|
CMhang,
|
|
CMkill,
|
|
CMnohang,
|
|
CMnoswap,
|
|
CMpri,
|
|
CMprivate,
|
|
CMprofile,
|
|
CMstart,
|
|
CMstartstop,
|
|
CMstartsyscall,
|
|
CMstop,
|
|
CMwaitstop,
|
|
CMwired,
|
|
CMtrace,
|
|
CMinterrupt,
|
|
CMnointerrupt,
|
|
/* real time */
|
|
CMperiod,
|
|
CMdeadline,
|
|
CMcost,
|
|
CMsporadic,
|
|
CMdeadlinenotes,
|
|
CMadmit,
|
|
CMextra,
|
|
CMexpel,
|
|
CMevent,
|
|
};
|
|
|
|
enum{
|
|
Nevents = 0x4000,
|
|
Emask = Nevents - 1,
|
|
};
|
|
|
|
#define STATSIZE (2*KNAMELEN+12+9*12)
|
|
/*
|
|
* 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
|
|
*/
|
|
Dirtab procdir[] =
|
|
{
|
|
"args", {Qargs}, 0, 0660,
|
|
"ctl", {Qctl}, 0, 0000,
|
|
"fd", {Qfd}, 0, 0444,
|
|
"fpregs", {Qfpregs}, sizeof(FPsave), 0000,
|
|
"kregs", {Qkregs}, sizeof(Ureg), 0400,
|
|
"mem", {Qmem}, 0, 0000,
|
|
"note", {Qnote}, 0, 0000,
|
|
"noteid", {Qnoteid}, 0, 0664,
|
|
"notepg", {Qnotepg}, 0, 0000,
|
|
"ns", {Qns}, 0, 0444,
|
|
"ppid", {Qppid}, 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,
|
|
};
|
|
|
|
static
|
|
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,
|
|
CMinterrupt, "interrupt", 1,
|
|
CMnointerrupt, "nointerrupt", 1,
|
|
CMperiod, "period", 2,
|
|
CMdeadline, "deadline", 2,
|
|
CMcost, "cost", 2,
|
|
CMsporadic, "sporadic", 1,
|
|
CMdeadlinenotes, "deadlinenotes", 1,
|
|
CMadmit, "admit", 1,
|
|
CMextra, "extra", 1,
|
|
CMexpel, "expel", 1,
|
|
CMevent, "event", 1,
|
|
};
|
|
|
|
/* Segment type from portdat.h */
|
|
static char *sname[]={ "Text", "Data", "Bss", "Stack", "Shared", "Phys", };
|
|
|
|
/*
|
|
* Qids are, in path:
|
|
* 4 bits of file type (qids above)
|
|
* 23 bits of process slot number + 1
|
|
* 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 QID(q) ((((ulong)(q).path)&0x0000001F)>>0)
|
|
#define SLOT(q) (((((ulong)(q).path)&0x07FFFFFE0)>>QSHIFT)-1)
|
|
#define PID(q) ((q).vers)
|
|
#define NOTEID(q) ((q).vers)
|
|
|
|
void procctlreq(Proc*, char*, int);
|
|
int procctlmemio(Proc*, ulong, int, void*, int);
|
|
Chan* proctext(Chan*, Proc*);
|
|
Segment* txt2data(Proc*, Segment*);
|
|
int procstopped(void*);
|
|
void mntscan(Mntwalk*, Proc*);
|
|
|
|
static Traceevent *tevents;
|
|
static Lock tlock;
|
|
static int topens;
|
|
static int tproduced, tconsumed;
|
|
void (*proctrace)(Proc*, int, vlong);
|
|
|
|
extern int unfair;
|
|
|
|
static void
|
|
profclock(Ureg *ur, Timer *)
|
|
{
|
|
Tos *tos;
|
|
|
|
if(up == 0 || up->state != Running)
|
|
return;
|
|
|
|
/* user profiling clock */
|
|
if(userureg(ur)){
|
|
tos = (Tos*)(USTKTOP-sizeof(Tos));
|
|
tos->clock += TK2MS(1);
|
|
segclock(ur->pc);
|
|
}
|
|
}
|
|
|
|
static int
|
|
procgen(Chan *c, char *name, Dirtab *tab, int, int s, Dir *dp)
|
|
{
|
|
Qid qid;
|
|
Proc *p;
|
|
char *ename;
|
|
Segment *q;
|
|
ulong pid, 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){
|
|
strcpy(up->genbuf, "trace");
|
|
mkqid(&qid, Qtrace, -1, QTFILE);
|
|
devdir(c, qid, up->genbuf, 0, eve, 0444, dp);
|
|
return 1;
|
|
}
|
|
|
|
if(name != nil){
|
|
/* ignore s and use name to find pid */
|
|
pid = strtol(name, &ename, 10);
|
|
if(pid==0 || ename[0]!='\0')
|
|
return -1;
|
|
s = procindex(pid);
|
|
if(s < 0)
|
|
return -1;
|
|
}
|
|
else if(--s >= conf.nproc)
|
|
return -1;
|
|
|
|
p = proctab(s);
|
|
pid = p->pid;
|
|
if(pid == 0)
|
|
return 0;
|
|
sprint(up->genbuf, "%lud", pid);
|
|
/*
|
|
* String comparison is done in devwalk so name must match its formatted pid
|
|
*/
|
|
if(name != nil && strcmp(name, up->genbuf) != 0)
|
|
return -1;
|
|
mkqid(&qid, (s+1)<<QSHIFT, pid, QTDIR);
|
|
devdir(c, qid, up->genbuf, 0, p->user, DMDIR|0555, dp);
|
|
return 1;
|
|
}
|
|
if(c->qid.path == Qtrace){
|
|
strcpy(up->genbuf, "trace");
|
|
mkqid(&qid, Qtrace, -1, QTFILE);
|
|
devdir(c, qid, up->genbuf, 0, eve, 0444, dp);
|
|
return 1;
|
|
}
|
|
if(s >= nelem(procdir))
|
|
return -1;
|
|
if(tab)
|
|
panic("procgen");
|
|
|
|
tab = &procdir[s];
|
|
path = c->qid.path&~(((1<<QSHIFT)-1)); /* slot component */
|
|
|
|
/* p->procmode determines default mode for files in /proc */
|
|
p = proctab(SLOT(c->qid));
|
|
perm = tab->perm;
|
|
if(perm == 0)
|
|
perm = p->procmode;
|
|
else /* just copy read bits */
|
|
perm |= p->procmode & 0444;
|
|
|
|
len = tab->length;
|
|
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;
|
|
}
|
|
|
|
mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
|
|
devdir(c, qid, tab->name, len, p->user, perm, dp);
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
_proctrace(Proc* p, Tevent etype, vlong ts)
|
|
{
|
|
Traceevent *te;
|
|
|
|
if (p->trace == 0 || topens == 0 ||
|
|
tproduced - tconsumed >= Nevents)
|
|
return;
|
|
|
|
te = &tevents[tproduced&Emask];
|
|
te->pid = p->pid;
|
|
te->etype = etype;
|
|
if (ts == 0)
|
|
te->time = todget(nil);
|
|
else
|
|
te->time = ts;
|
|
tproduced++;
|
|
}
|
|
|
|
static void
|
|
procinit(void)
|
|
{
|
|
if(conf.nproc >= (1<<(16-QSHIFT))-1)
|
|
print("warning: too many procs for devproc\n");
|
|
addclock0link((void (*)(void))profclock, 113); /* Relative prime to HZ */
|
|
}
|
|
|
|
static Chan*
|
|
procattach(char *spec)
|
|
{
|
|
return devattach('p', spec);
|
|
}
|
|
|
|
static Walkqid*
|
|
procwalk(Chan *c, Chan *nc, char **name, int nname)
|
|
{
|
|
return devwalk(c, nc, name, nname, 0, 0, procgen);
|
|
}
|
|
|
|
static int
|
|
procstat(Chan *c, uchar *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(Proc *p)
|
|
{
|
|
if(p == up)
|
|
return;
|
|
if(strcmp(up->user, "none") != 0)
|
|
return;
|
|
if(iseve())
|
|
return;
|
|
error(Eperm);
|
|
}
|
|
|
|
static Chan*
|
|
procopen(Chan *c, int omode)
|
|
{
|
|
Proc *p;
|
|
Pgrp *pg;
|
|
Chan *tc;
|
|
int pid;
|
|
|
|
if(c->qid.type & QTDIR)
|
|
return devopen(c, omode, 0, 0, procgen);
|
|
|
|
if(QID(c->qid) == Qtrace){
|
|
if (omode != OREAD)
|
|
error(Eperm);
|
|
lock(&tlock);
|
|
if (waserror()){
|
|
unlock(&tlock);
|
|
nexterror();
|
|
}
|
|
if (topens > 0)
|
|
error("already open");
|
|
topens++;
|
|
if (tevents == nil){
|
|
tevents = (Traceevent*)malloc(sizeof(Traceevent) * Nevents);
|
|
if(tevents == nil)
|
|
error(Enomem);
|
|
tproduced = tconsumed = 0;
|
|
}
|
|
proctrace = _proctrace;
|
|
unlock(&tlock);
|
|
poperror();
|
|
|
|
c->mode = openmode(omode);
|
|
c->flag |= COPEN;
|
|
c->offset = 0;
|
|
return c;
|
|
}
|
|
|
|
p = proctab(SLOT(c->qid));
|
|
eqlock(&p->debug);
|
|
if(waserror()){
|
|
qunlock(&p->debug);
|
|
nexterror();
|
|
}
|
|
pid = PID(c->qid);
|
|
if(p->pid != pid)
|
|
error(Eprocdied);
|
|
|
|
omode = openmode(omode);
|
|
|
|
switch(QID(c->qid)){
|
|
case Qtext:
|
|
if(omode != OREAD)
|
|
error(Eperm);
|
|
tc = proctext(c, p);
|
|
tc->offset = 0;
|
|
qunlock(&p->debug);
|
|
poperror();
|
|
return tc;
|
|
|
|
case Qproc:
|
|
case Qkregs:
|
|
case Qsegment:
|
|
case Qprofile:
|
|
case Qfd:
|
|
if(omode != OREAD)
|
|
error(Eperm);
|
|
break;
|
|
|
|
case Qnote:
|
|
if(p->privatemem)
|
|
error(Eperm);
|
|
break;
|
|
|
|
case Qmem:
|
|
case Qctl:
|
|
if(p->privatemem)
|
|
error(Eperm);
|
|
nonone(p);
|
|
break;
|
|
|
|
case Qargs:
|
|
case Qnoteid:
|
|
case Qstatus:
|
|
case Qwait:
|
|
case Qregs:
|
|
case Qfpregs:
|
|
case Qsyscall:
|
|
case Qppid:
|
|
nonone(p);
|
|
break;
|
|
|
|
case Qns:
|
|
if(omode != OREAD)
|
|
error(Eperm);
|
|
c->aux = smalloc(sizeof(Mntwalk));
|
|
break;
|
|
|
|
case Qnotepg:
|
|
nonone(p);
|
|
pg = p->pgrp;
|
|
if(pg == nil)
|
|
error(Eprocdied);
|
|
if(omode!=OWRITE || pg->pgrpid == 1)
|
|
error(Eperm);
|
|
c->pgrpid.path = pg->pgrpid+1;
|
|
c->pgrpid.vers = p->noteid;
|
|
break;
|
|
|
|
default:
|
|
pprint("procopen %#lux\n", QID(c->qid));
|
|
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();
|
|
if(p->pid != pid)
|
|
error(Eprocdied);
|
|
|
|
tc = devopen(c, omode, 0, 0, procgen);
|
|
qunlock(&p->debug);
|
|
poperror();
|
|
|
|
return tc;
|
|
}
|
|
|
|
static int
|
|
procwstat(Chan *c, uchar *db, int n)
|
|
{
|
|
Proc *p;
|
|
Dir *d;
|
|
|
|
if(c->qid.type&QTDIR)
|
|
error(Eperm);
|
|
|
|
if(QID(c->qid) == Qtrace)
|
|
return devwstat(c, db, n);
|
|
|
|
p = proctab(SLOT(c->qid));
|
|
nonone(p);
|
|
d = nil;
|
|
|
|
eqlock(&p->debug);
|
|
if(waserror()){
|
|
qunlock(&p->debug);
|
|
free(d);
|
|
nexterror();
|
|
}
|
|
|
|
if(p->pid != PID(c->qid))
|
|
error(Eprocdied);
|
|
|
|
if(strcmp(up->user, p->user) != 0 && strcmp(up->user, eve) != 0)
|
|
error(Eperm);
|
|
|
|
d = smalloc(sizeof(Dir)+n);
|
|
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(up->user, eve) != 0)
|
|
error(Eperm);
|
|
else
|
|
kstrdup(&p->user, d->uid);
|
|
}
|
|
/* p->procmode determines default mode for files in /proc */
|
|
if(d->mode != ~0UL)
|
|
p->procmode = d->mode&0777;
|
|
|
|
qunlock(&p->debug);
|
|
poperror();
|
|
free(d);
|
|
return n;
|
|
}
|
|
|
|
|
|
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(Chan *c)
|
|
{
|
|
char buf[32];
|
|
|
|
return sprint(buf, "%lud", c->qid.vers);
|
|
}
|
|
|
|
int
|
|
procfdprint(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 %4ld (%.16llux %*lud %.2ux) %5ld %8lld %s\n",
|
|
fd,
|
|
&"r w rw"[(c->mode&3)<<1],
|
|
devtab[c->type]->dc, c->dev,
|
|
c->qid.path, w, c->qid.vers, c->qid.type,
|
|
c->iounit, c->offset, c->path->s);
|
|
return n;
|
|
}
|
|
|
|
static int
|
|
procfds(Proc *p, char *va, int count, long offset)
|
|
{
|
|
Fgrp *f;
|
|
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;
|
|
|
|
eqlock(&p->debug);
|
|
f = p->fgrp;
|
|
if(f == nil){
|
|
qunlock(&p->debug);
|
|
return 0;
|
|
}
|
|
lock(f);
|
|
if(waserror()){
|
|
unlock(f);
|
|
qunlock(&p->debug);
|
|
nexterror();
|
|
}
|
|
|
|
n = readstr(0, a, count, p->dot->path->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 == nil)
|
|
continue;
|
|
ww = procqidwidth(c);
|
|
if(ww > w)
|
|
w = ww;
|
|
}
|
|
for(i = 0; i <= f->maxfd; i++) {
|
|
c = f->fd[i];
|
|
if(c == nil)
|
|
continue;
|
|
n += procfdprint(c, i, w, a+n, count-n);
|
|
offset = procoffset(offset, a, &n);
|
|
}
|
|
unlock(f);
|
|
qunlock(&p->debug);
|
|
poperror();
|
|
|
|
/* copy result to user space, now that locks are released */
|
|
memmove(va, buf, n);
|
|
|
|
return n;
|
|
}
|
|
|
|
static void
|
|
procclose(Chan * c)
|
|
{
|
|
if(QID(c->qid) == Qtrace){
|
|
lock(&tlock);
|
|
if(topens > 0)
|
|
topens--;
|
|
if(topens == 0)
|
|
proctrace = nil;
|
|
unlock(&tlock);
|
|
}
|
|
if(QID(c->qid) == Qns && c->aux != 0)
|
|
free(c->aux);
|
|
}
|
|
|
|
static 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';
|
|
}
|
|
|
|
static int
|
|
procargs(Proc *p, char *buf, int nbuf)
|
|
{
|
|
int j, k, m;
|
|
char *a;
|
|
int n;
|
|
|
|
a = p->args;
|
|
if(p->setargs){
|
|
snprint(buf, nbuf, "%s [%s]", p->text, p->args);
|
|
return strlen(buf);
|
|
}
|
|
n = p->nargs;
|
|
for(j = 0; j < nbuf - 1; j += m){
|
|
if(n <= 0)
|
|
break;
|
|
if(j != 0)
|
|
buf[j++] = ' ';
|
|
m = snprint(buf+j, nbuf-j, "%q", a);
|
|
k = strlen(a) + 1;
|
|
a += k;
|
|
n -= k;
|
|
}
|
|
return j;
|
|
}
|
|
|
|
static int
|
|
eventsavailable(void *)
|
|
{
|
|
return tproduced > tconsumed;
|
|
}
|
|
|
|
static long
|
|
procread(Chan *c, void *va, long n, vlong off)
|
|
{
|
|
/* NSEG*32 was too small for worst cases */
|
|
char *a, flag[10], *sps, *srv, statbuf[NSEG*64];
|
|
int i, j, m, navail, ne, pid, rsize;
|
|
long l;
|
|
uchar *rptr;
|
|
ulong offset;
|
|
Confmem *cm;
|
|
Mntwalk *mw;
|
|
Proc *p;
|
|
Segment *sg, *s;
|
|
Ureg kur;
|
|
Waitq *wq;
|
|
|
|
a = va;
|
|
offset = off;
|
|
|
|
if(c->qid.type & QTDIR)
|
|
return devdirread(c, a, n, 0, 0, procgen);
|
|
|
|
if(QID(c->qid) == Qtrace){
|
|
if(!eventsavailable(nil))
|
|
return 0;
|
|
|
|
rptr = (uchar*)va;
|
|
navail = tproduced - tconsumed;
|
|
if(navail > n / sizeof(Traceevent))
|
|
navail = n / sizeof(Traceevent);
|
|
while(navail > 0) {
|
|
ne = ((tconsumed & Emask) + navail > Nevents)?
|
|
Nevents - (tconsumed & Emask): navail;
|
|
memmove(rptr, &tevents[tconsumed & Emask],
|
|
ne * sizeof(Traceevent));
|
|
|
|
tconsumed += ne;
|
|
rptr += ne * sizeof(Traceevent);
|
|
navail -= ne;
|
|
}
|
|
return rptr - (uchar*)va;
|
|
}
|
|
|
|
p = proctab(SLOT(c->qid));
|
|
if(p->pid != PID(c->qid))
|
|
error(Eprocdied);
|
|
|
|
switch(QID(c->qid)){
|
|
case Qargs:
|
|
eqlock(&p->debug);
|
|
j = procargs(p, up->genbuf, sizeof up->genbuf);
|
|
qunlock(&p->debug);
|
|
if(offset >= j)
|
|
return 0;
|
|
if(offset+n > j)
|
|
n = j-offset;
|
|
memmove(a, &up->genbuf[offset], n);
|
|
return n;
|
|
case Qsyscall:
|
|
if(!p->syscalltrace)
|
|
return 0;
|
|
n = readstr(offset, a, n, p->syscalltrace);
|
|
return n;
|
|
|
|
case Qmem:
|
|
if(offset < KZERO)
|
|
return procctlmemio(p, offset, n, va, 1);
|
|
|
|
if(!iseve())
|
|
error(Eperm);
|
|
|
|
/* validate kernel addresses */
|
|
if(offset < (ulong)end) {
|
|
if(offset+n > (ulong)end)
|
|
n = (ulong)end - offset;
|
|
memmove(a, (char*)offset, n);
|
|
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(a, (char*)offset, n);
|
|
return n;
|
|
}
|
|
}
|
|
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)
|
|
return 0;
|
|
if(offset+n > i)
|
|
n = i - offset;
|
|
memmove(a, ((char*)s->profile)+offset, n);
|
|
return n;
|
|
|
|
case Qnote:
|
|
eqlock(&p->debug);
|
|
if(waserror()){
|
|
qunlock(&p->debug);
|
|
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 {
|
|
m = strlen(p->note[0].msg) + 1;
|
|
if(m > n)
|
|
m = n;
|
|
memmove(va, p->note[0].msg, m);
|
|
((char*)va)[m-1] = '\0';
|
|
p->nnote--;
|
|
memmove(p->note, p->note+1, p->nnote*sizeof(Note));
|
|
n = m;
|
|
}
|
|
if(p->nnote == 0)
|
|
p->notepending = 0;
|
|
poperror();
|
|
qunlock(&p->debug);
|
|
return n;
|
|
|
|
case Qproc:
|
|
if(offset >= sizeof(Proc))
|
|
return 0;
|
|
if(offset+n > sizeof(Proc))
|
|
n = sizeof(Proc) - offset;
|
|
memmove(a, ((char*)p)+offset, n);
|
|
return n;
|
|
|
|
case Qregs:
|
|
rptr = (uchar*)p->dbgreg;
|
|
rsize = sizeof(Ureg);
|
|
goto regread;
|
|
|
|
case Qkregs:
|
|
memset(&kur, 0, sizeof(Ureg));
|
|
setkernur(&kur, p);
|
|
rptr = (uchar*)&kur;
|
|
rsize = sizeof(Ureg);
|
|
goto regread;
|
|
|
|
case Qfpregs:
|
|
rptr = (uchar*)&p->fpsave;
|
|
rsize = sizeof(FPsave);
|
|
regread:
|
|
if(rptr == 0)
|
|
error(Enoreg);
|
|
if(offset >= rsize)
|
|
return 0;
|
|
if(offset+n > rsize)
|
|
n = rsize - offset;
|
|
memmove(a, rptr+offset, n);
|
|
return n;
|
|
|
|
case Qstatus:
|
|
if(offset >= STATSIZE)
|
|
return 0;
|
|
if(offset+n > STATSIZE)
|
|
n = STATSIZE - offset;
|
|
|
|
sps = p->psstate;
|
|
if(sps == 0)
|
|
sps = statename[p->state];
|
|
memset(statbuf, ' ', sizeof statbuf);
|
|
memmove(statbuf+0*KNAMELEN, p->text, strlen(p->text));
|
|
memmove(statbuf+1*KNAMELEN, p->user, strlen(p->user));
|
|
memmove(statbuf+2*KNAMELEN, sps, strlen(sps));
|
|
j = 2*KNAMELEN + 12;
|
|
|
|
for(i = 0; i < 6; i++) {
|
|
l = p->time[i];
|
|
if(i == TReal)
|
|
l = MACHP(0)->ticks - l;
|
|
l = TK2MS(l);
|
|
readnum(0, statbuf+j+NUMSIZE*i, NUMSIZE, l, NUMSIZE);
|
|
}
|
|
l = 0;
|
|
for(i=0; i<NSEG; i++){
|
|
if(s = p->seg[i]){
|
|
eqlock(&s->lk);
|
|
l += mcountseg(s);
|
|
qunlock(&s->lk);
|
|
}
|
|
}
|
|
readnum(0, statbuf+j+NUMSIZE*6, NUMSIZE, l*BY2PG/1024, NUMSIZE);
|
|
readnum(0, statbuf+j+NUMSIZE*7, NUMSIZE, p->basepri, NUMSIZE);
|
|
readnum(0, statbuf+j+NUMSIZE*8, NUMSIZE, p->priority, NUMSIZE);
|
|
memmove(a, statbuf+offset, n);
|
|
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 %.8lux %.8lux %4ld\n",
|
|
sname[sg->type&SG_TYPE],
|
|
sg->type&SG_RONLY ? 'R' : ' ',
|
|
sg->profile ? 'P' : ' ',
|
|
sg->base, sg->top, sg->ref);
|
|
}
|
|
if(offset >= j)
|
|
return 0;
|
|
if(offset+n > j)
|
|
n = j-offset;
|
|
if(n == 0 && offset == 0)
|
|
exhausted("segments");
|
|
memmove(a, &statbuf[offset], n);
|
|
return n;
|
|
|
|
case Qwait:
|
|
if(!canqlock(&p->qwaitr))
|
|
error(Einuse);
|
|
|
|
if(waserror()) {
|
|
qunlock(&p->qwaitr);
|
|
nexterror();
|
|
}
|
|
|
|
lock(&p->exl);
|
|
pid = p->pid;
|
|
while(p->waitq == 0) {
|
|
if(up == p && p->nchild == 0) {
|
|
unlock(&p->exl);
|
|
error(Enochild);
|
|
}
|
|
unlock(&p->exl);
|
|
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);
|
|
|
|
qunlock(&p->qwaitr);
|
|
poperror();
|
|
n = snprint(a, n, "%d %lud %lud %lud %q",
|
|
wq->w.pid,
|
|
wq->w.time[TUser], wq->w.time[TSys], wq->w.time[TReal],
|
|
wq->w.msg);
|
|
free(wq);
|
|
return n;
|
|
|
|
case Qns:
|
|
eqlock(&p->debug);
|
|
if(waserror()){
|
|
qunlock(&p->debug);
|
|
nexterror();
|
|
}
|
|
if(p->pgrp == nil || p->pid != PID(c->qid))
|
|
error(Eprocdied);
|
|
mw = c->aux;
|
|
if(mw->cddone){
|
|
qunlock(&p->debug);
|
|
poperror();
|
|
return 0;
|
|
}
|
|
mntscan(mw, p);
|
|
if(mw->mh == 0){
|
|
mw->cddone = 1;
|
|
i = snprint(a, n, "cd %s\n", p->dot->path->s);
|
|
qunlock(&p->debug);
|
|
poperror();
|
|
return i;
|
|
}
|
|
int2flag(mw->cm->mflag, flag);
|
|
if(strcmp(mw->cm->to->path->s, "#M") == 0){
|
|
srv = srvname(mw->cm->to->mchan);
|
|
i = snprint(a, n, "mount %s %s %s %s\n", flag,
|
|
srv==nil? mw->cm->to->mchan->path->s : srv,
|
|
mw->mh->from->path->s, mw->cm->spec? mw->cm->spec : "");
|
|
free(srv);
|
|
}else
|
|
i = snprint(a, n, "bind %s %s %s\n", flag,
|
|
mw->cm->to->path->s, mw->mh->from->path->s);
|
|
qunlock(&p->debug);
|
|
poperror();
|
|
return i;
|
|
|
|
case Qnoteid:
|
|
return readnum(offset, va, n, p->noteid, NUMSIZE);
|
|
case Qppid:
|
|
return readnum(offset, va, n, p->parentpid, NUMSIZE);
|
|
case Qfd:
|
|
return procfds(p, va, n, offset);
|
|
}
|
|
error(Egreg);
|
|
return 0; /* not reached */
|
|
}
|
|
|
|
void
|
|
mntscan(Mntwalk *mw, Proc *p)
|
|
{
|
|
Pgrp *pg;
|
|
Mount *t;
|
|
Mhead *f;
|
|
int nxt, i;
|
|
ulong last, bestmid;
|
|
|
|
pg = p->pgrp;
|
|
rlock(&pg->ns);
|
|
|
|
nxt = 0;
|
|
bestmid = ~0;
|
|
|
|
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 < bestmid)) {
|
|
mw->cm = t;
|
|
mw->mh = f;
|
|
bestmid = mw->cm->mountid;
|
|
nxt = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(nxt == 0)
|
|
mw->mh = 0;
|
|
|
|
runlock(&pg->ns);
|
|
}
|
|
|
|
static long
|
|
procwrite(Chan *c, void *va, long n, vlong off)
|
|
{
|
|
int id, m;
|
|
Proc *p, *t, *et;
|
|
char *a, *arg, buf[ERRMAX];
|
|
ulong offset = off;
|
|
|
|
a = va;
|
|
if(c->qid.type & QTDIR)
|
|
error(Eisdir);
|
|
|
|
p = proctab(SLOT(c->qid));
|
|
|
|
/* Use the remembered noteid in the channel rather
|
|
* than the process pgrpid
|
|
*/
|
|
if(QID(c->qid) == Qnotepg) {
|
|
pgrpnote(NOTEID(c->pgrpid), va, n, NUser);
|
|
return n;
|
|
}
|
|
|
|
eqlock(&p->debug);
|
|
if(waserror()){
|
|
qunlock(&p->debug);
|
|
nexterror();
|
|
}
|
|
if(p->pid != PID(c->qid))
|
|
error(Eprocdied);
|
|
|
|
switch(QID(c->qid)){
|
|
case Qargs:
|
|
if(n == 0)
|
|
error(Eshort);
|
|
if(n >= ERRMAX)
|
|
error(Etoobig);
|
|
arg = malloc(n+1);
|
|
if(arg == nil)
|
|
error(Enomem);
|
|
memmove(arg, va, n);
|
|
m = n;
|
|
if(arg[m-1] != 0)
|
|
arg[m++] = 0;
|
|
free(p->args);
|
|
p->nargs = m;
|
|
p->args = arg;
|
|
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:
|
|
if(offset >= sizeof(FPsave))
|
|
n = 0;
|
|
else if(offset+n > sizeof(FPsave))
|
|
n = sizeof(FPsave) - offset;
|
|
memmove((uchar*)&p->fpsave+offset, va, n);
|
|
break;
|
|
|
|
case Qctl:
|
|
procctlreq(p, va, n);
|
|
break;
|
|
|
|
case Qnote:
|
|
if(p->kp)
|
|
error(Eperm);
|
|
if(n >= ERRMAX-1)
|
|
error(Etoobig);
|
|
memmove(buf, va, n);
|
|
buf[n] = 0;
|
|
if(!postnote(p, 0, buf, NUser))
|
|
error("note not posted");
|
|
break;
|
|
case Qnoteid:
|
|
id = atoi(a);
|
|
if(id == p->pid) {
|
|
p->noteid = id;
|
|
break;
|
|
}
|
|
t = proctab(0);
|
|
for(et = t+conf.nproc; t < et; t++) {
|
|
if(t->state == Dead)
|
|
continue;
|
|
if(id == t->noteid) {
|
|
if(strcmp(p->user, t->user) != 0)
|
|
error(Eperm);
|
|
p->noteid = id;
|
|
break;
|
|
}
|
|
}
|
|
if(p->noteid != id)
|
|
error(Ebadarg);
|
|
break;
|
|
default:
|
|
pprint("unknown qid in procwrite\n");
|
|
error(Egreg);
|
|
}
|
|
poperror();
|
|
qunlock(&p->debug);
|
|
return n;
|
|
}
|
|
|
|
Dev procdevtab = {
|
|
'p',
|
|
"proc",
|
|
|
|
devreset,
|
|
procinit,
|
|
devshutdown,
|
|
procattach,
|
|
procwalk,
|
|
procstat,
|
|
procopen,
|
|
devcreate,
|
|
procclose,
|
|
procread,
|
|
devbread,
|
|
procwrite,
|
|
devbwrite,
|
|
devremove,
|
|
procwstat,
|
|
};
|
|
|
|
Chan*
|
|
proctext(Chan *c, Proc *p)
|
|
{
|
|
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);
|
|
|
|
if(incref(tc) == 1 || (tc->flag&COPEN) == 0 || tc->mode!=OREAD) {
|
|
cclose(tc);
|
|
error(Eprocdied);
|
|
}
|
|
|
|
if(p->pid != PID(c->qid))
|
|
error(Eprocdied);
|
|
|
|
unlock(i);
|
|
poperror();
|
|
|
|
return tc;
|
|
}
|
|
|
|
void
|
|
procstopwait(Proc *p, int ctl)
|
|
{
|
|
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);
|
|
up->psstate = "Stopwait";
|
|
if(waserror()) {
|
|
p->pdbg = 0;
|
|
qlock(&p->debug);
|
|
nexterror();
|
|
}
|
|
sleep(&up->sleep, procstopped, p);
|
|
poperror();
|
|
qlock(&p->debug);
|
|
if(p->pid != pid)
|
|
error(Eprocdied);
|
|
}
|
|
|
|
static void
|
|
procctlcloseone(Proc *p, Fgrp *f, int fd)
|
|
{
|
|
Chan *c;
|
|
|
|
c = f->fd[fd];
|
|
if(c == nil)
|
|
return;
|
|
f->fd[fd] = nil;
|
|
unlock(f);
|
|
qunlock(&p->debug);
|
|
cclose(c);
|
|
qlock(&p->debug);
|
|
lock(f);
|
|
}
|
|
|
|
void
|
|
procctlclosefiles(Proc *p, int all, int fd)
|
|
{
|
|
int i;
|
|
Fgrp *f;
|
|
|
|
f = p->fgrp;
|
|
if(f == nil)
|
|
error(Eprocdied);
|
|
|
|
lock(f);
|
|
f->ref++;
|
|
if(all)
|
|
for(i = 0; i < f->maxfd; i++)
|
|
procctlcloseone(p, f, i);
|
|
else
|
|
procctlcloseone(p, f, fd);
|
|
unlock(f);
|
|
closefgrp(f);
|
|
}
|
|
|
|
static char *
|
|
parsetime(vlong *rt, char *s)
|
|
{
|
|
uvlong ticks;
|
|
ulong l;
|
|
char *e, *p;
|
|
static int p10[] = {100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1};
|
|
|
|
if (s == nil)
|
|
return("missing value");
|
|
ticks=strtoul(s, &e, 10);
|
|
if (*e == '.'){
|
|
p = e+1;
|
|
l = strtoul(p, &e, 10);
|
|
if(e-p > nelem(p10))
|
|
return "too many digits after decimal point";
|
|
if(e-p == 0)
|
|
return "ill-formed number";
|
|
l *= p10[e-p-1];
|
|
}else
|
|
l = 0;
|
|
if (*e == '\0' || strcmp(e, "s") == 0){
|
|
ticks = 1000000000 * ticks + l;
|
|
}else if (strcmp(e, "ms") == 0){
|
|
ticks = 1000000 * ticks + l/1000;
|
|
}else if (strcmp(e, "µs") == 0 || strcmp(e, "us") == 0){
|
|
ticks = 1000 * ticks + l/1000000;
|
|
}else if (strcmp(e, "ns") != 0)
|
|
return "unrecognized unit";
|
|
*rt = ticks;
|
|
return nil;
|
|
}
|
|
|
|
void
|
|
procctlreq(Proc *p, char *va, int n)
|
|
{
|
|
Segment *s;
|
|
int npc, pri;
|
|
Cmdbuf *cb;
|
|
Cmdtab *ct;
|
|
vlong time;
|
|
char *e;
|
|
void (*pt)(Proc*, int, vlong);
|
|
|
|
if(p->kp) /* no ctl requests to kprocs */
|
|
error(Eperm);
|
|
|
|
cb = parsecmd(va, n);
|
|
if(waserror()){
|
|
free(cb);
|
|
nexterror();
|
|
}
|
|
|
|
ct = lookupcmd(cb, proccmd, nelem(proccmd));
|
|
|
|
switch(ct->index){
|
|
case CMclose:
|
|
procctlclosefiles(p, 0, atoi(cb->f[1]));
|
|
break;
|
|
case CMclosefiles:
|
|
procctlclosefiles(p, 1, 0);
|
|
break;
|
|
case CMhang:
|
|
p->hang = 1;
|
|
break;
|
|
case CMkill:
|
|
switch(p->state) {
|
|
case Broken:
|
|
unbreak(p);
|
|
break;
|
|
case Stopped:
|
|
p->procctl = Proc_exitme;
|
|
postnote(p, 0, "sys: killed", NExit);
|
|
ready(p);
|
|
break;
|
|
default:
|
|
p->procctl = Proc_exitme;
|
|
postnote(p, 0, "sys: killed", NExit);
|
|
}
|
|
break;
|
|
case CMnohang:
|
|
p->hang = 0;
|
|
break;
|
|
case CMnoswap:
|
|
p->noswap = 1;
|
|
break;
|
|
case CMpri:
|
|
pri = atoi(cb->f[1]);
|
|
if(pri > PriNormal && !iseve())
|
|
error(Eperm);
|
|
procpriority(p, pri, 0);
|
|
break;
|
|
case CMfixedpri:
|
|
pri = atoi(cb->f[1]);
|
|
if(pri > PriNormal && !iseve())
|
|
error(Eperm);
|
|
procpriority(p, pri, 1);
|
|
break;
|
|
case CMprivate:
|
|
p->privatemem = 1;
|
|
break;
|
|
case CMprofile:
|
|
s = p->seg[TSEG];
|
|
if(s == 0 || (s->type&SG_TYPE) != SG_TEXT)
|
|
error(Ebadctl);
|
|
if(s->profile != 0)
|
|
free(s->profile);
|
|
npc = (s->top-s->base)>>LRESPROF;
|
|
s->profile = malloc(npc*sizeof(*s->profile));
|
|
if(s->profile == 0)
|
|
error(Enomem);
|
|
break;
|
|
case CMstart:
|
|
if(p->state != Stopped)
|
|
error(Ebadctl);
|
|
ready(p);
|
|
break;
|
|
case CMstartstop:
|
|
if(p->state != Stopped)
|
|
error(Ebadctl);
|
|
p->procctl = Proc_traceme;
|
|
ready(p);
|
|
procstopwait(p, Proc_traceme);
|
|
break;
|
|
case CMstartsyscall:
|
|
if(p->state != Stopped)
|
|
error(Ebadctl);
|
|
p->procctl = Proc_tracesyscall;
|
|
ready(p);
|
|
procstopwait(p, Proc_tracesyscall);
|
|
break;
|
|
case CMstop:
|
|
procstopwait(p, Proc_stopme);
|
|
break;
|
|
case CMwaitstop:
|
|
procstopwait(p, 0);
|
|
break;
|
|
case CMwired:
|
|
procwired(p, atoi(cb->f[1]));
|
|
break;
|
|
case CMtrace:
|
|
switch(cb->nf){
|
|
case 1:
|
|
p->trace ^= 1;
|
|
break;
|
|
case 2:
|
|
p->trace = (atoi(cb->f[1]) != 0);
|
|
break;
|
|
default:
|
|
error("args");
|
|
}
|
|
break;
|
|
case CMinterrupt:
|
|
postnote(p, 0, nil, NUser);
|
|
break;
|
|
case CMnointerrupt:
|
|
if(p->nnote == 0)
|
|
p->notepending = 0;
|
|
else
|
|
error("notes pending");
|
|
break;
|
|
/* real time */
|
|
case CMperiod:
|
|
if(p->edf == nil)
|
|
edfinit(p);
|
|
if(e=parsetime(&time, cb->f[1])) /* time in ns */
|
|
error(e);
|
|
edfstop(p);
|
|
p->edf->T = time/1000; /* Edf times are in µs */
|
|
break;
|
|
case CMdeadline:
|
|
if(p->edf == nil)
|
|
edfinit(p);
|
|
if(e=parsetime(&time, cb->f[1]))
|
|
error(e);
|
|
edfstop(p);
|
|
p->edf->D = time/1000;
|
|
break;
|
|
case CMcost:
|
|
if(p->edf == nil)
|
|
edfinit(p);
|
|
if(e=parsetime(&time, cb->f[1]))
|
|
error(e);
|
|
edfstop(p);
|
|
p->edf->C = time/1000;
|
|
break;
|
|
case CMsporadic:
|
|
if(p->edf == nil)
|
|
edfinit(p);
|
|
p->edf->flags |= Sporadic;
|
|
break;
|
|
case CMdeadlinenotes:
|
|
if(p->edf == nil)
|
|
edfinit(p);
|
|
p->edf->flags |= Sendnotes;
|
|
break;
|
|
case CMadmit:
|
|
if(p->edf == 0)
|
|
error("edf params");
|
|
if(e = edfadmit(p))
|
|
error(e);
|
|
break;
|
|
case CMextra:
|
|
if(p->edf == nil)
|
|
edfinit(p);
|
|
p->edf->flags |= Extratime;
|
|
break;
|
|
case CMexpel:
|
|
if(p->edf)
|
|
edfstop(p);
|
|
break;
|
|
case CMevent:
|
|
pt = proctrace;
|
|
if(up->trace && pt)
|
|
pt(up, SUser, 0);
|
|
break;
|
|
}
|
|
|
|
poperror();
|
|
free(cb);
|
|
}
|
|
|
|
int
|
|
procstopped(void *a)
|
|
{
|
|
Proc *p = a;
|
|
return p->state == Stopped;
|
|
}
|
|
|
|
int
|
|
procctlmemio(Proc *p, ulong offset, int n, void *va, int read)
|
|
{
|
|
KMap *k;
|
|
Pte *pte;
|
|
Page *pg;
|
|
Segment *s;
|
|
ulong soff, l;
|
|
char *a = va, *b;
|
|
|
|
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) == 0)
|
|
break;
|
|
poperror();
|
|
s->steal--;
|
|
}
|
|
poperror();
|
|
pte = s->map[soff/PTEMAPMEM];
|
|
if(pte == 0)
|
|
panic("procctlmemio");
|
|
pg = pte->pages[(soff&(PTEMAPMEM-1))/BY2PG];
|
|
if(pagedout(pg))
|
|
panic("procctlmemio1");
|
|
|
|
l = BY2PG - (offset&(BY2PG-1));
|
|
if(n > l)
|
|
n = l;
|
|
|
|
k = kmap(pg);
|
|
if(waserror()) {
|
|
s->steal--;
|
|
kunmap(k);
|
|
nexterror();
|
|
}
|
|
b = (char*)VA(k);
|
|
b += offset&(BY2PG-1);
|
|
if(read == 1)
|
|
memmove(a, b, n); /* This can fault */
|
|
else
|
|
memmove(b, a, n);
|
|
kunmap(k);
|
|
poperror();
|
|
|
|
/* 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;
|
|
}
|
|
|
|
Segment*
|
|
txt2data(Proc *p, Segment *s)
|
|
{
|
|
int i;
|
|
Segment *ps;
|
|
|
|
ps = newseg(SG_DATA, s->base, s->size);
|
|
ps->image = s->image;
|
|
incref(ps->image);
|
|
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;
|
|
incref(ps->image);
|
|
ps->fstart = s->fstart;
|
|
ps->flen = s->flen;
|
|
ps->flushme = 1;
|
|
|
|
return ps;
|
|
}
|