xref: /plan9/sys/src/9/pcboot/devbios.c (revision 07b4782c30a417782adf37507cd9a64239a8e687)
1 /*
2  * read-only driver for BIOS LBA devices.
3  * devbios must be initialised first and no disks may be accessed
4  * via non-BIOS means (i.e., talking to the disk controller directly).
5  * EDD 4.0 defines the INT 0x13 functions.
6  *
7  * heavily dependent upon correct BIOS implementation.
8  * some bioses (e.g., vmware) seem to hang for two minutes then report
9  * a disk timeout on reset and extended read operations.
10  */
11 #include	"u.h"
12 #include	"../port/lib.h"
13 #include	"mem.h"
14 #include	"dat.h"
15 #include	"fns.h"
16 #include	"io.h"
17 #include	"ureg.h"
18 #include	"pool.h"
19 #include	"../port/error.h"
20 #include	"../port/netif.h"
21 #include	"../port/sd.h"
22 #include	"dosfs.h"
23 
24 #define TYPE(q)		((ulong)(q).path & 0xf)
25 #define UNIT(q)		(((ulong)(q).path>>4) & 0xff)
26 #define L(q)		(((ulong)(q).path>>12) & 0xf)
27 #define QID(u, t) 	((u)<<4 | (t))
28 
29 typedef struct Biosdev Biosdev;
30 typedef struct Dap Dap;
31 typedef uvlong Devbytes, Devsects;
32 typedef uchar Devid;
33 typedef struct Edrvparam Edrvparam;
34 
35 enum {
36 	Debug = 0,
37 	Pause = 0,			/* delay to read debugging */
38 
39 	Minsectsz	= 512,		/* for disks */
40 	Maxsectsz	= 2048,		/* for optical (CDs, etc.) */
41 
42 	Highshort	= ((1ul<<16) - 1) << 16,  /* upper short of a long */
43 
44 	Maxdevs		= 8,
45 	CF		= 1,		/* carry flag: indicates an error */
46 	Flopid		= 0,		/* first floppy */
47 	Baseid		= 0x80,		/* first disk */
48 
49 	Diskint		= 0x13,		/* "INT 13" for bios disk i/o */
50 
51 	/* cx capability bits in Biosckext results */
52 	Fixeddisk	= 1<<0,		/* fixed disk access subset */
53 	Drlock		= 1<<1,
54 	Edd		= 1<<2,		/* enhanced disk drive support */
55 	Bit64ext	= 1<<3,
56 
57 	/* bios calls: int 0x13 disk services w buffer at es:bx */
58 	Biosinit	= 0,		/* initialise disk & floppy ctlrs */
59 	Biosdrvsts,			/* status of last int 0x13 call */
60 	Biosdrvparam	= 8,
61 	Biosctlrinit,
62 	Biosreset	=  0xd,		/* reset disk */
63 	Biosdrvrdy	= 0x10,
64 	/* extended int 0x13 calls w dap at ds:si */
65 	Biosckext	= 0x41,
66 	Biosrdsect,
67 	Biosedrvparam	= 0x48,
68 
69 	/* magic numbers for bios calls */
70 	Imok		= 0x55aa,
71 	Youreok		= 0xaa55,
72 };
73 enum {
74 	Qzero,				/* assumed to be 0 by devattach */
75 	Qtopdir		= 1,
76 	Qtopbase,
77 	Qtopctl		= Qtopbase,
78 	Qtopend,
79 
80 	Qunitdir,
81 	Qunitbase,
82 	Qctl		= Qunitbase,
83 	Qdata,
84 
85 	Qtopfiles	= Qtopend-Qtopbase,
86 };
87 
88 struct Biosdev {
89 	Devbytes size;
90 	Devbytes offset;
91 	Devid	id;			/* drive number; e.g., 0x80 */
92 	ushort	sectsz;
93 	Chan	*rootchan;
94 	Bootfs;
95 };
96 
97 struct Dap {				/* a device address packet */
98 	uchar	size;
99 	uchar	_unused1;
100 	uchar	nsects;
101 	uchar	_unused2;
102 	union {
103 		ulong	addr;		/* actual address (nominally seg:off) */
104 		struct {
105 			ushort	addroff;	/* :offset */
106 			ushort	addrseg;	/* segment: */
107 		};
108 	};
109 	uvlong	stsect;			/* starting sector */
110 
111 	uvlong	addr64;			/* instead of addr, if addr is ~0 */
112 	ulong	lnsects;		/* nsects to match addr64 */
113 	ulong	_unused3;
114 };
115 
116 struct Edrvparam {
117 	ushort	size;			/* max. buffer (struct) size */
118 	ushort	flags;
119 	ulong	physcyls;
120 	ulong	physheads;
121 	ulong	phystracksects;
122 	uvlong	physsects;
123 	ushort	sectsz;
124 
125 	/* pointer is required to be unaligned, bytes 26-29.  ick. */
126 //	void	*dpte;			/* ~0ull: invalid */
127 	ushort	dpteoff;		/* device parameter table extension */
128 	ushort	dpteseg;
129 
130 	/* remainder from edd 3.0 spec */
131 	ushort	key;			/* 0xbedd if device path info present */
132 	uchar	dpilen;			/* must be 44 (says edd 4.0) */
133 	uchar	_unused1;
134 	ushort	_unused2;
135 	char	bustype[4];		/* "PCI" or "ISA" */
136 	char	ifctype[8]; /* "ATA", "ATAPI", "SCSI", "USB", "1394", "FIBRE" */
137 	uvlong	ifcpath;
138 	uvlong	devpath[2];
139 	uchar	_unused3;
140 	uchar	dpicksum;
141 };
142 
143 int biosinited;
144 int biosndevs;
145 
146 void *biosgetfspart(int i, char *name, int chatty);
147 
148 static Biosdev bdev[Maxdevs];
149 static Ureg regs;
150 static RWlock devs;
151 
152 static int	dreset(Devid drive);
153 static Devbytes	extgetsize(Biosdev *);
154 static int	drivecap(Devid drive);
155 
156 /* convert ah error code to a string (just common cases) */
157 static char *
strerr(uchar err)158 strerr(uchar err)
159 {
160 	switch (err) {
161 	case 0:
162 		return "no error";
163 	case 1:
164 		return "bad command";
165 	case 0x80:
166 		return "disk timeout";
167 	default:
168 		return "unknown";
169 	}
170 }
171 
172 static void
assertlow64k(uintptr p,char * tag)173 assertlow64k(uintptr p, char *tag)
174 {
175 	if (p & Highshort)
176 		panic("devbios: %s address %#p not in bottom 64k", tag, p);
177 }
178 
179 static void
initrealregs(Ureg * ureg)180 initrealregs(Ureg *ureg)
181 {
182 	memset(ureg, 0, sizeof *ureg);
183 }
184 
185 /*
186  * caller must zero or otherwise initialise *ureg,
187  * other than ax, bx, dx, si & ds.
188  */
189 static int
biosdiskcall(Ureg * ureg,uchar op,ulong bx,ulong dx,ulong si)190 biosdiskcall(Ureg *ureg, uchar op, ulong bx, ulong dx, ulong si)
191 {
192 	int s;
193 	uchar err;
194 
195 	s = splhi();		/* don't let the bios call be interrupted */
196 	initrealregs(ureg);
197 	ureg->ax = op << 8;
198 	ureg->bx = bx;
199 	ureg->dx = dx;		/* often drive id */
200 	assertlow64k(si, "dap");
201 	if(si && (si & Highshort) != ((si + Maxsectsz - 1) & Highshort))
202 		print("biosdiskcall: dap address %#lux too near segment boundary\n",
203 			si);
204 
205 	ureg->si = si;		/* ds:si forms data address packet addr */
206 	ureg->ds = 0;		/* bottom 64K */
207 	ureg->es = 0;		/* es:bx is conventional buffer */
208 	ureg->di = 0;		/* buffer segment? */
209 	ureg->flags = 0;
210 
211 	/*
212 	 * *ureg is copied into low memory (realmoderegs) and thence into
213 	 * the machine registers before the BIOS call, and the registers are
214 	 * copied into realmoderegs and thence into *ureg after.
215 	 *
216 	 * realmode loads these registers: di, si, ax, bx, cx, dx, ds, es.
217 	 */
218 	ureg->trap = Diskint;
219 	realmode(ureg);
220 
221 	if (ureg->flags & CF) {
222 		if (dx == Baseid) {
223 			err = ureg->ax >> 8;
224 			print("\nbiosdiskcall: int %#x op %#ux drive %#lux "
225 				"failed, ah error code %#ux (%s)\n",
226 				Diskint, op, dx, err, strerr(err));
227 		}
228 		splx(s);
229 		return -1;
230 	}
231 	splx(s);
232 	return 0;
233 }
234 
235 /*
236  * Find out what the bios knows about devices.
237  * our boot device could be usb; ghod only knows where it will appear.
238  */
239 int
biosinit0(void)240 biosinit0(void)
241 {
242 	int cap, mask, lastbit, ndrive;
243 	Devbytes size;
244 	Devid devid;
245 	Biosdev *bdp;
246 	static int beenhere;
247 
248 	delay(Pause);		/* pause to read the screen (DEBUG) */
249 	if (biosinited || beenhere)
250 		return 0;
251 	beenhere = 1;
252 
253 	ndrive = *(uchar *)KADDR(0x475);		/* from bda */
254 	if (Debug)
255 		print("%d bios drive(s)\n", ndrive);
256 	mask = lastbit = 0;
257 	for (devid = Baseid, biosndevs = 0; devid != 0 && biosndevs < Maxdevs &&
258 	    biosndevs < ndrive; devid++) {
259 		cap = drivecap(devid);
260 		/* don't reset; it seems to hang the bios */
261 		if(cap < 0 || (cap & (Fixeddisk|Edd)) != (Fixeddisk|Edd)
262 		    /* || devid != Baseid && dreset(devid) < 0 || */)
263 			continue;		/* no suitable device */
264 
265 		/* found a live one */
266 		lastbit = 1 << biosndevs;
267 		mask |= lastbit;
268 
269 		bdp = &bdev[biosndevs];
270 		bdp->id = devid;
271 		size = extgetsize(bdp);
272 		if (size == 0)
273 			continue;		/* no device */
274 		bdp->size = size;
275 
276 		print("bios%d: drive %#ux: %,llud bytes, %d-byte sectors\n",
277 			biosndevs, devid, size, bdp->sectsz);
278 		biosndevs++;
279 	}
280 	USED(lastbit);
281 
282 	if (Debug && ndrive != biosndevs)
283 		print("devbios: expected %d drives, found %d\n", ndrive, biosndevs);
284 
285 	/*
286 	 * some bioses seem to only be able to read from drive number 0x80 and
287 	 * can't read from the highest drive number, even if there is only one.
288 	 */
289 	if (biosndevs > 0)
290 		biosinited = 1;
291 	else
292 		panic("devbios: no bios drives seen"); /* 9loadusb needs ≥ 1 */
293 	delay(Pause);		/* pause to read the screen (DEBUG) */
294 	return mask;
295 }
296 
297 static void
biosreset(void)298 biosreset(void)
299 {
300 	biosinit0();
301 }
302 
303 static void
biosinit(void)304 biosinit(void)
305 {
306 }
307 
308 static Chan*
biosattach(char * spec)309 biosattach(char *spec)
310 {
311 	ulong drive;
312 	char *p;
313 	Chan *chan;
314 
315 	drive = 0;
316 	if(spec && *spec){
317 		drive = strtoul(spec, &p, 0);
318 		if((drive == 0 && p == spec) || *p || (drive >= Maxdevs))
319 			error(Ebadarg);
320 	}
321 	if(bdev[drive].rootchan)
322 		return bdev[drive].rootchan;
323 
324 	chan = devattach(L'☹', spec);
325 	if(waserror()){
326 		chanfree(chan);
327 		nexterror();
328 	}
329 	chan->dev = drive;
330 	bdev[drive].rootchan = chan;
331 	/* arbitrary initialisation can go here */
332 	poperror();
333 	return chan;
334 }
335 
336 static int
unitgen(Chan * c,ulong type,Dir * dp)337 unitgen(Chan *c, ulong type, Dir *dp)
338 {
339 	int perm, t;
340 	ulong vers;
341 	vlong size;
342 	char *p;
343 	Qid q;
344 
345 	perm = 0644;
346 	size = 0;
347 //	d = unit2dev(UNIT(c->qid));
348 //	vers = d->vers;
349 	vers = 0;
350 	t = QTFILE;
351 
352 	switch(type){
353 	default:
354 		return -1;
355 	case Qctl:
356 		p = "ctl";
357 		break;
358 	case Qdata:
359 		p = "data";
360 		perm = 0640;
361 		break;
362 	}
363 	mkqid(&q, QID(UNIT(c->qid), type), vers, t);
364 	devdir(c, q, p, size, eve, perm, dp);
365 	return 1;
366 }
367 
368 static int
topgen(Chan * c,ulong type,Dir * d)369 topgen(Chan *c, ulong type, Dir *d)
370 {
371 	int perm;
372 	vlong size;
373 	char *p;
374 	Qid q;
375 
376 	size = 0;
377 	switch(type){
378 	default:
379 		return -1;
380 	case Qdata:
381 		p = "data";
382 		perm = 0644;
383 		break;
384 	}
385 	mkqid(&q, type, 0, QTFILE);
386 	devdir(c, q, p, size, eve, perm, d);
387 	return 1;
388 }
389 
390 static int
biosgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)391 biosgen(Chan *c, char *, Dirtab *, int, int s, Dir *dp)
392 {
393 	Qid q;
394 
395 	if(c->qid.path == 0){
396 		switch(s){
397 		case DEVDOTDOT:
398 			q.path = 0;
399 			q.type = QTDIR;
400 			devdir(c, q, "#☹", 0, eve, 0555, dp);
401 			break;
402 		case 0:
403 			q.path = Qtopdir;
404 			q.type = QTDIR;
405 			devdir(c, q, "bios", 0, eve, 0555, dp);
406 			break;
407 		default:
408 			return -1;
409 		}
410 		return 1;
411 	}
412 
413 	switch(TYPE(c->qid)){
414 	default:
415 		return -1;
416 	case Qtopdir:
417 		if(s == DEVDOTDOT){
418 			mkqid(&q, Qzero, 0, QTDIR);
419 			devdir(c, q, "bios", 0, eve, 0555, dp);
420 			return 1;
421 		}
422 		if(s < Qtopfiles)
423 			return topgen(c, Qtopbase + s, dp);
424 		s -= Qtopfiles;
425 		if(s >= 1)
426 			return -1;
427 		mkqid(&q, QID(s, Qunitdir), 0, QTDIR);
428 		devdir(c, q, "bios", 0, eve, 0555, dp);
429 		return 1;
430 	case Qdata:
431 		return unitgen(c, TYPE(c->qid), dp);
432 	}
433 }
434 
435 static Walkqid*
bioswalk(Chan * c,Chan * nc,char ** name,int nname)436 bioswalk(Chan *c, Chan *nc, char **name, int nname)
437 {
438 	return devwalk(c, nc, name, nname, nil, 0, biosgen);
439 }
440 
441 static int
biosstat(Chan * c,uchar * db,int n)442 biosstat(Chan *c, uchar *db, int n)
443 {
444 	return devstat(c, db, n, nil, 0, biosgen);
445 }
446 
447 static Chan*
biosopen(Chan * c,int omode)448 biosopen(Chan *c, int omode)
449 {
450 	return devopen(c, omode, 0, 0, biosgen);
451 }
452 
453 static void
biosclose(Chan *)454 biosclose(Chan *)
455 {
456 }
457 
458 #ifdef UNUSED
459 int
biosboot(int dev,char * file,Boot * b)460 biosboot(int dev, char *file, Boot *b)
461 {
462 	Bootfs *fs;
463 
464 	if(strncmp(file, "dos!", 4) == 0)
465 		file += 4;
466 	if(strchr(file, '!') != nil || strcmp(file, "") == 0) {
467 		print("syntax is bios0!file\n");
468 		return -1;
469 	}
470 
471 	fs = biosgetfspart(dev, "9fat", 1);
472 	if(fs == nil)
473 		return -1;
474 	return fsboot(fs, file, b);
475 }
476 #endif
477 
478 /* read n bytes at sector offset into a from drive id */
479 long
sectread(Biosdev * bdp,void * a,long n,Devsects offset)480 sectread(Biosdev *bdp, void *a, long n, Devsects offset)
481 {
482 	uchar *xch;
483 	uintptr xchaddr;
484 	Dap *dap;
485 
486 	if(bdp->sectsz <= 0 || n < 0 || n > bdp->sectsz)
487 		return -1;
488 	xch = (uchar *)BIOSXCHG;
489 	assertlow64k(PADDR(xch), "biosxchg");
490 	if(Debug)
491 		/* scribble on the buffer to provoke trouble */
492 		memset(xch, 'r', bdp->sectsz);
493 
494 	/* read into BIOSXCHG; alloc space for a worst-case (optical) sector */
495 	dap = (Dap *)(xch + Maxsectsz);
496 	assertlow64k(PADDR(dap), "Dap");
497 	memset(dap, 0, sizeof *dap);
498 	dap->size = sizeof *dap;
499 	dap->nsects = 1;
500 	dap->stsect = offset;
501 
502 	xchaddr = PADDR(xch);
503 	assertlow64k(xchaddr, "sectread buffer");
504 	dap->addr = xchaddr;		/* ulong version */
505 	dap->addroff = xchaddr;		/* pedantic seg:off */
506 	dap->addrseg = 0;
507 	dap->addr64 = xchaddr;		/* paranoid redundancy */
508 	dap->lnsects = 1;
509 
510 	/*
511 	 * ensure that entire buffer fits in low memory.
512 	 */
513 	if((dap->addr & Highshort) !=
514 	    ((dap->addr + Minsectsz - 1) & Highshort))
515 		print("devbios: sectread: address %#lux too near seg boundary\n",
516 			dap->addr);
517 	if (Debug)
518 		print("reading bios drive %#ux sector %lld -> %#lux...",
519 			bdp->id, offset, dap->addr);
520 	delay(Pause);			/* pause to read the screen (DEBUG) */
521 
522 	/*
523 	 * int 13 read sector expects buffer seg in di?,
524 	 * dap in si, 0x42 in ah, drive in dl.
525 	 */
526 	if (biosdiskcall(&regs, Biosrdsect, 0, bdp->id, PADDR(dap)) < 0) {
527 		print("devbios: sectread: bios failed to read %ld @ sector %lld of %#ux\n",
528 			n, offset, bdp->id);
529 		return -1;
530 	}
531 	if (dap->nsects != 1)
532 		panic("devbios: sector read ok but read %d sectors",
533 			dap->nsects);
534 	if (Debug)
535 		print("OK\n");
536 
537 	/* copy into caller's buffer */
538 	memmove(a, xch, n);
539 	if(0 && Debug)
540 		print("-%ux %ux %ux %ux--%16.16s-\n",
541 			xch[0], xch[1], xch[2], xch[3], (char *)xch + 480);
542 	delay(Pause);		/* pause to read the screen (DEBUG) */
543 	return n;
544 }
545 
546 /* seems to hang bioses, at least vmware's */
547 static int
dreset(Devid drive)548 dreset(Devid drive)
549 {
550 	print("devbios: resetting %#ux...", drive);
551 	/* ignore carry flag for Biosinit */
552 	biosdiskcall(&regs, Biosinit, 0, drive, 0);
553 	print("\n");
554 	return regs.ax? -1: 0;		/* ax != 0 on error */
555 }
556 
557 /* returns capabilities bitmap */
558 static int
drivecap(Devid drive)559 drivecap(Devid drive)
560 {
561 	int cap;
562 
563 	if (biosdiskcall(&regs, Biosckext, Imok, drive, 0) < 0)
564 		/*
565 		 * we have an old bios without extensions, in theory.
566 		 * in practice, there may just be no drive for this number.
567 		 */
568 		return -1;
569 	if(regs.bx != Youreok){
570 		print("devbios: buggy bios: drive %#ux extension check "
571 			 "returned %lux in bx\n", drive, regs.bx);
572 		return -1;
573 	}
574 	cap = regs.cx;
575 	if (Debug) {
576 		print("bios drive %#ux extensions version %#x.%d cx %#ux\n",
577 			drive, (uchar)(regs.ax >> 8), (uchar)regs.ax, cap);
578 		if ((uchar)(regs.ax >> 8) < 0x30) {
579 			print("drivecap: extensions prior to 0x30\n");
580 			return -1;
581 		}
582 		print("\tsubsets supported:");
583 		if (cap & Fixeddisk)
584 			print(" fixed disk access;");
585 		if (cap & Drlock)
586 			print(" drive locking;");
587 		if (cap & Edd)
588 			print(" enhanced disk support;");
589 		if (cap & Bit64ext)
590 			print(" 64-bit extensions;");
591 		print("\n");
592 	}
593 	delay(Pause);			/* pause to read the screen (DEBUG) */
594 	return cap;
595 }
596 
597 /* extended get size; reads bdp->id, fills in bdp->sectsz, returns # sectors */
598 static Devbytes
extgetsize(Biosdev * bdp)599 extgetsize(Biosdev *bdp)
600 {
601 	ulong sectsz;
602 	Edrvparam *edp;
603 
604 	edp = (Edrvparam *)BIOSXCHG;
605 	memset(edp, 0, sizeof *edp);
606 	edp->size = sizeof *edp;
607 	edp->dpteseg = edp->dpteoff = ~0;	/* no pointer */
608 	edp->dpilen = 44;
609 
610 	if (biosdiskcall(&regs, Biosedrvparam, 0, bdp->id, PADDR(edp)) < 0)
611 		return 0;		/* old bios without extensions */
612 	if(Debug) {
613 		print("bios drive %#ux info flags %#ux", bdp->id, edp->flags);
614 		if (edp->key == 0xbedd)
615 			print("; edd 3.0  %.4s %.8s",
616 				edp->bustype, edp->ifctype);
617 		else
618 			print("; NOT edd 3.0 compliant (key %#ux)", edp->key);
619 		print("\n");
620 	}
621 	if (edp->sectsz <= 0) {
622 		print("devbios: drive %#ux: sector size <= 0\n", bdp->id);
623 		edp->sectsz = 1;		/* don't divide by 0 */
624 		return 0;
625 	}
626 	sectsz = edp->sectsz;
627 	if (sectsz > Maxsectsz) {
628 		print("devbios: sector size %lud > %d\n", sectsz, Maxsectsz);
629 		return 0;
630 	}
631 	bdp->sectsz = sectsz;
632 	return edp->physsects * sectsz;
633 }
634 
635 vlong
biossize(uint dev)636 biossize(uint dev)
637 {
638 	Biosdev *bdp;
639 
640 	if (dev >= biosndevs)
641 		return -1;
642 	bdp = &bdev[dev];
643 	if (bdp->sectsz <= 0)
644 		return -1;
645 	return bdp->size / bdp->sectsz;
646 }
647 
648 long
biossectsz(uint dev)649 biossectsz(uint dev)
650 {
651 	Biosdev *bdp;
652 
653 	if (dev >= biosndevs)
654 		return -1;
655 	bdp = &bdev[dev];
656 	if (bdp->sectsz <= 0)
657 		return -1;
658 	return bdp->sectsz;
659 }
660 
661 long
biosread0(Bootfs * fs,void * a,long n)662 biosread0(Bootfs *fs, void *a, long n)
663 {
664 	int want, got, part, dev;
665 	long totnr, stuck;
666 	Devbytes offset;
667 	Biosdev *bdp;
668 
669 	dev = fs->dev;				/* only use of fs */
670 	if(dev > biosndevs)
671 		return -1;
672 	if (n <= 0)
673 		return n;
674 	bdp = &bdev[dev];
675 	offset = bdp->offset;
676 	stuck = 0;
677 	for (totnr = 0; totnr < n && stuck < 4; totnr += got) {
678 		if (bdp->sectsz == 0) {
679 			print("devbios: zero sector size\n");
680 			return -1;
681 		}
682 		want = bdp->sectsz;
683 		if (totnr + want > n)
684 			want = n - totnr;
685 		if(0 && Debug && debugload)
686 			print("bios%d, read: %ld @ off %lld, want: %d, id: %#ux\n",
687 				dev, n, offset, want, bdp->id);
688 		part = offset % bdp->sectsz;
689 		if (part != 0) {	/* back up to start of sector */
690 			offset -= part;
691 			totnr  -= part;
692 			if (totnr < 0) {
693 				print("biosread0: negative count %ld\n", totnr);
694 				return -1;
695 			}
696 		}
697 		if ((vlong)offset < 0) {
698 			print("biosread0: negative offset %lld\n", offset);
699 			return -1;
700 		}
701 		got = sectread(bdp, (char *)a + totnr, want,
702 			offset / bdp->sectsz);
703 		if(got <= 0)
704 			return -1;
705 		offset += got;
706 		bdp->offset = offset;
707 		if (got < bdp->sectsz)
708 			stuck++;	/* we'll have to re-read this sector */
709 		else
710 			stuck = 0;
711 	}
712 	return totnr;
713 }
714 
715 vlong
biosseek(Bootfs * fs,vlong off)716 biosseek(Bootfs *fs, vlong off)
717 {
718 	if (off < 0) {
719 		print("biosseek(fs, %lld) is illegal\n", off);
720 		return -1;
721 	}
722 	if(fs->dev > biosndevs) {
723 		print("biosseek: fs->dev %d > biosndevs %d\n", fs->dev, biosndevs);
724 		return -1;
725 	}
726 	bdev[fs->dev].offset = off;	/* do not know size... (yet) */
727 	return off;
728 }
729 
730 static long
biosread(Chan * c,void * db,long n,vlong off)731 biosread(Chan *c, void *db, long n, vlong off)
732 {
733 	Biosdev *bp;
734 
735 	switch(TYPE(c->qid)){
736 	default:
737 		error(Eperm);
738 	case Qzero:
739 	case Qtopdir:
740 		return devdirread(c, db, n, 0, 0, biosgen);
741 	case Qdata:
742 		bp = &bdev[UNIT(c->qid)];
743 		if (bp->rootchan == nil)
744 			panic("biosread: nil root chan for bios%ld",
745 				UNIT(c->qid));
746 		biosseek(&bp->Bootfs, off);
747 		return biosread0(&bp->Bootfs, db, n);
748 	}
749 }
750 
751 /* name is typically "9fat" */
752 void *
biosgetfspart(int i,char * name,int chatty)753 biosgetfspart(int i, char *name, int chatty)
754 {
755 	char part[32];
756 	static Bootfs fs;
757 
758 	fs.dev = i;
759 	fs.diskread = biosread0;
760 	fs.diskseek = biosseek;
761 	snprint(part, sizeof part, "#S/sdB0/%s", name);
762 	if(dosinit(&fs, part) < 0){
763 		if(chatty)
764 			print("bios%d!%s does not contain a FAT file system\n",
765 				i, name);
766 		return nil;
767 	}
768 	return &fs;
769 }
770 
771 static long
bioswrite(Chan *,void *,long,vlong)772 bioswrite(Chan *, void *, long, vlong)
773 {
774 	error("bios devices are read-only in bootstrap");
775 	return 0;
776 }
777 
778 Dev biosdevtab = {
779 	L'☹',
780 	"bios",
781 
782 	biosreset,
783 	biosinit,
784 	devshutdown,
785 	biosattach,
786 	bioswalk,
787 	biosstat,
788 	biosopen,
789 	devcreate,
790 	biosclose,
791 	biosread,
792 	devbread,
793 	bioswrite,
794 	devbwrite,
795 	devremove,
796 	devwstat,
797 	devpower,
798 	devconfig,
799 };
800