123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 |
- /*
- * boot driver for BIOS devices
- */
- #include <u.h>
- #include "lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "io.h"
- #include "ureg.h"
- #include "fs.h"
- typedef uvlong Devbytes, Devsects;
- typedef struct Biosdrive Biosdrive; /* 1 drive -> ndevs */
- typedef struct Biosdev Biosdev;
- enum {
- Debug = 0,
- Maxdevs = 4,
- CF = 1,
- Flopid = 0, /* first floppy */
- Baseid = 0x80, /* first disk */
- /* bios calls: int 13 disk services */
- Biosinit = 0, /* initialise disk & floppy ctlrs */
- Biosdrvsts,
- Bioschsrdsects,
- Biosdrvparam = 8,
- Biosctlrinit,
- Biosreset = 0xd, /* reset disk */
- Biosdrvrdy = 0x10,
- Biosdrvtype = 0x15,
- Biosckext = 0x41,
- Biosrdsect,
- Biosedrvparam = 0x48,
- /* disk types */
- Typenone = 0,
- Typedisk = 3,
- };
- struct Biosdrive {
- int ndevs;
- };
- struct Biosdev {
- Devbytes size;
- Devbytes offset;
- uchar id; /* drive number; e.g., 0x80 */
- char type;
- ushort sectsz;
- };
- typedef struct Extread {
- uchar size;
- uchar unused1;
- uchar nsects;
- uchar unused2;
- ulong addr; /* segment:offset */
- uvlong stsect; /* starting sector */
- } Extread;
- typedef struct Edrvparam {
- /* from edd 1.1 spec */
- ushort size; /* max. buffer size */
- ushort flags;
- ulong physcyls;
- ulong physheads;
- ulong phystracksects;
- uvlong physsects;
- ushort sectsz;
- void *dpte; /* ~0ull: invalid */
- /* remainder from edd 3.0 spec */
- ushort key; /* 0xbedd if present */
- uchar dpilen;
- uchar unused1;
- ushort unused2;
- char bustype[4]; /* "PCI" or "ISA" */
- char ifctype[8]; /* "ATA", "ATAPI", "SCSI", "USB", "1394", "FIBRE" */
- uvlong ifcpath;
- uvlong devpath;
- uchar unused3;
- uchar dpicksum;
- } Edrvparam;
- void realmode(int intr, Ureg *ureg); /* from trap.c */
- int onlybios0;
- int biosinited;
- static Biosdev bdev[Maxdevs];
- static Biosdrive bdrive;
- static Ureg regs;
- static int dreset(uchar drive);
- static Devbytes extgetsize(Biosdev *);
- static Devsects getsize(uchar drive, char *type);
- static int islba(uchar drive);
- static int
- biosdiskcall(Ureg *rp, uchar op, ulong bx, ulong dx, ulong si)
- {
- memset(rp, 0, sizeof *rp);
- rp->ax = op << 8;
- rp->bx = bx;
- rp->dx = dx; /* often drive id */
- rp->si = si;
- /* pass command in *rp, get results from there */
- realmode(0x13, rp);
- if (rp->flags & CF) {
- // print("biosdiskcall: int 13 op 0x%ux drive 0x%lux failed, "
- // "ah error code 0x%ux\n", op, dx, (uchar)(rp->ax >> 8));
- return -1;
- }
- return 0;
- }
- /*
- * Find out what the bios knows about devices.
- * our boot device could be usb; ghod only knows where it will appear.
- */
- int
- biosinit(void)
- {
- int devid, lba, mask, lastbit;
- Devbytes size;
- char type;
- Biosdev *bdp;
- static int beenhere;
- mask = lastbit = 0;
- if (beenhere)
- return mask;
- beenhere = 1;
- /* 9pxeload can't use bios int 13 calls; they wedge the machine */
- if (pxe || getconf("*nobiosload") != nil || onlybios0 || !biosinited)
- return mask;
- for (devid = 0; devid < (1 << 8) && bdrive.ndevs < Maxdevs; devid++) {
- lba = islba(devid);
- if(!lba /* || devid != Baseid && dreset(devid) < 0 */ )
- continue;
- type = Typedisk; /* HACK */
- if (getsize(devid, &type) == 0) { /* no device, end of range */
- devid &= ~0xf;
- devid += 0x10;
- devid--;
- continue;
- }
- lastbit = 1 << bdrive.ndevs;
- mask |= lastbit;
- bdp = &bdev[bdrive.ndevs];
- bdp->id = devid;
- bdp->type = type;
- size = extgetsize(bdp);
- bdp->size = size;
- print("bios%d: drive 0x%ux: %llud bytes, type %d\n",
- bdrive.ndevs, devid, size, type);
- bdrive.ndevs++;
- }
- /*
- * bioses seem to only be able to read from drive number 0x80
- * and certainly can't read from the highest drive number when we
- * call them, even if there is only one. attempting to read from
- * the last drive number yields a hung machine or a two-minute pause.
- */
- if (bdrive.ndevs > 0) {
- if (bdrive.ndevs == 1) {
- print("biosinit: sorry, only one bios drive; "
- "can't read last one\n");
- onlybios0 = 1;
- } else
- biosinited = 1;
- bdrive.ndevs--; /* omit last drive number; it can't be read */
- mask &= ~lastbit;
- }
- return mask;
- }
- void
- biosinitdev(int i, char *name)
- {
- if(i >= bdrive.ndevs)
- panic("biosinitdev");
- sprint(name, "bios%d", i);
- }
- void
- biosprintdevs(int i)
- {
- if(i >= bdrive.ndevs){
- print("got a print for %d, only got %d\n", i, bdrive.ndevs);
- panic("biosprintdevs");
- }
- print(" bios%d", i);
- }
- int
- biosboot(int dev, char *file, Boot *b)
- {
- Fs *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);
- }
- /* read n bytes at sector offset into a from drive id */
- long
- sectread(Biosdev *bdp, void *a, long n, Devsects offset)
- {
- uchar *biosparam, *cp;
- Extread *erp;
- if(n < 0 || n > bdp->sectsz)
- return -1;
- if(Debug)
- memset((uchar *)BIOSXCHG, 'r', bdp->sectsz); /* preclean the buffer. */
- biosdiskcall(®s, Biosdrvrdy, 0, bdp->id, 0);
- /* space for a BIG sector, just in case... */
- biosparam = (uchar *)BIOSXCHG + 2*1024;
- /* read into BIOSXCHG */
- erp = (Extread *)biosparam;
- memset(erp, 0, sizeof *erp);
- erp->size = sizeof *erp;
- erp->nsects = 1;
- erp->addr = PADDR(BIOSXCHG);
- erp->stsect = offset;
- if (biosdiskcall(®s, Biosrdsect, 0, bdp->id, PADDR(erp)) < 0) {
- print("sectread: bios failed to read %ld @ sector %lld of 0x%ux\n",
- n, offset, bdp->id);
- return -1;
- }
- /* copy into caller's buffer */
- memmove(a, (char *)BIOSXCHG, n);
- if(Debug){
- cp = (uchar *)BIOSXCHG;
- print("-%ux %ux %ux %ux--%16.16s-\n",
- cp[0], cp[1], cp[2], cp[3], (char *)cp + 480);
- }
- return n;
- }
- /* not tested yet. */
- static int
- dreset(uchar drive)
- {
- if (0) {
- print("devbios: resetting disk controllers...");
- biosdiskcall(®s, Biosinit, 0, drive, 0);
- print("\n");
- }
- return regs.ax? -1: 0; /* ax!=0 on error */
- }
- static int
- islba(uchar drive)
- {
- if (biosdiskcall(®s, Biosckext, 0x55aa, drive, 0) < 0)
- return 0;
- if(regs.bx != 0xaa55){
- print("islba: buggy bios\n");
- return 0;
- }
- if (Debug)
- print("islba: drive 0x%ux extensions version %d.%d cx 0x%lux\n",
- drive, (uchar)(regs.ax >> 8),
- (uchar)regs.ax, regs.cx); /* cx: 4=edd, 1=use dap */
- return regs.cx & 1; /* dap bit */
- }
- /*
- * works so so... some floppies are 0x80+x when they shouldn't be,
- * and report lba even if they cannot...
- */
- static Devsects
- getsize(uchar id, char *typep)
- {
- int dtype;
- if (biosdiskcall(®s, Biosdrvtype, 0x55aa, id, 0) < 0)
- return 0;
- dtype = (ushort)regs.ax >> 8;
- if(dtype == Typenone){
- print("no such device 0x%ux of type %d\n", id, dtype);
- return 0;
- }
- if(dtype != Typedisk){
- print("non-disk device 0x%ux of type %d\n", id, dtype);
- return 0;
- }
- *typep = dtype;
- return (ushort)regs.cx | regs.dx << 16;
- }
- /* extended get size */
- static Devbytes
- extgetsize(Biosdev *bdp)
- {
- Edrvparam *edp;
- edp = (Edrvparam *)BIOSXCHG;
- memset(edp, 0, sizeof *edp);
- edp->size = sizeof *edp;
- edp->dpilen = 36;
- if (biosdiskcall(®s, Biosedrvparam, 0, bdp->id, PADDR(edp)) < 0)
- return 0;
- if(Debug) {
- print("extgetsize: drive 0x%ux info flags 0x%ux",
- bdp->id, edp->flags);
- if (edp->key == 0xbedd)
- print(" %s %s", edp->bustype, edp->ifctype);
- print("\n");
- }
- if (edp->sectsz <= 0) {
- print("extgetsize: drive 0x%ux: non-positive sector size\n",
- bdp->id);
- edp->sectsz = 1; /* don't divide by zero */
- }
- bdp->sectsz = edp->sectsz;
- return edp->physsects * edp->sectsz;
- }
- long
- biosread(Fs *fs, void *a, long n)
- {
- int want, got, part;
- long totnr, stuck;
- Devbytes offset;
- Biosdev *bdp;
- if(fs->dev > bdrive.ndevs)
- return -1;
- if (n <= 0)
- return n;
- bdp = &bdev[fs->dev];
- offset = bdp->offset;
- stuck = 0;
- for (totnr = 0; totnr < n && stuck < 4; totnr += got) {
- if (bdp->sectsz == 0) {
- print("devbios: zero sector size\n");
- return -1;
- }
- want = bdp->sectsz;
- if (totnr + want > n)
- want = n - totnr;
- if(Debug)
- print("bios%d, read: %ld @ off %lld, want: %d, id: 0x%ux\n",
- fs->dev, n, offset, want, bdp->id);
- part = offset % bdp->sectsz;
- if (part != 0) { /* back up to start of sector */
- offset -= part;
- totnr -= part;
- if (totnr < 0) {
- print("biosread: negative count %ld\n", totnr);
- return -1;
- }
- }
- if ((vlong)offset < 0) {
- print("biosread: negative offset %lld\n", offset);
- return -1;
- }
- got = sectread(bdp, (char *)a + totnr, want, offset/bdp->sectsz);
- if(got <= 0){
- // print("biosread: failed to read %ld @ off %lld of 0x%ux, "
- // "want %d got %d\n",
- // n, offset, bdp->id, want, got);
- return -1;
- }
- offset += got;
- bdp->offset = offset;
- if (got < bdp->sectsz)
- stuck++; /* we'll have to re-read this sector */
- else
- stuck = 0;
- }
- return totnr;
- }
- vlong
- biosseek(Fs *fs, vlong off)
- {
- if (off < 0) {
- print("biosseek(fs, %lld) is illegal\n", off);
- return -1;
- }
- if(fs->dev > bdrive.ndevs) {
- print("biosseek: fs->dev %d > bdrive.ndevs %d\n",
- fs->dev, bdrive.ndevs);
- return -1;
- }
- bdev[fs->dev].offset = off; /* do not know size... (yet) */
- return off;
- }
- void *
- biosgetfspart(int i, char *name, int chatty)
- {
- static Fs fs;
- if(strcmp(name, "9fat") != 0){
- if(chatty)
- print("unknown partition bios%d!%s (use bios%d!9fat)\n",
- i, name, i);
- return nil;
- }
- fs.dev = i;
- fs.diskread = biosread;
- fs.diskseek = biosseek;
- if(dosinit(&fs) < 0){
- if(chatty)
- print("bios%d!%s does not contain a FAT file system\n",
- i, name);
- return nil;
- }
- return &fs;
- }
|