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* 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 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 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 170 segmentinit(void) 171 { 172 _globalsegattach = globalsegattach; 173 } 174 175 static Chan* 176 segmentattach(char *spec) 177 { 178 return devattach('g', spec); 179 } 180 181 static Walkqid* 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 188 segmentstat(Chan *c, uchar *db, int n) 189 { 190 return devstat(c, db, n, 0, 0, segmentgen); 191 } 192 193 static int 194 cmddone(void *arg) 195 { 196 Globalseg *g = arg; 197 198 return g->cmd == Cnone; 199 } 200 201 static Chan* 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 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 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 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 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 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 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* 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 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 510 cmdready(void *arg) 511 { 512 Globalseg *g = arg; 513 514 return g->cmd != Cnone; 515 } 516 517 static void 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