biosdisk.c revision 87734
113007Swpaul/*- 213007Swpaul * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 313007Swpaul * All rights reserved. 413007Swpaul * 513007Swpaul * Redistribution and use in source and binary forms, with or without 613007Swpaul * modification, are permitted provided that the following conditions 713007Swpaul * are met: 813007Swpaul * 1. Redistributions of source code must retain the above copyright 913007Swpaul * notice, this list of conditions and the following disclaimer. 1013007Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1113007Swpaul * notice, this list of conditions and the following disclaimer in the 1213007Swpaul * documentation and/or other materials provided with the distribution. 1313007Swpaul * 1413007Swpaul * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1513007Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1613007Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1713007Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1813007Swpaul * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1913007Swpaul * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2013007Swpaul * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2113007Swpaul * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2213007Swpaul * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2313007Swpaul * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2413007Swpaul * SUCH DAMAGE. 2513007Swpaul * 2613007Swpaul * $FreeBSD: head/sys/boot/pc98/libpc98/biosdisk.c 87734 2001-12-12 13:47:04Z nyan $ 2713007Swpaul */ 2813007Swpaul 2913007Swpaul/* 3013007Swpaul * BIOS disk device handling. 3113007Swpaul * 3224349Simp * Ideas and algorithms from: 3313007Swpaul * 3413007Swpaul * - NetBSD libi386/biosdisk.c 3513007Swpaul * - FreeBSD biosboot/disk.c 3613007Swpaul * 3713007Swpaul */ 3813007Swpaul 3913007Swpaul#include <stand.h> 4013007Swpaul 4113007Swpaul#include <sys/disklabel.h> 4213007Swpaul#include <sys/diskslice.h> 4313007Swpaul#include <sys/reboot.h> 4413007Swpaul 4513007Swpaul#include <stdarg.h> 4613376Swpaul 4713007Swpaul#include <bootstrap.h> 4813007Swpaul#include <btxv86.h> 4913007Swpaul#include "libi386.h" 5016132Swpaul 5113007Swpaul#define BIOS_NUMDRIVES 0x475 5213007Swpaul#define BIOSDISK_SECSIZE 512 5313007Swpaul#define BUFSIZE (1 * BIOSDISK_SECSIZE) 5424349Simp#define MAXBDDEV MAXDEV 5513007Swpaul 5613007Swpaul#define DT_ATAPI 0x10 /* disk type for ATAPI floppies */ 5713007Swpaul#define WDMAJOR 0 /* major numbers for devices we frontend for */ 5813007Swpaul#define WFDMAJOR 1 5913007Swpaul#define FDMAJOR 2 6013007Swpaul#define DAMAJOR 4 6113007Swpaul 6213007Swpaul#ifdef DISK_DEBUG 6313007Swpaul# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 6413007Swpaul#else 6513007Swpaul# define DEBUG(fmt, args...) 6613007Swpaul#endif 6713007Swpaul 6813007Swpaulstruct open_disk { 6913007Swpaul int od_dkunit; /* disk unit number */ 7013007Swpaul int od_unit; /* BIOS unit number */ 7113007Swpaul int od_cyl; /* BIOS geometry */ 7213007Swpaul int od_hds; 7313007Swpaul int od_sec; 7413007Swpaul int od_boff; /* block offset from beginning of BIOS disk */ 7513007Swpaul int od_flags; 7613007Swpaul#define BD_MODEINT13 0x0000 7713276Swpaul#define BD_MODEEDD1 0x0001 7813276Swpaul#define BD_MODEEDD3 0x0002 7913007Swpaul#define BD_MODEMASK 0x0003 8013007Swpaul#define BD_FLOPPY 0x0004 8113007Swpaul#define BD_LABELOK 0x0008 8213007Swpaul#define BD_PARTTABOK 0x0010 8313007Swpaul struct disklabel od_disklabel; 8413007Swpaul int od_nslices; /* slice count */ 8513007Swpaul struct dos_partition od_slicetab[MAX_SLICES]; 8613007Swpaul}; 8713007Swpaul 8813007Swpaul/* 8913007Swpaul * List of BIOS devices, translation from disk unit number to 9016132Swpaul * BIOS unit number. 9116132Swpaul */ 9213007Swpaulstatic struct bdinfo 9313007Swpaul{ 9413007Swpaul int bd_unit; /* BIOS unit number */ 9513007Swpaul int bd_flags; 9613007Swpaul int bd_type; /* BIOS 'drive type' (floppy only) */ 9713007Swpaul#ifdef PC98 9813007Swpaul int bd_da_unit; /* kernel unit number for da */ 9913007Swpaul#endif 10013007Swpaul} bdinfo [MAXBDDEV]; 10113007Swpaulstatic int nbdinfo = 0; 10213007Swpaul 10313007Swpaulstatic int bd_getgeom(struct open_disk *od); 10413007Swpaulstatic int bd_read(struct open_disk *od, daddr_t dblk, int blks, 10513007Swpaul caddr_t dest); 10613007Swpaulstatic int bd_write(struct open_disk *od, daddr_t dblk, int blks, 10713007Swpaul caddr_t dest); 10813007Swpaul 10913007Swpaulstatic int bd_int13probe(struct bdinfo *bd); 11013007Swpaul 11113007Swpaulstatic void bd_printslice(struct open_disk *od, struct dos_partition *dp, 11213007Swpaul char *prefix, int verbose); 11313007Swpaulstatic void bd_printbsdslice(struct open_disk *od, daddr_t offset, 11413007Swpaul char *prefix, int verbose); 11513007Swpaul 11613007Swpaulstatic int bd_init(void); 11713007Swpaulstatic int bd_strategy(void *devdata, int flag, daddr_t dblk, 11813007Swpaul size_t size, char *buf, size_t *rsize); 11913007Swpaulstatic int bd_realstrategy(void *devdata, int flag, daddr_t dblk, 12013007Swpaul size_t size, char *buf, size_t *rsize); 12113007Swpaulstatic int bd_open(struct open_file *f, ...); 12213007Swpaulstatic int bd_close(struct open_file *f); 12313007Swpaulstatic void bd_print(int verbose); 12413007Swpaul 12513007Swpaulstruct devsw biosdisk = { 12613007Swpaul "disk", 12713007Swpaul DEVT_DISK, 12813007Swpaul bd_init, 12913007Swpaul bd_strategy, 13013007Swpaul bd_open, 13113007Swpaul bd_close, 13213007Swpaul noioctl, 13313007Swpaul bd_print, 13413007Swpaul NULL 13513007Swpaul}; 13613007Swpaul 13713007Swpaulstatic int bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev); 13813007Swpaulstatic void bd_closedisk(struct open_disk *od); 13913007Swpaulstatic int bd_bestslice(struct open_disk *od); 14013007Swpaulstatic void bd_checkextended(struct open_disk *od, int slicenum); 14116132Swpaul 14213007Swpaul/* 14313007Swpaul * Translate between BIOS device numbers and our private unit numbers. 14413007Swpaul */ 14513007Swpaulint 14613007Swpaulbd_bios2unit(int biosdev) 14713007Swpaul{ 14813007Swpaul int i; 14913007Swpaul 15013007Swpaul DEBUG("looking for bios device 0x%x", biosdev); 15113007Swpaul for (i = 0; i < nbdinfo; i++) { 15213007Swpaul DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit); 15313007Swpaul if (bdinfo[i].bd_unit == biosdev) 15413007Swpaul return(i); 15513007Swpaul } 15613007Swpaul return(-1); 15713007Swpaul} 15813007Swpaul 15913007Swpaulint 16013007Swpaulbd_unit2bios(int unit) 16113007Swpaul{ 16213007Swpaul if ((unit >= 0) && (unit < nbdinfo)) 16313007Swpaul return(bdinfo[unit].bd_unit); 16413007Swpaul return(-1); 16519065Swpaul} 16619181Swpaul 16719181Swpaul/* 16813007Swpaul * Quiz the BIOS for disk devices, save a little info about them. 16913007Swpaul */ 17013007Swpaulstatic int 17113007Swpaulbd_init(void) 17213007Swpaul{ 17313007Swpaul#ifdef PC98 17413007Swpaul int base, unit; 17513007Swpaul int da_drive=0, n=-0x10; 17613007Swpaul 17713007Swpaul /* sequence 0x90, 0x80, 0xa0 */ 17813007Swpaul for (base = 0x90; base <= 0xa0; base += n, n += 0x30) { 17924349Simp for (unit = base; (nbdinfo < MAXBDDEV) || ((unit & 0x0f) < 4); unit++) { 18013007Swpaul bdinfo[nbdinfo].bd_unit = unit; 18113007Swpaul bdinfo[nbdinfo].bd_flags = (unit & 0xf0) == 0x90 ? BD_FLOPPY : 0; 18213007Swpaul 18313007Swpaul if (!bd_int13probe(&bdinfo[nbdinfo])){ 18413007Swpaul if (((unit & 0xf0) == 0x90 && (unit & 0x0f) < 4) || 18513007Swpaul ((unit & 0xf0) == 0xa0 && (unit & 0x0f) < 6)) 18613007Swpaul continue; /* Target IDs are not contiguous. */ 18713007Swpaul else 18813007Swpaul break; 18913007Swpaul } 19013007Swpaul 19113007Swpaul if (bdinfo[nbdinfo].bd_flags & BD_FLOPPY){ 19213007Swpaul /* available 1.44MB access? */ 19313007Swpaul if (*(u_char *)PTOV(0xA15AE) & (1<<(unit & 0xf))) { 19413007Swpaul /* boot media 1.2MB FD? */ 19513007Swpaul if ((*(u_char *)PTOV(0xA1584) & 0xf0) != 0x90) 19613007Swpaul bdinfo[nbdinfo].bd_unit = 0x30 + (unit & 0xf); 19713007Swpaul } 19813007Swpaul } 19913007Swpaul else { 20013007Swpaul if ((unit & 0xa0) == 0xa0) 20113007Swpaul bdinfo[nbdinfo].bd_da_unit = da_drive++; 20213007Swpaul } 20313007Swpaul /* XXX we need "disk aliases" to make this simpler */ 20413007Swpaul printf("BIOS drive %c: is disk%d\n", 20513007Swpaul 'A' + nbdinfo, nbdinfo); 20613007Swpaul nbdinfo++; 20713007Swpaul } 20813007Swpaul } 20913007Swpaul#else 21013007Swpaul int base, unit, nfd = 0; 21113007Swpaul 21213007Swpaul /* sequence 0, 0x80 */ 21313007Swpaul for (base = 0; base <= 0x80; base += 0x80) { 21413007Swpaul for (unit = base; (nbdinfo < MAXBDDEV); unit++) { 21513007Swpaul /* check the BIOS equipment list for number of fixed disks */ 21613007Swpaul if((base == 0x80) && 21713007Swpaul (nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES))) 21813007Swpaul break; 21913007Swpaul 22013007Swpaul bdinfo[nbdinfo].bd_unit = unit; 22113007Swpaul bdinfo[nbdinfo].bd_flags = (unit < 0x80) ? BD_FLOPPY : 0; 22213007Swpaul 22313007Swpaul if (!bd_int13probe(&bdinfo[nbdinfo])) 22413007Swpaul break; 22513007Swpaul 22613007Swpaul /* XXX we need "disk aliases" to make this simpler */ 22713007Swpaul printf("BIOS drive %c: is disk%d\n", 22813007Swpaul (unit < 0x80) ? ('A' + unit) : ('C' + unit - 0x80), nbdinfo); 22913007Swpaul nbdinfo++; 23013007Swpaul if (base == 0x80) 23113007Swpaul nfd++; 23213007Swpaul } 23313007Swpaul } 23413007Swpaul#endif 23513007Swpaul return(0); 23613007Swpaul} 23713007Swpaul 23813007Swpaul/* 23913007Swpaul * Try to detect a device supported by the legacy int13 BIOS 24013007Swpaul */ 24113007Swpaulstatic int 24213007Swpaulbd_int13probe(struct bdinfo *bd) 24313007Swpaul{ 24413007Swpaul#ifdef PC98 24513007Swpaul int addr; 24613007Swpaul 24713007Swpaul if (bd->bd_flags & BD_FLOPPY) { 24813007Swpaul addr = 0xa155c; 24913007Swpaul } else { 25013007Swpaul if ((bd->bd_unit & 0xf0) == 0x80) 25113007Swpaul addr = 0xa155d; 25213007Swpaul else 25313007Swpaul addr = 0xa1482; 25413007Swpaul } 25513007Swpaul if ( *(u_char *)PTOV(addr) & (1<<(bd->bd_unit & 0x0f))) { 25613007Swpaul bd->bd_flags |= BD_MODEINT13; 25713007Swpaul return(1); 25813007Swpaul } 25913007Swpaul return(0); 26013007Swpaul#else 26113007Swpaul v86.ctl = V86_FLAGS; 26213007Swpaul v86.addr = 0x13; 26313007Swpaul v86.eax = 0x800; 26413007Swpaul v86.edx = bd->bd_unit; 26513007Swpaul v86int(); 26613007Swpaul 26713007Swpaul if (!(v86.efl & 0x1) && /* carry clear */ 26813007Swpaul ((v86.edx & 0xff) > ((unsigned)bd->bd_unit & 0x7f))) { /* unit # OK */ 26913007Swpaul bd->bd_flags |= BD_MODEINT13; 27013007Swpaul bd->bd_type = v86.ebx & 0xff; 27113007Swpaul 27213007Swpaul /* Determine if we can use EDD with this device. */ 27313007Swpaul v86.eax = 0x4100; 27413007Swpaul v86.edx = bd->bd_unit; 27513007Swpaul v86.ebx = 0x55aa; 27613007Swpaul v86int(); 27713007Swpaul if (!(v86.efl & 0x1) && /* carry clear */ 27813007Swpaul ((v86.ebx & 0xffff) == 0xaa55) && /* signature */ 27913007Swpaul (v86.ecx & 0x1)) { /* packets mode ok */ 28013007Swpaul bd->bd_flags |= BD_MODEEDD1; 28113007Swpaul if((v86.eax & 0xff00) > 0x300) 28213007Swpaul bd->bd_flags |= BD_MODEEDD3; 28313007Swpaul } 28413007Swpaul return(1); 28513007Swpaul } 28613007Swpaul return(0); 28713007Swpaul#endif 28813007Swpaul} 28913007Swpaul 29013007Swpaul/* 29113007Swpaul * Print information about disks 29213007Swpaul */ 29313007Swpaulstatic void 29413007Swpaulbd_print(int verbose) 29513007Swpaul{ 29613007Swpaul int i, j; 29713007Swpaul char line[80]; 29813007Swpaul struct i386_devdesc dev; 29913007Swpaul struct open_disk *od; 30013007Swpaul struct dos_partition *dptr; 30113007Swpaul 30213007Swpaul for (i = 0; i < nbdinfo; i++) { 30313007Swpaul#ifdef PC98 30413007Swpaul sprintf(line, " disk%d: BIOS drive %c:\n", i, 'A' + i); 30513007Swpaul#else 30613007Swpaul sprintf(line, " disk%d: BIOS drive %c:\n", i, 30713007Swpaul (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit) : ('C' + bdinfo[i].bd_unit - 0x80)); 30813007Swpaul#endif 30913007Swpaul pager_output(line); 31013007Swpaul 31113007Swpaul /* try to open the whole disk */ 31213007Swpaul dev.d_kind.biosdisk.unit = i; 31313007Swpaul dev.d_kind.biosdisk.slice = -1; 31413007Swpaul dev.d_kind.biosdisk.partition = -1; 31513007Swpaul 31613007Swpaul if (!bd_opendisk(&od, &dev)) { 31713007Swpaul 31813007Swpaul /* Do we have a partition table? */ 31913007Swpaul if (od->od_flags & BD_PARTTABOK) { 32013007Swpaul dptr = &od->od_slicetab[0]; 32113007Swpaul 32213276Swpaul /* Check for a "dedicated" disk */ 32313276Swpaul#ifdef PC98 32413276Swpaul for (j = 0; j < od->od_nslices; j++) { 32513007Swpaul switch(dptr[j].dp_mid) { 32613007Swpaul case DOSMID_386BSD: 32713007Swpaul sprintf(line, " disk%ds%d", i, j + 1); 32813007Swpaul bd_printbsdslice(od, 32913007Swpaul dptr[j].dp_scyl * od->od_hds * od->od_sec + 33013007Swpaul dptr[j].dp_shd * od->od_sec + dptr[j].dp_ssect, 33113007Swpaul line, verbose); 33213007Swpaul break; 33313007Swpaul default: 33413007Swpaul } 33513007Swpaul } 33613007Swpaul#else 33713007Swpaul if ((dptr[3].dp_typ == DOSPTYP_386BSD) && 33819065Swpaul (dptr[3].dp_start == 0) && 33919065Swpaul (dptr[3].dp_size == 50000)) { 34019065Swpaul sprintf(line, " disk%d", i); 34119065Swpaul bd_printbsdslice(od, 0, line, verbose); 34219065Swpaul } else { 34319065Swpaul for (j = 0; j < od->od_nslices; j++) { 34419065Swpaul sprintf(line, " disk%ds%d", i, j + 1); 34519065Swpaul bd_printslice(od, &dptr[j], line, verbose); 34619065Swpaul } 34719065Swpaul } 34813007Swpaul#endif 34913007Swpaul } 35013007Swpaul bd_closedisk(od); 35113276Swpaul } 35213376Swpaul } 35313376Swpaul} 35413007Swpaul 35513007Swpaul#ifndef PC98 35613007Swpaul/* 35719181Swpaul * Print information about slices on a disk. For the size calculations we 35819181Swpaul * assume a 512 byte sector. 35919181Swpaul */ 36019181Swpaulstatic void 36119181Swpaulbd_printslice(struct open_disk *od, struct dos_partition *dp, char *prefix, 36219181Swpaul int verbose) 36319181Swpaul{ 36419181Swpaul char line[80]; 36513007Swpaul 36613007Swpaul switch (dp->dp_typ) { 36713007Swpaul case DOSPTYP_386BSD: 36813007Swpaul bd_printbsdslice(od, (daddr_t)dp->dp_start, prefix, verbose); 36913007Swpaul return; 37013007Swpaul case DOSPTYP_LINSWP: 37113007Swpaul if (verbose) 37213007Swpaul sprintf(line, "%s: Linux swap %.6dMB (%d - %d)\n", 37313007Swpaul prefix, dp->dp_size / 2048, 37413007Swpaul dp->dp_start, dp->dp_start + dp->dp_size); 37513007Swpaul else 37613007Swpaul sprintf(line, "%s: Linux swap\n", prefix); 37713007Swpaul break; 37813007Swpaul case DOSPTYP_LINUX: 37913007Swpaul /* 38013007Swpaul * XXX 38113007Swpaul * read the superblock to confirm this is an ext2fs partition? 38213007Swpaul */ 38313007Swpaul if (verbose) 38413007Swpaul sprintf(line, "%s: ext2fs %.6dMB (%d - %d)\n", prefix, 38513007Swpaul dp->dp_size / 2048, dp->dp_start, 38613007Swpaul dp->dp_start + dp->dp_size); 38713007Swpaul else 38813007Swpaul sprintf(line, "%s: ext2fs\n", prefix); 38913007Swpaul break; 39013007Swpaul case 0x00: /* unused partition */ 39113007Swpaul case DOSPTYP_EXT: 39213007Swpaul return; 39313007Swpaul case 0x01: 39413007Swpaul if (verbose) 39513007Swpaul sprintf(line, "%s: FAT-12 %.6dMB (%d - %d)\n", prefix, 39613007Swpaul dp->dp_size / 2048, dp->dp_start, 39713007Swpaul dp->dp_start + dp->dp_size); 39813007Swpaul else 39913007Swpaul sprintf(line, "%s: FAT-12\n", prefix); 40019065Swpaul break; 40119065Swpaul case 0x04: 40219065Swpaul case 0x06: 40319065Swpaul case 0x0e: 40419065Swpaul if (verbose) 40519065Swpaul sprintf(line, "%s: FAT-16 %.6dMB (%d - %d)\n", prefix, 40619065Swpaul dp->dp_size / 2048, dp->dp_start, 40719065Swpaul dp->dp_start + dp->dp_size); 40819065Swpaul else 40919065Swpaul sprintf(line, "%s: FAT-16\n", prefix); 41016132Swpaul break; 41119065Swpaul case 0x0b: 41216132Swpaul case 0x0c: 41316132Swpaul if (verbose) 41416132Swpaul sprintf(line, "%s: FAT-32 %.6dMB (%d - %d)\n", prefix, 41516132Swpaul dp->dp_size / 2048, dp->dp_start, 41613007Swpaul dp->dp_start + dp->dp_size); 41716132Swpaul else 41813007Swpaul sprintf(line, "%s: FAT-32\n", prefix); 41913007Swpaul break; 42013007Swpaul default: 42113007Swpaul if (verbose) 42213007Swpaul sprintf(line, "%s: Unknown fs: 0x%x %.6dMB (%d - %d)\n", 42313007Swpaul prefix, dp->dp_typ, dp->dp_size / 2048, 42413007Swpaul dp->dp_start, dp->dp_start + dp->dp_size); 42513007Swpaul else 42613007Swpaul sprintf(line, "%s: Unknown fs: 0x%x\n", prefix, 42713007Swpaul dp->dp_typ); 42813007Swpaul } 42913007Swpaul pager_output(line); 43013007Swpaul} 43116132Swpaul#endif 43213007Swpaul 43313007Swpaul/* 43413007Swpaul * Print out each valid partition in the disklabel of a FreeBSD slice. 43513007Swpaul * For size calculations, we assume a 512 byte sector size. 43613007Swpaul */ 43713007Swpaulstatic void 43813007Swpaulbd_printbsdslice(struct open_disk *od, daddr_t offset, char *prefix, 43913007Swpaul int verbose) 44013007Swpaul{ 44116132Swpaul char line[80]; 44213007Swpaul char buf[BIOSDISK_SECSIZE]; 44313007Swpaul struct disklabel *lp; 44413007Swpaul int i; 44513007Swpaul 44613007Swpaul /* read disklabel */ 44713007Swpaul if (bd_read(od, offset + LABELSECTOR, 1, buf)) 44813007Swpaul return; 44913007Swpaul lp =(struct disklabel *)(&buf[0]); 45013007Swpaul if (lp->d_magic != DISKMAGIC) { 45116132Swpaul sprintf(line, "%s: FFS bad disklabel\n", prefix); 45213007Swpaul pager_output(line); 45313007Swpaul return; 45413007Swpaul } 45513007Swpaul 45613007Swpaul /* Print partitions */ 45713007Swpaul for (i = 0; i < lp->d_npartitions; i++) { 45813007Swpaul /* 45913007Swpaul * For each partition, make sure we know what type of fs it is. If 46013007Swpaul * not, then skip it. However, since floppies often have bogus 46113007Swpaul * fstypes, print the 'a' partition on a floppy even if it is marked 46213007Swpaul * unused. 46316132Swpaul */ 46413007Swpaul if ((lp->d_partitions[i].p_fstype == FS_BSDFFS) || 46513007Swpaul (lp->d_partitions[i].p_fstype == FS_SWAP) || 46613007Swpaul (lp->d_partitions[i].p_fstype == FS_VINUM) || 46713007Swpaul ((lp->d_partitions[i].p_fstype == FS_UNUSED) && 46813007Swpaul (od->od_flags & BD_FLOPPY) && (i == 0))) { 46913007Swpaul 47013007Swpaul /* Only print out statistics in verbose mode */ 47113007Swpaul if (verbose) 47213007Swpaul sprintf(line, " %s%c: %s %.6dMB (%d - %d)\n", prefix, 'a' + i, 47313007Swpaul (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : 47413007Swpaul (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" : 47513007Swpaul "FFS", 47613007Swpaul lp->d_partitions[i].p_size / 2048, 47716132Swpaul lp->d_partitions[i].p_offset, 47813007Swpaul lp->d_partitions[i].p_offset + lp->d_partitions[i].p_size); 47913007Swpaul else 48013007Swpaul sprintf(line, " %s%c: %s\n", prefix, 'a' + i, 48113007Swpaul (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : 48219181Swpaul (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" : 48319181Swpaul "FFS"); 48419181Swpaul pager_output(line); 48519181Swpaul } 48619181Swpaul } 48719181Swpaul} 48819181Swpaul 48919181Swpaul 49019181Swpaul/* 49119181Swpaul * Attempt to open the disk described by (dev) for use by (f). 49219181Swpaul * 49319181Swpaul * Note that the philosophy here is "give them exactly what 49419181Swpaul * they ask for". This is necessary because being too "smart" 49519181Swpaul * about what the user might want leads to complications. 49619181Swpaul * (eg. given no slice or partition value, with a disk that is 49719181Swpaul * sliced - are they after the first BSD slice, or the DOS 49819181Swpaul * slice before it?) 49919181Swpaul */ 50019181Swpaulstatic int 50119181Swpaulbd_open(struct open_file *f, ...) 50219181Swpaul{ 50319181Swpaul va_list ap; 50419181Swpaul struct i386_devdesc *dev; 50519181Swpaul struct open_disk *od; 50613007Swpaul int error; 50713007Swpaul 50813007Swpaul va_start(ap, f); 50913007Swpaul dev = va_arg(ap, struct i386_devdesc *); 51013007Swpaul va_end(ap); 51113007Swpaul if ((error = bd_opendisk(&od, dev))) 51213007Swpaul return(error); 51313007Swpaul 51413007Swpaul /* 51513276Swpaul * Save our context 51613007Swpaul */ 51716132Swpaul ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od; 51816132Swpaul DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff); 51916132Swpaul return(0); 52016132Swpaul} 52116132Swpaul 52213007Swpaulstatic int 52313007Swpaulbd_opendisk(struct open_disk **odp, struct i386_devdesc *dev) 52413007Swpaul{ 52513007Swpaul struct dos_partition *dptr; 52613276Swpaul struct disklabel *lp; 52713376Swpaul struct open_disk *od; 52813376Swpaul int sector, slice, i; 52913007Swpaul int error; 53013007Swpaul char buf[BUFSIZE]; 53113007Swpaul 53213007Swpaul if (dev->d_kind.biosdisk.unit >= nbdinfo) { 53313007Swpaul DEBUG("attempt to open nonexistent disk"); 53413007Swpaul return(ENXIO); 53513007Swpaul } 53613007Swpaul 53713007Swpaul od = (struct open_disk *)malloc(sizeof(struct open_disk)); 53813376Swpaul if (!od) { 53913007Swpaul DEBUG("no memory"); 54013007Swpaul return (ENOMEM); 54113376Swpaul } 54213376Swpaul 54313376Swpaul /* Look up BIOS unit number, intialise open_disk structure */ 54413376Swpaul od->od_dkunit = dev->d_kind.biosdisk.unit; 54513376Swpaul od->od_unit = bdinfo[od->od_dkunit].bd_unit; 54613376Swpaul od->od_flags = bdinfo[od->od_dkunit].bd_flags; 54713376Swpaul od->od_boff = 0; 54813007Swpaul od->od_nslices = 0; 54913007Swpaul error = 0; 55013007Swpaul DEBUG("open '%s', unit 0x%x slice %d partition %c", 55113376Swpaul i386_fmtdev(dev), dev->d_kind.biosdisk.unit, 55213376Swpaul dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition + 'a'); 55313376Swpaul 55413376Swpaul /* Get geometry for this open (removable device may have changed) */ 55513376Swpaul if (bd_getgeom(od)) { 55613376Swpaul DEBUG("can't get geometry"); 55713376Swpaul error = ENXIO; 55813007Swpaul goto out; 55913007Swpaul } 56013007Swpaul 56113007Swpaul /* 56213007Swpaul * Following calculations attempt to determine the correct value 56313007Swpaul * for d->od_boff by looking for the slice and partition specified, 56413007Swpaul * or searching for reasonable defaults. 56513007Swpaul */ 56613007Swpaul 56713007Swpaul /* 568 * Find the slice in the DOS slice table. 569 */ 570#ifdef PC98 571 if (od->od_flags & BD_FLOPPY) { 572 sector = 0; 573 goto unsliced; 574 } 575#endif 576 if (bd_read(od, 0, 1, buf)) { 577 DEBUG("error reading MBR"); 578 error = EIO; 579 goto out; 580 } 581 582 /* 583 * Check the slice table magic. 584 */ 585 if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { 586 /* If a slice number was explicitly supplied, this is an error */ 587 if (dev->d_kind.biosdisk.slice > 0) { 588 DEBUG("no slice table/MBR (no magic)"); 589 error = ENOENT; 590 goto out; 591 } 592 sector = 0; 593 goto unsliced; /* may be a floppy */ 594 } 595#ifdef PC98 596 if (bd_read(od, 1, 1, buf)) { 597 DEBUG("error reading MBR"); 598 error = EIO; 599 goto out; 600 } 601#endif 602 603 /* 604 * copy the partition table, then pick up any extended partitions. 605 */ 606 bcopy(buf + DOSPARTOFF, &od->od_slicetab, 607 sizeof(struct dos_partition) * NDOSPART); 608#ifdef PC98 609 od->od_nslices = NDOSPART; /* extended slices start here */ 610#else 611 od->od_nslices = 4; /* extended slices start here */ 612 for (i = 0; i < NDOSPART; i++) 613 bd_checkextended(od, i); 614#endif 615 od->od_flags |= BD_PARTTABOK; 616 dptr = &od->od_slicetab[0]; 617 618 /* Is this a request for the whole disk? */ 619 if (dev->d_kind.biosdisk.slice == -1) { 620 sector = 0; 621 goto unsliced; 622 } 623 624 /* 625 * if a slice number was supplied but not found, this is an error. 626 */ 627 if (dev->d_kind.biosdisk.slice > 0) { 628 slice = dev->d_kind.biosdisk.slice - 1; 629 if (slice >= od->od_nslices) { 630 DEBUG("slice %d not found", slice); 631 error = ENOENT; 632 goto out; 633 } 634 } 635 636#ifndef PC98 637 /* 638 * Check for the historically bogus MBR found on true dedicated disks 639 */ 640 if ((dptr[3].dp_typ == DOSPTYP_386BSD) && 641 (dptr[3].dp_start == 0) && 642 (dptr[3].dp_size == 50000)) { 643 sector = 0; 644 goto unsliced; 645 } 646#endif 647 648 /* Try to auto-detect the best slice; this should always give a slice number */ 649 if (dev->d_kind.biosdisk.slice == 0) { 650 slice = bd_bestslice(od); 651 if (slice == -1) { 652 error = ENOENT; 653 goto out; 654 } 655 dev->d_kind.biosdisk.slice = slice; 656 } 657 658 dptr = &od->od_slicetab[0]; 659 /* 660 * Accept the supplied slice number unequivocally (we may be looking 661 * at a DOS partition). 662 */ 663 dptr += (dev->d_kind.biosdisk.slice - 1); /* we number 1-4, offsets are 0-3 */ 664#ifdef PC98 665 sector = dptr->dp_scyl * od->od_hds * od->od_sec + 666 dptr->dp_shd * od->od_sec + dptr->dp_ssect; 667 { 668 int end = dptr->dp_ecyl * od->od_hds * od->od_sec + 669 dptr->dp_ehd * od->od_sec + dptr->dp_esect; 670 DEBUG("slice entry %d at %d, %d sectors", 671 dev->d_kind.biosdisk.slice - 1, sector, end-sector); 672 } 673#else 674 sector = dptr->dp_start; 675 DEBUG("slice entry %d at %d, %d sectors", dev->d_kind.biosdisk.slice - 1, sector, dptr->dp_size); 676#endif 677 678 /* 679 * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition 680 */ 681#ifdef PC98 682 if ((dptr->dp_mid == DOSMID_386BSD) && (dev->d_kind.biosdisk.partition < 0)) 683#else 684 if ((dptr->dp_typ == DOSPTYP_386BSD) && (dev->d_kind.biosdisk.partition < 0)) 685#endif 686 dev->d_kind.biosdisk.partition = 0; 687 688 unsliced: 689 /* 690 * Now we have the slice offset, look for the partition in the disklabel if we have 691 * a partition to start with. 692 * 693 * XXX we might want to check the label checksum. 694 */ 695 if (dev->d_kind.biosdisk.partition < 0) { 696 od->od_boff = sector; /* no partition, must be after the slice */ 697 DEBUG("opening raw slice"); 698 } else { 699 700 if (bd_read(od, sector + LABELSECTOR, 1, buf)) { 701 DEBUG("error reading disklabel"); 702 error = EIO; 703 goto out; 704 } 705 DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel); 706 bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel)); 707 lp = &od->od_disklabel; 708 od->od_flags |= BD_LABELOK; 709 710 if (lp->d_magic != DISKMAGIC) { 711 DEBUG("no disklabel"); 712 error = ENOENT; 713 goto out; 714 } 715 if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) { 716 DEBUG("partition '%c' exceeds partitions in table (a-'%c')", 717 'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions); 718 error = EPART; 719 goto out; 720 721 } 722 723#ifdef DISK_DEBUG 724 /* Complain if the partition is unused unless this is a floppy. */ 725 if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) && 726 !(od->od_flags & BD_FLOPPY)) 727 DEBUG("warning, partition marked as unused"); 728#endif 729 730 od->od_boff = lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset; 731 } 732 733 out: 734 if (error) { 735 free(od); 736 } else { 737 *odp = od; /* return the open disk */ 738 } 739 return(error); 740} 741 742#ifndef PC98 743static void 744bd_checkextended(struct open_disk *od, int slicenum) 745{ 746 char buf[BIOSDISK_SECSIZE]; 747 struct dos_partition *dp; 748 u_int base; 749 int i, start, end; 750 751 dp = &od->od_slicetab[slicenum]; 752 start = od->od_nslices; 753 754 if (dp->dp_size == 0) 755 goto done; 756 if (dp->dp_typ != DOSPTYP_EXT) 757 goto done; 758 if (bd_read(od, (daddr_t)dp->dp_start, 1, buf)) 759 goto done; 760 if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { 761 DEBUG("no magic in extended table"); 762 goto done; 763 } 764 base = dp->dp_start; 765 dp = (struct dos_partition *)(&buf[DOSPARTOFF]); 766 for (i = 0; i < NDOSPART; i++, dp++) { 767 if (dp->dp_size == 0) 768 continue; 769 if (od->od_nslices == MAX_SLICES) 770 goto done; 771 dp->dp_start += base; 772 bcopy(dp, &od->od_slicetab[od->od_nslices], sizeof(*dp)); 773 od->od_nslices++; 774 } 775 end = od->od_nslices; 776 777 /* 778 * now, recursively check the slices we just added 779 */ 780 for (i = start; i < end; i++) 781 bd_checkextended(od, i); 782done: 783 return; 784} 785#endif 786 787/* 788 * Search for a slice with the following preferences: 789 * 790 * 1: Active FreeBSD slice 791 * 2: Non-active FreeBSD slice 792 * 3: Active Linux slice 793 * 4: non-active Linux slice 794 * 5: Active FAT/FAT32 slice 795 * 6: non-active FAT/FAT32 slice 796 */ 797#define PREF_RAWDISK 0 798#define PREF_FBSD_ACT 1 799#define PREF_FBSD 2 800#define PREF_LINUX_ACT 3 801#define PREF_LINUX 4 802#define PREF_DOS_ACT 5 803#define PREF_DOS 6 804#define PREF_NONE 7 805 806/* 807 * slicelimit is in the range 0 .. NDOSPART 808 */ 809static int 810bd_bestslice(struct open_disk *od) 811{ 812 struct dos_partition *dp; 813 int pref, preflevel; 814 int i, prefslice; 815 816 prefslice = 0; 817 preflevel = PREF_NONE; 818 819 dp = &od->od_slicetab[0]; 820 for (i = 0; i < od->od_nslices; i++, dp++) { 821 822#ifdef PC98 823 switch(dp->dp_mid & 0x7f) { 824 case DOSMID_386BSD & 0x7f: /* FreeBSD */ 825 if ((dp->dp_mid & 0x80) && 826 (preflevel > PREF_FBSD_ACT)) { 827 pref = i; 828 preflevel = PREF_FBSD_ACT; 829 } else if (preflevel > PREF_FBSD) { 830 pref = i; 831 preflevel = PREF_FBSD; 832 } 833 break; 834 835 case 0x11: /* DOS/Windows */ 836 case 0x20: 837 case 0x21: 838 case 0x22: 839 case 0x23: 840 case 0x63: 841 if ((dp->dp_mid & 0x80) && 842 (preflevel > PREF_DOS_ACT)) { 843 pref = i; 844 preflevel = PREF_DOS_ACT; 845 } else if (preflevel > PREF_DOS) { 846 pref = i; 847 preflevel = PREF_DOS; 848 } 849 break; 850 } 851#else 852 switch (dp->dp_typ) { 853 case DOSPTYP_386BSD: /* FreeBSD */ 854 pref = dp->dp_flag & 0x80 ? PREF_FBSD_ACT : PREF_FBSD; 855 break; 856 857 case DOSPTYP_LINUX: 858 pref = dp->dp_flag & 0x80 ? PREF_LINUX_ACT : PREF_LINUX; 859 break; 860 861 case 0x01: /* DOS/Windows */ 862 case 0x04: 863 case 0x06: 864 case 0x0b: 865 case 0x0c: 866 case 0x0e: 867 pref = dp->dp_flag & 0x80 ? PREF_DOS_ACT : PREF_DOS; 868 break; 869 870 default: 871 pref = PREF_NONE; 872 } 873 if (pref < preflevel) { 874 preflevel = pref; 875 prefslice = i + 1; 876 } 877#endif 878 } 879 return (prefslice); 880} 881 882static int 883bd_close(struct open_file *f) 884{ 885 struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data); 886 887 bd_closedisk(od); 888 return(0); 889} 890 891static void 892bd_closedisk(struct open_disk *od) 893{ 894 DEBUG("open_disk %p", od); 895#if 0 896 /* XXX is this required? (especially if disk already open...) */ 897 if (od->od_flags & BD_FLOPPY) 898 delay(3000000); 899#endif 900 free(od); 901} 902 903static int 904bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) 905{ 906 struct bcache_devdata bcd; 907 struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data); 908 909 bcd.dv_strategy = bd_realstrategy; 910 bcd.dv_devdata = devdata; 911 return(bcache_strategy(&bcd, od->od_unit, rw, dblk+od->od_boff, size, buf, rsize)); 912} 913 914static int 915bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) 916{ 917 struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data); 918 int blks; 919#ifdef BD_SUPPORT_FRAGS 920 char fragbuf[BIOSDISK_SECSIZE]; 921 size_t fragsize; 922 923 fragsize = size % BIOSDISK_SECSIZE; 924#else 925 if (size % BIOSDISK_SECSIZE) 926 panic("bd_strategy: %d bytes I/O not multiple of block size", size); 927#endif 928 929 DEBUG("open_disk %p", od); 930 931 932 switch(rw){ 933 case F_READ: 934 blks = size / BIOSDISK_SECSIZE; 935 DEBUG("read %d from %d to %p", blks, dblk, buf); 936 937 if (rsize) 938 *rsize = 0; 939 if (blks && bd_read(od, dblk, blks, buf)) { 940 DEBUG("read error"); 941 return (EIO); 942 } 943#ifdef BD_SUPPORT_FRAGS 944 DEBUG("bd_strategy: frag read %d from %d+%d to %p", 945 fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE)); 946 if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) { 947 DEBUG("frag read error"); 948 return(EIO); 949 } 950 bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize); 951#endif 952 if (rsize) 953 *rsize = size; 954 return (0); 955 break; 956 957 case F_WRITE : 958 blks = size / BIOSDISK_SECSIZE; 959 DEBUG("write %d from %d to %p", blks, dblk, buf); 960 961 if (rsize) 962 *rsize = 0; 963 if (blks && bd_write(od, dblk, blks, buf)) { 964 DEBUG("write error"); 965 return (EIO); 966 } 967#ifdef BD_SUPPORT_FRAGS 968 if(fragsize) { 969 DEBUG("Attempted to write a frag"); 970 return (EIO); 971 } 972#endif 973 974 if (rsize) 975 *rsize = size; 976 return (0); 977 default: 978 /* DO NOTHING */ 979 } 980 981 return EROFS; 982} 983 984/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */ 985#define FLOPPY_BOUNCEBUF 18 986 987static int 988bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) 989{ 990 u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer; 991 caddr_t p, xp, bbuf, breg; 992 993 /* Just in case some idiot actually tries to read -1 blocks... */ 994 if (blks < 0) 995 return (-1); 996 997 bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ 998 resid = blks; 999 p = dest; 1000 1001 /* Decide whether we have to bounce */ 1002#ifdef PC98 1003 if ( 1004#else 1005 if ((od->od_unit < 0x80) && 1006#endif 1007 ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) { 1008 1009 /* 1010 * There is a 64k physical boundary somewhere in the destination buffer, so we have 1011 * to arrange a suitable bounce buffer. Allocate a buffer twice as large as we 1012 * need to. Use the bottom half unless there is a break there, in which case we 1013 * use the top half. 1014 */ 1015#ifdef PC98 1016 x = min(od->od_sec, (unsigned)blks); 1017#else 1018 x = min(FLOPPY_BOUNCEBUF, (unsigned)blks); 1019#endif 1020 bbuf = malloc(x * 2 * BIOSDISK_SECSIZE); 1021 if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(dest + x * BIOSDISK_SECSIZE) & 0xffff0000)) { 1022 breg = bbuf; 1023 } else { 1024 breg = bbuf + x * BIOSDISK_SECSIZE; 1025 } 1026 maxfer = x; /* limit transfers to bounce region size */ 1027 } else { 1028 breg = bbuf = NULL; 1029 maxfer = 0; 1030 } 1031 1032 while (resid > 0) { 1033 x = dblk; 1034 cyl = x / bpc; /* block # / blocks per cylinder */ 1035 x %= bpc; /* block offset into cylinder */ 1036 hd = x / od->od_sec; /* offset / blocks per track */ 1037 sec = x % od->od_sec; /* offset into track */ 1038 1039 /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */ 1040 x = min(od->od_sec - sec, resid); 1041 if (maxfer > 0) 1042 x = min(x, maxfer); /* fit bounce buffer */ 1043 1044 /* where do we transfer to? */ 1045 xp = bbuf == NULL ? p : breg; 1046 1047 /* correct sector number for 1-based BIOS numbering */ 1048#ifdef PC98 1049 if ((od->od_unit & 0xf0) == 0x30 || (od->od_unit & 0xf0) == 0x90) 1050 sec++; 1051#else 1052 sec++; 1053#endif 1054 1055 /* Loop retrying the operation a couple of times. The BIOS may also retry. */ 1056 for (retry = 0; retry < 3; retry++) { 1057 /* if retrying, reset the drive */ 1058 if (retry > 0) { 1059#ifdef PC98 1060 v86.ctl = V86_FLAGS; 1061 v86.addr = 0x1b; 1062 v86.eax = 0x0300 | od->od_unit; 1063#else 1064 v86.ctl = V86_FLAGS; 1065 v86.addr = 0x13; 1066 v86.eax = 0; 1067 v86.edx = od->od_unit; 1068#endif 1069 v86int(); 1070 } 1071 1072#ifdef PC98 1073 v86.ctl = V86_FLAGS; 1074 v86.addr = 0x1b; 1075 if (od->od_flags & BD_FLOPPY) { 1076 v86.eax = 0xd600 | od->od_unit; 1077 v86.ecx = 0x0200 | (cyl & 0xff); 1078 } 1079 else { 1080 v86.eax = 0x0600 | od->od_unit; 1081 v86.ecx = cyl; 1082 } 1083 v86.edx = (hd << 8) | sec; 1084 v86.ebx = x * BIOSDISK_SECSIZE; 1085 v86.es = VTOPSEG(xp); 1086 v86.ebp = VTOPOFF(xp); 1087 v86int(); 1088 result = (v86.efl & 0x1); 1089 if (result == 0) 1090 break; 1091#else 1092 if(cyl > 1023) { 1093 /* use EDD if the disk supports it, otherwise, return error */ 1094 if(od->od_flags & BD_MODEEDD1) { 1095 static unsigned short packet[8]; 1096 1097 packet[0] = 0x10; 1098 packet[1] = x; 1099 packet[2] = VTOPOFF(xp); 1100 packet[3] = VTOPSEG(xp); 1101 packet[4] = dblk & 0xffff; 1102 packet[5] = dblk >> 16; 1103 packet[6] = 0; 1104 packet[7] = 0; 1105 v86.ctl = V86_FLAGS; 1106 v86.addr = 0x13; 1107 v86.eax = 0x4200; 1108 v86.edx = od->od_unit; 1109 v86.ds = VTOPSEG(packet); 1110 v86.esi = VTOPOFF(packet); 1111 v86int(); 1112 result = (v86.efl & 0x1); 1113 if(result == 0) 1114 break; 1115 } else { 1116 result = 1; 1117 break; 1118 } 1119 } else { 1120 /* Use normal CHS addressing */ 1121 v86.ctl = V86_FLAGS; 1122 v86.addr = 0x13; 1123 v86.eax = 0x200 | x; 1124 v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; 1125 v86.edx = (hd << 8) | od->od_unit; 1126 v86.es = VTOPSEG(xp); 1127 v86.ebx = VTOPOFF(xp); 1128 v86int(); 1129 result = (v86.efl & 0x1); 1130 if (result == 0) 1131 break; 1132 } 1133#endif 1134 } 1135 1136#ifdef PC98 1137 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"); 1138 /* BUG here, cannot use v86 in printf because putchar uses it too */ 1139 DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 1140 od->od_flags & BD_FLOPPY ? 0xd600 | od->od_unit : 0x0600 | od->od_unit, 1141 od->od_flags & BD_FLOPPY ? 0x0200 | cyl : cyl, (hd << 8) | sec, 1142 (v86.eax >> 8) & 0xff); 1143#else 1144 DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok"); 1145 /* BUG here, cannot use v86 in printf because putchar uses it too */ 1146 DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 1147 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff); 1148#endif 1149 if (result) { 1150 if (bbuf != NULL) 1151 free(bbuf); 1152 return(-1); 1153 } 1154 if (bbuf != NULL) 1155 bcopy(breg, p, x * BIOSDISK_SECSIZE); 1156 p += (x * BIOSDISK_SECSIZE); 1157 dblk += x; 1158 resid -= x; 1159 } 1160 1161/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */ 1162 if (bbuf != NULL) 1163 free(bbuf); 1164 return(0); 1165} 1166 1167 1168static int 1169bd_write(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) 1170{ 1171 u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer; 1172 caddr_t p, xp, bbuf, breg; 1173 1174 /* Just in case some idiot actually tries to read -1 blocks... */ 1175 if (blks < 0) 1176 return (-1); 1177 1178 bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ 1179 resid = blks; 1180 p = dest; 1181 1182 /* Decide whether we have to bounce */ 1183#ifdef PC98 1184 if ( 1185#else 1186 if ((od->od_unit < 0x80) && 1187#endif 1188 ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) { 1189 1190 /* 1191 * There is a 64k physical boundary somewhere in the destination buffer, so we have 1192 * to arrange a suitable bounce buffer. Allocate a buffer twice as large as we 1193 * need to. Use the bottom half unless there is a break there, in which case we 1194 * use the top half. 1195 */ 1196 1197#ifdef PC98 1198 x = min(od->od_sec, (unsigned)blks); 1199#else 1200 x = min(FLOPPY_BOUNCEBUF, (unsigned)blks); 1201#endif 1202 bbuf = malloc(x * 2 * BIOSDISK_SECSIZE); 1203 if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(dest + x * BIOSDISK_SECSIZE) & 0xffff0000)) { 1204 breg = bbuf; 1205 } else { 1206 breg = bbuf + x * BIOSDISK_SECSIZE; 1207 } 1208 maxfer = x; /* limit transfers to bounce region size */ 1209 } else { 1210 breg = bbuf = NULL; 1211 maxfer = 0; 1212 } 1213 1214 while (resid > 0) { 1215 x = dblk; 1216 cyl = x / bpc; /* block # / blocks per cylinder */ 1217 x %= bpc; /* block offset into cylinder */ 1218 hd = x / od->od_sec; /* offset / blocks per track */ 1219 sec = x % od->od_sec; /* offset into track */ 1220 1221 /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */ 1222 x = min(od->od_sec - sec, resid); 1223 if (maxfer > 0) 1224 x = min(x, maxfer); /* fit bounce buffer */ 1225 1226 /* where do we transfer to? */ 1227 xp = bbuf == NULL ? p : breg; 1228 1229 /* correct sector number for 1-based BIOS numbering */ 1230#ifdef PC98 1231 if ((od->od_unit & 0xf0) == 0x30 || (od->od_unit & 0xf0) == 0x90) 1232 sec++; 1233#else 1234 sec++; 1235#endif 1236 1237 1238 /* Put your Data In, Put your Data out, 1239 Put your Data In, and shake it all about 1240 */ 1241 if (bbuf != NULL) 1242 bcopy(p, breg, x * BIOSDISK_SECSIZE); 1243 p += (x * BIOSDISK_SECSIZE); 1244 dblk += x; 1245 resid -= x; 1246 1247 /* Loop retrying the operation a couple of times. The BIOS may also retry. */ 1248 for (retry = 0; retry < 3; retry++) { 1249 /* if retrying, reset the drive */ 1250 if (retry > 0) { 1251#ifdef PC98 1252 v86.ctl = V86_FLAGS; 1253 v86.addr = 0x1b; 1254 v86.eax = 0x0300 | od->od_unit; 1255#else 1256 v86.ctl = V86_FLAGS; 1257 v86.addr = 0x13; 1258 v86.eax = 0; 1259 v86.edx = od->od_unit; 1260#endif 1261 v86int(); 1262 } 1263 1264#ifdef PC98 1265 v86.ctl = V86_FLAGS; 1266 v86.addr = 0x1b; 1267 if (od->od_flags & BD_FLOPPY) { 1268 v86.eax = 0xd500 | od->od_unit; 1269 v86.ecx = 0x0200 | (cyl & 0xff); 1270 } else { 1271 v86.eax = 0x0500 | od->od_unit; 1272 v86.ecx = cyl; 1273 } 1274 v86.edx = (hd << 8) | sec; 1275 v86.ebx = x * BIOSDISK_SECSIZE; 1276 v86.es = VTOPSEG(xp); 1277 v86.ebp = VTOPOFF(xp); 1278 v86int(); 1279 result = (v86.efl & 0x1); 1280 if (result == 0) 1281 break; 1282#else 1283 if(cyl > 1023) { 1284 /* use EDD if the disk supports it, otherwise, return error */ 1285 if(od->od_flags & BD_MODEEDD1) { 1286 static unsigned short packet[8]; 1287 1288 packet[0] = 0x10; 1289 packet[1] = x; 1290 packet[2] = VTOPOFF(xp); 1291 packet[3] = VTOPSEG(xp); 1292 packet[4] = dblk & 0xffff; 1293 packet[5] = dblk >> 16; 1294 packet[6] = 0; 1295 packet[7] = 0; 1296 v86.ctl = V86_FLAGS; 1297 v86.addr = 0x13; 1298 /* Should we Write with verify ?? 0x4302 ? */ 1299 v86.eax = 0x4300; 1300 v86.edx = od->od_unit; 1301 v86.ds = VTOPSEG(packet); 1302 v86.esi = VTOPOFF(packet); 1303 v86int(); 1304 result = (v86.efl & 0x1); 1305 if(result == 0) 1306 break; 1307 } else { 1308 result = 1; 1309 break; 1310 } 1311 } else { 1312 /* Use normal CHS addressing */ 1313 v86.ctl = V86_FLAGS; 1314 v86.addr = 0x13; 1315 v86.eax = 0x300 | x; 1316 v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; 1317 v86.edx = (hd << 8) | od->od_unit; 1318 v86.es = VTOPSEG(xp); 1319 v86.ebx = VTOPOFF(xp); 1320 v86int(); 1321 result = (v86.efl & 0x1); 1322 if (result == 0) 1323 break; 1324 } 1325#endif 1326 } 1327 1328#ifdef PC98 1329 DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, 1330 od->od_flags & BD_FLOPPY ? sec - 1 : sec, p, VTOP(p), 1331 result ? "failed" : "ok"); 1332 /* BUG here, cannot use v86 in printf because putchar uses it too */ 1333 DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 1334 od->od_flags & BD_FLOPPY ? 0xd600 | od->od_unit : 0x0600 | od->od_unit, 1335 od->od_flags & BD_FLOPPY ? 0x0200 | cyl : cyl, (hd << 8) | sec, 1336 (v86.eax >> 8) & 0xff); 1337#else 1338 DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok"); 1339 /* BUG here, cannot use v86 in printf because putchar uses it too */ 1340 DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 1341 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff); 1342#endif 1343 if (result) { 1344 if (bbuf != NULL) 1345 free(bbuf); 1346 return(-1); 1347 } 1348 } 1349 1350/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */ 1351 if (bbuf != NULL) 1352 free(bbuf); 1353 return(0); 1354} 1355static int 1356bd_getgeom(struct open_disk *od) 1357{ 1358 1359#ifdef PC98 1360 if (od->od_flags & BD_FLOPPY) { 1361 od->od_cyl = 79; 1362 od->od_hds = 2; 1363 od->od_sec = (od->od_unit & 0xf0) == 0x30 ? 18 : 15; 1364 } else { 1365 v86.ctl = V86_FLAGS; 1366 v86.addr = 0x1b; 1367 v86.eax = 0x8400 | od->od_unit; 1368 v86int(); 1369 1370 od->od_cyl = v86.ecx; 1371 od->od_hds = (v86.edx >> 8) & 0xff; 1372 od->od_sec = v86.edx & 0xff; 1373 if (v86.efl & 0x1) 1374 return(1); 1375 } 1376#else 1377 v86.ctl = V86_FLAGS; 1378 v86.addr = 0x13; 1379 v86.eax = 0x800; 1380 v86.edx = od->od_unit; 1381 v86int(); 1382 1383 if ((v86.efl & 0x1) || /* carry set */ 1384 ((v86.edx & 0xff) <= (unsigned)(od->od_unit & 0x7f))) /* unit # bad */ 1385 return(1); 1386 1387 /* convert max cyl # -> # of cylinders */ 1388 od->od_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1; 1389 /* convert max head # -> # of heads */ 1390 od->od_hds = ((v86.edx & 0xff00) >> 8) + 1; 1391 od->od_sec = v86.ecx & 0x3f; 1392#endif 1393 1394 DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec); 1395 return(0); 1396} 1397 1398/* 1399 * Return the BIOS geometry of a given "fixed drive" in a format 1400 * suitable for the legacy bootinfo structure. Since the kernel is 1401 * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we 1402 * prefer to get the information directly, rather than rely on being 1403 * able to put it together from information already maintained for 1404 * different purposes and for a probably different number of drives. 1405 * 1406 * For valid drives, the geometry is expected in the format (31..0) 1407 * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are 1408 * indicated by returning the geometry of a "1.2M" PC-format floppy 1409 * disk. And, incidentally, what is returned is not the geometry as 1410 * such but the highest valid cylinder, head, and sector numbers. 1411 */ 1412u_int32_t 1413bd_getbigeom(int bunit) 1414{ 1415 1416#ifdef PC98 1417 int hds = 0; 1418 int unit = 0x80; /* IDE HDD */ 1419 u_int addr = 0xA155d; 1420 1421 while (unit < 0xa7) { 1422 if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f))) 1423 if (hds++ == bunit) 1424 break; 1425 if (++unit == 0x84) { 1426 unit = 0xa0; /* SCSI HDD */ 1427 addr = 0xA1482; 1428 } 1429 } 1430 if (unit == 0xa7) 1431 return 0x4f010f; 1432 v86.ctl = V86_FLAGS; 1433 v86.addr = 0x1b; 1434 v86.eax = 0x8400 | unit; 1435 v86int(); 1436 if (v86.efl & 0x1) 1437 return 0x4f010f; 1438 return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff); 1439#else 1440 v86.ctl = V86_FLAGS; 1441 v86.addr = 0x13; 1442 v86.eax = 0x800; 1443 v86.edx = 0x80 + bunit; 1444 v86int(); 1445 if (v86.efl & 0x1) 1446 return 0x4f010f; 1447 return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) | 1448 (v86.edx & 0xff00) | (v86.ecx & 0x3f); 1449#endif 1450} 1451 1452/* 1453 * Return a suitable dev_t value for (dev). 1454 * 1455 * In the case where it looks like (dev) is a SCSI disk, we allow the number of 1456 * IDE disks to be specified in $num_ide_disks. There should be a Better Way. 1457 */ 1458int 1459bd_getdev(struct i386_devdesc *dev) 1460{ 1461 struct open_disk *od; 1462 int biosdev; 1463 int major; 1464 int rootdev; 1465 char *nip, *cp; 1466 int unitofs = 0, i, unit; 1467 1468 biosdev = bd_unit2bios(dev->d_kind.biosdisk.unit); 1469 DEBUG("unit %d BIOS device %d", dev->d_kind.biosdisk.unit, biosdev); 1470 if (biosdev == -1) /* not a BIOS device */ 1471 return(-1); 1472 if (bd_opendisk(&od, dev) != 0) /* oops, not a viable device */ 1473 return(-1); 1474 1475#ifdef PC98 1476 if ((biosdev & 0xf0) == 0x90 || (biosdev & 0xf0) == 0x30) { 1477#else 1478 if (biosdev < 0x80) { 1479#endif 1480 /* floppy (or emulated floppy) or ATAPI device */ 1481 if (bdinfo[dev->d_kind.biosdisk.unit].bd_type == DT_ATAPI) { 1482 /* is an ATAPI disk */ 1483 major = WFDMAJOR; 1484 } else { 1485 /* is a floppy disk */ 1486 major = FDMAJOR; 1487 } 1488 } else { 1489 /* harddisk */ 1490 if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) { 1491 /* label OK, disk labelled as SCSI */ 1492 major = DAMAJOR; 1493 /* check for unit number correction hint, now deprecated */ 1494 if ((nip = getenv("num_ide_disks")) != NULL) { 1495 i = strtol(nip, &cp, 0); 1496 /* check for parse error */ 1497 if ((cp != nip) && (*cp == 0)) 1498 unitofs = i; 1499 } 1500 } else { 1501 /* assume an IDE disk */ 1502 major = WDMAJOR; 1503 } 1504 } 1505 /* default root disk unit number */ 1506#ifdef PC98 1507 if ((biosdev & 0xf0) == 0xa0) 1508 unit = bdinfo[dev->d_kind.biosdisk.unit].bd_da_unit; 1509 else 1510 unit = biosdev & 0xf; 1511#else 1512 unit = (biosdev & 0x7f) - unitofs; 1513#endif 1514 1515 /* XXX a better kludge to set the root disk unit number */ 1516 if ((nip = getenv("root_disk_unit")) != NULL) { 1517 i = strtol(nip, &cp, 0); 1518 /* check for parse error */ 1519 if ((cp != nip) && (*cp == 0)) 1520 unit = i; 1521 } 1522 1523 rootdev = MAKEBOOTDEV(major, 1524 (dev->d_kind.biosdisk.slice + 1) >> 4, /* XXX slices may be wrong here */ 1525 (dev->d_kind.biosdisk.slice + 1) & 0xf, 1526 unit, 1527 dev->d_kind.biosdisk.partition); 1528 DEBUG("dev is 0x%x\n", rootdev); 1529 return(rootdev); 1530} 1531