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