xref: /plan9/sys/src/9/pcboot/devbios.c (revision 07b4782c30a417782adf37507cd9a64239a8e687)
125210b06SDavid du Colombier /*
225210b06SDavid du Colombier  * read-only driver for BIOS LBA devices.
325210b06SDavid du Colombier  * devbios must be initialised first and no disks may be accessed
425210b06SDavid du Colombier  * via non-BIOS means (i.e., talking to the disk controller directly).
525210b06SDavid du Colombier  * EDD 4.0 defines the INT 0x13 functions.
625210b06SDavid du Colombier  *
725210b06SDavid du Colombier  * heavily dependent upon correct BIOS implementation.
825210b06SDavid du Colombier  * some bioses (e.g., vmware) seem to hang for two minutes then report
925210b06SDavid du Colombier  * a disk timeout on reset and extended read operations.
1025210b06SDavid du Colombier  */
1125210b06SDavid du Colombier #include	"u.h"
1225210b06SDavid du Colombier #include	"../port/lib.h"
1325210b06SDavid du Colombier #include	"mem.h"
1425210b06SDavid du Colombier #include	"dat.h"
1525210b06SDavid du Colombier #include	"fns.h"
1625210b06SDavid du Colombier #include	"io.h"
1725210b06SDavid du Colombier #include	"ureg.h"
1825210b06SDavid du Colombier #include	"pool.h"
1925210b06SDavid du Colombier #include	"../port/error.h"
2025210b06SDavid du Colombier #include	"../port/netif.h"
2125210b06SDavid du Colombier #include	"../port/sd.h"
2225210b06SDavid du Colombier #include	"dosfs.h"
2325210b06SDavid du Colombier 
2425210b06SDavid du Colombier #define TYPE(q)		((ulong)(q).path & 0xf)
2525210b06SDavid du Colombier #define UNIT(q)		(((ulong)(q).path>>4) & 0xff)
2625210b06SDavid du Colombier #define L(q)		(((ulong)(q).path>>12) & 0xf)
2725210b06SDavid du Colombier #define QID(u, t) 	((u)<<4 | (t))
2825210b06SDavid du Colombier 
2925210b06SDavid du Colombier typedef struct Biosdev Biosdev;
3025210b06SDavid du Colombier typedef struct Dap Dap;
3125210b06SDavid du Colombier typedef uvlong Devbytes, Devsects;
3225210b06SDavid du Colombier typedef uchar Devid;
3325210b06SDavid du Colombier typedef struct Edrvparam Edrvparam;
3425210b06SDavid du Colombier 
3525210b06SDavid du Colombier enum {
3625210b06SDavid du Colombier 	Debug = 0,
3725210b06SDavid du Colombier 	Pause = 0,			/* delay to read debugging */
3825210b06SDavid du Colombier 
3925210b06SDavid du Colombier 	Minsectsz	= 512,		/* for disks */
4025210b06SDavid du Colombier 	Maxsectsz	= 2048,		/* for optical (CDs, etc.) */
4125210b06SDavid du Colombier 
4225210b06SDavid du Colombier 	Highshort	= ((1ul<<16) - 1) << 16,  /* upper short of a long */
4325210b06SDavid du Colombier 
4425210b06SDavid du Colombier 	Maxdevs		= 8,
4525210b06SDavid du Colombier 	CF		= 1,		/* carry flag: indicates an error */
4625210b06SDavid du Colombier 	Flopid		= 0,		/* first floppy */
4725210b06SDavid du Colombier 	Baseid		= 0x80,		/* first disk */
4825210b06SDavid du Colombier 
4925210b06SDavid du Colombier 	Diskint		= 0x13,		/* "INT 13" for bios disk i/o */
5025210b06SDavid du Colombier 
5125210b06SDavid du Colombier 	/* cx capability bits in Biosckext results */
5225210b06SDavid du Colombier 	Fixeddisk	= 1<<0,		/* fixed disk access subset */
5325210b06SDavid du Colombier 	Drlock		= 1<<1,
5425210b06SDavid du Colombier 	Edd		= 1<<2,		/* enhanced disk drive support */
5525210b06SDavid du Colombier 	Bit64ext	= 1<<3,
5625210b06SDavid du Colombier 
5725210b06SDavid du Colombier 	/* bios calls: int 0x13 disk services w buffer at es:bx */
5825210b06SDavid du Colombier 	Biosinit	= 0,		/* initialise disk & floppy ctlrs */
5925210b06SDavid du Colombier 	Biosdrvsts,			/* status of last int 0x13 call */
6025210b06SDavid du Colombier 	Biosdrvparam	= 8,
6125210b06SDavid du Colombier 	Biosctlrinit,
6225210b06SDavid du Colombier 	Biosreset	=  0xd,		/* reset disk */
6325210b06SDavid du Colombier 	Biosdrvrdy	= 0x10,
6425210b06SDavid du Colombier 	/* extended int 0x13 calls w dap at ds:si */
6525210b06SDavid du Colombier 	Biosckext	= 0x41,
6625210b06SDavid du Colombier 	Biosrdsect,
6725210b06SDavid du Colombier 	Biosedrvparam	= 0x48,
6825210b06SDavid du Colombier 
6925210b06SDavid du Colombier 	/* magic numbers for bios calls */
7025210b06SDavid du Colombier 	Imok		= 0x55aa,
7125210b06SDavid du Colombier 	Youreok		= 0xaa55,
7225210b06SDavid du Colombier };
7325210b06SDavid du Colombier enum {
7425210b06SDavid du Colombier 	Qzero,				/* assumed to be 0 by devattach */
7525210b06SDavid du Colombier 	Qtopdir		= 1,
7625210b06SDavid du Colombier 	Qtopbase,
7725210b06SDavid du Colombier 	Qtopctl		= Qtopbase,
7825210b06SDavid du Colombier 	Qtopend,
7925210b06SDavid du Colombier 
8025210b06SDavid du Colombier 	Qunitdir,
8125210b06SDavid du Colombier 	Qunitbase,
8225210b06SDavid du Colombier 	Qctl		= Qunitbase,
8325210b06SDavid du Colombier 	Qdata,
8425210b06SDavid du Colombier 
8525210b06SDavid du Colombier 	Qtopfiles	= Qtopend-Qtopbase,
8625210b06SDavid du Colombier };
8725210b06SDavid du Colombier 
8825210b06SDavid du Colombier struct Biosdev {
8925210b06SDavid du Colombier 	Devbytes size;
9025210b06SDavid du Colombier 	Devbytes offset;
9125210b06SDavid du Colombier 	Devid	id;			/* drive number; e.g., 0x80 */
9225210b06SDavid du Colombier 	ushort	sectsz;
9325210b06SDavid du Colombier 	Chan	*rootchan;
9425210b06SDavid du Colombier 	Bootfs;
9525210b06SDavid du Colombier };
9625210b06SDavid du Colombier 
9725210b06SDavid du Colombier struct Dap {				/* a device address packet */
9825210b06SDavid du Colombier 	uchar	size;
9925210b06SDavid du Colombier 	uchar	_unused1;
10025210b06SDavid du Colombier 	uchar	nsects;
10125210b06SDavid du Colombier 	uchar	_unused2;
10225210b06SDavid du Colombier 	union {
10325210b06SDavid du Colombier 		ulong	addr;		/* actual address (nominally seg:off) */
10425210b06SDavid du Colombier 		struct {
10525210b06SDavid du Colombier 			ushort	addroff;	/* :offset */
10625210b06SDavid du Colombier 			ushort	addrseg;	/* segment: */
10725210b06SDavid du Colombier 		};
10825210b06SDavid du Colombier 	};
10925210b06SDavid du Colombier 	uvlong	stsect;			/* starting sector */
11025210b06SDavid du Colombier 
11125210b06SDavid du Colombier 	uvlong	addr64;			/* instead of addr, if addr is ~0 */
11225210b06SDavid du Colombier 	ulong	lnsects;		/* nsects to match addr64 */
11325210b06SDavid du Colombier 	ulong	_unused3;
11425210b06SDavid du Colombier };
11525210b06SDavid du Colombier 
11625210b06SDavid du Colombier struct Edrvparam {
11725210b06SDavid du Colombier 	ushort	size;			/* max. buffer (struct) size */
11825210b06SDavid du Colombier 	ushort	flags;
11925210b06SDavid du Colombier 	ulong	physcyls;
12025210b06SDavid du Colombier 	ulong	physheads;
12125210b06SDavid du Colombier 	ulong	phystracksects;
12225210b06SDavid du Colombier 	uvlong	physsects;
12325210b06SDavid du Colombier 	ushort	sectsz;
12425210b06SDavid du Colombier 
12525210b06SDavid du Colombier 	/* pointer is required to be unaligned, bytes 26-29.  ick. */
12625210b06SDavid du Colombier //	void	*dpte;			/* ~0ull: invalid */
12725210b06SDavid du Colombier 	ushort	dpteoff;		/* device parameter table extension */
12825210b06SDavid du Colombier 	ushort	dpteseg;
12925210b06SDavid du Colombier 
13025210b06SDavid du Colombier 	/* remainder from edd 3.0 spec */
13125210b06SDavid du Colombier 	ushort	key;			/* 0xbedd if device path info present */
13225210b06SDavid du Colombier 	uchar	dpilen;			/* must be 44 (says edd 4.0) */
13325210b06SDavid du Colombier 	uchar	_unused1;
13425210b06SDavid du Colombier 	ushort	_unused2;
13525210b06SDavid du Colombier 	char	bustype[4];		/* "PCI" or "ISA" */
13625210b06SDavid du Colombier 	char	ifctype[8]; /* "ATA", "ATAPI", "SCSI", "USB", "1394", "FIBRE" */
13725210b06SDavid du Colombier 	uvlong	ifcpath;
13825210b06SDavid du Colombier 	uvlong	devpath[2];
13925210b06SDavid du Colombier 	uchar	_unused3;
14025210b06SDavid du Colombier 	uchar	dpicksum;
14125210b06SDavid du Colombier };
14225210b06SDavid du Colombier 
14325210b06SDavid du Colombier int biosinited;
14425210b06SDavid du Colombier int biosndevs;
14525210b06SDavid du Colombier 
14625210b06SDavid du Colombier void *biosgetfspart(int i, char *name, int chatty);
14725210b06SDavid du Colombier 
14825210b06SDavid du Colombier static Biosdev bdev[Maxdevs];
14925210b06SDavid du Colombier static Ureg regs;
15025210b06SDavid du Colombier static RWlock devs;
15125210b06SDavid du Colombier 
15225210b06SDavid du Colombier static int	dreset(Devid drive);
15325210b06SDavid du Colombier static Devbytes	extgetsize(Biosdev *);
15425210b06SDavid du Colombier static int	drivecap(Devid drive);
15525210b06SDavid du Colombier 
15625210b06SDavid du Colombier /* convert ah error code to a string (just common cases) */
15725210b06SDavid du Colombier static char *
strerr(uchar err)15825210b06SDavid du Colombier strerr(uchar err)
15925210b06SDavid du Colombier {
16025210b06SDavid du Colombier 	switch (err) {
16125210b06SDavid du Colombier 	case 0:
16225210b06SDavid du Colombier 		return "no error";
16325210b06SDavid du Colombier 	case 1:
16425210b06SDavid du Colombier 		return "bad command";
16525210b06SDavid du Colombier 	case 0x80:
16625210b06SDavid du Colombier 		return "disk timeout";
16725210b06SDavid du Colombier 	default:
16825210b06SDavid du Colombier 		return "unknown";
16925210b06SDavid du Colombier 	}
17025210b06SDavid du Colombier }
17125210b06SDavid du Colombier 
17225210b06SDavid du Colombier static void
assertlow64k(uintptr p,char * tag)17325210b06SDavid du Colombier assertlow64k(uintptr p, char *tag)
17425210b06SDavid du Colombier {
17525210b06SDavid du Colombier 	if (p & Highshort)
17625210b06SDavid du Colombier 		panic("devbios: %s address %#p not in bottom 64k", tag, p);
17725210b06SDavid du Colombier }
17825210b06SDavid du Colombier 
17925210b06SDavid du Colombier static void
initrealregs(Ureg * ureg)18025210b06SDavid du Colombier initrealregs(Ureg *ureg)
18125210b06SDavid du Colombier {
18225210b06SDavid du Colombier 	memset(ureg, 0, sizeof *ureg);
18325210b06SDavid du Colombier }
18425210b06SDavid du Colombier 
18525210b06SDavid du Colombier /*
18625210b06SDavid du Colombier  * caller must zero or otherwise initialise *ureg,
18725210b06SDavid du Colombier  * other than ax, bx, dx, si & ds.
18825210b06SDavid du Colombier  */
18925210b06SDavid du Colombier static int
biosdiskcall(Ureg * ureg,uchar op,ulong bx,ulong dx,ulong si)19025210b06SDavid du Colombier biosdiskcall(Ureg *ureg, uchar op, ulong bx, ulong dx, ulong si)
19125210b06SDavid du Colombier {
19225210b06SDavid du Colombier 	int s;
19325210b06SDavid du Colombier 	uchar err;
19425210b06SDavid du Colombier 
19525210b06SDavid du Colombier 	s = splhi();		/* don't let the bios call be interrupted */
19625210b06SDavid du Colombier 	initrealregs(ureg);
19725210b06SDavid du Colombier 	ureg->ax = op << 8;
19825210b06SDavid du Colombier 	ureg->bx = bx;
19925210b06SDavid du Colombier 	ureg->dx = dx;		/* often drive id */
20025210b06SDavid du Colombier 	assertlow64k(si, "dap");
20125210b06SDavid du Colombier 	if(si && (si & Highshort) != ((si + Maxsectsz - 1) & Highshort))
20225210b06SDavid du Colombier 		print("biosdiskcall: dap address %#lux too near segment boundary\n",
20325210b06SDavid du Colombier 			si);
20425210b06SDavid du Colombier 
20525210b06SDavid du Colombier 	ureg->si = si;		/* ds:si forms data address packet addr */
20625210b06SDavid du Colombier 	ureg->ds = 0;		/* bottom 64K */
20725210b06SDavid du Colombier 	ureg->es = 0;		/* es:bx is conventional buffer */
20825210b06SDavid du Colombier 	ureg->di = 0;		/* buffer segment? */
20925210b06SDavid du Colombier 	ureg->flags = 0;
21025210b06SDavid du Colombier 
21125210b06SDavid du Colombier 	/*
21225210b06SDavid du Colombier 	 * *ureg is copied into low memory (realmoderegs) and thence into
21325210b06SDavid du Colombier 	 * the machine registers before the BIOS call, and the registers are
21425210b06SDavid du Colombier 	 * copied into realmoderegs and thence into *ureg after.
21525210b06SDavid du Colombier 	 *
21625210b06SDavid du Colombier 	 * realmode loads these registers: di, si, ax, bx, cx, dx, ds, es.
21725210b06SDavid du Colombier 	 */
21825210b06SDavid du Colombier 	ureg->trap = Diskint;
21925210b06SDavid du Colombier 	realmode(ureg);
22025210b06SDavid du Colombier 
22125210b06SDavid du Colombier 	if (ureg->flags & CF) {
22225210b06SDavid du Colombier 		if (dx == Baseid) {
22325210b06SDavid du Colombier 			err = ureg->ax >> 8;
22425210b06SDavid du Colombier 			print("\nbiosdiskcall: int %#x op %#ux drive %#lux "
22525210b06SDavid du Colombier 				"failed, ah error code %#ux (%s)\n",
22625210b06SDavid du Colombier 				Diskint, op, dx, err, strerr(err));
22725210b06SDavid du Colombier 		}
22825210b06SDavid du Colombier 		splx(s);
22925210b06SDavid du Colombier 		return -1;
23025210b06SDavid du Colombier 	}
23125210b06SDavid du Colombier 	splx(s);
23225210b06SDavid du Colombier 	return 0;
23325210b06SDavid du Colombier }
23425210b06SDavid du Colombier 
23525210b06SDavid du Colombier /*
23625210b06SDavid du Colombier  * Find out what the bios knows about devices.
23725210b06SDavid du Colombier  * our boot device could be usb; ghod only knows where it will appear.
23825210b06SDavid du Colombier  */
23925210b06SDavid du Colombier int
biosinit0(void)24025210b06SDavid du Colombier biosinit0(void)
24125210b06SDavid du Colombier {
24225210b06SDavid du Colombier 	int cap, mask, lastbit, ndrive;
24325210b06SDavid du Colombier 	Devbytes size;
24425210b06SDavid du Colombier 	Devid devid;
24525210b06SDavid du Colombier 	Biosdev *bdp;
24625210b06SDavid du Colombier 	static int beenhere;
24725210b06SDavid du Colombier 
24825210b06SDavid du Colombier 	delay(Pause);		/* pause to read the screen (DEBUG) */
24925210b06SDavid du Colombier 	if (biosinited || beenhere)
25025210b06SDavid du Colombier 		return 0;
25125210b06SDavid du Colombier 	beenhere = 1;
25225210b06SDavid du Colombier 
25325210b06SDavid du Colombier 	ndrive = *(uchar *)KADDR(0x475);		/* from bda */
25425210b06SDavid du Colombier 	if (Debug)
25525210b06SDavid du Colombier 		print("%d bios drive(s)\n", ndrive);
25625210b06SDavid du Colombier 	mask = lastbit = 0;
25725210b06SDavid du Colombier 	for (devid = Baseid, biosndevs = 0; devid != 0 && biosndevs < Maxdevs &&
25825210b06SDavid du Colombier 	    biosndevs < ndrive; devid++) {
25925210b06SDavid du Colombier 		cap = drivecap(devid);
26025210b06SDavid du Colombier 		/* don't reset; it seems to hang the bios */
26125210b06SDavid du Colombier 		if(cap < 0 || (cap & (Fixeddisk|Edd)) != (Fixeddisk|Edd)
26225210b06SDavid du Colombier 		    /* || devid != Baseid && dreset(devid) < 0 || */)
26325210b06SDavid du Colombier 			continue;		/* no suitable device */
26425210b06SDavid du Colombier 
26525210b06SDavid du Colombier 		/* found a live one */
26625210b06SDavid du Colombier 		lastbit = 1 << biosndevs;
26725210b06SDavid du Colombier 		mask |= lastbit;
26825210b06SDavid du Colombier 
26925210b06SDavid du Colombier 		bdp = &bdev[biosndevs];
27025210b06SDavid du Colombier 		bdp->id = devid;
27125210b06SDavid du Colombier 		size = extgetsize(bdp);
27225210b06SDavid du Colombier 		if (size == 0)
27325210b06SDavid du Colombier 			continue;		/* no device */
27425210b06SDavid du Colombier 		bdp->size = size;
27525210b06SDavid du Colombier 
27625210b06SDavid du Colombier 		print("bios%d: drive %#ux: %,llud bytes, %d-byte sectors\n",
27725210b06SDavid du Colombier 			biosndevs, devid, size, bdp->sectsz);
27825210b06SDavid du Colombier 		biosndevs++;
27925210b06SDavid du Colombier 	}
28025210b06SDavid du Colombier 	USED(lastbit);
28125210b06SDavid du Colombier 
28225210b06SDavid du Colombier 	if (Debug && ndrive != biosndevs)
28325210b06SDavid du Colombier 		print("devbios: expected %d drives, found %d\n", ndrive, biosndevs);
28425210b06SDavid du Colombier 
28525210b06SDavid du Colombier 	/*
28625210b06SDavid du Colombier 	 * some bioses seem to only be able to read from drive number 0x80 and
28725210b06SDavid du Colombier 	 * can't read from the highest drive number, even if there is only one.
28825210b06SDavid du Colombier 	 */
28925210b06SDavid du Colombier 	if (biosndevs > 0)
29025210b06SDavid du Colombier 		biosinited = 1;
29125210b06SDavid du Colombier 	else
29225210b06SDavid du Colombier 		panic("devbios: no bios drives seen"); /* 9loadusb needs ≥ 1 */
29325210b06SDavid du Colombier 	delay(Pause);		/* pause to read the screen (DEBUG) */
29425210b06SDavid du Colombier 	return mask;
29525210b06SDavid du Colombier }
29625210b06SDavid du Colombier 
29725210b06SDavid du Colombier static void
biosreset(void)29825210b06SDavid du Colombier biosreset(void)
29925210b06SDavid du Colombier {
30025210b06SDavid du Colombier 	biosinit0();
30125210b06SDavid du Colombier }
30225210b06SDavid du Colombier 
30325210b06SDavid du Colombier static void
biosinit(void)30425210b06SDavid du Colombier biosinit(void)
30525210b06SDavid du Colombier {
30625210b06SDavid du Colombier }
30725210b06SDavid du Colombier 
30825210b06SDavid du Colombier static Chan*
biosattach(char * spec)30925210b06SDavid du Colombier biosattach(char *spec)
31025210b06SDavid du Colombier {
31125210b06SDavid du Colombier 	ulong drive;
31225210b06SDavid du Colombier 	char *p;
31325210b06SDavid du Colombier 	Chan *chan;
31425210b06SDavid du Colombier 
31525210b06SDavid du Colombier 	drive = 0;
31625210b06SDavid du Colombier 	if(spec && *spec){
31725210b06SDavid du Colombier 		drive = strtoul(spec, &p, 0);
31825210b06SDavid du Colombier 		if((drive == 0 && p == spec) || *p || (drive >= Maxdevs))
31925210b06SDavid du Colombier 			error(Ebadarg);
32025210b06SDavid du Colombier 	}
32125210b06SDavid du Colombier 	if(bdev[drive].rootchan)
32225210b06SDavid du Colombier 		return bdev[drive].rootchan;
32325210b06SDavid du Colombier 
32425210b06SDavid du Colombier 	chan = devattach(L'☹', spec);
32525210b06SDavid du Colombier 	if(waserror()){
32625210b06SDavid du Colombier 		chanfree(chan);
32725210b06SDavid du Colombier 		nexterror();
32825210b06SDavid du Colombier 	}
32925210b06SDavid du Colombier 	chan->dev = drive;
33025210b06SDavid du Colombier 	bdev[drive].rootchan = chan;
33125210b06SDavid du Colombier 	/* arbitrary initialisation can go here */
33225210b06SDavid du Colombier 	poperror();
33325210b06SDavid du Colombier 	return chan;
33425210b06SDavid du Colombier }
33525210b06SDavid du Colombier 
33625210b06SDavid du Colombier static int
unitgen(Chan * c,ulong type,Dir * dp)33725210b06SDavid du Colombier unitgen(Chan *c, ulong type, Dir *dp)
33825210b06SDavid du Colombier {
33925210b06SDavid du Colombier 	int perm, t;
34025210b06SDavid du Colombier 	ulong vers;
34125210b06SDavid du Colombier 	vlong size;
34225210b06SDavid du Colombier 	char *p;
34325210b06SDavid du Colombier 	Qid q;
34425210b06SDavid du Colombier 
34525210b06SDavid du Colombier 	perm = 0644;
34625210b06SDavid du Colombier 	size = 0;
34725210b06SDavid du Colombier //	d = unit2dev(UNIT(c->qid));
34825210b06SDavid du Colombier //	vers = d->vers;
34925210b06SDavid du Colombier 	vers = 0;
35025210b06SDavid du Colombier 	t = QTFILE;
35125210b06SDavid du Colombier 
35225210b06SDavid du Colombier 	switch(type){
35325210b06SDavid du Colombier 	default:
35425210b06SDavid du Colombier 		return -1;
35525210b06SDavid du Colombier 	case Qctl:
35625210b06SDavid du Colombier 		p = "ctl";
35725210b06SDavid du Colombier 		break;
35825210b06SDavid du Colombier 	case Qdata:
35925210b06SDavid du Colombier 		p = "data";
36025210b06SDavid du Colombier 		perm = 0640;
36125210b06SDavid du Colombier 		break;
36225210b06SDavid du Colombier 	}
36325210b06SDavid du Colombier 	mkqid(&q, QID(UNIT(c->qid), type), vers, t);
36425210b06SDavid du Colombier 	devdir(c, q, p, size, eve, perm, dp);
36525210b06SDavid du Colombier 	return 1;
36625210b06SDavid du Colombier }
36725210b06SDavid du Colombier 
36825210b06SDavid du Colombier static int
topgen(Chan * c,ulong type,Dir * d)36925210b06SDavid du Colombier topgen(Chan *c, ulong type, Dir *d)
37025210b06SDavid du Colombier {
37125210b06SDavid du Colombier 	int perm;
37225210b06SDavid du Colombier 	vlong size;
37325210b06SDavid du Colombier 	char *p;
37425210b06SDavid du Colombier 	Qid q;
37525210b06SDavid du Colombier 
37625210b06SDavid du Colombier 	size = 0;
37725210b06SDavid du Colombier 	switch(type){
37825210b06SDavid du Colombier 	default:
37925210b06SDavid du Colombier 		return -1;
38025210b06SDavid du Colombier 	case Qdata:
38125210b06SDavid du Colombier 		p = "data";
38225210b06SDavid du Colombier 		perm = 0644;
38325210b06SDavid du Colombier 		break;
38425210b06SDavid du Colombier 	}
38525210b06SDavid du Colombier 	mkqid(&q, type, 0, QTFILE);
38625210b06SDavid du Colombier 	devdir(c, q, p, size, eve, perm, d);
38725210b06SDavid du Colombier 	return 1;
38825210b06SDavid du Colombier }
38925210b06SDavid du Colombier 
39025210b06SDavid du Colombier static int
biosgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)39125210b06SDavid du Colombier biosgen(Chan *c, char *, Dirtab *, int, int s, Dir *dp)
39225210b06SDavid du Colombier {
39325210b06SDavid du Colombier 	Qid q;
39425210b06SDavid du Colombier 
39525210b06SDavid du Colombier 	if(c->qid.path == 0){
39625210b06SDavid du Colombier 		switch(s){
39725210b06SDavid du Colombier 		case DEVDOTDOT:
39825210b06SDavid du Colombier 			q.path = 0;
39925210b06SDavid du Colombier 			q.type = QTDIR;
40025210b06SDavid du Colombier 			devdir(c, q, "#☹", 0, eve, 0555, dp);
40125210b06SDavid du Colombier 			break;
40225210b06SDavid du Colombier 		case 0:
40325210b06SDavid du Colombier 			q.path = Qtopdir;
40425210b06SDavid du Colombier 			q.type = QTDIR;
40525210b06SDavid du Colombier 			devdir(c, q, "bios", 0, eve, 0555, dp);
40625210b06SDavid du Colombier 			break;
40725210b06SDavid du Colombier 		default:
40825210b06SDavid du Colombier 			return -1;
40925210b06SDavid du Colombier 		}
41025210b06SDavid du Colombier 		return 1;
41125210b06SDavid du Colombier 	}
41225210b06SDavid du Colombier 
41325210b06SDavid du Colombier 	switch(TYPE(c->qid)){
41425210b06SDavid du Colombier 	default:
41525210b06SDavid du Colombier 		return -1;
41625210b06SDavid du Colombier 	case Qtopdir:
41725210b06SDavid du Colombier 		if(s == DEVDOTDOT){
41825210b06SDavid du Colombier 			mkqid(&q, Qzero, 0, QTDIR);
41925210b06SDavid du Colombier 			devdir(c, q, "bios", 0, eve, 0555, dp);
42025210b06SDavid du Colombier 			return 1;
42125210b06SDavid du Colombier 		}
42225210b06SDavid du Colombier 		if(s < Qtopfiles)
42325210b06SDavid du Colombier 			return topgen(c, Qtopbase + s, dp);
42425210b06SDavid du Colombier 		s -= Qtopfiles;
42525210b06SDavid du Colombier 		if(s >= 1)
42625210b06SDavid du Colombier 			return -1;
42725210b06SDavid du Colombier 		mkqid(&q, QID(s, Qunitdir), 0, QTDIR);
42825210b06SDavid du Colombier 		devdir(c, q, "bios", 0, eve, 0555, dp);
42925210b06SDavid du Colombier 		return 1;
43025210b06SDavid du Colombier 	case Qdata:
43125210b06SDavid du Colombier 		return unitgen(c, TYPE(c->qid), dp);
43225210b06SDavid du Colombier 	}
43325210b06SDavid du Colombier }
43425210b06SDavid du Colombier 
43525210b06SDavid du Colombier static Walkqid*
bioswalk(Chan * c,Chan * nc,char ** name,int nname)43625210b06SDavid du Colombier bioswalk(Chan *c, Chan *nc, char **name, int nname)
43725210b06SDavid du Colombier {
43825210b06SDavid du Colombier 	return devwalk(c, nc, name, nname, nil, 0, biosgen);
43925210b06SDavid du Colombier }
44025210b06SDavid du Colombier 
44125210b06SDavid du Colombier static int
biosstat(Chan * c,uchar * db,int n)44225210b06SDavid du Colombier biosstat(Chan *c, uchar *db, int n)
44325210b06SDavid du Colombier {
44425210b06SDavid du Colombier 	return devstat(c, db, n, nil, 0, biosgen);
44525210b06SDavid du Colombier }
44625210b06SDavid du Colombier 
44725210b06SDavid du Colombier static Chan*
biosopen(Chan * c,int omode)44825210b06SDavid du Colombier biosopen(Chan *c, int omode)
44925210b06SDavid du Colombier {
45025210b06SDavid du Colombier 	return devopen(c, omode, 0, 0, biosgen);
45125210b06SDavid du Colombier }
45225210b06SDavid du Colombier 
45325210b06SDavid du Colombier static void
biosclose(Chan *)45425210b06SDavid du Colombier biosclose(Chan *)
45525210b06SDavid du Colombier {
45625210b06SDavid du Colombier }
45725210b06SDavid du Colombier 
45825210b06SDavid du Colombier #ifdef UNUSED
45925210b06SDavid du Colombier int
biosboot(int dev,char * file,Boot * b)46025210b06SDavid du Colombier biosboot(int dev, char *file, Boot *b)
46125210b06SDavid du Colombier {
46225210b06SDavid du Colombier 	Bootfs *fs;
46325210b06SDavid du Colombier 
46425210b06SDavid du Colombier 	if(strncmp(file, "dos!", 4) == 0)
46525210b06SDavid du Colombier 		file += 4;
46625210b06SDavid du Colombier 	if(strchr(file, '!') != nil || strcmp(file, "") == 0) {
46725210b06SDavid du Colombier 		print("syntax is bios0!file\n");
46825210b06SDavid du Colombier 		return -1;
46925210b06SDavid du Colombier 	}
47025210b06SDavid du Colombier 
47125210b06SDavid du Colombier 	fs = biosgetfspart(dev, "9fat", 1);
47225210b06SDavid du Colombier 	if(fs == nil)
47325210b06SDavid du Colombier 		return -1;
47425210b06SDavid du Colombier 	return fsboot(fs, file, b);
47525210b06SDavid du Colombier }
47625210b06SDavid du Colombier #endif
47725210b06SDavid du Colombier 
47825210b06SDavid du Colombier /* read n bytes at sector offset into a from drive id */
47925210b06SDavid du Colombier long
sectread(Biosdev * bdp,void * a,long n,Devsects offset)48025210b06SDavid du Colombier sectread(Biosdev *bdp, void *a, long n, Devsects offset)
48125210b06SDavid du Colombier {
48225210b06SDavid du Colombier 	uchar *xch;
48325210b06SDavid du Colombier 	uintptr xchaddr;
48425210b06SDavid du Colombier 	Dap *dap;
48525210b06SDavid du Colombier 
48625210b06SDavid du Colombier 	if(bdp->sectsz <= 0 || n < 0 || n > bdp->sectsz)
48725210b06SDavid du Colombier 		return -1;
48825210b06SDavid du Colombier 	xch = (uchar *)BIOSXCHG;
48925210b06SDavid du Colombier 	assertlow64k(PADDR(xch), "biosxchg");
49025210b06SDavid du Colombier 	if(Debug)
49125210b06SDavid du Colombier 		/* scribble on the buffer to provoke trouble */
49225210b06SDavid du Colombier 		memset(xch, 'r', bdp->sectsz);
49325210b06SDavid du Colombier 
49425210b06SDavid du Colombier 	/* read into BIOSXCHG; alloc space for a worst-case (optical) sector */
49525210b06SDavid du Colombier 	dap = (Dap *)(xch + Maxsectsz);
49625210b06SDavid du Colombier 	assertlow64k(PADDR(dap), "Dap");
49725210b06SDavid du Colombier 	memset(dap, 0, sizeof *dap);
49825210b06SDavid du Colombier 	dap->size = sizeof *dap;
49925210b06SDavid du Colombier 	dap->nsects = 1;
50025210b06SDavid du Colombier 	dap->stsect = offset;
50125210b06SDavid du Colombier 
50225210b06SDavid du Colombier 	xchaddr = PADDR(xch);
50325210b06SDavid du Colombier 	assertlow64k(xchaddr, "sectread buffer");
50425210b06SDavid du Colombier 	dap->addr = xchaddr;		/* ulong version */
50525210b06SDavid du Colombier 	dap->addroff = xchaddr;		/* pedantic seg:off */
50625210b06SDavid du Colombier 	dap->addrseg = 0;
50725210b06SDavid du Colombier 	dap->addr64 = xchaddr;		/* paranoid redundancy */
50825210b06SDavid du Colombier 	dap->lnsects = 1;
50925210b06SDavid du Colombier 
51025210b06SDavid du Colombier 	/*
51125210b06SDavid du Colombier 	 * ensure that entire buffer fits in low memory.
51225210b06SDavid du Colombier 	 */
51325210b06SDavid du Colombier 	if((dap->addr & Highshort) !=
51425210b06SDavid du Colombier 	    ((dap->addr + Minsectsz - 1) & Highshort))
51525210b06SDavid du Colombier 		print("devbios: sectread: address %#lux too near seg boundary\n",
51625210b06SDavid du Colombier 			dap->addr);
51725210b06SDavid du Colombier 	if (Debug)
51825210b06SDavid du Colombier 		print("reading bios drive %#ux sector %lld -> %#lux...",
51925210b06SDavid du Colombier 			bdp->id, offset, dap->addr);
52025210b06SDavid du Colombier 	delay(Pause);			/* pause to read the screen (DEBUG) */
52125210b06SDavid du Colombier 
52225210b06SDavid du Colombier 	/*
52325210b06SDavid du Colombier 	 * int 13 read sector expects buffer seg in di?,
52425210b06SDavid du Colombier 	 * dap in si, 0x42 in ah, drive in dl.
52525210b06SDavid du Colombier 	 */
52625210b06SDavid du Colombier 	if (biosdiskcall(&regs, Biosrdsect, 0, bdp->id, PADDR(dap)) < 0) {
52725210b06SDavid du Colombier 		print("devbios: sectread: bios failed to read %ld @ sector %lld of %#ux\n",
52825210b06SDavid du Colombier 			n, offset, bdp->id);
52925210b06SDavid du Colombier 		return -1;
53025210b06SDavid du Colombier 	}
53125210b06SDavid du Colombier 	if (dap->nsects != 1)
53225210b06SDavid du Colombier 		panic("devbios: sector read ok but read %d sectors",
53325210b06SDavid du Colombier 			dap->nsects);
53425210b06SDavid du Colombier 	if (Debug)
53525210b06SDavid du Colombier 		print("OK\n");
53625210b06SDavid du Colombier 
53725210b06SDavid du Colombier 	/* copy into caller's buffer */
53825210b06SDavid du Colombier 	memmove(a, xch, n);
53925210b06SDavid du Colombier 	if(0 && Debug)
54025210b06SDavid du Colombier 		print("-%ux %ux %ux %ux--%16.16s-\n",
54125210b06SDavid du Colombier 			xch[0], xch[1], xch[2], xch[3], (char *)xch + 480);
54225210b06SDavid du Colombier 	delay(Pause);		/* pause to read the screen (DEBUG) */
54325210b06SDavid du Colombier 	return n;
54425210b06SDavid du Colombier }
54525210b06SDavid du Colombier 
54625210b06SDavid du Colombier /* seems to hang bioses, at least vmware's */
54725210b06SDavid du Colombier static int
dreset(Devid drive)54825210b06SDavid du Colombier dreset(Devid drive)
54925210b06SDavid du Colombier {
55025210b06SDavid du Colombier 	print("devbios: resetting %#ux...", drive);
55125210b06SDavid du Colombier 	/* ignore carry flag for Biosinit */
55225210b06SDavid du Colombier 	biosdiskcall(&regs, Biosinit, 0, drive, 0);
55325210b06SDavid du Colombier 	print("\n");
55425210b06SDavid du Colombier 	return regs.ax? -1: 0;		/* ax != 0 on error */
55525210b06SDavid du Colombier }
55625210b06SDavid du Colombier 
55725210b06SDavid du Colombier /* returns capabilities bitmap */
55825210b06SDavid du Colombier static int
drivecap(Devid drive)55925210b06SDavid du Colombier drivecap(Devid drive)
56025210b06SDavid du Colombier {
56125210b06SDavid du Colombier 	int cap;
56225210b06SDavid du Colombier 
56325210b06SDavid du Colombier 	if (biosdiskcall(&regs, Biosckext, Imok, drive, 0) < 0)
56425210b06SDavid du Colombier 		/*
56525210b06SDavid du Colombier 		 * we have an old bios without extensions, in theory.
56625210b06SDavid du Colombier 		 * in practice, there may just be no drive for this number.
56725210b06SDavid du Colombier 		 */
56825210b06SDavid du Colombier 		return -1;
56925210b06SDavid du Colombier 	if(regs.bx != Youreok){
57025210b06SDavid du Colombier 		print("devbios: buggy bios: drive %#ux extension check "
57125210b06SDavid du Colombier 			 "returned %lux in bx\n", drive, regs.bx);
57225210b06SDavid du Colombier 		return -1;
57325210b06SDavid du Colombier 	}
57425210b06SDavid du Colombier 	cap = regs.cx;
57525210b06SDavid du Colombier 	if (Debug) {
57625210b06SDavid du Colombier 		print("bios drive %#ux extensions version %#x.%d cx %#ux\n",
57725210b06SDavid du Colombier 			drive, (uchar)(regs.ax >> 8), (uchar)regs.ax, cap);
57825210b06SDavid du Colombier 		if ((uchar)(regs.ax >> 8) < 0x30) {
57925210b06SDavid du Colombier 			print("drivecap: extensions prior to 0x30\n");
58025210b06SDavid du Colombier 			return -1;
58125210b06SDavid du Colombier 		}
58225210b06SDavid du Colombier 		print("\tsubsets supported:");
58325210b06SDavid du Colombier 		if (cap & Fixeddisk)
58425210b06SDavid du Colombier 			print(" fixed disk access;");
58525210b06SDavid du Colombier 		if (cap & Drlock)
58625210b06SDavid du Colombier 			print(" drive locking;");
58725210b06SDavid du Colombier 		if (cap & Edd)
58825210b06SDavid du Colombier 			print(" enhanced disk support;");
58925210b06SDavid du Colombier 		if (cap & Bit64ext)
59025210b06SDavid du Colombier 			print(" 64-bit extensions;");
59125210b06SDavid du Colombier 		print("\n");
59225210b06SDavid du Colombier 	}
59325210b06SDavid du Colombier 	delay(Pause);			/* pause to read the screen (DEBUG) */
59425210b06SDavid du Colombier 	return cap;
59525210b06SDavid du Colombier }
59625210b06SDavid du Colombier 
59725210b06SDavid du Colombier /* extended get size; reads bdp->id, fills in bdp->sectsz, returns # sectors */
59825210b06SDavid du Colombier static Devbytes
extgetsize(Biosdev * bdp)59925210b06SDavid du Colombier extgetsize(Biosdev *bdp)
60025210b06SDavid du Colombier {
60125210b06SDavid du Colombier 	ulong sectsz;
60225210b06SDavid du Colombier 	Edrvparam *edp;
60325210b06SDavid du Colombier 
60425210b06SDavid du Colombier 	edp = (Edrvparam *)BIOSXCHG;
60525210b06SDavid du Colombier 	memset(edp, 0, sizeof *edp);
60625210b06SDavid du Colombier 	edp->size = sizeof *edp;
60725210b06SDavid du Colombier 	edp->dpteseg = edp->dpteoff = ~0;	/* no pointer */
60825210b06SDavid du Colombier 	edp->dpilen = 44;
60925210b06SDavid du Colombier 
61025210b06SDavid du Colombier 	if (biosdiskcall(&regs, Biosedrvparam, 0, bdp->id, PADDR(edp)) < 0)
61125210b06SDavid du Colombier 		return 0;		/* old bios without extensions */
61225210b06SDavid du Colombier 	if(Debug) {
61325210b06SDavid du Colombier 		print("bios drive %#ux info flags %#ux", bdp->id, edp->flags);
61425210b06SDavid du Colombier 		if (edp->key == 0xbedd)
61525210b06SDavid du Colombier 			print("; edd 3.0  %.4s %.8s",
61625210b06SDavid du Colombier 				edp->bustype, edp->ifctype);
61725210b06SDavid du Colombier 		else
61825210b06SDavid du Colombier 			print("; NOT edd 3.0 compliant (key %#ux)", edp->key);
61925210b06SDavid du Colombier 		print("\n");
62025210b06SDavid du Colombier 	}
62125210b06SDavid du Colombier 	if (edp->sectsz <= 0) {
62225210b06SDavid du Colombier 		print("devbios: drive %#ux: sector size <= 0\n", bdp->id);
62325210b06SDavid du Colombier 		edp->sectsz = 1;		/* don't divide by 0 */
62425210b06SDavid du Colombier 		return 0;
62525210b06SDavid du Colombier 	}
62625210b06SDavid du Colombier 	sectsz = edp->sectsz;
62725210b06SDavid du Colombier 	if (sectsz > Maxsectsz) {
62825210b06SDavid du Colombier 		print("devbios: sector size %lud > %d\n", sectsz, Maxsectsz);
62925210b06SDavid du Colombier 		return 0;
63025210b06SDavid du Colombier 	}
63125210b06SDavid du Colombier 	bdp->sectsz = sectsz;
63225210b06SDavid du Colombier 	return edp->physsects * sectsz;
63325210b06SDavid du Colombier }
63425210b06SDavid du Colombier 
63525210b06SDavid du Colombier vlong
biossize(uint dev)63625210b06SDavid du Colombier biossize(uint dev)
63725210b06SDavid du Colombier {
63825210b06SDavid du Colombier 	Biosdev *bdp;
63925210b06SDavid du Colombier 
64025210b06SDavid du Colombier 	if (dev >= biosndevs)
64125210b06SDavid du Colombier 		return -1;
64225210b06SDavid du Colombier 	bdp = &bdev[dev];
64325210b06SDavid du Colombier 	if (bdp->sectsz <= 0)
64425210b06SDavid du Colombier 		return -1;
64525210b06SDavid du Colombier 	return bdp->size / bdp->sectsz;
64625210b06SDavid du Colombier }
64725210b06SDavid du Colombier 
64825210b06SDavid du Colombier long
biossectsz(uint dev)64925210b06SDavid du Colombier biossectsz(uint dev)
65025210b06SDavid du Colombier {
65125210b06SDavid du Colombier 	Biosdev *bdp;
65225210b06SDavid du Colombier 
65325210b06SDavid du Colombier 	if (dev >= biosndevs)
65425210b06SDavid du Colombier 		return -1;
65525210b06SDavid du Colombier 	bdp = &bdev[dev];
65625210b06SDavid du Colombier 	if (bdp->sectsz <= 0)
65725210b06SDavid du Colombier 		return -1;
65825210b06SDavid du Colombier 	return bdp->sectsz;
65925210b06SDavid du Colombier }
66025210b06SDavid du Colombier 
66125210b06SDavid du Colombier long
biosread0(Bootfs * fs,void * a,long n)66225210b06SDavid du Colombier biosread0(Bootfs *fs, void *a, long n)
66325210b06SDavid du Colombier {
66425210b06SDavid du Colombier 	int want, got, part, dev;
66525210b06SDavid du Colombier 	long totnr, stuck;
66625210b06SDavid du Colombier 	Devbytes offset;
66725210b06SDavid du Colombier 	Biosdev *bdp;
66825210b06SDavid du Colombier 
66925210b06SDavid du Colombier 	dev = fs->dev;				/* only use of fs */
67025210b06SDavid du Colombier 	if(dev > biosndevs)
67125210b06SDavid du Colombier 		return -1;
67225210b06SDavid du Colombier 	if (n <= 0)
67325210b06SDavid du Colombier 		return n;
67425210b06SDavid du Colombier 	bdp = &bdev[dev];
67525210b06SDavid du Colombier 	offset = bdp->offset;
67625210b06SDavid du Colombier 	stuck = 0;
67725210b06SDavid du Colombier 	for (totnr = 0; totnr < n && stuck < 4; totnr += got) {
67825210b06SDavid du Colombier 		if (bdp->sectsz == 0) {
67925210b06SDavid du Colombier 			print("devbios: zero sector size\n");
68025210b06SDavid du Colombier 			return -1;
68125210b06SDavid du Colombier 		}
68225210b06SDavid du Colombier 		want = bdp->sectsz;
68325210b06SDavid du Colombier 		if (totnr + want > n)
68425210b06SDavid du Colombier 			want = n - totnr;
68525210b06SDavid du Colombier 		if(0 && Debug && debugload)
68625210b06SDavid du Colombier 			print("bios%d, read: %ld @ off %lld, want: %d, id: %#ux\n",
68725210b06SDavid du Colombier 				dev, n, offset, want, bdp->id);
68825210b06SDavid du Colombier 		part = offset % bdp->sectsz;
68925210b06SDavid du Colombier 		if (part != 0) {	/* back up to start of sector */
69025210b06SDavid du Colombier 			offset -= part;
69125210b06SDavid du Colombier 			totnr  -= part;
69225210b06SDavid du Colombier 			if (totnr < 0) {
69325210b06SDavid du Colombier 				print("biosread0: negative count %ld\n", totnr);
69425210b06SDavid du Colombier 				return -1;
69525210b06SDavid du Colombier 			}
69625210b06SDavid du Colombier 		}
69725210b06SDavid du Colombier 		if ((vlong)offset < 0) {
69825210b06SDavid du Colombier 			print("biosread0: negative offset %lld\n", offset);
69925210b06SDavid du Colombier 			return -1;
70025210b06SDavid du Colombier 		}
70125210b06SDavid du Colombier 		got = sectread(bdp, (char *)a + totnr, want,
70225210b06SDavid du Colombier 			offset / bdp->sectsz);
70325210b06SDavid du Colombier 		if(got <= 0)
70425210b06SDavid du Colombier 			return -1;
70525210b06SDavid du Colombier 		offset += got;
70625210b06SDavid du Colombier 		bdp->offset = offset;
70725210b06SDavid du Colombier 		if (got < bdp->sectsz)
70825210b06SDavid du Colombier 			stuck++;	/* we'll have to re-read this sector */
70925210b06SDavid du Colombier 		else
71025210b06SDavid du Colombier 			stuck = 0;
71125210b06SDavid du Colombier 	}
71225210b06SDavid du Colombier 	return totnr;
71325210b06SDavid du Colombier }
71425210b06SDavid du Colombier 
71525210b06SDavid du Colombier vlong
biosseek(Bootfs * fs,vlong off)71625210b06SDavid du Colombier biosseek(Bootfs *fs, vlong off)
71725210b06SDavid du Colombier {
71825210b06SDavid du Colombier 	if (off < 0) {
71925210b06SDavid du Colombier 		print("biosseek(fs, %lld) is illegal\n", off);
72025210b06SDavid du Colombier 		return -1;
72125210b06SDavid du Colombier 	}
72225210b06SDavid du Colombier 	if(fs->dev > biosndevs) {
72325210b06SDavid du Colombier 		print("biosseek: fs->dev %d > biosndevs %d\n", fs->dev, biosndevs);
72425210b06SDavid du Colombier 		return -1;
72525210b06SDavid du Colombier 	}
72625210b06SDavid du Colombier 	bdev[fs->dev].offset = off;	/* do not know size... (yet) */
72725210b06SDavid du Colombier 	return off;
72825210b06SDavid du Colombier }
72925210b06SDavid du Colombier 
73025210b06SDavid du Colombier static long
biosread(Chan * c,void * db,long n,vlong off)73125210b06SDavid du Colombier biosread(Chan *c, void *db, long n, vlong off)
73225210b06SDavid du Colombier {
73325210b06SDavid du Colombier 	Biosdev *bp;
73425210b06SDavid du Colombier 
73525210b06SDavid du Colombier 	switch(TYPE(c->qid)){
73625210b06SDavid du Colombier 	default:
73725210b06SDavid du Colombier 		error(Eperm);
73825210b06SDavid du Colombier 	case Qzero:
73925210b06SDavid du Colombier 	case Qtopdir:
74025210b06SDavid du Colombier 		return devdirread(c, db, n, 0, 0, biosgen);
74125210b06SDavid du Colombier 	case Qdata:
74225210b06SDavid du Colombier 		bp = &bdev[UNIT(c->qid)];
74325210b06SDavid du Colombier 		if (bp->rootchan == nil)
74425210b06SDavid du Colombier 			panic("biosread: nil root chan for bios%ld",
74525210b06SDavid du Colombier 				UNIT(c->qid));
74625210b06SDavid du Colombier 		biosseek(&bp->Bootfs, off);
74725210b06SDavid du Colombier 		return biosread0(&bp->Bootfs, db, n);
74825210b06SDavid du Colombier 	}
74925210b06SDavid du Colombier }
75025210b06SDavid du Colombier 
751*07b4782cSDavid du Colombier /* name is typically "9fat" */
75225210b06SDavid du Colombier void *
biosgetfspart(int i,char * name,int chatty)75325210b06SDavid du Colombier biosgetfspart(int i, char *name, int chatty)
75425210b06SDavid du Colombier {
755*07b4782cSDavid du Colombier 	char part[32];
75625210b06SDavid du Colombier 	static Bootfs fs;
75725210b06SDavid du Colombier 
75825210b06SDavid du Colombier 	fs.dev = i;
75925210b06SDavid du Colombier 	fs.diskread = biosread0;
76025210b06SDavid du Colombier 	fs.diskseek = biosseek;
761*07b4782cSDavid du Colombier 	snprint(part, sizeof part, "#S/sdB0/%s", name);
762*07b4782cSDavid du Colombier 	if(dosinit(&fs, part) < 0){
76325210b06SDavid du Colombier 		if(chatty)
76425210b06SDavid du Colombier 			print("bios%d!%s does not contain a FAT file system\n",
76525210b06SDavid du Colombier 				i, name);
76625210b06SDavid du Colombier 		return nil;
76725210b06SDavid du Colombier 	}
76825210b06SDavid du Colombier 	return &fs;
76925210b06SDavid du Colombier }
77025210b06SDavid du Colombier 
77125210b06SDavid du Colombier static long
bioswrite(Chan *,void *,long,vlong)77225210b06SDavid du Colombier bioswrite(Chan *, void *, long, vlong)
77325210b06SDavid du Colombier {
77425210b06SDavid du Colombier 	error("bios devices are read-only in bootstrap");
77525210b06SDavid du Colombier 	return 0;
77625210b06SDavid du Colombier }
77725210b06SDavid du Colombier 
77825210b06SDavid du Colombier Dev biosdevtab = {
77925210b06SDavid du Colombier 	L'☹',
78025210b06SDavid du Colombier 	"bios",
78125210b06SDavid du Colombier 
78225210b06SDavid du Colombier 	biosreset,
78325210b06SDavid du Colombier 	biosinit,
78425210b06SDavid du Colombier 	devshutdown,
78525210b06SDavid du Colombier 	biosattach,
78625210b06SDavid du Colombier 	bioswalk,
78725210b06SDavid du Colombier 	biosstat,
78825210b06SDavid du Colombier 	biosopen,
78925210b06SDavid du Colombier 	devcreate,
79025210b06SDavid du Colombier 	biosclose,
79125210b06SDavid du Colombier 	biosread,
79225210b06SDavid du Colombier 	devbread,
79325210b06SDavid du Colombier 	bioswrite,
79425210b06SDavid du Colombier 	devbwrite,
79525210b06SDavid du Colombier 	devremove,
79625210b06SDavid du Colombier 	devwstat,
79725210b06SDavid du Colombier 	devpower,
79825210b06SDavid du Colombier 	devconfig,
79925210b06SDavid du Colombier };
800