biosdisk.c revision 51586
143561Skato/*- 243561Skato * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 343561Skato * All rights reserved. 443561Skato * 543561Skato * Redistribution and use in source and binary forms, with or without 643561Skato * modification, are permitted provided that the following conditions 743561Skato * are met: 843561Skato * 1. Redistributions of source code must retain the above copyright 943561Skato * notice, this list of conditions and the following disclaimer. 1043561Skato * 2. Redistributions in binary form must reproduce the above copyright 1143561Skato * notice, this list of conditions and the following disclaimer in the 1243561Skato * documentation and/or other materials provided with the distribution. 1343561Skato * 1443561Skato * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1543561Skato * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1643561Skato * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1743561Skato * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1843561Skato * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1943561Skato * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2043561Skato * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2143561Skato * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2243561Skato * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2343561Skato * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2443561Skato * SUCH DAMAGE. 2543561Skato * 2650477Speter * $FreeBSD: head/sys/boot/pc98/libpc98/biosdisk.c 51586 1999-09-23 03:57:16Z kato $ 2743561Skato */ 2843561Skato 2943561Skato/* 3043561Skato * BIOS disk device handling. 3143561Skato * 3243561Skato * Ideas and algorithms from: 3343561Skato * 3443561Skato * - NetBSD libi386/biosdisk.c 3543561Skato * - FreeBSD biosboot/disk.c 3643561Skato * 3743561Skato * XXX Todo: add bad144 support. 3843561Skato */ 3943561Skato 4043561Skato#include <stand.h> 4143561Skato 4243561Skato#include <sys/disklabel.h> 4343561Skato#include <sys/diskslice.h> 4443561Skato#include <sys/reboot.h> 4543561Skato 4643561Skato#include <stdarg.h> 4743561Skato 4843561Skato#include <bootstrap.h> 4943561Skato#include <btxv86.h> 5043561Skato#include "libi386.h" 5143561Skato 5243561Skato#define BIOSDISK_SECSIZE 512 5343561Skato#define BUFSIZE (1 * BIOSDISK_SECSIZE) 5443561Skato#define MAXBDDEV MAXDEV 5543561Skato 5643561Skato#define DT_ATAPI 0x10 /* disk type for ATAPI floppies */ 5743561Skato#define WDMAJOR 0 /* major numbers for devices we frontend for */ 5843561Skato#define WFDMAJOR 1 5943561Skato#define FDMAJOR 2 6043561Skato#define DAMAJOR 4 6143561Skato 6243561Skato#ifdef DISK_DEBUG 6343561Skato# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __FUNCTION__ , ## args) 6443561Skato#else 6543561Skato# define DEBUG(fmt, args...) 6643561Skato#endif 6743561Skato 6843561Skatostruct open_disk { 6943561Skato int od_dkunit; /* disk unit number */ 7043561Skato int od_unit; /* BIOS unit number */ 7143561Skato int od_cyl; /* BIOS geometry */ 7243561Skato int od_hds; 7343561Skato int od_sec; 7443561Skato int od_boff; /* block offset from beginning of BIOS disk */ 7543561Skato int od_flags; 7643561Skato#define BD_MODEMASK 0x3 7743561Skato#define BD_MODEINT13 0x0 7843561Skato#define BD_MODEEDD1 0x1 7943561Skato#define BD_MODEEDD3 0x2 8043561Skato#define BD_FLOPPY (1<<2) 8143561Skato struct disklabel od_disklabel; 8243561Skato struct dos_partition od_parttab[NDOSPART]; /* XXX needs to grow for extended partitions */ 8343561Skato#define BD_LABELOK (1<<3) 8443561Skato#define BD_PARTTABOK (1<<4) 8543561Skato}; 8643561Skato 8743561Skato/* 8843561Skato * List of BIOS devices, translation from disk unit number to 8943561Skato * BIOS unit number. 9043561Skato */ 9143561Skatostatic struct bdinfo 9243561Skato{ 9343561Skato int bd_unit; /* BIOS unit number */ 9443561Skato int bd_flags; 9543561Skato int bd_type; /* BIOS 'drive type' (floppy only) */ 9643561Skato#ifdef PC98 9749426Skato int bd_da_unit; /* kernel unit number for da */ 9843561Skato#endif 9943561Skato} bdinfo [MAXBDDEV]; 10043561Skatostatic int nbdinfo = 0; 10143561Skato 10243561Skatostatic int bd_getgeom(struct open_disk *od); 10343561Skatostatic int bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest); 10443561Skato 10543561Skatostatic int bd_int13probe(struct bdinfo *bd); 10643561Skato 10743561Skatostatic void bd_printslice(struct open_disk *od, int offset, char *prefix); 10843561Skato 10943561Skatostatic int bd_init(void); 11043561Skatostatic int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size, void *buf, size_t *rsize); 11143561Skatostatic int bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size, void *buf, size_t *rsize); 11243561Skatostatic int bd_open(struct open_file *f, ...); 11343561Skatostatic int bd_close(struct open_file *f); 11443561Skatostatic void bd_print(int verbose); 11543561Skato 11643561Skatostruct devsw biosdisk = { 11743561Skato "disk", 11843561Skato DEVT_DISK, 11943561Skato bd_init, 12043561Skato bd_strategy, 12143561Skato bd_open, 12243561Skato bd_close, 12343561Skato noioctl, 12443561Skato bd_print 12543561Skato}; 12643561Skato 12743561Skatostatic int bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev); 12843561Skatostatic void bd_closedisk(struct open_disk *od); 12943561Skatostatic int bd_bestslice(struct dos_partition *dptr); 13043561Skato 13143561Skato/* 13243561Skato * Translate between BIOS device numbers and our private unit numbers. 13343561Skato */ 13443561Skatoint 13543561Skatobd_bios2unit(int biosdev) 13643561Skato{ 13743561Skato int i; 13843561Skato 13943561Skato DEBUG("looking for bios device 0x%x", biosdev); 14043561Skato for (i = 0; i < nbdinfo; i++) { 14143561Skato DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit); 14243561Skato if (bdinfo[i].bd_unit == biosdev) 14343561Skato return(i); 14443561Skato } 14543561Skato return(-1); 14643561Skato} 14743561Skato 14843561Skatoint 14943561Skatobd_unit2bios(int unit) 15043561Skato{ 15143561Skato if ((unit >= 0) && (unit < nbdinfo)) 15243561Skato return(bdinfo[unit].bd_unit); 15343561Skato return(-1); 15443561Skato} 15543561Skato 15643561Skato/* 15743561Skato * Quiz the BIOS for disk devices, save a little info about them. 15843561Skato * 15943561Skato * XXX should we be consulting the BIOS equipment list, specifically 16043561Skato * the value at 0x475? 16143561Skato */ 16243561Skatostatic int 16343561Skatobd_init(void) 16443561Skato{ 16543561Skato int base, unit; 16643561Skato 16743561Skato#ifdef PC98 16849426Skato int da_drive=0, n=-0x10; 16943561Skato /* sequence 0x90, 0x80, 0xa0 */ 17043561Skato for (base = 0x90; base <= 0xa0; base += n, n += 0x30) { 17143561Skato for (unit = base; (nbdinfo < MAXBDDEV) || ((unit & 0x0f) < 4); unit++) { 17243561Skato bdinfo[nbdinfo].bd_unit = unit; 17343561Skato bdinfo[nbdinfo].bd_flags = (unit & 0xf0) == 0x90 ? BD_FLOPPY : 0; 17443561Skato 17543561Skato /* XXX add EDD probes */ 17644463Skato if (!bd_int13probe(&bdinfo[nbdinfo])){ 17749425Skato if (((unit & 0xf0) == 0x90 && (unit & 0x0f) < 4) || 17849425Skato ((unit & 0xf0) == 0xa0 && (unit & 0x0f) < 6)) 17944463Skato continue; /* Target IDs are not contiguous. */ 18044463Skato else 18144463Skato break; 18244463Skato } 18343561Skato 18443561Skato if (bdinfo[nbdinfo].bd_flags & BD_FLOPPY){ 18544467Skato /* available 1.44MB access? */ 18644467Skato if (*(u_char *)PTOV(0xA15AE) & (1<<(unit & 0xf))){ 18744467Skato /* boot media 1.2MB FD? */ 18844467Skato if ((*(u_char *)PTOV(0xA1584) & 0xf0) != 0x90) 18944467Skato bdinfo[nbdinfo].bd_unit = 0x30 + (unit & 0xf); 19044467Skato } 19143561Skato } 19249426Skato else { 19349426Skato if ((unit & 0xa0) == 0xa0) 19449426Skato bdinfo[nbdinfo].bd_da_unit = da_drive++; 19549426Skato } 19643561Skato /* XXX we need "disk aliases" to make this simpler */ 19743561Skato printf("BIOS drive %c: is disk%d\n", 19849426Skato 'A' + nbdinfo, nbdinfo); 19943561Skato nbdinfo++; 20043561Skato } 20143561Skato } 20243561Skato#else 20343561Skato /* sequence 0, 0x80 */ 20443561Skato for (base = 0; base <= 0x80; base += 0x80) { 20543561Skato for (unit = base; (nbdinfo < MAXBDDEV); unit++) { 20643561Skato bdinfo[nbdinfo].bd_unit = unit; 20743561Skato bdinfo[nbdinfo].bd_flags = (unit < 0x80) ? BD_FLOPPY : 0; 20843561Skato 20943561Skato /* XXX add EDD probes */ 21043561Skato if (!bd_int13probe(&bdinfo[nbdinfo])) 21143561Skato break; 21243561Skato 21343561Skato /* XXX we need "disk aliases" to make this simpler */ 21443561Skato printf("BIOS drive %c: is disk%d\n", 21543561Skato (unit < 0x80) ? ('A' + unit) : ('C' + unit - 0x80), nbdinfo); 21643561Skato nbdinfo++; 21743561Skato } 21843561Skato } 21943561Skato#endif 22043561Skato return(0); 22143561Skato} 22243561Skato 22343561Skato/* 22443561Skato * Try to detect a device supported by the legacy int13 BIOS 22543561Skato */ 22643561Skato 22743561Skatostatic int 22843561Skatobd_int13probe(struct bdinfo *bd) 22943561Skato{ 23043561Skato#ifdef PC98 23143561Skato int addr; 23243561Skato if (bd->bd_flags & BD_FLOPPY){ 23343561Skato addr = 0xa155c; 23443561Skato } 23543561Skato else { 23643561Skato if ((bd->bd_unit & 0xf0) == 0x80) 23743561Skato addr = 0xa155d; 23843561Skato else 23943561Skato addr = 0xa1482; 24043561Skato } 24143561Skato if ( *(u_char *)PTOV(addr) & (1<<(bd->bd_unit & 0x0f))) { 24243561Skato bd->bd_flags |= BD_MODEINT13; 24343561Skato return(1); 24443561Skato } 24543561Skato return(0); 24643561Skato#else 24743561Skato v86.ctl = V86_FLAGS; 24843561Skato v86.addr = 0x13; 24943561Skato v86.eax = 0x800; 25043561Skato v86.edx = bd->bd_unit; 25143561Skato v86int(); 25243561Skato 25343561Skato if (!(v86.efl & 0x1) && /* carry clear */ 25443561Skato ((v86.edx & 0xff) > (bd->bd_unit & 0x7f))) { /* unit # OK */ 25543561Skato bd->bd_flags |= BD_MODEINT13; 25643561Skato bd->bd_type = v86.ebx & 0xff; 25743561Skato return(1); 25843561Skato } 25943561Skato#endif 26043561Skato return(0); 26143561Skato} 26243561Skato 26343561Skato/* 26443561Skato * Print information about disks 26543561Skato */ 26643561Skatostatic void 26743561Skatobd_print(int verbose) 26843561Skato{ 26943561Skato int i, j; 27043561Skato char line[80]; 27143561Skato struct i386_devdesc dev; 27243561Skato struct open_disk *od; 27343561Skato struct dos_partition *dptr; 27443561Skato 27543561Skato for (i = 0; i < nbdinfo; i++) { 27643561Skato#ifdef PC98 27749426Skato sprintf(line, " disk%d: BIOS drive %c:\n", i, 'A' + i); 27843561Skato#else 27943561Skato sprintf(line, " disk%d: BIOS drive %c:\n", i, 28043561Skato (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit) : ('C' + bdinfo[i].bd_unit - 0x80)); 28143561Skato#endif 28243561Skato pager_output(line); 28343561Skato 28443561Skato /* try to open the whole disk */ 28543561Skato dev.d_kind.biosdisk.unit = i; 28643561Skato dev.d_kind.biosdisk.slice = -1; 28743561Skato dev.d_kind.biosdisk.partition = -1; 28843561Skato 28943561Skato if (!bd_opendisk(&od, &dev)) { 29043561Skato 29143561Skato /* Do we have a partition table? */ 29243561Skato if (od->od_flags & BD_PARTTABOK) { 29343561Skato dptr = &od->od_parttab[0]; 29443561Skato 29543561Skato /* Check for a "truly dedicated" disk */ 29643561Skato#ifdef PC98 29743561Skato for (j = 0; j < NDOSPART; j++) { 29843561Skato switch(dptr[j].dp_mid) { 29943561Skato case DOSMID_386BSD: 30043561Skato sprintf(line, " disk%ds%d", i, j + 1); 30143561Skato bd_printslice(od, dptr[j].dp_scyl * od->od_hds * od->od_sec + dptr[j].dp_shd * od->od_sec + dptr[j].dp_ssect, line); 30243561Skato break; 30343561Skato default: 30443561Skato } 30543561Skato } 30643561Skato#else 30743561Skato if ((dptr[3].dp_typ == DOSPTYP_386BSD) && 30843561Skato (dptr[3].dp_start == 0) && 30943561Skato (dptr[3].dp_size == 50000)) { 31043561Skato sprintf(line, " disk%d", i); 31143561Skato bd_printslice(od, 0, line); 31243561Skato } else { 31343561Skato for (j = 0; j < NDOSPART; j++) { 31443561Skato switch(dptr[j].dp_typ) { 31543561Skato case DOSPTYP_386BSD: 31643561Skato sprintf(line, " disk%ds%d", i, j + 1); 31743561Skato bd_printslice(od, dptr[j].dp_start, line); 31843561Skato break; 31943561Skato default: 32043561Skato } 32143561Skato } 32243561Skato 32343561Skato } 32443561Skato#endif 32543561Skato } 32643561Skato bd_closedisk(od); 32743561Skato } 32843561Skato } 32943561Skato} 33043561Skato 33143561Skatostatic void 33243561Skatobd_printslice(struct open_disk *od, int offset, char *prefix) 33343561Skato{ 33443561Skato char line[80]; 33543561Skato u_char buf[BIOSDISK_SECSIZE]; 33643561Skato struct disklabel *lp; 33743561Skato int i; 33843561Skato 33943561Skato /* read disklabel */ 34043561Skato if (bd_read(od, offset + LABELSECTOR, 1, buf)) 34143561Skato return; 34243561Skato lp =(struct disklabel *)(&buf[0]); 34343561Skato if (lp->d_magic != DISKMAGIC) { 34443561Skato sprintf(line, "bad disklabel\n"); 34543561Skato pager_output(line); 34643561Skato return; 34743561Skato } 34843561Skato 34943561Skato /* Print partitions */ 35043561Skato for (i = 0; i < lp->d_npartitions; i++) { 35143561Skato if ((lp->d_partitions[i].p_fstype == FS_BSDFFS) || (lp->d_partitions[i].p_fstype == FS_SWAP) || 35243561Skato ((lp->d_partitions[i].p_fstype == FS_UNUSED) && 35343561Skato (od->od_flags & BD_FLOPPY) && (i == 0))) { /* Floppies often have bogus fstype, print 'a' */ 35443561Skato sprintf(line, " %s%c: %s %.6dMB (%d - %d)\n", prefix, 'a' + i, 35543561Skato (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : "FFS", 35643561Skato lp->d_partitions[i].p_size / 2048, /* 512-byte sector assumption */ 35743561Skato lp->d_partitions[i].p_offset, lp->d_partitions[i].p_offset + lp->d_partitions[i].p_size); 35843561Skato pager_output(line); 35943561Skato } 36043561Skato } 36143561Skato} 36243561Skato 36343561Skato 36443561Skato/* 36543561Skato * Attempt to open the disk described by (dev) for use by (f). 36643561Skato * 36743561Skato * Note that the philosophy here is "give them exactly what 36843561Skato * they ask for". This is necessary because being too "smart" 36943561Skato * about what the user might want leads to complications. 37043561Skato * (eg. given no slice or partition value, with a disk that is 37143561Skato * sliced - are they after the first BSD slice, or the DOS 37243561Skato * slice before it?) 37343561Skato */ 37443561Skatostatic int 37543561Skatobd_open(struct open_file *f, ...) 37643561Skato{ 37743561Skato va_list ap; 37843561Skato struct i386_devdesc *dev; 37943561Skato struct open_disk *od; 38043561Skato int error; 38143561Skato 38243561Skato va_start(ap, f); 38343561Skato dev = va_arg(ap, struct i386_devdesc *); 38443561Skato va_end(ap); 38543561Skato if ((error = bd_opendisk(&od, dev))) 38643561Skato return(error); 38743561Skato 38843561Skato /* 38943561Skato * Save our context 39043561Skato */ 39143561Skato ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od; 39243561Skato DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff); 39343561Skato return(0); 39443561Skato} 39543561Skato 39643561Skatostatic int 39743561Skatobd_opendisk(struct open_disk **odp, struct i386_devdesc *dev) 39843561Skato{ 39943561Skato struct dos_partition *dptr; 40043561Skato struct disklabel *lp; 40143561Skato struct open_disk *od; 40243561Skato int sector, slice, i; 40343561Skato int error; 40443561Skato u_char buf[BUFSIZE]; 40543561Skato daddr_t pref_slice[4]; 40643561Skato 40743561Skato if (dev->d_kind.biosdisk.unit >= nbdinfo) { 40843561Skato DEBUG("attempt to open nonexistent disk"); 40943561Skato return(ENXIO); 41043561Skato } 41143561Skato 41243561Skato od = (struct open_disk *)malloc(sizeof(struct open_disk)); 41343561Skato if (!od) { 41443561Skato DEBUG("no memory"); 41543561Skato return (ENOMEM); 41643561Skato } 41743561Skato 41843561Skato /* Look up BIOS unit number, intialise open_disk structure */ 41943561Skato od->od_dkunit = dev->d_kind.biosdisk.unit; 42043561Skato od->od_unit = bdinfo[od->od_dkunit].bd_unit; 42143561Skato od->od_flags = bdinfo[od->od_dkunit].bd_flags; 42243561Skato od->od_boff = 0; 42343561Skato error = 0; 42443561Skato DEBUG("open '%s', unit 0x%x slice %d partition %c", 42543561Skato i386_fmtdev(dev), dev->d_kind.biosdisk.unit, 42643561Skato dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition + 'a'); 42743561Skato 42843561Skato /* Get geometry for this open (removable device may have changed) */ 42943561Skato if (bd_getgeom(od)) { 43043561Skato DEBUG("can't get geometry"); 43143561Skato error = ENXIO; 43243561Skato goto out; 43343561Skato } 43443561Skato 43543561Skato /* 43643561Skato * Following calculations attempt to determine the correct value 43743561Skato * for d->od_boff by looking for the slice and partition specified, 43843561Skato * or searching for reasonable defaults. 43943561Skato */ 44043561Skato 44143561Skato /* 44243561Skato * Find the slice in the DOS slice table. 44343561Skato */ 44443561Skato#ifdef PC98 44543561Skato if (od->od_flags & BD_FLOPPY) { 44643561Skato sector = 0; 44743561Skato goto unsliced; 44843561Skato } 44943561Skato#endif 45043561Skato if (bd_read(od, 0, 1, buf)) { 45143561Skato DEBUG("error reading MBR"); 45243561Skato error = EIO; 45343561Skato goto out; 45443561Skato } 45543561Skato 45643561Skato /* 45743561Skato * Check the slice table magic. 45843561Skato */ 45943561Skato if ((buf[0x1fe] != 0x55) || (buf[0x1ff] != 0xaa)) { 46043561Skato /* If a slice number was explicitly supplied, this is an error */ 46143561Skato if (dev->d_kind.biosdisk.slice > 0) { 46243561Skato DEBUG("no slice table/MBR (no magic)"); 46343561Skato error = ENOENT; 46443561Skato goto out; 46543561Skato } 46643561Skato sector = 0; 46743561Skato goto unsliced; /* may be a floppy */ 46843561Skato } 46943561Skato#ifdef PC98 47043561Skato if (bd_read(od, 1, 1, buf)) { 47143561Skato DEBUG("error reading MBR"); 47243561Skato error = EIO; 47343561Skato goto out; 47443561Skato } 47543561Skato#endif 47643561Skato bcopy(buf + DOSPARTOFF, &od->od_parttab, sizeof(struct dos_partition) * NDOSPART); 47743561Skato dptr = &od->od_parttab[0]; 47843561Skato od->od_flags |= BD_PARTTABOK; 47943561Skato 48043561Skato /* Is this a request for the whole disk? */ 48143561Skato if (dev->d_kind.biosdisk.slice == -1) { 48243561Skato sector = 0; 48343561Skato goto unsliced; 48443561Skato } 48543561Skato 48643561Skato /* Try to auto-detect the best slice; this should always give a slice number */ 48743561Skato if (dev->d_kind.biosdisk.slice == 0) 48843561Skato dev->d_kind.biosdisk.slice = bd_bestslice(dptr); 48943561Skato 49043561Skato switch (dev->d_kind.biosdisk.slice) { 49143561Skato case -1: 49243561Skato error = ENOENT; 49343561Skato goto out; 49443561Skato case 0: 49543561Skato sector = 0; 49643561Skato goto unsliced; 49743561Skato default: 49843561Skato break; 49943561Skato } 50043561Skato 50143561Skato /* 50243561Skato * Accept the supplied slice number unequivocally (we may be looking 50343561Skato * at a DOS partition). 50443561Skato */ 50543561Skato dptr += (dev->d_kind.biosdisk.slice - 1); /* we number 1-4, offsets are 0-3 */ 50643561Skato#ifdef PC98 50743561Skato sector = dptr->dp_scyl * od->od_hds * od->od_sec + dptr->dp_shd * od->od_sec + dptr->dp_ssect; 50843561Skato { 50943561Skato int end = dptr->dp_ecyl * od->od_hds * od->od_sec + dptr->dp_ehd * od->od_sec + dptr->dp_esect; 51043561Skato DEBUG("slice entry %d at %d, %d sectors", dev->d_kind.biosdisk.slice - 1, sector, end-sector); 51143561Skato } 51243561Skato#else 51343561Skato sector = dptr->dp_start; 51443561Skato DEBUG("slice entry %d at %d, %d sectors", dev->d_kind.biosdisk.slice - 1, sector, dptr->dp_size); 51543561Skato#endif 51643561Skato 51743561Skato /* 51843561Skato * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition 51943561Skato */ 52043561Skato#ifdef PC98 52143561Skato if ((dptr->dp_mid == DOSMID_386BSD) && (dev->d_kind.biosdisk.partition < 0)) 52243561Skato#else 52343561Skato if ((dptr->dp_typ == DOSPTYP_386BSD) && (dev->d_kind.biosdisk.partition < 0)) 52443561Skato#endif 52543561Skato dev->d_kind.biosdisk.partition = 0; 52643561Skato 52743561Skato unsliced: 52843561Skato /* 52943561Skato * Now we have the slice offset, look for the partition in the disklabel if we have 53043561Skato * a partition to start with. 53143561Skato * 53243561Skato * XXX we might want to check the label checksum. 53343561Skato */ 53443561Skato if (dev->d_kind.biosdisk.partition < 0) { 53543561Skato od->od_boff = sector; /* no partition, must be after the slice */ 53643561Skato DEBUG("opening raw slice"); 53743561Skato } else { 53843561Skato if (bd_read(od, sector + LABELSECTOR, 1, buf)) { 53943561Skato DEBUG("error reading disklabel"); 54043561Skato error = EIO; 54143561Skato goto out; 54243561Skato } 54343561Skato DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel); 54443561Skato bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel)); 54543561Skato lp = &od->od_disklabel; 54643561Skato od->od_flags |= BD_LABELOK; 54743561Skato 54843561Skato if (lp->d_magic != DISKMAGIC) { 54943561Skato DEBUG("no disklabel"); 55043561Skato error = ENOENT; 55143561Skato goto out; 55243561Skato } 55343561Skato if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) { 55443561Skato DEBUG("partition '%c' exceeds partitions in table (a-'%c')", 55543561Skato 'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions); 55643561Skato error = EPART; 55743561Skato goto out; 55843561Skato 55943561Skato } 56043561Skato 56143561Skato /* Complain if the partition type is wrong */ 56243561Skato if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) && 56343561Skato !(od->od_flags & BD_FLOPPY)) /* Floppies often have bogus fstype */ 56443561Skato DEBUG("warning, partition marked as unused"); 56543561Skato 56643561Skato od->od_boff = lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset; 56743561Skato } 56843561Skato 56943561Skato out: 57043561Skato if (error) { 57143561Skato free(od); 57243561Skato } else { 57343561Skato *odp = od; /* return the open disk */ 57443561Skato } 57543561Skato return(error); 57643561Skato} 57743561Skato 57843561Skato 57943561Skato/* 58043561Skato * Search for a slice with the following preferences: 58143561Skato * 58243561Skato * 1: Active FreeBSD slice 58343561Skato * 2: Non-active FreeBSD slice 58443561Skato * 3: Active FAT/FAT32 slice 58543561Skato * 4: non-active FAT/FAT32 slice 58643561Skato */ 58743561Skato#define PREF_FBSD_ACT 0 58843561Skato#define PREF_FBSD 1 58943561Skato#define PREF_DOS_ACT 2 59043561Skato#define PREF_DOS 3 59143561Skato#define PREF_NONE 4 59243561Skato 59343561Skatostatic int 59443561Skatobd_bestslice(struct dos_partition *dptr) 59543561Skato{ 59643561Skato int i; 59743561Skato int preflevel, pref; 59843561Skato 59943561Skato 60043561Skato#ifndef PC98 60143561Skato /* 60243561Skato * Check for the historically bogus MBR found on true dedicated disks 60343561Skato */ 60443561Skato if ((dptr[3].dp_typ == DOSPTYP_386BSD) && 60543561Skato (dptr[3].dp_start == 0) && 60643561Skato (dptr[3].dp_size == 50000)) 60743561Skato return(0); 60843561Skato#endif 60943561Skato 61043561Skato preflevel = PREF_NONE; 61143561Skato pref = -1; 61243561Skato 61343561Skato /* 61443561Skato * XXX No support here for 'extended' slices 61543561Skato */ 61643561Skato for (i = 0; i < NDOSPART; i++) { 61743561Skato#ifdef PC98 61843561Skato switch(dptr[i].dp_mid & 0x7f) { 61943561Skato case DOSMID_386BSD & 0x7f: /* FreeBSD */ 62043561Skato if ((dptr[i].dp_mid & 0x80) && (preflevel > PREF_FBSD_ACT)) { 62143561Skato pref = i; 62243561Skato preflevel = PREF_FBSD_ACT; 62343561Skato } else if (preflevel > PREF_FBSD) { 62443561Skato pref = i; 62543561Skato preflevel = PREF_FBSD; 62643561Skato } 62743561Skato break; 62843561Skato 62943561Skato case 0x11: /* DOS/Windows */ 63043561Skato case 0x20: 63143561Skato case 0x21: 63243561Skato case 0x22: 63343561Skato case 0x23: 63443561Skato case 0x63: 63543561Skato if ((dptr[i].dp_mid & 0x80) && (preflevel > PREF_DOS_ACT)) { 63643561Skato pref = i; 63743561Skato preflevel = PREF_DOS_ACT; 63843561Skato } else if (preflevel > PREF_DOS) { 63943561Skato pref = i; 64043561Skato preflevel = PREF_DOS; 64143561Skato } 64243561Skato break; 64343561Skato } 64443561Skato#else 64543561Skato switch(dptr[i].dp_typ) { 64643561Skato case DOSPTYP_386BSD: /* FreeBSD */ 64743561Skato if ((dptr[i].dp_flag & 0x80) && (preflevel > PREF_FBSD_ACT)) { 64843561Skato pref = i; 64943561Skato preflevel = PREF_FBSD_ACT; 65043561Skato } else if (preflevel > PREF_FBSD) { 65143561Skato pref = i; 65243561Skato preflevel = PREF_FBSD; 65343561Skato } 65443561Skato break; 65543561Skato 65643561Skato case 0x04: /* DOS/Windows */ 65743561Skato case 0x06: 65843561Skato case 0x0b: 65943561Skato case 0x0c: 66043561Skato case 0x0e: 66143561Skato case 0x63: 66243561Skato if ((dptr[i].dp_flag & 0x80) && (preflevel > PREF_DOS_ACT)) { 66343561Skato pref = i; 66443561Skato preflevel = PREF_DOS_ACT; 66543561Skato } else if (preflevel > PREF_DOS) { 66643561Skato pref = i; 66743561Skato preflevel = PREF_DOS; 66843561Skato } 66943561Skato break; 67043561Skato } 67143561Skato#endif 67243561Skato } 67343561Skato return(pref + 1); /* slices numbered 1-4 */ 67443561Skato} 67543561Skato 67643561Skato 67743561Skatostatic int 67843561Skatobd_close(struct open_file *f) 67943561Skato{ 68043561Skato struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data); 68143561Skato 68243561Skato bd_closedisk(od); 68343561Skato return(0); 68443561Skato} 68543561Skato 68643561Skatostatic void 68743561Skatobd_closedisk(struct open_disk *od) 68843561Skato{ 68943561Skato DEBUG("open_disk %p", od); 69043561Skato#if 0 69143561Skato /* XXX is this required? (especially if disk already open...) */ 69243561Skato if (od->od_flags & BD_FLOPPY) 69343561Skato delay(3000000); 69443561Skato#endif 69543561Skato free(od); 69643561Skato} 69743561Skato 69843561Skatostatic int 69943561Skatobd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, void *buf, size_t *rsize) 70043561Skato{ 70143561Skato struct bcache_devdata bcd; 70243561Skato 70343561Skato bcd.dv_strategy = bd_realstrategy; 70443561Skato bcd.dv_devdata = devdata; 70543561Skato return(bcache_strategy(&bcd, rw, dblk, size, buf, rsize)); 70643561Skato} 70743561Skato 70843561Skatostatic int 70943561Skatobd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, void *buf, size_t *rsize) 71043561Skato{ 71143561Skato struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data); 71243561Skato int blks; 71343561Skato#ifdef BD_SUPPORT_FRAGS 71443561Skato char fragbuf[BIOSDISK_SECSIZE]; 71543561Skato size_t fragsize; 71643561Skato 71743561Skato fragsize = size % BIOSDISK_SECSIZE; 71843561Skato#else 71943561Skato if (size % BIOSDISK_SECSIZE) 72043561Skato panic("bd_strategy: %d bytes I/O not multiple of block size", size); 72143561Skato#endif 72243561Skato 72343561Skato DEBUG("open_disk %p", od); 72443561Skato 72543561Skato if (rw != F_READ) 72643561Skato return(EROFS); 72743561Skato 72843561Skato 72943561Skato blks = size / BIOSDISK_SECSIZE; 73043561Skato DEBUG("read %d from %d+%d to %p", blks, od->od_boff, dblk, buf); 73143561Skato 73243561Skato if (rsize) 73343561Skato *rsize = 0; 73443561Skato if (blks && bd_read(od, dblk + od->od_boff, blks, buf)) { 73543561Skato DEBUG("read error"); 73643561Skato return (EIO); 73743561Skato } 73843561Skato#ifdef BD_SUPPORT_FRAGS 73943561Skato DEBUG("bd_strategy: frag read %d from %d+%d+d to %p", 74043561Skato fragsize, od->od_boff, dblk, blks, buf + (blks * BIOSDISK_SECSIZE)); 74143561Skato if (fragsize && bd_read(od, dblk + od->od_boff + blks, 1, fragsize)) { 74243561Skato DEBUG("frag read error"); 74343561Skato return(EIO); 74443561Skato } 74543561Skato bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize); 74643561Skato#endif 74743561Skato if (rsize) 74843561Skato *rsize = size; 74943561Skato return (0); 75043561Skato} 75143561Skato 75243561Skato/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */ 75343561Skato#define FLOPPY_BOUNCEBUF 18 75443561Skato 75543561Skatostatic int 75643561Skatobd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) 75743561Skato{ 75843561Skato int x, bpc, cyl, hd, sec, result, resid, cnt, retry, maxfer; 75943561Skato caddr_t p, xp, bbuf, breg; 76043561Skato 76143561Skato bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ 76243561Skato resid = blks; 76343561Skato p = dest; 76443561Skato 76543561Skato /* Decide whether we have to bounce */ 76643561Skato#ifdef PC98 76751586Skato if ( 76843561Skato#else 76943561Skato if ((od->od_unit < 0x80) && 77043561Skato#endif 77143561Skato ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) { 77243561Skato 77343561Skato /* 77443561Skato * There is a 64k physical boundary somewhere in the destination buffer, so we have 77543561Skato * to arrange a suitable bounce buffer. Allocate a buffer twice as large as we 77643561Skato * need to. Use the bottom half unless there is a break there, in which case we 77743561Skato * use the top half. 77843561Skato */ 77951586Skato#ifdef PC98 78051586Skato x = min(od->od_sec, blks); 78151586Skato#else 78243561Skato x = min(FLOPPY_BOUNCEBUF, blks); 78351586Skato#endif 78443561Skato bbuf = malloc(x * 2 * BIOSDISK_SECSIZE); 78543561Skato if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(dest + x * BIOSDISK_SECSIZE) & 0xffff0000)) { 78643561Skato breg = bbuf; 78743561Skato } else { 78843561Skato breg = bbuf + x * BIOSDISK_SECSIZE; 78943561Skato } 79043561Skato maxfer = x; /* limit transfers to bounce region size */ 79143561Skato } else { 79243561Skato bbuf = NULL; 79343561Skato maxfer = 0; 79443561Skato } 79543561Skato 79643561Skato while (resid > 0) { 79743561Skato x = dblk; 79843561Skato cyl = x / bpc; /* block # / blocks per cylinder */ 79943561Skato x %= bpc; /* block offset into cylinder */ 80043561Skato hd = x / od->od_sec; /* offset / blocks per track */ 80143561Skato sec = x % od->od_sec; /* offset into track */ 80243561Skato 80343561Skato /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */ 80443561Skato x = min(od->od_sec - sec, resid); 80543561Skato if (maxfer > 0) 80643561Skato x = min(x, maxfer); /* fit bounce buffer */ 80743561Skato 80843561Skato /* where do we transfer to? */ 80943561Skato xp = bbuf == NULL ? p : breg; 81043561Skato 81143561Skato /* correct sector number for 1-based BIOS numbering */ 81243561Skato#ifdef PC98 81343561Skato if ((od->od_unit & 0xf0) == 0x30 || (od->od_unit & 0xf0) == 0x90) 81443561Skato sec++; 81543561Skato#else 81643561Skato sec++; 81743561Skato#endif 81843561Skato 81943561Skato /* Loop retrying the operation a couple of times. The BIOS may also retry. */ 82043561Skato for (retry = 0; retry < 3; retry++) { 82143561Skato /* if retrying, reset the drive */ 82243561Skato if (retry > 0) { 82343561Skato#ifdef PC98 82451586Skato v86.ctl = V86_FLAGS; 82551586Skato v86.addr = 0x1b; 82651586Skato v86.eax = 0x0300 | od->od_unit; 82743561Skato#else 82843561Skato v86.ctl = V86_FLAGS; 82943561Skato v86.addr = 0x13; 83043561Skato v86.eax = 0; 83143561Skato v86.edx = od->od_unit; 83251586Skato#endif 83343561Skato v86int(); 83443561Skato } 83543561Skato 83643561Skato /* build request XXX support EDD requests too */ 83751586Skato v86.ctl = V86_FLAGS; 83843561Skato#ifdef PC98 83943561Skato v86.addr = 0x1b; 84043561Skato if (od->od_flags & BD_FLOPPY) { 84143561Skato v86.eax = 0xd600 | od->od_unit; 84243561Skato v86.ecx = 0x0200 | (cyl & 0xff); 84343561Skato } 84443561Skato else { 84543561Skato v86.eax = 0x0600 | od->od_unit; 84643561Skato v86.ecx = cyl; 84743561Skato } 84843561Skato v86.edx = (hd << 8) | sec; 84943561Skato v86.ebx = x * BIOSDISK_SECSIZE; 85043561Skato v86.es = VTOPSEG(xp); 85143561Skato v86.ebp = VTOPOFF(xp); 85243561Skato#else 85343561Skato v86.addr = 0x13; 85443561Skato v86.eax = 0x200 | x; 85543561Skato v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; 85643561Skato v86.edx = (hd << 8) | od->od_unit; 85743561Skato v86.es = VTOPSEG(xp); 85843561Skato v86.ebx = VTOPOFF(xp); 85951586Skato#endif 86043561Skato v86int(); 86143561Skato result = (v86.efl & 0x1); 86243561Skato if (result == 0) 86343561Skato break; 86443561Skato } 86543561Skato 86643561Skato#ifdef PC98 86743561Skato DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, od->od_flags & BD_FLOPPY ? sec - 1 : sec, p, VTOP(p), result ? "failed" : "ok"); 86843561Skato /* BUG here, cannot use v86 in printf because putchar uses it too */ 86943561Skato DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 87043561Skato od->od_flags & BD_FLOPPY ? 0xd600 | od->od_unit : 0x0600 | od->od_unit, 87143561Skato od->od_flags & BD_FLOPPY ? 0x0200 | cyl : cyl, (hd << 8) | sec, 87243561Skato (v86.eax >> 8) & 0xff); 87343561Skato#else 87443561Skato DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok"); 87543561Skato /* BUG here, cannot use v86 in printf because putchar uses it too */ 87643561Skato DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 87743561Skato 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff); 87843561Skato#endif 87943561Skato if (result) { 88043561Skato if (bbuf != NULL) 88143561Skato free(bbuf); 88243561Skato return(-1); 88343561Skato } 88443561Skato if (bbuf != NULL) 88543561Skato bcopy(breg, p, x * BIOSDISK_SECSIZE); 88643561Skato p += (x * BIOSDISK_SECSIZE); 88743561Skato dblk += x; 88843561Skato resid -= x; 88943561Skato } 89043561Skato 89143561Skato/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */ 89243561Skato if (bbuf != NULL) 89343561Skato free(bbuf); 89443561Skato return(0); 89543561Skato} 89643561Skato 89743561Skatostatic int 89843561Skatobd_getgeom(struct open_disk *od) 89943561Skato{ 90043561Skato 90143561Skato#ifdef PC98 90243561Skato if (od->od_flags & BD_FLOPPY) { 90343561Skato od->od_cyl = 79; 90443561Skato od->od_hds = 2; 90543561Skato od->od_sec = (od->od_unit & 0xf0) == 0x30 ? 18 : 15; 90643561Skato } 90743561Skato else { 90851586Skato v86.ctl = V86_FLAGS; 90943561Skato v86.addr = 0x1b; 91043561Skato v86.eax = 0x8400 | od->od_unit; 91143561Skato v86int(); 91243561Skato 91343561Skato od->od_cyl = v86.ecx; 91443561Skato od->od_hds = (v86.edx >> 8) & 0xff; 91543561Skato od->od_sec = v86.edx & 0xff; 91651586Skato if (v86.efl & 0x1) 91751586Skato return(1); 91843561Skato } 91943561Skato#else 92043561Skato v86.ctl = V86_FLAGS; 92143561Skato v86.addr = 0x13; 92243561Skato v86.eax = 0x800; 92343561Skato v86.edx = od->od_unit; 92443561Skato v86int(); 92543561Skato 92643561Skato if ((v86.efl & 0x1) || /* carry set */ 92743561Skato ((v86.edx & 0xff) <= (od->od_unit & 0x7f))) /* unit # bad */ 92843561Skato return(1); 92943561Skato 93043561Skato /* convert max cyl # -> # of cylinders */ 93143561Skato od->od_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1; 93243561Skato /* convert max head # -> # of heads */ 93343561Skato od->od_hds = ((v86.edx & 0xff00) >> 8) + 1; 93443561Skato od->od_sec = v86.ecx & 0x3f; 93543561Skato#endif 93643561Skato 93743561Skato DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec); 93843561Skato return(0); 93943561Skato} 94043561Skato 94143561Skato/* 94243561Skato * Return a suitable dev_t value for (dev). 94343561Skato * 94443561Skato * In the case where it looks like (dev) is a SCSI disk, we allow the number of 94543561Skato * IDE disks to be specified in $num_ide_disks. There should be a Better Way. 94643561Skato */ 94743561Skatoint 94843561Skatobd_getdev(struct i386_devdesc *dev) 94943561Skato{ 95043561Skato struct open_disk *od; 95143561Skato int biosdev; 95243561Skato int major; 95343561Skato int rootdev; 95443561Skato char *nip, *cp; 95543561Skato int unitofs = 0, i, unit; 95643561Skato 95743561Skato biosdev = bd_unit2bios(dev->d_kind.biosdisk.unit); 95843561Skato DEBUG("unit %d BIOS device %d", dev->d_kind.biosdisk.unit, biosdev); 95943561Skato if (biosdev == -1) /* not a BIOS device */ 96043561Skato return(-1); 96143561Skato if (bd_opendisk(&od, dev) != 0) /* oops, not a viable device */ 96243561Skato return(-1); 96343561Skato 96443561Skato#ifdef PC98 96543561Skato if ((biosdev & 0xf0) == 0x90 || (biosdev & 0xf0) == 0x30) { 96643561Skato#else 96743561Skato if (biosdev < 0x80) { 96843561Skato#endif 96943561Skato /* floppy (or emulated floppy) or ATAPI device */ 97043561Skato if (bdinfo[dev->d_kind.biosdisk.unit].bd_type == DT_ATAPI) { 97143561Skato /* is an ATAPI disk */ 97243561Skato major = WFDMAJOR; 97343561Skato } else { 97443561Skato /* is a floppy disk */ 97543561Skato major = FDMAJOR; 97643561Skato } 97743561Skato } else { 97843561Skato /* harddisk */ 97943561Skato if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) { 98043561Skato /* label OK, disk labelled as SCSI */ 98143561Skato major = DAMAJOR; 98243561Skato /* check for unit number correction hint, now deprecated */ 98343561Skato if ((nip = getenv("num_ide_disks")) != NULL) { 98443561Skato i = strtol(nip, &cp, 0); 98543561Skato /* check for parse error */ 98643561Skato if ((cp != nip) && (*cp == 0)) 98743561Skato unitofs = i; 98843561Skato } 98943561Skato } else { 99043561Skato /* assume an IDE disk */ 99143561Skato major = WDMAJOR; 99243561Skato } 99343561Skato } 99443561Skato /* XXX a better kludge to set the root disk unit number */ 99543561Skato if ((nip = getenv("root_disk_unit")) != NULL) { 99643561Skato i = strtol(nip, &cp, 0); 99743561Skato /* check for parse error */ 99843561Skato if ((cp != nip) && (*cp == 0)) 99943561Skato unit = i; 100043561Skato } else { 100143561Skato#ifdef PC98 100249426Skato if ((biosdev & 0xf0) == 0xa0) 100349426Skato unit = bdinfo[dev->d_kind.biosdisk.unit].bd_da_unit; 100449426Skato else 100549426Skato unit = biosdev & 0xf; 100643561Skato#else 100743561Skato unit = (biosdev & 0x7f) - unitofs; /* allow for #wd compenstation in da case */ 100843561Skato#endif 100943561Skato } 101043561Skato 101143561Skato rootdev = MAKEBOOTDEV(major, 101243561Skato (dev->d_kind.biosdisk.slice + 1) >> 4, /* XXX slices may be wrong here */ 101343561Skato (dev->d_kind.biosdisk.slice + 1) & 0xf, 101443561Skato unit, 101543561Skato dev->d_kind.biosdisk.partition); 101643561Skato DEBUG("dev is 0x%x\n", rootdev); 101743561Skato return(rootdev); 101843561Skato} 101943561Skato 102043561Skato/* 102143561Skato * Fix (dev) so that it refers to the 'real' disk/slice/partition that it implies. 102243561Skato */ 102343561Skatoint 102443561Skatobd_fixupdev(struct i386_devdesc *dev) 102543561Skato{ 102643561Skato struct open_disk *od; 102743561Skato 102843561Skato /* 102943561Skato * Open the disk. This will fix up the slice and partition fields. 103043561Skato */ 103143561Skato if (bd_opendisk(&od, dev) != 0) 103243561Skato return(ENOENT); 103343561Skato 103443561Skato bd_closedisk(od); 103543561Skato} 1036