2cf9407bab408fa66c08e8dde335d7a0053bd845
[dragonfly.git] / sys / boot / pc32 / libi386 / biosdisk.c
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: src/sys/boot/i386/libi386/biosdisk.c,v 1.45 2004/09/21 06:46:44 wes Exp $
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/disklabel32.h>
42 #include <sys/disklabel64.h>
43 #include <sys/diskmbr.h>
44 #include <sys/dtype.h>
45 #include <machine/bootinfo.h>
46
47 #include <stdarg.h>
48
49 #include <bootstrap.h>
50 #include <btxv86.h>
51 #include "libi386.h"
52
53 #define BIOS_NUMDRIVES          0x475
54 #define BIOSDISK_SECSIZE        512
55 #define BUFSIZE                 (4 * BIOSDISK_SECSIZE)
56 #define MAXBDDEV                MAXDEV
57
58 #define DT_ATAPI                0x10            /* disk type for ATAPI floppies */
59 #define WDMAJOR                 0               /* major numbers for devices we frontend for */
60 #define WFDMAJOR                1
61 #define FDMAJOR                 2
62 #define DAMAJOR                 4
63
64 #ifdef DISK_DEBUG
65 # define DEBUG(fmt, args...)    printf("%s: " fmt "\n" , __func__ , ## args)
66 #else
67 # define DEBUG(fmt, args...)
68 #endif
69
70 struct open_disk {
71     int                 od_dkunit;              /* disk unit number */
72     int                 od_unit;                /* BIOS unit number */
73     int                 od_cyl;                 /* BIOS geometry */
74     int                 od_hds;
75     int                 od_sec;
76     int                 od_boff;                /* block offset from beginning of BIOS disk */
77     int                 od_flags;
78 #define BD_MODEINT13            0x0000
79 #define BD_MODEEDD1             0x0001
80 #define BD_MODEEDD3             0x0002
81 #define BD_MODEMASK             0x0003
82 #define BD_FLOPPY               0x0004
83 #define BD_LABELOK              0x0008
84 #define BD_PARTTABOK            0x0010
85     union {
86         struct disklabel32              od_disklabel;
87         struct disklabel64              od_disklabel64;
88     };
89     int                         od_nslices;     /* slice count */
90     struct dos_partition        od_slicetab[NEXTDOSPART];
91 };
92
93 /*
94  * List of BIOS devices, translation from disk unit number to
95  * BIOS unit number.
96  */
97 static struct bdinfo
98 {
99     int         bd_unit;                /* BIOS unit number */
100     int         bd_flags;
101     int         bd_type;                /* BIOS 'drive type' (floppy only) */
102 } bdinfo [MAXBDDEV];
103 static int nbdinfo = 0;
104
105 static int      bd_getgeom(struct open_disk *od);
106 static int      bd_read(struct open_disk *od, daddr_t dblk, int blks,
107                     caddr_t dest);
108 static int      bd_write(struct open_disk *od, daddr_t dblk, int blks,
109                     caddr_t dest);
110
111 static int      bd_int13probe(struct bdinfo *bd);
112
113 static void     bd_printslice(struct open_disk *od, struct dos_partition *dp,
114                     char *prefix, int verbose);
115 static void     bd_printbsdslice(struct open_disk *od, daddr_t offset,
116                     char *prefix, int verbose);
117
118 static int      bd_init(void);
119 static int      bd_strategy(void *devdata, int flag, daddr_t dblk,
120                     size_t size, char *buf, size_t *rsize);
121 static int      bd_realstrategy(void *devdata, int flag, daddr_t dblk,
122                     size_t size, char *buf, size_t *rsize);
123 static int      bd_open(struct open_file *f, ...);
124 static int      bd_close(struct open_file *f);
125 static void     bd_print(int verbose);
126
127 struct devsw biosdisk = {
128     "disk", 
129     DEVT_DISK, 
130     bd_init,
131     bd_strategy, 
132     bd_open, 
133     bd_close, 
134     noioctl,
135     bd_print,
136     NULL
137 };
138
139 static int      bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev);
140 static void     bd_closedisk(struct open_disk *od);
141 static int      bd_bestslice(struct open_disk *od);
142 static void     bd_chainextended(struct open_disk *od, u_int32_t base,
143                                         u_int32_t offset);
144
145 char    bounce_base[BOUNCEBUF_SIZE];    /* also used by CD code */
146
147
148 /*
149  * Translate between BIOS device numbers and our private unit numbers.
150  */
151 int
152 bd_bios2unit(int biosdev)
153 {
154     int         i;
155     
156     DEBUG("looking for bios device 0x%x", biosdev);
157     for (i = 0; i < nbdinfo; i++) {
158         DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit);
159         if (bdinfo[i].bd_unit == biosdev)
160             return(i);
161     }
162     return(-1);
163 }
164
165 int
166 bd_unit2bios(int unit)
167 {
168     if ((unit >= 0) && (unit < nbdinfo))
169         return(bdinfo[unit].bd_unit);
170     return(-1);
171 }
172
173 /*    
174  * Quiz the BIOS for disk devices, save a little info about them.
175  */
176 static int
177 bd_init(void) 
178 {
179     int base, unit, nfd = 0;
180
181     /* sequence 0, 0x80 */
182     for (base = 0; base <= 0x80; base += 0x80) {
183         for (unit = base; (nbdinfo < MAXBDDEV); unit++) {
184             /* check the BIOS equipment list for number of fixed disks */
185             if((base == 0x80) &&
186                (nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES)))
187                 break;
188
189             bdinfo[nbdinfo].bd_unit = unit;
190             bdinfo[nbdinfo].bd_flags = (unit < 0x80) ? BD_FLOPPY : 0;
191
192             if (!bd_int13probe(&bdinfo[nbdinfo]))
193                 break;
194
195             /* XXX we need "disk aliases" to make this simpler */
196             printf("BIOS drive %c: is disk%d\n", 
197                    (unit < 0x80) ? ('A' + unit) : ('C' + unit - 0x80), nbdinfo);
198             nbdinfo++;
199             if (base == 0x80)
200                 nfd++;
201         }
202     }
203     return(0);
204 }
205
206 /*
207  * Try to detect a device supported by the legacy int13 BIOS
208  */
209 static int
210 bd_int13probe(struct bdinfo *bd)
211 {
212     v86.ctl = V86_FLAGS;
213     v86.addr = 0x13;
214     v86.eax = 0x800;
215     v86.edx = bd->bd_unit;
216     v86int();
217     
218     if (!(v86.efl & 0x1) &&                             /* carry clear */
219         ((v86.edx & 0xff) > ((unsigned)bd->bd_unit & 0x7f))) {  /* unit # OK */
220
221         /*
222          * Ignore devices with an absurd sector size.
223          */
224         if ((v86.ecx & 0x3f) == 0) {
225                 DEBUG("Invalid geometry for unit %d", bd->bd_unit);
226                 return(0);
227         }
228         bd->bd_flags |= BD_MODEINT13;
229         bd->bd_type = v86.ebx & 0xff;
230
231         /* Determine if we can use EDD with this device. */
232         v86.ctl = V86_FLAGS;
233         v86.addr = 0x13;
234         v86.eax = 0x4100;
235         v86.edx = bd->bd_unit;
236         v86.ebx = 0x55aa;
237         v86int();
238         if (!(v86.efl & 0x1) &&                         /* carry clear */
239             ((v86.ebx & 0xffff) == 0xaa55) &&           /* signature */
240             (v86.ecx & 0x1)) {                          /* packets mode ok */
241             bd->bd_flags |= BD_MODEEDD1;
242             if((v86.eax & 0xff00) > 0x300)
243                 bd->bd_flags |= BD_MODEEDD3;
244         }
245         return(1);
246     }
247     return(0);
248 }
249
250 /*
251  * Print information about disks
252  */
253 static void
254 bd_print(int verbose)
255 {
256     int                         i, j;
257     char                        line[80];
258     struct i386_devdesc         dev;
259     struct open_disk            *od;
260     struct dos_partition        *dptr;
261     
262     for (i = 0; i < nbdinfo; i++) {
263         sprintf(line, "    disk%d:   BIOS drive %c:\n", i, 
264                 (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit) : ('C' + bdinfo[i].bd_unit - 0x80));
265         pager_output(line);
266
267         /* try to open the whole disk */
268         dev.d_kind.biosdisk.unit = i;
269         dev.d_kind.biosdisk.slice = -1;
270         dev.d_kind.biosdisk.partition = -1;
271         
272         if (!bd_opendisk(&od, &dev)) {
273
274             /* Do we have a partition table? */
275             if (od->od_flags & BD_PARTTABOK) {
276                 dptr = &od->od_slicetab[0];
277
278                 /* Check for a "dedicated" disk */
279                 if (((dptr[3].dp_typ == DOSPTYP_386BSD) ||
280                         (dptr[3].dp_typ == DOSPTYP_NETBSD)) &&
281                     (dptr[3].dp_start == 0) &&
282                     (dptr[3].dp_size == 50000)) {
283                     sprintf(line, "      disk%d", i);
284                     bd_printbsdslice(od, 0, line, verbose);
285                 } else {
286                     for (j = 0; j < od->od_nslices; j++) {
287                         sprintf(line, "      disk%ds%d", i, j + 1);
288                         bd_printslice(od, &dptr[j], line, verbose);
289                     }
290                 }
291             }
292             bd_closedisk(od);
293         }
294     }
295 }
296
297 /*
298  * Print information about slices on a disk.  For the size calculations we
299  * assume a 512 byte sector.
300  */
301 static void
302 bd_printslice(struct open_disk *od, struct dos_partition *dp, char *prefix,
303         int verbose)
304 {
305         char line[80];
306
307         switch (dp->dp_typ) {
308         case DOSPTYP_386BSD:
309         case DOSPTYP_NETBSD:
310         /* XXX: possibly add types 0 and 1, as in subr_disk, for gpt magic */
311                 bd_printbsdslice(od, (daddr_t)dp->dp_start, prefix, verbose);
312                 return;
313         case DOSPTYP_LINSWP:
314                 if (verbose)
315                         sprintf(line, "%s: Linux swap %.6dMB (%d - %d)\n",
316                             prefix, dp->dp_size / 2048,
317                             dp->dp_start, dp->dp_start + dp->dp_size);
318                 else
319                         sprintf(line, "%s: Linux swap\n", prefix);
320                 break;
321         case DOSPTYP_LINUX:
322                 /*
323                  * XXX
324                  * read the superblock to confirm this is an ext2fs partition?
325                  */
326                 if (verbose)
327                         sprintf(line, "%s: ext2fs  %.6dMB (%d - %d)\n", prefix,
328                             dp->dp_size / 2048, dp->dp_start,
329                             dp->dp_start + dp->dp_size);
330                 else
331                         sprintf(line, "%s: ext2fs\n", prefix);
332                 break;
333         case 0x00:                              /* unused partition */
334         case DOSPTYP_EXT:
335                 return;
336         case 0x01:
337                 if (verbose)
338                         sprintf(line, "%s: FAT-12  %.6dMB (%d - %d)\n", prefix,
339                             dp->dp_size / 2048, dp->dp_start,
340                             dp->dp_start + dp->dp_size);
341                 else
342                         sprintf(line, "%s: FAT-12\n", prefix);
343                 break;
344         case 0x04:
345         case 0x06:
346         case 0x0e:
347                 if (verbose)
348                         sprintf(line, "%s: FAT-16  %.6dMB (%d - %d)\n", prefix,
349                             dp->dp_size / 2048, dp->dp_start,
350                             dp->dp_start + dp->dp_size);
351                 else
352                         sprintf(line, "%s: FAT-16\n", prefix);
353                 break;
354         case 0x0b:
355         case 0x0c:
356                 if (verbose)
357                         sprintf(line, "%s: FAT-32  %.6dMB (%d - %d)\n", prefix,
358                             dp->dp_size / 2048, dp->dp_start,
359                             dp->dp_start + dp->dp_size);
360                 else
361                         sprintf(line, "%s: FAT-32\n", prefix);
362                 break;
363         default:
364                 if (verbose)
365                         sprintf(line, "%s: Unknown fs: 0x%x  %.6dMB (%d - %d)\n",
366                             prefix, dp->dp_typ, dp->dp_size / 2048,
367                             dp->dp_start, dp->dp_start + dp->dp_size);
368                 else
369                         sprintf(line, "%s: Unknown fs: 0x%x\n", prefix,
370                             dp->dp_typ);
371         }
372         pager_output(line);
373 }
374
375 static void
376 print_partition(u_int8_t fstype, unsigned long long offset,
377                 unsigned long long size, int i, int od_flags,
378                 char *prefix, int verbose, int type)
379 {
380         char    line[80];
381
382         /*
383          * For each partition, make sure we know what type of fs it is.  If
384          * not, then skip it.  However, since floppies often have bogus
385          * fstypes, print the 'a' partition on a floppy even if it is marked
386          * unused.
387          */
388         if ((fstype == FS_SWAP) ||
389             (fstype == FS_VINUM) ||
390             (fstype == FS_HAMMER) ||
391             (fstype == FS_HAMMER2) ||
392             (fstype == FS_BSDFFS) ||
393             (fstype == FS_ZFS) ||
394             (fstype == FS_JFS2) ||
395             ((fstype == FS_UNUSED) &&
396              (od_flags & BD_FLOPPY) && (i == 0))) {
397
398                 /* Only print out statistics in verbose mode */
399                 if (verbose) {
400                         sprintf(line, "%c %s%c: %s  %.6lluMB (%llu - %llu)\n",
401                             /* prefix disks that can be used to load modules with '*' */
402                             ((fstype == FS_BSDFFS) || (fstype == FS_UNUSED) ||
403                             (fstype == FS_VINUM)) ? '*' : ' ',
404                             prefix, 'a' + i,
405                             (fstype == FS_SWAP) ? "swap" :
406                             (fstype == FS_VINUM) ? "vinum" :
407                             (fstype == FS_HAMMER) ? "HAMMER" :
408                             (fstype == FS_HAMMER2) ? "HAMMER2" :
409                             (fstype == FS_JFS2) ? "JFS2" :
410                             (fstype == FS_ZFS) ? "ZFS" :
411                             (fstype == FS_BSDFFS) ? "FFS" :
412                             "(unknown)",
413                             (type==32)?(size / 2048):(size/1024/1024),
414                             offset,
415                             offset + size);
416                 } else {
417                         sprintf(line, "%c %s%c: %s\n",
418                             /* prefix disks that can be used to load modules with '*' */
419                             ((fstype == FS_BSDFFS) || (fstype == FS_UNUSED) ||
420                             (fstype == FS_VINUM)) ? '*' : ' ',
421                             prefix, 'a' + i,
422                             (fstype == FS_SWAP) ? "swap" :
423                             (fstype == FS_VINUM) ? "vinum" :
424                             (fstype == FS_HAMMER) ? "HAMMER" :
425                             (fstype == FS_HAMMER2) ? "HAMMER2" :
426                             (fstype == FS_JFS2) ? "JFS2" :
427                             (fstype == FS_ZFS) ? "ZFS" :
428                             (fstype == FS_BSDFFS) ? "FFS" :
429                             "(unknown)");
430                 }
431
432                 pager_output(line);
433         }
434 }
435
436 /*
437  * Print out each valid partition in the disklabel of a FreeBSD slice.
438  * For size calculations, we assume a 512 byte sector size.
439  */
440 static void
441 bd_printbsdslice(struct open_disk *od, daddr_t offset, char *prefix,
442     int verbose)
443 {
444         char            line[80];
445         char            buf[BIOSDISK_SECSIZE*2];
446         struct disklabel32      *lp = NULL;
447         struct disklabel64      *lp64 = NULL;
448         int                     i;
449
450         /* read disklabel */
451         if (bd_read(od, offset + LABELSECTOR32, 1, buf))
452                 return;
453         lp =(struct disklabel32 *)(&buf[0]);
454         if (lp->d_magic != DISKMAGIC32) {
455                 if (bd_read(od, offset, 2, buf))
456                         return;
457
458                 lp64 =(struct disklabel64 *)(&buf[0]);
459                 if (lp64->d_magic != DISKMAGIC64) {
460                         sprintf(line, "%s: bad disklabel\n", prefix);
461                         pager_output(line);
462                         return;
463                 }
464                 lp = NULL; /* PARANOID */
465         }
466         if (lp64) {
467                 /* We got ourselves a disklabel64 here */
468                 for (i = 0; i < lp64->d_npartitions; i++) {
469                         if (lp64->d_partitions[i].p_bsize == 0)
470                                 continue;
471
472                         print_partition(lp64->d_partitions[i].p_fstype,
473                                         lp64->d_partitions[i].p_boffset,
474                                         lp64->d_partitions[i].p_bsize,
475                                         i, od->od_flags, prefix, verbose, 64);
476                 }
477         } else if (lp) {
478                 /* Print partitions */
479                 for (i = 0; i < lp->d_npartitions; i++) {
480                         print_partition(lp->d_partitions[i].p_fstype,
481                                         lp->d_partitions[i].p_offset,
482                                         lp->d_partitions[i].p_size,
483                                         i, od->od_flags, prefix, verbose, 32);
484                 }
485         }
486 }
487
488
489 /*
490  * Attempt to open the disk described by (dev) for use by (f).
491  *
492  * Note that the philosophy here is "give them exactly what
493  * they ask for".  This is necessary because being too "smart"
494  * about what the user might want leads to complications.
495  * (eg. given no slice or partition value, with a disk that is
496  *  sliced - are they after the first BSD slice, or the DOS
497  *  slice before it?)
498  */
499 static int 
500 bd_open(struct open_file *f, ...)
501 {
502     va_list                     ap;
503     struct i386_devdesc         *dev;
504     struct open_disk            *od;
505     int                         error;
506
507     va_start(ap, f);
508     dev = va_arg(ap, struct i386_devdesc *);
509     va_end(ap);
510     if ((error = bd_opendisk(&od, dev)))
511         return(error);
512     
513     /*
514      * Save our context
515      */
516     ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od;
517     DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff);
518     return(0);
519 }
520
521 static int
522 bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
523 {
524     struct dos_partition        *dptr;
525     struct disklabel32          *lp;
526     struct disklabel64          *lp64;
527     struct open_disk            *od;
528     int                         sector, slice, i;
529     int                         error;
530     static char                 buf[BUFSIZE];
531
532     if (dev->d_kind.biosdisk.unit >= nbdinfo) {
533         DEBUG("attempt to open nonexistent disk");
534         return(ENXIO);
535     }
536     
537     od = (struct open_disk *)malloc(sizeof(struct open_disk));
538     if (!od) {
539         DEBUG("no memory");
540         return (ENOMEM);
541     }
542
543     /* Look up BIOS unit number, intialise open_disk structure */
544     od->od_dkunit = dev->d_kind.biosdisk.unit;
545     od->od_unit = bdinfo[od->od_dkunit].bd_unit;
546     od->od_flags = bdinfo[od->od_dkunit].bd_flags;
547     od->od_boff = 0;
548     od->od_nslices = 0;
549     error = 0;
550     DEBUG("open '%s', unit 0x%x slice %d partition %c",
551              i386_fmtdev(dev), dev->d_kind.biosdisk.unit, 
552              dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition + 'a');
553
554     /* Get geometry for this open (removable device may have changed) */
555     if (bd_getgeom(od)) {
556         DEBUG("can't get geometry");
557         error = ENXIO;
558         goto out;
559     }
560
561     /*
562      * Following calculations attempt to determine the correct value
563      * for d->od_boff by looking for the slice and partition specified,
564      * or searching for reasonable defaults.
565      */
566
567     /*
568      * Find the slice in the DOS slice table.
569      */
570     if (bd_read(od, 0, 1, buf)) {
571         DEBUG("error reading MBR");
572         error = EIO;
573         goto out;
574     }
575
576     /* 
577      * Check the slice table magic.
578      */
579     if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) {
580         /* If a slice number was explicitly supplied, this is an error */
581         if (dev->d_kind.biosdisk.slice > 0) {
582             DEBUG("no slice table/MBR (no magic)");
583             error = ENOENT;
584             goto out;
585         }
586         sector = 0;
587         goto unsliced;          /* may be a floppy */
588     }
589
590     /*
591      * copy the partition table, then pick up any extended partitions.  The
592      * base partition table always has four entries, even if some of them
593      * represented extended partitions.  However, any additional sub-extended
594      * partitions will be silently recursed and not included in the slice
595      * table.
596      */
597     bcopy(buf + DOSPARTOFF, &od->od_slicetab,
598       sizeof(struct dos_partition) * NDOSPART);
599     od->od_nslices = NDOSPART;
600
601     dptr = &od->od_slicetab[0];
602     for (i = 0; i < NDOSPART; i++, dptr++) {
603         if ((dptr->dp_typ == DOSPTYP_EXT) || (dptr->dp_typ == DOSPTYP_EXTLBA))
604             bd_chainextended(od, dptr->dp_start, 0); /* 1st offset is zero */
605     }
606     od->od_flags |= BD_PARTTABOK;
607     dptr = &od->od_slicetab[0];
608
609     /*
610      * Overflow entries are not loaded into memory but we still keep
611      * track of the count.  Fix it up now.
612      */
613     if (od->od_nslices > NEXTDOSPART)
614         od->od_nslices = NEXTDOSPART;
615
616     /* 
617      * Is this a request for the whole disk? 
618      */
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     /*
637      * Check for the historically bogus MBR found on true dedicated disks
638      */
639     if ((dptr[3].dp_typ == DOSPTYP_386BSD) &&
640       (dptr[3].dp_start == 0) &&
641       (dptr[3].dp_size == 50000)) {
642         sector = 0;
643         goto unsliced;
644     }
645
646     /* Try to auto-detect the best slice; this should always give a slice number */
647     if (dev->d_kind.biosdisk.slice == 0) {
648         slice = bd_bestslice(od);
649         if (slice == -1) {
650             error = ENOENT;
651             goto out;
652         }
653         dev->d_kind.biosdisk.slice = slice;
654     }
655
656     dptr = &od->od_slicetab[0];
657     /*
658      * Accept the supplied slice number unequivocally (we may be looking
659      * at a DOS partition).
660      */
661     dptr += (dev->d_kind.biosdisk.slice - 1);   /* we number 1-4, offsets are 0-3 */
662     sector = dptr->dp_start;
663     DEBUG("slice entry %d at %d, %d sectors", dev->d_kind.biosdisk.slice - 1, sector, dptr->dp_size);
664
665     /*
666      * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition
667      */
668     if ((dptr->dp_typ == DOSPTYP_386BSD) && (dev->d_kind.biosdisk.partition < 0))
669         dev->d_kind.biosdisk.partition = 0;
670
671 unsliced:
672     /* 
673      * Now we have the slice offset, look for the partition in the disklabel if we have
674      * a partition to start with.
675      *
676      * XXX we might want to check the label checksum.
677      */
678     if (dev->d_kind.biosdisk.partition < 0) {
679         od->od_boff = sector;           /* no partition, must be after the slice */
680         DEBUG("opening raw slice");
681     } else {
682         
683         if (bd_read(od, sector + LABELSECTOR32, 1, buf)) {
684             DEBUG("error reading disklabel");
685             error = EIO;
686             goto out;
687         }
688         DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel32), buf + LABELOFFSET32, &od->od_disklabel);
689         bcopy(buf + LABELOFFSET32, &od->od_disklabel, sizeof(struct disklabel32));
690         lp = &od->od_disklabel;
691
692         if (lp->d_magic == DISKMAGIC32) {
693             od->od_flags |= BD_LABELOK;
694
695             if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) {
696                 DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
697                       'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions);
698                 error = EPART;
699                 goto out;
700
701             }
702
703 #ifdef DISK_DEBUG
704             /* Complain if the partition is unused unless this is a floppy. */
705             if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) &&
706                 !(od->od_flags & BD_FLOPPY))
707                 DEBUG("warning, partition marked as unused");
708 #endif
709             
710             od->od_boff = 
711                     lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset -
712                     lp->d_partitions[RAW_PART].p_offset +
713                     sector;
714
715             goto out;
716         }
717
718         /* else maybe DISKLABEL64? */
719
720         if (bd_read(od, sector, (sizeof(od->od_disklabel64) + 511) / 512, buf)) {
721             DEBUG("error reading disklabel");
722             error = EIO;
723             goto out;
724         }
725         DEBUG("copy %d bytes of label from %p to %p", sizeof(od->od_disklabel64), buf, &od->od_disklabel64);
726         bcopy(buf, &od->od_disklabel64, sizeof(od->od_disklabel64));
727         lp64 = &od->od_disklabel64;
728
729         if (lp64->d_magic == DISKMAGIC64) {
730             od->od_flags |= BD_LABELOK;
731
732             if (dev->d_kind.biosdisk.partition >= lp64->d_npartitions ||
733                 lp64->d_partitions[dev->d_kind.biosdisk.partition].p_bsize == 0) {
734                 DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
735                       'a' + dev->d_kind.biosdisk.partition, 'a' + lp64->d_npartitions);
736                 error = EPART;
737                 goto out;
738
739             }
740
741             od->od_boff = 
742                     lp64->d_partitions[dev->d_kind.biosdisk.partition].p_boffset / 512 +
743                     sector;
744
745             DEBUG("disklabel64 slice at %d", od->od_boff);
746
747         } else {
748             DEBUG("no disklabel");
749             error = ENOENT;
750         }
751     }
752
753  out:
754     if (error) {
755         free(od);
756     } else {
757         *odp = od;      /* return the open disk */
758     }
759     return(error);
760 }
761
762
763 static void
764 bd_chainextended(struct open_disk *od, u_int32_t base, u_int32_t offset)
765 {
766         char   buf[BIOSDISK_SECSIZE];
767         struct dos_partition *dp1, *dp2;
768         int i;
769
770         if (bd_read(od, (daddr_t)(base + offset), 1, buf)) {
771                 printf("\nerror reading extended partition table");
772                 return;
773         }
774
775         /*
776          * dp1 points to the first record in the on-disk XPT,
777          * dp2 points to the next entry in the in-memory parition table.
778          *
779          * NOTE: dp2 may be out of bounds if od_nslices >= NEXTDOSPART.
780          *
781          * NOTE: unlike the extended partitions in our primary dos partition
782          * table, we do not record recursed extended partitions themselves 
783          * in our in-memory partition table.
784          *
785          * NOTE: recording within our in-memory table must be breadth first
786          * ot match what the kernel does.  Thus, two passes are required.
787          *
788          * NOTE: partitioning programs which support extended partitions seem
789          * to always use the first entry for the user partition and the
790          * second entry to chain, and also appear to disallow more then one
791          * extended partition at each level.  Nevertheless we make our code
792          * somewhat more generic (and the same as the kernel's own slice
793          * scan).
794          */
795         dp1 = (struct dos_partition *)(&buf[DOSPARTOFF]);
796         dp2 = &od->od_slicetab[od->od_nslices];
797
798         for (i = 0; i < NDOSPART; ++i, ++dp1) {
799                 if (dp1->dp_scyl == 0 && dp1->dp_shd == 0 &&
800                     dp1->dp_ssect == 0 && dp1->dp_start == 0 && 
801                     dp1->dp_size == 0) {
802                         continue;
803                 }
804                 if ((dp1->dp_typ == DOSPTYP_EXT) || 
805                     (dp1->dp_typ == DOSPTYP_EXTLBA)) {
806                         /*
807                          * breadth first traversal, must skip in the 
808                          * first pass
809                          */
810                         continue;
811                 }
812
813                 /*
814                  * Only load up the in-memory data if we haven't overflowed
815                  * our in-memory array.
816                  */
817                 if (od->od_nslices < NEXTDOSPART) {
818                         dp2->dp_typ = dp1->dp_typ;
819                         dp2->dp_start = base + offset + dp1->dp_start;
820                         dp2->dp_size = dp1->dp_size;
821                 }
822                 ++od->od_nslices;
823                 ++dp2;
824         }
825
826         /*
827          * Pass 2 - handle extended partitions.  Note that the extended
828          * slice itself is not stored in the slice array when we recurse,
829          * but any 'original' top-level extended slices are.  This is to
830          * match what the kernel does.
831          */
832         dp1 -= NDOSPART;
833         for (i = 0; i < NDOSPART; ++i, ++dp1) {
834                 if (dp1->dp_scyl == 0 && dp1->dp_shd == 0 &&
835                     dp1->dp_ssect == 0 && dp1->dp_start == 0 && 
836                     dp1->dp_size == 0) {
837                         continue;
838                 }
839                 if ((dp1->dp_typ == DOSPTYP_EXT) || 
840                     (dp1->dp_typ == DOSPTYP_EXTLBA))
841                         bd_chainextended(od, base, dp1->dp_start);
842         }
843 }
844
845 /*
846  * Search for a slice with the following preferences:
847  *
848  * 1: Active FreeBSD slice
849  * 2: Non-active FreeBSD slice
850  * 3: Active Linux slice
851  * 4: non-active Linux slice
852  * 5: Active FAT/FAT32 slice
853  * 6: non-active FAT/FAT32 slice
854  */
855 #define PREF_RAWDISK    0
856 #define PREF_FBSD_ACT   1
857 #define PREF_FBSD       2
858 #define PREF_LINUX_ACT  3
859 #define PREF_LINUX      4
860 #define PREF_DOS_ACT    5
861 #define PREF_DOS        6
862 #define PREF_NONE       7
863
864 /*
865  * slicelimit is in the range 0 .. NDOSPART
866  */
867 static int
868 bd_bestslice(struct open_disk *od)
869 {
870         struct dos_partition *dp;
871         int pref, preflevel;
872         int i, prefslice;
873         
874         prefslice = 0;
875         preflevel = PREF_NONE;
876
877         dp = &od->od_slicetab[0];
878         for (i = 0; i < od->od_nslices; i++, dp++) {
879                 switch (dp->dp_typ) {
880                 case DOSPTYP_386BSD:            /* FreeBSD */
881                         pref = dp->dp_flag & 0x80 ? PREF_FBSD_ACT : PREF_FBSD;
882                         break;
883
884                 case DOSPTYP_LINUX:
885                         pref = dp->dp_flag & 0x80 ? PREF_LINUX_ACT : PREF_LINUX;
886                         break;
887     
888                 case 0x01:              /* DOS/Windows */
889                 case 0x04:
890                 case 0x06:
891                 case 0x0b:
892                 case 0x0c:
893                 case 0x0e:
894                         pref = dp->dp_flag & 0x80 ? PREF_DOS_ACT : PREF_DOS;
895                         break;
896
897                 default:
898                         pref = PREF_NONE;
899                 }
900                 if (pref < preflevel) {
901                         preflevel = pref;
902                         prefslice = i + 1;
903                 }
904         }
905         return (prefslice);
906 }
907  
908 static int 
909 bd_close(struct open_file *f)
910 {
911     struct open_disk    *od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data);
912
913     bd_closedisk(od);
914     return(0);
915 }
916
917 static void
918 bd_closedisk(struct open_disk *od)
919 {
920     DEBUG("open_disk %p", od);
921 #if 0
922     /* XXX is this required? (especially if disk already open...) */
923     if (od->od_flags & BD_FLOPPY)
924         delay(3000000);
925 #endif
926     free(od);
927 }
928
929 static int 
930 bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
931 {
932     struct bcache_devdata       bcd;
933     struct open_disk    *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data);
934
935     bcd.dv_strategy = bd_realstrategy;
936     bcd.dv_devdata = devdata;
937     return(bcache_strategy(&bcd, od->od_unit, rw, dblk+od->od_boff, size, buf, rsize));
938 }
939
940 static int 
941 bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
942 {
943     struct open_disk    *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data);
944     int                 blks;
945 #ifdef BD_SUPPORT_FRAGS
946     char                fragbuf[BIOSDISK_SECSIZE];
947     size_t              fragsize;
948
949     fragsize = size % BIOSDISK_SECSIZE;
950 #else
951     if (size % BIOSDISK_SECSIZE)
952         panic("bd_strategy: %d bytes I/O not multiple of block size", size);
953 #endif
954
955     DEBUG("open_disk %p", od);
956
957     switch(rw){
958     case F_READ:
959         blks = size / BIOSDISK_SECSIZE;
960         DEBUG("read %d from %d to %p", blks, dblk, buf);
961
962         if (rsize)
963             *rsize = 0;
964         if (blks && bd_read(od, dblk, blks, buf)) {
965             DEBUG("read error");
966             return (EIO);
967         }
968 #ifdef BD_SUPPORT_FRAGS
969         DEBUG("bd_strategy: frag read %d from %d+%d to %p", 
970              fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE));
971         if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) {
972             DEBUG("frag read error");
973             return(EIO);
974         }
975         bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize);
976 #endif
977         if (rsize)
978             *rsize = size;
979         return (0);
980     case F_WRITE :
981         blks = size / BIOSDISK_SECSIZE;
982         DEBUG("write %d from %d to %p", blks, dblk, buf);
983
984         if (rsize)
985             *rsize = 0;
986         if (blks && bd_write(od, dblk, blks, buf)) {
987             DEBUG("write error");
988             return (EIO);
989         }
990 #ifdef BD_SUPPORT_FRAGS
991         if(fragsize) {
992             DEBUG("Attempted to write a frag");
993             return (EIO);
994         }
995 #endif
996         if (rsize)
997             *rsize = size;
998         return (0);
999     default:
1000         break; /* DO NOTHING */
1001     }
1002     return EROFS;
1003 }
1004
1005 static int
1006 bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest)
1007 {
1008     u_int       x, bpc, cyl, hd, sec, result, resid, retry, maxfer;
1009     caddr_t     p, xp, bbuf, breg;
1010     
1011     /* Just in case some idiot actually tries to read -1 blocks... */
1012     if (blks < 0)
1013         return (-1);
1014
1015     bpc = (od->od_sec * od->od_hds);            /* blocks per cylinder */
1016     resid = blks;
1017     p = dest;
1018
1019     /*
1020      * Always bounce.  Our buffer is probably not segment-addressable.
1021      */
1022     if (1) {
1023
1024         /* 
1025          * There is a 64k physical boundary somewhere in the destination
1026          * buffer, so we have to arrange a suitable bounce buffer.  Allocate
1027          * a buffer twice as large as we need to.  Use the bottom half unless
1028          * there is a break there, in which case we use the top half.
1029          */
1030         bbuf = bounce_base;
1031         x = min(BOUNCEBUF_SECTS, (unsigned)blks);
1032
1033         if (((u_int32_t)VTOP(bbuf) & 0xffff8000) ==
1034             ((u_int32_t)VTOP(bbuf + x * BIOSDISK_SECSIZE - 1) & 0xffff8000)) {
1035             breg = bbuf;
1036         } else {
1037             breg = bbuf + x * BIOSDISK_SECSIZE;
1038         }
1039         maxfer = x;             /* limit transfers to bounce region size */
1040     } else {
1041         breg = bbuf = NULL;
1042         maxfer = 0;
1043     }
1044     
1045     while (resid > 0) {
1046         x = dblk;
1047         cyl = x / bpc;                  /* block # / blocks per cylinder */
1048         x %= bpc;                       /* block offset into cylinder */
1049         hd = x / od->od_sec;            /* offset / blocks per track */
1050         sec = x % od->od_sec;           /* offset into track */
1051
1052         /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */
1053         x = szmin(od->od_sec - sec, resid);
1054         if (maxfer > 0)
1055             x = min(x, maxfer);         /* fit bounce buffer */
1056
1057         /* where do we transfer to? */
1058         xp = (bbuf == NULL ? p : breg);
1059
1060         /* correct sector number for 1-based BIOS numbering */
1061         sec++;
1062
1063         /*
1064          * Loop retrying the operation a couple of times.  The BIOS may also
1065          * retry.
1066          */
1067         for (retry = 0; retry < 3; retry++) {
1068             /*
1069              * If retrying, reset the drive.
1070              */
1071             if (retry > 0) {
1072                 v86.ctl = V86_FLAGS;
1073                 v86.addr = 0x13;
1074                 v86.eax = 0;
1075                 v86.edx = od->od_unit;
1076                 v86int();
1077             }
1078             
1079             /*
1080              * Always use EDD if the disk supports it, otherwise fall back
1081              * to CHS mode (returning an error if the cylinder number is
1082              * too large).
1083              */
1084             if (od->od_flags & BD_MODEEDD1) {
1085                 static unsigned short packet[8];
1086
1087                 packet[0] = 0x10;
1088                 packet[1] = x;
1089                 packet[2] = VTOPOFF(xp);
1090                 packet[3] = VTOPSEG(xp);
1091                 packet[4] = dblk & 0xffff;
1092                 packet[5] = dblk >> 16;
1093                 packet[6] = 0;
1094                 packet[7] = 0;
1095                 v86.ctl = V86_FLAGS;
1096                 v86.addr = 0x13;
1097                 v86.eax = 0x4200;
1098                 v86.edx = od->od_unit;
1099                 v86.ds = VTOPSEG(packet);
1100                 v86.esi = VTOPOFF(packet);
1101                 v86int();
1102                 result = (v86.efl & 0x1);
1103                 if (result == 0)
1104                     break;
1105             } else if (cyl < 1024) {
1106                 /* Use normal CHS addressing */
1107                 v86.ctl = V86_FLAGS;
1108                 v86.addr = 0x13;
1109                 v86.eax = 0x200 | x;
1110                 v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
1111                 v86.edx = (hd << 8) | od->od_unit;
1112                 v86.es = VTOPSEG(xp);
1113                 v86.ebx = VTOPOFF(xp);
1114                 v86int();
1115                 result = (v86.efl & 0x1);
1116                 if (result == 0)
1117                     break;
1118             } else {
1119                 result = 1;
1120                 break;
1121             }
1122         }
1123         
1124         DEBUG("%u sectors from %u/%u/%d to %p (0x%x) %s",
1125               x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok");
1126         /* BUG here, cannot use v86 in printf because putchar uses it too */
1127         DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 
1128               0x200 | x,
1129               ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec,
1130               (hd << 8) | od->od_unit,
1131               (v86.eax >> 8) & 0xff);
1132         if (result)
1133             return(-1);
1134         if (bbuf != NULL)
1135             bcopy(breg, p, x * BIOSDISK_SECSIZE);
1136         p += (x * BIOSDISK_SECSIZE);
1137         dblk += x;
1138         resid -= x;
1139     }
1140         
1141 /*    hexdump(dest, (blks * BIOSDISK_SECSIZE)); */
1142     return(0);
1143 }
1144
1145
1146 static int
1147 bd_write(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest)
1148 {
1149     u_int       x, bpc, cyl, hd, sec, result, resid, retry, maxfer;
1150     caddr_t     p, xp, bbuf, breg;
1151     
1152     /* Just in case some idiot actually tries to read -1 blocks... */
1153     if (blks < 0)
1154         return (-1);
1155
1156     bpc = (od->od_sec * od->od_hds);            /* blocks per cylinder */
1157     resid = blks;
1158     p = dest;
1159
1160     /*
1161      * Always bounce.  Our buffer is probably not segment-addressable.
1162      */
1163     if (1) {
1164         /* 
1165          * There is a 64k physical boundary somewhere in the destination
1166          * buffer, so we have to arrange a suitable bounce buffer.  Allocate
1167          * a buffer twice as large as we need to.  Use the bottom half
1168          * unless there is a break there, in which case we use the top half.
1169          */
1170         bbuf = bounce_base;
1171         x = min(BOUNCEBUF_SECTS, (unsigned)blks);
1172
1173         if (((u_int32_t)VTOP(bbuf) & 0xffff0000) ==
1174             ((u_int32_t)VTOP(bbuf + x * BIOSDISK_SECSIZE - 1) & 0xffff0000)) {
1175             breg = bbuf;
1176         } else {
1177             breg = bbuf + x * BIOSDISK_SECSIZE;
1178         }
1179         maxfer = x;             /* limit transfers to bounce region size */
1180     } else {
1181         breg = bbuf = NULL;
1182         maxfer = 0;
1183     }
1184
1185     while (resid > 0) {
1186         x = dblk;
1187         cyl = x / bpc;                  /* block # / blocks per cylinder */
1188         x %= bpc;                       /* block offset into cylinder */
1189         hd = x / od->od_sec;            /* offset / blocks per track */
1190         sec = x % od->od_sec;           /* offset into track */
1191
1192         /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */
1193         x = szmin(od->od_sec - sec, resid);
1194         if (maxfer > 0)
1195             x = szmin(x, maxfer);               /* fit bounce buffer */
1196
1197         /* where do we transfer to? */
1198         xp = (bbuf == NULL ? p : breg);
1199
1200         /* correct sector number for 1-based BIOS numbering */
1201         sec++;
1202
1203
1204         /* Put your Data In, Put your Data out,
1205            Put your Data In, and shake it all about 
1206         */
1207         if (bbuf != NULL)
1208             bcopy(p, breg, x * BIOSDISK_SECSIZE);
1209         p += (x * BIOSDISK_SECSIZE);
1210         dblk += x;
1211         resid -= x;
1212
1213         /*
1214          * Loop retrying the operation a couple of times.  The BIOS may also
1215          * retry.
1216          */
1217         for (retry = 0; retry < 3; retry++) {
1218             /*
1219              * If retrying, reset the drive.
1220              */
1221             if (retry > 0) {
1222                 v86.ctl = V86_FLAGS;
1223                 v86.addr = 0x13;
1224                 v86.eax = 0;
1225                 v86.edx = od->od_unit;
1226                 v86int();
1227             }
1228
1229             /*
1230              * Always use EDD if the disk supports it, otherwise fall back
1231              * to CHS mode (returning an error if the cylinder number is
1232              * too large).
1233              */
1234             if (od->od_flags & BD_MODEEDD1) {
1235                 static unsigned short packet[8];
1236
1237                 packet[0] = 0x10;
1238                 packet[1] = x;
1239                 packet[2] = VTOPOFF(xp);
1240                 packet[3] = VTOPSEG(xp);
1241                 packet[4] = dblk & 0xffff;
1242                 packet[5] = dblk >> 16;
1243                 packet[6] = 0;
1244                 packet[7] = 0;
1245                 v86.ctl = V86_FLAGS;
1246                 v86.addr = 0x13;
1247                     /* Should we Write with verify ?? 0x4302 ? */
1248                 v86.eax = 0x4300;
1249                 v86.edx = od->od_unit;
1250                 v86.ds = VTOPSEG(packet);
1251                 v86.esi = VTOPOFF(packet);
1252                 v86int();
1253                 result = (v86.efl & 0x1);
1254                 if (result == 0)
1255                     break;
1256             } else if (cyl < 1024) {
1257                 /* Use normal CHS addressing */
1258                 v86.ctl = V86_FLAGS;
1259                 v86.addr = 0x13;
1260                 v86.eax = 0x300 | x;
1261                 v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
1262                 v86.edx = (hd << 8) | od->od_unit;
1263                 v86.es = VTOPSEG(xp);
1264                 v86.ebx = VTOPOFF(xp);
1265                 v86int();
1266                 result = (v86.efl & 0x1);
1267                 if (result == 0)
1268                     break;
1269             } else {
1270                 result = 1;
1271                 break;
1272             }
1273         }
1274         
1275         DEBUG("%u sectors from %u/%u/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok");
1276         /* BUG here, cannot use v86 in printf because putchar uses it too */
1277         DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 
1278               0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff);
1279         if (result)
1280             return(-1);
1281     }
1282         
1283     /* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */
1284     return(0);
1285 }
1286 static int
1287 bd_getgeom(struct open_disk *od)
1288 {
1289
1290     v86.ctl = V86_FLAGS;
1291     v86.addr = 0x13;
1292     v86.eax = 0x800;
1293     v86.edx = od->od_unit;
1294     v86int();
1295
1296     if ((v86.efl & 0x1) ||                              /* carry set */
1297         ((v86.edx & 0xff) <= (unsigned)(od->od_unit & 0x7f)))   /* unit # bad */
1298         return(1);
1299     
1300     /* convert max cyl # -> # of cylinders */
1301     od->od_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
1302     /* convert max head # -> # of heads */
1303     od->od_hds = ((v86.edx & 0xff00) >> 8) + 1;
1304     od->od_sec = v86.ecx & 0x3f;
1305
1306     DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec);
1307     return(0);
1308 }
1309
1310 /*
1311  * Return the BIOS geometry of a given "fixed drive" in a format
1312  * suitable for the legacy bootinfo structure.  Since the kernel is
1313  * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we
1314  * prefer to get the information directly, rather than rely on being
1315  * able to put it together from information already maintained for
1316  * different purposes and for a probably different number of drives.
1317  *
1318  * For valid drives, the geometry is expected in the format (31..0)
1319  * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are
1320  * indicated by returning the geometry of a "1.2M" PC-format floppy
1321  * disk.  And, incidentally, what is returned is not the geometry as
1322  * such but the highest valid cylinder, head, and sector numbers.
1323  */
1324 u_int32_t
1325 bd_getbigeom(int bunit)
1326 {
1327
1328     v86.ctl = V86_FLAGS;
1329     v86.addr = 0x13;
1330     v86.eax = 0x800;
1331     v86.edx = 0x80 + bunit;
1332     v86int();
1333     if (v86.efl & 0x1)
1334         return 0x4f010f;
1335     return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) |
1336            (v86.edx & 0xff00) | (v86.ecx & 0x3f);
1337 }
1338
1339 /*
1340  * Return a suitable cdev_t value for (dev).
1341  *
1342  * In the case where it looks like (dev) is a SCSI disk, we allow the number of
1343  * IDE disks to be specified in $num_ide_disks.  There should be a Better Way.
1344  */
1345 int
1346 bd_getdev(struct i386_devdesc *dev)
1347 {
1348     struct open_disk            *od;
1349     int                         biosdev;
1350     int                         major;
1351     int                         rootdev;
1352     char                        *nip, *cp;
1353     int                         unitofs = 0, i, unit;
1354
1355     biosdev = bd_unit2bios(dev->d_kind.biosdisk.unit);
1356     DEBUG("unit %d BIOS device %d", dev->d_kind.biosdisk.unit, biosdev);
1357     if (biosdev == -1)                          /* not a BIOS device */
1358         return(-1);
1359     if (bd_opendisk(&od, dev) != 0)             /* oops, not a viable device */
1360         return(-1);
1361
1362     if (biosdev < 0x80) {
1363         /* floppy (or emulated floppy) or ATAPI device */
1364         if (bdinfo[dev->d_kind.biosdisk.unit].bd_type == DT_ATAPI) {
1365             /* is an ATAPI disk */
1366             major = WFDMAJOR;
1367         } else {
1368             /* is a floppy disk */
1369             major = FDMAJOR;
1370         }
1371     } else {
1372         /* harddisk */
1373         if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) {
1374             /* label OK, disk labelled as SCSI */
1375             major = DAMAJOR;
1376             /* check for unit number correction hint, now deprecated */
1377             if ((nip = getenv("num_ide_disks")) != NULL) {
1378                 i = strtol(nip, &cp, 0);
1379                 /* check for parse error */
1380                 if ((cp != nip) && (*cp == 0))
1381                     unitofs = i;
1382             }
1383         } else {
1384             /* assume an IDE disk */
1385             major = WDMAJOR;
1386         }
1387     }
1388     /* default root disk unit number */
1389     unit = (biosdev & 0x7f) - unitofs;
1390
1391     /* XXX a better kludge to set the root disk unit number */
1392     if ((nip = getenv("root_disk_unit")) != NULL) {
1393         i = strtol(nip, &cp, 0);
1394         /* check for parse error */
1395         if ((cp != nip) && (*cp == 0))
1396             unit = i;
1397     }
1398
1399     rootdev = MAKEBOOTDEV(major,
1400                           (dev->d_kind.biosdisk.slice + 1) >> 4,        /* XXX slices may be wrong here */
1401                           (dev->d_kind.biosdisk.slice + 1) & 0xf, 
1402                           unit,
1403                           dev->d_kind.biosdisk.partition);
1404     DEBUG("dev is 0x%x\n", rootdev);
1405     return(rootdev);
1406 }