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(&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
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