biosdisk.c revision 68358
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 68358 2000-11-05 12:35:41Z nyan $ 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 */ 3843561Skato 3943561Skato#include <stand.h> 4043561Skato 4143561Skato#include <sys/disklabel.h> 4243561Skato#include <sys/diskslice.h> 4343561Skato#include <sys/reboot.h> 4443561Skato 4543561Skato#include <stdarg.h> 4643561Skato 4743561Skato#include <bootstrap.h> 4843561Skato#include <btxv86.h> 4943561Skato#include "libi386.h" 5043561Skato 5163101Snyan#define BIOS_NUMDRIVES 0x475 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; 7659777Snyan#define BD_MODEINT13 0x0000 7759777Snyan#define BD_MODEEDD1 0x0001 7859777Snyan#define BD_MODEEDD3 0x0002 7963101Snyan#define BD_MODEMASK 0x0003 8059777Snyan#define BD_FLOPPY 0x0004 8159777Snyan#define BD_LABELOK 0x0008 8259777Snyan#define BD_PARTTABOK 0x0010 8343561Skato struct disklabel od_disklabel; 8459777Snyan int od_nslices; /* slice count */ 8559777Snyan struct dos_partition od_slicetab[MAX_SLICES]; 8643561Skato}; 8743561Skato 8843561Skato/* 8943561Skato * List of BIOS devices, translation from disk unit number to 9043561Skato * BIOS unit number. 9143561Skato */ 9243561Skatostatic struct bdinfo 9343561Skato{ 9443561Skato int bd_unit; /* BIOS unit number */ 9543561Skato int bd_flags; 9643561Skato int bd_type; /* BIOS 'drive type' (floppy only) */ 9743561Skato#ifdef PC98 9853207Snyan int bd_da_unit; /* kernel unit number for da */ 9943561Skato#endif 10043561Skato} bdinfo [MAXBDDEV]; 10143561Skatostatic int nbdinfo = 0; 10243561Skato 10343561Skatostatic int bd_getgeom(struct open_disk *od); 10459777Snyanstatic int bd_read(struct open_disk *od, daddr_t dblk, int blks, 10559777Snyan caddr_t dest); 10643561Skato 10743561Skatostatic int bd_int13probe(struct bdinfo *bd); 10843561Skato 10959777Snyanstatic void bd_printslice(struct open_disk *od, struct dos_partition *dp, 11068358Snyan char *prefix, int verbose); 11168358Snyanstatic void bd_printbsdslice(struct open_disk *od, daddr_t offset, 11268358Snyan char *prefix, int verbose); 11343561Skato 11443561Skatostatic int bd_init(void); 11559777Snyanstatic int bd_strategy(void *devdata, int flag, daddr_t dblk, 11668358Snyan size_t size, char *buf, size_t *rsize); 11759777Snyanstatic int bd_realstrategy(void *devdata, int flag, daddr_t dblk, 11868358Snyan size_t size, char *buf, size_t *rsize); 11943561Skatostatic int bd_open(struct open_file *f, ...); 12043561Skatostatic int bd_close(struct open_file *f); 12143561Skatostatic void bd_print(int verbose); 12243561Skato 12343561Skatostruct devsw biosdisk = { 12443561Skato "disk", 12543561Skato DEVT_DISK, 12643561Skato bd_init, 12743561Skato bd_strategy, 12843561Skato bd_open, 12943561Skato bd_close, 13043561Skato noioctl, 13168358Snyan bd_print, 13268358Snyan NULL 13343561Skato}; 13443561Skato 13543561Skatostatic int bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev); 13643561Skatostatic void bd_closedisk(struct open_disk *od); 13759777Snyanstatic int bd_bestslice(struct open_disk *od); 13859777Snyanstatic void bd_checkextended(struct open_disk *od, int slicenum); 13943561Skato 14043561Skato/* 14143561Skato * Translate between BIOS device numbers and our private unit numbers. 14243561Skato */ 14343561Skatoint 14443561Skatobd_bios2unit(int biosdev) 14543561Skato{ 14643561Skato int i; 14743561Skato 14843561Skato DEBUG("looking for bios device 0x%x", biosdev); 14943561Skato for (i = 0; i < nbdinfo; i++) { 15043561Skato DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit); 15143561Skato if (bdinfo[i].bd_unit == biosdev) 15243561Skato return(i); 15343561Skato } 15443561Skato return(-1); 15543561Skato} 15643561Skato 15743561Skatoint 15843561Skatobd_unit2bios(int unit) 15943561Skato{ 16043561Skato if ((unit >= 0) && (unit < nbdinfo)) 16143561Skato return(bdinfo[unit].bd_unit); 16243561Skato return(-1); 16343561Skato} 16443561Skato 16543561Skato/* 16643561Skato * Quiz the BIOS for disk devices, save a little info about them. 16743561Skato */ 16843561Skatostatic int 16943561Skatobd_init(void) 17043561Skato{ 17163101Snyan#ifdef PC98 17243561Skato int base, unit; 17363101Snyan int da_drive=0, n=-0x10; 17443561Skato 17543561Skato /* sequence 0x90, 0x80, 0xa0 */ 17643561Skato for (base = 0x90; base <= 0xa0; base += n, n += 0x30) { 17743561Skato for (unit = base; (nbdinfo < MAXBDDEV) || ((unit & 0x0f) < 4); unit++) { 17843561Skato bdinfo[nbdinfo].bd_unit = unit; 17943561Skato bdinfo[nbdinfo].bd_flags = (unit & 0xf0) == 0x90 ? BD_FLOPPY : 0; 18043561Skato 18144463Skato if (!bd_int13probe(&bdinfo[nbdinfo])){ 18249425Skato if (((unit & 0xf0) == 0x90 && (unit & 0x0f) < 4) || 18349425Skato ((unit & 0xf0) == 0xa0 && (unit & 0x0f) < 6)) 18444463Skato continue; /* Target IDs are not contiguous. */ 18544463Skato else 18644463Skato break; 18744463Skato } 18843561Skato 18943561Skato if (bdinfo[nbdinfo].bd_flags & BD_FLOPPY){ 19044467Skato /* available 1.44MB access? */ 19159777Snyan if (*(u_char *)PTOV(0xA15AE) & (1<<(unit & 0xf))) { 19244467Skato /* boot media 1.2MB FD? */ 19344467Skato if ((*(u_char *)PTOV(0xA1584) & 0xf0) != 0x90) 19444467Skato bdinfo[nbdinfo].bd_unit = 0x30 + (unit & 0xf); 19544467Skato } 19643561Skato } 19749426Skato else { 19849426Skato if ((unit & 0xa0) == 0xa0) 19949426Skato bdinfo[nbdinfo].bd_da_unit = da_drive++; 20049426Skato } 20143561Skato /* XXX we need "disk aliases" to make this simpler */ 20243561Skato printf("BIOS drive %c: is disk%d\n", 20349426Skato 'A' + nbdinfo, nbdinfo); 20443561Skato nbdinfo++; 20543561Skato } 20643561Skato } 20743561Skato#else 20863101Snyan int base, unit, nfd = 0; 20963101Snyan 21043561Skato /* sequence 0, 0x80 */ 21143561Skato for (base = 0; base <= 0x80; base += 0x80) { 21243561Skato for (unit = base; (nbdinfo < MAXBDDEV); unit++) { 21363101Snyan /* check the BIOS equipment list for number of fixed disks */ 21463101Snyan if((base == 0x80) && 21568358Snyan (nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES))) 21663101Snyan break; 21763101Snyan 21843561Skato bdinfo[nbdinfo].bd_unit = unit; 21943561Skato bdinfo[nbdinfo].bd_flags = (unit < 0x80) ? BD_FLOPPY : 0; 22043561Skato 22143561Skato if (!bd_int13probe(&bdinfo[nbdinfo])) 22243561Skato break; 22343561Skato 22443561Skato /* XXX we need "disk aliases" to make this simpler */ 22543561Skato printf("BIOS drive %c: is disk%d\n", 22643561Skato (unit < 0x80) ? ('A' + unit) : ('C' + unit - 0x80), nbdinfo); 22743561Skato nbdinfo++; 22863101Snyan if (base == 0x80) 22963101Snyan nfd++; 23043561Skato } 23143561Skato } 23243561Skato#endif 23343561Skato return(0); 23443561Skato} 23543561Skato 23643561Skato/* 23743561Skato * Try to detect a device supported by the legacy int13 BIOS 23843561Skato */ 23943561Skatostatic int 24043561Skatobd_int13probe(struct bdinfo *bd) 24143561Skato{ 24243561Skato#ifdef PC98 24343561Skato int addr; 24463101Snyan 24559777Snyan if (bd->bd_flags & BD_FLOPPY) { 24643561Skato addr = 0xa155c; 24759777Snyan } else { 24843561Skato if ((bd->bd_unit & 0xf0) == 0x80) 24943561Skato addr = 0xa155d; 25043561Skato else 25143561Skato addr = 0xa1482; 25243561Skato } 25343561Skato if ( *(u_char *)PTOV(addr) & (1<<(bd->bd_unit & 0x0f))) { 25443561Skato bd->bd_flags |= BD_MODEINT13; 25543561Skato return(1); 25643561Skato } 25743561Skato return(0); 25843561Skato#else 25943561Skato v86.ctl = V86_FLAGS; 26043561Skato v86.addr = 0x13; 26143561Skato v86.eax = 0x800; 26243561Skato v86.edx = bd->bd_unit; 26343561Skato v86int(); 26443561Skato 26543561Skato if (!(v86.efl & 0x1) && /* carry clear */ 26668358Snyan ((v86.edx & 0xff) > ((unsigned)bd->bd_unit & 0x7f))) { /* unit # OK */ 26743561Skato bd->bd_flags |= BD_MODEINT13; 26843561Skato bd->bd_type = v86.ebx & 0xff; 26963101Snyan 27063101Snyan /* Determine if we can use EDD with this device. */ 27163101Snyan v86.eax = 0x4100; 27263101Snyan v86.edx = bd->bd_unit; 27363101Snyan v86.ebx = 0x55aa; 27463101Snyan v86int(); 27563101Snyan if (!(v86.efl & 0x1) && /* carry clear */ 27663101Snyan ((v86.ebx & 0xffff) == 0xaa55) && /* signature */ 27763101Snyan (v86.ecx & 0x1)) { /* packets mode ok */ 27863101Snyan bd->bd_flags |= BD_MODEEDD1; 27968358Snyan if((v86.eax & 0xff00) > 0x300) 28063101Snyan bd->bd_flags |= BD_MODEEDD3; 28163101Snyan } 28243561Skato return(1); 28343561Skato } 28463101Snyan return(0); 28543561Skato#endif 28643561Skato} 28743561Skato 28843561Skato/* 28943561Skato * Print information about disks 29043561Skato */ 29143561Skatostatic void 29243561Skatobd_print(int verbose) 29343561Skato{ 29443561Skato int i, j; 29543561Skato char line[80]; 29643561Skato struct i386_devdesc dev; 29743561Skato struct open_disk *od; 29843561Skato struct dos_partition *dptr; 29943561Skato 30043561Skato for (i = 0; i < nbdinfo; i++) { 30143561Skato#ifdef PC98 30249426Skato sprintf(line, " disk%d: BIOS drive %c:\n", i, 'A' + i); 30343561Skato#else 30443561Skato sprintf(line, " disk%d: BIOS drive %c:\n", i, 30543561Skato (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit) : ('C' + bdinfo[i].bd_unit - 0x80)); 30643561Skato#endif 30743561Skato pager_output(line); 30843561Skato 30943561Skato /* try to open the whole disk */ 31043561Skato dev.d_kind.biosdisk.unit = i; 31143561Skato dev.d_kind.biosdisk.slice = -1; 31243561Skato dev.d_kind.biosdisk.partition = -1; 31343561Skato 31443561Skato if (!bd_opendisk(&od, &dev)) { 31543561Skato 31643561Skato /* Do we have a partition table? */ 31743561Skato if (od->od_flags & BD_PARTTABOK) { 31859777Snyan dptr = &od->od_slicetab[0]; 31943561Skato 32068358Snyan /* Check for a "dedicated" disk */ 32143561Skato#ifdef PC98 32259777Snyan for (j = 0; j < od->od_nslices; j++) { 32343561Skato switch(dptr[j].dp_mid) { 32443561Skato case DOSMID_386BSD: 32543561Skato sprintf(line, " disk%ds%d", i, j + 1); 32659777Snyan bd_printbsdslice(od, 32759777Snyan dptr[j].dp_scyl * od->od_hds * od->od_sec + 32859777Snyan dptr[j].dp_shd * od->od_sec + dptr[j].dp_ssect, 32968358Snyan line, verbose); 33043561Skato break; 33143561Skato default: 33243561Skato } 33343561Skato } 33443561Skato#else 33543561Skato if ((dptr[3].dp_typ == DOSPTYP_386BSD) && 33643561Skato (dptr[3].dp_start == 0) && 33743561Skato (dptr[3].dp_size == 50000)) { 33843561Skato sprintf(line, " disk%d", i); 33968358Snyan bd_printbsdslice(od, 0, line, verbose); 34043561Skato } else { 34159777Snyan for (j = 0; j < od->od_nslices; j++) { 34259777Snyan sprintf(line, " disk%ds%d", i, j + 1); 34368358Snyan bd_printslice(od, &dptr[j], line, verbose); 34459777Snyan } 34559777Snyan } 34643561Skato#endif 34743561Skato } 34843561Skato bd_closedisk(od); 34943561Skato } 35043561Skato } 35143561Skato} 35243561Skato 35368358Snyan#ifndef PC98 35459777Snyan/* 35568358Snyan * Print information about slices on a disk. For the size calculations we 35668358Snyan * assume a 512 byte sector. 35759777Snyan */ 35843561Skatostatic void 35968358Snyanbd_printslice(struct open_disk *od, struct dos_partition *dp, char *prefix, 36068358Snyan int verbose) 36143561Skato{ 36259777Snyan char line[80]; 36359777Snyan 36459777Snyan switch (dp->dp_typ) { 36559777Snyan case DOSPTYP_386BSD: 36668358Snyan bd_printbsdslice(od, (daddr_t)dp->dp_start, prefix, verbose); 36759777Snyan return; 36859777Snyan case DOSPTYP_LINSWP: 36968358Snyan if (verbose) 37068358Snyan sprintf(line, "%s: Linux swap %.6dMB (%d - %d)\n", 37168358Snyan prefix, dp->dp_size / 2048, 37268358Snyan dp->dp_start, dp->dp_start + dp->dp_size); 37368358Snyan else 37468358Snyan sprintf(line, "%s: Linux swap\n", prefix); 37559777Snyan break; 37659777Snyan case DOSPTYP_LINUX: 37759777Snyan /* 37859777Snyan * XXX 37959777Snyan * read the superblock to confirm this is an ext2fs partition? 38059777Snyan */ 38168358Snyan if (verbose) 38268358Snyan sprintf(line, "%s: ext2fs %.6dMB (%d - %d)\n", prefix, 38368358Snyan dp->dp_size / 2048, dp->dp_start, 38468358Snyan dp->dp_start + dp->dp_size); 38568358Snyan else 38668358Snyan sprintf(line, "%s: ext2fs\n", prefix); 38759777Snyan break; 38859777Snyan case 0x00: /* unused partition */ 38959777Snyan case DOSPTYP_EXT: 39059777Snyan return; 39159777Snyan case 0x01: 39268358Snyan if (verbose) 39368358Snyan sprintf(line, "%s: FAT-12 %.6dMB (%d - %d)\n", prefix, 39468358Snyan dp->dp_size / 2048, dp->dp_start, 39568358Snyan dp->dp_start + dp->dp_size); 39668358Snyan else 39768358Snyan sprintf(line, "%s: FAT-12\n", prefix); 39859777Snyan break; 39959777Snyan case 0x04: 40059777Snyan case 0x06: 40159777Snyan case 0x0e: 40268358Snyan if (verbose) 40368358Snyan sprintf(line, "%s: FAT-16 %.6dMB (%d - %d)\n", prefix, 40468358Snyan dp->dp_size / 2048, dp->dp_start, 40568358Snyan dp->dp_start + dp->dp_size); 40668358Snyan else 40768358Snyan sprintf(line, "%s: FAT-16\n", prefix); 40859777Snyan break; 40959777Snyan case 0x0b: 41059777Snyan case 0x0c: 41168358Snyan if (verbose) 41268358Snyan sprintf(line, "%s: FAT-32 %.6dMB (%d - %d)\n", prefix, 41368358Snyan dp->dp_size / 2048, dp->dp_start, 41468358Snyan dp->dp_start + dp->dp_size); 41568358Snyan else 41668358Snyan sprintf(line, "%s: FAT-32\n", prefix); 41759777Snyan break; 41859777Snyan default: 41968358Snyan if (verbose) 42068358Snyan sprintf(line, "%s: Unknown fs: 0x%x %.6dMB (%d - %d)\n", 42168358Snyan prefix, dp->dp_typ, dp->dp_size / 2048, 42268358Snyan dp->dp_start, dp->dp_start + dp->dp_size); 42368358Snyan else 42468358Snyan sprintf(line, "%s: Unknown fs: 0x%x\n", prefix, 42568358Snyan dp->dp_typ); 42659777Snyan } 42759777Snyan pager_output(line); 42859777Snyan} 42959777Snyan#endif 43059777Snyan 43168358Snyan/* 43268358Snyan * Print out each valid partition in the disklabel of a FreeBSD slice. 43368358Snyan * For size calculations, we assume a 512 byte sector size. 43468358Snyan */ 43559777Snyanstatic void 43668358Snyanbd_printbsdslice(struct open_disk *od, daddr_t offset, char *prefix, 43768358Snyan int verbose) 43859777Snyan{ 43943561Skato char line[80]; 44068358Snyan char buf[BIOSDISK_SECSIZE]; 44143561Skato struct disklabel *lp; 44243561Skato int i; 44343561Skato 44443561Skato /* read disklabel */ 44543561Skato if (bd_read(od, offset + LABELSECTOR, 1, buf)) 44643561Skato return; 44743561Skato lp =(struct disklabel *)(&buf[0]); 44843561Skato if (lp->d_magic != DISKMAGIC) { 44963101Snyan sprintf(line, "%s: FFS bad disklabel\n", prefix); 45043561Skato pager_output(line); 45143561Skato return; 45243561Skato } 45343561Skato 45443561Skato /* Print partitions */ 45543561Skato for (i = 0; i < lp->d_npartitions; i++) { 45668358Snyan /* 45768358Snyan * For each partition, make sure we know what type of fs it is. If 45868358Snyan * not, then skip it. However, since floppies often have bogus 45968358Snyan * fstypes, print the 'a' partition on a floppy even if it is marked 46068358Snyan * unused. 46168358Snyan */ 46263101Snyan if ((lp->d_partitions[i].p_fstype == FS_BSDFFS) || 46363101Snyan (lp->d_partitions[i].p_fstype == FS_SWAP) || 46463101Snyan (lp->d_partitions[i].p_fstype == FS_VINUM) || 46543561Skato ((lp->d_partitions[i].p_fstype == FS_UNUSED) && 46668358Snyan (od->od_flags & BD_FLOPPY) && (i == 0))) { 46768358Snyan 46868358Snyan /* Only print out statistics in verbose mode */ 46968358Snyan if (verbose) 47068358Snyan sprintf(line, " %s%c: %s %.6dMB (%d - %d)\n", prefix, 'a' + i, 47163101Snyan (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : 47268358Snyan (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" : 47368358Snyan "FFS", 47468358Snyan lp->d_partitions[i].p_size / 2048, 47568358Snyan lp->d_partitions[i].p_offset, 47668358Snyan lp->d_partitions[i].p_offset + lp->d_partitions[i].p_size); 47768358Snyan else 47868358Snyan sprintf(line, " %s%c: %s\n", prefix, 'a' + i, 47968358Snyan (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : 48068358Snyan (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" : 48168358Snyan "FFS"); 48243561Skato pager_output(line); 48343561Skato } 48443561Skato } 48543561Skato} 48643561Skato 48743561Skato 48843561Skato/* 48943561Skato * Attempt to open the disk described by (dev) for use by (f). 49043561Skato * 49143561Skato * Note that the philosophy here is "give them exactly what 49243561Skato * they ask for". This is necessary because being too "smart" 49343561Skato * about what the user might want leads to complications. 49443561Skato * (eg. given no slice or partition value, with a disk that is 49543561Skato * sliced - are they after the first BSD slice, or the DOS 49643561Skato * slice before it?) 49743561Skato */ 49843561Skatostatic int 49943561Skatobd_open(struct open_file *f, ...) 50043561Skato{ 50143561Skato va_list ap; 50243561Skato struct i386_devdesc *dev; 50343561Skato struct open_disk *od; 50443561Skato int error; 50543561Skato 50643561Skato va_start(ap, f); 50743561Skato dev = va_arg(ap, struct i386_devdesc *); 50843561Skato va_end(ap); 50943561Skato if ((error = bd_opendisk(&od, dev))) 51043561Skato return(error); 51143561Skato 51243561Skato /* 51343561Skato * Save our context 51443561Skato */ 51543561Skato ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od; 51643561Skato DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff); 51743561Skato return(0); 51843561Skato} 51943561Skato 52043561Skatostatic int 52143561Skatobd_opendisk(struct open_disk **odp, struct i386_devdesc *dev) 52243561Skato{ 52343561Skato struct dos_partition *dptr; 52443561Skato struct disklabel *lp; 52543561Skato struct open_disk *od; 52643561Skato int sector, slice, i; 52743561Skato int error; 52868358Snyan char buf[BUFSIZE]; 52943561Skato 53043561Skato if (dev->d_kind.biosdisk.unit >= nbdinfo) { 53143561Skato DEBUG("attempt to open nonexistent disk"); 53243561Skato return(ENXIO); 53343561Skato } 53443561Skato 53543561Skato od = (struct open_disk *)malloc(sizeof(struct open_disk)); 53643561Skato if (!od) { 53743561Skato DEBUG("no memory"); 53843561Skato return (ENOMEM); 53943561Skato } 54043561Skato 54143561Skato /* Look up BIOS unit number, intialise open_disk structure */ 54243561Skato od->od_dkunit = dev->d_kind.biosdisk.unit; 54343561Skato od->od_unit = bdinfo[od->od_dkunit].bd_unit; 54443561Skato od->od_flags = bdinfo[od->od_dkunit].bd_flags; 54543561Skato od->od_boff = 0; 54659777Snyan od->od_nslices = 0; 54743561Skato error = 0; 54843561Skato DEBUG("open '%s', unit 0x%x slice %d partition %c", 54943561Skato i386_fmtdev(dev), dev->d_kind.biosdisk.unit, 55043561Skato dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition + 'a'); 55143561Skato 55243561Skato /* Get geometry for this open (removable device may have changed) */ 55343561Skato if (bd_getgeom(od)) { 55443561Skato DEBUG("can't get geometry"); 55543561Skato error = ENXIO; 55643561Skato goto out; 55743561Skato } 55843561Skato 55943561Skato /* 56043561Skato * Following calculations attempt to determine the correct value 56143561Skato * for d->od_boff by looking for the slice and partition specified, 56243561Skato * or searching for reasonable defaults. 56343561Skato */ 56443561Skato 56543561Skato /* 56643561Skato * Find the slice in the DOS slice table. 56743561Skato */ 56843561Skato#ifdef PC98 56943561Skato if (od->od_flags & BD_FLOPPY) { 57043561Skato sector = 0; 57143561Skato goto unsliced; 57243561Skato } 57343561Skato#endif 57443561Skato if (bd_read(od, 0, 1, buf)) { 57543561Skato DEBUG("error reading MBR"); 57643561Skato error = EIO; 57743561Skato goto out; 57843561Skato } 57943561Skato 58043561Skato /* 58143561Skato * Check the slice table magic. 58243561Skato */ 58368358Snyan if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { 58443561Skato /* If a slice number was explicitly supplied, this is an error */ 58543561Skato if (dev->d_kind.biosdisk.slice > 0) { 58643561Skato DEBUG("no slice table/MBR (no magic)"); 58743561Skato error = ENOENT; 58843561Skato goto out; 58943561Skato } 59043561Skato sector = 0; 59143561Skato goto unsliced; /* may be a floppy */ 59243561Skato } 59343561Skato#ifdef PC98 59443561Skato if (bd_read(od, 1, 1, buf)) { 59543561Skato DEBUG("error reading MBR"); 59643561Skato error = EIO; 59743561Skato goto out; 59843561Skato } 59943561Skato#endif 60059777Snyan 60159777Snyan /* 60259777Snyan * copy the partition table, then pick up any extended partitions. 60359777Snyan */ 60459777Snyan bcopy(buf + DOSPARTOFF, &od->od_slicetab, 60559777Snyan sizeof(struct dos_partition) * NDOSPART); 60663101Snyan#ifdef PC98 60759777Snyan od->od_nslices = NDOSPART; /* extended slices start here */ 60863101Snyan#else 60963101Snyan od->od_nslices = 4; /* extended slices start here */ 61059777Snyan for (i = 0; i < NDOSPART; i++) 61159777Snyan bd_checkextended(od, i); 61259777Snyan#endif 61343561Skato od->od_flags |= BD_PARTTABOK; 61459777Snyan dptr = &od->od_slicetab[0]; 61543561Skato 61643561Skato /* Is this a request for the whole disk? */ 61743561Skato if (dev->d_kind.biosdisk.slice == -1) { 61843561Skato sector = 0; 61943561Skato goto unsliced; 62043561Skato } 62143561Skato 62259777Snyan /* 62359777Snyan * if a slice number was supplied but not found, this is an error. 62459777Snyan */ 62559777Snyan if (dev->d_kind.biosdisk.slice > 0) { 62659777Snyan slice = dev->d_kind.biosdisk.slice - 1; 62759777Snyan if (slice >= od->od_nslices) { 62859777Snyan DEBUG("slice %d not found", slice); 62959777Snyan error = ENOENT; 63059777Snyan goto out; 63159777Snyan } 63259777Snyan } 63343561Skato 63459777Snyan#ifndef PC98 63559777Snyan /* 63659777Snyan * Check for the historically bogus MBR found on true dedicated disks 63759777Snyan */ 63859777Snyan if ((dptr[3].dp_typ == DOSPTYP_386BSD) && 63959777Snyan (dptr[3].dp_start == 0) && 64059777Snyan (dptr[3].dp_size == 50000)) { 64159777Snyan sector = 0; 64259777Snyan goto unsliced; 64343561Skato } 64459777Snyan#endif 64543561Skato 64659777Snyan /* Try to auto-detect the best slice; this should always give a slice number */ 64759777Snyan if (dev->d_kind.biosdisk.slice == 0) { 64859777Snyan slice = bd_bestslice(od); 64959777Snyan if (slice == -1) { 65059777Snyan error = ENOENT; 65159777Snyan goto out; 65259777Snyan } 65359777Snyan dev->d_kind.biosdisk.slice = slice; 65459777Snyan } 65559777Snyan 65659777Snyan dptr = &od->od_slicetab[0]; 65743561Skato /* 65843561Skato * Accept the supplied slice number unequivocally (we may be looking 65943561Skato * at a DOS partition). 66043561Skato */ 66143561Skato dptr += (dev->d_kind.biosdisk.slice - 1); /* we number 1-4, offsets are 0-3 */ 66243561Skato#ifdef PC98 66359777Snyan sector = dptr->dp_scyl * od->od_hds * od->od_sec + 66459777Snyan dptr->dp_shd * od->od_sec + dptr->dp_ssect; 66543561Skato { 66659777Snyan int end = dptr->dp_ecyl * od->od_hds * od->od_sec + 66759777Snyan dptr->dp_ehd * od->od_sec + dptr->dp_esect; 66859777Snyan DEBUG("slice entry %d at %d, %d sectors", 66959777Snyan dev->d_kind.biosdisk.slice - 1, sector, end-sector); 67043561Skato } 67143561Skato#else 67243561Skato sector = dptr->dp_start; 67343561Skato DEBUG("slice entry %d at %d, %d sectors", dev->d_kind.biosdisk.slice - 1, sector, dptr->dp_size); 67443561Skato#endif 67543561Skato 67643561Skato /* 67743561Skato * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition 67843561Skato */ 67943561Skato#ifdef PC98 68043561Skato if ((dptr->dp_mid == DOSMID_386BSD) && (dev->d_kind.biosdisk.partition < 0)) 68143561Skato#else 68243561Skato if ((dptr->dp_typ == DOSPTYP_386BSD) && (dev->d_kind.biosdisk.partition < 0)) 68343561Skato#endif 68443561Skato dev->d_kind.biosdisk.partition = 0; 68543561Skato 68643561Skato unsliced: 68743561Skato /* 68843561Skato * Now we have the slice offset, look for the partition in the disklabel if we have 68943561Skato * a partition to start with. 69043561Skato * 69143561Skato * XXX we might want to check the label checksum. 69243561Skato */ 69343561Skato if (dev->d_kind.biosdisk.partition < 0) { 69443561Skato od->od_boff = sector; /* no partition, must be after the slice */ 69543561Skato DEBUG("opening raw slice"); 69643561Skato } else { 69753207Snyan 69843561Skato if (bd_read(od, sector + LABELSECTOR, 1, buf)) { 69943561Skato DEBUG("error reading disklabel"); 70043561Skato error = EIO; 70143561Skato goto out; 70243561Skato } 70343561Skato DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel); 70443561Skato bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel)); 70543561Skato lp = &od->od_disklabel; 70643561Skato od->od_flags |= BD_LABELOK; 70743561Skato 70843561Skato if (lp->d_magic != DISKMAGIC) { 70943561Skato DEBUG("no disklabel"); 71043561Skato error = ENOENT; 71143561Skato goto out; 71243561Skato } 71343561Skato if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) { 71443561Skato DEBUG("partition '%c' exceeds partitions in table (a-'%c')", 71543561Skato 'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions); 71643561Skato error = EPART; 71743561Skato goto out; 71843561Skato 71943561Skato } 72043561Skato 72168358Snyan#ifdef DISK_DEBUG 72268358Snyan /* Complain if the partition is unused unless this is a floppy. */ 72343561Skato if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) && 72468358Snyan !(od->od_flags & BD_FLOPPY)) 72543561Skato DEBUG("warning, partition marked as unused"); 72668358Snyan#endif 72743561Skato 72843561Skato od->od_boff = lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset; 72943561Skato } 73043561Skato 73143561Skato out: 73243561Skato if (error) { 73343561Skato free(od); 73443561Skato } else { 73543561Skato *odp = od; /* return the open disk */ 73643561Skato } 73743561Skato return(error); 73843561Skato} 73943561Skato 74059777Snyan#ifndef PC98 74159777Snyanstatic void 74259777Snyanbd_checkextended(struct open_disk *od, int slicenum) 74359777Snyan{ 74468358Snyan char buf[BIOSDISK_SECSIZE]; 74559777Snyan struct dos_partition *dp; 74659777Snyan u_int base; 74759777Snyan int i, start, end; 74843561Skato 74959777Snyan dp = &od->od_slicetab[slicenum]; 75059777Snyan start = od->od_nslices; 75159777Snyan 75259777Snyan if (dp->dp_size == 0) 75359777Snyan goto done; 75459777Snyan if (dp->dp_typ != DOSPTYP_EXT) 75559777Snyan goto done; 75668358Snyan if (bd_read(od, (daddr_t)dp->dp_start, 1, buf)) 75759777Snyan goto done; 75868358Snyan if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { 75959777Snyan DEBUG("no magic in extended table"); 76059777Snyan goto done; 76159777Snyan } 76259777Snyan base = dp->dp_start; 76359777Snyan dp = (struct dos_partition *)(&buf[DOSPARTOFF]); 76459777Snyan for (i = 0; i < NDOSPART; i++, dp++) { 76559777Snyan if (dp->dp_size == 0) 76659777Snyan continue; 76759777Snyan if (od->od_nslices == MAX_SLICES) 76859777Snyan goto done; 76959777Snyan dp->dp_start += base; 77059777Snyan bcopy(dp, &od->od_slicetab[od->od_nslices], sizeof(*dp)); 77159777Snyan od->od_nslices++; 77259777Snyan } 77359777Snyan end = od->od_nslices; 77459777Snyan 77559777Snyan /* 77659777Snyan * now, recursively check the slices we just added 77759777Snyan */ 77859777Snyan for (i = start; i < end; i++) 77959777Snyan bd_checkextended(od, i); 78059777Snyandone: 78159777Snyan return; 78259777Snyan} 78359777Snyan#endif 78459777Snyan 78543561Skato/* 78643561Skato * Search for a slice with the following preferences: 78743561Skato * 78843561Skato * 1: Active FreeBSD slice 78943561Skato * 2: Non-active FreeBSD slice 79059777Snyan * 3: Active Linux slice 79159777Snyan * 4: non-active Linux slice 79259777Snyan * 5: Active FAT/FAT32 slice 79359777Snyan * 6: non-active FAT/FAT32 slice 79443561Skato */ 79559777Snyan#define PREF_RAWDISK 0 79659777Snyan#define PREF_FBSD_ACT 1 79759777Snyan#define PREF_FBSD 2 79859777Snyan#define PREF_LINUX_ACT 3 79959777Snyan#define PREF_LINUX 4 80059777Snyan#define PREF_DOS_ACT 5 80159777Snyan#define PREF_DOS 6 80259777Snyan#define PREF_NONE 7 80343561Skato 80459777Snyan/* 80559777Snyan * slicelimit is in the range 0 .. NDOSPART 80659777Snyan */ 80743561Skatostatic int 80859777Snyanbd_bestslice(struct open_disk *od) 80943561Skato{ 81059777Snyan struct dos_partition *dp; 81159777Snyan int pref, preflevel; 81259777Snyan int i, prefslice; 81353207Snyan 81459777Snyan prefslice = 0; 81559777Snyan preflevel = PREF_NONE; 81643561Skato 81759777Snyan dp = &od->od_slicetab[0]; 81859777Snyan for (i = 0; i < od->od_nslices; i++, dp++) { 81943561Skato#ifdef PC98 82059777Snyan switch(dp->dp_mid & 0x7f) { 82159777Snyan case DOSMID_386BSD & 0x7f: /* FreeBSD */ 82259777Snyan if ((dp->dp_mid & 0x80) && 82359777Snyan (preflevel > PREF_FBSD_ACT)) { 82459777Snyan pref = i; 82559777Snyan preflevel = PREF_FBSD_ACT; 82659777Snyan } else if (preflevel > PREF_FBSD) { 82759777Snyan pref = i; 82859777Snyan preflevel = PREF_FBSD; 82959777Snyan } 83059777Snyan break; 83159777Snyan 83259777Snyan case 0x11: /* DOS/Windows */ 83359777Snyan case 0x20: 83459777Snyan case 0x21: 83559777Snyan case 0x22: 83659777Snyan case 0x23: 83759777Snyan case 0x63: 83859777Snyan if ((dp->dp_mid & 0x80) && 83959777Snyan (preflevel > PREF_DOS_ACT)) { 84059777Snyan pref = i; 84159777Snyan preflevel = PREF_DOS_ACT; 84259777Snyan } else if (preflevel > PREF_DOS) { 84359777Snyan pref = i; 84459777Snyan preflevel = PREF_DOS; 84559777Snyan } 84659777Snyan break; 84759777Snyan } 84843561Skato#else 84959777Snyan switch (dp->dp_typ) { 85059777Snyan case DOSPTYP_386BSD: /* FreeBSD */ 85159777Snyan pref = dp->dp_flag & 0x80 ? PREF_FBSD_ACT : PREF_FBSD; 85259777Snyan break; 85359777Snyan 85459777Snyan case DOSPTYP_LINUX: 85559777Snyan pref = dp->dp_flag & 0x80 ? PREF_LINUX_ACT : PREF_LINUX; 85659777Snyan break; 85759777Snyan 85859777Snyan case 0x01: /* DOS/Windows */ 85959777Snyan case 0x04: 86059777Snyan case 0x06: 86159777Snyan case 0x0b: 86259777Snyan case 0x0c: 86359777Snyan case 0x0e: 86459777Snyan pref = dp->dp_flag & 0x80 ? PREF_DOS_ACT : PREF_DOS; 86559777Snyan break; 86659777Snyan 86759777Snyan default: 86859777Snyan pref = PREF_NONE; 86959777Snyan } 87059777Snyan if (pref < preflevel) { 87159777Snyan preflevel = pref; 87259777Snyan prefslice = i + 1; 87359777Snyan } 87459777Snyan#endif 87543561Skato } 87659777Snyan return (prefslice); 87743561Skato} 87843561Skato 87943561Skatostatic int 88043561Skatobd_close(struct open_file *f) 88143561Skato{ 88243561Skato struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data); 88343561Skato 88443561Skato bd_closedisk(od); 88543561Skato return(0); 88643561Skato} 88743561Skato 88843561Skatostatic void 88943561Skatobd_closedisk(struct open_disk *od) 89043561Skato{ 89143561Skato DEBUG("open_disk %p", od); 89243561Skato#if 0 89343561Skato /* XXX is this required? (especially if disk already open...) */ 89443561Skato if (od->od_flags & BD_FLOPPY) 89543561Skato delay(3000000); 89643561Skato#endif 89743561Skato free(od); 89843561Skato} 89943561Skato 90043561Skatostatic int 90168358Snyanbd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) 90243561Skato{ 90343561Skato struct bcache_devdata bcd; 90458165Snyan struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data); 90558165Snyan 90643561Skato bcd.dv_strategy = bd_realstrategy; 90743561Skato bcd.dv_devdata = devdata; 90858165Snyan return(bcache_strategy(&bcd, od->od_unit, rw, dblk+od->od_boff, size, buf, rsize)); 90943561Skato} 91043561Skato 91143561Skatostatic int 91268358Snyanbd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) 91343561Skato{ 91443561Skato struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data); 91543561Skato int blks; 91643561Skato#ifdef BD_SUPPORT_FRAGS 91743561Skato char fragbuf[BIOSDISK_SECSIZE]; 91843561Skato size_t fragsize; 91943561Skato 92043561Skato fragsize = size % BIOSDISK_SECSIZE; 92143561Skato#else 92243561Skato if (size % BIOSDISK_SECSIZE) 92343561Skato panic("bd_strategy: %d bytes I/O not multiple of block size", size); 92443561Skato#endif 92543561Skato 92643561Skato DEBUG("open_disk %p", od); 92743561Skato 92843561Skato if (rw != F_READ) 92943561Skato return(EROFS); 93043561Skato 93143561Skato 93243561Skato blks = size / BIOSDISK_SECSIZE; 93358165Snyan DEBUG("read %d from %d to %p", blks, dblk, buf); 93443561Skato 93543561Skato if (rsize) 93643561Skato *rsize = 0; 93758165Snyan if (blks && bd_read(od, dblk, blks, buf)) { 93843561Skato DEBUG("read error"); 93943561Skato return (EIO); 94043561Skato } 94143561Skato#ifdef BD_SUPPORT_FRAGS 94258165Snyan DEBUG("bd_strategy: frag read %d from %d+%d to %p", 94358165Snyan fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE)); 94458165Snyan if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) { 94543561Skato DEBUG("frag read error"); 94643561Skato return(EIO); 94743561Skato } 94843561Skato bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize); 94943561Skato#endif 95043561Skato if (rsize) 95143561Skato *rsize = size; 95243561Skato return (0); 95343561Skato} 95443561Skato 95543561Skato/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */ 95643561Skato#define FLOPPY_BOUNCEBUF 18 95743561Skato 95843561Skatostatic int 95943561Skatobd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) 96043561Skato{ 96168358Snyan u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer; 96243561Skato caddr_t p, xp, bbuf, breg; 96343561Skato 96468358Snyan /* Just in case some idiot actually tries to read -1 blocks... */ 96568358Snyan if (blks < 0) 96668358Snyan return (-1); 96768358Snyan 96843561Skato bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ 96943561Skato resid = blks; 97043561Skato p = dest; 97143561Skato 97243561Skato /* Decide whether we have to bounce */ 97343561Skato#ifdef PC98 97451586Skato if ( 97543561Skato#else 97643561Skato if ((od->od_unit < 0x80) && 97743561Skato#endif 97843561Skato ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) { 97943561Skato 98043561Skato /* 98143561Skato * There is a 64k physical boundary somewhere in the destination buffer, so we have 98243561Skato * to arrange a suitable bounce buffer. Allocate a buffer twice as large as we 98343561Skato * need to. Use the bottom half unless there is a break there, in which case we 98443561Skato * use the top half. 98543561Skato */ 98651586Skato#ifdef PC98 98768358Snyan x = min(od->od_sec, (unsigned)blks); 98851586Skato#else 98968358Snyan x = min(FLOPPY_BOUNCEBUF, (unsigned)blks); 99051586Skato#endif 99143561Skato bbuf = malloc(x * 2 * BIOSDISK_SECSIZE); 99243561Skato if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(dest + x * BIOSDISK_SECSIZE) & 0xffff0000)) { 99343561Skato breg = bbuf; 99443561Skato } else { 99543561Skato breg = bbuf + x * BIOSDISK_SECSIZE; 99643561Skato } 99743561Skato maxfer = x; /* limit transfers to bounce region size */ 99843561Skato } else { 99968358Snyan breg = bbuf = NULL; 100043561Skato maxfer = 0; 100143561Skato } 100243561Skato 100343561Skato while (resid > 0) { 100443561Skato x = dblk; 100543561Skato cyl = x / bpc; /* block # / blocks per cylinder */ 100643561Skato x %= bpc; /* block offset into cylinder */ 100743561Skato hd = x / od->od_sec; /* offset / blocks per track */ 100843561Skato sec = x % od->od_sec; /* offset into track */ 100943561Skato 101043561Skato /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */ 101143561Skato x = min(od->od_sec - sec, resid); 101243561Skato if (maxfer > 0) 101343561Skato x = min(x, maxfer); /* fit bounce buffer */ 101443561Skato 101543561Skato /* where do we transfer to? */ 101643561Skato xp = bbuf == NULL ? p : breg; 101743561Skato 101843561Skato /* correct sector number for 1-based BIOS numbering */ 101943561Skato#ifdef PC98 102043561Skato if ((od->od_unit & 0xf0) == 0x30 || (od->od_unit & 0xf0) == 0x90) 102143561Skato sec++; 102243561Skato#else 102343561Skato sec++; 102443561Skato#endif 102543561Skato 102643561Skato /* Loop retrying the operation a couple of times. The BIOS may also retry. */ 102743561Skato for (retry = 0; retry < 3; retry++) { 102843561Skato /* if retrying, reset the drive */ 102943561Skato if (retry > 0) { 103043561Skato#ifdef PC98 103151586Skato v86.ctl = V86_FLAGS; 103251586Skato v86.addr = 0x1b; 103351586Skato v86.eax = 0x0300 | od->od_unit; 103443561Skato#else 103543561Skato v86.ctl = V86_FLAGS; 103643561Skato v86.addr = 0x13; 103743561Skato v86.eax = 0; 103843561Skato v86.edx = od->od_unit; 103951586Skato#endif 104043561Skato v86int(); 104143561Skato } 104243561Skato 104363101Snyan#ifdef PC98 104451586Skato v86.ctl = V86_FLAGS; 104543561Skato v86.addr = 0x1b; 104643561Skato if (od->od_flags & BD_FLOPPY) { 104743561Skato v86.eax = 0xd600 | od->od_unit; 104843561Skato v86.ecx = 0x0200 | (cyl & 0xff); 104943561Skato } 105043561Skato else { 105143561Skato v86.eax = 0x0600 | od->od_unit; 105243561Skato v86.ecx = cyl; 105343561Skato } 105443561Skato v86.edx = (hd << 8) | sec; 105543561Skato v86.ebx = x * BIOSDISK_SECSIZE; 105643561Skato v86.es = VTOPSEG(xp); 105743561Skato v86.ebp = VTOPOFF(xp); 105843561Skato v86int(); 105943561Skato result = (v86.efl & 0x1); 106043561Skato if (result == 0) 106143561Skato break; 106263101Snyan#else 106363101Snyan if(cyl > 1023) { 106463101Snyan /* use EDD if the disk supports it, otherwise, return error */ 106563101Snyan if(od->od_flags & BD_MODEEDD1) { 106663101Snyan static unsigned short packet[8]; 106763101Snyan 106863101Snyan packet[0] = 0x10; 106963101Snyan packet[1] = x; 107063101Snyan packet[2] = VTOPOFF(xp); 107163101Snyan packet[3] = VTOPSEG(xp); 107263101Snyan packet[4] = dblk & 0xffff; 107363101Snyan packet[5] = dblk >> 16; 107463101Snyan packet[6] = 0; 107563101Snyan packet[7] = 0; 107663101Snyan v86.ctl = V86_FLAGS; 107763101Snyan v86.addr = 0x13; 107863101Snyan v86.eax = 0x4200; 107963101Snyan v86.edx = od->od_unit; 108063101Snyan v86.ds = VTOPSEG(packet); 108163101Snyan v86.esi = VTOPOFF(packet); 108263101Snyan v86int(); 108363101Snyan result = (v86.efl & 0x1); 108463101Snyan if(result == 0) 108563101Snyan break; 108663101Snyan } else { 108763101Snyan result = 1; 108863101Snyan break; 108963101Snyan } 109063101Snyan } else { 109163101Snyan /* Use normal CHS addressing */ 109263101Snyan v86.ctl = V86_FLAGS; 109363101Snyan v86.addr = 0x13; 109463101Snyan v86.eax = 0x200 | x; 109563101Snyan v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; 109663101Snyan v86.edx = (hd << 8) | od->od_unit; 109763101Snyan v86.es = VTOPSEG(xp); 109863101Snyan v86.ebx = VTOPOFF(xp); 109963101Snyan v86int(); 110063101Snyan result = (v86.efl & 0x1); 110163101Snyan if (result == 0) 110263101Snyan break; 110363101Snyan } 110463101Snyan#endif 110543561Skato } 110643561Skato 110743561Skato#ifdef PC98 110843561Skato 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"); 110943561Skato /* BUG here, cannot use v86 in printf because putchar uses it too */ 111043561Skato DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 111143561Skato od->od_flags & BD_FLOPPY ? 0xd600 | od->od_unit : 0x0600 | od->od_unit, 111243561Skato od->od_flags & BD_FLOPPY ? 0x0200 | cyl : cyl, (hd << 8) | sec, 111343561Skato (v86.eax >> 8) & 0xff); 111443561Skato#else 111543561Skato DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok"); 111643561Skato /* BUG here, cannot use v86 in printf because putchar uses it too */ 111743561Skato DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 111843561Skato 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff); 111943561Skato#endif 112043561Skato if (result) { 112143561Skato if (bbuf != NULL) 112243561Skato free(bbuf); 112343561Skato return(-1); 112443561Skato } 112543561Skato if (bbuf != NULL) 112643561Skato bcopy(breg, p, x * BIOSDISK_SECSIZE); 112743561Skato p += (x * BIOSDISK_SECSIZE); 112843561Skato dblk += x; 112943561Skato resid -= x; 113043561Skato } 113143561Skato 113243561Skato/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */ 113343561Skato if (bbuf != NULL) 113443561Skato free(bbuf); 113543561Skato return(0); 113643561Skato} 113743561Skato 113843561Skatostatic int 113943561Skatobd_getgeom(struct open_disk *od) 114043561Skato{ 114143561Skato 114243561Skato#ifdef PC98 114343561Skato if (od->od_flags & BD_FLOPPY) { 114454819Snyan od->od_cyl = 79; 114543561Skato od->od_hds = 2; 114643561Skato od->od_sec = (od->od_unit & 0xf0) == 0x30 ? 18 : 15; 114759777Snyan } else { 114854819Snyan v86.ctl = V86_FLAGS; 114943561Skato v86.addr = 0x1b; 115043561Skato v86.eax = 0x8400 | od->od_unit; 115143561Skato v86int(); 115243561Skato 115343561Skato od->od_cyl = v86.ecx; 115443561Skato od->od_hds = (v86.edx >> 8) & 0xff; 115543561Skato od->od_sec = v86.edx & 0xff; 115651586Skato if (v86.efl & 0x1) 115751586Skato return(1); 115843561Skato } 115943561Skato#else 116043561Skato v86.ctl = V86_FLAGS; 116143561Skato v86.addr = 0x13; 116243561Skato v86.eax = 0x800; 116343561Skato v86.edx = od->od_unit; 116443561Skato v86int(); 116543561Skato 116643561Skato if ((v86.efl & 0x1) || /* carry set */ 116768358Snyan ((v86.edx & 0xff) <= (unsigned)(od->od_unit & 0x7f))) /* unit # bad */ 116843561Skato return(1); 116943561Skato 117043561Skato /* convert max cyl # -> # of cylinders */ 117143561Skato od->od_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1; 117243561Skato /* convert max head # -> # of heads */ 117343561Skato od->od_hds = ((v86.edx & 0xff00) >> 8) + 1; 117443561Skato od->od_sec = v86.ecx & 0x3f; 117543561Skato#endif 117643561Skato 117743561Skato DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec); 117843561Skato return(0); 117943561Skato} 118043561Skato 118143561Skato/* 118253207Snyan * Return the BIOS geometry of a given "fixed drive" in a format 118353207Snyan * suitable for the legacy bootinfo structure. Since the kernel is 118453207Snyan * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we 118553207Snyan * prefer to get the information directly, rather than rely on being 118653207Snyan * able to put it together from information already maintained for 118753207Snyan * different purposes and for a probably different number of drives. 118853207Snyan * 118953207Snyan * For valid drives, the geometry is expected in the format (31..0) 119053207Snyan * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are 119153207Snyan * indicated by returning the geometry of a "1.2M" PC-format floppy 119253207Snyan * disk. And, incidentally, what is returned is not the geometry as 119353207Snyan * such but the highest valid cylinder, head, and sector numbers. 119453207Snyan */ 119553207Snyanu_int32_t 119653207Snyanbd_getbigeom(int bunit) 119753207Snyan{ 119853207Snyan 119954819Snyan#ifdef PC98 120054819Snyan int hds = 0; 120155339Snyan int unit = 0x80; /* IDE HDD */ 120254819Snyan u_int addr = 0xA155d; 120355339Snyan 120454819Snyan while (unit < 0xa7) { 120555339Snyan if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f))) 120655339Snyan if (hds++ == bunit) 120754819Snyan break; 120854819Snyan if (++unit == 0x84) { 120955339Snyan unit = 0xa0; /* SCSI HDD */ 121054819Snyan addr = 0xA1482; 121154819Snyan } 121254819Snyan } 121354819Snyan if (unit == 0xa7) 121454819Snyan return 0x4f010f; 121553207Snyan v86.ctl = V86_FLAGS; 121654819Snyan v86.addr = 0x1b; 121754819Snyan v86.eax = 0x8400 | unit; 121854819Snyan v86int(); 121954819Snyan if (v86.efl & 0x1) 122054819Snyan return 0x4f010f; 122154819Snyan return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff); 122254819Snyan#else 122354819Snyan v86.ctl = V86_FLAGS; 122453207Snyan v86.addr = 0x13; 122553207Snyan v86.eax = 0x800; 122653207Snyan v86.edx = 0x80 + bunit; 122753207Snyan v86int(); 122853207Snyan if (v86.efl & 0x1) 122953207Snyan return 0x4f010f; 123053207Snyan return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) | 123153207Snyan (v86.edx & 0xff00) | (v86.ecx & 0x3f); 123254819Snyan#endif 123353207Snyan} 123453207Snyan 123553207Snyan/* 123643561Skato * Return a suitable dev_t value for (dev). 123743561Skato * 123843561Skato * In the case where it looks like (dev) is a SCSI disk, we allow the number of 123943561Skato * IDE disks to be specified in $num_ide_disks. There should be a Better Way. 124043561Skato */ 124143561Skatoint 124243561Skatobd_getdev(struct i386_devdesc *dev) 124343561Skato{ 124443561Skato struct open_disk *od; 124543561Skato int biosdev; 124643561Skato int major; 124743561Skato int rootdev; 124843561Skato char *nip, *cp; 124943561Skato int unitofs = 0, i, unit; 125043561Skato 125143561Skato biosdev = bd_unit2bios(dev->d_kind.biosdisk.unit); 125243561Skato DEBUG("unit %d BIOS device %d", dev->d_kind.biosdisk.unit, biosdev); 125343561Skato if (biosdev == -1) /* not a BIOS device */ 125443561Skato return(-1); 125543561Skato if (bd_opendisk(&od, dev) != 0) /* oops, not a viable device */ 125643561Skato return(-1); 125743561Skato 125843561Skato#ifdef PC98 125943561Skato if ((biosdev & 0xf0) == 0x90 || (biosdev & 0xf0) == 0x30) { 126043561Skato#else 126143561Skato if (biosdev < 0x80) { 126243561Skato#endif 126343561Skato /* floppy (or emulated floppy) or ATAPI device */ 126443561Skato if (bdinfo[dev->d_kind.biosdisk.unit].bd_type == DT_ATAPI) { 126543561Skato /* is an ATAPI disk */ 126643561Skato major = WFDMAJOR; 126743561Skato } else { 126843561Skato /* is a floppy disk */ 126943561Skato major = FDMAJOR; 127043561Skato } 127143561Skato } else { 127243561Skato /* harddisk */ 127343561Skato if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) { 127443561Skato /* label OK, disk labelled as SCSI */ 127543561Skato major = DAMAJOR; 127643561Skato /* check for unit number correction hint, now deprecated */ 127743561Skato if ((nip = getenv("num_ide_disks")) != NULL) { 127843561Skato i = strtol(nip, &cp, 0); 127943561Skato /* check for parse error */ 128043561Skato if ((cp != nip) && (*cp == 0)) 128143561Skato unitofs = i; 128243561Skato } 128343561Skato } else { 128443561Skato /* assume an IDE disk */ 128543561Skato major = WDMAJOR; 128643561Skato } 128743561Skato } 128868358Snyan /* default root disk unit number */ 128968358Snyan#ifdef PC98 129068358Snyan if ((biosdev & 0xf0) == 0xa0) 129168358Snyan unit = bdinfo[dev->d_kind.biosdisk.unit].bd_da_unit; 129268358Snyan else 129368358Snyan unit = biosdev & 0xf; 129468358Snyan#else 129568358Snyan unit = (biosdev & 0x7f) - unitofs; 129668358Snyan#endif 129768358Snyan 129843561Skato /* XXX a better kludge to set the root disk unit number */ 129943561Skato if ((nip = getenv("root_disk_unit")) != NULL) { 130043561Skato i = strtol(nip, &cp, 0); 130143561Skato /* check for parse error */ 130243561Skato if ((cp != nip) && (*cp == 0)) 130343561Skato unit = i; 130443561Skato } 130543561Skato 130643561Skato rootdev = MAKEBOOTDEV(major, 130743561Skato (dev->d_kind.biosdisk.slice + 1) >> 4, /* XXX slices may be wrong here */ 130843561Skato (dev->d_kind.biosdisk.slice + 1) & 0xf, 130943561Skato unit, 131043561Skato dev->d_kind.biosdisk.partition); 131143561Skato DEBUG("dev is 0x%x\n", rootdev); 131243561Skato return(rootdev); 131343561Skato} 1314