/* * 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. Load more
/* * 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 void assertlow64k(uintptr p, char *tag) { if (p & Highshort) panic("devbios: %s address %#p not in bottom 64k", tag, p); } static void initrealregs(Ureg *ureg) { memset(ureg, 0, sizeof *ureg); } /* * caller must zero or otherwise initialise *ureg, * other than ax, bx, dx, si & ds. */ static int biosdiskcall(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. */ int biosinit0(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 void biosreset(void) { biosinit0(); } static void biosinit(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 int unitgen(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 int topgen(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 int biosgen(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 int biosstat(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 void biosclose(Chan *) { } #ifdef UNUSED int biosboot(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 */ long sectread(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 */