xref: /inferno-os/os/boot/mpc/dosboot.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
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