biosdisk.c revision 344421
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: stable/11/stand/pc98/libpc98/biosdisk.c 344421 2019-02-21 06:02:51Z kevans $");
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 <sys/disk.h>
41#include <sys/limits.h>
42#include <stand.h>
43#include <machine/bootinfo.h>
44#include <stdarg.h>
45
46#include <sys/disklabel.h>
47#include <sys/diskpc98.h>
48
49#include <bootstrap.h>
50#include <btxv86.h>
51#include "disk.h"
52#include "libi386.h"
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#define BD_LABELOK		0x0008
87#define BD_PARTTABOK		0x0010
88#define BD_OPTICAL		0x0020
89	int		bd_type;	/* BIOS 'drive type' (floppy only) */
90	uint16_t	bd_sectorsize;	/* Sector size */
91	uint64_t	bd_sectors;	/* Disk size */
92	int		bd_da_unit;	/* kernel unit number for da */
93	int		bd_open;	/* reference counter */
94	void		*bd_bcache;	/* buffer cache data */
95} bdinfo [MAXBDDEV];
96static int nbdinfo = 0;
97
98#define	BD(dev)	(bdinfo[(dev)->dd.d_unit])
99
100static int bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks,
101    caddr_t dest);
102static int bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks,
103    caddr_t dest);
104static int bd_int13probe(struct bdinfo *bd);
105
106static int bd_init(void);
107static int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
108    char *buf, size_t *rsize);
109static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size,
110    char *buf, size_t *rsize);
111static int bd_open(struct open_file *f, ...);
112static int bd_close(struct open_file *f);
113static int bd_ioctl(struct open_file *f, u_long cmd, void *data);
114static int bd_print(int verbose);
115
116struct devsw biosdisk = {
117	"disk",
118	DEVT_DISK,
119	bd_init,
120	bd_strategy,
121	bd_open,
122	bd_close,
123	bd_ioctl,
124	bd_print,
125	NULL
126};
127
128/*
129 * Translate between BIOS device numbers and our private unit numbers.
130 */
131int
132bd_bios2unit(int biosdev)
133{
134	int i;
135
136	DEBUG("looking for bios device 0x%x", biosdev);
137	for (i = 0; i < nbdinfo; i++) {
138		DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit);
139		if (bdinfo[i].bd_unit == biosdev)
140			return (i);
141	}
142	return (-1);
143}
144
145int
146bd_unit2bios(int unit)
147{
148
149	if ((unit >= 0) && (unit < nbdinfo))
150		return (bdinfo[unit].bd_unit);
151	return (-1);
152}
153
154/*
155 * Quiz the BIOS for disk devices, save a little info about them.
156 */
157static int
158bd_init(void)
159{
160	int base, unit;
161	int da_drive=0, n=-0x10;
162
163	/* sequence 0x90, 0x80, 0xa0 */
164	for (base = 0x90; base <= 0xa0; base += n, n += 0x30) {
165		for (unit = base; (nbdinfo < MAXBDDEV) || ((unit & 0x0f) < 4);
166		     unit++) {
167			bdinfo[nbdinfo].bd_open = 0;
168			bdinfo[nbdinfo].bd_bcache = NULL;
169			bdinfo[nbdinfo].bd_unit = unit;
170			bdinfo[nbdinfo].bd_flags =
171				(unit & 0xf0) == 0x90 ? BD_FLOPPY : 0;
172			if (!bd_int13probe(&bdinfo[nbdinfo])) {
173				if (((unit & 0xf0) == 0x90 &&
174					(unit & 0x0f) < 4) ||
175				    ((unit & 0xf0) == 0xa0 &&
176					(unit & 0x0f) < 6))
177					/* Target IDs are not contiguous. */
178					continue;
179				else
180					break;
181			}
182
183			if (bdinfo[nbdinfo].bd_flags & BD_FLOPPY) {
184				/* available 1.44MB access? */
185				if (*(u_char *)PTOV(0xA15AE) &
186				    (1<<(unit & 0xf))) {
187					/* boot media 1.2MB FD? */
188					if ((*(u_char *)PTOV(0xA1584) &
189						0xf0) != 0x90)
190						bdinfo[nbdinfo].bd_unit =
191							0x30 + (unit & 0xf);
192				}
193			} else {
194				if ((unit & 0xF0) == 0xA0) /* SCSI HD or MO */
195					bdinfo[nbdinfo].bd_da_unit =
196						da_drive++;
197			}
198			/* XXX we need "disk aliases" to make this simpler */
199			printf("BIOS drive %c: is disk%d\n",
200			    'A' + nbdinfo, nbdinfo);
201			nbdinfo++;
202		}
203	}
204	bcache_add_dev(nbdinfo);
205	return(0);
206}
207
208/*
209 * Try to detect a device supported by the legacy int13 BIOS
210 */
211static int
212bd_int13probe(struct bdinfo *bd)
213{
214	int addr;
215
216	if (bd->bd_flags & BD_FLOPPY) {
217		addr = 0xa155c;
218	} else {
219		if ((bd->bd_unit & 0xf0) == 0x80)
220			addr = 0xa155d;
221		else
222			addr = 0xa1482;
223	}
224	if ( *(u_char *)PTOV(addr) & (1<<(bd->bd_unit & 0x0f))) {
225		bd->bd_flags |= BD_MODEINT13;
226		return (1);
227	}
228	if ((bd->bd_unit & 0xF0) == 0xA0) {
229		int media =
230			((unsigned *)PTOV(0xA1460))[bd->bd_unit & 0x0F] & 0x1F;
231
232		if (media == 7) { /* MO */
233			bd->bd_flags |= BD_MODEINT13 | BD_OPTICAL;
234			return(1);
235		}
236	}
237	return (0);
238}
239
240/*
241 * Print information about disks
242 */
243static int
244bd_print(int verbose)
245{
246	char line[80];
247	struct disk_devdesc dev;
248	int i, ret = 0;
249	struct pc98_partition *dptr;
250
251	if (nbdinfo == 0)
252		return (0);
253
254	printf("%s devices:", biosdisk.dv_name);
255	if ((ret = pager_output("\n")) != 0)
256		return (ret);
257
258	for (i = 0; i < nbdinfo; i++) {
259		snprintf(line, sizeof(line),
260		    "    disk%d:   BIOS drive %c (%ju X %u):\n", i,
261		    (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit):
262		    ('C' + bdinfo[i].bd_unit - 0x80),
263		    (uintmax_t)bdinfo[i].bd_sectors,
264		    bdinfo[i].bd_sectorsize);
265		if ((ret = pager_output(line)) != 0)
266			break;
267
268		/* try to open the whole disk */
269		dev.dd.d_dev = &biosdisk;
270		dev.dd.d_unit = i;
271		dev.d_slice = -1;
272		dev.d_partition = -1;
273		if (disk_open(&dev,
274		    bdinfo[i].bd_sectorsize * bdinfo[i].bd_sectors,
275		    bdinfo[i].bd_sectorsize) == 0) {
276			snprintf(line, sizeof(line), "    disk%d", i);
277			ret = disk_print(&dev, line, verbose);
278			disk_close(&dev);
279			if (ret != 0)
280			    return (ret);
281		}
282	}
283	return (ret);
284}
285
286/* Given a size in 512 byte sectors, convert it to a human-readable number. */
287static char *
288display_size(uint64_t size)
289{
290	static char buf[80];
291	char unit;
292
293	size /= 2;
294	unit = 'K';
295	if (size >= 10485760000LL) {
296		size /= 1073741824;
297		unit = 'T';
298	} else if (size >= 10240000) {
299		size /= 1048576;
300		unit = 'G';
301	} else if (size >= 10000) {
302		size /= 1024;
303		unit = 'M';
304	}
305	sprintf(buf, "%6ld%cB", (long)size, unit);
306	return (buf);
307}
308
309/*
310 * Attempt to open the disk described by (dev) for use by (f).
311 *
312 * Note that the philosophy here is "give them exactly what
313 * they ask for".  This is necessary because being too "smart"
314 * about what the user might want leads to complications.
315 * (eg. given no slice or partition value, with a disk that is
316 *  sliced - are they after the first BSD slice, or the DOS
317 *  slice before it?)
318 */
319static int
320bd_open(struct open_file *f, ...)
321{
322	va_list				ap;
323	struct disk_devdesc		*dev;
324	struct disk_devdesc		disk;
325	int				err;
326	uint64_t			size;
327
328	va_start(ap, f);
329	dev = va_arg(ap, struct disk_devdesc *);
330	va_end(ap);
331
332	if (dev->dd.d_unit < 0 || dev->dd.d_unit >= nbdinfo)
333		return (EIO);
334	BD(dev).bd_open++;
335	if (BD(dev).bd_bcache == NULL)
336		BD(dev).bd_bcache = bcache_allocate();
337
338	/*
339	 * Read disk size from partition.
340	 * This is needed to work around buggy BIOS systems returning
341	 * wrong (truncated) disk media size.
342	 * During bd_probe() we tested if the mulitplication of bd_sectors
343	 * would overflow so it should be safe to perform here.
344	 */
345	disk.dd.d_dev = dev->dd.d_dev;
346	disk.dd.d_unit = dev->dd.d_unit;
347	disk.d_slice = -1;
348	disk.d_partition = -1;
349	disk.d_offset = 0;
350	if (disk_open(&disk, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
351	    BD(dev).bd_sectorsize) == 0) {
352
353		if (disk_ioctl(&disk, DIOCGMEDIASIZE, &size) == 0) {
354			size /= BD(dev).bd_sectorsize;
355			if (size > BD(dev).bd_sectors)
356				BD(dev).bd_sectors = size;
357		}
358		disk_close(&disk);
359	}
360
361	err = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
362	    BD(dev).bd_sectorsize);
363	return(err);
364}
365
366static int
367bd_close(struct open_file *f)
368{
369	struct disk_devdesc *dev;
370
371	dev = (struct disk_devdesc *)f->f_devdata;
372	BD(dev).bd_open--;
373	if (BD(dev).bd_open == 0) {
374	    bcache_free(BD(dev).bd_bcache);
375	    BD(dev).bd_bcache = NULL;
376	}
377	return (disk_close(dev));
378}
379
380static int
381bd_ioctl(struct open_file *f, u_long cmd, void *data)
382{
383	struct disk_devdesc *dev;
384	int rc;
385
386	dev = (struct disk_devdesc *)f->f_devdata;
387
388	rc = disk_ioctl(dev, cmd, data);
389	if (rc != ENOTTY)
390		return (rc);
391
392	switch (cmd) {
393	case DIOCGSECTORSIZE:
394		*(u_int *)data = BD(dev).bd_sectorsize;
395		break;
396	case DIOCGMEDIASIZE:
397		*(uint64_t *)data = BD(dev).bd_sectors * BD(dev).bd_sectorsize;
398		break;
399	default:
400		return (ENOTTY);
401	}
402	return (0);
403}
404
405static int
406bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
407    char *buf, size_t *rsize)
408{
409	struct bcache_devdata bcd;
410	struct disk_devdesc *dev;
411
412	dev = (struct disk_devdesc *)devdata;
413	bcd.dv_strategy = bd_realstrategy;
414	bcd.dv_devdata = devdata;
415	bcd.dv_cache = BD(dev).bd_bcache;
416	return (bcache_strategy(&bcd, rw, dblk + dev->d_offset,
417	    size, buf, rsize));
418}
419
420static int
421bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
422    char *buf, size_t *rsize)
423{
424    struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
425    uint64_t		disk_blocks;
426    int			blks, rc;
427#ifdef BD_SUPPORT_FRAGS /* XXX: sector size */
428    char		fragbuf[BIOSDISK_SECSIZE];
429    size_t		fragsize;
430
431    fragsize = size % BIOSDISK_SECSIZE;
432#else
433    if (size % BD(dev).bd_sectorsize)
434	panic("bd_strategy: %d bytes I/O not multiple of block size", size);
435#endif
436
437    DEBUG("open_disk %p", dev);
438
439    /*
440     * Check the value of the size argument. We do have quite small
441     * heap (64MB), but we do not know good upper limit, so we check against
442     * INT_MAX here. This will also protect us against possible overflows
443     * while translating block count to bytes.
444     */
445    if (size > INT_MAX) {
446	DEBUG("too large read: %zu bytes", size);
447	return (EIO);
448    }
449
450    blks = size / BD(dev).bd_sectorsize;
451    if (dblk > dblk + blks)
452	return (EIO);
453
454    if (rsize)
455	*rsize = 0;
456
457    /* Get disk blocks, this value is either for whole disk or for partition */
458    if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks)) {
459	/* DIOCGMEDIASIZE does return bytes. */
460        disk_blocks /= BD(dev).bd_sectorsize;
461    } else {
462	/* We should not get here. Just try to survive. */
463	disk_blocks = BD(dev).bd_sectors - dev->d_offset;
464    }
465
466    /* Validate source block address. */
467    if (dblk < dev->d_offset || dblk >= dev->d_offset + disk_blocks)
468	return (EIO);
469
470    /*
471     * Truncate if we are crossing disk or partition end.
472     */
473    if (dblk + blks >= dev->d_offset + disk_blocks) {
474	blks = dev->d_offset + disk_blocks - dblk;
475	size = blks * BD(dev).bd_sectorsize;
476	DEBUG("short read %d", blks);
477    }
478
479    switch (rw & F_MASK) {
480    case F_READ:
481	DEBUG("read %d from %lld to %p", blks, dblk, buf);
482
483	if (blks && (rc = bd_read(dev, dblk, blks, buf))) {
484	    /* Filter out floppy controller errors */
485	    if (BD(dev).bd_flags != BD_FLOPPY || rc != 0x20) {
486		printf("read %d from %lld to %p, error: 0x%x", blks, dblk,
487		    buf, rc);
488	    }
489	    return (EIO);
490	}
491#ifdef BD_SUPPORT_FRAGS /* XXX: sector size */
492	DEBUG("bd_strategy: frag read %d from %d+%d to %p",
493	    fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE));
494	if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) {
495	    DEBUG("frag read error");
496	    return(EIO);
497	}
498	bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize);
499#endif
500	break;
501    case F_WRITE :
502	DEBUG("write %d from %d to %p", blks, dblk, buf);
503
504	if (blks && bd_write(dev, dblk, blks, buf)) {
505	    DEBUG("write error");
506	    return (EIO);
507	}
508#ifdef BD_SUPPORT_FRAGS
509	if(fragsize) {
510	    DEBUG("Attempted to write a frag");
511	    return (EIO);
512	}
513#endif
514	break;
515    default:
516	/* DO NOTHING */
517	return (EROFS);
518    }
519
520    if (rsize)
521	*rsize = size;
522    return (0);
523}
524
525/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */
526#define FLOPPY_BOUNCEBUF	18
527
528static int
529bd_chs_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
530    int write)
531{
532    u_int	x, bpc, cyl, hd, sec;
533
534    bpc = BD(dev).bd_sec * BD(dev).bd_hds;	/* blocks per cylinder */
535    x = dblk;
536    cyl = x / bpc;			/* block # / blocks per cylinder */
537    x %= bpc;				/* block offset into cylinder */
538    hd = x / BD(dev).bd_sec;		/* offset / blocks per track */
539    sec = x % BD(dev).bd_sec;		/* offset into track */
540
541    v86.ctl = V86_FLAGS;
542    v86.addr = 0x1b;
543    if (write)
544        v86.eax = 0x0500 | BD(dev).bd_unit;
545    else
546	v86.eax = 0x0600 | BD(dev).bd_unit;
547    if (BD(dev).bd_flags & BD_FLOPPY) {
548	v86.eax |= 0xd000;
549	v86.ecx = 0x0200 | (cyl & 0xff);
550	v86.edx = (hd << 8) | (sec + 1);
551    } else if (BD(dev).bd_flags & BD_OPTICAL) {
552	v86.eax &= 0xFF7F;
553	v86.ecx = dblk & 0xFFFF;
554	v86.edx = dblk >> 16;
555    } else {
556	v86.ecx = cyl;
557	v86.edx = (hd << 8) | sec;
558    }
559    v86.ebx = blks * BIOSDISK_SECSIZE;
560    v86.es = VTOPSEG(dest);
561    v86.ebp = VTOPOFF(dest);
562    v86int();
563    return (V86_CY(v86.efl));
564}
565
566static int
567bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, int write)
568{
569    u_int	x, sec, result, resid, retry, maxfer;
570    caddr_t	p, xp, bbuf;
571
572    /* Just in case some idiot actually tries to read/write -1 blocks... */
573    if (blks < 0)
574	return (-1);
575
576    resid = blks;
577    p = dest;
578
579    /* Decide whether we have to bounce */
580    if (VTOP(dest) >> 20 != 0 || (BD(dev).bd_unit < 0x80 &&
581	(VTOP(dest) >> 16) != (VTOP(dest +
582	blks * BD(dev).bd_sectorsize) >> 16))) {
583
584	/*
585	 * There is a 64k physical boundary somewhere in the
586	 * destination buffer, or the destination buffer is above
587	 * first 1MB of physical memory so we have to arrange a
588	 * suitable bounce buffer.  Allocate a buffer twice as large
589	 * as we need to.  Use the bottom half unless there is a break
590	 * there, in which case we use the top half.
591	 */
592	x = V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize;
593	x = min(x, (unsigned)blks);
594	bbuf = PTOV(V86_IO_BUFFER);
595	maxfer = x;		/* limit transfers to bounce region size */
596    } else {
597	bbuf = NULL;
598	maxfer = 0;
599    }
600
601    while (resid > 0) {
602	/*
603	 * Play it safe and don't cross track boundaries.
604	 * (XXX this is probably unnecessary)
605	 */
606	sec = dblk % BD(dev).bd_sec;	/* offset into track */
607	x = min(BD(dev).bd_sec - sec, resid);
608	if (maxfer > 0)
609	    x = min(x, maxfer);		/* fit bounce buffer */
610
611	/* where do we transfer to? */
612	xp = bbuf == NULL ? p : bbuf;
613
614	/*
615	 * Put your Data In, Put your Data out,
616	 * Put your Data In, and shake it all about
617	 */
618	if (write && bbuf != NULL)
619	    bcopy(p, bbuf, x * BD(dev).bd_sectorsize);
620
621	/*
622	 * Loop retrying the operation a couple of times.  The BIOS
623	 * may also retry.
624	 */
625	for (retry = 0; retry < 3; retry++) {
626	    /* if retrying, reset the drive */
627	    if (retry > 0) {
628		v86.ctl = V86_FLAGS;
629		v86.addr = 0x1b;
630		v86.eax = 0x0300 | BD(dev).bd_unit;
631		v86int();
632	    }
633
634	    result = bd_chs_io(dev, dblk, x, xp, write);
635	    if (result == 0)
636		break;
637	}
638
639	if (write)
640	    DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x,
641		p, VTOP(p), dblk, result ? "failed" : "ok");
642	else
643	    DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x,
644		dblk, p, VTOP(p), result ? "failed" : "ok");
645	if (result) {
646	    return (result);
647	}
648	if (!write && bbuf != NULL)
649	    bcopy(bbuf, p, x * BD(dev).bd_sectorsize);
650	p += (x * BD(dev).bd_sectorsize);
651	dblk += x;
652	resid -= x;
653    }
654
655/*    hexdump(dest, (blks * BD(dev).bd_sectorsize)); */
656    return(0);
657}
658
659static int
660bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks,
661    caddr_t dest)
662{
663
664	return (bd_io(dev, dblk, blks, dest, 0));
665}
666
667static int
668bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks,
669    caddr_t dest)
670{
671
672	return (bd_io(dev, dblk, blks, dest, 1));
673}
674
675#if 0
676static int
677bd_getgeom(struct open_disk *od)
678{
679
680    if (od->od_flags & BD_FLOPPY) {
681	od->od_cyl = 79;
682	od->od_hds = 2;
683	od->od_sec = (od->od_unit & 0xf0) == 0x30 ? 18 : 15;
684    } else if (od->od_flags & BD_OPTICAL) {
685	od->od_cyl = 0xFFFE;
686	od->od_hds = 8;
687	od->od_sec = 32;
688    } else {
689	v86.ctl = V86_FLAGS;
690	v86.addr = 0x1b;
691	v86.eax = 0x8400 | od->od_unit;
692	v86int();
693
694	od->od_cyl = v86.ecx;
695	od->od_hds = (v86.edx >> 8) & 0xff;
696	od->od_sec = v86.edx & 0xff;
697	if (V86_CY(v86.efl))
698	    return(1);
699    }
700
701    DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec);
702    return(0);
703}
704#endif
705
706/*
707 * Return the BIOS geometry of a given "fixed drive" in a format
708 * suitable for the legacy bootinfo structure.  Since the kernel is
709 * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we
710 * prefer to get the information directly, rather than rely on being
711 * able to put it together from information already maintained for
712 * different purposes and for a probably different number of drives.
713 *
714 * For valid drives, the geometry is expected in the format (31..0)
715 * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are
716 * indicated by returning the geometry of a "1.2M" PC-format floppy
717 * disk.  And, incidentally, what is returned is not the geometry as
718 * such but the highest valid cylinder, head, and sector numbers.
719 */
720u_int32_t
721bd_getbigeom(int bunit)
722{
723    int hds = 0;
724    int unit = 0x80;		/* IDE HDD */
725    u_int addr = 0xA155d;
726
727    while (unit < 0xa7) {
728	if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f)))
729	    if (hds++ == bunit)
730		break;
731
732	if (unit >= 0xA0) {
733	    int  media = ((unsigned *)PTOV(0xA1460))[unit & 0x0F] & 0x1F;
734
735	    if (media == 7 && hds++ == bunit)	/* SCSI MO */
736		return(0xFFFE0820); /* C:65535 H:8 S:32 */
737	}
738	if (++unit == 0x84) {
739	    unit = 0xA0;	/* SCSI HDD */
740	    addr = 0xA1482;
741	}
742    }
743    if (unit == 0xa7)
744	return 0x4F020F;	/* 1200KB FD C:80 H:2 S:15 */
745    v86.ctl = V86_FLAGS;
746    v86.addr = 0x1b;
747    v86.eax = 0x8400 | unit;
748    v86int();
749    if (V86_CY(v86.efl))
750	return 0x4F020F;	/* 1200KB FD C:80 H:2 S:15 */
751    return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff);
752}
753
754/*
755 * Return a suitable dev_t value for (dev).
756 *
757 * In the case where it looks like (dev) is a SCSI disk, we allow the number of
758 * IDE disks to be specified in $num_ide_disks.  There should be a Better Way.
759 */
760int
761bd_getdev(struct i386_devdesc *d)
762{
763    struct disk_devdesc		*dev;
764    int				biosdev;
765    int 			major;
766    int				rootdev;
767    char			*nip, *cp;
768    int				unitofs = 0, i, unit;
769
770    dev = (struct disk_devdesc *)d;
771    biosdev = bd_unit2bios(dev->dd.d_unit);
772    DEBUG("unit %d BIOS device %d", dev->dd.d_unit, biosdev);
773    if (biosdev == -1)				/* not a BIOS device */
774	return(-1);
775    if (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
776	BD(dev).bd_sectorsize) != 0)		/* oops, not a viable device */
777	    return (-1);
778    else
779	disk_close(dev);
780
781    if ((biosdev & 0xf0) == 0x90 || (biosdev & 0xf0) == 0x30) {
782	/* floppy (or emulated floppy) or ATAPI device */
783	if (BD(dev).bd_type == DT_ATAPI) {
784	    /* is an ATAPI disk */
785	    major = WFDMAJOR;
786	} else {
787	    /* is a floppy disk */
788	    major = FDMAJOR;
789	}
790    } else {
791	/* harddisk */
792	if ((BD(dev).bd_flags & BD_LABELOK) && 0) {
793//	    (BD(dev).bd_disklabel.d_type == DTYPE_SCSI)) {
794	    /* label OK, disk labelled as SCSI */
795	    major = DAMAJOR;
796	    /* check for unit number correction hint, now deprecated */
797	    if ((nip = getenv("num_ide_disks")) != NULL) {
798		i = strtol(nip, &cp, 0);
799		/* check for parse error */
800		if ((cp != nip) && (*cp == 0))
801		    unitofs = i;
802	    }
803	} else {
804	    /* assume an IDE disk */
805	    major = WDMAJOR;
806	}
807    }
808    /* default root disk unit number */
809    if ((biosdev & 0xf0) == 0xa0)
810	unit = BD(dev).bd_da_unit;
811    else
812	unit = biosdev & 0xf;
813
814    /* XXX a better kludge to set the root disk unit number */
815    if ((nip = getenv("root_disk_unit")) != NULL) {
816	i = strtol(nip, &cp, 0);
817	/* check for parse error */
818	if ((cp != nip) && (*cp == 0))
819	    unit = i;
820    }
821
822    rootdev = MAKEBOOTDEV(major, dev->d_slice + 1, unit, dev->d_partition);
823    DEBUG("dev is 0x%x\n", rootdev);
824    return(rootdev);
825}
826