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: stable/10/sys/boot/i386/libi386/biosdisk.c 305458 2016-09-06 06:13:47Z avg $");
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
bd_bios2unit(int biosdev)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
bd_unit2bios(int unit)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
bd_init(void)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
bd_cleanup(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
bd_int13probe(struct bdinfo * bd)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(¶ms);
247 v86.esi = VTOPOFF(¶ms);
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
bd_print(int verbose)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 (%ju X %u):\n", i,
270 (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit):
271 ('C' + bdinfo[i].bd_unit - 0x80),
272 (uintmax_t)bdinfo[i].bd_sectors,
273 bdinfo[i].bd_sectorsize);
274 pager_output(line);
275 dev.d_dev = &biosdisk;
276 dev.d_unit = i;
277 dev.d_slice = -1;
278 dev.d_partition = -1;
279 if (disk_open(&dev,
280 bdinfo[i].bd_sectorsize * bdinfo[i].bd_sectors,
281 bdinfo[i].bd_sectorsize,
282 (bdinfo[i].bd_flags & BD_FLOPPY) ?
283 DISK_F_NOCACHE: 0) == 0) {
284 sprintf(line, " disk%d", i);
285 disk_print(&dev, line, verbose);
286 disk_close(&dev);
287 }
288 }
289 }
290
291 /*
292 * Attempt to open the disk described by (dev) for use by (f).
293 *
294 * Note that the philosophy here is "give them exactly what
295 * they ask for". This is necessary because being too "smart"
296 * about what the user might want leads to complications.
297 * (eg. given no slice or partition value, with a disk that is
298 * sliced - are they after the first BSD slice, or the DOS
299 * slice before it?)
300 */
301 static int
bd_open(struct open_file * f,...)302 bd_open(struct open_file *f, ...)
303 {
304 struct disk_devdesc *dev;
305 va_list ap;
306
307 va_start(ap, f);
308 dev = va_arg(ap, struct disk_devdesc *);
309 va_end(ap);
310
311 if (dev->d_unit < 0 || dev->d_unit >= nbdinfo)
312 return (EIO);
313
314 return (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
315 BD(dev).bd_sectorsize, (BD(dev).bd_flags & BD_FLOPPY) ?
316 DISK_F_NOCACHE: 0));
317 }
318
319 static int
bd_close(struct open_file * f)320 bd_close(struct open_file *f)
321 {
322 struct disk_devdesc *dev;
323
324 dev = (struct disk_devdesc *)f->f_devdata;
325 return (disk_close(dev));
326 }
327
328 static int
bd_ioctl(struct open_file * f,u_long cmd,void * data)329 bd_ioctl(struct open_file *f, u_long cmd, void *data)
330 {
331 struct disk_devdesc *dev;
332
333 dev = (struct disk_devdesc *)f->f_devdata;
334 switch (cmd) {
335 case DIOCGSECTORSIZE:
336 *(u_int *)data = BD(dev).bd_sectorsize;
337 break;
338 case DIOCGMEDIASIZE:
339 *(off_t *)data = BD(dev).bd_sectors * BD(dev).bd_sectorsize;
340 break;
341 default:
342 return (ENOTTY);
343 }
344 return (0);
345 }
346
347 static int
bd_strategy(void * devdata,int rw,daddr_t dblk,size_t size,char * buf,size_t * rsize)348 bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
349 size_t *rsize)
350 {
351 struct bcache_devdata bcd;
352 struct disk_devdesc *dev;
353
354 dev = (struct disk_devdesc *)devdata;
355 bcd.dv_strategy = bd_realstrategy;
356 bcd.dv_devdata = devdata;
357 return (bcache_strategy(&bcd, BD(dev).bd_unit, rw, dblk + dev->d_offset,
358 size, buf, rsize));
359 }
360
361 static int
bd_realstrategy(void * devdata,int rw,daddr_t dblk,size_t size,char * buf,size_t * rsize)362 bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
363 size_t *rsize)
364 {
365 struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
366 int blks;
367 #ifdef BD_SUPPORT_FRAGS /* XXX: sector size */
368 char fragbuf[BIOSDISK_SECSIZE];
369 size_t fragsize;
370
371 fragsize = size % BIOSDISK_SECSIZE;
372 #else
373 if (size % BD(dev).bd_sectorsize)
374 panic("bd_strategy: %d bytes I/O not multiple of block size", size);
375 #endif
376
377 DEBUG("open_disk %p", dev);
378 blks = size / BD(dev).bd_sectorsize;
379 if (rsize)
380 *rsize = 0;
381
382 switch(rw){
383 case F_READ:
384 DEBUG("read %d from %lld to %p", blks, dblk, buf);
385
386 if (blks && bd_read(dev, dblk, blks, buf)) {
387 DEBUG("read error");
388 return (EIO);
389 }
390 #ifdef BD_SUPPORT_FRAGS /* XXX: sector size */
391 DEBUG("bd_strategy: frag read %d from %d+%d to %p",
392 fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE));
393 if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) {
394 DEBUG("frag read error");
395 return(EIO);
396 }
397 bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize);
398 #endif
399 break;
400 case F_WRITE :
401 DEBUG("write %d from %d to %p", blks, dblk, buf);
402
403 if (blks && bd_write(dev, dblk, blks, buf)) {
404 DEBUG("write error");
405 return (EIO);
406 }
407 #ifdef BD_SUPPORT_FRAGS
408 if(fragsize) {
409 DEBUG("Attempted to write a frag");
410 return (EIO);
411 }
412 #endif
413 break;
414 default:
415 /* DO NOTHING */
416 return (EROFS);
417 }
418
419 if (rsize)
420 *rsize = size;
421 return (0);
422 }
423
424 /* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */
425 #define FLOPPY_BOUNCEBUF 18
426
427 static int
bd_edd_io(struct disk_devdesc * dev,daddr_t dblk,int blks,caddr_t dest,int write)428 bd_edd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
429 int write)
430 {
431 static struct edd_packet packet;
432
433 packet.len = sizeof(struct edd_packet);
434 packet.count = blks;
435 packet.off = VTOPOFF(dest);
436 packet.seg = VTOPSEG(dest);
437 packet.lba = dblk;
438 v86.ctl = V86_FLAGS;
439 v86.addr = 0x13;
440 if (write)
441 /* Should we Write with verify ?? 0x4302 ? */
442 v86.eax = 0x4300;
443 else
444 v86.eax = 0x4200;
445 v86.edx = BD(dev).bd_unit;
446 v86.ds = VTOPSEG(&packet);
447 v86.esi = VTOPOFF(&packet);
448 v86int();
449 return (V86_CY(v86.efl));
450 }
451
452 static int
bd_chs_io(struct disk_devdesc * dev,daddr_t dblk,int blks,caddr_t dest,int write)453 bd_chs_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
454 int write)
455 {
456 u_int x, bpc, cyl, hd, sec;
457
458 bpc = BD(dev).bd_sec * BD(dev).bd_hds; /* blocks per cylinder */
459 x = dblk;
460 cyl = x / bpc; /* block # / blocks per cylinder */
461 x %= bpc; /* block offset into cylinder */
462 hd = x / BD(dev).bd_sec; /* offset / blocks per track */
463 sec = x % BD(dev).bd_sec; /* offset into track */
464
465 /* correct sector number for 1-based BIOS numbering */
466 sec++;
467
468 if (cyl > 1023)
469 /* CHS doesn't support cylinders > 1023. */
470 return (1);
471
472 v86.ctl = V86_FLAGS;
473 v86.addr = 0x13;
474 if (write)
475 v86.eax = 0x300 | blks;
476 else
477 v86.eax = 0x200 | blks;
478 v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
479 v86.edx = (hd << 8) | BD(dev).bd_unit;
480 v86.es = VTOPSEG(dest);
481 v86.ebx = VTOPOFF(dest);
482 v86int();
483 return (V86_CY(v86.efl));
484 }
485
486 static int
bd_io(struct disk_devdesc * dev,daddr_t dblk,int blks,caddr_t dest,int write)487 bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, int write)
488 {
489 u_int x, sec, result, resid, retry, maxfer;
490 caddr_t p, xp, bbuf, breg;
491
492 /* Just in case some idiot actually tries to read/write -1 blocks... */
493 if (blks < 0)
494 return (-1);
495
496 resid = blks;
497 p = dest;
498
499 /* Decide whether we have to bounce */
500 if (VTOP(dest) >> 20 != 0 || (BD(dev).bd_unit < 0x80 &&
501 (VTOP(dest) >> 16) != (VTOP(dest +
502 blks * BD(dev).bd_sectorsize) >> 16))) {
503
504 /*
505 * There is a 64k physical boundary somewhere in the
506 * destination buffer, or the destination buffer is above
507 * first 1MB of physical memory so we have to arrange a
508 * suitable bounce buffer. Allocate a buffer twice as large
509 * as we need to. Use the bottom half unless there is a break
510 * there, in which case we use the top half.
511 */
512 x = min(FLOPPY_BOUNCEBUF, (unsigned)blks);
513 bbuf = alloca(x * 2 * BD(dev).bd_sectorsize);
514 if (((u_int32_t)VTOP(bbuf) & 0xffff0000) ==
515 ((u_int32_t)VTOP(bbuf + x * BD(dev).bd_sectorsize) & 0xffff0000)) {
516 breg = bbuf;
517 } else {
518 breg = bbuf + x * BD(dev).bd_sectorsize;
519 }
520 maxfer = x; /* limit transfers to bounce region size */
521 } else {
522 breg = bbuf = NULL;
523 maxfer = 0;
524 }
525
526 while (resid > 0) {
527 /*
528 * Play it safe and don't cross track boundaries.
529 * (XXX this is probably unnecessary)
530 */
531 sec = dblk % BD(dev).bd_sec; /* offset into track */
532 x = min(BD(dev).bd_sec - sec, resid);
533 if (maxfer > 0)
534 x = min(x, maxfer); /* fit bounce buffer */
535
536 /* where do we transfer to? */
537 xp = bbuf == NULL ? p : breg;
538
539 /*
540 * Put your Data In, Put your Data out,
541 * Put your Data In, and shake it all about
542 */
543 if (write && bbuf != NULL)
544 bcopy(p, breg, x * BD(dev).bd_sectorsize);
545
546 /*
547 * Loop retrying the operation a couple of times. The BIOS
548 * may also retry.
549 */
550 for (retry = 0; retry < 3; retry++) {
551 /* if retrying, reset the drive */
552 if (retry > 0) {
553 v86.ctl = V86_FLAGS;
554 v86.addr = 0x13;
555 v86.eax = 0;
556 v86.edx = BD(dev).bd_unit;
557 v86int();
558 }
559
560 if (BD(dev).bd_flags & BD_MODEEDD1)
561 result = bd_edd_io(dev, dblk, x, xp, write);
562 else
563 result = bd_chs_io(dev, dblk, x, xp, write);
564 if (result == 0)
565 break;
566 }
567
568 if (write)
569 DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x,
570 p, VTOP(p), dblk, result ? "failed" : "ok");
571 else
572 DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x,
573 dblk, p, VTOP(p), result ? "failed" : "ok");
574 if (result) {
575 return(-1);
576 }
577 if (!write && bbuf != NULL)
578 bcopy(breg, p, x * BD(dev).bd_sectorsize);
579 p += (x * BD(dev).bd_sectorsize);
580 dblk += x;
581 resid -= x;
582 }
583
584 /* hexdump(dest, (blks * BD(dev).bd_sectorsize)); */
585 return(0);
586 }
587
588 static int
bd_read(struct disk_devdesc * dev,daddr_t dblk,int blks,caddr_t dest)589 bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest)
590 {
591
592 return (bd_io(dev, dblk, blks, dest, 0));
593 }
594
595 static int
bd_write(struct disk_devdesc * dev,daddr_t dblk,int blks,caddr_t dest)596 bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest)
597 {
598
599 return (bd_io(dev, dblk, blks, dest, 1));
600 }
601
602 /*
603 * Return the BIOS geometry of a given "fixed drive" in a format
604 * suitable for the legacy bootinfo structure. Since the kernel is
605 * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we
606 * prefer to get the information directly, rather than rely on being
607 * able to put it together from information already maintained for
608 * different purposes and for a probably different number of drives.
609 *
610 * For valid drives, the geometry is expected in the format (31..0)
611 * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are
612 * indicated by returning the geometry of a "1.2M" PC-format floppy
613 * disk. And, incidentally, what is returned is not the geometry as
614 * such but the highest valid cylinder, head, and sector numbers.
615 */
616 u_int32_t
bd_getbigeom(int bunit)617 bd_getbigeom(int bunit)
618 {
619
620 v86.ctl = V86_FLAGS;
621 v86.addr = 0x13;
622 v86.eax = 0x800;
623 v86.edx = 0x80 + bunit;
624 v86int();
625 if (V86_CY(v86.efl))
626 return 0x4f010f;
627 return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) |
628 (v86.edx & 0xff00) | (v86.ecx & 0x3f);
629 }
630
631 /*
632 * Return a suitable dev_t value for (dev).
633 *
634 * In the case where it looks like (dev) is a SCSI disk, we allow the number of
635 * IDE disks to be specified in $num_ide_disks. There should be a Better Way.
636 */
637 int
bd_getdev(struct i386_devdesc * d)638 bd_getdev(struct i386_devdesc *d)
639 {
640 struct disk_devdesc *dev;
641 int biosdev;
642 int major;
643 int rootdev;
644 char *nip, *cp;
645 int i, unit;
646
647 dev = (struct disk_devdesc *)d;
648 biosdev = bd_unit2bios(dev->d_unit);
649 DEBUG("unit %d BIOS device %d", dev->d_unit, biosdev);
650 if (biosdev == -1) /* not a BIOS device */
651 return(-1);
652 if (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
653 BD(dev).bd_sectorsize,(BD(dev).bd_flags & BD_FLOPPY) ?
654 DISK_F_NOCACHE: 0) != 0) /* oops, not a viable device */
655 return (-1);
656 else
657 disk_close(dev);
658
659 if (biosdev < 0x80) {
660 /* floppy (or emulated floppy) or ATAPI device */
661 if (bdinfo[dev->d_unit].bd_type == DT_ATAPI) {
662 /* is an ATAPI disk */
663 major = WFDMAJOR;
664 } else {
665 /* is a floppy disk */
666 major = FDMAJOR;
667 }
668 } else {
669 /* assume an IDE disk */
670 major = WDMAJOR;
671 }
672 /* default root disk unit number */
673 unit = biosdev & 0x7f;
674
675 /* XXX a better kludge to set the root disk unit number */
676 if ((nip = getenv("root_disk_unit")) != NULL) {
677 i = strtol(nip, &cp, 0);
678 /* check for parse error */
679 if ((cp != nip) && (*cp == 0))
680 unit = i;
681 }
682
683 rootdev = MAKEBOOTDEV(major, dev->d_slice + 1, unit, dev->d_partition);
684 DEBUG("dev is 0x%x\n", rootdev);
685 return(rootdev);
686 }
687