1 #include "u.h" 2 #include "lib.h" 3 #include "mem.h" 4 #include "dat.h" 5 #include "fns.h" 6 #include "dosfs.h" 7 8 extern char *premature; 9 10 /* 11 * predeclared 12 */ 13 static void bootdump(Dosboot*); 14 static void setname(Dosfile*, char*); 15 long dosreadseg(Dosfile*, long, long); 16 17 /* 18 * debugging 19 */ 20 #define chatty 1 21 #define chat if(chatty)print 22 23 /* 24 * block io buffers 25 */ 26 enum 27 { 28 Nbio= 16, 29 }; 30 typedef struct Clustbuf Clustbuf; 31 struct Clustbuf 32 { 33 int age; 34 long sector; 35 uchar *iobuf; 36 Dos *dos; 37 int size; 38 }; 39 Clustbuf bio[Nbio]; 40 41 /* 42 * get an io block from an io buffer 43 */ 44 Clustbuf* 45 getclust(Dos *dos, long sector) 46 { 47 Clustbuf *p, *oldest; 48 int size; 49 50 chat("getclust @ %d\n", sector); 51 52 /* 53 * if we have it, just return it 54 */ 55 for(p = bio; p < &bio[Nbio]; p++){ 56 if(sector == p->sector && dos == p->dos){ 57 p->age = m->ticks; 58 chat("getclust %d in cache\n", sector); 59 return p; 60 } 61 } 62 63 /* 64 * otherwise, reuse the oldest entry 65 */ 66 oldest = bio; 67 for(p = &bio[1]; p < &bio[Nbio]; p++){ 68 if(p->age <= oldest->age) 69 oldest = p; 70 } 71 p = oldest; 72 73 /* 74 * make sure the buffer is big enough 75 */ 76 size = dos->clustsize*dos->sectsize; 77 if(p->iobuf==0 || p->size < size) 78 p->iobuf = ialloc(size, 0); 79 p->size = size; 80 81 /* 82 * read in the cluster 83 */ 84 chat("getclust addr %d\n", (sector+dos->start)*dos->sectsize); 85 if((*dos->seek)(dos->dev, (sector+dos->start)*dos->sectsize) < 0){ 86 chat("can't seek block\n"); 87 return 0; 88 } 89 if((*dos->read)(dos->dev, p->iobuf, size) != size){ 90 chat("can't read block\n"); 91 return 0; 92 } 93 94 p->age = m->ticks; 95 p->dos = dos; 96 p->sector = sector; 97 chat("getclust %d read\n", sector); 98 return p; 99 } 100 101 /* 102 * walk the fat one level ( n is a current cluster number ). 103 * return the new cluster number or -1 if no more. 104 */ 105 static long 106 fatwalk(Dos *dos, int n) 107 { 108 ulong k, sect; 109 Clustbuf *p; 110 int o; 111 112 chat("fatwalk %d\n", n); 113 114 if(n < 2 || n >= dos->fatclusters) 115 return -1; 116 117 switch(dos->fatbits){ 118 case 12: 119 k = (3*n)/2; break; 120 case 16: 121 k = 2*n; break; 122 default: 123 return -1; 124 } 125 if(k >= dos->fatsize*dos->sectsize) 126 panic("getfat"); 127 128 sect = (k/(dos->sectsize*dos->clustsize))*dos->clustsize + dos->fataddr; 129 o = k%(dos->sectsize*dos->clustsize); 130 p = getclust(dos, sect); 131 k = p->iobuf[o++]; 132 if(o >= dos->sectsize*dos->clustsize){ 133 p = getclust(dos, sect+dos->clustsize); 134 o = 0; 135 } 136 k |= p->iobuf[o]<<8; 137 if(dos->fatbits == 12){ 138 if(n&1) 139 k >>= 4; 140 else 141 k &= 0xfff; 142 if(k >= 0xff8) 143 k |= 0xf000; 144 } 145 k = k < 0xfff8 ? k : -1; 146 chat("fatwalk %d -> %d\n", n, k); 147 return k; 148 } 149 150 /* 151 * map a file's logical cluster address to a physical sector address 152 */ 153 static long 154 fileaddr(Dosfile *fp, long ltarget) 155 { 156 Dos *dos = fp->dos; 157 long l; 158 long p; 159 160 chat("fileaddr %8.8s %d\n", fp->name, ltarget); 161 /* 162 * root directory is contiguous and easy 163 */ 164 if(fp->pstart == 0){ 165 if(ltarget*dos->sectsize*dos->clustsize >= dos->rootsize*sizeof(Dosdir)) 166 return -1; 167 l = dos->rootaddr + ltarget*dos->clustsize; 168 chat("fileaddr %d -> %d\n", ltarget, l); 169 return l; 170 } 171 172 /* 173 * anything else requires a walk through the fat 174 */ 175 if(ltarget >= fp->lcurrent && fp->pcurrent){ 176 /* start at the currrent point */ 177 l = fp->lcurrent; 178 p = fp->pcurrent; 179 } else { 180 /* go back to the beginning */ 181 l = 0; 182 p = fp->pstart; 183 } 184 while(l != ltarget){ 185 /* walk the fat */ 186 p = fatwalk(dos, p); 187 if(p < 0) 188 return -1; 189 l++; 190 } 191 fp->lcurrent = l; 192 fp->pcurrent = p; 193 194 /* 195 * clusters start at 2 instead of 0 (why? - presotto) 196 */ 197 l = dos->dataaddr + (p-2)*dos->clustsize; 198 chat("fileaddr %d -> %d\n", ltarget, l); 199 return l; 200 } 201 202 /* 203 * read from a dos file 204 */ 205 long 206 dosread(Dosfile *fp, void *a, long n) 207 { 208 long addr; 209 long rv; 210 int i; 211 int off; 212 Clustbuf *p; 213 uchar *from, *to; 214 215 if((fp->attr & DDIR) == 0){ 216 if(fp->offset >= fp->length) 217 return 0; 218 if(fp->offset+n > fp->length) 219 n = fp->length - fp->offset; 220 } 221 222 to = a; 223 for(rv = 0; rv < n; rv+=i){ 224 /* 225 * read the cluster 226 */ 227 addr = fileaddr(fp, fp->offset/fp->dos->clustbytes); 228 if(addr < 0) 229 return -1; 230 p = getclust(fp->dos, addr); 231 if(p == 0) 232 return -1; 233 234 /* 235 * copy the bytes we need 236 */ 237 off = fp->offset % fp->dos->clustbytes; 238 from = &p->iobuf[off]; 239 i = n - rv; 240 if(i > fp->dos->clustbytes - off) 241 i = fp->dos->clustbytes - off; 242 memmove(to, from, i); 243 to += i; 244 fp->offset += i; 245 } 246 247 return rv; 248 } 249 250 /* 251 * walk a directory returns 252 * -1 if something went wrong 253 * 0 if not found 254 * 1 if found 255 */ 256 int 257 doswalk(Dosfile *file, char *name) 258 { 259 Dosdir d; 260 long n; 261 262 if((file->attr & DDIR) == 0){ 263 chat("walking non-directory!\n"); 264 return -1; 265 } 266 267 setname(file, name); 268 269 file->offset = 0; /* start at the beginning */ 270 while((n = dosread(file, &d, sizeof(d))) == sizeof(d)){ 271 chat("comparing to %8.8s.%3.3s\n", d.name, d.ext); 272 if(memcmp(file->name, d.name, sizeof(d.name)) != 0) 273 continue; 274 if(memcmp(file->ext, d.ext, sizeof(d.ext)) != 0) 275 continue; 276 if(d.attr & DVLABEL){ 277 chat("%8.8s.%3.3s is a LABEL\n", d.name, d.ext); 278 continue; 279 } 280 file->attr = d.attr; 281 file->pstart = GSHORT(d.start); 282 file->length = GLONG(d.length); 283 file->pcurrent = 0; 284 file->lcurrent = 0; 285 file->offset = 0; 286 return 1; 287 } 288 return n >= 0 ? 0 : -1; 289 } 290 291 292 /* 293 * instructions that boot blocks can start with 294 */ 295 #define JMPSHORT 0xeb 296 #define JMPNEAR 0xe9 297 298 /* 299 * read dos file system properties 300 */ 301 int 302 dosinit(Dos *dos, int start, int ishard) 303 { 304 Dosboot *b; 305 int i; 306 Clustbuf *p; 307 Dospart *dp; 308 ulong mbroffset, offset; 309 310 /* defaults till we know better */ 311 dos->start = start; 312 dos->sectsize = 512; 313 dos->clustsize = 1; 314 mbroffset = 0; 315 316 dmddo: 317 /* get first sector */ 318 p = getclust(dos, mbroffset); 319 if(p == 0){ 320 chat("can't read boot block\n"); 321 return -1; 322 } 323 324 /* 325 * If it's a hard disc then look for an MBR and pick either an 326 * active partition or the FAT with the lowest starting LBA. 327 * Things are tricky because we could be pointing to, amongst others: 328 * 1) a floppy BPB; 329 * 2) a hard disc MBR; 330 * 3) a hard disc extended partition table; 331 * 4) a logical drive on a hard disc; 332 * 5) a disc-manager boot block. 333 * They all have the same magic at the end of the block. 334 */ 335 if(p->iobuf[0x1FE] != 0x55 || p->iobuf[0x1FF] != 0xAA) { 336 chat("not DOS\n"); 337 return -1; 338 } 339 p->dos = 0; 340 b = (Dosboot *)p->iobuf; 341 if(ishard && b->mediadesc != 0xF8){ 342 dp = (Dospart*)&p->iobuf[0x1BE]; 343 offset = 0xFFFFFFFF; 344 for(i = 0; i < 4; i++, dp++){ 345 if(dp->type == DMDDO){ 346 mbroffset = 63; 347 goto dmddo; 348 } 349 if(dp->type != FAT12 && dp->type != FAT16 && dp->type != FATHUGE) 350 continue; 351 if(dp->flag & 0x80){ 352 offset = GLONG(dp->start); 353 break; 354 } 355 if(GLONG(dp->start) < offset) 356 offset = GLONG(dp->start); 357 } 358 if(i != 4 || offset != 0xFFFFFFFF){ 359 dos->start = mbroffset+offset; 360 p = getclust(dos, 0); 361 if(p == 0 || p->iobuf[0x1FE] != 0x55 || p->iobuf[0x1FF] != 0xAA) 362 return -1; 363 } 364 p->dos = 0; 365 } 366 367 b = (Dosboot *)p->iobuf; 368 if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){ 369 chat("no dos file system\n"); 370 return -1; 371 } 372 373 if(chatty) 374 bootdump(b); 375 376 /* 377 * determine the systems' wondersous properties 378 */ 379 dos->sectsize = GSHORT(b->sectsize); 380 dos->clustsize = b->clustsize; 381 dos->clustbytes = dos->sectsize*dos->clustsize; 382 dos->nresrv = GSHORT(b->nresrv); 383 dos->nfats = b->nfats; 384 dos->rootsize = GSHORT(b->rootsize); 385 dos->volsize = GSHORT(b->volsize); 386 if(dos->volsize == 0) 387 dos->volsize = GLONG(b->bigvolsize); 388 dos->mediadesc = b->mediadesc; 389 dos->fatsize = GSHORT(b->fatsize); 390 dos->fataddr = dos->nresrv; 391 dos->rootaddr = dos->fataddr + dos->nfats*dos->fatsize; 392 i = dos->rootsize*sizeof(Dosdir) + dos->sectsize - 1; 393 i = i/dos->sectsize; 394 dos->dataaddr = dos->rootaddr + i; 395 dos->fatclusters = 2+(dos->volsize - dos->dataaddr)/dos->clustsize; 396 if(dos->fatclusters < 4087) 397 dos->fatbits = 12; 398 else 399 dos->fatbits = 16; 400 dos->freeptr = 2; 401 402 /* 403 * set up the root 404 */ 405 dos->root.dos = dos; 406 dos->root.pstart = 0; 407 dos->root.pcurrent = dos->root.lcurrent = 0; 408 dos->root.offset = 0; 409 dos->root.attr = DDIR; 410 dos->root.length = dos->rootsize*sizeof(Dosdir); 411 412 return 0; 413 } 414 415 static void 416 bootdump(Dosboot *b) 417 { 418 if(chatty == 0) 419 return; 420 print("magic: 0x%2.2x 0x%2.2x 0x%2.2x\n", 421 b->magic[0], b->magic[1], b->magic[2]); 422 print("version: \"%8.8s\"\n", b->version); 423 print("sectsize: %d\n", GSHORT(b->sectsize)); 424 print("allocsize: %d\n", b->clustsize); 425 print("nresrv: %d\n", GSHORT(b->nresrv)); 426 print("nfats: %d\n", b->nfats); 427 print("rootsize: %d\n", GSHORT(b->rootsize)); 428 print("volsize: %d\n", GSHORT(b->volsize)); 429 print("mediadesc: 0x%2.2x\n", b->mediadesc); 430 print("fatsize: %d\n", GSHORT(b->fatsize)); 431 print("trksize: %d\n", GSHORT(b->trksize)); 432 print("nheads: %d\n", GSHORT(b->nheads)); 433 print("nhidden: %d\n", GLONG(b->nhidden)); 434 print("bigvolsize: %d\n", GLONG(b->bigvolsize)); 435 print("driveno: %d\n", b->driveno); 436 print("reserved0: 0x%2.2x\n", b->reserved0); 437 print("bootsig: 0x%2.2x\n", b->bootsig); 438 print("volid: 0x%8.8x\n", GLONG(b->volid)); 439 print("label: \"%11.11s\"\n", b->label); 440 } 441 442 /* 443 * grab next element from a path, return the pointer to unprocessed portion of 444 * path. 445 */ 446 static char * 447 nextelem(char *path, char *elem) 448 { 449 int i; 450 451 while(*path == '/') 452 path++; 453 if(*path==0 || *path==' ') 454 return 0; 455 for(i=0; *path!='\0' && *path!='/' && *path!=' '; i++){ 456 if(i==28){ 457 print("name component too long\n"); 458 return 0; 459 } 460 *elem++ = *path++; 461 } 462 *elem = '\0'; 463 return path; 464 } 465 466 int 467 dosstat(Dos *dos, char *path, Dosfile *f) 468 { 469 char element[NAMELEN]; 470 471 *f = dos->root; 472 while(path = nextelem(path, element)){ 473 switch(doswalk(f, element)){ 474 case -1: 475 return -1; 476 case 0: 477 return 0; 478 } 479 } 480 return 1; 481 } 482 483 /* 484 * boot 485 */ 486 int 487 dosboot(Dos *dos, char *path) 488 { 489 Dosfile file; 490 long n; 491 long addr; 492 Exec *ep; 493 void (*b)(void); 494 495 switch(dosstat(dos, path, &file)){ 496 497 case -1: 498 print("error walking to %s\n", path); 499 return -1; 500 case 0: 501 print("%s not found\n", path); 502 return -1; 503 case 1: 504 print("found %8.8s.%3.3s attr 0x%ux start 0x%lux len %d\n", file.name, 505 file.ext, file.attr, file.pstart, file.length); 506 break; 507 } 508 509 /* 510 * read header 511 */ 512 ep = (Exec*)ialloc(sizeof(Exec), 0); 513 n = sizeof(Exec); 514 if(dosreadseg(&file, n, (ulong) ep) != n){ 515 print(premature); 516 return -1; 517 } 518 if(GLLONG(ep->magic) != Q_MAGIC){ 519 print("bad magic 0x%lux not a plan 9 executable!\n", GLLONG(ep->magic)); 520 return -1; 521 } 522 523 /* 524 * read text 525 */ 526 addr = PADDR(GLLONG(ep->entry)); 527 n = GLLONG(ep->text); 528 print("+%d", n); 529 if(dosreadseg(&file, n, addr) != n){ 530 print(premature); 531 return -1; 532 } 533 534 /* 535 * read data (starts at first page after kernel) 536 */ 537 addr = PGROUND(addr+n); 538 n = GLLONG(ep->data); 539 print("+%d", n); 540 if(dosreadseg(&file, n, addr) != n){ 541 print(premature); 542 return -1; 543 } 544 545 /* 546 * bss and entry point 547 */ 548 print("+%d\nstart at 0x%lux\n", GLLONG(ep->bss), GLLONG(ep->entry)); 549 550 /* 551 * Go to new code. It's up to the program to get its PC relocated to 552 * the right place. 553 */ 554 b = (void (*)(void))(PADDR(GLLONG(ep->entry))); 555 (*b)(); 556 return 0; 557 } 558 559 /* 560 * read in a segment 561 */ 562 long 563 dosreadseg(Dosfile *fp, long len, long addr) 564 { 565 char *a; 566 long n, sofar; 567 568 a = (char *)addr; 569 for(sofar = 0; sofar < len; sofar += n){ 570 n = 8*1024; 571 if(len - sofar < n) 572 n = len - sofar; 573 n = dosread(fp, a + sofar, n); 574 if(n <= 0) 575 break; 576 print("."); 577 } 578 return sofar; 579 } 580 581 /* 582 * set up a dos file name 583 */ 584 static void 585 setname(Dosfile *fp, char *from) 586 { 587 char *to; 588 589 to = fp->name; 590 for(; *from && to-fp->name < 8; from++, to++){ 591 if(*from == '.'){ 592 from++; 593 break; 594 } 595 if(*from >= 'a' && *from <= 'z') 596 *to = *from + 'A' - 'a'; 597 else 598 *to = *from; 599 } 600 while(to - fp->name < 8) 601 *to++ = ' '; 602 603 to = fp->ext; 604 for(; *from && to-fp->ext < 3; from++, to++){ 605 if(*from >= 'a' && *from <= 'z') 606 *to = *from + 'A' - 'a'; 607 else 608 *to = *from; 609 } 610 while(to-fp->ext < 3) 611 *to++ = ' '; 612 613 chat("name is %8.8s %3.3s\n", fp->name, fp->ext); 614 } 615