808 lines · 17.0 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * read-only driver for BIOS LBA devices. * devbios must be initialised first and no disks may be accessed * via non-BIOS means (i.e., talking to the disk controller directly). * EDD 4.0 defines the INT 0x13 functions. * * heavily dependent upon correct BIOS implementation. * some bioses (e.g., vmware) seem to hang for two minutes then report * a disk timeout on reset and extended read operations. */#include	"u.h"#include	"../port/lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"io.h"#include	"ureg.h"#include	"pool.h"#include	"../port/error.h"#include	"../port/netif.h"#include	"../port/sd.h"#include	"dosfs.h" #define TYPE(q)		((ulong)(q).path & 0xf)#define UNIT(q)		(((ulong)(q).path>>4) & 0xff)#define L(q)		(((ulong)(q).path>>12) & 0xf)#define QID(u, t) 	((u)<<4 | (t)) typedef struct Biosdev Biosdev;typedef struct Dap Dap;typedef uvlong Devbytes, Devsects;typedef uchar Devid;typedef struct Edrvparam Edrvparam; enum {	Debug = 0,	Pause = 0,			/* delay to read debugging */ 	Minsectsz	= 512,		/* for disks */	Maxsectsz	= 2048,		/* for optical (CDs, etc.) */ 	Highshort	= ((1ul<<16) - 1) << 16,  /* upper short of a long */ 	Maxdevs		= 8,	CF		= 1,		/* carry flag: indicates an error */	Flopid		= 0,		/* first floppy */	Baseid		= 0x80,		/* first disk */ 	Diskint		= 0x13,		/* "INT 13" for bios disk i/o */ 	/* cx capability bits in Biosckext results */	Fixeddisk	= 1<<0,		/* fixed disk access subset */	Drlock		= 1<<1,	Edd		= 1<<2,		/* enhanced disk drive support */	Bit64ext	= 1<<3, 	/* bios calls: int 0x13 disk services w buffer at es:bx */	Biosinit	= 0,		/* initialise disk & floppy ctlrs */	Biosdrvsts,			/* status of last int 0x13 call */	Biosdrvparam	= 8,	Biosctlrinit,	Biosreset	=  0xd,		/* reset disk */	Biosdrvrdy	= 0x10,	/* extended int 0x13 calls w dap at ds:si */	Biosckext	= 0x41,	Biosrdsect,	Biosedrvparam	= 0x48, 	/* magic numbers for bios calls */	Imok		= 0x55aa,	Youreok		= 0xaa55,};enum {	Qzero,				/* assumed to be 0 by devattach */	Qtopdir		= 1,	Qtopbase,	Qtopctl		= Qtopbase,	Qtopend, 	Qunitdir,	Qunitbase,	Qctl		= Qunitbase,	Qdata, 	Qtopfiles	= Qtopend-Qtopbase,}; struct Biosdev {	Devbytes size;	Devbytes offset;	Devid	id;			/* drive number; e.g., 0x80 */	ushort	sectsz;	Chan	*rootchan;	Bootfs;}; struct Dap {				/* a device address packet */	uchar	size;	uchar	_unused1;	uchar	nsects;	uchar	_unused2;	union {		ulong	addr;		/* actual address (nominally seg:off) */		struct {			ushort	addroff;	/* :offset */			ushort	addrseg;	/* segment: */		};	};	uvlong	stsect;			/* starting sector */ 	uvlong	addr64;			/* instead of addr, if addr is ~0 */	ulong	lnsects;		/* nsects to match addr64 */	ulong	_unused3;}; struct Edrvparam {	ushort	size;			/* max. buffer (struct) size */	ushort	flags;	ulong	physcyls;	ulong	physheads;	ulong	phystracksects;	uvlong	physsects;	ushort	sectsz; 	/* pointer is required to be unaligned, bytes 26-29.  ick. *///	void	*dpte;			/* ~0ull: invalid */	ushort	dpteoff;		/* device parameter table extension */	ushort	dpteseg; 	/* remainder from edd 3.0 spec */	ushort	key;			/* 0xbedd if device path info present */	uchar	dpilen;			/* must be 44 (says edd 4.0) */	uchar	_unused1;	ushort	_unused2;	char	bustype[4];		/* "PCI" or "ISA" */	char	ifctype[8]; /* "ATA", "ATAPI", "SCSI", "USB", "1394", "FIBRE" */	uvlong	ifcpath;	uvlong	devpath[2];	uchar	_unused3;	uchar	dpicksum;}; int biosinited;int biosndevs; void *biosgetfspart(int i, char *name, int chatty); static Biosdev bdev[Maxdevs];static Ureg regs;static RWlock devs; static int	dreset(Devid drive);static Devbytes	extgetsize(Biosdev *);static int	drivecap(Devid drive); /* convert ah error code to a string (just common cases) */static char *strerr(uchar err){	switch (err) {	case 0:		return "no error";	case 1:		return "bad command";	case 0x80:		return "disk timeout";	default:		return "unknown";	}} static voidassertlow64k(uintptr p, char *tag){	if (p & Highshort)		panic("devbios: %s address %#p not in bottom 64k", tag, p);} static voidinitrealregs(Ureg *ureg){	memset(ureg, 0, sizeof *ureg);} /* * caller must zero or otherwise initialise *ureg, * other than ax, bx, dx, si & ds. */static intbiosdiskcall(Ureg *ureg, uchar op, ulong bx, ulong dx, ulong si){	int s;	uchar err; 	s = splhi();		/* don't let the bios call be interrupted */	initrealregs(ureg);	ureg->ax = op << 8;	ureg->bx = bx;	ureg->dx = dx;		/* often drive id */	assertlow64k(si, "dap");	if(si && (si & Highshort) != ((si + Maxsectsz - 1) & Highshort))		print("biosdiskcall: dap address %#lux too near segment boundary\n",			si); 	ureg->si = si;		/* ds:si forms data address packet addr */	ureg->ds = 0;		/* bottom 64K */	ureg->es = 0;		/* es:bx is conventional buffer */	ureg->di = 0;		/* buffer segment? */	ureg->flags = 0; 	/*	 * *ureg is copied into low memory (realmoderegs) and thence into	 * the machine registers before the BIOS call, and the registers are	 * copied into realmoderegs and thence into *ureg after.	 *	 * realmode loads these registers: di, si, ax, bx, cx, dx, ds, es.	 */	ureg->trap = Diskint;	realmode(ureg); 	if (ureg->flags & CF) {		if (dx == Baseid) {			err = ureg->ax >> 8;			print("\nbiosdiskcall: int %#x op %#ux drive %#lux "				"failed, ah error code %#ux (%s)\n",				Diskint, op, dx, err, strerr(err));		}		splx(s);		return -1;	}	splx(s);	return 0;} /* * Find out what the bios knows about devices. * our boot device could be usb; ghod only knows where it will appear. */intbiosinit0(void){	int cap, mask, lastbit, ndrive;	Devbytes size;	Devid devid;	Biosdev *bdp;	static int beenhere; 	delay(Pause);		/* pause to read the screen (DEBUG) */	if (biosinited || beenhere)		return 0;	beenhere = 1; 	ndrive = *(uchar *)KADDR(0x475);		/* from bda */	if (Debug)		print("%d bios drive(s)\n", ndrive);	mask = lastbit = 0;	for (devid = Baseid, biosndevs = 0; devid != 0 && biosndevs < Maxdevs &&	    biosndevs < ndrive; devid++) {		cap = drivecap(devid);		/* don't reset; it seems to hang the bios */		if(cap < 0 || (cap & (Fixeddisk|Edd)) != (Fixeddisk|Edd)		    /* || devid != Baseid && dreset(devid) < 0 || */)			continue;		/* no suitable device */ 		/* found a live one */		lastbit = 1 << biosndevs;		mask |= lastbit; 		bdp = &bdev[biosndevs];		bdp->id = devid;		size = extgetsize(bdp);		if (size == 0)			continue;		/* no device */		bdp->size = size; 		print("bios%d: drive %#ux: %,llud bytes, %d-byte sectors\n",			biosndevs, devid, size, bdp->sectsz);		biosndevs++;	}	USED(lastbit); 	if (Debug && ndrive != biosndevs)		print("devbios: expected %d drives, found %d\n", ndrive, biosndevs); 	/*	 * some bioses seem to only be able to read from drive number 0x80 and	 * can't read from the highest drive number, even if there is only one.	 */	if (biosndevs > 0)		biosinited = 1;	else		panic("devbios: no bios drives seen"); /* 9loadusb needs ≥ 1 */	delay(Pause);		/* pause to read the screen (DEBUG) */	return mask;} static voidbiosreset(void){	biosinit0();} static voidbiosinit(void){} static Chan*biosattach(char *spec){	ulong drive;	char *p;	Chan *chan; 	drive = 0;	if(spec && *spec){		drive = strtoul(spec, &p, 0);		if((drive == 0 && p == spec) || *p || (drive >= Maxdevs))			error(Ebadarg);	}	if(bdev[drive].rootchan)		return bdev[drive].rootchan; 	chan = devattach(L'☹', spec);	if(waserror()){		chanfree(chan);		nexterror();	}	chan->dev = drive;	bdev[drive].rootchan = chan;	/* arbitrary initialisation can go here */	poperror();	return chan;} static intunitgen(Chan *c, ulong type, Dir *dp){	int perm, t;	ulong vers;	vlong size;	char *p;	Qid q; 	perm = 0644;	size = 0;//	d = unit2dev(UNIT(c->qid));//	vers = d->vers;	vers = 0;	t = QTFILE; 	switch(type){	default:		return -1;	case Qctl:		p = "ctl";		break;	case Qdata:		p = "data";		perm = 0640;		break;	}	mkqid(&q, QID(UNIT(c->qid), type), vers, t);	devdir(c, q, p, size, eve, perm, dp);	return 1;} static inttopgen(Chan *c, ulong type, Dir *d){	int perm;	vlong size;	char *p;	Qid q; 	size = 0;	switch(type){	default:		return -1;	case Qdata:		p = "data";		perm = 0644;		break;	}	mkqid(&q, type, 0, QTFILE);	devdir(c, q, p, size, eve, perm, d);	return 1;} static intbiosgen(Chan *c, char *, Dirtab *, int, int s, Dir *dp){	Qid q; 	if(c->qid.path == 0){		switch(s){		case DEVDOTDOT:			q.path = 0;			q.type = QTDIR;			devdir(c, q, "#☹", 0, eve, 0555, dp);			break;		case 0:			q.path = Qtopdir;			q.type = QTDIR;			devdir(c, q, "bios", 0, eve, 0555, dp);			break;		default:			return -1;		}		return 1;	} 	switch(TYPE(c->qid)){	default:		return -1;	case Qtopdir:		if(s == DEVDOTDOT){			mkqid(&q, Qzero, 0, QTDIR);			devdir(c, q, "bios", 0, eve, 0555, dp);			return 1;		}		if(s < Qtopfiles)			return topgen(c, Qtopbase + s, dp);		s -= Qtopfiles;		if(s >= 1)			return -1;		mkqid(&q, QID(s, Qunitdir), 0, QTDIR);		devdir(c, q, "bios", 0, eve, 0555, dp);		return 1;	case Qdata:		return unitgen(c, TYPE(c->qid), dp);	}} static Walkqid*bioswalk(Chan *c, Chan *nc, char **name, int nname){	return devwalk(c, nc, name, nname, nil, 0, biosgen);} static intbiosstat(Chan *c, uchar *db, int n){	return devstat(c, db, n, nil, 0, biosgen);} static Chan*biosopen(Chan *c, int omode){	return devopen(c, omode, 0, 0, biosgen);} static voidbiosclose(Chan *){} #ifdef UNUSEDintbiosboot(int dev, char *file, Boot *b){	Bootfs *fs; 	if(strncmp(file, "dos!", 4) == 0)		file += 4;	if(strchr(file, '!') != nil || strcmp(file, "") == 0) {		print("syntax is bios0!file\n");		return -1;	} 	fs = biosgetfspart(dev, "9fat", 1);	if(fs == nil)		return -1;	return fsboot(fs, file, b);}#endif /* read n bytes at sector offset into a from drive id */longsectread(Biosdev *bdp, void *a, long n, Devsects offset){	uchar *xch;	uintptr xchaddr;	Dap *dap; 	if(bdp->sectsz <= 0 || n < 0 || n > bdp->sectsz)		return -1;	xch = (uchar *)BIOSXCHG;	assertlow64k(PADDR(xch), "biosxchg");	if(Debug)		/* scribble on the buffer to provoke trouble */

Lines 1–500 of 808.