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*
getclust(Dos * dos,long sector)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
fatwalk(Dos * dos,int n)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
fileaddr(Dosfile * fp,long ltarget)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
dosread(Dosfile * fp,void * a,long n)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
doswalk(Dosfile * file,char * name)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
dosinit(Dos * dos,int start,int ishard)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
bootdump(Dosboot * b)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 *
nextelem(char * path,char * elem)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
dosstat(Dos * dos,char * path,Dosfile * f)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
dosboot(Dos * dos,char * path)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) != E_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
dosreadseg(Dosfile * fp,long len,long addr)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
setname(Dosfile * fp,char * from)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