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 enum
9 {
10 Qtopdir,
11 Qsegdir,
12 Qctl,
13 Qdata,
14
15 /* commands to kproc */
16 Cnone=0,
17 Cread,
18 Cwrite,
19 Cstart,
20 Cdie,
21 };
22
23 #define TYPE(x) (int)( (c)->qid.path & 0x7 )
24 #define SEG(x) ( ((c)->qid.path >> 3) & 0x3f )
25 #define PATH(s, t) ( ((s)<<3) | (t) )
26
27 typedef struct Globalseg Globalseg;
28 struct Globalseg
29 {
30 Ref;
31 Segment *s;
32
33 char *name;
34 char *uid;
35 vlong length;
36 long perm;
37
38 /* kproc to do reading and writing */
39 QLock l; /* sync kproc access */
40 Rendez cmdwait; /* where kproc waits */
41 Rendez replywait; /* where requestor waits */
42 Proc *kproc;
43 char *data;
44 long off;
45 int dlen;
46 int cmd;
47 char err[64];
48 };
49
50 static Globalseg *globalseg[100];
51 static Lock globalseglock;
52
53
54 Segment* (*_globalsegattach)(Proc*, char*);
55 static Segment* globalsegattach(Proc *p, char *name);
56 static int cmddone(void*);
57 static void segmentkproc(void*);
58 static void docmd(Globalseg *g, int cmd);
59
60 /*
61 * returns with globalseg incref'd
62 */
63 static Globalseg*
getgseg(Chan * c)64 getgseg(Chan *c)
65 {
66 int x;
67 Globalseg *g;
68
69 x = SEG(c);
70 lock(&globalseglock);
71 if(x >= nelem(globalseg))
72 panic("getgseg");
73 g = globalseg[x];
74 if(g != nil)
75 incref(g);
76 unlock(&globalseglock);
77 if(g == nil)
78 error("global segment disappeared");
79 return g;
80 }
81
82 static void
putgseg(Globalseg * g)83 putgseg(Globalseg *g)
84 {
85 if(decref(g) > 0)
86 return;
87 if(g->s != nil)
88 putseg(g->s);
89 if(g->kproc)
90 docmd(g, Cdie);
91 free(g->name);
92 free(g->uid);
93 free(g);
94 }
95
96 static int
segmentgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)97 segmentgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
98 {
99 Qid q;
100 Globalseg *g;
101 ulong size;
102
103 switch(TYPE(c)) {
104 case Qtopdir:
105 if(s == DEVDOTDOT){
106 q.vers = 0;
107 q.path = PATH(0, Qtopdir);
108 q.type = QTDIR;
109 devdir(c, q, "#g", 0, eve, DMDIR|0777, dp);
110 break;
111 }
112
113 if(s >= nelem(globalseg))
114 return -1;
115
116 lock(&globalseglock);
117 g = globalseg[s];
118 if(g == nil){
119 unlock(&globalseglock);
120 return 0;
121 }
122 q.vers = 0;
123 q.path = PATH(s, Qsegdir);
124 q.type = QTDIR;
125 devdir(c, q, g->name, 0, g->uid, DMDIR|0777, dp);
126 unlock(&globalseglock);
127
128 break;
129 case Qsegdir:
130 if(s == DEVDOTDOT){
131 q.vers = 0;
132 q.path = PATH(0, Qtopdir);
133 q.type = QTDIR;
134 devdir(c, q, "#g", 0, eve, DMDIR|0777, dp);
135 break;
136 }
137 /* fall through */
138 case Qctl:
139 case Qdata:
140 switch(s){
141 case 0:
142 g = getgseg(c);
143 q.vers = 0;
144 q.path = PATH(SEG(c), Qctl);
145 q.type = QTFILE;
146 devdir(c, q, "ctl", 0, g->uid, g->perm, dp);
147 putgseg(g);
148 break;
149 case 1:
150 g = getgseg(c);
151 q.vers = 0;
152 q.path = PATH(SEG(c), Qdata);
153 q.type = QTFILE;
154 if(g->s != nil)
155 size = g->s->top - g->s->base;
156 else
157 size = 0;
158 devdir(c, q, "data", size, g->uid, g->perm, dp);
159 putgseg(g);
160 break;
161 default:
162 return -1;
163 }
164 break;
165 }
166 return 1;
167 }
168
169 static void
segmentinit(void)170 segmentinit(void)
171 {
172 _globalsegattach = globalsegattach;
173 }
174
175 static Chan*
segmentattach(char * spec)176 segmentattach(char *spec)
177 {
178 return devattach('g', spec);
179 }
180
181 static Walkqid*
segmentwalk(Chan * c,Chan * nc,char ** name,int nname)182 segmentwalk(Chan *c, Chan *nc, char **name, int nname)
183 {
184 return devwalk(c, nc, name, nname, 0, 0, segmentgen);
185 }
186
187 static int
segmentstat(Chan * c,uchar * db,int n)188 segmentstat(Chan *c, uchar *db, int n)
189 {
190 return devstat(c, db, n, 0, 0, segmentgen);
191 }
192
193 static int
cmddone(void * arg)194 cmddone(void *arg)
195 {
196 Globalseg *g = arg;
197
198 return g->cmd == Cnone;
199 }
200
201 static Chan*
segmentopen(Chan * c,int omode)202 segmentopen(Chan *c, int omode)
203 {
204 Globalseg *g;
205
206 switch(TYPE(c)){
207 case Qtopdir:
208 case Qsegdir:
209 if(omode != 0)
210 error(Eisdir);
211 break;
212 case Qctl:
213 g = getgseg(c);
214 if(waserror()){
215 putgseg(g);
216 nexterror();
217 }
218 devpermcheck(g->uid, g->perm, omode);
219 c->aux = g;
220 poperror();
221 c->flag |= COPEN;
222 break;
223 case Qdata:
224 g = getgseg(c);
225 if(waserror()){
226 putgseg(g);
227 nexterror();
228 }
229 devpermcheck(g->uid, g->perm, omode);
230 if(g->s == nil)
231 error("segment not yet allocated");
232 if(g->kproc == nil){
233 qlock(&g->l);
234 if(waserror()){
235 qunlock(&g->l);
236 nexterror();
237 }
238 if(g->kproc == nil){
239 g->cmd = Cnone;
240 kproc(g->name, segmentkproc, g);
241 docmd(g, Cstart);
242 }
243 qunlock(&g->l);
244 poperror();
245 }
246 c->aux = g;
247 poperror();
248 c->flag |= COPEN;
249 break;
250 default:
251 panic("segmentopen");
252 }
253 c->mode = openmode(omode);
254 c->offset = 0;
255 return c;
256 }
257
258 static void
segmentclose(Chan * c)259 segmentclose(Chan *c)
260 {
261 if(TYPE(c) == Qtopdir)
262 return;
263 if(c->flag & COPEN)
264 putgseg(c->aux);
265 }
266
267 static void
segmentcreate(Chan * c,char * name,int omode,ulong perm)268 segmentcreate(Chan *c, char *name, int omode, ulong perm)
269 {
270 int x, xfree;
271 Globalseg *g;
272
273 if(TYPE(c) != Qtopdir)
274 error(Eperm);
275
276 if(isphysseg(name))
277 error(Eexist);
278
279 if((perm & DMDIR) == 0)
280 error(Ebadarg);
281
282 if(waserror()){
283 unlock(&globalseglock);
284 nexterror();
285 }
286 lock(&globalseglock);
287 xfree = -1;
288 for(x = 0; x < nelem(globalseg); x++){
289 g = globalseg[x];
290 if(g == nil){
291 if(xfree < 0)
292 xfree = x;
293 } else {
294 if(strcmp(g->name, name) == 0)
295 error(Eexist);
296 }
297 }
298 if(xfree < 0)
299 error("too many global segments");
300 g = smalloc(sizeof(Globalseg));
301 g->ref = 1;
302 kstrdup(&g->name, name);
303 kstrdup(&g->uid, up->user);
304 g->perm = 0660;
305 globalseg[xfree] = g;
306 unlock(&globalseglock);
307 poperror();
308
309 c->qid.path = PATH(x, Qsegdir);
310 c->qid.type = QTDIR;
311 c->qid.vers = 0;
312 c->mode = openmode(omode);
313 c->mode = OWRITE;
314 }
315
316 static long
segmentread(Chan * c,void * a,long n,vlong voff)317 segmentread(Chan *c, void *a, long n, vlong voff)
318 {
319 Globalseg *g;
320 char buf[32];
321
322 if(c->qid.type == QTDIR)
323 return devdirread(c, a, n, (Dirtab *)0, 0L, segmentgen);
324
325 switch(TYPE(c)){
326 case Qctl:
327 g = c->aux;
328 if(g->s == nil)
329 error("segment not yet allocated");
330 snprint(buf, sizeof buf, "va %#lux %#lux\n", g->s->base,
331 g->s->top-g->s->base);
332 return readstr(voff, a, n, buf);
333 case Qdata:
334 g = c->aux;
335 if(voff > g->s->top - g->s->base)
336 error(Ebadarg);
337 if(voff + n > g->s->top - g->s->base)
338 n = g->s->top - g->s->base - voff;
339 qlock(&g->l);
340 g->off = voff + g->s->base;
341 g->data = smalloc(n);
342 if(waserror()){
343 free(g->data);
344 qunlock(&g->l);
345 nexterror();
346 }
347 g->dlen = n;
348 docmd(g, Cread);
349 memmove(a, g->data, g->dlen);
350 free(g->data);
351 qunlock(&g->l);
352 poperror();
353 return g->dlen;
354 default:
355 panic("segmentread");
356 }
357 return 0; /* not reached */
358 }
359
360 static long
segmentwrite(Chan * c,void * a,long n,vlong voff)361 segmentwrite(Chan *c, void *a, long n, vlong voff)
362 {
363 Cmdbuf *cb;
364 Globalseg *g;
365 ulong va, len, top;
366
367 if(c->qid.type == QTDIR)
368 error(Eperm);
369
370 switch(TYPE(c)){
371 case Qctl:
372 g = c->aux;
373 cb = parsecmd(a, n);
374 if(strcmp(cb->f[0], "va") == 0){
375 if(g->s != nil)
376 error("already has a virtual address");
377 if(cb->nf < 3)
378 error(Ebadarg);
379 va = strtoul(cb->f[1], 0, 0);
380 len = strtoul(cb->f[2], 0, 0);
381 top = PGROUND(va + len);
382 va = va&~(BY2PG-1);
383 len = (top - va) / BY2PG;
384 if(len == 0)
385 error(Ebadarg);
386 g->s = newseg(SG_SHARED, va, len);
387 } else
388 error(Ebadctl);
389 break;
390 case Qdata:
391 g = c->aux;
392 if(voff + n > g->s->top - g->s->base)
393 error(Ebadarg);
394 qlock(&g->l);
395 g->off = voff + g->s->base;
396 g->data = smalloc(n);
397 if(waserror()){
398 free(g->data);
399 qunlock(&g->l);
400 nexterror();
401 }
402 g->dlen = n;
403 memmove(g->data, a, g->dlen);
404 docmd(g, Cwrite);
405 free(g->data);
406 qunlock(&g->l);
407 poperror();
408 return g->dlen;
409 default:
410 panic("segmentwrite");
411 }
412 return 0; /* not reached */
413 }
414
415 static int
segmentwstat(Chan * c,uchar * dp,int n)416 segmentwstat(Chan *c, uchar *dp, int n)
417 {
418 Globalseg *g;
419 Dir *d;
420
421 if(c->qid.type == QTDIR)
422 error(Eperm);
423
424 g = getgseg(c);
425 if(waserror()){
426 putgseg(g);
427 nexterror();
428 }
429
430 if(strcmp(g->uid, up->user) && !iseve())
431 error(Eperm);
432 d = smalloc(sizeof(Dir)+n);
433 n = convM2D(dp, n, &d[0], (char*)&d[1]);
434 g->perm = d->mode & 0777;
435
436 putgseg(g);
437 poperror();
438
439 free(d);
440 return n;
441 }
442
443 static void
segmentremove(Chan * c)444 segmentremove(Chan *c)
445 {
446 Globalseg *g;
447 int x;
448
449 if(TYPE(c) != Qsegdir)
450 error(Eperm);
451 lock(&globalseglock);
452 x = SEG(c);
453 g = globalseg[x];
454 globalseg[x] = nil;
455 unlock(&globalseglock);
456 if(g != nil)
457 putgseg(g);
458 }
459
460 /*
461 * called by segattach()
462 */
463 static Segment*
globalsegattach(Proc * p,char * name)464 globalsegattach(Proc *p, char *name)
465 {
466 int x;
467 Globalseg *g;
468 Segment *s;
469
470 g = nil;
471 if(waserror()){
472 unlock(&globalseglock);
473 nexterror();
474 }
475 lock(&globalseglock);
476 for(x = 0; x < nelem(globalseg); x++){
477 g = globalseg[x];
478 if(g != nil && strcmp(g->name, name) == 0)
479 break;
480 }
481 if(x == nelem(globalseg)){
482 unlock(&globalseglock);
483 poperror();
484 return nil;
485 }
486 devpermcheck(g->uid, g->perm, ORDWR);
487 s = g->s;
488 if(s == nil)
489 error("global segment not assigned a virtual address");
490 if(isoverlap(p, s->base, s->top - s->base) != nil)
491 error("overlaps existing segment");
492 incref(s);
493 unlock(&globalseglock);
494 poperror();
495 return s;
496 }
497
498 static void
docmd(Globalseg * g,int cmd)499 docmd(Globalseg *g, int cmd)
500 {
501 g->err[0] = 0;
502 g->cmd = cmd;
503 wakeup(&g->cmdwait);
504 sleep(&g->replywait, cmddone, g);
505 if(g->err[0])
506 error(g->err);
507 }
508
509 static int
cmdready(void * arg)510 cmdready(void *arg)
511 {
512 Globalseg *g = arg;
513
514 return g->cmd != Cnone;
515 }
516
517 static void
segmentkproc(void * arg)518 segmentkproc(void *arg)
519 {
520 Globalseg *g = arg;
521 int done;
522 int sno;
523
524 for(sno = 0; sno < NSEG; sno++)
525 if(up->seg[sno] == nil && sno != ESEG)
526 break;
527 if(sno == NSEG)
528 panic("segmentkproc");
529 g->kproc = up;
530
531 incref(g->s);
532 up->seg[sno] = g->s;
533
534 for(done = 0; !done;){
535 sleep(&g->cmdwait, cmdready, g);
536 if(waserror()){
537 strncpy(g->err, up->errstr, sizeof(g->err));
538 } else {
539 switch(g->cmd){
540 case Cstart:
541 break;
542 case Cdie:
543 done = 1;
544 break;
545 case Cread:
546 memmove(g->data, (char*)g->off, g->dlen);
547 break;
548 case Cwrite:
549 memmove((char*)g->off, g->data, g->dlen);
550 break;
551 }
552 poperror();
553 }
554 g->cmd = Cnone;
555 wakeup(&g->replywait);
556 }
557 }
558
559 Dev segmentdevtab = {
560 'g',
561 "segment",
562
563 devreset,
564 segmentinit,
565 devshutdown,
566 segmentattach,
567 segmentwalk,
568 segmentstat,
569 segmentopen,
570 segmentcreate,
571 segmentclose,
572 segmentread,
573 devbread,
574 segmentwrite,
575 devbwrite,
576 segmentremove,
577 segmentwstat,
578 };
579
580