]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/boot/i386/libi386/biosdisk.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / boot / i386 / libi386 / biosdisk.c
1 /*-
2  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3  * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 /*
32  * BIOS disk device handling.
33  * 
34  * Ideas and algorithms from:
35  *
36  * - NetBSD libi386/biosdisk.c
37  * - FreeBSD biosboot/disk.c
38  *
39  */
40
41 #include <sys/disk.h>
42 #include <stand.h>
43 #include <machine/bootinfo.h>
44 #include <stdarg.h>
45
46 #include <bootstrap.h>
47 #include <btxv86.h>
48 #include <edd.h>
49 #include "disk.h"
50 #include "libi386.h"
51
52 CTASSERT(sizeof(struct i386_devdesc) >= sizeof(struct disk_devdesc));
53
54 #define BIOS_NUMDRIVES          0x475
55 #define BIOSDISK_SECSIZE        512
56 #define BUFSIZE                 (1 * BIOSDISK_SECSIZE)
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 /*
71  * List of BIOS devices, translation from disk unit number to
72  * BIOS unit number.
73  */
74 static struct bdinfo
75 {
76         int             bd_unit;        /* BIOS unit number */
77         int             bd_cyl;         /* BIOS geometry */
78         int             bd_hds;
79         int             bd_sec;
80         int             bd_flags;
81 #define BD_MODEINT13    0x0000
82 #define BD_MODEEDD1     0x0001
83 #define BD_MODEEDD3     0x0002
84 #define BD_MODEMASK     0x0003
85 #define BD_FLOPPY       0x0004
86         int             bd_type;        /* BIOS 'drive type' (floppy only) */
87         uint16_t        bd_sectorsize;  /* Sector size */
88         uint64_t        bd_sectors;     /* Disk size */
89 } bdinfo [MAXBDDEV];
90 static int nbdinfo = 0;
91
92 #define BD(dev)         (bdinfo[(dev)->d_unit])
93
94 static int bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks,
95     caddr_t dest);
96 static int bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks,
97     caddr_t dest);
98 static int bd_int13probe(struct bdinfo *bd);
99
100 static int bd_init(void);
101 static int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
102     char *buf, size_t *rsize);
103 static int bd_realstrategy(void *devdata, int flag, daddr_t dblk,
104     size_t size, char *buf, size_t *rsize);
105 static int bd_open(struct open_file *f, ...);
106 static int bd_close(struct open_file *f);
107 static int bd_ioctl(struct open_file *f, u_long cmd, void *data);
108 static void bd_print(int verbose);
109 static void bd_cleanup(void);
110
111 struct devsw biosdisk = {
112         "disk",
113         DEVT_DISK,
114         bd_init,
115         bd_strategy,
116         bd_open,
117         bd_close,
118         bd_ioctl,
119         bd_print,
120         bd_cleanup
121 };
122
123 /*
124  * Translate between BIOS device numbers and our private unit numbers.
125  */
126 int
127 bd_bios2unit(int biosdev)
128 {
129         int i;
130
131         DEBUG("looking for bios device 0x%x", biosdev);
132         for (i = 0; i < nbdinfo; i++) {
133                 DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit);
134                 if (bdinfo[i].bd_unit == biosdev)
135                         return (i);
136         }
137         return (-1);
138 }
139
140 int
141 bd_unit2bios(int unit)
142 {
143
144         if ((unit >= 0) && (unit < nbdinfo))
145                 return (bdinfo[unit].bd_unit);
146         return (-1);
147 }
148
149 /*
150  * Quiz the BIOS for disk devices, save a little info about them.
151  */
152 static int
153 bd_init(void)
154 {
155         int base, unit, nfd = 0;
156
157         /* sequence 0, 0x80 */
158         for (base = 0; base <= 0x80; base += 0x80) {
159                 for (unit = base; (nbdinfo < MAXBDDEV); unit++) {
160 #ifndef VIRTUALBOX
161                         /*
162                          * Check the BIOS equipment list for number
163                          * of fixed disks.
164                          */
165                         if(base == 0x80 &&
166                             (nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES)))
167                                 break;
168 #endif
169                         bdinfo[nbdinfo].bd_unit = unit;
170                         bdinfo[nbdinfo].bd_flags = unit < 0x80 ? BD_FLOPPY: 0;
171                         if (!bd_int13probe(&bdinfo[nbdinfo]))
172                                 break;
173
174                         /* XXX we need "disk aliases" to make this simpler */
175                         printf("BIOS drive %c: is disk%d\n", (unit < 0x80) ?
176                             ('A' + unit): ('C' + unit - 0x80), nbdinfo);
177                         nbdinfo++;
178                         if (base == 0x80)
179                                 nfd++;
180                 }
181         }
182         return(0);
183 }
184
185 static void
186 bd_cleanup(void)
187 {
188
189         disk_cleanup(&biosdisk);
190 }
191
192 /*
193  * Try to detect a device supported by the legacy int13 BIOS
194  */
195 static int
196 bd_int13probe(struct bdinfo *bd)
197 {
198         struct edd_params params;
199
200         v86.ctl = V86_FLAGS;
201         v86.addr = 0x13;
202         v86.eax = 0x800;
203         v86.edx = bd->bd_unit;
204         v86int();
205
206         if (V86_CY(v86.efl) ||  /* carry set */
207             (v86.ecx & 0x3f) == 0 || /* absurd sector number */
208             (v86.edx & 0xff) <= (unsigned)(bd->bd_unit & 0x7f)) /* unit # bad */
209                 return (0);     /* skip device */
210
211         /* Convert max cyl # -> # of cylinders */
212         bd->bd_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
213         /* Convert max head # -> # of heads */
214         bd->bd_hds = ((v86.edx & 0xff00) >> 8) + 1;
215         bd->bd_sec = v86.ecx & 0x3f;
216         bd->bd_type = v86.ebx & 0xff;
217         bd->bd_flags |= BD_MODEINT13;
218
219         /* Calculate sectors count from the geometry */
220         bd->bd_sectors = bd->bd_cyl * bd->bd_hds * bd->bd_sec;
221         bd->bd_sectorsize = BIOSDISK_SECSIZE;
222         DEBUG("unit 0x%x geometry %d/%d/%d", bd->bd_unit, bd->bd_cyl,
223             bd->bd_hds, bd->bd_sec);
224
225         /* Determine if we can use EDD with this device. */
226         v86.ctl = V86_FLAGS;
227         v86.addr = 0x13;
228         v86.eax = 0x4100;
229         v86.edx = bd->bd_unit;
230         v86.ebx = 0x55aa;
231         v86int();
232         if (V86_CY(v86.efl) ||  /* carry set */
233             (v86.ebx & 0xffff) != 0xaa55 || /* signature */
234             (v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0)
235                 return (1);
236         /* EDD supported */
237         bd->bd_flags |= BD_MODEEDD1;
238         if ((v86.eax & 0xff00) >= 0x3000)
239                 bd->bd_flags |= BD_MODEEDD3;
240         /* Get disk params */
241         params.len = sizeof(struct edd_params);
242         v86.ctl = V86_FLAGS;
243         v86.addr = 0x13;
244         v86.eax = 0x4800;
245         v86.edx = bd->bd_unit;
246         v86.ds = VTOPSEG(&params);
247         v86.esi = VTOPOFF(&params);
248         v86int();
249         if (!V86_CY(v86.efl)) {
250                 bd->bd_sectors = params.sectors;
251                 bd->bd_sectorsize = params.sector_size;
252         }
253         DEBUG("unit 0x%x flags %x, sectors %llu, sectorsize %u",
254             bd->bd_unit, bd->bd_flags, bd->bd_sectors, bd->bd_sectorsize);
255         return (1);
256 }
257
258 /*
259  * Print information about disks
260  */
261 static void
262 bd_print(int verbose)
263 {
264         static char line[80];
265         struct disk_devdesc dev;
266         int i;
267
268         for (i = 0; i < nbdinfo; i++) {
269                 sprintf(line, "    disk%d:   BIOS drive %c:\n", i,
270                     (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit):
271                     ('C' + bdinfo[i].bd_unit - 0x80));
272                 pager_output(line);
273                 dev.d_dev = &biosdisk;
274                 dev.d_unit = i;
275                 dev.d_slice = -1;
276                 dev.d_partition = -1;
277                 if (disk_open(&dev,
278                     bdinfo[i].bd_sectorsize * bdinfo[i].bd_sectors,
279                     bdinfo[i].bd_sectorsize,
280                     (bdinfo[i].bd_flags & BD_FLOPPY) ?
281                     DISK_F_NOCACHE: 0) == 0) {
282                         sprintf(line, "    disk%d", i);
283                         disk_print(&dev, line, verbose);
284                         disk_close(&dev);
285                 }
286         }
287 }
288
289 /*
290  * Attempt to open the disk described by (dev) for use by (f).
291  *
292  * Note that the philosophy here is "give them exactly what
293  * they ask for".  This is necessary because being too "smart"
294  * about what the user might want leads to complications.
295  * (eg. given no slice or partition value, with a disk that is
296  *  sliced - are they after the first BSD slice, or the DOS
297  *  slice before it?)
298  */
299 static int
300 bd_open(struct open_file *f, ...)
301 {
302         struct disk_devdesc *dev;
303         va_list ap;
304
305         va_start(ap, f);
306         dev = va_arg(ap, struct disk_devdesc *);
307         va_end(ap);
308
309         if (dev->d_unit < 0 || dev->d_unit >= nbdinfo)
310                 return (EIO);
311
312         return (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
313             BD(dev).bd_sectorsize, (BD(dev).bd_flags & BD_FLOPPY) ?
314             DISK_F_NOCACHE: 0));
315 }
316
317 static int
318 bd_close(struct open_file *f)
319 {
320         struct disk_devdesc *dev;
321
322         dev = (struct disk_devdesc *)f->f_devdata;
323         return (disk_close(dev));
324 }
325
326 static int
327 bd_ioctl(struct open_file *f, u_long cmd, void *data)
328 {
329         struct disk_devdesc *dev;
330
331         dev = (struct disk_devdesc *)f->f_devdata;
332         switch (cmd) {
333         case DIOCGSECTORSIZE:
334                 *(u_int *)data = BD(dev).bd_sectorsize;
335                 break;
336         case DIOCGMEDIASIZE:
337                 *(off_t *)data = BD(dev).bd_sectors * BD(dev).bd_sectorsize;
338                 break;
339         default:
340                 return (ENOTTY);
341         }
342         return (0);
343 }
344
345 static int
346 bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
347     size_t *rsize)
348 {
349         struct bcache_devdata bcd;
350         struct disk_devdesc *dev;
351
352         dev = (struct disk_devdesc *)devdata;
353         bcd.dv_strategy = bd_realstrategy;
354         bcd.dv_devdata = devdata;
355         return (bcache_strategy(&bcd, BD(dev).bd_unit, rw, dblk + dev->d_offset,
356             size, buf, rsize));
357 }
358
359 static int
360 bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
361     size_t *rsize)
362 {
363     struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
364     int                 blks;
365 #ifdef BD_SUPPORT_FRAGS /* XXX: sector size */
366     char                fragbuf[BIOSDISK_SECSIZE];
367     size_t              fragsize;
368
369     fragsize = size % BIOSDISK_SECSIZE;
370 #else
371     if (size % BD(dev).bd_sectorsize)
372         panic("bd_strategy: %d bytes I/O not multiple of block size", size);
373 #endif
374
375     DEBUG("open_disk %p", dev);
376     blks = size / BD(dev).bd_sectorsize;
377     if (rsize)
378         *rsize = 0;
379
380     switch(rw){
381     case F_READ:
382         DEBUG("read %d from %lld to %p", blks, dblk, buf);
383
384         if (blks && bd_read(dev, dblk, blks, buf)) {
385             DEBUG("read error");
386             return (EIO);
387         }
388 #ifdef BD_SUPPORT_FRAGS /* XXX: sector size */
389         DEBUG("bd_strategy: frag read %d from %d+%d to %p",
390             fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE));
391         if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) {
392             DEBUG("frag read error");
393             return(EIO);
394         }
395         bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize);
396 #endif
397         break;
398     case F_WRITE :
399         DEBUG("write %d from %d to %p", blks, dblk, buf);
400
401         if (blks && bd_write(dev, dblk, blks, buf)) {
402             DEBUG("write error");
403             return (EIO);
404         }
405 #ifdef BD_SUPPORT_FRAGS
406         if(fragsize) {
407             DEBUG("Attempted to write a frag");
408             return (EIO);
409         }
410 #endif
411         break;
412     default:
413         /* DO NOTHING */
414         return (EROFS);
415     }
416
417     if (rsize)
418         *rsize = size;
419     return (0);
420 }
421
422 /* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */
423 #define FLOPPY_BOUNCEBUF        18
424
425 static int
426 bd_edd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
427     int write)
428 {
429     static struct edd_packet packet;
430
431     packet.len = sizeof(struct edd_packet);
432     packet.count = blks;
433     packet.off = VTOPOFF(dest);
434     packet.seg = VTOPSEG(dest);
435     packet.lba = dblk;
436     v86.ctl = V86_FLAGS;
437     v86.addr = 0x13;
438     if (write)
439         /* Should we Write with verify ?? 0x4302 ? */
440         v86.eax = 0x4300;
441     else
442         v86.eax = 0x4200;
443     v86.edx = BD(dev).bd_unit;
444     v86.ds = VTOPSEG(&packet);
445     v86.esi = VTOPOFF(&packet);
446     v86int();
447     return (V86_CY(v86.efl));
448 }
449
450 static int
451 bd_chs_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
452     int write)
453 {
454     u_int       x, bpc, cyl, hd, sec;
455
456     bpc = BD(dev).bd_sec * BD(dev).bd_hds;      /* blocks per cylinder */
457     x = dblk;
458     cyl = x / bpc;                      /* block # / blocks per cylinder */
459     x %= bpc;                           /* block offset into cylinder */
460     hd = x / BD(dev).bd_sec;            /* offset / blocks per track */
461     sec = x % BD(dev).bd_sec;           /* offset into track */
462
463     /* correct sector number for 1-based BIOS numbering */
464     sec++;
465
466     if (cyl > 1023)
467         /* CHS doesn't support cylinders > 1023. */
468         return (1);
469
470     v86.ctl = V86_FLAGS;
471     v86.addr = 0x13;
472     if (write)
473         v86.eax = 0x300 | blks;
474     else
475         v86.eax = 0x200 | blks;
476     v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
477     v86.edx = (hd << 8) | BD(dev).bd_unit;
478     v86.es = VTOPSEG(dest);
479     v86.ebx = VTOPOFF(dest);
480     v86int();
481     return (V86_CY(v86.efl));
482 }
483
484 static int
485 bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, int write)
486 {
487     u_int       x, sec, result, resid, retry, maxfer;
488     caddr_t     p, xp, bbuf, breg;
489     
490     /* Just in case some idiot actually tries to read/write -1 blocks... */
491     if (blks < 0)
492         return (-1);
493
494     resid = blks;
495     p = dest;
496
497     /* Decide whether we have to bounce */
498     if (VTOP(dest) >> 20 != 0 || (BD(dev).bd_unit < 0x80 &&
499         (VTOP(dest) >> 16) != (VTOP(dest +
500         blks * BD(dev).bd_sectorsize) >> 16))) {
501
502         /* 
503          * There is a 64k physical boundary somewhere in the
504          * destination buffer, or the destination buffer is above
505          * first 1MB of physical memory so we have to arrange a
506          * suitable bounce buffer.  Allocate a buffer twice as large
507          * as we need to.  Use the bottom half unless there is a break
508          * there, in which case we use the top half.
509          */
510         x = min(FLOPPY_BOUNCEBUF, (unsigned)blks);
511         bbuf = alloca(x * 2 * BD(dev).bd_sectorsize);
512         if (((u_int32_t)VTOP(bbuf) & 0xffff0000) ==
513             ((u_int32_t)VTOP(bbuf + x * BD(dev).bd_sectorsize) & 0xffff0000)) {
514             breg = bbuf;
515         } else {
516             breg = bbuf + x * BD(dev).bd_sectorsize;
517         }
518         maxfer = x;             /* limit transfers to bounce region size */
519     } else {
520         breg = bbuf = NULL;
521         maxfer = 0;
522     }
523     
524     while (resid > 0) {
525         /*
526          * Play it safe and don't cross track boundaries.
527          * (XXX this is probably unnecessary)
528          */
529         sec = dblk % BD(dev).bd_sec;    /* offset into track */
530         x = min(BD(dev).bd_sec - sec, resid);
531         if (maxfer > 0)
532             x = min(x, maxfer);         /* fit bounce buffer */
533
534         /* where do we transfer to? */
535         xp = bbuf == NULL ? p : breg;
536
537         /*
538          * Put your Data In, Put your Data out,
539          * Put your Data In, and shake it all about 
540          */
541         if (write && bbuf != NULL)
542             bcopy(p, breg, x * BD(dev).bd_sectorsize);
543
544         /*
545          * Loop retrying the operation a couple of times.  The BIOS
546          * may also retry.
547          */
548         for (retry = 0; retry < 3; retry++) {
549             /* if retrying, reset the drive */
550             if (retry > 0) {
551                 v86.ctl = V86_FLAGS;
552                 v86.addr = 0x13;
553                 v86.eax = 0;
554                 v86.edx = BD(dev).bd_unit;
555                 v86int();
556             }
557
558             if (BD(dev).bd_flags & BD_MODEEDD1)
559                 result = bd_edd_io(dev, dblk, x, xp, write);
560             else
561                 result = bd_chs_io(dev, dblk, x, xp, write);
562             if (result == 0)
563                 break;
564         }
565
566         if (write)
567             DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x,
568                 p, VTOP(p), dblk, result ? "failed" : "ok");
569         else
570             DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x,
571                 dblk, p, VTOP(p), result ? "failed" : "ok");
572         if (result) {
573             return(-1);
574         }
575         if (!write && bbuf != NULL)
576             bcopy(breg, p, x * BD(dev).bd_sectorsize);
577         p += (x * BD(dev).bd_sectorsize);
578         dblk += x;
579         resid -= x;
580     }
581
582 /*    hexdump(dest, (blks * BD(dev).bd_sectorsize)); */
583     return(0);
584 }
585
586 static int
587 bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest)
588 {
589
590         return (bd_io(dev, dblk, blks, dest, 0));
591 }
592
593 static int
594 bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest)
595 {
596
597         return (bd_io(dev, dblk, blks, dest, 1));
598 }
599
600 /*
601  * Return the BIOS geometry of a given "fixed drive" in a format
602  * suitable for the legacy bootinfo structure.  Since the kernel is
603  * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we
604  * prefer to get the information directly, rather than rely on being
605  * able to put it together from information already maintained for
606  * different purposes and for a probably different number of drives.
607  *
608  * For valid drives, the geometry is expected in the format (31..0)
609  * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are
610  * indicated by returning the geometry of a "1.2M" PC-format floppy
611  * disk.  And, incidentally, what is returned is not the geometry as
612  * such but the highest valid cylinder, head, and sector numbers.
613  */
614 u_int32_t
615 bd_getbigeom(int bunit)
616 {
617
618     v86.ctl = V86_FLAGS;
619     v86.addr = 0x13;
620     v86.eax = 0x800;
621     v86.edx = 0x80 + bunit;
622     v86int();
623     if (V86_CY(v86.efl))
624         return 0x4f010f;
625     return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) |
626            (v86.edx & 0xff00) | (v86.ecx & 0x3f);
627 }
628
629 /*
630  * Return a suitable dev_t value for (dev).
631  *
632  * In the case where it looks like (dev) is a SCSI disk, we allow the number of
633  * IDE disks to be specified in $num_ide_disks.  There should be a Better Way.
634  */
635 int
636 bd_getdev(struct i386_devdesc *d)
637 {
638     struct disk_devdesc         *dev;
639     int                         biosdev;
640     int                         major;
641     int                         rootdev;
642     char                        *nip, *cp;
643     int                         i, unit;
644
645     dev = (struct disk_devdesc *)d;
646     biosdev = bd_unit2bios(dev->d_unit);
647     DEBUG("unit %d BIOS device %d", dev->d_unit, biosdev);
648     if (biosdev == -1)                          /* not a BIOS device */
649         return(-1);
650     if (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
651         BD(dev).bd_sectorsize,(BD(dev).bd_flags & BD_FLOPPY) ?
652         DISK_F_NOCACHE: 0) != 0)                /* oops, not a viable device */
653             return (-1);
654     else
655         disk_close(dev);
656
657     if (biosdev < 0x80) {
658         /* floppy (or emulated floppy) or ATAPI device */
659         if (bdinfo[dev->d_unit].bd_type == DT_ATAPI) {
660             /* is an ATAPI disk */
661             major = WFDMAJOR;
662         } else {
663             /* is a floppy disk */
664             major = FDMAJOR;
665         }
666     } else {
667             /* assume an IDE disk */
668             major = WDMAJOR;
669     }
670     /* default root disk unit number */
671     unit = biosdev & 0x7f;
672
673     /* XXX a better kludge to set the root disk unit number */
674     if ((nip = getenv("root_disk_unit")) != NULL) {
675         i = strtol(nip, &cp, 0);
676         /* check for parse error */
677         if ((cp != nip) && (*cp == 0))
678             unit = i;
679     }
680
681     rootdev = MAKEBOOTDEV(major, dev->d_slice + 1, unit, dev->d_partition);
682     DEBUG("dev is 0x%x\n", rootdev);
683     return(rootdev);
684 }