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(®s, 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(®s, 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(®s, 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(®s, 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