biosdisk.c revision 63101
1/*-
2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/boot/pc98/libpc98/biosdisk.c 63101 2000-07-14 04:23:45Z nyan $
27 */
28
29/*
30 * BIOS disk device handling.
31 *
32 * Ideas and algorithms from:
33 *
34 * - NetBSD libi386/biosdisk.c
35 * - FreeBSD biosboot/disk.c
36 *
37 */
38
39#include <stand.h>
40
41#include <sys/disklabel.h>
42#include <sys/diskslice.h>
43#include <sys/reboot.h>
44
45#include <stdarg.h>
46
47#include <bootstrap.h>
48#include <btxv86.h>
49#include "libi386.h"
50
51#define BIOS_NUMDRIVES		0x475
52#define BIOSDISK_SECSIZE	512
53#define BUFSIZE			(1 * BIOSDISK_SECSIZE)
54#define	MAXBDDEV		MAXDEV
55
56#define DT_ATAPI		0x10		/* disk type for ATAPI floppies */
57#define WDMAJOR			0		/* major numbers for devices we frontend for */
58#define WFDMAJOR		1
59#define FDMAJOR			2
60#define DAMAJOR			4
61
62#ifdef DISK_DEBUG
63# define DEBUG(fmt, args...)	printf("%s: " fmt "\n" , __FUNCTION__ , ## args)
64#else
65# define DEBUG(fmt, args...)
66#endif
67
68struct open_disk {
69    int			od_dkunit;		/* disk unit number */
70    int			od_unit;		/* BIOS unit number */
71    int			od_cyl;			/* BIOS geometry */
72    int			od_hds;
73    int			od_sec;
74    int			od_boff;		/* block offset from beginning of BIOS disk */
75    int			od_flags;
76#define BD_MODEINT13		0x0000
77#define BD_MODEEDD1		0x0001
78#define BD_MODEEDD3		0x0002
79#define BD_MODEMASK		0x0003
80#define BD_FLOPPY		0x0004
81#define BD_LABELOK		0x0008
82#define BD_PARTTABOK		0x0010
83    struct disklabel		od_disklabel;
84    int				od_nslices;	/* slice count */
85    struct dos_partition	od_slicetab[MAX_SLICES];
86};
87
88/*
89 * List of BIOS devices, translation from disk unit number to
90 * BIOS unit number.
91 */
92static struct bdinfo
93{
94    int		bd_unit;		/* BIOS unit number */
95    int		bd_flags;
96    int		bd_type;		/* BIOS 'drive type' (floppy only) */
97#ifdef PC98
98    int		bd_da_unit;		/* kernel unit number for da */
99#endif
100} bdinfo [MAXBDDEV];
101static int nbdinfo = 0;
102
103static int	bd_getgeom(struct open_disk *od);
104static int	bd_read(struct open_disk *od, daddr_t dblk, int blks,
105		    caddr_t dest);
106
107static int	bd_int13probe(struct bdinfo *bd);
108
109static void	bd_printslice(struct open_disk *od, struct dos_partition *dp,
110		    char *prefix);
111static void	bd_printbsdslice(struct open_disk *od, int offset, char *prefix);
112
113static int	bd_init(void);
114static int	bd_strategy(void *devdata, int flag, daddr_t dblk,
115		    size_t size, void *buf, size_t *rsize);
116static int	bd_realstrategy(void *devdata, int flag, daddr_t dblk,
117		    size_t size, void *buf, size_t *rsize);
118static int	bd_open(struct open_file *f, ...);
119static int	bd_close(struct open_file *f);
120static void	bd_print(int verbose);
121
122struct devsw biosdisk = {
123    "disk",
124    DEVT_DISK,
125    bd_init,
126    bd_strategy,
127    bd_open,
128    bd_close,
129    noioctl,
130    bd_print
131};
132
133static int	bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev);
134static void	bd_closedisk(struct open_disk *od);
135static int	bd_bestslice(struct open_disk *od);
136static void	bd_checkextended(struct open_disk *od, int slicenum);
137
138/*
139 * Translate between BIOS device numbers and our private unit numbers.
140 */
141int
142bd_bios2unit(int biosdev)
143{
144    int		i;
145
146    DEBUG("looking for bios device 0x%x", biosdev);
147    for (i = 0; i < nbdinfo; i++) {
148	DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit);
149	if (bdinfo[i].bd_unit == biosdev)
150	    return(i);
151    }
152    return(-1);
153}
154
155int
156bd_unit2bios(int unit)
157{
158    if ((unit >= 0) && (unit < nbdinfo))
159	return(bdinfo[unit].bd_unit);
160    return(-1);
161}
162
163/*
164 * Quiz the BIOS for disk devices, save a little info about them.
165 */
166static int
167bd_init(void)
168{
169#ifdef PC98
170    int		base, unit;
171    int		da_drive=0, n=-0x10;
172
173    /* sequence 0x90, 0x80, 0xa0 */
174    for (base = 0x90; base <= 0xa0; base += n, n += 0x30) {
175	for (unit = base; (nbdinfo < MAXBDDEV) || ((unit & 0x0f) < 4); unit++) {
176	    bdinfo[nbdinfo].bd_unit = unit;
177	    bdinfo[nbdinfo].bd_flags = (unit & 0xf0) == 0x90 ? BD_FLOPPY : 0;
178
179	    if (!bd_int13probe(&bdinfo[nbdinfo])){
180		if (((unit & 0xf0) == 0x90 && (unit & 0x0f) < 4) ||
181		    ((unit & 0xf0) == 0xa0 && (unit & 0x0f) < 6))
182		    continue;	/* Target IDs are not contiguous. */
183		else
184		    break;
185	    }
186
187	    if (bdinfo[nbdinfo].bd_flags & BD_FLOPPY){
188		/* available 1.44MB access? */
189		if (*(u_char *)PTOV(0xA15AE) & (1<<(unit & 0xf))) {
190		    /* boot media 1.2MB FD? */
191		    if ((*(u_char *)PTOV(0xA1584) & 0xf0) != 0x90)
192		        bdinfo[nbdinfo].bd_unit = 0x30 + (unit & 0xf);
193		}
194	    }
195	    else {
196		if ((unit & 0xa0) == 0xa0)
197		    bdinfo[nbdinfo].bd_da_unit = da_drive++;
198	    }
199	    /* XXX we need "disk aliases" to make this simpler */
200	    printf("BIOS drive %c: is disk%d\n",
201		   'A' + nbdinfo, nbdinfo);
202	    nbdinfo++;
203	}
204    }
205#else
206    int		base, unit, nfd = 0;
207
208    /* sequence 0, 0x80 */
209    for (base = 0; base <= 0x80; base += 0x80) {
210	for (unit = base; (nbdinfo < MAXBDDEV); unit++) {
211	    /* check the BIOS equipment list for number of fixed disks */
212	    if((base == 0x80) &&
213	       (nfd >= *(unsigned short *)PTOV(BIOS_NUMDRIVES)))
214	        break;
215
216	    bdinfo[nbdinfo].bd_unit = unit;
217	    bdinfo[nbdinfo].bd_flags = (unit < 0x80) ? BD_FLOPPY : 0;
218
219	    if (!bd_int13probe(&bdinfo[nbdinfo]))
220		break;
221
222	    /* XXX we need "disk aliases" to make this simpler */
223	    printf("BIOS drive %c: is disk%d\n",
224		   (unit < 0x80) ? ('A' + unit) : ('C' + unit - 0x80), nbdinfo);
225	    nbdinfo++;
226	    if (base == 0x80)
227	        nfd++;
228	}
229    }
230#endif
231    return(0);
232}
233
234/*
235 * Try to detect a device supported by the legacy int13 BIOS
236 */
237static int
238bd_int13probe(struct bdinfo *bd)
239{
240#ifdef PC98
241    int addr;
242
243    if (bd->bd_flags & BD_FLOPPY) {
244	addr = 0xa155c;
245    } else {
246	if ((bd->bd_unit & 0xf0) == 0x80)
247	    addr = 0xa155d;
248	else
249	    addr = 0xa1482;
250    }
251    if ( *(u_char *)PTOV(addr) & (1<<(bd->bd_unit & 0x0f))) {
252	bd->bd_flags |= BD_MODEINT13;
253	return(1);
254    }
255    return(0);
256#else
257    v86.ctl = V86_FLAGS;
258    v86.addr = 0x13;
259    v86.eax = 0x800;
260    v86.edx = bd->bd_unit;
261    v86int();
262
263    if (!(v86.efl & 0x1) &&				/* carry clear */
264	((v86.edx & 0xff) > (bd->bd_unit & 0x7f))) {	/* unit # OK */
265	bd->bd_flags |= BD_MODEINT13;
266	bd->bd_type = v86.ebx & 0xff;
267
268	/* Determine if we can use EDD with this device. */
269	v86.eax = 0x4100;
270	v86.edx = bd->bd_unit;
271	v86.ebx = 0x55aa;
272	v86int();
273	if (!(v86.efl & 0x1) &&				/* carry clear */
274	    ((v86.ebx & 0xffff) == 0xaa55) &&		/* signature */
275	    (v86.ecx & 0x1)) {				/* packets mode ok */
276	    bd->bd_flags |= BD_MODEEDD1;
277	    if(v86.eax & 0xff00 > 0x300)
278	        bd->bd_flags |= BD_MODEEDD3;
279	}
280	return(1);
281    }
282    return(0);
283#endif
284}
285
286/*
287 * Print information about disks
288 */
289static void
290bd_print(int verbose)
291{
292    int				i, j;
293    char			line[80];
294    struct i386_devdesc		dev;
295    struct open_disk		*od;
296    struct dos_partition	*dptr;
297
298    for (i = 0; i < nbdinfo; i++) {
299#ifdef PC98
300	sprintf(line, "    disk%d:   BIOS drive %c:\n", i, 'A' + i);
301#else
302	sprintf(line, "    disk%d:   BIOS drive %c:\n", i,
303		(bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit) : ('C' + bdinfo[i].bd_unit - 0x80));
304#endif
305	pager_output(line);
306
307	/* try to open the whole disk */
308	dev.d_kind.biosdisk.unit = i;
309	dev.d_kind.biosdisk.slice = -1;
310	dev.d_kind.biosdisk.partition = -1;
311
312	if (!bd_opendisk(&od, &dev)) {
313
314	    /* Do we have a partition table? */
315	    if (od->od_flags & BD_PARTTABOK) {
316		dptr = &od->od_slicetab[0];
317
318		/* Check for a "truly dedicated" disk */
319#ifdef PC98
320		for (j = 0; j < od->od_nslices; j++) {
321		    switch(dptr[j].dp_mid) {
322		    case DOSMID_386BSD:
323		        sprintf(line, "      disk%ds%d", i, j + 1);
324			bd_printbsdslice(od,
325			    dptr[j].dp_scyl * od->od_hds * od->od_sec +
326			    dptr[j].dp_shd * od->od_sec + dptr[j].dp_ssect,
327			    line);
328			break;
329		    default:
330		    }
331		}
332#else
333		if ((dptr[3].dp_typ == DOSPTYP_386BSD) &&
334		    (dptr[3].dp_start == 0) &&
335		    (dptr[3].dp_size == 50000)) {
336		    sprintf(line, "      disk%d", i);
337		    bd_printbsdslice(od, 0, line);
338		} else {
339		    for (j = 0; j < od->od_nslices; j++) {
340		        sprintf(line, "      disk%ds%d", i, j + 1);
341			bd_printslice(od, &dptr[j], line);
342                    }
343                }
344#endif
345	    }
346	    bd_closedisk(od);
347	}
348    }
349}
350
351/*
352 * Print information about slices on a disk
353 */
354#ifndef PC98
355static void
356bd_printslice(struct open_disk *od, struct dos_partition *dp, char *prefix)
357{
358	char line[80];
359
360	switch (dp->dp_typ) {
361	case DOSPTYP_386BSD:
362		bd_printbsdslice(od, dp->dp_start, prefix);
363		return;
364	case DOSPTYP_LINSWP:
365		sprintf(line, "%s: Linux swap %.6dMB (%d - %d)\n", prefix,
366		    dp->dp_size / 2048,  /* 512-byte sector assumption */
367		    dp->dp_start, dp->dp_start + dp->dp_size);
368		break;
369	case DOSPTYP_LINUX:
370		/*
371		 * XXX
372		 * read the superblock to confirm this is an ext2fs partition?
373		 */
374		sprintf(line, "%s: ext2fs  %.6dMB (%d - %d)\n", prefix,
375		    dp->dp_size / 2048,  /* 512-byte sector assumption */
376		    dp->dp_start, dp->dp_start + dp->dp_size);
377		break;
378	case 0x00:				/* unused partition */
379	case DOSPTYP_EXT:
380		return;
381	case 0x01:
382		sprintf(line, "%s: FAT-12  %.6dMB (%d - %d)\n", prefix,
383		    dp->dp_size / 2048,  /* 512-byte sector assumption */
384		    dp->dp_start, dp->dp_start + dp->dp_size);
385		break;
386	case 0x04:
387	case 0x06:
388	case 0x0e:
389		sprintf(line, "%s: FAT-16  %.6dMB (%d - %d)\n", prefix,
390		    dp->dp_size / 2048,  /* 512-byte sector assumption */
391		    dp->dp_start, dp->dp_start + dp->dp_size);
392		break;
393	case 0x0b:
394	case 0x0c:
395		sprintf(line, "%s: FAT-32  %.6dMB (%d - %d)\n", prefix,
396		    dp->dp_size / 2048,  /* 512-byte sector assumption */
397		    dp->dp_start, dp->dp_start + dp->dp_size);
398		break;
399	default:
400		sprintf(line, "%s: Unknown fs: 0x%x  %.6dMB (%d - %d)\n",
401		    prefix, dp->dp_typ,
402		    dp->dp_size / 2048,  /* 512-byte sector assumption */
403		    dp->dp_start, dp->dp_start + dp->dp_size);
404	}
405	pager_output(line);
406}
407#endif
408
409static void
410bd_printbsdslice(struct open_disk *od, int offset, char *prefix)
411{
412    char		line[80];
413    u_char		buf[BIOSDISK_SECSIZE];
414    struct disklabel	*lp;
415    int			i;
416
417    /* read disklabel */
418    if (bd_read(od, offset + LABELSECTOR, 1, buf))
419	return;
420    lp =(struct disklabel *)(&buf[0]);
421    if (lp->d_magic != DISKMAGIC) {
422	sprintf(line, "%s: FFS  bad disklabel\n", prefix);
423	pager_output(line);
424	return;
425    }
426
427    /* Print partitions */
428    for (i = 0; i < lp->d_npartitions; i++) {
429	if ((lp->d_partitions[i].p_fstype == FS_BSDFFS) ||
430            (lp->d_partitions[i].p_fstype == FS_SWAP) ||
431            (lp->d_partitions[i].p_fstype == FS_VINUM) ||
432	    ((lp->d_partitions[i].p_fstype == FS_UNUSED) &&
433	     (od->od_flags & BD_FLOPPY) && (i == 0))) {	/* Floppies often have bogus fstype, print 'a' */
434	    sprintf(line, "  %s%c: %s  %.6dMB (%d - %d)\n", prefix, 'a' + i,
435		    (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" :
436		    (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" : "FFS",
437		    lp->d_partitions[i].p_size / 2048,	/* 512-byte sector assumption */
438		    lp->d_partitions[i].p_offset, lp->d_partitions[i].p_offset + lp->d_partitions[i].p_size);
439	    pager_output(line);
440	}
441    }
442}
443
444
445/*
446 * Attempt to open the disk described by (dev) for use by (f).
447 *
448 * Note that the philosophy here is "give them exactly what
449 * they ask for".  This is necessary because being too "smart"
450 * about what the user might want leads to complications.
451 * (eg. given no slice or partition value, with a disk that is
452 *  sliced - are they after the first BSD slice, or the DOS
453 *  slice before it?)
454 */
455static int
456bd_open(struct open_file *f, ...)
457{
458    va_list			ap;
459    struct i386_devdesc		*dev;
460    struct open_disk		*od;
461    int				error;
462
463    va_start(ap, f);
464    dev = va_arg(ap, struct i386_devdesc *);
465    va_end(ap);
466    if ((error = bd_opendisk(&od, dev)))
467	return(error);
468
469    /*
470     * Save our context
471     */
472    ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od;
473    DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff);
474    return(0);
475}
476
477static int
478bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
479{
480    struct dos_partition	*dptr;
481    struct disklabel		*lp;
482    struct open_disk		*od;
483    int				sector, slice, i;
484    int				error;
485    u_char			buf[BUFSIZE];
486
487    if (dev->d_kind.biosdisk.unit >= nbdinfo) {
488	DEBUG("attempt to open nonexistent disk");
489	return(ENXIO);
490    }
491
492    od = (struct open_disk *)malloc(sizeof(struct open_disk));
493    if (!od) {
494	DEBUG("no memory");
495	return (ENOMEM);
496    }
497
498    /* Look up BIOS unit number, intialise open_disk structure */
499    od->od_dkunit = dev->d_kind.biosdisk.unit;
500    od->od_unit = bdinfo[od->od_dkunit].bd_unit;
501    od->od_flags = bdinfo[od->od_dkunit].bd_flags;
502    od->od_boff = 0;
503    od->od_nslices = 0;
504    error = 0;
505    DEBUG("open '%s', unit 0x%x slice %d partition %c",
506	     i386_fmtdev(dev), dev->d_kind.biosdisk.unit,
507	     dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition + 'a');
508
509    /* Get geometry for this open (removable device may have changed) */
510    if (bd_getgeom(od)) {
511	DEBUG("can't get geometry");
512	error = ENXIO;
513	goto out;
514    }
515
516    /*
517     * Following calculations attempt to determine the correct value
518     * for d->od_boff by looking for the slice and partition specified,
519     * or searching for reasonable defaults.
520     */
521
522    /*
523     * Find the slice in the DOS slice table.
524     */
525#ifdef PC98
526    if (od->od_flags & BD_FLOPPY) {
527	sector = 0;
528	goto unsliced;
529    }
530#endif
531    if (bd_read(od, 0, 1, buf)) {
532	DEBUG("error reading MBR");
533	error = EIO;
534	goto out;
535    }
536
537    /*
538     * Check the slice table magic.
539     */
540    if ((buf[0x1fe] != 0x55) || (buf[0x1ff] != 0xaa)) {
541	/* If a slice number was explicitly supplied, this is an error */
542	if (dev->d_kind.biosdisk.slice > 0) {
543	    DEBUG("no slice table/MBR (no magic)");
544	    error = ENOENT;
545	    goto out;
546	}
547	sector = 0;
548	goto unsliced;		/* may be a floppy */
549    }
550#ifdef PC98
551    if (bd_read(od, 1, 1, buf)) {
552	DEBUG("error reading MBR");
553	error = EIO;
554	goto out;
555    }
556#endif
557
558    /*
559     * copy the partition table, then pick up any extended partitions.
560     */
561    bcopy(buf + DOSPARTOFF, &od->od_slicetab,
562      sizeof(struct dos_partition) * NDOSPART);
563#ifdef PC98
564    od->od_nslices = NDOSPART;		/* extended slices start here */
565#else
566    od->od_nslices = 4;			/* extended slices start here */
567    for (i = 0; i < NDOSPART; i++)
568        bd_checkextended(od, i);
569#endif
570    od->od_flags |= BD_PARTTABOK;
571    dptr = &od->od_slicetab[0];
572
573    /* Is this a request for the whole disk? */
574    if (dev->d_kind.biosdisk.slice == -1) {
575	sector = 0;
576	goto unsliced;
577    }
578
579    /*
580     * if a slice number was supplied but not found, this is an error.
581     */
582    if (dev->d_kind.biosdisk.slice > 0) {
583        slice = dev->d_kind.biosdisk.slice - 1;
584        if (slice >= od->od_nslices) {
585            DEBUG("slice %d not found", slice);
586	    error = ENOENT;
587	    goto out;
588        }
589    }
590
591#ifndef PC98
592    /*
593     * Check for the historically bogus MBR found on true dedicated disks
594     */
595    if ((dptr[3].dp_typ == DOSPTYP_386BSD) &&
596      (dptr[3].dp_start == 0) &&
597      (dptr[3].dp_size == 50000)) {
598        sector = 0;
599        goto unsliced;
600    }
601#endif
602
603    /* Try to auto-detect the best slice; this should always give a slice number */
604    if (dev->d_kind.biosdisk.slice == 0) {
605	slice = bd_bestslice(od);
606        if (slice == -1) {
607	    error = ENOENT;
608            goto out;
609        }
610        dev->d_kind.biosdisk.slice = slice;
611    }
612
613    dptr = &od->od_slicetab[0];
614    /*
615     * Accept the supplied slice number unequivocally (we may be looking
616     * at a DOS partition).
617     */
618    dptr += (dev->d_kind.biosdisk.slice - 1);	/* we number 1-4, offsets are 0-3 */
619#ifdef PC98
620    sector = dptr->dp_scyl * od->od_hds * od->od_sec +
621	dptr->dp_shd * od->od_sec + dptr->dp_ssect;
622    {
623	int end = dptr->dp_ecyl * od->od_hds * od->od_sec +
624	    dptr->dp_ehd * od->od_sec + dptr->dp_esect;
625	DEBUG("slice entry %d at %d, %d sectors",
626	      dev->d_kind.biosdisk.slice - 1, sector, end-sector);
627    }
628#else
629    sector = dptr->dp_start;
630    DEBUG("slice entry %d at %d, %d sectors", dev->d_kind.biosdisk.slice - 1, sector, dptr->dp_size);
631#endif
632
633    /*
634     * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition
635     */
636#ifdef PC98
637    if ((dptr->dp_mid == DOSMID_386BSD) && (dev->d_kind.biosdisk.partition < 0))
638#else
639    if ((dptr->dp_typ == DOSPTYP_386BSD) && (dev->d_kind.biosdisk.partition < 0))
640#endif
641	dev->d_kind.biosdisk.partition = 0;
642
643 unsliced:
644    /*
645     * Now we have the slice offset, look for the partition in the disklabel if we have
646     * a partition to start with.
647     *
648     * XXX we might want to check the label checksum.
649     */
650    if (dev->d_kind.biosdisk.partition < 0) {
651	od->od_boff = sector;		/* no partition, must be after the slice */
652	DEBUG("opening raw slice");
653    } else {
654
655	if (bd_read(od, sector + LABELSECTOR, 1, buf)) {
656	    DEBUG("error reading disklabel");
657	    error = EIO;
658	    goto out;
659	}
660	DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel);
661	bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel));
662	lp = &od->od_disklabel;
663	od->od_flags |= BD_LABELOK;
664
665	if (lp->d_magic != DISKMAGIC) {
666	    DEBUG("no disklabel");
667	    error = ENOENT;
668	    goto out;
669	}
670	if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) {
671	    DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
672		  'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions);
673	    error = EPART;
674	    goto out;
675
676	}
677
678	/* Complain if the partition type is wrong */
679	if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) &&
680	    !(od->od_flags & BD_FLOPPY))	    /* Floppies often have bogus fstype */
681	    DEBUG("warning, partition marked as unused");
682
683	od->od_boff = lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset;
684    }
685
686 out:
687    if (error) {
688	free(od);
689    } else {
690	*odp = od;	/* return the open disk */
691    }
692    return(error);
693}
694
695#ifndef PC98
696static void
697bd_checkextended(struct open_disk *od, int slicenum)
698{
699	u_char buf[BIOSDISK_SECSIZE];
700	struct dos_partition *dp;
701	u_int base;
702	int i, start, end;
703
704	dp = &od->od_slicetab[slicenum];
705	start = od->od_nslices;
706
707	if (dp->dp_size == 0)
708		goto done;
709	if (dp->dp_typ != DOSPTYP_EXT)
710		goto done;
711	if (bd_read(od, dp->dp_start, 1, buf))
712		goto done;
713	if ((buf[0x1fe] != 0x55) || (buf[0x1ff] != 0xaa)) {
714		DEBUG("no magic in extended table");
715		goto done;
716	}
717	base = dp->dp_start;
718	dp = (struct dos_partition *)(&buf[DOSPARTOFF]);
719	for (i = 0; i < NDOSPART; i++, dp++) {
720		if (dp->dp_size == 0)
721			continue;
722		if (od->od_nslices == MAX_SLICES)
723			goto done;
724		dp->dp_start += base;
725		bcopy(dp, &od->od_slicetab[od->od_nslices], sizeof(*dp));
726		od->od_nslices++;
727	}
728	end = od->od_nslices;
729
730	/*
731	 * now, recursively check the slices we just added
732	 */
733	for (i = start; i < end; i++)
734		bd_checkextended(od, i);
735done:
736	return;
737}
738#endif
739
740/*
741 * Search for a slice with the following preferences:
742 *
743 * 1: Active FreeBSD slice
744 * 2: Non-active FreeBSD slice
745 * 3: Active Linux slice
746 * 4: non-active Linux slice
747 * 5: Active FAT/FAT32 slice
748 * 6: non-active FAT/FAT32 slice
749 */
750#define PREF_RAWDISK	0
751#define PREF_FBSD_ACT	1
752#define PREF_FBSD	2
753#define PREF_LINUX_ACT	3
754#define PREF_LINUX	4
755#define PREF_DOS_ACT	5
756#define PREF_DOS	6
757#define PREF_NONE	7
758
759/*
760 * slicelimit is in the range 0 .. NDOSPART
761 */
762static int
763bd_bestslice(struct open_disk *od)
764{
765	struct dos_partition *dp;
766	int pref, preflevel;
767	int i, prefslice;
768
769	prefslice = 0;
770	preflevel = PREF_NONE;
771
772	dp = &od->od_slicetab[0];
773	for (i = 0; i < od->od_nslices; i++, dp++) {
774#ifdef PC98
775		switch(dp->dp_mid & 0x7f) {
776		case DOSMID_386BSD & 0x7f:		/* FreeBSD */
777			if ((dp->dp_mid & 0x80) &&
778			    (preflevel > PREF_FBSD_ACT)) {
779				pref = i;
780				preflevel = PREF_FBSD_ACT;
781			} else if (preflevel > PREF_FBSD) {
782				pref = i;
783				preflevel = PREF_FBSD;
784			}
785			break;
786
787		case 0x11:				/* DOS/Windows */
788		case 0x20:
789		case 0x21:
790		case 0x22:
791		case 0x23:
792		case 0x63:
793			if ((dp->dp_mid & 0x80) &&
794			    (preflevel > PREF_DOS_ACT)) {
795				pref = i;
796				preflevel = PREF_DOS_ACT;
797			} else if (preflevel > PREF_DOS) {
798				pref = i;
799				preflevel = PREF_DOS;
800			}
801			break;
802		}
803#else
804		switch (dp->dp_typ) {
805		case DOSPTYP_386BSD:		/* FreeBSD */
806			pref = dp->dp_flag & 0x80 ? PREF_FBSD_ACT : PREF_FBSD;
807			break;
808
809		case DOSPTYP_LINUX:
810			pref = dp->dp_flag & 0x80 ? PREF_LINUX_ACT : PREF_LINUX;
811			break;
812
813		case 0x01:		/* DOS/Windows */
814		case 0x04:
815		case 0x06:
816		case 0x0b:
817		case 0x0c:
818		case 0x0e:
819			pref = dp->dp_flag & 0x80 ? PREF_DOS_ACT : PREF_DOS;
820			break;
821
822		default:
823		        pref = PREF_NONE;
824		}
825		if (pref < preflevel) {
826			preflevel = pref;
827			prefslice = i + 1;
828		}
829#endif
830	}
831	return (prefslice);
832}
833
834static int
835bd_close(struct open_file *f)
836{
837    struct open_disk	*od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data);
838
839    bd_closedisk(od);
840    return(0);
841}
842
843static void
844bd_closedisk(struct open_disk *od)
845{
846    DEBUG("open_disk %p", od);
847#if 0
848    /* XXX is this required? (especially if disk already open...) */
849    if (od->od_flags & BD_FLOPPY)
850	delay(3000000);
851#endif
852    free(od);
853}
854
855static int
856bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, void *buf, size_t *rsize)
857{
858    struct bcache_devdata	bcd;
859    struct open_disk	*od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data);
860
861    bcd.dv_strategy = bd_realstrategy;
862    bcd.dv_devdata = devdata;
863    return(bcache_strategy(&bcd, od->od_unit, rw, dblk+od->od_boff, size, buf, rsize));
864}
865
866static int
867bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, void *buf, size_t *rsize)
868{
869    struct open_disk	*od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data);
870    int			blks;
871#ifdef BD_SUPPORT_FRAGS
872    char		fragbuf[BIOSDISK_SECSIZE];
873    size_t		fragsize;
874
875    fragsize = size % BIOSDISK_SECSIZE;
876#else
877    if (size % BIOSDISK_SECSIZE)
878	panic("bd_strategy: %d bytes I/O not multiple of block size", size);
879#endif
880
881    DEBUG("open_disk %p", od);
882
883    if (rw != F_READ)
884	return(EROFS);
885
886
887    blks = size / BIOSDISK_SECSIZE;
888    DEBUG("read %d from %d to %p", blks, dblk, buf);
889
890    if (rsize)
891	*rsize = 0;
892    if (blks && bd_read(od, dblk, blks, buf)) {
893	DEBUG("read error");
894	return (EIO);
895    }
896#ifdef BD_SUPPORT_FRAGS
897    DEBUG("bd_strategy: frag read %d from %d+%d to %p",
898	     fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE));
899    if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) {
900	DEBUG("frag read error");
901	return(EIO);
902    }
903    bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize);
904#endif
905    if (rsize)
906	*rsize = size;
907    return (0);
908}
909
910/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */
911#define FLOPPY_BOUNCEBUF	18
912
913static int
914bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest)
915{
916    int		x, bpc, cyl, hd, sec, result, resid, cnt, retry, maxfer;
917    caddr_t	p, xp, bbuf, breg;
918
919    bpc = (od->od_sec * od->od_hds);		/* blocks per cylinder */
920    resid = blks;
921    p = dest;
922
923    /* Decide whether we have to bounce */
924#ifdef PC98
925    if (
926#else
927    if ((od->od_unit < 0x80) &&
928#endif
929	((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) {
930
931	/*
932	 * There is a 64k physical boundary somewhere in the destination buffer, so we have
933	 * to arrange a suitable bounce buffer.  Allocate a buffer twice as large as we
934	 * need to.  Use the bottom half unless there is a break there, in which case we
935	 * use the top half.
936	 */
937#ifdef PC98
938	x = min(od->od_sec, blks);
939#else
940	x = min(FLOPPY_BOUNCEBUF, blks);
941#endif
942	bbuf = malloc(x * 2 * BIOSDISK_SECSIZE);
943	if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(dest + x * BIOSDISK_SECSIZE) & 0xffff0000)) {
944	    breg = bbuf;
945	} else {
946	    breg = bbuf + x * BIOSDISK_SECSIZE;
947	}
948	maxfer = x;			/* limit transfers to bounce region size */
949    } else {
950	bbuf = NULL;
951	maxfer = 0;
952    }
953
954    while (resid > 0) {
955	x = dblk;
956	cyl = x / bpc;			/* block # / blocks per cylinder */
957	x %= bpc;			/* block offset into cylinder */
958	hd = x / od->od_sec;		/* offset / blocks per track */
959	sec = x % od->od_sec;		/* offset into track */
960
961	/* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */
962	x = min(od->od_sec - sec, resid);
963	if (maxfer > 0)
964	    x = min(x, maxfer);		/* fit bounce buffer */
965
966	/* where do we transfer to? */
967	xp = bbuf == NULL ? p : breg;
968
969	/* correct sector number for 1-based BIOS numbering */
970#ifdef PC98
971	if ((od->od_unit & 0xf0) == 0x30 || (od->od_unit & 0xf0) == 0x90)
972	    sec++;
973#else
974	sec++;
975#endif
976
977	/* Loop retrying the operation a couple of times.  The BIOS may also retry. */
978	for (retry = 0; retry < 3; retry++) {
979	    /* if retrying, reset the drive */
980	    if (retry > 0) {
981#ifdef PC98
982		v86.ctl = V86_FLAGS;
983		v86.addr = 0x1b;
984		v86.eax = 0x0300 | od->od_unit;
985#else
986		v86.ctl = V86_FLAGS;
987		v86.addr = 0x13;
988		v86.eax = 0;
989		v86.edx = od->od_unit;
990#endif
991		v86int();
992	    }
993
994#ifdef PC98
995	    v86.ctl = V86_FLAGS;
996	    v86.addr = 0x1b;
997	    if (od->od_flags & BD_FLOPPY) {
998	        v86.eax = 0xd600 | od->od_unit;
999		v86.ecx = 0x0200 | (cyl & 0xff);
1000	    }
1001	    else {
1002	        v86.eax = 0x0600 | od->od_unit;
1003		v86.ecx = cyl;
1004	    }
1005	    v86.edx = (hd << 8) | sec;
1006	    v86.ebx = x * BIOSDISK_SECSIZE;
1007	    v86.es = VTOPSEG(xp);
1008	    v86.ebp = VTOPOFF(xp);
1009	    v86int();
1010	    result = (v86.efl & 0x1);
1011	    if (result == 0)
1012		break;
1013#else
1014	    if(cyl > 1023) {
1015	        /* use EDD if the disk supports it, otherwise, return error */
1016	        if(od->od_flags & BD_MODEEDD1) {
1017		    static unsigned short packet[8];
1018
1019		    packet[0] = 0x10;
1020		    packet[1] = x;
1021		    packet[2] = VTOPOFF(xp);
1022		    packet[3] = VTOPSEG(xp);
1023		    packet[4] = dblk & 0xffff;
1024		    packet[5] = dblk >> 16;
1025		    packet[6] = 0;
1026		    packet[7] = 0;
1027		    v86.ctl = V86_FLAGS;
1028		    v86.addr = 0x13;
1029		    v86.eax = 0x4200;
1030		    v86.edx = od->od_unit;
1031		    v86.ds = VTOPSEG(packet);
1032		    v86.esi = VTOPOFF(packet);
1033		    v86int();
1034		    result = (v86.efl & 0x1);
1035		    if(result == 0)
1036		      break;
1037		} else {
1038		    result = 1;
1039		    break;
1040		}
1041	    } else {
1042	        /* Use normal CHS addressing */
1043	        v86.ctl = V86_FLAGS;
1044		v86.addr = 0x13;
1045		v86.eax = 0x200 | x;
1046		v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
1047		v86.edx = (hd << 8) | od->od_unit;
1048		v86.es = VTOPSEG(xp);
1049		v86.ebx = VTOPOFF(xp);
1050		v86int();
1051		result = (v86.efl & 0x1);
1052		if (result == 0)
1053		  break;
1054	    }
1055#endif
1056	}
1057
1058#ifdef PC98
1059 	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");
1060	/* BUG here, cannot use v86 in printf because putchar uses it too */
1061	DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x",
1062	      od->od_flags & BD_FLOPPY ? 0xd600 | od->od_unit : 0x0600 | od->od_unit,
1063	      od->od_flags & BD_FLOPPY ? 0x0200 | cyl : cyl, (hd << 8) | sec,
1064	      (v86.eax >> 8) & 0xff);
1065#else
1066 	DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok");
1067	/* BUG here, cannot use v86 in printf because putchar uses it too */
1068	DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x",
1069	      0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff);
1070#endif
1071	if (result) {
1072	    if (bbuf != NULL)
1073		free(bbuf);
1074	    return(-1);
1075	}
1076	if (bbuf != NULL)
1077	    bcopy(breg, p, x * BIOSDISK_SECSIZE);
1078	p += (x * BIOSDISK_SECSIZE);
1079	dblk += x;
1080	resid -= x;
1081    }
1082
1083/*    hexdump(dest, (blks * BIOSDISK_SECSIZE)); */
1084    if (bbuf != NULL)
1085	free(bbuf);
1086    return(0);
1087}
1088
1089static int
1090bd_getgeom(struct open_disk *od)
1091{
1092
1093#ifdef PC98
1094    if (od->od_flags & BD_FLOPPY) {
1095	od->od_cyl = 79;
1096	od->od_hds = 2;
1097	od->od_sec = (od->od_unit & 0xf0) == 0x30 ? 18 : 15;
1098    } else {
1099	v86.ctl = V86_FLAGS;
1100	v86.addr = 0x1b;
1101	v86.eax = 0x8400 | od->od_unit;
1102	v86int();
1103
1104	od->od_cyl = v86.ecx;
1105	od->od_hds = (v86.edx >> 8) & 0xff;
1106	od->od_sec = v86.edx & 0xff;
1107	if (v86.efl & 0x1)
1108	    return(1);
1109    }
1110#else
1111    v86.ctl = V86_FLAGS;
1112    v86.addr = 0x13;
1113    v86.eax = 0x800;
1114    v86.edx = od->od_unit;
1115    v86int();
1116
1117    if ((v86.efl & 0x1) ||				/* carry set */
1118	((v86.edx & 0xff) <= (od->od_unit & 0x7f)))	/* unit # bad */
1119	return(1);
1120
1121    /* convert max cyl # -> # of cylinders */
1122    od->od_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
1123    /* convert max head # -> # of heads */
1124    od->od_hds = ((v86.edx & 0xff00) >> 8) + 1;
1125    od->od_sec = v86.ecx & 0x3f;
1126#endif
1127
1128    DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec);
1129    return(0);
1130}
1131
1132/*
1133 * Return the BIOS geometry of a given "fixed drive" in a format
1134 * suitable for the legacy bootinfo structure.  Since the kernel is
1135 * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we
1136 * prefer to get the information directly, rather than rely on being
1137 * able to put it together from information already maintained for
1138 * different purposes and for a probably different number of drives.
1139 *
1140 * For valid drives, the geometry is expected in the format (31..0)
1141 * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are
1142 * indicated by returning the geometry of a "1.2M" PC-format floppy
1143 * disk.  And, incidentally, what is returned is not the geometry as
1144 * such but the highest valid cylinder, head, and sector numbers.
1145 */
1146u_int32_t
1147bd_getbigeom(int bunit)
1148{
1149
1150#ifdef PC98
1151    int hds = 0;
1152    int unit = 0x80;		/* IDE HDD */
1153    u_int addr = 0xA155d;
1154
1155    while (unit < 0xa7) {
1156	if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f)))
1157	    if (hds++ == bunit)
1158		break;
1159	if (++unit == 0x84) {
1160	    unit = 0xa0;	/* SCSI HDD */
1161	    addr = 0xA1482;
1162	}
1163    }
1164    if (unit == 0xa7)
1165	return 0x4f010f;
1166    v86.ctl = V86_FLAGS;
1167    v86.addr = 0x1b;
1168    v86.eax = 0x8400 | unit;
1169    v86int();
1170    if (v86.efl & 0x1)
1171	return 0x4f010f;
1172    return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff);
1173#else
1174    v86.ctl = V86_FLAGS;
1175    v86.addr = 0x13;
1176    v86.eax = 0x800;
1177    v86.edx = 0x80 + bunit;
1178    v86int();
1179    if (v86.efl & 0x1)
1180	return 0x4f010f;
1181    return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) |
1182	   (v86.edx & 0xff00) | (v86.ecx & 0x3f);
1183#endif
1184}
1185
1186/*
1187 * Return a suitable dev_t value for (dev).
1188 *
1189 * In the case where it looks like (dev) is a SCSI disk, we allow the number of
1190 * IDE disks to be specified in $num_ide_disks.  There should be a Better Way.
1191 */
1192int
1193bd_getdev(struct i386_devdesc *dev)
1194{
1195    struct open_disk		*od;
1196    int				biosdev;
1197    int 			major;
1198    int				rootdev;
1199    char			*nip, *cp;
1200    int				unitofs = 0, i, unit;
1201
1202    biosdev = bd_unit2bios(dev->d_kind.biosdisk.unit);
1203    DEBUG("unit %d BIOS device %d", dev->d_kind.biosdisk.unit, biosdev);
1204    if (biosdev == -1)				/* not a BIOS device */
1205	return(-1);
1206    if (bd_opendisk(&od, dev) != 0)		/* oops, not a viable device */
1207	return(-1);
1208
1209#ifdef PC98
1210    if ((biosdev & 0xf0) == 0x90 || (biosdev & 0xf0) == 0x30) {
1211#else
1212    if (biosdev < 0x80) {
1213#endif
1214	/* floppy (or emulated floppy) or ATAPI device */
1215	if (bdinfo[dev->d_kind.biosdisk.unit].bd_type == DT_ATAPI) {
1216	    /* is an ATAPI disk */
1217	    major = WFDMAJOR;
1218	} else {
1219	    /* is a floppy disk */
1220	    major = FDMAJOR;
1221	}
1222    } else {
1223	/* harddisk */
1224	if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) {
1225	    /* label OK, disk labelled as SCSI */
1226	    major = DAMAJOR;
1227	    /* check for unit number correction hint, now deprecated */
1228	    if ((nip = getenv("num_ide_disks")) != NULL) {
1229		i = strtol(nip, &cp, 0);
1230		/* check for parse error */
1231		if ((cp != nip) && (*cp == 0))
1232		    unitofs = i;
1233	    }
1234	} else {
1235	    /* assume an IDE disk */
1236	    major = WDMAJOR;
1237	}
1238    }
1239    /* XXX a better kludge to set the root disk unit number */
1240    if ((nip = getenv("root_disk_unit")) != NULL) {
1241	i = strtol(nip, &cp, 0);
1242	/* check for parse error */
1243	if ((cp != nip) && (*cp == 0))
1244	    unit = i;
1245    } else {
1246#ifdef PC98
1247	if ((biosdev & 0xf0) == 0xa0)
1248	    unit = bdinfo[dev->d_kind.biosdisk.unit].bd_da_unit;
1249	else
1250	    unit = biosdev & 0xf;
1251#else
1252	unit = (biosdev & 0x7f) - unitofs;					/* allow for #wd compenstation in da case */
1253#endif
1254    }
1255
1256    rootdev = MAKEBOOTDEV(major,
1257			  (dev->d_kind.biosdisk.slice + 1) >> 4, 	/* XXX slices may be wrong here */
1258			  (dev->d_kind.biosdisk.slice + 1) & 0xf,
1259			  unit,
1260			  dev->d_kind.biosdisk.partition);
1261    DEBUG("dev is 0x%x\n", rootdev);
1262    return(rootdev);
1263}
1264
1265/*
1266 * Fix (dev) so that it refers to the 'real' disk/slice/partition that it implies.
1267 */
1268int
1269bd_fixupdev(struct i386_devdesc *dev)
1270{
1271    struct open_disk *od;
1272
1273    /*
1274     * Open the disk.  This will fix up the slice and partition fields.
1275     */
1276    if (bd_opendisk(&od, dev) != 0)
1277	return(ENOENT);
1278
1279    bd_closedisk(od);
1280}
1281