biosdisk.c revision 104621
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 104621 2002-10-07 15:26:10Z 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> 43104621Snyan#include <sys/diskpc98.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 5263101Snyan#define BIOS_NUMDRIVES 0x475 5343561Skato#define BIOSDISK_SECSIZE 512 5443561Skato#define BUFSIZE (1 * BIOSDISK_SECSIZE) 5543561Skato#define MAXBDDEV MAXDEV 5643561Skato 5743561Skato#define DT_ATAPI 0x10 /* disk type for ATAPI floppies */ 5843561Skato#define WDMAJOR 0 /* major numbers for devices we frontend for */ 5943561Skato#define WFDMAJOR 1 6043561Skato#define FDMAJOR 2 6143561Skato#define DAMAJOR 4 6243561Skato 6343561Skato#ifdef DISK_DEBUG 6487599Sobrien# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 6543561Skato#else 6643561Skato# define DEBUG(fmt, args...) 6743561Skato#endif 6843561Skato 6943561Skatostruct open_disk { 7043561Skato int od_dkunit; /* disk unit number */ 7143561Skato int od_unit; /* BIOS unit number */ 7243561Skato int od_cyl; /* BIOS geometry */ 7343561Skato int od_hds; 7443561Skato int od_sec; 7543561Skato int od_boff; /* block offset from beginning of BIOS disk */ 7643561Skato int od_flags; 7759777Snyan#define BD_MODEINT13 0x0000 7859777Snyan#define BD_MODEEDD1 0x0001 7959777Snyan#define BD_MODEEDD3 0x0002 8063101Snyan#define BD_MODEMASK 0x0003 8159777Snyan#define BD_FLOPPY 0x0004 8259777Snyan#define BD_LABELOK 0x0008 8359777Snyan#define BD_PARTTABOK 0x0010 8443561Skato struct disklabel od_disklabel; 8559777Snyan int od_nslices; /* slice count */ 8659777Snyan struct dos_partition od_slicetab[MAX_SLICES]; 8743561Skato}; 8843561Skato 8943561Skato/* 9043561Skato * List of BIOS devices, translation from disk unit number to 9143561Skato * BIOS unit number. 9243561Skato */ 9343561Skatostatic struct bdinfo 9443561Skato{ 9543561Skato int bd_unit; /* BIOS unit number */ 9643561Skato int bd_flags; 9743561Skato int bd_type; /* BIOS 'drive type' (floppy only) */ 9843561Skato#ifdef PC98 9953207Snyan int bd_da_unit; /* kernel unit number for da */ 10043561Skato#endif 10143561Skato} bdinfo [MAXBDDEV]; 10243561Skatostatic int nbdinfo = 0; 10343561Skato 10443561Skatostatic int bd_getgeom(struct open_disk *od); 10559777Snyanstatic int bd_read(struct open_disk *od, daddr_t dblk, int blks, 10659777Snyan caddr_t dest); 10787734Snyanstatic int bd_write(struct open_disk *od, daddr_t dblk, int blks, 10887734Snyan caddr_t dest); 10943561Skato 11043561Skatostatic int bd_int13probe(struct bdinfo *bd); 11143561Skato 11259777Snyanstatic void bd_printslice(struct open_disk *od, struct dos_partition *dp, 11368358Snyan char *prefix, int verbose); 11468358Snyanstatic void bd_printbsdslice(struct open_disk *od, daddr_t offset, 11568358Snyan char *prefix, int verbose); 11643561Skato 11743561Skatostatic int bd_init(void); 11859777Snyanstatic int bd_strategy(void *devdata, int flag, daddr_t dblk, 11968358Snyan size_t size, char *buf, size_t *rsize); 12059777Snyanstatic int bd_realstrategy(void *devdata, int flag, daddr_t dblk, 12168358Snyan size_t size, char *buf, size_t *rsize); 12243561Skatostatic int bd_open(struct open_file *f, ...); 12343561Skatostatic int bd_close(struct open_file *f); 12443561Skatostatic void bd_print(int verbose); 12543561Skato 12643561Skatostruct devsw biosdisk = { 12743561Skato "disk", 12843561Skato DEVT_DISK, 12943561Skato bd_init, 13043561Skato bd_strategy, 13143561Skato bd_open, 13243561Skato bd_close, 13343561Skato noioctl, 13468358Snyan bd_print, 13568358Snyan NULL 13643561Skato}; 13743561Skato 13843561Skatostatic int bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev); 13943561Skatostatic void bd_closedisk(struct open_disk *od); 14059777Snyanstatic int bd_bestslice(struct open_disk *od); 14159777Snyanstatic void bd_checkextended(struct open_disk *od, int slicenum); 14243561Skato 14343561Skato/* 14443561Skato * Translate between BIOS device numbers and our private unit numbers. 14543561Skato */ 14643561Skatoint 14743561Skatobd_bios2unit(int biosdev) 14843561Skato{ 14943561Skato int i; 15043561Skato 15143561Skato DEBUG("looking for bios device 0x%x", biosdev); 15243561Skato for (i = 0; i < nbdinfo; i++) { 15343561Skato DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit); 15443561Skato if (bdinfo[i].bd_unit == biosdev) 15543561Skato return(i); 15643561Skato } 15743561Skato return(-1); 15843561Skato} 15943561Skato 16043561Skatoint 16143561Skatobd_unit2bios(int unit) 16243561Skato{ 16343561Skato if ((unit >= 0) && (unit < nbdinfo)) 16443561Skato return(bdinfo[unit].bd_unit); 16543561Skato return(-1); 16643561Skato} 16743561Skato 16843561Skato/* 16943561Skato * Quiz the BIOS for disk devices, save a little info about them. 17043561Skato */ 17143561Skatostatic int 17243561Skatobd_init(void) 17343561Skato{ 17463101Snyan#ifdef PC98 17543561Skato int base, unit; 17663101Snyan int da_drive=0, n=-0x10; 17743561Skato 17843561Skato /* sequence 0x90, 0x80, 0xa0 */ 17943561Skato for (base = 0x90; base <= 0xa0; base += n, n += 0x30) { 18043561Skato for (unit = base; (nbdinfo < MAXBDDEV) || ((unit & 0x0f) < 4); unit++) { 18143561Skato bdinfo[nbdinfo].bd_unit = unit; 18243561Skato bdinfo[nbdinfo].bd_flags = (unit & 0xf0) == 0x90 ? BD_FLOPPY : 0; 18343561Skato 18444463Skato if (!bd_int13probe(&bdinfo[nbdinfo])){ 18549425Skato if (((unit & 0xf0) == 0x90 && (unit & 0x0f) < 4) || 18649425Skato ((unit & 0xf0) == 0xa0 && (unit & 0x0f) < 6)) 18744463Skato continue; /* Target IDs are not contiguous. */ 18844463Skato else 18944463Skato break; 19044463Skato } 19143561Skato 19243561Skato if (bdinfo[nbdinfo].bd_flags & BD_FLOPPY){ 19344467Skato /* available 1.44MB access? */ 19459777Snyan if (*(u_char *)PTOV(0xA15AE) & (1<<(unit & 0xf))) { 19544467Skato /* boot media 1.2MB FD? */ 19644467Skato if ((*(u_char *)PTOV(0xA1584) & 0xf0) != 0x90) 19744467Skato bdinfo[nbdinfo].bd_unit = 0x30 + (unit & 0xf); 19844467Skato } 19943561Skato } 20049426Skato else { 20149426Skato if ((unit & 0xa0) == 0xa0) 20249426Skato bdinfo[nbdinfo].bd_da_unit = da_drive++; 20349426Skato } 20443561Skato /* XXX we need "disk aliases" to make this simpler */ 20543561Skato printf("BIOS drive %c: is disk%d\n", 20649426Skato 'A' + nbdinfo, nbdinfo); 20743561Skato nbdinfo++; 20843561Skato } 20943561Skato } 21043561Skato#else 21163101Snyan int base, unit, nfd = 0; 21263101Snyan 21343561Skato /* sequence 0, 0x80 */ 21443561Skato for (base = 0; base <= 0x80; base += 0x80) { 21543561Skato for (unit = base; (nbdinfo < MAXBDDEV); unit++) { 21663101Snyan /* check the BIOS equipment list for number of fixed disks */ 21763101Snyan if((base == 0x80) && 21868358Snyan (nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES))) 21963101Snyan break; 22063101Snyan 22143561Skato bdinfo[nbdinfo].bd_unit = unit; 22243561Skato bdinfo[nbdinfo].bd_flags = (unit < 0x80) ? BD_FLOPPY : 0; 22343561Skato 22443561Skato if (!bd_int13probe(&bdinfo[nbdinfo])) 22543561Skato break; 22643561Skato 22743561Skato /* XXX we need "disk aliases" to make this simpler */ 22843561Skato printf("BIOS drive %c: is disk%d\n", 22943561Skato (unit < 0x80) ? ('A' + unit) : ('C' + unit - 0x80), nbdinfo); 23043561Skato nbdinfo++; 23163101Snyan if (base == 0x80) 23263101Snyan nfd++; 23343561Skato } 23443561Skato } 23543561Skato#endif 23643561Skato return(0); 23743561Skato} 23843561Skato 23943561Skato/* 24043561Skato * Try to detect a device supported by the legacy int13 BIOS 24143561Skato */ 24243561Skatostatic int 24343561Skatobd_int13probe(struct bdinfo *bd) 24443561Skato{ 24543561Skato#ifdef PC98 24643561Skato int addr; 24763101Snyan 24859777Snyan if (bd->bd_flags & BD_FLOPPY) { 24943561Skato addr = 0xa155c; 25059777Snyan } else { 25143561Skato if ((bd->bd_unit & 0xf0) == 0x80) 25243561Skato addr = 0xa155d; 25343561Skato else 25443561Skato addr = 0xa1482; 25543561Skato } 25643561Skato if ( *(u_char *)PTOV(addr) & (1<<(bd->bd_unit & 0x0f))) { 25743561Skato bd->bd_flags |= BD_MODEINT13; 25843561Skato return(1); 25943561Skato } 26043561Skato return(0); 26143561Skato#else 26243561Skato v86.ctl = V86_FLAGS; 26343561Skato v86.addr = 0x13; 26443561Skato v86.eax = 0x800; 26543561Skato v86.edx = bd->bd_unit; 26643561Skato v86int(); 26743561Skato 26843561Skato if (!(v86.efl & 0x1) && /* carry clear */ 26968358Snyan ((v86.edx & 0xff) > ((unsigned)bd->bd_unit & 0x7f))) { /* unit # OK */ 27043561Skato bd->bd_flags |= BD_MODEINT13; 27143561Skato bd->bd_type = v86.ebx & 0xff; 27263101Snyan 27363101Snyan /* Determine if we can use EDD with this device. */ 27463101Snyan v86.eax = 0x4100; 27563101Snyan v86.edx = bd->bd_unit; 27663101Snyan v86.ebx = 0x55aa; 27763101Snyan v86int(); 27863101Snyan if (!(v86.efl & 0x1) && /* carry clear */ 27963101Snyan ((v86.ebx & 0xffff) == 0xaa55) && /* signature */ 28063101Snyan (v86.ecx & 0x1)) { /* packets mode ok */ 28163101Snyan bd->bd_flags |= BD_MODEEDD1; 28268358Snyan if((v86.eax & 0xff00) > 0x300) 28363101Snyan bd->bd_flags |= BD_MODEEDD3; 28463101Snyan } 28543561Skato return(1); 28643561Skato } 28763101Snyan return(0); 28843561Skato#endif 28943561Skato} 29043561Skato 29143561Skato/* 29243561Skato * Print information about disks 29343561Skato */ 29443561Skatostatic void 29543561Skatobd_print(int verbose) 29643561Skato{ 29743561Skato int i, j; 29843561Skato char line[80]; 29943561Skato struct i386_devdesc dev; 30043561Skato struct open_disk *od; 30143561Skato struct dos_partition *dptr; 30243561Skato 30343561Skato for (i = 0; i < nbdinfo; i++) { 30443561Skato#ifdef PC98 30549426Skato sprintf(line, " disk%d: BIOS drive %c:\n", i, 'A' + i); 30643561Skato#else 30743561Skato sprintf(line, " disk%d: BIOS drive %c:\n", i, 30843561Skato (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit) : ('C' + bdinfo[i].bd_unit - 0x80)); 30943561Skato#endif 31043561Skato pager_output(line); 31143561Skato 31243561Skato /* try to open the whole disk */ 31343561Skato dev.d_kind.biosdisk.unit = i; 31443561Skato dev.d_kind.biosdisk.slice = -1; 31543561Skato dev.d_kind.biosdisk.partition = -1; 31643561Skato 31743561Skato if (!bd_opendisk(&od, &dev)) { 31843561Skato 31943561Skato /* Do we have a partition table? */ 32043561Skato if (od->od_flags & BD_PARTTABOK) { 32159777Snyan dptr = &od->od_slicetab[0]; 32243561Skato 32368358Snyan /* Check for a "dedicated" disk */ 32443561Skato#ifdef PC98 32559777Snyan for (j = 0; j < od->od_nslices; j++) { 32643561Skato switch(dptr[j].dp_mid) { 32743561Skato case DOSMID_386BSD: 32843561Skato sprintf(line, " disk%ds%d", i, j + 1); 32959777Snyan bd_printbsdslice(od, 33059777Snyan dptr[j].dp_scyl * od->od_hds * od->od_sec + 33159777Snyan dptr[j].dp_shd * od->od_sec + dptr[j].dp_ssect, 33268358Snyan line, verbose); 33343561Skato break; 33443561Skato default: 33543561Skato } 33643561Skato } 33743561Skato#else 33843561Skato if ((dptr[3].dp_typ == DOSPTYP_386BSD) && 33943561Skato (dptr[3].dp_start == 0) && 34043561Skato (dptr[3].dp_size == 50000)) { 34143561Skato sprintf(line, " disk%d", i); 34268358Snyan bd_printbsdslice(od, 0, line, verbose); 34343561Skato } else { 34459777Snyan for (j = 0; j < od->od_nslices; j++) { 34559777Snyan sprintf(line, " disk%ds%d", i, j + 1); 34668358Snyan bd_printslice(od, &dptr[j], line, verbose); 34759777Snyan } 34859777Snyan } 34943561Skato#endif 35043561Skato } 35143561Skato bd_closedisk(od); 35243561Skato } 35343561Skato } 35443561Skato} 35543561Skato 35668358Snyan#ifndef PC98 35759777Snyan/* 35868358Snyan * Print information about slices on a disk. For the size calculations we 35968358Snyan * assume a 512 byte sector. 36059777Snyan */ 36143561Skatostatic void 36268358Snyanbd_printslice(struct open_disk *od, struct dos_partition *dp, char *prefix, 36368358Snyan int verbose) 36443561Skato{ 36559777Snyan char line[80]; 36659777Snyan 36759777Snyan switch (dp->dp_typ) { 36859777Snyan case DOSPTYP_386BSD: 36968358Snyan bd_printbsdslice(od, (daddr_t)dp->dp_start, prefix, verbose); 37059777Snyan return; 37159777Snyan case DOSPTYP_LINSWP: 37268358Snyan if (verbose) 37368358Snyan sprintf(line, "%s: Linux swap %.6dMB (%d - %d)\n", 37468358Snyan prefix, dp->dp_size / 2048, 37568358Snyan dp->dp_start, dp->dp_start + dp->dp_size); 37668358Snyan else 37768358Snyan sprintf(line, "%s: Linux swap\n", prefix); 37859777Snyan break; 37959777Snyan case DOSPTYP_LINUX: 38059777Snyan /* 38159777Snyan * XXX 38259777Snyan * read the superblock to confirm this is an ext2fs partition? 38359777Snyan */ 38468358Snyan if (verbose) 38568358Snyan sprintf(line, "%s: ext2fs %.6dMB (%d - %d)\n", prefix, 38668358Snyan dp->dp_size / 2048, dp->dp_start, 38768358Snyan dp->dp_start + dp->dp_size); 38868358Snyan else 38968358Snyan sprintf(line, "%s: ext2fs\n", prefix); 39059777Snyan break; 39159777Snyan case 0x00: /* unused partition */ 39259777Snyan case DOSPTYP_EXT: 39359777Snyan return; 39459777Snyan case 0x01: 39568358Snyan if (verbose) 39668358Snyan sprintf(line, "%s: FAT-12 %.6dMB (%d - %d)\n", prefix, 39768358Snyan dp->dp_size / 2048, dp->dp_start, 39868358Snyan dp->dp_start + dp->dp_size); 39968358Snyan else 40068358Snyan sprintf(line, "%s: FAT-12\n", prefix); 40159777Snyan break; 40259777Snyan case 0x04: 40359777Snyan case 0x06: 40459777Snyan case 0x0e: 40568358Snyan if (verbose) 40668358Snyan sprintf(line, "%s: FAT-16 %.6dMB (%d - %d)\n", prefix, 40768358Snyan dp->dp_size / 2048, dp->dp_start, 40868358Snyan dp->dp_start + dp->dp_size); 40968358Snyan else 41068358Snyan sprintf(line, "%s: FAT-16\n", prefix); 41159777Snyan break; 41259777Snyan case 0x0b: 41359777Snyan case 0x0c: 41468358Snyan if (verbose) 41568358Snyan sprintf(line, "%s: FAT-32 %.6dMB (%d - %d)\n", prefix, 41668358Snyan dp->dp_size / 2048, dp->dp_start, 41768358Snyan dp->dp_start + dp->dp_size); 41868358Snyan else 41968358Snyan sprintf(line, "%s: FAT-32\n", prefix); 42059777Snyan break; 42159777Snyan default: 42268358Snyan if (verbose) 42368358Snyan sprintf(line, "%s: Unknown fs: 0x%x %.6dMB (%d - %d)\n", 42468358Snyan prefix, dp->dp_typ, dp->dp_size / 2048, 42568358Snyan dp->dp_start, dp->dp_start + dp->dp_size); 42668358Snyan else 42768358Snyan sprintf(line, "%s: Unknown fs: 0x%x\n", prefix, 42868358Snyan dp->dp_typ); 42959777Snyan } 43059777Snyan pager_output(line); 43159777Snyan} 43259777Snyan#endif 43359777Snyan 43468358Snyan/* 43568358Snyan * Print out each valid partition in the disklabel of a FreeBSD slice. 43668358Snyan * For size calculations, we assume a 512 byte sector size. 43768358Snyan */ 43859777Snyanstatic void 43968358Snyanbd_printbsdslice(struct open_disk *od, daddr_t offset, char *prefix, 44068358Snyan int verbose) 44159777Snyan{ 44243561Skato char line[80]; 44368358Snyan char buf[BIOSDISK_SECSIZE]; 44443561Skato struct disklabel *lp; 44543561Skato int i; 44643561Skato 44743561Skato /* read disklabel */ 44843561Skato if (bd_read(od, offset + LABELSECTOR, 1, buf)) 44943561Skato return; 45043561Skato lp =(struct disklabel *)(&buf[0]); 45143561Skato if (lp->d_magic != DISKMAGIC) { 45263101Snyan sprintf(line, "%s: FFS bad disklabel\n", prefix); 45343561Skato pager_output(line); 45443561Skato return; 45543561Skato } 45643561Skato 45743561Skato /* Print partitions */ 45843561Skato for (i = 0; i < lp->d_npartitions; i++) { 45968358Snyan /* 46068358Snyan * For each partition, make sure we know what type of fs it is. If 46168358Snyan * not, then skip it. However, since floppies often have bogus 46268358Snyan * fstypes, print the 'a' partition on a floppy even if it is marked 46368358Snyan * unused. 46468358Snyan */ 46563101Snyan if ((lp->d_partitions[i].p_fstype == FS_BSDFFS) || 46663101Snyan (lp->d_partitions[i].p_fstype == FS_SWAP) || 46763101Snyan (lp->d_partitions[i].p_fstype == FS_VINUM) || 46843561Skato ((lp->d_partitions[i].p_fstype == FS_UNUSED) && 46968358Snyan (od->od_flags & BD_FLOPPY) && (i == 0))) { 47068358Snyan 47168358Snyan /* Only print out statistics in verbose mode */ 47268358Snyan if (verbose) 47368358Snyan sprintf(line, " %s%c: %s %.6dMB (%d - %d)\n", prefix, 'a' + i, 47463101Snyan (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : 47568358Snyan (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" : 47668358Snyan "FFS", 47768358Snyan lp->d_partitions[i].p_size / 2048, 47868358Snyan lp->d_partitions[i].p_offset, 47968358Snyan lp->d_partitions[i].p_offset + lp->d_partitions[i].p_size); 48068358Snyan else 48168358Snyan sprintf(line, " %s%c: %s\n", prefix, 'a' + i, 48268358Snyan (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : 48368358Snyan (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" : 48468358Snyan "FFS"); 48543561Skato pager_output(line); 48643561Skato } 48743561Skato } 48843561Skato} 48943561Skato 49043561Skato 49143561Skato/* 49243561Skato * Attempt to open the disk described by (dev) for use by (f). 49343561Skato * 49443561Skato * Note that the philosophy here is "give them exactly what 49543561Skato * they ask for". This is necessary because being too "smart" 49643561Skato * about what the user might want leads to complications. 49743561Skato * (eg. given no slice or partition value, with a disk that is 49843561Skato * sliced - are they after the first BSD slice, or the DOS 49943561Skato * slice before it?) 50043561Skato */ 50143561Skatostatic int 50243561Skatobd_open(struct open_file *f, ...) 50343561Skato{ 50443561Skato va_list ap; 50543561Skato struct i386_devdesc *dev; 50643561Skato struct open_disk *od; 50743561Skato int error; 50843561Skato 50943561Skato va_start(ap, f); 51043561Skato dev = va_arg(ap, struct i386_devdesc *); 51143561Skato va_end(ap); 51243561Skato if ((error = bd_opendisk(&od, dev))) 51343561Skato return(error); 51443561Skato 51543561Skato /* 51643561Skato * Save our context 51743561Skato */ 51843561Skato ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od; 51943561Skato DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff); 52043561Skato return(0); 52143561Skato} 52243561Skato 52343561Skatostatic int 52443561Skatobd_opendisk(struct open_disk **odp, struct i386_devdesc *dev) 52543561Skato{ 52643561Skato struct dos_partition *dptr; 52743561Skato struct disklabel *lp; 52843561Skato struct open_disk *od; 52943561Skato int sector, slice, i; 53043561Skato int error; 53168358Snyan char buf[BUFSIZE]; 53243561Skato 53343561Skato if (dev->d_kind.biosdisk.unit >= nbdinfo) { 53443561Skato DEBUG("attempt to open nonexistent disk"); 53543561Skato return(ENXIO); 53643561Skato } 53743561Skato 53843561Skato od = (struct open_disk *)malloc(sizeof(struct open_disk)); 53943561Skato if (!od) { 54043561Skato DEBUG("no memory"); 54143561Skato return (ENOMEM); 54243561Skato } 54343561Skato 54443561Skato /* Look up BIOS unit number, intialise open_disk structure */ 54543561Skato od->od_dkunit = dev->d_kind.biosdisk.unit; 54643561Skato od->od_unit = bdinfo[od->od_dkunit].bd_unit; 54743561Skato od->od_flags = bdinfo[od->od_dkunit].bd_flags; 54843561Skato od->od_boff = 0; 54959777Snyan od->od_nslices = 0; 55043561Skato error = 0; 55143561Skato DEBUG("open '%s', unit 0x%x slice %d partition %c", 55243561Skato i386_fmtdev(dev), dev->d_kind.biosdisk.unit, 55343561Skato dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition + 'a'); 55443561Skato 55543561Skato /* Get geometry for this open (removable device may have changed) */ 55643561Skato if (bd_getgeom(od)) { 55743561Skato DEBUG("can't get geometry"); 55843561Skato error = ENXIO; 55943561Skato goto out; 56043561Skato } 56143561Skato 56243561Skato /* 56343561Skato * Following calculations attempt to determine the correct value 56443561Skato * for d->od_boff by looking for the slice and partition specified, 56543561Skato * or searching for reasonable defaults. 56643561Skato */ 56743561Skato 56843561Skato /* 56943561Skato * Find the slice in the DOS slice table. 57043561Skato */ 57143561Skato#ifdef PC98 57243561Skato if (od->od_flags & BD_FLOPPY) { 57343561Skato sector = 0; 57443561Skato goto unsliced; 57543561Skato } 57643561Skato#endif 57743561Skato if (bd_read(od, 0, 1, buf)) { 57843561Skato DEBUG("error reading MBR"); 57943561Skato error = EIO; 58043561Skato goto out; 58143561Skato } 58243561Skato 58343561Skato /* 58443561Skato * Check the slice table magic. 58543561Skato */ 58668358Snyan if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { 58743561Skato /* If a slice number was explicitly supplied, this is an error */ 58843561Skato if (dev->d_kind.biosdisk.slice > 0) { 58943561Skato DEBUG("no slice table/MBR (no magic)"); 59043561Skato error = ENOENT; 59143561Skato goto out; 59243561Skato } 59343561Skato sector = 0; 59443561Skato goto unsliced; /* may be a floppy */ 59543561Skato } 59643561Skato#ifdef PC98 59743561Skato if (bd_read(od, 1, 1, buf)) { 59843561Skato DEBUG("error reading MBR"); 59943561Skato error = EIO; 60043561Skato goto out; 60143561Skato } 60243561Skato#endif 60359777Snyan 60459777Snyan /* 60559777Snyan * copy the partition table, then pick up any extended partitions. 60659777Snyan */ 60759777Snyan bcopy(buf + DOSPARTOFF, &od->od_slicetab, 60859777Snyan sizeof(struct dos_partition) * NDOSPART); 60963101Snyan#ifdef PC98 61059777Snyan od->od_nslices = NDOSPART; /* extended slices start here */ 61163101Snyan#else 61263101Snyan od->od_nslices = 4; /* extended slices start here */ 61359777Snyan for (i = 0; i < NDOSPART; i++) 61459777Snyan bd_checkextended(od, i); 61559777Snyan#endif 61643561Skato od->od_flags |= BD_PARTTABOK; 61759777Snyan dptr = &od->od_slicetab[0]; 61843561Skato 61943561Skato /* Is this a request for the whole disk? */ 62043561Skato if (dev->d_kind.biosdisk.slice == -1) { 62143561Skato sector = 0; 62243561Skato goto unsliced; 62343561Skato } 62443561Skato 62559777Snyan /* 62659777Snyan * if a slice number was supplied but not found, this is an error. 62759777Snyan */ 62859777Snyan if (dev->d_kind.biosdisk.slice > 0) { 62959777Snyan slice = dev->d_kind.biosdisk.slice - 1; 63059777Snyan if (slice >= od->od_nslices) { 63159777Snyan DEBUG("slice %d not found", slice); 63259777Snyan error = ENOENT; 63359777Snyan goto out; 63459777Snyan } 63559777Snyan } 63643561Skato 63759777Snyan#ifndef PC98 63859777Snyan /* 63959777Snyan * Check for the historically bogus MBR found on true dedicated disks 64059777Snyan */ 64159777Snyan if ((dptr[3].dp_typ == DOSPTYP_386BSD) && 64259777Snyan (dptr[3].dp_start == 0) && 64359777Snyan (dptr[3].dp_size == 50000)) { 64459777Snyan sector = 0; 64559777Snyan goto unsliced; 64643561Skato } 64759777Snyan#endif 64843561Skato 64959777Snyan /* Try to auto-detect the best slice; this should always give a slice number */ 65059777Snyan if (dev->d_kind.biosdisk.slice == 0) { 65159777Snyan slice = bd_bestslice(od); 65259777Snyan if (slice == -1) { 65359777Snyan error = ENOENT; 65459777Snyan goto out; 65559777Snyan } 65659777Snyan dev->d_kind.biosdisk.slice = slice; 65759777Snyan } 65859777Snyan 65959777Snyan dptr = &od->od_slicetab[0]; 66043561Skato /* 66143561Skato * Accept the supplied slice number unequivocally (we may be looking 66243561Skato * at a DOS partition). 66343561Skato */ 66443561Skato dptr += (dev->d_kind.biosdisk.slice - 1); /* we number 1-4, offsets are 0-3 */ 66543561Skato#ifdef PC98 66659777Snyan sector = dptr->dp_scyl * od->od_hds * od->od_sec + 66759777Snyan dptr->dp_shd * od->od_sec + dptr->dp_ssect; 66843561Skato { 66959777Snyan int end = dptr->dp_ecyl * od->od_hds * od->od_sec + 67059777Snyan dptr->dp_ehd * od->od_sec + dptr->dp_esect; 67159777Snyan DEBUG("slice entry %d at %d, %d sectors", 67259777Snyan dev->d_kind.biosdisk.slice - 1, sector, end-sector); 67343561Skato } 67443561Skato#else 67543561Skato sector = dptr->dp_start; 67643561Skato DEBUG("slice entry %d at %d, %d sectors", dev->d_kind.biosdisk.slice - 1, sector, dptr->dp_size); 67743561Skato#endif 67843561Skato 67943561Skato /* 68043561Skato * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition 68143561Skato */ 68243561Skato#ifdef PC98 68343561Skato if ((dptr->dp_mid == DOSMID_386BSD) && (dev->d_kind.biosdisk.partition < 0)) 68443561Skato#else 68543561Skato if ((dptr->dp_typ == DOSPTYP_386BSD) && (dev->d_kind.biosdisk.partition < 0)) 68643561Skato#endif 68743561Skato dev->d_kind.biosdisk.partition = 0; 68843561Skato 68943561Skato unsliced: 69043561Skato /* 69143561Skato * Now we have the slice offset, look for the partition in the disklabel if we have 69243561Skato * a partition to start with. 69343561Skato * 69443561Skato * XXX we might want to check the label checksum. 69543561Skato */ 69643561Skato if (dev->d_kind.biosdisk.partition < 0) { 69743561Skato od->od_boff = sector; /* no partition, must be after the slice */ 69843561Skato DEBUG("opening raw slice"); 69943561Skato } else { 70053207Snyan 70143561Skato if (bd_read(od, sector + LABELSECTOR, 1, buf)) { 70243561Skato DEBUG("error reading disklabel"); 70343561Skato error = EIO; 70443561Skato goto out; 70543561Skato } 70643561Skato DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel); 70743561Skato bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel)); 70843561Skato lp = &od->od_disklabel; 70943561Skato od->od_flags |= BD_LABELOK; 71043561Skato 71143561Skato if (lp->d_magic != DISKMAGIC) { 71243561Skato DEBUG("no disklabel"); 71343561Skato error = ENOENT; 71443561Skato goto out; 71543561Skato } 71643561Skato if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) { 71743561Skato DEBUG("partition '%c' exceeds partitions in table (a-'%c')", 71843561Skato 'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions); 71943561Skato error = EPART; 72043561Skato goto out; 72143561Skato 72243561Skato } 72343561Skato 72468358Snyan#ifdef DISK_DEBUG 72568358Snyan /* Complain if the partition is unused unless this is a floppy. */ 72643561Skato if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) && 72768358Snyan !(od->od_flags & BD_FLOPPY)) 72843561Skato DEBUG("warning, partition marked as unused"); 72968358Snyan#endif 73043561Skato 73143561Skato od->od_boff = lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset; 73243561Skato } 73343561Skato 73443561Skato out: 73543561Skato if (error) { 73643561Skato free(od); 73743561Skato } else { 73843561Skato *odp = od; /* return the open disk */ 73943561Skato } 74043561Skato return(error); 74143561Skato} 74243561Skato 74359777Snyan#ifndef PC98 74459777Snyanstatic void 74559777Snyanbd_checkextended(struct open_disk *od, int slicenum) 74659777Snyan{ 74768358Snyan char buf[BIOSDISK_SECSIZE]; 74859777Snyan struct dos_partition *dp; 74959777Snyan u_int base; 75059777Snyan int i, start, end; 75143561Skato 75259777Snyan dp = &od->od_slicetab[slicenum]; 75359777Snyan start = od->od_nslices; 75459777Snyan 75559777Snyan if (dp->dp_size == 0) 75659777Snyan goto done; 75759777Snyan if (dp->dp_typ != DOSPTYP_EXT) 75859777Snyan goto done; 75968358Snyan if (bd_read(od, (daddr_t)dp->dp_start, 1, buf)) 76059777Snyan goto done; 76168358Snyan if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { 76259777Snyan DEBUG("no magic in extended table"); 76359777Snyan goto done; 76459777Snyan } 76559777Snyan base = dp->dp_start; 76659777Snyan dp = (struct dos_partition *)(&buf[DOSPARTOFF]); 76759777Snyan for (i = 0; i < NDOSPART; i++, dp++) { 76859777Snyan if (dp->dp_size == 0) 76959777Snyan continue; 77059777Snyan if (od->od_nslices == MAX_SLICES) 77159777Snyan goto done; 77259777Snyan dp->dp_start += base; 77359777Snyan bcopy(dp, &od->od_slicetab[od->od_nslices], sizeof(*dp)); 77459777Snyan od->od_nslices++; 77559777Snyan } 77659777Snyan end = od->od_nslices; 77759777Snyan 77859777Snyan /* 77959777Snyan * now, recursively check the slices we just added 78059777Snyan */ 78159777Snyan for (i = start; i < end; i++) 78259777Snyan bd_checkextended(od, i); 78359777Snyandone: 78459777Snyan return; 78559777Snyan} 78659777Snyan#endif 78759777Snyan 78843561Skato/* 78943561Skato * Search for a slice with the following preferences: 79043561Skato * 79143561Skato * 1: Active FreeBSD slice 79243561Skato * 2: Non-active FreeBSD slice 79359777Snyan * 3: Active Linux slice 79459777Snyan * 4: non-active Linux slice 79559777Snyan * 5: Active FAT/FAT32 slice 79659777Snyan * 6: non-active FAT/FAT32 slice 79743561Skato */ 79859777Snyan#define PREF_RAWDISK 0 79959777Snyan#define PREF_FBSD_ACT 1 80059777Snyan#define PREF_FBSD 2 80159777Snyan#define PREF_LINUX_ACT 3 80259777Snyan#define PREF_LINUX 4 80359777Snyan#define PREF_DOS_ACT 5 80459777Snyan#define PREF_DOS 6 80559777Snyan#define PREF_NONE 7 80643561Skato 80759777Snyan/* 80859777Snyan * slicelimit is in the range 0 .. NDOSPART 80959777Snyan */ 81043561Skatostatic int 81159777Snyanbd_bestslice(struct open_disk *od) 81243561Skato{ 81359777Snyan struct dos_partition *dp; 81459777Snyan int pref, preflevel; 81559777Snyan int i, prefslice; 81653207Snyan 81759777Snyan prefslice = 0; 81859777Snyan preflevel = PREF_NONE; 81943561Skato 82059777Snyan dp = &od->od_slicetab[0]; 82159777Snyan for (i = 0; i < od->od_nslices; i++, dp++) { 82287734Snyan 82343561Skato#ifdef PC98 82459777Snyan switch(dp->dp_mid & 0x7f) { 82559777Snyan case DOSMID_386BSD & 0x7f: /* FreeBSD */ 82659777Snyan if ((dp->dp_mid & 0x80) && 82759777Snyan (preflevel > PREF_FBSD_ACT)) { 82859777Snyan pref = i; 82959777Snyan preflevel = PREF_FBSD_ACT; 83059777Snyan } else if (preflevel > PREF_FBSD) { 83159777Snyan pref = i; 83259777Snyan preflevel = PREF_FBSD; 83359777Snyan } 83459777Snyan break; 83559777Snyan 83659777Snyan case 0x11: /* DOS/Windows */ 83759777Snyan case 0x20: 83859777Snyan case 0x21: 83959777Snyan case 0x22: 84059777Snyan case 0x23: 84159777Snyan case 0x63: 84259777Snyan if ((dp->dp_mid & 0x80) && 84359777Snyan (preflevel > PREF_DOS_ACT)) { 84459777Snyan pref = i; 84559777Snyan preflevel = PREF_DOS_ACT; 84659777Snyan } else if (preflevel > PREF_DOS) { 84759777Snyan pref = i; 84859777Snyan preflevel = PREF_DOS; 84959777Snyan } 85059777Snyan break; 85159777Snyan } 85243561Skato#else 85359777Snyan switch (dp->dp_typ) { 85459777Snyan case DOSPTYP_386BSD: /* FreeBSD */ 85559777Snyan pref = dp->dp_flag & 0x80 ? PREF_FBSD_ACT : PREF_FBSD; 85659777Snyan break; 85759777Snyan 85859777Snyan case DOSPTYP_LINUX: 85959777Snyan pref = dp->dp_flag & 0x80 ? PREF_LINUX_ACT : PREF_LINUX; 86059777Snyan break; 86159777Snyan 86259777Snyan case 0x01: /* DOS/Windows */ 86359777Snyan case 0x04: 86459777Snyan case 0x06: 86559777Snyan case 0x0b: 86659777Snyan case 0x0c: 86759777Snyan case 0x0e: 86859777Snyan pref = dp->dp_flag & 0x80 ? PREF_DOS_ACT : PREF_DOS; 86959777Snyan break; 87059777Snyan 87159777Snyan default: 87259777Snyan pref = PREF_NONE; 87359777Snyan } 87459777Snyan if (pref < preflevel) { 87559777Snyan preflevel = pref; 87659777Snyan prefslice = i + 1; 87759777Snyan } 87859777Snyan#endif 87943561Skato } 88059777Snyan return (prefslice); 88143561Skato} 88287734Snyan 88343561Skatostatic int 88443561Skatobd_close(struct open_file *f) 88543561Skato{ 88643561Skato struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data); 88743561Skato 88843561Skato bd_closedisk(od); 88943561Skato return(0); 89043561Skato} 89143561Skato 89243561Skatostatic void 89343561Skatobd_closedisk(struct open_disk *od) 89443561Skato{ 89543561Skato DEBUG("open_disk %p", od); 89643561Skato#if 0 89743561Skato /* XXX is this required? (especially if disk already open...) */ 89843561Skato if (od->od_flags & BD_FLOPPY) 89943561Skato delay(3000000); 90043561Skato#endif 90143561Skato free(od); 90243561Skato} 90343561Skato 90443561Skatostatic int 90568358Snyanbd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) 90643561Skato{ 90743561Skato struct bcache_devdata bcd; 90858165Snyan struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data); 90958165Snyan 91043561Skato bcd.dv_strategy = bd_realstrategy; 91143561Skato bcd.dv_devdata = devdata; 91258165Snyan return(bcache_strategy(&bcd, od->od_unit, rw, dblk+od->od_boff, size, buf, rsize)); 91343561Skato} 91443561Skato 91543561Skatostatic int 91668358Snyanbd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) 91743561Skato{ 91843561Skato struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data); 91943561Skato int blks; 92043561Skato#ifdef BD_SUPPORT_FRAGS 92143561Skato char fragbuf[BIOSDISK_SECSIZE]; 92243561Skato size_t fragsize; 92343561Skato 92443561Skato fragsize = size % BIOSDISK_SECSIZE; 92543561Skato#else 92643561Skato if (size % BIOSDISK_SECSIZE) 92743561Skato panic("bd_strategy: %d bytes I/O not multiple of block size", size); 92843561Skato#endif 92943561Skato 93043561Skato DEBUG("open_disk %p", od); 93143561Skato 93243561Skato 93387734Snyan switch(rw){ 93487734Snyan case F_READ: 93543561Skato blks = size / BIOSDISK_SECSIZE; 93658165Snyan DEBUG("read %d from %d to %p", blks, dblk, buf); 93743561Skato 93843561Skato if (rsize) 93943561Skato *rsize = 0; 94058165Snyan if (blks && bd_read(od, dblk, blks, buf)) { 94143561Skato DEBUG("read error"); 94243561Skato return (EIO); 94343561Skato } 94443561Skato#ifdef BD_SUPPORT_FRAGS 94558165Snyan DEBUG("bd_strategy: frag read %d from %d+%d to %p", 94658165Snyan fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE)); 94758165Snyan if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) { 94843561Skato DEBUG("frag read error"); 94943561Skato return(EIO); 95043561Skato } 95143561Skato bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize); 95243561Skato#endif 95343561Skato if (rsize) 95443561Skato *rsize = size; 95543561Skato return (0); 95687734Snyan break; 95787734Snyan 95887734Snyan case F_WRITE : 95987734Snyan blks = size / BIOSDISK_SECSIZE; 96087734Snyan DEBUG("write %d from %d to %p", blks, dblk, buf); 96187734Snyan 96287734Snyan if (rsize) 96387734Snyan *rsize = 0; 96487734Snyan if (blks && bd_write(od, dblk, blks, buf)) { 96587734Snyan DEBUG("write error"); 96687734Snyan return (EIO); 96787734Snyan } 96887734Snyan#ifdef BD_SUPPORT_FRAGS 96987734Snyan if(fragsize) { 97087734Snyan DEBUG("Attempted to write a frag"); 97187734Snyan return (EIO); 97287734Snyan } 97387734Snyan#endif 97487734Snyan 97587734Snyan if (rsize) 97687734Snyan *rsize = size; 97787734Snyan return (0); 97887734Snyan default: 97987734Snyan /* DO NOTHING */ 98087734Snyan } 98187734Snyan 98287734Snyan return EROFS; 98343561Skato} 98443561Skato 98543561Skato/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */ 98643561Skato#define FLOPPY_BOUNCEBUF 18 98743561Skato 98843561Skatostatic int 98943561Skatobd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) 99043561Skato{ 99168358Snyan u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer; 99243561Skato caddr_t p, xp, bbuf, breg; 99343561Skato 99468358Snyan /* Just in case some idiot actually tries to read -1 blocks... */ 99568358Snyan if (blks < 0) 99668358Snyan return (-1); 99768358Snyan 99843561Skato bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ 99943561Skato resid = blks; 100043561Skato p = dest; 100143561Skato 100243561Skato /* Decide whether we have to bounce */ 100343561Skato#ifdef PC98 100451586Skato if ( 100543561Skato#else 100643561Skato if ((od->od_unit < 0x80) && 100743561Skato#endif 100843561Skato ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) { 100943561Skato 101043561Skato /* 101143561Skato * There is a 64k physical boundary somewhere in the destination buffer, so we have 101243561Skato * to arrange a suitable bounce buffer. Allocate a buffer twice as large as we 101343561Skato * need to. Use the bottom half unless there is a break there, in which case we 101443561Skato * use the top half. 101543561Skato */ 101651586Skato#ifdef PC98 101768358Snyan x = min(od->od_sec, (unsigned)blks); 101851586Skato#else 101968358Snyan x = min(FLOPPY_BOUNCEBUF, (unsigned)blks); 102051586Skato#endif 102143561Skato bbuf = malloc(x * 2 * BIOSDISK_SECSIZE); 102243561Skato if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(dest + x * BIOSDISK_SECSIZE) & 0xffff0000)) { 102343561Skato breg = bbuf; 102443561Skato } else { 102543561Skato breg = bbuf + x * BIOSDISK_SECSIZE; 102643561Skato } 102743561Skato maxfer = x; /* limit transfers to bounce region size */ 102843561Skato } else { 102968358Snyan breg = bbuf = NULL; 103043561Skato maxfer = 0; 103143561Skato } 103243561Skato 103343561Skato while (resid > 0) { 103443561Skato x = dblk; 103543561Skato cyl = x / bpc; /* block # / blocks per cylinder */ 103643561Skato x %= bpc; /* block offset into cylinder */ 103743561Skato hd = x / od->od_sec; /* offset / blocks per track */ 103843561Skato sec = x % od->od_sec; /* offset into track */ 103943561Skato 104043561Skato /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */ 104143561Skato x = min(od->od_sec - sec, resid); 104243561Skato if (maxfer > 0) 104343561Skato x = min(x, maxfer); /* fit bounce buffer */ 104443561Skato 104543561Skato /* where do we transfer to? */ 104643561Skato xp = bbuf == NULL ? p : breg; 104743561Skato 104843561Skato /* correct sector number for 1-based BIOS numbering */ 104943561Skato#ifdef PC98 105043561Skato if ((od->od_unit & 0xf0) == 0x30 || (od->od_unit & 0xf0) == 0x90) 105143561Skato sec++; 105243561Skato#else 105343561Skato sec++; 105443561Skato#endif 105543561Skato 105643561Skato /* Loop retrying the operation a couple of times. The BIOS may also retry. */ 105743561Skato for (retry = 0; retry < 3; retry++) { 105843561Skato /* if retrying, reset the drive */ 105943561Skato if (retry > 0) { 106043561Skato#ifdef PC98 106151586Skato v86.ctl = V86_FLAGS; 106251586Skato v86.addr = 0x1b; 106351586Skato v86.eax = 0x0300 | od->od_unit; 106443561Skato#else 106543561Skato v86.ctl = V86_FLAGS; 106643561Skato v86.addr = 0x13; 106743561Skato v86.eax = 0; 106843561Skato v86.edx = od->od_unit; 106951586Skato#endif 107043561Skato v86int(); 107143561Skato } 107243561Skato 107363101Snyan#ifdef PC98 107451586Skato v86.ctl = V86_FLAGS; 107543561Skato v86.addr = 0x1b; 107643561Skato if (od->od_flags & BD_FLOPPY) { 107743561Skato v86.eax = 0xd600 | od->od_unit; 107843561Skato v86.ecx = 0x0200 | (cyl & 0xff); 107943561Skato } 108043561Skato else { 108143561Skato v86.eax = 0x0600 | od->od_unit; 108243561Skato v86.ecx = cyl; 108343561Skato } 108443561Skato v86.edx = (hd << 8) | sec; 108543561Skato v86.ebx = x * BIOSDISK_SECSIZE; 108643561Skato v86.es = VTOPSEG(xp); 108743561Skato v86.ebp = VTOPOFF(xp); 108843561Skato v86int(); 108943561Skato result = (v86.efl & 0x1); 109043561Skato if (result == 0) 109143561Skato break; 109263101Snyan#else 109363101Snyan if(cyl > 1023) { 109463101Snyan /* use EDD if the disk supports it, otherwise, return error */ 109563101Snyan if(od->od_flags & BD_MODEEDD1) { 109663101Snyan static unsigned short packet[8]; 109763101Snyan 109863101Snyan packet[0] = 0x10; 109963101Snyan packet[1] = x; 110063101Snyan packet[2] = VTOPOFF(xp); 110163101Snyan packet[3] = VTOPSEG(xp); 110263101Snyan packet[4] = dblk & 0xffff; 110363101Snyan packet[5] = dblk >> 16; 110463101Snyan packet[6] = 0; 110563101Snyan packet[7] = 0; 110663101Snyan v86.ctl = V86_FLAGS; 110763101Snyan v86.addr = 0x13; 110863101Snyan v86.eax = 0x4200; 110963101Snyan v86.edx = od->od_unit; 111063101Snyan v86.ds = VTOPSEG(packet); 111163101Snyan v86.esi = VTOPOFF(packet); 111263101Snyan v86int(); 111363101Snyan result = (v86.efl & 0x1); 111463101Snyan if(result == 0) 111563101Snyan break; 111663101Snyan } else { 111763101Snyan result = 1; 111863101Snyan break; 111963101Snyan } 112063101Snyan } else { 112163101Snyan /* Use normal CHS addressing */ 112263101Snyan v86.ctl = V86_FLAGS; 112363101Snyan v86.addr = 0x13; 112463101Snyan v86.eax = 0x200 | x; 112563101Snyan v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; 112663101Snyan v86.edx = (hd << 8) | od->od_unit; 112763101Snyan v86.es = VTOPSEG(xp); 112863101Snyan v86.ebx = VTOPOFF(xp); 112963101Snyan v86int(); 113063101Snyan result = (v86.efl & 0x1); 113163101Snyan if (result == 0) 113263101Snyan break; 113363101Snyan } 113463101Snyan#endif 113543561Skato } 113643561Skato 113743561Skato#ifdef PC98 113843561Skato 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"); 113943561Skato /* BUG here, cannot use v86 in printf because putchar uses it too */ 114043561Skato DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 114143561Skato od->od_flags & BD_FLOPPY ? 0xd600 | od->od_unit : 0x0600 | od->od_unit, 114243561Skato od->od_flags & BD_FLOPPY ? 0x0200 | cyl : cyl, (hd << 8) | sec, 114343561Skato (v86.eax >> 8) & 0xff); 114443561Skato#else 114543561Skato DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok"); 114643561Skato /* BUG here, cannot use v86 in printf because putchar uses it too */ 114743561Skato DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 114843561Skato 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff); 114943561Skato#endif 115043561Skato if (result) { 115143561Skato if (bbuf != NULL) 115243561Skato free(bbuf); 115343561Skato return(-1); 115443561Skato } 115543561Skato if (bbuf != NULL) 115643561Skato bcopy(breg, p, x * BIOSDISK_SECSIZE); 115743561Skato p += (x * BIOSDISK_SECSIZE); 115843561Skato dblk += x; 115943561Skato resid -= x; 116043561Skato } 116143561Skato 116243561Skato/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */ 116343561Skato if (bbuf != NULL) 116443561Skato free(bbuf); 116543561Skato return(0); 116643561Skato} 116743561Skato 116887734Snyan 116943561Skatostatic int 117087734Snyanbd_write(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) 117187734Snyan{ 117287734Snyan u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer; 117387734Snyan caddr_t p, xp, bbuf, breg; 117487734Snyan 117587734Snyan /* Just in case some idiot actually tries to read -1 blocks... */ 117687734Snyan if (blks < 0) 117787734Snyan return (-1); 117887734Snyan 117987734Snyan bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ 118087734Snyan resid = blks; 118187734Snyan p = dest; 118287734Snyan 118387734Snyan /* Decide whether we have to bounce */ 118487734Snyan#ifdef PC98 118587734Snyan if ( 118687734Snyan#else 118787734Snyan if ((od->od_unit < 0x80) && 118887734Snyan#endif 118987734Snyan ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) { 119087734Snyan 119187734Snyan /* 119287734Snyan * There is a 64k physical boundary somewhere in the destination buffer, so we have 119387734Snyan * to arrange a suitable bounce buffer. Allocate a buffer twice as large as we 119487734Snyan * need to. Use the bottom half unless there is a break there, in which case we 119587734Snyan * use the top half. 119687734Snyan */ 119787734Snyan 119887734Snyan#ifdef PC98 119987734Snyan x = min(od->od_sec, (unsigned)blks); 120087734Snyan#else 120187734Snyan x = min(FLOPPY_BOUNCEBUF, (unsigned)blks); 120287734Snyan#endif 120387734Snyan bbuf = malloc(x * 2 * BIOSDISK_SECSIZE); 120487734Snyan if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(dest + x * BIOSDISK_SECSIZE) & 0xffff0000)) { 120587734Snyan breg = bbuf; 120687734Snyan } else { 120787734Snyan breg = bbuf + x * BIOSDISK_SECSIZE; 120887734Snyan } 120987734Snyan maxfer = x; /* limit transfers to bounce region size */ 121087734Snyan } else { 121187734Snyan breg = bbuf = NULL; 121287734Snyan maxfer = 0; 121387734Snyan } 121487734Snyan 121587734Snyan while (resid > 0) { 121687734Snyan x = dblk; 121787734Snyan cyl = x / bpc; /* block # / blocks per cylinder */ 121887734Snyan x %= bpc; /* block offset into cylinder */ 121987734Snyan hd = x / od->od_sec; /* offset / blocks per track */ 122087734Snyan sec = x % od->od_sec; /* offset into track */ 122187734Snyan 122287734Snyan /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */ 122387734Snyan x = min(od->od_sec - sec, resid); 122487734Snyan if (maxfer > 0) 122587734Snyan x = min(x, maxfer); /* fit bounce buffer */ 122687734Snyan 122787734Snyan /* where do we transfer to? */ 122887734Snyan xp = bbuf == NULL ? p : breg; 122987734Snyan 123087734Snyan /* correct sector number for 1-based BIOS numbering */ 123187734Snyan#ifdef PC98 123287734Snyan if ((od->od_unit & 0xf0) == 0x30 || (od->od_unit & 0xf0) == 0x90) 123387734Snyan sec++; 123487734Snyan#else 123587734Snyan sec++; 123687734Snyan#endif 123787734Snyan 123887734Snyan 123987734Snyan /* Put your Data In, Put your Data out, 124087734Snyan Put your Data In, and shake it all about 124187734Snyan */ 124287734Snyan if (bbuf != NULL) 124387734Snyan bcopy(p, breg, x * BIOSDISK_SECSIZE); 124487734Snyan p += (x * BIOSDISK_SECSIZE); 124587734Snyan dblk += x; 124687734Snyan resid -= x; 124787734Snyan 124887734Snyan /* Loop retrying the operation a couple of times. The BIOS may also retry. */ 124987734Snyan for (retry = 0; retry < 3; retry++) { 125087734Snyan /* if retrying, reset the drive */ 125187734Snyan if (retry > 0) { 125287734Snyan#ifdef PC98 125387734Snyan v86.ctl = V86_FLAGS; 125487734Snyan v86.addr = 0x1b; 125587734Snyan v86.eax = 0x0300 | od->od_unit; 125687734Snyan#else 125787734Snyan v86.ctl = V86_FLAGS; 125887734Snyan v86.addr = 0x13; 125987734Snyan v86.eax = 0; 126087734Snyan v86.edx = od->od_unit; 126187734Snyan#endif 126287734Snyan v86int(); 126387734Snyan } 126487734Snyan 126587734Snyan#ifdef PC98 126687734Snyan v86.ctl = V86_FLAGS; 126787734Snyan v86.addr = 0x1b; 126887734Snyan if (od->od_flags & BD_FLOPPY) { 126987734Snyan v86.eax = 0xd500 | od->od_unit; 127087734Snyan v86.ecx = 0x0200 | (cyl & 0xff); 127187734Snyan } else { 127287734Snyan v86.eax = 0x0500 | od->od_unit; 127387734Snyan v86.ecx = cyl; 127487734Snyan } 127587734Snyan v86.edx = (hd << 8) | sec; 127687734Snyan v86.ebx = x * BIOSDISK_SECSIZE; 127787734Snyan v86.es = VTOPSEG(xp); 127887734Snyan v86.ebp = VTOPOFF(xp); 127987734Snyan v86int(); 128087734Snyan result = (v86.efl & 0x1); 128187734Snyan if (result == 0) 128287734Snyan break; 128387734Snyan#else 128487734Snyan if(cyl > 1023) { 128587734Snyan /* use EDD if the disk supports it, otherwise, return error */ 128687734Snyan if(od->od_flags & BD_MODEEDD1) { 128787734Snyan static unsigned short packet[8]; 128887734Snyan 128987734Snyan packet[0] = 0x10; 129087734Snyan packet[1] = x; 129187734Snyan packet[2] = VTOPOFF(xp); 129287734Snyan packet[3] = VTOPSEG(xp); 129387734Snyan packet[4] = dblk & 0xffff; 129487734Snyan packet[5] = dblk >> 16; 129587734Snyan packet[6] = 0; 129687734Snyan packet[7] = 0; 129787734Snyan v86.ctl = V86_FLAGS; 129887734Snyan v86.addr = 0x13; 129987734Snyan /* Should we Write with verify ?? 0x4302 ? */ 130087734Snyan v86.eax = 0x4300; 130187734Snyan v86.edx = od->od_unit; 130287734Snyan v86.ds = VTOPSEG(packet); 130387734Snyan v86.esi = VTOPOFF(packet); 130487734Snyan v86int(); 130587734Snyan result = (v86.efl & 0x1); 130687734Snyan if(result == 0) 130787734Snyan break; 130887734Snyan } else { 130987734Snyan result = 1; 131087734Snyan break; 131187734Snyan } 131287734Snyan } else { 131387734Snyan /* Use normal CHS addressing */ 131487734Snyan v86.ctl = V86_FLAGS; 131587734Snyan v86.addr = 0x13; 131687734Snyan v86.eax = 0x300 | x; 131787734Snyan v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; 131887734Snyan v86.edx = (hd << 8) | od->od_unit; 131987734Snyan v86.es = VTOPSEG(xp); 132087734Snyan v86.ebx = VTOPOFF(xp); 132187734Snyan v86int(); 132287734Snyan result = (v86.efl & 0x1); 132387734Snyan if (result == 0) 132487734Snyan break; 132587734Snyan } 132687734Snyan#endif 132787734Snyan } 132887734Snyan 132987734Snyan#ifdef PC98 133087734Snyan DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, 133187734Snyan od->od_flags & BD_FLOPPY ? sec - 1 : sec, p, VTOP(p), 133287734Snyan result ? "failed" : "ok"); 133387734Snyan /* BUG here, cannot use v86 in printf because putchar uses it too */ 133487734Snyan DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 133587734Snyan od->od_flags & BD_FLOPPY ? 0xd600 | od->od_unit : 0x0600 | od->od_unit, 133687734Snyan od->od_flags & BD_FLOPPY ? 0x0200 | cyl : cyl, (hd << 8) | sec, 133787734Snyan (v86.eax >> 8) & 0xff); 133887734Snyan#else 133987734Snyan DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok"); 134087734Snyan /* BUG here, cannot use v86 in printf because putchar uses it too */ 134187734Snyan DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 134287734Snyan 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff); 134387734Snyan#endif 134487734Snyan if (result) { 134587734Snyan if (bbuf != NULL) 134687734Snyan free(bbuf); 134787734Snyan return(-1); 134887734Snyan } 134987734Snyan } 135087734Snyan 135187734Snyan/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */ 135287734Snyan if (bbuf != NULL) 135387734Snyan free(bbuf); 135487734Snyan return(0); 135587734Snyan} 135687734Snyanstatic int 135743561Skatobd_getgeom(struct open_disk *od) 135843561Skato{ 135943561Skato 136043561Skato#ifdef PC98 136143561Skato if (od->od_flags & BD_FLOPPY) { 136254819Snyan od->od_cyl = 79; 136343561Skato od->od_hds = 2; 136443561Skato od->od_sec = (od->od_unit & 0xf0) == 0x30 ? 18 : 15; 136559777Snyan } else { 136654819Snyan v86.ctl = V86_FLAGS; 136743561Skato v86.addr = 0x1b; 136843561Skato v86.eax = 0x8400 | od->od_unit; 136943561Skato v86int(); 137043561Skato 137143561Skato od->od_cyl = v86.ecx; 137243561Skato od->od_hds = (v86.edx >> 8) & 0xff; 137343561Skato od->od_sec = v86.edx & 0xff; 137451586Skato if (v86.efl & 0x1) 137551586Skato return(1); 137643561Skato } 137743561Skato#else 137843561Skato v86.ctl = V86_FLAGS; 137943561Skato v86.addr = 0x13; 138043561Skato v86.eax = 0x800; 138143561Skato v86.edx = od->od_unit; 138243561Skato v86int(); 138343561Skato 138443561Skato if ((v86.efl & 0x1) || /* carry set */ 138568358Snyan ((v86.edx & 0xff) <= (unsigned)(od->od_unit & 0x7f))) /* unit # bad */ 138643561Skato return(1); 138743561Skato 138843561Skato /* convert max cyl # -> # of cylinders */ 138943561Skato od->od_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1; 139043561Skato /* convert max head # -> # of heads */ 139143561Skato od->od_hds = ((v86.edx & 0xff00) >> 8) + 1; 139243561Skato od->od_sec = v86.ecx & 0x3f; 139343561Skato#endif 139443561Skato 139543561Skato DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec); 139643561Skato return(0); 139743561Skato} 139843561Skato 139943561Skato/* 140053207Snyan * Return the BIOS geometry of a given "fixed drive" in a format 140153207Snyan * suitable for the legacy bootinfo structure. Since the kernel is 140253207Snyan * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we 140353207Snyan * prefer to get the information directly, rather than rely on being 140453207Snyan * able to put it together from information already maintained for 140553207Snyan * different purposes and for a probably different number of drives. 140653207Snyan * 140753207Snyan * For valid drives, the geometry is expected in the format (31..0) 140853207Snyan * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are 140953207Snyan * indicated by returning the geometry of a "1.2M" PC-format floppy 141053207Snyan * disk. And, incidentally, what is returned is not the geometry as 141153207Snyan * such but the highest valid cylinder, head, and sector numbers. 141253207Snyan */ 141353207Snyanu_int32_t 141453207Snyanbd_getbigeom(int bunit) 141553207Snyan{ 141653207Snyan 141754819Snyan#ifdef PC98 141854819Snyan int hds = 0; 141955339Snyan int unit = 0x80; /* IDE HDD */ 142054819Snyan u_int addr = 0xA155d; 142155339Snyan 142254819Snyan while (unit < 0xa7) { 142355339Snyan if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f))) 142455339Snyan if (hds++ == bunit) 142554819Snyan break; 142654819Snyan if (++unit == 0x84) { 142755339Snyan unit = 0xa0; /* SCSI HDD */ 142854819Snyan addr = 0xA1482; 142954819Snyan } 143054819Snyan } 143154819Snyan if (unit == 0xa7) 143254819Snyan return 0x4f010f; 143353207Snyan v86.ctl = V86_FLAGS; 143454819Snyan v86.addr = 0x1b; 143554819Snyan v86.eax = 0x8400 | unit; 143654819Snyan v86int(); 143754819Snyan if (v86.efl & 0x1) 143854819Snyan return 0x4f010f; 143954819Snyan return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff); 144054819Snyan#else 144154819Snyan v86.ctl = V86_FLAGS; 144253207Snyan v86.addr = 0x13; 144353207Snyan v86.eax = 0x800; 144453207Snyan v86.edx = 0x80 + bunit; 144553207Snyan v86int(); 144653207Snyan if (v86.efl & 0x1) 144753207Snyan return 0x4f010f; 144853207Snyan return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) | 144953207Snyan (v86.edx & 0xff00) | (v86.ecx & 0x3f); 145054819Snyan#endif 145153207Snyan} 145253207Snyan 145353207Snyan/* 145443561Skato * Return a suitable dev_t value for (dev). 145543561Skato * 145643561Skato * In the case where it looks like (dev) is a SCSI disk, we allow the number of 145743561Skato * IDE disks to be specified in $num_ide_disks. There should be a Better Way. 145843561Skato */ 145943561Skatoint 146043561Skatobd_getdev(struct i386_devdesc *dev) 146143561Skato{ 146243561Skato struct open_disk *od; 146343561Skato int biosdev; 146443561Skato int major; 146543561Skato int rootdev; 146643561Skato char *nip, *cp; 146743561Skato int unitofs = 0, i, unit; 146843561Skato 146943561Skato biosdev = bd_unit2bios(dev->d_kind.biosdisk.unit); 147043561Skato DEBUG("unit %d BIOS device %d", dev->d_kind.biosdisk.unit, biosdev); 147143561Skato if (biosdev == -1) /* not a BIOS device */ 147243561Skato return(-1); 147343561Skato if (bd_opendisk(&od, dev) != 0) /* oops, not a viable device */ 147443561Skato return(-1); 147543561Skato 147643561Skato#ifdef PC98 147743561Skato if ((biosdev & 0xf0) == 0x90 || (biosdev & 0xf0) == 0x30) { 147843561Skato#else 147943561Skato if (biosdev < 0x80) { 148043561Skato#endif 148143561Skato /* floppy (or emulated floppy) or ATAPI device */ 148243561Skato if (bdinfo[dev->d_kind.biosdisk.unit].bd_type == DT_ATAPI) { 148343561Skato /* is an ATAPI disk */ 148443561Skato major = WFDMAJOR; 148543561Skato } else { 148643561Skato /* is a floppy disk */ 148743561Skato major = FDMAJOR; 148843561Skato } 148943561Skato } else { 149043561Skato /* harddisk */ 149143561Skato if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) { 149243561Skato /* label OK, disk labelled as SCSI */ 149343561Skato major = DAMAJOR; 149443561Skato /* check for unit number correction hint, now deprecated */ 149543561Skato if ((nip = getenv("num_ide_disks")) != NULL) { 149643561Skato i = strtol(nip, &cp, 0); 149743561Skato /* check for parse error */ 149843561Skato if ((cp != nip) && (*cp == 0)) 149943561Skato unitofs = i; 150043561Skato } 150143561Skato } else { 150243561Skato /* assume an IDE disk */ 150343561Skato major = WDMAJOR; 150443561Skato } 150543561Skato } 150668358Snyan /* default root disk unit number */ 150768358Snyan#ifdef PC98 150868358Snyan if ((biosdev & 0xf0) == 0xa0) 150968358Snyan unit = bdinfo[dev->d_kind.biosdisk.unit].bd_da_unit; 151068358Snyan else 151168358Snyan unit = biosdev & 0xf; 151268358Snyan#else 151368358Snyan unit = (biosdev & 0x7f) - unitofs; 151468358Snyan#endif 151568358Snyan 151643561Skato /* XXX a better kludge to set the root disk unit number */ 151743561Skato if ((nip = getenv("root_disk_unit")) != NULL) { 151843561Skato i = strtol(nip, &cp, 0); 151943561Skato /* check for parse error */ 152043561Skato if ((cp != nip) && (*cp == 0)) 152143561Skato unit = i; 152243561Skato } 152343561Skato 152443561Skato rootdev = MAKEBOOTDEV(major, 152543561Skato (dev->d_kind.biosdisk.slice + 1) >> 4, /* XXX slices may be wrong here */ 152643561Skato (dev->d_kind.biosdisk.slice + 1) & 0xf, 152743561Skato unit, 152843561Skato dev->d_kind.biosdisk.partition); 152943561Skato DEBUG("dev is 0x%x\n", rootdev); 153043561Skato return(rootdev); 153143561Skato} 1532