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