Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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 *
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
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
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
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
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
298
biosreset(void)
299
{
300
	biosinit0();
301
}
302
 
303
static void
304
biosinit(void)
305
{
306
}
307
 
308
static Chan*
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
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
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
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*
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
442
biosstat(Chan *c, uchar *db, int n)
443
{
444
	return devstat(c, db, n, nil, 0, biosgen);
445
}
446
 
447
static Chan*
448
biosopen(Chan *c, int omode)
449
{
450
	return devopen(c, omode, 0, 0, biosgen);
451
}
452
 
453
static void
454
biosclose(Chan *)
455
{
456
}
457
 
458
#ifdef UNUSED
459
int
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
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
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
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
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
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
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
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
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
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 *
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
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
};