#include <stand.h>
#include <sys/disklabel32.h>
#include <sys/disklabel64.h>
#include <sys/diskmbr.h>
#include <sys/dtype.h>
#include <machine/bootinfo.h>
#include <machine/psl.h>
#include <stdarg.h>
#include <bootstrap.h>
#include <btxv86.h>
#include "libi386.h"
#define BIOS_NUMDRIVES 0x475
#define BIOSDISK_SECSIZE 512
#define BUFSIZE (4 * BIOSDISK_SECSIZE)
#define MAXBDDEV MAXDEV
#define DT_ATAPI 0x10
#define WDMAJOR 0
#define WFDMAJOR 1
#define FDMAJOR 2
#define DAMAJOR 4
#ifdef DISK_DEBUG
# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
#else
# define DEBUG(fmt, args...)
#endif
struct open_disk {
int od_dkunit;
int od_unit;
int od_cyl;
int od_hds;
int od_sec;
int od_boff;
int od_flags;
#define BD_MODEINT13 0x0000
#define BD_MODEEDD1 0x0001
#define BD_MODEEDD3 0x0002
#define BD_MODEMASK 0x0003
#define BD_FLOPPY 0x0004
#define BD_LABELOK 0x0008
#define BD_PARTTABOK 0x0010
union {
struct disklabel32 od_disklabel;
struct disklabel64 od_disklabel64;
};
int od_nslices;
struct dos_partition od_slicetab[NEXTDOSPART];
};
static struct bdinfo
{
int bd_unit;
int bd_flags;
int bd_type;
} bdinfo [MAXBDDEV];
static int nbdinfo = 0;
static int bd_getgeom(struct open_disk *od);
static int bd_read(struct open_disk *od, daddr_t dblk, int blks,
caddr_t dest);
static int bd_write(struct open_disk *od, daddr_t dblk, int blks,
caddr_t dest);
static int bd_int13probe(struct bdinfo *bd);
static void bd_printslice(struct open_disk *od, struct dos_partition *dp,
char *prefix, int verbose);
static void bd_printbsdslice(struct open_disk *od, daddr_t offset,
char *prefix, int verbose);
static int bd_init(void);
static int bd_strategy(void *devdata, int flag, daddr_t dblk,
size_t size, char *buf, size_t *rsize);
static int bd_realstrategy(void *devdata, int flag, daddr_t dblk,
size_t size, char *buf, size_t *rsize);
static int bd_open(struct open_file *f, ...);
static int bd_close(struct open_file *f);
static void bd_print(int verbose);
struct devsw biosdisk = {
"disk",
DEVT_DISK,
bd_init,
bd_strategy,
bd_open,
bd_close,
noioctl,
bd_print,
NULL
};
static int bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev);
static void bd_closedisk(struct open_disk *od);
static int bd_bestslice(struct open_disk *od);
static void bd_chainextended(struct open_disk *od, u_int32_t base,
u_int32_t offset);
char __aligned(16) bounce_base[BOUNCEBUF_SIZE];
int
bd_bios2unit(int biosdev)
{
int i;
DEBUG("looking for bios device 0x%x", biosdev);
for (i = 0; i < nbdinfo; i++) {
DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit);
if (bdinfo[i].bd_unit == biosdev)
return(i);
}
return(-1);
}
int
bd_unit2bios(int unit)
{
if ((unit >= 0) && (unit < nbdinfo))
return(bdinfo[unit].bd_unit);
return(-1);
}
static int
bd_init(void)
{
int base, unit, nfd = 0;
for (base = 0; base <= 0x80; base += 0x80) {
for (unit = base; (nbdinfo < MAXBDDEV); unit++) {
if((base == 0x80) &&
(nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES)))
break;
bdinfo[nbdinfo].bd_unit = unit;
bdinfo[nbdinfo].bd_flags = (unit < 0x80) ? BD_FLOPPY : 0;
if (!bd_int13probe(&bdinfo[nbdinfo]))
break;
printf("BIOS drive %c: is disk%d\n",
(unit < 0x80) ? ('A' + unit) : ('C' + unit - 0x80), nbdinfo);
nbdinfo++;
if (base == 0x80)
nfd++;
}
}
return(0);
}
static int
bd_int13probe(struct bdinfo *bd)
{
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0x800;
v86.edx = bd->bd_unit;
v86int();
if (!(v86.efl & PSL_C) &&
((v86.edx & 0xff) > ((unsigned)bd->bd_unit & 0x7f))) {
if ((v86.ecx & 0x3f) == 0) {
DEBUG("Invalid geometry for unit %d", bd->bd_unit);
return(0);
}
bd->bd_flags |= BD_MODEINT13;
bd->bd_type = v86.ebx & 0xff;
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0x4100;
v86.edx = bd->bd_unit;
v86.ebx = 0x55aa;
v86int();
if (!(v86.efl & PSL_C) &&
((v86.ebx & 0xffff) == 0xaa55) &&
(v86.ecx & 0x1)) {
bd->bd_flags |= BD_MODEEDD1;
if((v86.eax & 0xff00) > 0x300)
bd->bd_flags |= BD_MODEEDD3;
}
return(1);
}
return(0);
}
static void
bd_print(int verbose)
{
int i, j;
char line[80];
struct i386_devdesc dev;
struct open_disk *od;
struct dos_partition *dptr;
for (i = 0; i < nbdinfo; i++) {
sprintf(line, " disk%d: BIOS drive %c:\n", i,
(bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit) : ('C' + bdinfo[i].bd_unit - 0x80));
pager_output(line);
dev.d_kind.biosdisk.unit = i;
dev.d_kind.biosdisk.slice = -1;
dev.d_kind.biosdisk.partition = -1;
if (!bd_opendisk(&od, &dev)) {
if (od->od_flags & BD_PARTTABOK) {
dptr = &od->od_slicetab[0];
if ((dptr[3].dp_typ == DOSPTYP_386BSD ||
dptr[3].dp_typ == DOSPTYP_NETBSD ||
dptr[3].dp_typ == DOSPTYP_DFLYBSD) &&
(dptr[3].dp_start == 0) &&
(dptr[3].dp_size == 50000)) {
sprintf(line, " disk%d", i);
bd_printbsdslice(od, 0, line, verbose);
} else {
for (j = 0; j < od->od_nslices; j++) {
sprintf(line, " disk%ds%d", i, j + 1);
bd_printslice(od, &dptr[j], line, verbose);
}
}
}
bd_closedisk(od);
}
}
}
static void
bd_printslice(struct open_disk *od, struct dos_partition *dp, char *prefix,
int verbose)
{
char line[80];
switch (dp->dp_typ) {
case DOSPTYP_DFLYBSD:
case DOSPTYP_386BSD:
case DOSPTYP_NETBSD:
bd_printbsdslice(od, (daddr_t)dp->dp_start, prefix, verbose);
return;
case DOSPTYP_LINSWP:
if (verbose)
sprintf(line, "%s: Linux swap %.6dMB (%d - %d)\n",
prefix, dp->dp_size / 2048,
dp->dp_start, dp->dp_start + dp->dp_size);
else
sprintf(line, "%s: Linux swap\n", prefix);
break;
case DOSPTYP_LINUX:
if (verbose)
sprintf(line, "%s: ext2fs %.6dMB (%d - %d)\n", prefix,
dp->dp_size / 2048, dp->dp_start,
dp->dp_start + dp->dp_size);
else
sprintf(line, "%s: ext2fs\n", prefix);
break;
case 0x00:
case DOSPTYP_EXT:
return;
case 0x01:
if (verbose)
sprintf(line, "%s: FAT-12 %.6dMB (%d - %d)\n", prefix,
dp->dp_size / 2048, dp->dp_start,
dp->dp_start + dp->dp_size);
else
sprintf(line, "%s: FAT-12\n", prefix);
break;
case 0x04:
case 0x06:
case 0x0e:
if (verbose)
sprintf(line, "%s: FAT-16 %.6dMB (%d - %d)\n", prefix,
dp->dp_size / 2048, dp->dp_start,
dp->dp_start + dp->dp_size);
else
sprintf(line, "%s: FAT-16\n", prefix);
break;
case 0x0b:
case 0x0c:
if (verbose)
sprintf(line, "%s: FAT-32 %.6dMB (%d - %d)\n", prefix,
dp->dp_size / 2048, dp->dp_start,
dp->dp_start + dp->dp_size);
else
sprintf(line, "%s: FAT-32\n", prefix);
break;
default:
if (verbose)
sprintf(line, "%s: Unknown fs: 0x%x %.6dMB (%d - %d)\n",
prefix, dp->dp_typ, dp->dp_size / 2048,
dp->dp_start, dp->dp_start + dp->dp_size);
else
sprintf(line, "%s: Unknown fs: 0x%x\n", prefix,
dp->dp_typ);
}
pager_output(line);
}
static void
print_partition(u_int8_t fstype, unsigned long long offset,
unsigned long long size, int i, int od_flags,
char *prefix, int verbose, int type)
{
char line[80];
if ((fstype == FS_SWAP) ||
(fstype == FS_VINUM) ||
(fstype == FS_HAMMER) ||
(fstype == FS_HAMMER2) ||
(fstype == FS_BSDFFS) ||
(fstype == FS_ZFS) ||
(fstype == FS_JFS2) ||
((fstype == FS_UNUSED) &&
(od_flags & BD_FLOPPY) && (i == 0))) {
if (verbose) {
sprintf(line, "%c %s%c: %s %.6lluMB (%llu - %llu)\n",
((fstype == FS_BSDFFS) || (fstype == FS_UNUSED) ||
(fstype == FS_VINUM)) ? '*' : ' ',
prefix, 'a' + i,
(fstype == FS_SWAP) ? "swap" :
(fstype == FS_VINUM) ? "vinum" :
(fstype == FS_HAMMER) ? "HAMMER" :
(fstype == FS_HAMMER2) ? "HAMMER2" :
(fstype == FS_JFS2) ? "JFS2" :
(fstype == FS_ZFS) ? "ZFS" :
(fstype == FS_BSDFFS) ? "FFS" :
"(unknown)",
(type==32)?(size / 2048):(size/1024/1024),
offset,
offset + size);
} else {
sprintf(line, "%c %s%c: %s\n",
((fstype == FS_BSDFFS) || (fstype == FS_UNUSED) ||
(fstype == FS_VINUM)) ? '*' : ' ',
prefix, 'a' + i,
(fstype == FS_SWAP) ? "swap" :
(fstype == FS_VINUM) ? "vinum" :
(fstype == FS_HAMMER) ? "HAMMER" :
(fstype == FS_HAMMER2) ? "HAMMER2" :
(fstype == FS_JFS2) ? "JFS2" :
(fstype == FS_ZFS) ? "ZFS" :
(fstype == FS_BSDFFS) ? "FFS" :
"(unknown)");
}
pager_output(line);
}
}
static void
bd_printbsdslice(struct open_disk *od, daddr_t offset, char *prefix,
int verbose)
{
char line[80];
char buf[BIOSDISK_SECSIZE*2];
struct disklabel32 *lp = NULL;
struct disklabel64 *lp64 = NULL;
int i;
if (bd_read(od, offset + LABELSECTOR32, 1, buf))
return;
lp =(struct disklabel32 *)(&buf[0]);
if (lp->d_magic != DISKMAGIC32) {
if (bd_read(od, offset, 2, buf))
return;
lp64 =(struct disklabel64 *)(&buf[0]);
if (lp64->d_magic != DISKMAGIC64) {
sprintf(line, "%s: bad disklabel\n", prefix);
pager_output(line);
return;
}
lp = NULL;
}
if (lp64) {
for (i = 0; i < lp64->d_npartitions; i++) {
if (lp64->d_partitions[i].p_bsize == 0)
continue;
print_partition(lp64->d_partitions[i].p_fstype,
lp64->d_partitions[i].p_boffset,
lp64->d_partitions[i].p_bsize,
i, od->od_flags, prefix, verbose, 64);
}
} else if (lp) {
for (i = 0; i < lp->d_npartitions; i++) {
print_partition(lp->d_partitions[i].p_fstype,
lp->d_partitions[i].p_offset,
lp->d_partitions[i].p_size,
i, od->od_flags, prefix, verbose, 32);
}
}
}
static int
bd_open(struct open_file *f, ...)
{
va_list ap;
struct i386_devdesc *dev;
struct open_disk *od;
int error;
va_start(ap, f);
dev = va_arg(ap, struct i386_devdesc *);
va_end(ap);
if ((error = bd_opendisk(&od, dev)))
return(error);
((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od;
DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff);
return(0);
}
static int
bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
{
struct dos_partition *dptr;
struct disklabel32 *lp;
struct disklabel64 *lp64;
struct open_disk *od;
int sector, slice, i;
int error;
static char buf[BUFSIZE];
if (dev->d_kind.biosdisk.unit >= nbdinfo) {
DEBUG("attempt to open nonexistent disk");
return(ENXIO);
}
od = (struct open_disk *)malloc(sizeof(struct open_disk));
if (!od) {
DEBUG("no memory");
return (ENOMEM);
}
od->od_dkunit = dev->d_kind.biosdisk.unit;
od->od_unit = bdinfo[od->od_dkunit].bd_unit;
od->od_flags = bdinfo[od->od_dkunit].bd_flags;
od->od_boff = 0;
od->od_nslices = 0;
error = 0;
DEBUG("open '%s', unit 0x%x slice %d partition %c",
i386_fmtdev(dev), dev->d_kind.biosdisk.unit,
dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition + 'a');
if (bd_getgeom(od)) {
DEBUG("can't get geometry");
error = ENXIO;
goto out;
}
if (bd_read(od, 0, 1, buf)) {
DEBUG("error reading MBR");
error = EIO;
goto out;
}
if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) {
if (dev->d_kind.biosdisk.slice > 0) {
DEBUG("no slice table/MBR (no magic)");
error = ENOENT;
goto out;
}
sector = 0;
goto unsliced;
}
bcopy(buf + DOSPARTOFF, &od->od_slicetab,
sizeof(struct dos_partition) * NDOSPART);
od->od_nslices = NDOSPART;
dptr = &od->od_slicetab[0];
for (i = 0; i < NDOSPART; i++, dptr++) {
if ((dptr->dp_typ == DOSPTYP_EXT) || (dptr->dp_typ == DOSPTYP_EXTLBA))
bd_chainextended(od, dptr->dp_start, 0);
}
od->od_flags |= BD_PARTTABOK;
dptr = &od->od_slicetab[0];
if (od->od_nslices > NEXTDOSPART)
od->od_nslices = NEXTDOSPART;
if (dev->d_kind.biosdisk.slice == -1) {
sector = 0;
goto unsliced;
}
if (dev->d_kind.biosdisk.slice > 0) {
slice = dev->d_kind.biosdisk.slice - 1;
if (slice >= od->od_nslices) {
DEBUG("slice %d not found", slice);
error = ENOENT;
goto out;
}
}
if ((dptr[3].dp_typ == DOSPTYP_386BSD ||
dptr[3].dp_typ == DOSPTYP_DFLYBSD) &&
(dptr[3].dp_start == 0) &&
(dptr[3].dp_size == 50000)) {
sector = 0;
goto unsliced;
}
if (dev->d_kind.biosdisk.slice == 0) {
slice = bd_bestslice(od);
if (slice == -1) {
error = ENOENT;
goto out;
}
dev->d_kind.biosdisk.slice = slice;
}
dptr = &od->od_slicetab[0];
dptr += (dev->d_kind.biosdisk.slice - 1);
sector = dptr->dp_start;
DEBUG("slice entry %d at %d, %d sectors", dev->d_kind.biosdisk.slice - 1, sector, dptr->dp_size);
if ((dptr->dp_typ == DOSPTYP_386BSD ||
dptr->dp_typ == DOSPTYP_DFLYBSD) &&
dev->d_kind.biosdisk.partition < 0) {
dev->d_kind.biosdisk.partition = 0;
}
unsliced:
if (dev->d_kind.biosdisk.partition < 0) {
od->od_boff = sector;
DEBUG("opening raw slice");
} else {
if (bd_read(od, sector + LABELSECTOR32, 1, buf)) {
DEBUG("error reading disklabel");
error = EIO;
goto out;
}
DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel32), buf + LABELOFFSET32, &od->od_disklabel);
bcopy(buf + LABELOFFSET32, &od->od_disklabel, sizeof(struct disklabel32));
lp = &od->od_disklabel;
if (lp->d_magic == DISKMAGIC32) {
od->od_flags |= BD_LABELOK;
if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) {
DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions);
error = EPART;
goto out;
}
#ifdef DISK_DEBUG
if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) &&
!(od->od_flags & BD_FLOPPY))
DEBUG("warning, partition marked as unused");
#endif
od->od_boff =
lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset -
lp->d_partitions[RAW_PART].p_offset +
sector;
goto out;
}
if (bd_read(od, sector, (sizeof(od->od_disklabel64) + 511) / 512, buf)) {
DEBUG("error reading disklabel");
error = EIO;
goto out;
}
DEBUG("copy %d bytes of label from %p to %p", sizeof(od->od_disklabel64), buf, &od->od_disklabel64);
bcopy(buf, &od->od_disklabel64, sizeof(od->od_disklabel64));
lp64 = &od->od_disklabel64;
if (lp64->d_magic == DISKMAGIC64) {
od->od_flags |= BD_LABELOK;
if (dev->d_kind.biosdisk.partition >= lp64->d_npartitions ||
lp64->d_partitions[dev->d_kind.biosdisk.partition].p_bsize == 0) {
DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
'a' + dev->d_kind.biosdisk.partition, 'a' + lp64->d_npartitions);
error = EPART;
goto out;
}
od->od_boff =
lp64->d_partitions[dev->d_kind.biosdisk.partition].p_boffset / 512 +
sector;
DEBUG("disklabel64 slice at %d", od->od_boff);
} else {
DEBUG("no disklabel");
error = ENOENT;
}
}
out:
if (error) {
free(od);
} else {
*odp = od;
}
return(error);
}
static void
bd_chainextended(struct open_disk *od, u_int32_t base, u_int32_t offset)
{
char buf[BIOSDISK_SECSIZE];
struct dos_partition *dp1, *dp2;
int i;
if (bd_read(od, (daddr_t)(base + offset), 1, buf)) {
printf("\nerror reading extended partition table");
return;
}
dp1 = (struct dos_partition *)(&buf[DOSPARTOFF]);
dp2 = &od->od_slicetab[od->od_nslices];
for (i = 0; i < NDOSPART; ++i, ++dp1) {
if (dp1->dp_scyl == 0 && dp1->dp_shd == 0 &&
dp1->dp_ssect == 0 && dp1->dp_start == 0 &&
dp1->dp_size == 0) {
continue;
}
if ((dp1->dp_typ == DOSPTYP_EXT) ||
(dp1->dp_typ == DOSPTYP_EXTLBA)) {
continue;
}
if (od->od_nslices < NEXTDOSPART) {
dp2->dp_typ = dp1->dp_typ;
dp2->dp_start = base + offset + dp1->dp_start;
dp2->dp_size = dp1->dp_size;
}
++od->od_nslices;
++dp2;
}
dp1 -= NDOSPART;
for (i = 0; i < NDOSPART; ++i, ++dp1) {
if (dp1->dp_scyl == 0 && dp1->dp_shd == 0 &&
dp1->dp_ssect == 0 && dp1->dp_start == 0 &&
dp1->dp_size == 0) {
continue;
}
if ((dp1->dp_typ == DOSPTYP_EXT) ||
(dp1->dp_typ == DOSPTYP_EXTLBA))
bd_chainextended(od, base, dp1->dp_start);
}
}
#define PREF_RAWDISK 0
#define PREF_FBSD_ACT 1
#define PREF_FBSD 2
#define PREF_LINUX_ACT 3
#define PREF_LINUX 4
#define PREF_DOS_ACT 5
#define PREF_DOS 6
#define PREF_NONE 7
static int
bd_bestslice(struct open_disk *od)
{
struct dos_partition *dp;
int pref, preflevel;
int i, prefslice;
prefslice = 0;
preflevel = PREF_NONE;
dp = &od->od_slicetab[0];
for (i = 0; i < od->od_nslices; i++, dp++) {
switch (dp->dp_typ) {
case DOSPTYP_DFLYBSD:
case DOSPTYP_386BSD:
pref = dp->dp_flag & 0x80 ? PREF_FBSD_ACT : PREF_FBSD;
break;
case DOSPTYP_LINUX:
pref = dp->dp_flag & 0x80 ? PREF_LINUX_ACT : PREF_LINUX;
break;
case 0x01:
case 0x04:
case 0x06:
case 0x0b:
case 0x0c:
case 0x0e:
pref = dp->dp_flag & 0x80 ? PREF_DOS_ACT : PREF_DOS;
break;
default:
pref = PREF_NONE;
}
if (pref < preflevel) {
preflevel = pref;
prefslice = i + 1;
}
}
return (prefslice);
}
static int
bd_close(struct open_file *f)
{
struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data);
bd_closedisk(od);
return(0);
}
static void
bd_closedisk(struct open_disk *od)
{
DEBUG("open_disk %p", od);
#if 0
if (od->od_flags & BD_FLOPPY)
delay(3000000);
#endif
free(od);
}
static int
bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
{
struct bcache_devdata bcd;
struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data);
bcd.dv_strategy = bd_realstrategy;
bcd.dv_devdata = devdata;
return(bcache_strategy(&bcd, od->od_unit, rw, dblk+od->od_boff, size, buf, rsize));
}
static int
bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
{
struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data);
int blks;
#ifdef BD_SUPPORT_FRAGS
char fragbuf[BIOSDISK_SECSIZE];
size_t fragsize;
fragsize = size % BIOSDISK_SECSIZE;
#else
if (size % BIOSDISK_SECSIZE)
panic("bd_strategy: %d bytes I/O not multiple of block size", size);
#endif
DEBUG("open_disk %p", od);
switch(rw){
case F_READ:
blks = size / BIOSDISK_SECSIZE;
DEBUG("read %d from %d to %p", blks, dblk, buf);
if (rsize)
*rsize = 0;
if (blks && bd_read(od, dblk, blks, buf)) {
DEBUG("read error");
return (EIO);
}
#ifdef BD_SUPPORT_FRAGS
DEBUG("bd_strategy: frag read %d from %d+%d to %p",
fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE));
if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) {
DEBUG("frag read error");
return(EIO);
}
bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize);
#endif
if (rsize)
*rsize = size;
return (0);
case F_WRITE :
blks = size / BIOSDISK_SECSIZE;
DEBUG("write %d from %d to %p", blks, dblk, buf);
if (rsize)
*rsize = 0;
if (blks && bd_write(od, dblk, blks, buf)) {
DEBUG("write error");
return (EIO);
}
#ifdef BD_SUPPORT_FRAGS
if(fragsize) {
DEBUG("Attempted to write a frag");
return (EIO);
}
#endif
if (rsize)
*rsize = size;
return (0);
default:
break;
}
return EROFS;
}
static int
bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest)
{
u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer;
caddr_t p, xp, bbuf, breg;
if (blks < 0)
return (-1);
bpc = (od->od_sec * od->od_hds);
resid = blks;
p = dest;
if (1) {
bbuf = bounce_base;
x = min(BOUNCEBUF_SECTS / 2, (unsigned)blks);
if (((u_int32_t)VTOP(bbuf) & 0xffff8000) ==
((u_int32_t)VTOP(bbuf + x * BIOSDISK_SECSIZE - 1) & 0xffff8000)) {
breg = bbuf;
} else {
breg = bbuf + x * BIOSDISK_SECSIZE;
}
maxfer = x;
} else {
breg = bbuf = NULL;
maxfer = 0;
}
while (resid > 0) {
x = dblk;
cyl = x / bpc;
x %= bpc;
hd = x / od->od_sec;
sec = x % od->od_sec;
x = szmin(od->od_sec - sec, resid);
if (maxfer > 0)
x = min(x, maxfer);
xp = (bbuf == NULL ? p : breg);
sec++;
for (retry = 0; retry < 3; retry++) {
if (retry > 0) {
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0;
v86.edx = od->od_unit;
v86int();
}
if (od->od_flags & BD_MODEEDD1) {
static unsigned short packet[8];
packet[0] = 0x10;
packet[1] = x;
packet[2] = VTOPOFF(xp);
packet[3] = VTOPSEG(xp);
packet[4] = dblk & 0xffff;
packet[5] = dblk >> 16;
packet[6] = 0;
packet[7] = 0;
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0x4200;
v86.edx = od->od_unit;
v86.ds = VTOPSEG(packet);
v86.esi = VTOPOFF(packet);
v86int();
result = (v86.efl & PSL_C);
if (result == 0)
break;
} else if (cyl < 1024) {
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0x200 | x;
v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
v86.edx = (hd << 8) | od->od_unit;
v86.es = VTOPSEG(xp);
v86.ebx = VTOPOFF(xp);
v86int();
result = (v86.efl & PSL_C);
if (result == 0)
break;
} else {
result = 1;
break;
}
}
DEBUG("%u sectors from %u/%u/%d to %p (0x%x) %s",
x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok");
DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x",
0x200 | x,
((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec,
(hd << 8) | od->od_unit,
(v86.eax >> 8) & 0xff);
if (result)
return(-1);
if (bbuf != NULL)
bcopy(breg, p, x * BIOSDISK_SECSIZE);
p += (x * BIOSDISK_SECSIZE);
dblk += x;
resid -= x;
}
return(0);
}
static int
bd_write(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest)
{
u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer;
caddr_t p, xp, bbuf, breg;
if (blks < 0)
return (-1);
bpc = (od->od_sec * od->od_hds);
resid = blks;
p = dest;
if (1) {
bbuf = bounce_base;
x = min(BOUNCEBUF_SECTS / 2, (unsigned)blks);
if (((u_int32_t)VTOP(bbuf) & 0xffff8000) ==
((u_int32_t)VTOP(bbuf + x * BIOSDISK_SECSIZE - 1) & 0xffff8000)) {
breg = bbuf;
} else {
breg = bbuf + x * BIOSDISK_SECSIZE;
}
maxfer = x;
} else {
breg = bbuf = NULL;
maxfer = 0;
}
while (resid > 0) {
x = dblk;
cyl = x / bpc;
x %= bpc;
hd = x / od->od_sec;
sec = x % od->od_sec;
x = szmin(od->od_sec - sec, resid);
if (maxfer > 0)
x = szmin(x, maxfer);
xp = (bbuf == NULL ? p : breg);
sec++;
if (bbuf != NULL)
bcopy(p, breg, x * BIOSDISK_SECSIZE);
p += (x * BIOSDISK_SECSIZE);
dblk += x;
resid -= x;
for (retry = 0; retry < 3; retry++) {
if (retry > 0) {
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0;
v86.edx = od->od_unit;
v86int();
}
if (od->od_flags & BD_MODEEDD1) {
static unsigned short packet[8];
packet[0] = 0x10;
packet[1] = x;
packet[2] = VTOPOFF(xp);
packet[3] = VTOPSEG(xp);
packet[4] = dblk & 0xffff;
packet[5] = dblk >> 16;
packet[6] = 0;
packet[7] = 0;
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0x4300;
v86.edx = od->od_unit;
v86.ds = VTOPSEG(packet);
v86.esi = VTOPOFF(packet);
v86int();
result = (v86.efl & PSL_C);
if (result == 0)
break;
} else if (cyl < 1024) {
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0x300 | x;
v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
v86.edx = (hd << 8) | od->od_unit;
v86.es = VTOPSEG(xp);
v86.ebx = VTOPOFF(xp);
v86int();
result = (v86.efl & PSL_C);
if (result == 0)
break;
} else {
result = 1;
break;
}
}
DEBUG("%u sectors from %u/%u/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok");
DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x",
0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff);
if (result)
return(-1);
}
return(0);
}
static int
bd_getgeom(struct open_disk *od)
{
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0x800;
v86.edx = od->od_unit;
v86int();
if ((v86.efl & PSL_C) ||
((v86.edx & 0xff) <= (unsigned)(od->od_unit & 0x7f)))
return(1);
od->od_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
od->od_hds = ((v86.edx & 0xff00) >> 8) + 1;
od->od_sec = v86.ecx & 0x3f;
DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec);
return(0);
}
u_int32_t
bd_getbigeom(int bunit)
{
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0x800;
v86.edx = 0x80 + bunit;
v86int();
if (v86.efl & PSL_C)
return 0x4f010f;
return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) |
(v86.edx & 0xff00) | (v86.ecx & 0x3f);
}
int
bd_getdev(struct i386_devdesc *dev)
{
struct open_disk *od;
int biosdev;
int major;
int rootdev;
char *nip, *cp;
int unitofs = 0, i, unit;
biosdev = bd_unit2bios(dev->d_kind.biosdisk.unit);
DEBUG("unit %d BIOS device %d", dev->d_kind.biosdisk.unit, biosdev);
if (biosdev == -1)
return(-1);
if (bd_opendisk(&od, dev) != 0)
return(-1);
if (biosdev < 0x80) {
if (bdinfo[dev->d_kind.biosdisk.unit].bd_type == DT_ATAPI) {
major = WFDMAJOR;
} else {
major = FDMAJOR;
}
} else {
if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) {
major = DAMAJOR;
if ((nip = getenv("num_ide_disks")) != NULL) {
i = strtol(nip, &cp, 0);
if ((cp != nip) && (*cp == 0))
unitofs = i;
}
} else {
major = WDMAJOR;
}
}
unit = (biosdev & 0x7f) - unitofs;
if ((nip = getenv("root_disk_unit")) != NULL) {
i = strtol(nip, &cp, 0);
if ((cp != nip) && (*cp == 0))
unit = i;
}
rootdev = MAKEBOOTDEV(major, dev->d_kind.biosdisk.slice + 1, unit,
dev->d_kind.biosdisk.partition);
DEBUG("dev is 0x%x\n", rootdev);
return(rootdev);
}