143561Skato/*-
243561Skato * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
343561Skato * All rights reserved.
443561Skato *
543561Skato * Redistribution and use in source and binary forms, with or without
643561Skato * modification, are permitted provided that the following conditions
743561Skato * are met:
843561Skato * 1. Redistributions of source code must retain the above copyright
943561Skato *    notice, this list of conditions and the following disclaimer.
1043561Skato * 2. Redistributions in binary form must reproduce the above copyright
1143561Skato *    notice, this list of conditions and the following disclaimer in the
1243561Skato *    documentation and/or other materials provided with the distribution.
1343561Skato *
1443561Skato * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1543561Skato * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1643561Skato * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1743561Skato * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1843561Skato * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1943561Skato * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2043561Skato * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2143561Skato * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2243561Skato * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2343561Skato * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2443561Skato * SUCH DAMAGE.
2543561Skato */
2643561Skato
27119880Sobrien#include <sys/cdefs.h>
28119880Sobrien__FBSDID("$FreeBSD: stable/11/stand/pc98/libpc98/biosdisk.c 344421 2019-02-21 06:02:51Z kevans $");
29119880Sobrien
3043561Skato/*
3143561Skato * BIOS disk device handling.
3243561Skato *
3343561Skato * Ideas and algorithms from:
3443561Skato *
3543561Skato * - NetBSD libi386/biosdisk.c
3643561Skato * - FreeBSD biosboot/disk.c
3743561Skato *
3843561Skato */
3943561Skato
40339406Simp#include <sys/disk.h>
41339406Simp#include <sys/limits.h>
4243561Skato#include <stand.h>
43339406Simp#include <machine/bootinfo.h>
44339406Simp#include <stdarg.h>
4543561Skato
4643561Skato#include <sys/disklabel.h>
47104621Snyan#include <sys/diskpc98.h>
4843561Skato
4943561Skato#include <bootstrap.h>
5043561Skato#include <btxv86.h>
51339406Simp#include "disk.h"
5243561Skato#include "libi386.h"
5343561Skato
5463101Snyan#define BIOS_NUMDRIVES		0x475
5543561Skato#define BIOSDISK_SECSIZE	512
5643561Skato#define BUFSIZE			(1 * BIOSDISK_SECSIZE)
5743561Skato
5843561Skato#define DT_ATAPI		0x10		/* disk type for ATAPI floppies */
5943561Skato#define WDMAJOR			0		/* major numbers for devices we frontend for */
6043561Skato#define WFDMAJOR		1
6143561Skato#define FDMAJOR			2
6243561Skato#define DAMAJOR			4
6343561Skato
6443561Skato#ifdef DISK_DEBUG
6587599Sobrien# define DEBUG(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
6643561Skato#else
6743561Skato# define DEBUG(fmt, args...)
6843561Skato#endif
6943561Skato
70339406Simp/*
71339406Simp * List of BIOS devices, translation from disk unit number to
72339406Simp * BIOS unit number.
73339406Simp */
74339406Simpstatic struct bdinfo
75339406Simp{
76339406Simp	int		bd_unit;	/* BIOS unit number */
77339406Simp	int		bd_cyl;		/* BIOS geometry */
78339406Simp	int		bd_hds;
79339406Simp	int		bd_sec;
80339406Simp	int		bd_flags;
8159777Snyan#define BD_MODEINT13		0x0000
8259777Snyan#define BD_MODEEDD1		0x0001
8359777Snyan#define BD_MODEEDD3		0x0002
8463101Snyan#define BD_MODEMASK		0x0003
8559777Snyan#define BD_FLOPPY		0x0004
8659777Snyan#define BD_LABELOK		0x0008
8759777Snyan#define BD_PARTTABOK		0x0010
88108791Snyan#define BD_OPTICAL		0x0020
89333049Snyan	int		bd_type;	/* BIOS 'drive type' (floppy only) */
90339406Simp	uint16_t	bd_sectorsize;	/* Sector size */
91339406Simp	uint64_t	bd_sectors;	/* Disk size */
92333049Snyan	int		bd_da_unit;	/* kernel unit number for da */
93333049Snyan	int		bd_open;	/* reference counter */
94333049Snyan	void		*bd_bcache;	/* buffer cache data */
9543561Skato} bdinfo [MAXBDDEV];
9643561Skatostatic int nbdinfo = 0;
9743561Skato
98332154Skevans#define	BD(dev)	(bdinfo[(dev)->dd.d_unit])
99298230Sallanjude
100339406Simpstatic int bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks,
101333049Snyan    caddr_t dest);
102339406Simpstatic int bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks,
103333049Snyan    caddr_t dest);
104333049Snyanstatic int bd_int13probe(struct bdinfo *bd);
10543561Skato
106333049Snyanstatic int bd_init(void);
107333049Snyanstatic int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
108333049Snyan    char *buf, size_t *rsize);
109333049Snyanstatic int bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size,
110333049Snyan    char *buf, size_t *rsize);
111333049Snyanstatic int bd_open(struct open_file *f, ...);
112333049Snyanstatic int bd_close(struct open_file *f);
113339406Simpstatic int bd_ioctl(struct open_file *f, u_long cmd, void *data);
114333049Snyanstatic int bd_print(int verbose);
11543561Skato
11643561Skatostruct devsw biosdisk = {
117333049Snyan	"disk",
118333049Snyan	DEVT_DISK,
119333049Snyan	bd_init,
120333049Snyan	bd_strategy,
121333049Snyan	bd_open,
122333049Snyan	bd_close,
123339406Simp	bd_ioctl,
124333049Snyan	bd_print,
125333049Snyan	NULL
12643561Skato};
12743561Skato
12843561Skato/*
12943561Skato * Translate between BIOS device numbers and our private unit numbers.
13043561Skato */
13143561Skatoint
13243561Skatobd_bios2unit(int biosdev)
13343561Skato{
134333049Snyan	int i;
135333049Snyan
136333049Snyan	DEBUG("looking for bios device 0x%x", biosdev);
137333049Snyan	for (i = 0; i < nbdinfo; i++) {
138333049Snyan		DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit);
139333049Snyan		if (bdinfo[i].bd_unit == biosdev)
140333049Snyan			return (i);
141333049Snyan	}
142333049Snyan	return (-1);
14343561Skato}
14443561Skato
14543561Skatoint
14643561Skatobd_unit2bios(int unit)
14743561Skato{
148333049Snyan
149333049Snyan	if ((unit >= 0) && (unit < nbdinfo))
150333049Snyan		return (bdinfo[unit].bd_unit);
151333049Snyan	return (-1);
15243561Skato}
15343561Skato
154333049Snyan/*
15543561Skato * Quiz the BIOS for disk devices, save a little info about them.
15643561Skato */
15743561Skatostatic int
158333049Snyanbd_init(void)
15943561Skato{
160333049Snyan	int base, unit;
161333049Snyan	int da_drive=0, n=-0x10;
16243561Skato
163333049Snyan	/* sequence 0x90, 0x80, 0xa0 */
164333049Snyan	for (base = 0x90; base <= 0xa0; base += n, n += 0x30) {
165333049Snyan		for (unit = base; (nbdinfo < MAXBDDEV) || ((unit & 0x0f) < 4);
166333049Snyan		     unit++) {
167333049Snyan			bdinfo[nbdinfo].bd_open = 0;
168333049Snyan			bdinfo[nbdinfo].bd_bcache = NULL;
169333049Snyan			bdinfo[nbdinfo].bd_unit = unit;
170333049Snyan			bdinfo[nbdinfo].bd_flags =
171333049Snyan				(unit & 0xf0) == 0x90 ? BD_FLOPPY : 0;
172333049Snyan			if (!bd_int13probe(&bdinfo[nbdinfo])) {
173333049Snyan				if (((unit & 0xf0) == 0x90 &&
174333049Snyan					(unit & 0x0f) < 4) ||
175333049Snyan				    ((unit & 0xf0) == 0xa0 &&
176333049Snyan					(unit & 0x0f) < 6))
177333049Snyan					/* Target IDs are not contiguous. */
178333049Snyan					continue;
179333049Snyan				else
180333049Snyan					break;
181333049Snyan			}
18243561Skato
183333049Snyan			if (bdinfo[nbdinfo].bd_flags & BD_FLOPPY) {
184333049Snyan				/* available 1.44MB access? */
185333049Snyan				if (*(u_char *)PTOV(0xA15AE) &
186333049Snyan				    (1<<(unit & 0xf))) {
187333049Snyan					/* boot media 1.2MB FD? */
188333049Snyan					if ((*(u_char *)PTOV(0xA1584) &
189333049Snyan						0xf0) != 0x90)
190333049Snyan						bdinfo[nbdinfo].bd_unit =
191333049Snyan							0x30 + (unit & 0xf);
192333049Snyan				}
193333049Snyan			} else {
194333049Snyan				if ((unit & 0xF0) == 0xA0) /* SCSI HD or MO */
195333049Snyan					bdinfo[nbdinfo].bd_da_unit =
196333049Snyan						da_drive++;
197333049Snyan			}
198333049Snyan			/* XXX we need "disk aliases" to make this simpler */
199333049Snyan			printf("BIOS drive %c: is disk%d\n",
200333049Snyan			    'A' + nbdinfo, nbdinfo);
201333049Snyan			nbdinfo++;
20244467Skato		}
20343561Skato	}
204333049Snyan	bcache_add_dev(nbdinfo);
205333049Snyan	return(0);
20643561Skato}
20743561Skato
20843561Skato/*
20943561Skato * Try to detect a device supported by the legacy int13 BIOS
21043561Skato */
21143561Skatostatic int
21243561Skatobd_int13probe(struct bdinfo *bd)
21343561Skato{
214333049Snyan	int addr;
21563101Snyan
216333049Snyan	if (bd->bd_flags & BD_FLOPPY) {
217333049Snyan		addr = 0xa155c;
218333049Snyan	} else {
219333049Snyan		if ((bd->bd_unit & 0xf0) == 0x80)
220333049Snyan			addr = 0xa155d;
221333049Snyan		else
222333049Snyan			addr = 0xa1482;
223333049Snyan	}
224333049Snyan	if ( *(u_char *)PTOV(addr) & (1<<(bd->bd_unit & 0x0f))) {
225333049Snyan		bd->bd_flags |= BD_MODEINT13;
226333049Snyan		return (1);
227333049Snyan	}
228333049Snyan	if ((bd->bd_unit & 0xF0) == 0xA0) {
229333049Snyan		int media =
230333049Snyan			((unsigned *)PTOV(0xA1460))[bd->bd_unit & 0x0F] & 0x1F;
231108791Snyan
232333049Snyan		if (media == 7) { /* MO */
233333049Snyan			bd->bd_flags |= BD_MODEINT13 | BD_OPTICAL;
234333049Snyan			return(1);
235333049Snyan		}
236108791Snyan	}
237333049Snyan	return (0);
23843561Skato}
23943561Skato
24043561Skato/*
24143561Skato * Print information about disks
24243561Skato */
243328889Skevansstatic int
24443561Skatobd_print(int verbose)
24543561Skato{
246333049Snyan	char line[80];
247339406Simp	struct disk_devdesc dev;
248339406Simp	int i, ret = 0;
249333049Snyan	struct pc98_partition *dptr;
25043561Skato
251333049Snyan	if (nbdinfo == 0)
252333049Snyan		return (0);
25343561Skato
254333049Snyan	printf("%s devices:", biosdisk.dv_name);
255333049Snyan	if ((ret = pager_output("\n")) != 0)
256333049Snyan		return (ret);
257328889Skevans
258333049Snyan	for (i = 0; i < nbdinfo; i++) {
259339406Simp		snprintf(line, sizeof(line),
260339406Simp		    "    disk%d:   BIOS drive %c (%ju X %u):\n", i,
261339406Simp		    (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit):
262339406Simp		    ('C' + bdinfo[i].bd_unit - 0x80),
263339406Simp		    (uintmax_t)bdinfo[i].bd_sectors,
264339406Simp		    bdinfo[i].bd_sectorsize);
265333049Snyan		if ((ret = pager_output(line)) != 0)
266333049Snyan			break;
267328889Skevans
268333049Snyan		/* try to open the whole disk */
269339406Simp		dev.dd.d_dev = &biosdisk;
270333049Snyan		dev.dd.d_unit = i;
271339406Simp		dev.d_slice = -1;
272339406Simp		dev.d_partition = -1;
273339406Simp		if (disk_open(&dev,
274339406Simp		    bdinfo[i].bd_sectorsize * bdinfo[i].bd_sectors,
275339406Simp		    bdinfo[i].bd_sectorsize) == 0) {
276339406Simp			snprintf(line, sizeof(line), "    disk%d", i);
277339406Simp			ret = disk_print(&dev, line, verbose);
278339406Simp			disk_close(&dev);
279333049Snyan			if (ret != 0)
280339406Simp			    return (ret);
28143561Skato		}
28243561Skato	}
283333049Snyan	return (ret);
28443561Skato}
28543561Skato
286176654Snyan/* Given a size in 512 byte sectors, convert it to a human-readable number. */
287176654Snyanstatic char *
288176654Snyandisplay_size(uint64_t size)
289176654Snyan{
290333049Snyan	static char buf[80];
291333049Snyan	char unit;
292176654Snyan
293333049Snyan	size /= 2;
294333049Snyan	unit = 'K';
295333049Snyan	if (size >= 10485760000LL) {
296333049Snyan		size /= 1073741824;
297333049Snyan		unit = 'T';
298333049Snyan	} else if (size >= 10240000) {
299333049Snyan		size /= 1048576;
300333049Snyan		unit = 'G';
301333049Snyan	} else if (size >= 10000) {
302333049Snyan		size /= 1024;
303333049Snyan		unit = 'M';
304333049Snyan	}
305333049Snyan	sprintf(buf, "%6ld%cB", (long)size, unit);
306333049Snyan	return (buf);
307176654Snyan}
308176654Snyan
30959777Snyan/*
31043561Skato * Attempt to open the disk described by (dev) for use by (f).
31143561Skato *
31243561Skato * Note that the philosophy here is "give them exactly what
31343561Skato * they ask for".  This is necessary because being too "smart"
31443561Skato * about what the user might want leads to complications.
31543561Skato * (eg. given no slice or partition value, with a disk that is
31643561Skato *  sliced - are they after the first BSD slice, or the DOS
31743561Skato *  slice before it?)
31843561Skato */
31943561Skatostatic int
32043561Skatobd_open(struct open_file *f, ...)
32143561Skato{
322339406Simp	va_list				ap;
323339406Simp	struct disk_devdesc		*dev;
324339406Simp	struct disk_devdesc		disk;
325339406Simp	int				err;
326339406Simp	uint64_t			size;
32743561Skato
328339406Simp	va_start(ap, f);
329339406Simp	dev = va_arg(ap, struct disk_devdesc *);
330339406Simp	va_end(ap);
33143561Skato
332339406Simp	if (dev->dd.d_unit < 0 || dev->dd.d_unit >= nbdinfo)
333339406Simp		return (EIO);
334339406Simp	BD(dev).bd_open++;
335339406Simp	if (BD(dev).bd_bcache == NULL)
336339406Simp		BD(dev).bd_bcache = bcache_allocate();
337298230Sallanjude
338339406Simp	/*
339339406Simp	 * Read disk size from partition.
340339406Simp	 * This is needed to work around buggy BIOS systems returning
341339406Simp	 * wrong (truncated) disk media size.
342339406Simp	 * During bd_probe() we tested if the mulitplication of bd_sectors
343339406Simp	 * would overflow so it should be safe to perform here.
344339406Simp	 */
345339406Simp	disk.dd.d_dev = dev->dd.d_dev;
346339406Simp	disk.dd.d_unit = dev->dd.d_unit;
347339406Simp	disk.d_slice = -1;
348339406Simp	disk.d_partition = -1;
349339406Simp	disk.d_offset = 0;
350339406Simp	if (disk_open(&disk, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
351339406Simp	    BD(dev).bd_sectorsize) == 0) {
35243561Skato
353339406Simp		if (disk_ioctl(&disk, DIOCGMEDIASIZE, &size) == 0) {
354339406Simp			size /= BD(dev).bd_sectorsize;
355339406Simp			if (size > BD(dev).bd_sectors)
356339406Simp				BD(dev).bd_sectors = size;
357339406Simp		}
358339406Simp		disk_close(&disk);
359339406Simp	}
36043561Skato
361339406Simp	err = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
362339406Simp	    BD(dev).bd_sectorsize);
363339406Simp	return(err);
364172968Snyan}
365172968Snyan
366339406Simpstatic int
367339406Simpbd_close(struct open_file *f)
368172968Snyan{
369339406Simp	struct disk_devdesc *dev;
370172968Snyan
371339406Simp	dev = (struct disk_devdesc *)f->f_devdata;
372339406Simp	BD(dev).bd_open--;
373339406Simp	if (BD(dev).bd_open == 0) {
374339406Simp	    bcache_free(BD(dev).bd_bcache);
375339406Simp	    BD(dev).bd_bcache = NULL;
37643561Skato	}
377339406Simp	return (disk_close(dev));
37843561Skato}
37943561Skato
38043561Skatostatic int
381339406Simpbd_ioctl(struct open_file *f, u_long cmd, void *data)
38243561Skato{
383339406Simp	struct disk_devdesc *dev;
384339406Simp	int rc;
38543561Skato
386339406Simp	dev = (struct disk_devdesc *)f->f_devdata;
38759777Snyan
388339406Simp	rc = disk_ioctl(dev, cmd, data);
389339406Simp	if (rc != ENOTTY)
390339406Simp		return (rc);
391339406Simp
392339406Simp	switch (cmd) {
393339406Simp	case DIOCGSECTORSIZE:
394339406Simp		*(u_int *)data = BD(dev).bd_sectorsize;
395339406Simp		break;
396339406Simp	case DIOCGMEDIASIZE:
397339406Simp		*(uint64_t *)data = BD(dev).bd_sectors * BD(dev).bd_sectorsize;
398339406Simp		break;
399339406Simp	default:
400339406Simp		return (ENOTTY);
40143561Skato	}
402339406Simp	return (0);
40343561Skato}
40443561Skato
40543561Skatostatic int
406313355Stsoomebd_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
407298230Sallanjude    char *buf, size_t *rsize)
40843561Skato{
409339406Simp	struct bcache_devdata bcd;
410339406Simp	struct disk_devdesc *dev;
41158165Snyan
412339406Simp	dev = (struct disk_devdesc *)devdata;
413339406Simp	bcd.dv_strategy = bd_realstrategy;
414339406Simp	bcd.dv_devdata = devdata;
415339406Simp	bcd.dv_cache = BD(dev).bd_bcache;
416339406Simp	return (bcache_strategy(&bcd, rw, dblk + dev->d_offset,
417339406Simp	    size, buf, rsize));
41843561Skato}
41943561Skato
42043561Skatostatic int
421333049Snyanbd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
422333049Snyan    char *buf, size_t *rsize)
42343561Skato{
424339406Simp    struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
425339406Simp    uint64_t		disk_blocks;
426339406Simp    int			blks, rc;
427339406Simp#ifdef BD_SUPPORT_FRAGS /* XXX: sector size */
42843561Skato    char		fragbuf[BIOSDISK_SECSIZE];
42943561Skato    size_t		fragsize;
43043561Skato
43143561Skato    fragsize = size % BIOSDISK_SECSIZE;
43243561Skato#else
433339406Simp    if (size % BD(dev).bd_sectorsize)
43443561Skato	panic("bd_strategy: %d bytes I/O not multiple of block size", size);
43543561Skato#endif
43643561Skato
437339406Simp    DEBUG("open_disk %p", dev);
438339406Simp
439339406Simp    /*
440339406Simp     * Check the value of the size argument. We do have quite small
441339406Simp     * heap (64MB), but we do not know good upper limit, so we check against
442339406Simp     * INT_MAX here. This will also protect us against possible overflows
443339406Simp     * while translating block count to bytes.
444339406Simp     */
445339406Simp    if (size > INT_MAX) {
446339406Simp	DEBUG("too large read: %zu bytes", size);
447339406Simp	return (EIO);
448339406Simp    }
449339406Simp
450339406Simp    blks = size / BD(dev).bd_sectorsize;
451339406Simp    if (dblk > dblk + blks)
452339406Simp	return (EIO);
453339406Simp
45443561Skato    if (rsize)
45543561Skato	*rsize = 0;
456172925Snyan
457339406Simp    /* Get disk blocks, this value is either for whole disk or for partition */
458339406Simp    if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks)) {
459339406Simp	/* DIOCGMEDIASIZE does return bytes. */
460339406Simp        disk_blocks /= BD(dev).bd_sectorsize;
461339406Simp    } else {
462339406Simp	/* We should not get here. Just try to survive. */
463339406Simp	disk_blocks = BD(dev).bd_sectors - dev->d_offset;
464339406Simp    }
465339406Simp
466339406Simp    /* Validate source block address. */
467339406Simp    if (dblk < dev->d_offset || dblk >= dev->d_offset + disk_blocks)
468339406Simp	return (EIO);
469339406Simp
470339406Simp    /*
471339406Simp     * Truncate if we are crossing disk or partition end.
472339406Simp     */
473339406Simp    if (dblk + blks >= dev->d_offset + disk_blocks) {
474339406Simp	blks = dev->d_offset + disk_blocks - dblk;
475339406Simp	size = blks * BD(dev).bd_sectorsize;
476339406Simp	DEBUG("short read %d", blks);
477339406Simp    }
478339406Simp
479339406Simp    switch (rw & F_MASK) {
480172925Snyan    case F_READ:
481339406Simp	DEBUG("read %d from %lld to %p", blks, dblk, buf);
482172925Snyan
483339406Simp	if (blks && (rc = bd_read(dev, dblk, blks, buf))) {
484339406Simp	    /* Filter out floppy controller errors */
485339406Simp	    if (BD(dev).bd_flags != BD_FLOPPY || rc != 0x20) {
486339406Simp		printf("read %d from %lld to %p, error: 0x%x", blks, dblk,
487339406Simp		    buf, rc);
488339406Simp	    }
489172925Snyan	    return (EIO);
490172925Snyan	}
491339406Simp#ifdef BD_SUPPORT_FRAGS /* XXX: sector size */
492172925Snyan	DEBUG("bd_strategy: frag read %d from %d+%d to %p",
493172925Snyan	    fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE));
494172925Snyan	if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) {
495172925Snyan	    DEBUG("frag read error");
496172925Snyan	    return(EIO);
497172925Snyan	}
498172925Snyan	bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize);
49943561Skato#endif
500172925Snyan	break;
501172925Snyan    case F_WRITE :
502172925Snyan	DEBUG("write %d from %d to %p", blks, dblk, buf);
50387734Snyan
504339406Simp	if (blks && bd_write(dev, dblk, blks, buf)) {
505172925Snyan	    DEBUG("write error");
506172925Snyan	    return (EIO);
507172925Snyan	}
50887734Snyan#ifdef BD_SUPPORT_FRAGS
50987734Snyan	if(fragsize) {
510172925Snyan	    DEBUG("Attempted to write a frag");
511172925Snyan	    return (EIO);
51287734Snyan	}
51387734Snyan#endif
514172925Snyan	break;
515172925Snyan    default:
516172925Snyan	/* DO NOTHING */
517172925Snyan	return (EROFS);
518172925Snyan    }
51987734Snyan
52087734Snyan    if (rsize)
52187734Snyan	*rsize = size;
52287734Snyan    return (0);
52343561Skato}
52443561Skato
52543561Skato/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */
52643561Skato#define FLOPPY_BOUNCEBUF	18
52743561Skato
52843561Skatostatic int
529339406Simpbd_chs_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
530339406Simp    int write)
53143561Skato{
532172965Snyan    u_int	x, bpc, cyl, hd, sec;
533172965Snyan
534339406Simp    bpc = BD(dev).bd_sec * BD(dev).bd_hds;	/* blocks per cylinder */
535172965Snyan    x = dblk;
536172965Snyan    cyl = x / bpc;			/* block # / blocks per cylinder */
537172965Snyan    x %= bpc;				/* block offset into cylinder */
538339406Simp    hd = x / BD(dev).bd_sec;		/* offset / blocks per track */
539339406Simp    sec = x % BD(dev).bd_sec;		/* offset into track */
540172965Snyan
541172965Snyan    v86.ctl = V86_FLAGS;
542172965Snyan    v86.addr = 0x1b;
543172965Snyan    if (write)
544339406Simp        v86.eax = 0x0500 | BD(dev).bd_unit;
545172965Snyan    else
546339406Simp	v86.eax = 0x0600 | BD(dev).bd_unit;
547339406Simp    if (BD(dev).bd_flags & BD_FLOPPY) {
548172965Snyan	v86.eax |= 0xd000;
549172965Snyan	v86.ecx = 0x0200 | (cyl & 0xff);
550172965Snyan	v86.edx = (hd << 8) | (sec + 1);
551339406Simp    } else if (BD(dev).bd_flags & BD_OPTICAL) {
552172965Snyan	v86.eax &= 0xFF7F;
553172965Snyan	v86.ecx = dblk & 0xFFFF;
554172965Snyan	v86.edx = dblk >> 16;
555172965Snyan    } else {
556172965Snyan	v86.ecx = cyl;
557172965Snyan	v86.edx = (hd << 8) | sec;
558172965Snyan    }
559172965Snyan    v86.ebx = blks * BIOSDISK_SECSIZE;
560172965Snyan    v86.es = VTOPSEG(dest);
561172965Snyan    v86.ebp = VTOPOFF(dest);
562172965Snyan    v86int();
563292682Sjhb    return (V86_CY(v86.efl));
564172965Snyan}
565172965Snyan
566172965Snyanstatic int
567339406Simpbd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, int write)
568172965Snyan{
569172965Snyan    u_int	x, sec, result, resid, retry, maxfer;
570339406Simp    caddr_t	p, xp, bbuf;
57143561Skato
572172965Snyan    /* Just in case some idiot actually tries to read/write -1 blocks... */
57368358Snyan    if (blks < 0)
57468358Snyan	return (-1);
57568358Snyan
57643561Skato    resid = blks;
57743561Skato    p = dest;
57843561Skato
57943561Skato    /* Decide whether we have to bounce */
580339406Simp    if (VTOP(dest) >> 20 != 0 || (BD(dev).bd_unit < 0x80 &&
581339406Simp	(VTOP(dest) >> 16) != (VTOP(dest +
582339406Simp	blks * BD(dev).bd_sectorsize) >> 16))) {
58343561Skato
58443561Skato	/*
585172965Snyan	 * There is a 64k physical boundary somewhere in the
586172965Snyan	 * destination buffer, or the destination buffer is above
587172965Snyan	 * first 1MB of physical memory so we have to arrange a
588172965Snyan	 * suitable bounce buffer.  Allocate a buffer twice as large
589172965Snyan	 * as we need to.  Use the bottom half unless there is a break
590172965Snyan	 * there, in which case we use the top half.
59143561Skato	 */
592339406Simp	x = V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize;
593339406Simp	x = min(x, (unsigned)blks);
594339406Simp	bbuf = PTOV(V86_IO_BUFFER);
595172965Snyan	maxfer = x;		/* limit transfers to bounce region size */
59643561Skato    } else {
597339406Simp	bbuf = NULL;
59843561Skato	maxfer = 0;
59943561Skato    }
60043561Skato
60143561Skato    while (resid > 0) {
602172965Snyan	/*
603172965Snyan	 * Play it safe and don't cross track boundaries.
604172965Snyan	 * (XXX this is probably unnecessary)
605172965Snyan	 */
606339406Simp	sec = dblk % BD(dev).bd_sec;	/* offset into track */
607339406Simp	x = min(BD(dev).bd_sec - sec, resid);
60843561Skato	if (maxfer > 0)
60943561Skato	    x = min(x, maxfer);		/* fit bounce buffer */
61043561Skato
61143561Skato	/* where do we transfer to? */
612339406Simp	xp = bbuf == NULL ? p : bbuf;
61343561Skato
614172965Snyan	/*
615172965Snyan	 * Put your Data In, Put your Data out,
616172965Snyan	 * Put your Data In, and shake it all about
617172965Snyan	 */
618172965Snyan	if (write && bbuf != NULL)
619339406Simp	    bcopy(p, bbuf, x * BD(dev).bd_sectorsize);
62043561Skato
621172965Snyan	/*
622172965Snyan	 * Loop retrying the operation a couple of times.  The BIOS
623172965Snyan	 * may also retry.
624172965Snyan	 */
62543561Skato	for (retry = 0; retry < 3; retry++) {
62643561Skato	    /* if retrying, reset the drive */
62743561Skato	    if (retry > 0) {
62851586Skato		v86.ctl = V86_FLAGS;
62951586Skato		v86.addr = 0x1b;
630339406Simp		v86.eax = 0x0300 | BD(dev).bd_unit;
63143561Skato		v86int();
63243561Skato	    }
633172965Snyan
634339406Simp	    result = bd_chs_io(dev, dblk, x, xp, write);
63543561Skato	    if (result == 0)
63643561Skato		break;
63743561Skato	}
638172965Snyan
639172965Snyan	if (write)
640200631Snyan	    DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x,
641200631Snyan		p, VTOP(p), dblk, result ? "failed" : "ok");
642172965Snyan	else
643200631Snyan	    DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x,
644200631Snyan		dblk, p, VTOP(p), result ? "failed" : "ok");
64543561Skato	if (result) {
646339406Simp	    return (result);
64743561Skato	}
648172965Snyan	if (!write && bbuf != NULL)
649339406Simp	    bcopy(bbuf, p, x * BD(dev).bd_sectorsize);
650339406Simp	p += (x * BD(dev).bd_sectorsize);
65143561Skato	dblk += x;
65243561Skato	resid -= x;
65343561Skato    }
654172965Snyan
655339406Simp/*    hexdump(dest, (blks * BD(dev).bd_sectorsize)); */
65643561Skato    return(0);
65743561Skato}
65843561Skato
659172965Snyanstatic int
660339406Simpbd_read(struct disk_devdesc *dev, daddr_t dblk, int blks,
661339406Simp    caddr_t dest)
662172965Snyan{
66387734Snyan
664339406Simp	return (bd_io(dev, dblk, blks, dest, 0));
665172965Snyan}
666172965Snyan
66743561Skatostatic int
668339406Simpbd_write(struct disk_devdesc *dev, daddr_t dblk, int blks,
669339406Simp    caddr_t dest)
67087734Snyan{
67187734Snyan
672339406Simp	return (bd_io(dev, dblk, blks, dest, 1));
673172965Snyan}
67487734Snyan
675339406Simp#if 0
67687734Snyanstatic int
67743561Skatobd_getgeom(struct open_disk *od)
67843561Skato{
67943561Skato
68043561Skato    if (od->od_flags & BD_FLOPPY) {
68154819Snyan	od->od_cyl = 79;
68243561Skato	od->od_hds = 2;
68343561Skato	od->od_sec = (od->od_unit & 0xf0) == 0x30 ? 18 : 15;
684108791Snyan    } else if (od->od_flags & BD_OPTICAL) {
685108791Snyan	od->od_cyl = 0xFFFE;
686108791Snyan	od->od_hds = 8;
687108791Snyan	od->od_sec = 32;
68859777Snyan    } else {
68954819Snyan	v86.ctl = V86_FLAGS;
69043561Skato	v86.addr = 0x1b;
69143561Skato	v86.eax = 0x8400 | od->od_unit;
69243561Skato	v86int();
69343561Skato
69443561Skato	od->od_cyl = v86.ecx;
69543561Skato	od->od_hds = (v86.edx >> 8) & 0xff;
69643561Skato	od->od_sec = v86.edx & 0xff;
697292682Sjhb	if (V86_CY(v86.efl))
69851586Skato	    return(1);
69943561Skato    }
70043561Skato
70143561Skato    DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec);
70243561Skato    return(0);
70343561Skato}
704339406Simp#endif
70543561Skato
70643561Skato/*
70753207Snyan * Return the BIOS geometry of a given "fixed drive" in a format
70853207Snyan * suitable for the legacy bootinfo structure.  Since the kernel is
70953207Snyan * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we
71053207Snyan * prefer to get the information directly, rather than rely on being
71153207Snyan * able to put it together from information already maintained for
71253207Snyan * different purposes and for a probably different number of drives.
71353207Snyan *
71453207Snyan * For valid drives, the geometry is expected in the format (31..0)
71553207Snyan * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are
71653207Snyan * indicated by returning the geometry of a "1.2M" PC-format floppy
71753207Snyan * disk.  And, incidentally, what is returned is not the geometry as
71853207Snyan * such but the highest valid cylinder, head, and sector numbers.
71953207Snyan */
72053207Snyanu_int32_t
72153207Snyanbd_getbigeom(int bunit)
72253207Snyan{
72354819Snyan    int hds = 0;
72455339Snyan    int unit = 0x80;		/* IDE HDD */
72554819Snyan    u_int addr = 0xA155d;
72655339Snyan
72754819Snyan    while (unit < 0xa7) {
72855339Snyan	if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f)))
72955339Snyan	    if (hds++ == bunit)
73054819Snyan		break;
731108791Snyan
732108791Snyan	if (unit >= 0xA0) {
733108791Snyan	    int  media = ((unsigned *)PTOV(0xA1460))[unit & 0x0F] & 0x1F;
734108791Snyan
735108791Snyan	    if (media == 7 && hds++ == bunit)	/* SCSI MO */
736108791Snyan		return(0xFFFE0820); /* C:65535 H:8 S:32 */
737108791Snyan	}
73854819Snyan	if (++unit == 0x84) {
739108791Snyan	    unit = 0xA0;	/* SCSI HDD */
74054819Snyan	    addr = 0xA1482;
74154819Snyan	}
74254819Snyan    }
74354819Snyan    if (unit == 0xa7)
744108791Snyan	return 0x4F020F;	/* 1200KB FD C:80 H:2 S:15 */
74553207Snyan    v86.ctl = V86_FLAGS;
74654819Snyan    v86.addr = 0x1b;
74754819Snyan    v86.eax = 0x8400 | unit;
74854819Snyan    v86int();
749292682Sjhb    if (V86_CY(v86.efl))
750108791Snyan	return 0x4F020F;	/* 1200KB FD C:80 H:2 S:15 */
75154819Snyan    return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff);
75253207Snyan}
75353207Snyan
75453207Snyan/*
755130603Sphk * Return a suitable dev_t value for (dev).
75643561Skato *
75743561Skato * In the case where it looks like (dev) is a SCSI disk, we allow the number of
75843561Skato * IDE disks to be specified in $num_ide_disks.  There should be a Better Way.
75943561Skato */
76043561Skatoint
761339406Simpbd_getdev(struct i386_devdesc *d)
76243561Skato{
763339406Simp    struct disk_devdesc		*dev;
76443561Skato    int				biosdev;
76543561Skato    int 			major;
76643561Skato    int				rootdev;
76743561Skato    char			*nip, *cp;
76843561Skato    int				unitofs = 0, i, unit;
76943561Skato
770339406Simp    dev = (struct disk_devdesc *)d;
771332154Skevans    biosdev = bd_unit2bios(dev->dd.d_unit);
772332154Skevans    DEBUG("unit %d BIOS device %d", dev->dd.d_unit, biosdev);
77343561Skato    if (biosdev == -1)				/* not a BIOS device */
77443561Skato	return(-1);
775339406Simp    if (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
776339406Simp	BD(dev).bd_sectorsize) != 0)		/* oops, not a viable device */
777339406Simp	    return (-1);
778339406Simp    else
779339406Simp	disk_close(dev);
78043561Skato
78143561Skato    if ((biosdev & 0xf0) == 0x90 || (biosdev & 0xf0) == 0x30) {
78243561Skato	/* floppy (or emulated floppy) or ATAPI device */
783339406Simp	if (BD(dev).bd_type == DT_ATAPI) {
78443561Skato	    /* is an ATAPI disk */
78543561Skato	    major = WFDMAJOR;
78643561Skato	} else {
78743561Skato	    /* is a floppy disk */
78843561Skato	    major = FDMAJOR;
78943561Skato	}
79043561Skato    } else {
79143561Skato	/* harddisk */
792339406Simp	if ((BD(dev).bd_flags & BD_LABELOK) && 0) {
793339406Simp//	    (BD(dev).bd_disklabel.d_type == DTYPE_SCSI)) {
79443561Skato	    /* label OK, disk labelled as SCSI */
79543561Skato	    major = DAMAJOR;
79643561Skato	    /* check for unit number correction hint, now deprecated */
79743561Skato	    if ((nip = getenv("num_ide_disks")) != NULL) {
79843561Skato		i = strtol(nip, &cp, 0);
79943561Skato		/* check for parse error */
80043561Skato		if ((cp != nip) && (*cp == 0))
80143561Skato		    unitofs = i;
80243561Skato	    }
80343561Skato	} else {
80443561Skato	    /* assume an IDE disk */
80543561Skato	    major = WDMAJOR;
80643561Skato	}
80743561Skato    }
80868358Snyan    /* default root disk unit number */
80968358Snyan    if ((biosdev & 0xf0) == 0xa0)
810339406Simp	unit = BD(dev).bd_da_unit;
81168358Snyan    else
81268358Snyan	unit = biosdev & 0xf;
81368358Snyan
81443561Skato    /* XXX a better kludge to set the root disk unit number */
81543561Skato    if ((nip = getenv("root_disk_unit")) != NULL) {
81643561Skato	i = strtol(nip, &cp, 0);
81743561Skato	/* check for parse error */
81843561Skato	if ((cp != nip) && (*cp == 0))
81943561Skato	    unit = i;
82043561Skato    }
82143561Skato
822339406Simp    rootdev = MAKEBOOTDEV(major, dev->d_slice + 1, unit, dev->d_partition);
82343561Skato    DEBUG("dev is 0x%x\n", rootdev);
82443561Skato    return(rootdev);
82543561Skato}
826