xref: /plan9-contrib/sys/src/9k/port/devproc.c (revision 094d68186d4cdde21fdab9786d6c843a03693e4e)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"../port/error.h"
7 
8 #include	"../port/edf.h"
9 #include	"tos.h"
10 #include	<trace.h>
11 #include	"ureg.h"
12 
13 enum
14 {
15 	Qdir,
16 	Qtrace,
17 	Qargs,
18 	Qctl,
19 	Qfd,
20 	Qfpregs,
21 	Qkregs,
22 	Qmem,
23 	Qnote,
24 	Qnoteid,
25 	Qnotepg,
26 	Qns,
27 	Qproc,
28 	Qregs,
29 	Qsegment,
30 	Qstatus,
31 	Qtext,
32 	Qwait,
33 	Qprofile,
34 	Qsyscall,
35 };
36 
37 enum
38 {
39 	CMclose,
40 	CMclosefiles,
41 	CMfixedpri,
42 	CMhang,
43 	CMkill,
44 	CMnohang,
45 	CMnoswap,
46 	CMpri,
47 	CMprivate,
48 	CMprofile,
49 	CMstart,
50 	CMstartstop,
51 	CMstartsyscall,
52 	CMstop,
53 	CMwaitstop,
54 	CMwired,
55 	CMtrace,
56 	/* real time */
57 	CMperiod,
58 	CMdeadline,
59 	CMcost,
60 	CMsporadic,
61 	CMdeadlinenotes,
62 	CMadmit,
63 	CMextra,
64 	CMexpel,
65 	CMevent,
66 };
67 
68 enum{
69 	Nevents = 0x4000,
70 	Emask = Nevents - 1,
71 };
72 
73 #define STATSIZE	(2*KNAMELEN+12+9*12)
74 /*
75  * Status, fd, and ns are left fully readable (0444) because of their use in debugging,
76  * particularly on shared servers.
77  * Arguably, ns and fd shouldn't be readable; if you'd prefer, change them to 0000
78  */
79 Dirtab procdir[] =
80 {
81 	"args",		{Qargs},	0,			0660,
82 	"ctl",		{Qctl},		0,			0000,
83 	"fd",		{Qfd},		0,			0444,
84 	"fpregs",	{Qfpregs},	0,			0000,
85 	"kregs",	{Qkregs},	sizeof(Ureg),		0400,
86 	"mem",		{Qmem},		0,			0000,
87 	"note",		{Qnote},	0,			0000,
88 	"noteid",	{Qnoteid},	0,			0664,
89 	"notepg",	{Qnotepg},	0,			0000,
90 	"ns",		{Qns},		0,			0444,
91 	"proc",		{Qproc},	0,			0400,
92 	"regs",		{Qregs},	sizeof(Ureg),		0000,
93 	"segment",	{Qsegment},	0,			0444,
94 	"status",	{Qstatus},	STATSIZE,		0444,
95 	"text",		{Qtext},	0,			0000,
96 	"wait",		{Qwait},	0,			0400,
97 	"profile",	{Qprofile},	0,			0400,
98 	"syscall",	{Qsyscall},	0,			0400,
99 };
100 
101 static
102 Cmdtab proccmd[] = {
103 	CMclose,		"close",		2,
104 	CMclosefiles,		"closefiles",		1,
105 	CMfixedpri,		"fixedpri",		2,
106 	CMhang,			"hang",			1,
107 	CMnohang,		"nohang",		1,
108 	CMnoswap,		"noswap",		1,
109 	CMkill,			"kill",			1,
110 	CMpri,			"pri",			2,
111 	CMprivate,		"private",		1,
112 	CMprofile,		"profile",		1,
113 	CMstart,		"start",		1,
114 	CMstartstop,		"startstop",		1,
115 	CMstartsyscall,		"startsyscall",		1,
116 	CMstop,			"stop",			1,
117 	CMwaitstop,		"waitstop",		1,
118 	CMwired,		"wired",		2,
119 	CMtrace,		"trace",		0,
120 	CMperiod,		"period",		2,
121 	CMdeadline,		"deadline",		2,
122 	CMcost,			"cost",			2,
123 	CMsporadic,		"sporadic",		1,
124 	CMdeadlinenotes,	"deadlinenotes",	1,
125 	CMadmit,		"admit",		1,
126 	CMextra,		"extra",		1,
127 	CMexpel,		"expel",		1,
128 	CMevent,		"event",		1,
129 };
130 
131 /* Segment type from portdat.h */
132 static char *sname[]={ "Text", "Data", "Bss", "Stack", "Shared", "Phys", };
133 
134 /*
135  * Qids are, in path:
136  *	 5 bits of file type (qids above)
137  *	26 bits of process slot number + 1
138  *	     in vers,
139  *	32 bits of pid, for consistency checking
140  * If notepg, c->pgrpid.path is pgrp slot, .vers is noteid.
141  */
142 #define QSHIFT	5	/* location in qid of proc slot # */
143 
144 #define QID(q)		((((ulong)(q).path) & ((1<<QSHIFT)-1)) >> 0)
145 #define SLOT(q)		(((((ulong)(q).path) & ~(1UL<<31)) >> QSHIFT) - 1)
146 #define PID(q)		((q).vers)
147 #define NOTEID(q)	((q).vers)
148 
149 static void	procctlreq(Proc*, char*, int);
150 static int	procctlmemio(Proc*, uintptr, int, void*, int);
151 static Chan*	proctext(Chan*, Proc*);
152 static Segment* txt2data(Proc*, Segment*);
153 static int	procstopped(void*);
154 static void	mntscan(Mntwalk*, Proc*);
155 
156 static Traceevent *tevents;
157 static Lock tlock;
158 static int topens;
159 static int tproduced, tconsumed;
160 
161 static void
profclock(Ureg * ur,Timer *)162 profclock(Ureg *ur, Timer *)
163 {
164 	Tos *tos;
165 
166 	if(up == nil || up->state != Running)
167 		return;
168 
169 	/* user profiling clock */
170 	if(userureg(ur)){
171 		tos = (Tos*)(USTKTOP-sizeof(Tos));
172 		tos->clock += TK2MS(1);
173 		segclock(userpc(ur));
174 	}
175 }
176 
177 static int
procgen(Chan * c,char * name,Dirtab * tab,int,int s,Dir * dp)178 procgen(Chan *c, char *name, Dirtab *tab, int, int s, Dir *dp)
179 {
180 	Qid qid;
181 	Proc *p;
182 	char *ename;
183 	Segment *q;
184 	int pid;
185 	ulong path, perm, len;
186 
187 	if(s == DEVDOTDOT){
188 		mkqid(&qid, Qdir, 0, QTDIR);
189 		devdir(c, qid, "#p", 0, eve, 0555, dp);
190 		return 1;
191 	}
192 
193 	if(c->qid.path == Qdir){
194 		if(s == 0){
195 			strcpy(up->genbuf, "trace");
196 			mkqid(&qid, Qtrace, -1, QTFILE);
197 			devdir(c, qid, up->genbuf, 0, eve, 0444, dp);
198 			return 1;
199 		}
200 
201 		if(name != nil){
202 			/* ignore s and use name to find pid */
203 			pid = strtol(name, &ename, 10);
204 			if(pid<=0 || ename[0]!='\0')
205 				return -1;
206 			s = psindex(pid);
207 			if(s < 0)
208 				return -1;
209 		}
210 		else if(--s >= PROCMAX)
211 			return -1;
212 
213 		if((p = psincref(s)) == nil || (pid = p->pid) == 0)
214 			return 0;
215 		snprint(up->genbuf, sizeof up->genbuf, "%d", pid);
216 		/*
217 		 * String comparison is done in devwalk so
218 		 * name must match its formatted pid.
219 		 */
220 		if(name != nil && strcmp(name, up->genbuf) != 0)
221 			return -1;
222 		mkqid(&qid, (s+1)<<QSHIFT, pid, QTDIR);
223 		devdir(c, qid, up->genbuf, 0, p->user, DMDIR|0555, dp);
224 		psdecref(p);
225 		return 1;
226 	}
227 	if(c->qid.path == Qtrace){
228 		strcpy(up->genbuf, "trace");
229 		mkqid(&qid, Qtrace, -1, QTFILE);
230 		devdir(c, qid, up->genbuf, 0, eve, 0444, dp);
231 		return 1;
232 	}
233 	if(s >= nelem(procdir))
234 		return -1;
235 	if(tab)
236 		panic("procgen");
237 
238 	tab = &procdir[s];
239 	path = c->qid.path&~(((1<<QSHIFT)-1));	/* slot component */
240 
241 	/* p->procmode determines default mode for files in /proc */
242 	if((p = psincref(SLOT(c->qid))) == nil)
243 		return -1;
244 	perm = tab->perm;
245 	if(perm == 0)
246 		perm = p->procmode;
247 	else	/* just copy read bits */
248 		perm |= p->procmode & 0444;
249 
250 	len = tab->length;
251 	switch(QID(c->qid)) {
252 	case Qwait:
253 		len = p->nwait;	/* incorrect size, but >0 means there's something to read */
254 		break;
255 	case Qprofile:
256 		q = p->seg[TSEG];
257 		if(q && q->profile) {
258 			len = (q->top-q->base)>>LRESPROF;
259 			len *= sizeof(*q->profile);
260 		}
261 		break;
262 	}
263 
264 	mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
265 	devdir(c, qid, tab->name, len, p->user, perm, dp);
266 	psdecref(p);
267 	return 1;
268 }
269 
270 static void
_proctrace(Proc * p,Tevent etype,vlong ts,vlong)271 _proctrace(Proc* p, Tevent etype, vlong ts, vlong)
272 {
273 	Traceevent *te;
274 
275 	if (p->trace == 0 || topens == 0 ||
276 		tproduced - tconsumed >= Nevents)
277 		return;
278 
279 	te = &tevents[tproduced&Emask];
280 	te->pid = p->pid;
281 	te->etype = etype;
282 	if (ts == 0)
283 		te->time = todget(nil);
284 	else
285 		te->time = ts;
286 	tproduced++;
287 }
288 
289 static void
procinit(void)290 procinit(void)
291 {
292 	if(PROCMAX >= (1<<(31-QSHIFT))-1)
293 		print("warning: too many procs for devproc\n");
294 	addclock0link((void (*)(void))profclock, 113);	/* Relative prime to HZ */
295 }
296 
297 static Chan*
procattach(char * spec)298 procattach(char *spec)
299 {
300 	return devattach('p', spec);
301 }
302 
303 static Walkqid*
procwalk(Chan * c,Chan * nc,char ** name,int nname)304 procwalk(Chan *c, Chan *nc, char **name, int nname)
305 {
306 	return devwalk(c, nc, name, nname, 0, 0, procgen);
307 }
308 
309 static long
procstat(Chan * c,uchar * db,long n)310 procstat(Chan *c, uchar *db, long n)
311 {
312 	return devstat(c, db, n, 0, 0, procgen);
313 }
314 
315 /*
316  *  none can't read or write state on other
317  *  processes.  This is to contain access of
318  *  servers running as none should they be
319  *  subverted by, for example, a stack attack.
320  */
321 static void
nonone(Proc * p)322 nonone(Proc *p)
323 {
324 	if(p == up)
325 		return;
326 	if(strcmp(up->user, "none") != 0)
327 		return;
328 	if(iseve())
329 		return;
330 	error(Eperm);
331 }
332 
333 static Chan*
procopen(Chan * c,int omode)334 procopen(Chan *c, int omode)
335 {
336 	Proc *p;
337 	Pgrp *pg;
338 	Chan *tc;
339 	int pid;
340 
341 	if(c->qid.type & QTDIR)
342 		return devopen(c, omode, 0, 0, procgen);
343 
344 	if(QID(c->qid) == Qtrace){
345 		if (omode != OREAD)
346 			error(Eperm);
347 		lock(&tlock);
348 		if (waserror()){
349 			unlock(&tlock);
350 			nexterror();
351 		}
352 		if (topens > 0)
353 			error("already open");
354 		topens++;
355 		if (tevents == nil){
356 			tevents = (Traceevent*)malloc(sizeof(Traceevent) * Nevents);
357 			if(tevents == nil)
358 				error(Enomem);
359 			tproduced = tconsumed = 0;
360 		}
361 		proctrace = _proctrace;
362 		poperror();
363 		unlock(&tlock);
364 
365 		c->mode = openmode(omode);
366 		c->flag |= COPEN;
367 		c->offset = 0;
368 		return c;
369 	}
370 
371 	if((p = psincref(SLOT(c->qid))) == nil)
372 		error(Eprocdied);
373 	qlock(&p->debug);
374 	if(waserror()){
375 		qunlock(&p->debug);
376 		psdecref(p);
377 		nexterror();
378 	}
379 	pid = PID(c->qid);
380 	if(p->pid != pid)
381 		error(Eprocdied);
382 
383 	omode = openmode(omode);
384 
385 	switch(QID(c->qid)){
386 	case Qtext:
387 		if(omode != OREAD)
388 			error(Eperm);
389 		tc = proctext(c, p);
390 		tc->offset = 0;
391 		poperror();
392 		cclose(c);
393 		qunlock(&p->debug);
394 		psdecref(p);
395 		return tc;
396 
397 	case Qproc:
398 	case Qkregs:
399 	case Qsegment:
400 	case Qprofile:
401 	case Qfd:
402 		if(omode != OREAD)
403 			error(Eperm);
404 		break;
405 
406 	case Qnote:
407 		if(p->privatemem)
408 			error(Eperm);
409 		break;
410 
411 	case Qmem:
412 	case Qctl:
413 		if(p->privatemem)
414 			error(Eperm);
415 		nonone(p);
416 		break;
417 
418 	case Qargs:
419 	case Qnoteid:
420 	case Qstatus:
421 	case Qwait:
422 	case Qregs:
423 	case Qfpregs:
424 	case Qsyscall:
425 		nonone(p);
426 		break;
427 
428 	case Qns:
429 		if(omode != OREAD)
430 			error(Eperm);
431 		c->aux = malloc(sizeof(Mntwalk));
432 		break;
433 
434 	case Qnotepg:
435 		nonone(p);
436 		pg = p->pgrp;
437 		if(pg == nil)
438 			error(Eprocdied);
439 		if(omode!=OWRITE || pg->pgrpid == 1)
440 			error(Eperm);
441 		c->pgrpid.path = pg->pgrpid+1;
442 		c->pgrpid.vers = p->noteid;
443 		break;
444 
445 	default:
446 		poperror();
447 		qunlock(&p->debug);
448 		psdecref(p);
449 		pprint("procopen %#llux\n", c->qid.path);
450 		error(Egreg);
451 	}
452 
453 	/* Affix pid to qid */
454 	if(p->state != Dead)
455 		c->qid.vers = p->pid;
456 
457 	/* make sure the process slot didn't get reallocated while we were playing */
458 	coherence();
459 	if(p->pid != pid)
460 		error(Eprocdied);
461 
462 	tc = devopen(c, omode, 0, 0, procgen);
463 	poperror();
464 	qunlock(&p->debug);
465 	psdecref(p);
466 
467 	return tc;
468 }
469 
470 static long
procwstat(Chan * c,uchar * db,long n)471 procwstat(Chan *c, uchar *db, long n)
472 {
473 	Proc *p;
474 	Dir *d;
475 
476 	if(c->qid.type & QTDIR)
477 		error(Eperm);
478 
479 	if(QID(c->qid) == Qtrace)
480 		return devwstat(c, db, n);
481 
482 	if((p = psincref(SLOT(c->qid))) == nil)
483 		error(Eprocdied);
484 	nonone(p);
485 	d = nil;
486 	qlock(&p->debug);
487 	if(waserror()){
488 		qunlock(&p->debug);
489 		psdecref(p);
490 		free(d);
491 		nexterror();
492 	}
493 
494 	if(p->pid != PID(c->qid))
495 		error(Eprocdied);
496 
497 	if(strcmp(up->user, p->user) != 0 && strcmp(up->user, eve) != 0)
498 		error(Eperm);
499 
500 	d = smalloc(sizeof(Dir)+n);
501 	n = convM2D(db, n, &d[0], (char*)&d[1]);
502 	if(n == 0)
503 		error(Eshortstat);
504 	if(!emptystr(d->uid) && strcmp(d->uid, p->user) != 0){
505 		if(strcmp(up->user, eve) != 0)
506 			error(Eperm);
507 		else
508 			kstrdup(&p->user, d->uid);
509 	}
510 	/* p->procmode determines default mode for files in /proc */
511 	if(d->mode != ~0UL)
512 		p->procmode = d->mode&0777;
513 
514 	poperror();
515 	qunlock(&p->debug);
516 	psdecref(p);
517 	free(d);
518 
519 	return n;
520 }
521 
522 
523 static long
procoffset(long offset,char * va,int * np)524 procoffset(long offset, char *va, int *np)
525 {
526 	if(offset > 0) {
527 		offset -= *np;
528 		if(offset < 0) {
529 			memmove(va, va+*np+offset, -offset);
530 			*np = -offset;
531 		}
532 		else
533 			*np = 0;
534 	}
535 	return offset;
536 }
537 
538 static int
procqidwidth(Chan * c)539 procqidwidth(Chan *c)
540 {
541 	char buf[32];
542 
543 	return snprint(buf, sizeof buf, "%lud", c->qid.vers);
544 }
545 
546 int
procfdprint(Chan * c,int fd,int w,char * s,int ns)547 procfdprint(Chan *c, int fd, int w, char *s, int ns)
548 {
549 	int n;
550 
551 	if(w == 0)
552 		w = procqidwidth(c);
553 	n = snprint(s, ns, "%3d %.2s %C %4ud (%.16llux %*lud %.2ux) %5ld %8lld %s\n",
554 		fd,
555 		&"r w rw"[(c->mode&3)<<1],
556 		c->dev->dc, c->devno,
557 		c->qid.path, w, c->qid.vers, c->qid.type,
558 		c->iounit, c->offset, c->path->s);
559 	return n;
560 }
561 
562 static int
procfds(Proc * p,char * va,int count,long offset)563 procfds(Proc *p, char *va, int count, long offset)
564 {
565 	Fgrp *f;
566 	Chan *c;
567 	char buf[256];
568 	int n, i, w, ww;
569 	char *a;
570 
571 	/* print to buf to avoid holding fgrp lock while writing to user space */
572 	if(count > sizeof buf)
573 		count = sizeof buf;
574 	a = buf;
575 
576 	qlock(&p->debug);
577 	f = p->fgrp;
578 	if(f == nil){
579 		qunlock(&p->debug);
580 		return 0;
581 	}
582 	lock(f);
583 	if(waserror()){
584 		unlock(f);
585 		qunlock(&p->debug);
586 		nexterror();
587 	}
588 
589 	n = readstr(0, a, count, p->dot->path->s);
590 	n += snprint(a+n, count-n, "\n");
591 	offset = procoffset(offset, a, &n);
592 	/* compute width of qid.path */
593 	w = 0;
594 	for(i = 0; i <= f->maxfd; i++) {
595 		c = f->fd[i];
596 		if(c == nil)
597 			continue;
598 		ww = procqidwidth(c);
599 		if(ww > w)
600 			w = ww;
601 	}
602 	for(i = 0; i <= f->maxfd; i++) {
603 		c = f->fd[i];
604 		if(c == nil)
605 			continue;
606 		n += procfdprint(c, i, w, a+n, count-n);
607 		offset = procoffset(offset, a, &n);
608 	}
609 	poperror();
610 	unlock(f);
611 	qunlock(&p->debug);
612 
613 	/* copy result to user space, now that locks are released */
614 	memmove(va, buf, n);
615 
616 	return n;
617 }
618 
619 static void
procclose(Chan * c)620 procclose(Chan * c)
621 {
622 	if(QID(c->qid) == Qtrace){
623 		lock(&tlock);
624 		if(topens > 0)
625 			topens--;
626 		if(topens == 0)
627 			proctrace = nil;
628 		unlock(&tlock);
629 	}
630 	if(QID(c->qid) == Qns && c->aux != 0)
631 		free(c->aux);
632 }
633 
634 static void
int2flag(int flag,char * s)635 int2flag(int flag, char *s)
636 {
637 	if(flag == 0){
638 		*s = '\0';
639 		return;
640 	}
641 	*s++ = '-';
642 	if(flag & MAFTER)
643 		*s++ = 'a';
644 	if(flag & MBEFORE)
645 		*s++ = 'b';
646 	if(flag & MCREATE)
647 		*s++ = 'c';
648 	if(flag & MCACHE)
649 		*s++ = 'C';
650 	*s = '\0';
651 }
652 
653 static int
procargs(Proc * p,char * buf,int nbuf)654 procargs(Proc *p, char *buf, int nbuf)
655 {
656 	int j, k, m;
657 	char *a;
658 	int n;
659 
660 	a = p->args;
661 	if(p->setargs){
662 		snprint(buf, nbuf, "%s [%s]", p->text, p->args);
663 		return strlen(buf);
664 	}
665 	n = p->nargs;
666 	for(j = 0; j < nbuf - 1; j += m){
667 		if(n <= 0)
668 			break;
669 		if(j != 0)
670 			buf[j++] = ' ';
671 		m = snprint(buf+j, nbuf-j, "%q",  a);
672 		k = strlen(a) + 1;
673 		a += k;
674 		n -= k;
675 	}
676 	return j;
677 }
678 
679 static int
eventsavailable(void *)680 eventsavailable(void *)
681 {
682 	return tproduced > tconsumed;
683 }
684 
685 static long
procread(Chan * c,void * va,long n,vlong off)686 procread(Chan *c, void *va, long n, vlong off)
687 {
688 	Proc *p;
689 	long l, r;
690 	Waitq *wq;
691 	Ureg kur;
692 	uchar *rptr;
693 	Asm *asm;
694 	Mntwalk *mw;
695 	Segment *sg, *s;
696 	int i, j, navail, ne, pid, rsize;
697 	char flag[10], *sps, *srv, statbuf[NSEG*64];
698 	uintptr offset, klimit;
699 	uvlong u;
700 
701 	if(c->qid.type & QTDIR)
702 		return devdirread(c, va, n, 0, 0, procgen);
703 
704 	offset = off;
705 
706 	if(QID(c->qid) == Qtrace){
707 		if(!eventsavailable(nil))
708 			return 0;
709 
710 		rptr = va;
711 		navail = tproduced - tconsumed;
712 		if(navail > n / sizeof(Traceevent))
713 			navail = n / sizeof(Traceevent);
714 		while(navail > 0) {
715 			if((tconsumed & Emask) + navail > Nevents)
716 				ne = Nevents - (tconsumed & Emask);
717 			else
718 				ne = navail;
719 			i = ne * sizeof(Traceevent);
720 			memmove(rptr, &tevents[tconsumed & Emask], i);
721 
722 			tconsumed += ne;
723 			rptr += i;
724 			navail -= ne;
725 		}
726 		return rptr - (uchar*)va;
727 	}
728 
729 	if((p = psincref(SLOT(c->qid))) == nil)
730 		error(Eprocdied);
731 	if(p->pid != PID(c->qid)){
732 		psdecref(p);
733 		error(Eprocdied);
734 	}
735 
736 	switch(QID(c->qid)){
737 	default:
738 		psdecref(p);
739 		break;
740 	case Qargs:
741 		qlock(&p->debug);
742 		j = procargs(p, up->genbuf, sizeof up->genbuf);
743 		qunlock(&p->debug);
744 		psdecref(p);
745 		if(offset >= j)
746 			return 0;
747 		if(offset+n > j)
748 			n = j-offset;
749 		memmove(va, &up->genbuf[offset], n);
750 		return n;
751 
752 	case Qsyscall:
753 		if(p->syscalltrace == nil)
754 			return 0;
755 		return readstr(offset, va, n, p->syscalltrace);
756 
757 	case Qmem:
758 		if(!iskaddr(offset)
759 		|| (offset >= USTKTOP-USTKSIZE && offset < USTKTOP)){
760 			r = procctlmemio(p, offset, n, va, 1);
761 			psdecref(p);
762 			return r;
763 		}
764 
765 		if(!iseve()){
766 			psdecref(p);
767 			error(Eperm);
768 		}
769 
770 		/* validate kernel addresses */
771 		if(offset < PTR2UINT(end)) {
772 			if(offset+n > PTR2UINT(end))
773 				n = PTR2UINT(end) - offset;
774 			memmove(va, UINT2PTR(offset), n);
775 			psdecref(p);
776 			return n;
777 		}
778 		for(asm = asmlist; asm != nil; asm = asm->next){
779 			if(asm->kbase == 0)
780 				continue;
781 			klimit = asm->kbase + (asm->limit - asm->base);
782 
783 			/* klimit-1 because klimit might be zero!; hmmm not now but... */
784 			if(asm->kbase <= offset && offset <= klimit-1){
785 				if(offset+n >= klimit-1)
786 					n = klimit - offset;
787 				memmove(va, UINT2PTR(offset), n);
788 				psdecref(p);
789 				return n;
790 			}
791 		}
792 		psdecref(p);
793 		error(Ebadarg);
794 
795 	case Qprofile:
796 		s = p->seg[TSEG];
797 		if(s == 0 || s->profile == 0)
798 			error("profile is off");
799 		i = (s->top-s->base)>>LRESPROF;
800 		i *= sizeof(*s->profile);
801 		if(offset >= i){
802 			psdecref(p);
803 			return 0;
804 		}
805 		if(offset+n > i)
806 			n = i - offset;
807 		memmove(va, ((char*)s->profile)+offset, n);
808 		psdecref(p);
809 		return n;
810 
811 	case Qnote:
812 		qlock(&p->debug);
813 		if(waserror()){
814 			qunlock(&p->debug);
815 			psdecref(p);
816 			nexterror();
817 		}
818 		if(p->pid != PID(c->qid))
819 			error(Eprocdied);
820 		if(n < 1)	/* must accept at least the '\0' */
821 			error(Etoosmall);
822 		if(p->nnote == 0)
823 			n = 0;
824 		else {
825 			i = strlen(p->note[0].msg) + 1;
826 			if(i > n)
827 				i = n;
828 			rptr = va;
829 			memmove(rptr, p->note[0].msg, i);
830 			rptr[i-1] = '\0';
831 			p->nnote--;
832 			memmove(p->note, p->note+1, p->nnote*sizeof(Note));
833 			n = i;
834 		}
835 		if(p->nnote == 0)
836 			p->notepending = 0;
837 		poperror();
838 		qunlock(&p->debug);
839 		psdecref(p);
840 		return n;
841 
842 	case Qproc:
843 		if(offset >= sizeof(Proc)){
844 			psdecref(p);
845 			return 0;
846 		}
847 		if(offset+n > sizeof(Proc))
848 			n = sizeof(Proc) - offset;
849 		memmove(va, ((char*)p)+offset, n);
850 		psdecref(p);
851 		return n;
852 
853 	case Qregs:
854 		rptr = (uchar*)p->dbgreg;
855 		rsize = sizeof(Ureg);
856 	regread:
857 		if(rptr == 0){
858 			psdecref(p);
859 			error(Enoreg);
860 		}
861 		if(offset >= rsize){
862 			psdecref(p);
863 			return 0;
864 		}
865 		if(offset+n > rsize)
866 			n = rsize - offset;
867 		memmove(va, rptr+offset, n);
868 		psdecref(p);
869 		return n;
870 
871 	case Qkregs:
872 		memset(&kur, 0, sizeof(Ureg));
873 		setkernur(&kur, p);
874 		rptr = (uchar*)&kur;
875 		rsize = sizeof(Ureg);
876 		goto regread;
877 
878 	case Qfpregs:
879 		r = fpudevprocio(p, va, n, offset, 0);
880 		psdecref(p);
881 		return r;
882 
883 	case Qstatus:
884 		if(offset >= STATSIZE){
885 			psdecref(p);
886 			return 0;
887 		}
888 		if(offset+n > STATSIZE)
889 			n = STATSIZE - offset;
890 
891 		sps = p->psstate;
892 		if(sps == 0)
893 			sps = statename[p->state];
894 		memset(statbuf, ' ', sizeof statbuf);
895 		sprint(statbuf, "%-*.*s%-*.*s%-12.11s",
896 			KNAMELEN, KNAMELEN-1, p->text,
897 			KNAMELEN, KNAMELEN-1, p->user,
898 			sps);
899 		j = 2*KNAMELEN + 12;
900 
901 		for(i = 0; i < 6; i++) {
902 			l = p->time[i];
903 			if(i == TReal)
904 				l = sys->ticks - l;
905 			l = TK2MS(l);
906 			readnum(0, statbuf+j+NUMSIZE*i, NUMSIZE, l, NUMSIZE);
907 		}
908 		/* ignore stack, which is mostly non-existent */
909 		u = 0;
910 		for(i=1; i<NSEG; i++){
911 			s = p->seg[i];
912 			if(s)
913 				u += s->top - s->base;
914 		}
915 		readnum(0, statbuf+j+NUMSIZE*6, NUMSIZE, u>>10, NUMSIZE);
916 		readnum(0, statbuf+j+NUMSIZE*7, NUMSIZE, p->basepri, NUMSIZE);
917 		readnum(0, statbuf+j+NUMSIZE*8, NUMSIZE, p->priority, NUMSIZE);
918 		memmove(va, statbuf+offset, n);
919 		psdecref(p);
920 		return n;
921 
922 	case Qsegment:
923 		j = 0;
924 		for(i = 0; i < NSEG; i++) {
925 			sg = p->seg[i];
926 			if(sg == 0)
927 				continue;
928 			j += snprint(statbuf+j, sizeof statbuf - j,
929 				"%-6s %c%c %p %p %4d\n",
930 				sname[sg->type&SG_TYPE],
931 				sg->type&SG_RONLY ? 'R' : ' ',
932 				sg->profile ? 'P' : ' ',
933 				sg->base, sg->top, sg->ref);
934 		}
935 		psdecref(p);
936 		if(offset >= j)
937 			return 0;
938 		if(offset+n > j)
939 			n = j-offset;
940 		if(n == 0 && offset == 0)
941 			exhausted("segments");
942 		memmove(va, &statbuf[offset], n);
943 		return n;
944 
945 	case Qwait:
946 		if(!canqlock(&p->qwaitr)){
947 			psdecref(p);
948 			error(Einuse);
949 		}
950 
951 		if(waserror()) {
952 			qunlock(&p->qwaitr);
953 			psdecref(p);
954 			nexterror();
955 		}
956 
957 		lock(&p->exl);
958 		if(up == p && p->nchild == 0 && p->waitq == 0) {
959 			unlock(&p->exl);
960 			error(Enochild);
961 		}
962 		pid = p->pid;
963 		while(p->waitq == 0) {
964 			unlock(&p->exl);
965 			sleep(&p->waitr, haswaitq, p);
966 			if(p->pid != pid)
967 				error(Eprocdied);
968 			lock(&p->exl);
969 		}
970 		wq = p->waitq;
971 		p->waitq = wq->next;
972 		p->nwait--;
973 		unlock(&p->exl);
974 
975 		poperror();
976 		qunlock(&p->qwaitr);
977 		psdecref(p);
978 		n = snprint(va, n, "%d %lud %lud %lud %q",
979 			wq->w.pid,
980 			wq->w.time[TUser], wq->w.time[TSys], wq->w.time[TReal],
981 			wq->w.msg);
982 		free(wq);
983 		return n;
984 
985 	case Qns:
986 		qlock(&p->debug);
987 		if(waserror()){
988 			qunlock(&p->debug);
989 			psdecref(p);
990 			nexterror();
991 		}
992 		if(p->pgrp == nil || p->pid != PID(c->qid))
993 			error(Eprocdied);
994 		mw = c->aux;
995 		if(mw == nil)
996 			error(Enomem);
997 		if(mw->cddone){
998 			poperror();
999 			qunlock(&p->debug);
1000 			psdecref(p);
1001 			return 0;
1002 		}
1003 		mntscan(mw, p);
1004 		if(mw->mh == 0){
1005 			mw->cddone = 1;
1006 			i = snprint(va, n, "cd %s\n", p->dot->path->s);
1007 			poperror();
1008 			qunlock(&p->debug);
1009 			psdecref(p);
1010 			return i;
1011 		}
1012 		int2flag(mw->cm->mflag, flag);
1013 		if(strcmp(mw->cm->to->path->s, "#M") == 0){
1014 			srv = srvname(mw->cm->to->mchan);
1015 			i = snprint(va, n, "mount %s %s %s %s\n", flag,
1016 				srv==nil? mw->cm->to->mchan->path->s : srv,
1017 				mw->mh->from->path->s, mw->cm->spec? mw->cm->spec : "");
1018 			free(srv);
1019 		}else
1020 			i = snprint(va, n, "bind %s %s %s\n", flag,
1021 				mw->cm->to->path->s, mw->mh->from->path->s);
1022 		poperror();
1023 		qunlock(&p->debug);
1024 		psdecref(p);
1025 		return i;
1026 
1027 	case Qnoteid:
1028 		r = readnum(offset, va, n, p->noteid, NUMSIZE);
1029 		psdecref(p);
1030 		return r;
1031 	case Qfd:
1032 		r = procfds(p, va, n, offset);
1033 		psdecref(p);
1034 		return r;
1035 	}
1036 	error(Egreg);
1037 	return 0;			/* not reached */
1038 }
1039 
1040 static void
mntscan(Mntwalk * mw,Proc * p)1041 mntscan(Mntwalk *mw, Proc *p)
1042 {
1043 	Pgrp *pg;
1044 	Mount *t;
1045 	Mhead *f;
1046 	int best, i, last, nxt;
1047 
1048 	pg = p->pgrp;
1049 	rlock(&pg->ns);
1050 
1051 	nxt = 0;
1052 	best = (int)(~0U>>1);		/* largest 2's complement int */
1053 
1054 	last = 0;
1055 	if(mw->mh)
1056 		last = mw->cm->mountid;
1057 
1058 	for(i = 0; i < MNTHASH; i++) {
1059 		for(f = pg->mnthash[i]; f; f = f->hash) {
1060 			for(t = f->mount; t; t = t->next) {
1061 				if(mw->mh == 0 ||
1062 				  (t->mountid > last && t->mountid < best)) {
1063 					mw->cm = t;
1064 					mw->mh = f;
1065 					best = mw->cm->mountid;
1066 					nxt = 1;
1067 				}
1068 			}
1069 		}
1070 	}
1071 	if(nxt == 0)
1072 		mw->mh = 0;
1073 
1074 	runlock(&pg->ns);
1075 }
1076 
1077 static long
procwrite(Chan * c,void * va,long n,vlong off)1078 procwrite(Chan *c, void *va, long n, vlong off)
1079 {
1080 	Proc *p, *t;
1081 	int i, id, l;
1082 	char *args, buf[ERRMAX];
1083 	uintptr offset;
1084 
1085 	if(c->qid.type & QTDIR)
1086 		error(Eisdir);
1087 
1088 	/* Use the remembered noteid in the channel rather
1089 	 * than the process pgrpid
1090 	 */
1091 	if(QID(c->qid) == Qnotepg) {
1092 		pgrpnote(NOTEID(c->pgrpid), va, n, NUser);
1093 		return n;
1094 	}
1095 
1096 	if((p = psincref(SLOT(c->qid))) == nil)
1097 		error(Eprocdied);
1098 
1099 	qlock(&p->debug);
1100 	if(waserror()){
1101 		qunlock(&p->debug);
1102 		psdecref(p);
1103 		nexterror();
1104 	}
1105 	if(p->pid != PID(c->qid))
1106 		error(Eprocdied);
1107 
1108 	offset = off;
1109 
1110 	switch(QID(c->qid)){
1111 	case Qargs:
1112 		if(n == 0)
1113 			error(Eshort);
1114 		if(n >= ERRMAX)
1115 			error(Etoobig);
1116 		memmove(buf, va, n);
1117 		args = malloc(n+1);
1118 		if(args == nil)
1119 			error(Enomem);
1120 		memmove(args, buf, n);
1121 		l = n;
1122 		if(args[l-1] != 0)
1123 			args[l++] = 0;
1124 		free(p->args);
1125 		p->nargs = l;
1126 		p->args = args;
1127 		p->setargs = 1;
1128 		break;
1129 
1130 	case Qmem:
1131 		if(p->state != Stopped)
1132 			error(Ebadctl);
1133 
1134 		n = procctlmemio(p, offset, n, va, 0);
1135 		break;
1136 
1137 	case Qregs:
1138 		if(offset >= sizeof(Ureg))
1139 			n = 0;
1140 		else if(offset+n > sizeof(Ureg))
1141 			n = sizeof(Ureg) - offset;
1142 		if(p->dbgreg == 0)
1143 			error(Enoreg);
1144 		setregisters(p->dbgreg, (char*)(p->dbgreg)+offset, va, n);
1145 		break;
1146 
1147 	case Qfpregs:
1148 		n = fpudevprocio(p, va, n, offset, 1);
1149 		break;
1150 
1151 	case Qctl:
1152 		procctlreq(p, va, n);
1153 		break;
1154 
1155 	case Qnote:
1156 		if(p->kp)
1157 			error(Eperm);
1158 		if(n >= ERRMAX-1)
1159 			error(Etoobig);
1160 		memmove(buf, va, n);
1161 		buf[n] = 0;
1162 		if(!postnote(p, 0, buf, NUser))
1163 			error("note not posted");
1164 		break;
1165 	case Qnoteid:
1166 		id = atoi(va);
1167 		if(id == p->pid) {
1168 			p->noteid = id;
1169 			break;
1170 		}
1171 		for(i = 0; (t = psincref(i)) != nil; i++){
1172 			if(t->state == Dead || t->noteid != id){
1173 				psdecref(t);
1174 				continue;
1175 			}
1176 			if(strcmp(p->user, t->user) != 0){
1177 				psdecref(t);
1178 				error(Eperm);
1179 			}
1180 			psdecref(t);
1181 			p->noteid = id;
1182 			break;
1183 		}
1184 		if(p->noteid != id)
1185 			error(Ebadarg);
1186 		break;
1187 	default:
1188 		poperror();
1189 		qunlock(&p->debug);
1190 		psdecref(p);
1191 		pprint("unknown qid %#llux in procwrite\n", c->qid.path);
1192 		error(Egreg);
1193 	}
1194 	poperror();
1195 	qunlock(&p->debug);
1196 	psdecref(p);
1197 	return n;
1198 }
1199 
1200 Dev procdevtab = {
1201 	'p',
1202 	"proc",
1203 
1204 	devreset,
1205 	procinit,
1206 	devshutdown,
1207 	procattach,
1208 	procwalk,
1209 	procstat,
1210 	procopen,
1211 	devcreate,
1212 	procclose,
1213 	procread,
1214 	devbread,
1215 	procwrite,
1216 	devbwrite,
1217 	devremove,
1218 	procwstat,
1219 };
1220 
1221 static Chan*
proctext(Chan * c,Proc * p)1222 proctext(Chan *c, Proc *p)
1223 {
1224 	Chan *tc;
1225 	Image *i;
1226 	Segment *s;
1227 
1228 	s = p->seg[TSEG];
1229 	if(s == 0)
1230 		error(Enonexist);
1231 	if(p->state==Dead)
1232 		error(Eprocdied);
1233 
1234 	lock(s);
1235 	i = s->image;
1236 	if(i == 0) {
1237 		unlock(s);
1238 		error(Eprocdied);
1239 	}
1240 	unlock(s);
1241 
1242 	lock(i);
1243 	if(waserror()) {
1244 		unlock(i);
1245 		nexterror();
1246 	}
1247 
1248 	tc = i->c;
1249 	if(tc == 0)
1250 		error(Eprocdied);
1251 
1252 	if(incref(tc) == 1 || (tc->flag&COPEN) == 0 || tc->mode!=OREAD) {
1253 		cclose(tc);
1254 		error(Eprocdied);
1255 	}
1256 
1257 	if(p->pid != PID(c->qid)){
1258 		cclose(tc);
1259 		error(Eprocdied);
1260 	}
1261 
1262 	poperror();
1263 	unlock(i);
1264 
1265 	return tc;
1266 }
1267 
1268 void
procstopwait(Proc * p,int ctl)1269 procstopwait(Proc *p, int ctl)
1270 {
1271 	int pid;
1272 
1273 	if(p->pdbg)
1274 		error(Einuse);
1275 	if(procstopped(p) || p->state == Broken)
1276 		return;
1277 
1278 	if(ctl != 0)
1279 		p->procctl = ctl;
1280 	p->pdbg = up;
1281 	pid = p->pid;
1282 	qunlock(&p->debug);
1283 	up->psstate = "Stopwait";
1284 	if(waserror()) {
1285 		p->pdbg = 0;
1286 		qlock(&p->debug);
1287 		nexterror();
1288 	}
1289 	sleep(&up->sleep, procstopped, p);
1290 	poperror();
1291 	qlock(&p->debug);
1292 	if(p->pid != pid)
1293 		error(Eprocdied);
1294 }
1295 
1296 static void
procctlcloseone(Proc * p,Fgrp * f,int fd)1297 procctlcloseone(Proc *p, Fgrp *f, int fd)
1298 {
1299 	Chan *c;
1300 
1301 	c = f->fd[fd];
1302 	if(c == nil)
1303 		return;
1304 	f->fd[fd] = nil;
1305 	unlock(f);
1306 	qunlock(&p->debug);
1307 	cclose(c);
1308 	qlock(&p->debug);
1309 	lock(f);
1310 }
1311 
1312 void
procctlclosefiles(Proc * p,int all,int fd)1313 procctlclosefiles(Proc *p, int all, int fd)
1314 {
1315 	int i;
1316 	Fgrp *f;
1317 
1318 	f = p->fgrp;
1319 	if(f == nil)
1320 		error(Eprocdied);
1321 
1322 	lock(f);
1323 	f->ref++;
1324 	if(all)
1325 		for(i = 0; i < f->maxfd; i++)
1326 			procctlcloseone(p, f, i);
1327 	else
1328 		procctlcloseone(p, f, fd);
1329 	unlock(f);
1330 	closefgrp(f);
1331 }
1332 
1333 static char *
parsetime(vlong * rt,char * s)1334 parsetime(vlong *rt, char *s)
1335 {
1336 	uvlong ticks;
1337 	ulong l;
1338 	char *e, *p;
1339 	static int p10[] = {100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1};
1340 
1341 	if (s == nil)
1342 		return("missing value");
1343 	ticks=strtoul(s, &e, 10);
1344 	if (*e == '.'){
1345 		p = e+1;
1346 		l = strtoul(p, &e, 10);
1347 		if(e-p > nelem(p10))
1348 			return "too many digits after decimal point";
1349 		if(e-p == 0)
1350 			return "ill-formed number";
1351 		l *= p10[e-p-1];
1352 	}else
1353 		l = 0;
1354 	if (*e == '\0' || strcmp(e, "s") == 0){
1355 		ticks = 1000000000 * ticks + l;
1356 	}else if (strcmp(e, "ms") == 0){
1357 		ticks = 1000000 * ticks + l/1000;
1358 	}else if (strcmp(e, "µs") == 0 || strcmp(e, "us") == 0){
1359 		ticks = 1000 * ticks + l/1000000;
1360 	}else if (strcmp(e, "ns") != 0)
1361 		return "unrecognized unit";
1362 	*rt = ticks;
1363 	return nil;
1364 }
1365 
1366 static void
procctlreq(Proc * p,char * va,int n)1367 procctlreq(Proc *p, char *va, int n)
1368 {
1369 	Segment *s;
1370 	int npc, pri;
1371 	Cmdbuf *cb;
1372 	Cmdtab *ct;
1373 	vlong time;
1374 	char *e;
1375 	void (*pt)(Proc*, int, vlong, vlong);
1376 
1377 	if(p->kp)	/* no ctl requests to kprocs */
1378 		error(Eperm);
1379 
1380 	cb = parsecmd(va, n);
1381 	if(waserror()){
1382 		free(cb);
1383 		nexterror();
1384 	}
1385 
1386 	ct = lookupcmd(cb, proccmd, nelem(proccmd));
1387 
1388 	switch(ct->index){
1389 	case CMclose:
1390 		procctlclosefiles(p, 0, atoi(cb->f[1]));
1391 		break;
1392 	case CMclosefiles:
1393 		procctlclosefiles(p, 1, 0);
1394 		break;
1395 	case CMhang:
1396 		p->hang = 1;
1397 		break;
1398 	case CMkill:
1399 		switch(p->state) {
1400 		case Broken:
1401 			unbreak(p);
1402 			break;
1403 		case Stopped:
1404 			p->procctl = Proc_exitme;
1405 			postnote(p, 0, "sys: killed", NExit);
1406 			ready(p);
1407 			break;
1408 		default:
1409 			p->procctl = Proc_exitme;
1410 			postnote(p, 0, "sys: killed", NExit);
1411 		}
1412 		break;
1413 	case CMnohang:
1414 		p->hang = 0;
1415 		break;
1416 	case CMnoswap:
1417 		/*retired*/
1418 		break;
1419 	case CMpri:
1420 		pri = atoi(cb->f[1]);
1421 		if(pri > PriNormal && !iseve())
1422 			error(Eperm);
1423 		procpriority(p, pri, 0);
1424 		break;
1425 	case CMfixedpri:
1426 		pri = atoi(cb->f[1]);
1427 		if(pri > PriNormal && !iseve())
1428 			error(Eperm);
1429 		procpriority(p, pri, 1);
1430 		break;
1431 	case CMprivate:
1432 		p->privatemem = 1;
1433 		break;
1434 	case CMprofile:
1435 		s = p->seg[TSEG];
1436 		if(s == 0 || (s->type&SG_TYPE) != SG_TEXT)
1437 			error(Ebadctl);
1438 		if(s->profile != 0)
1439 			free(s->profile);
1440 		npc = (s->top-s->base)>>LRESPROF;
1441 		s->profile = malloc(npc*sizeof(*s->profile));
1442 		if(s->profile == 0)
1443 			error(Enomem);
1444 		break;
1445 	case CMstart:
1446 		if(p->state != Stopped)
1447 			error(Ebadctl);
1448 		ready(p);
1449 		break;
1450 	case CMstartstop:
1451 		if(p->state != Stopped)
1452 			error(Ebadctl);
1453 		p->procctl = Proc_traceme;
1454 		ready(p);
1455 		procstopwait(p, Proc_traceme);
1456 		break;
1457 	case CMstartsyscall:
1458 		if(p->state != Stopped)
1459 			error(Ebadctl);
1460 		p->procctl = Proc_tracesyscall;
1461 		ready(p);
1462 		procstopwait(p, Proc_tracesyscall);
1463 		break;
1464 	case CMstop:
1465 		procstopwait(p, Proc_stopme);
1466 		break;
1467 	case CMwaitstop:
1468 		procstopwait(p, 0);
1469 		break;
1470 	case CMwired:
1471 		procwired(p, atoi(cb->f[1]));
1472 		break;
1473 	case CMtrace:
1474 		switch(cb->nf){
1475 		case 1:
1476 			p->trace ^= 1;
1477 			break;
1478 		case 2:
1479 			p->trace = (atoi(cb->f[1]) != 0);
1480 			break;
1481 		default:
1482 			error("args");
1483 		}
1484 		break;
1485 	/* real time */
1486 	case CMperiod:
1487 		if(p->edf == nil)
1488 			edfinit(p);
1489 		if(e=parsetime(&time, cb->f[1]))	/* time in ns */
1490 			error(e);
1491 		edfstop(p);
1492 		p->edf->T = time/1000;			/* Edf times are µs */
1493 		break;
1494 	case CMdeadline:
1495 		if(p->edf == nil)
1496 			edfinit(p);
1497 		if(e=parsetime(&time, cb->f[1]))
1498 			error(e);
1499 		edfstop(p);
1500 		p->edf->D = time/1000;
1501 		break;
1502 	case CMcost:
1503 		if(p->edf == nil)
1504 			edfinit(p);
1505 		if(e=parsetime(&time, cb->f[1]))
1506 			error(e);
1507 		edfstop(p);
1508 		p->edf->C = time/1000;
1509 		break;
1510 	case CMsporadic:
1511 		if(p->edf == nil)
1512 			edfinit(p);
1513 		p->edf->flags |= Sporadic;
1514 		break;
1515 	case CMdeadlinenotes:
1516 		if(p->edf == nil)
1517 			edfinit(p);
1518 		p->edf->flags |= Sendnotes;
1519 		break;
1520 	case CMadmit:
1521 		if(p->edf == 0)
1522 			error("edf params");
1523 		if(e = edfadmit(p))
1524 			error(e);
1525 		break;
1526 	case CMextra:
1527 		if(p->edf == nil)
1528 			edfinit(p);
1529 		p->edf->flags |= Extratime;
1530 		break;
1531 	case CMexpel:
1532 		if(p->edf)
1533 			edfstop(p);
1534 		break;
1535 	case CMevent:
1536 		pt = proctrace;
1537 		if(up->trace && pt)
1538 			pt(up, SUser, 0, 0);
1539 		break;
1540 	}
1541 
1542 	poperror();
1543 	free(cb);
1544 }
1545 
1546 static int
procstopped(void * a)1547 procstopped(void *a)
1548 {
1549 	Proc *p = a;
1550 	return p->state == Stopped;
1551 }
1552 
1553 static int
procctlmemio(Proc * p,uintptr offset,int n,void * va,int read)1554 procctlmemio(Proc *p, uintptr offset, int n, void *va, int read)
1555 {
1556 	KMap *k;
1557 	Pte *pte;
1558 	Page *pg;
1559 	Segment *s;
1560 	uintptr soff, l, pgsize;	/* hmmmm */
1561 	uchar *b;
1562 
1563 	for(;;) {
1564 		s = seg(p, offset, 1);
1565 		if(s == 0)
1566 			error(Ebadarg);
1567 
1568 		if(offset+n >= s->top)
1569 			n = s->top-offset;
1570 
1571 		if(!read && (s->type&SG_TYPE) == SG_TEXT)
1572 			s = txt2data(p, s);
1573 
1574 		s->steal++;
1575 		soff = offset-s->base;
1576 		if(waserror()) {
1577 			s->steal--;
1578 			nexterror();
1579 		}
1580 		if(fixfault(s, offset, read, 0, s->color) == 0)
1581 			break;
1582 		poperror();
1583 		s->steal--;
1584 	}
1585 	poperror();
1586 	pte = s->map[soff/s->ptemapmem];
1587 	if(pte == 0)
1588 		panic("procctlmemio");
1589 	pg = pte->pages[(soff&(s->ptemapmem-1))>>s->lg2pgsize];
1590 	if(pagedout(pg))
1591 		panic("procctlmemio1");
1592 
1593 	pgsize = segpgsize(s);
1594 	l = pgsize - (offset&(pgsize-1));
1595 	if(n > l)
1596 		n = l;
1597 
1598 	k = kmap(pg);
1599 	if(waserror()) {
1600 		s->steal--;
1601 		kunmap(k);
1602 		nexterror();
1603 	}
1604 	b = (uchar*)VA(k);
1605 	b += offset&(pgsize-1);
1606 	if(read == 1)
1607 		memmove(va, b, n);	/* This can fault */
1608 	else
1609 		memmove(b, va, n);
1610 	poperror();
1611 	kunmap(k);
1612 
1613 	/* Ensure the process sees text page changes */
1614 	if(s->flushme)
1615 		mmucachectl(pg, PG_TXTFLUSH);
1616 
1617 	s->steal--;
1618 
1619 	if(read == 0)
1620 		p->newtlb = 1;
1621 
1622 	return n;
1623 }
1624 
1625 static Segment*
txt2data(Proc * p,Segment * s)1626 txt2data(Proc *p, Segment *s)
1627 {
1628 	int i;
1629 	Segment *ps;
1630 
1631 	ps = newseg(SG_DATA, s->base, s->top);
1632 	ps->image = s->image;
1633 	incref(ps->image);
1634 	ps->fstart = s->fstart;
1635 	ps->flen = s->flen;
1636 	ps->flushme = 1;
1637 
1638 	qlock(&p->seglock);
1639 	for(i = 0; i < NSEG; i++)
1640 		if(p->seg[i] == s)
1641 			break;
1642 	if(i == NSEG)
1643 		panic("segment gone");
1644 
1645 	qunlock(&s->lk);
1646 	putseg(s);
1647 	qlock(&ps->lk);
1648 	p->seg[i] = ps;
1649 	qunlock(&p->seglock);
1650 
1651 	return ps;
1652 }
1653 
1654 Segment*
data2txt(Segment * s)1655 data2txt(Segment *s)
1656 {
1657 	Segment *ps;
1658 
1659 	ps = newseg(SG_TEXT, s->base, s->top);
1660 	ps->image = s->image;
1661 	incref(ps->image);
1662 	ps->fstart = s->fstart;
1663 	ps->flen = s->flen;
1664 	ps->flushme = 1;
1665 
1666 	return ps;
1667 }
1668