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 sprint(buf, "va %#lux %#lux\n", g->s->base, g->s->top-g->s->base); 331 return readstr(voff, a, n, buf); 332 case Qdata: 333 g = c->aux; 334 if(voff > g->s->top - g->s->base) 335 error(Ebadarg); 336 if(voff + n > g->s->top - g->s->base) 337 n = g->s->top - g->s->base - voff; 338 qlock(&g->l); 339 g->off = voff + g->s->base; 340 g->data = smalloc(n); 341 if(waserror()){ 342 free(g->data); 343 qunlock(&g->l); 344 nexterror(); 345 } 346 g->dlen = n; 347 docmd(g, Cread); 348 memmove(a, g->data, g->dlen); 349 free(g->data); 350 qunlock(&g->l); 351 poperror(); 352 return g->dlen; 353 default: 354 panic("segmentread"); 355 } 356 return 0; /* not reached */ 357 } 358 359 static long 360 segmentwrite(Chan *c, void *a, long n, vlong voff) 361 { 362 Cmdbuf *cb; 363 Globalseg *g; 364 ulong va, len, top; 365 366 if(c->qid.type == QTDIR) 367 error(Eperm); 368 369 switch(TYPE(c)){ 370 case Qctl: 371 g = c->aux; 372 cb = parsecmd(a, n); 373 if(strcmp(cb->f[0], "va") == 0){ 374 if(g->s != nil) 375 error("already has a virtual address"); 376 if(cb->nf < 3) 377 error(Ebadarg); 378 va = strtoul(cb->f[1], 0, 0); 379 len = strtoul(cb->f[2], 0, 0); 380 top = PGROUND(va + len); 381 va = va&~(BY2PG-1); 382 len = (top - va) / BY2PG; 383 if(len == 0) 384 error(Ebadarg); 385 g->s = newseg(SG_SHARED, va, len); 386 } else 387 error(Ebadctl); 388 break; 389 case Qdata: 390 g = c->aux; 391 if(voff + n > g->s->top - g->s->base) 392 error(Ebadarg); 393 qlock(&g->l); 394 g->off = voff + g->s->base; 395 g->data = smalloc(n); 396 if(waserror()){ 397 free(g->data); 398 qunlock(&g->l); 399 nexterror(); 400 } 401 g->dlen = n; 402 memmove(g->data, a, g->dlen); 403 docmd(g, Cwrite); 404 free(g->data); 405 qunlock(&g->l); 406 poperror(); 407 return g->dlen; 408 default: 409 panic("segmentwrite"); 410 } 411 return 0; /* not reached */ 412 } 413 414 static int 415 segmentwstat(Chan *c, uchar *dp, int n) 416 { 417 Globalseg *g; 418 Dir *d; 419 420 if(c->qid.type == QTDIR) 421 error(Eperm); 422 423 g = getgseg(c); 424 if(waserror()){ 425 putgseg(g); 426 nexterror(); 427 } 428 429 if(strcmp(g->uid, up->user) && !iseve()) 430 error(Eperm); 431 d = smalloc(sizeof(Dir)+n); 432 n = convM2D(dp, n, &d[0], (char*)&d[1]); 433 g->perm = d->mode & 0777; 434 435 putgseg(g); 436 poperror(); 437 438 free(d); 439 return n; 440 } 441 442 static void 443 segmentremove(Chan *c) 444 { 445 Globalseg *g; 446 int x; 447 448 if(TYPE(c) != Qsegdir) 449 error(Eperm); 450 lock(&globalseglock); 451 x = SEG(c); 452 g = globalseg[x]; 453 globalseg[x] = nil; 454 unlock(&globalseglock); 455 if(g != nil) 456 putgseg(g); 457 } 458 459 /* 460 * called by segattach() 461 */ 462 static Segment* 463 globalsegattach(Proc *p, char *name) 464 { 465 int x; 466 Globalseg *g; 467 Segment *s; 468 469 g = nil; 470 if(waserror()){ 471 unlock(&globalseglock); 472 nexterror(); 473 } 474 lock(&globalseglock); 475 for(x = 0; x < nelem(globalseg); x++){ 476 g = globalseg[x]; 477 if(g != nil && strcmp(g->name, name) == 0) 478 break; 479 } 480 if(x == nelem(globalseg)){ 481 unlock(&globalseglock); 482 poperror(); 483 return nil; 484 } 485 devpermcheck(g->uid, g->perm, ORDWR); 486 s = g->s; 487 if(s == nil) 488 error("global segment not assigned a virtual address"); 489 if(isoverlap(p, s->base, s->top - s->base) != nil) 490 error("overlaps existing segment"); 491 incref(s); 492 unlock(&globalseglock); 493 poperror(); 494 return s; 495 } 496 497 static void 498 docmd(Globalseg *g, int cmd) 499 { 500 g->err[0] = 0; 501 g->cmd = cmd; 502 wakeup(&g->cmdwait); 503 sleep(&g->replywait, cmddone, g); 504 if(g->err[0]) 505 error(g->err); 506 } 507 508 static int 509 cmdready(void *arg) 510 { 511 Globalseg *g = arg; 512 513 return g->cmd != Cnone; 514 } 515 516 static void 517 segmentkproc(void *arg) 518 { 519 Globalseg *g = arg; 520 int done; 521 int sno; 522 523 for(sno = 0; sno < NSEG; sno++) 524 if(up->seg[sno] == nil && sno != ESEG) 525 break; 526 if(sno == NSEG) 527 panic("segmentkproc"); 528 g->kproc = up; 529 530 incref(g->s); 531 up->seg[sno] = g->s; 532 533 for(done = 0; !done;){ 534 sleep(&g->cmdwait, cmdready, g); 535 if(waserror()){ 536 strncpy(g->err, up->errstr, sizeof(g->err)); 537 } else { 538 switch(g->cmd){ 539 case Cstart: 540 break; 541 case Cdie: 542 done = 1; 543 break; 544 case Cread: 545 memmove(g->data, (char*)g->off, g->dlen); 546 break; 547 case Cwrite: 548 memmove((char*)g->off, g->data, g->dlen); 549 break; 550 } 551 poperror(); 552 } 553 g->cmd = Cnone; 554 wakeup(&g->replywait); 555 } 556 } 557 558 Dev segmentdevtab = { 559 'g', 560 "segment", 561 562 devreset, 563 segmentinit, 564 devshutdown, 565 segmentattach, 566 segmentwalk, 567 segmentstat, 568 segmentopen, 569 segmentcreate, 570 segmentclose, 571 segmentread, 572 devbread, 573 segmentwrite, 574 devbwrite, 575 segmentremove, 576 segmentwstat, 577 }; 578 579