bioscd.c revision 313355
1158559Snyan/*-
2158559Snyan * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3158559Snyan * Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org>
4158559Snyan * All rights reserved.
5158559Snyan *
6158559Snyan * Redistribution and use in source and binary forms, with or without
7158559Snyan * modification, are permitted provided that the following conditions
8158559Snyan * are met:
9158559Snyan * 1. Redistributions of source code must retain the above copyright
10158559Snyan *    notice, this list of conditions and the following disclaimer.
11158559Snyan * 2. Redistributions in binary form must reproduce the above copyright
12158559Snyan *    notice, this list of conditions and the following disclaimer in the
13158559Snyan *    documentation and/or other materials provided with the distribution.
14158559Snyan *
15158559Snyan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16158559Snyan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17158559Snyan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18158559Snyan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19158559Snyan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20158559Snyan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21158559Snyan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22158559Snyan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23158559Snyan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24158559Snyan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25158559Snyan * SUCH DAMAGE.
26158559Snyan */
27158559Snyan
28158559Snyan#include <sys/cdefs.h>
29158559Snyan__FBSDID("$FreeBSD: stable/11/sys/boot/pc98/libpc98/bioscd.c 313355 2017-02-06 22:03:07Z tsoome $");
30158559Snyan
31158559Snyan/*
32158559Snyan * BIOS CD device handling for CD's that have been booted off of via no
33158559Snyan * emulation booting as defined in the El Torito standard.
34158559Snyan *
35158559Snyan * Ideas and algorithms from:
36158559Snyan *
37158559Snyan * - FreeBSD libi386/biosdisk.c
38158559Snyan *
39158559Snyan */
40158559Snyan
41158559Snyan#include <stand.h>
42158559Snyan
43158559Snyan#include <sys/param.h>
44158559Snyan#include <machine/bootinfo.h>
45158559Snyan
46158559Snyan#include <stdarg.h>
47158559Snyan
48158559Snyan#include <bootstrap.h>
49158559Snyan#include <btxv86.h>
50158559Snyan#include "libi386.h"
51158559Snyan
52158559Snyan#define BIOSCD_SECSIZE		2048
53158559Snyan#define BUFSIZE			(1 * BIOSCD_SECSIZE)
54158559Snyan#define	MAXBCDEV		1
55158559Snyan
56158559Snyan/* Major numbers for devices we frontend for. */
57158559Snyan#define ACDMAJOR		117
58158559Snyan#define	CDMAJOR			15
59158559Snyan
60158559Snyan#ifdef DISK_DEBUG
61158559Snyan# define DEBUG(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
62158559Snyan#else
63158559Snyan# define DEBUG(fmt, args...)
64158559Snyan#endif
65158559Snyan
66158559Snyanstruct specification_packet {
67158559Snyan	u_char	sp_size;
68158559Snyan	u_char	sp_bootmedia;
69158559Snyan	u_char	sp_drive;
70158559Snyan	u_char	sp_controller;
71158559Snyan	u_int	sp_lba;
72158559Snyan	u_short	sp_devicespec;
73158559Snyan	u_short	sp_buffersegment;
74158559Snyan	u_short	sp_loadsegment;
75158559Snyan	u_short	sp_sectorcount;
76158559Snyan	u_short	sp_cylsec;
77158559Snyan	u_char	sp_head;
78158559Snyan};
79158559Snyan
80158559Snyan/*
81158559Snyan * List of BIOS devices, translation from disk unit number to
82158559Snyan * BIOS unit number.
83158559Snyan */
84158559Snyanstatic struct bcinfo {
85158559Snyan	int	bc_unit;		/* BIOS unit number */
86158559Snyan	struct specification_packet bc_sp;
87298230Sallanjude	int	bc_open;		/* reference counter */
88298230Sallanjude	void	*bc_bcache;		/* buffer cache data */
89158559Snyan} bcinfo [MAXBCDEV];
90158559Snyanstatic int nbcinfo = 0;
91158559Snyan
92298230Sallanjude#define	BC(dev)	(bcinfo[(dev)->d_unit])
93298230Sallanjude
94158559Snyanstatic int	bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
95158559Snyanstatic int	bc_init(void);
96158559Snyanstatic int	bc_strategy(void *devdata, int flag, daddr_t dblk,
97313355Stsoome		    size_t size, char *buf, size_t *rsize);
98298230Sallanjudestatic int	bc_realstrategy(void *devdata, int flag, daddr_t dblk,
99313355Stsoome		    size_t size, char *buf, size_t *rsize);
100158559Snyanstatic int	bc_open(struct open_file *f, ...);
101158559Snyanstatic int	bc_close(struct open_file *f);
102158559Snyanstatic void	bc_print(int verbose);
103158559Snyan
104158559Snyanstruct devsw bioscd = {
105158559Snyan	"cd",
106158559Snyan	DEVT_CD,
107158559Snyan	bc_init,
108158559Snyan	bc_strategy,
109158559Snyan	bc_open,
110158559Snyan	bc_close,
111158559Snyan	noioctl,
112158559Snyan	bc_print,
113158559Snyan	NULL
114158559Snyan};
115158559Snyan
116158559Snyan/*
117158559Snyan * Translate between BIOS device numbers and our private unit numbers.
118158559Snyan */
119158559Snyanint
120158559Snyanbc_bios2unit(int biosdev)
121158559Snyan{
122158559Snyan	int i;
123158559Snyan
124158559Snyan	DEBUG("looking for bios device 0x%x", biosdev);
125158559Snyan	for (i = 0; i < nbcinfo; i++) {
126158559Snyan		DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
127158559Snyan		if (bcinfo[i].bc_unit == biosdev)
128158559Snyan			return(i);
129158559Snyan	}
130158559Snyan	return(-1);
131158559Snyan}
132158559Snyan
133158559Snyanint
134158559Snyanbc_unit2bios(int unit)
135158559Snyan{
136158559Snyan	if ((unit >= 0) && (unit < nbcinfo))
137158559Snyan		return(bcinfo[unit].bc_unit);
138158559Snyan	return(-1);
139158559Snyan}
140158559Snyan
141158559Snyan/*
142158559Snyan * We can't quiz, we have to be told what device to use, so this functoin
143158559Snyan * doesn't do anything.  Instead, the loader calls bc_add() with the BIOS
144158559Snyan * device number to add.
145158559Snyan */
146158559Snyanstatic int
147158559Snyanbc_init(void)
148158559Snyan{
149158559Snyan
150158559Snyan	return (0);
151158559Snyan}
152158559Snyan
153158559Snyanint
154158559Snyanbc_add(int biosdev)
155158559Snyan{
156158559Snyan
157158559Snyan	if (nbcinfo >= MAXBCDEV)
158158559Snyan		return (-1);
159158559Snyan	bcinfo[nbcinfo].bc_unit = biosdev;
160158559Snyan
161158559Snyan	/* SCSI CD-ROM only */
162158559Snyan	if ((biosdev & 0xf0) != 0xa0)
163158559Snyan		return (-1);
164158559Snyan	if ((((uint32_t *)PTOV(0xA1460))[biosdev & 0x0f] & 0x1f) != 5)
165158559Snyan		return (-1);
166158559Snyan
167158559Snyan	printf("BIOS CD is cd%d\n", nbcinfo);
168158559Snyan	nbcinfo++;
169298230Sallanjude	bcache_add_dev(nbcinfo);	/* register cd device in bcache */
170158559Snyan	return(0);
171158559Snyan}
172158559Snyan
173158559Snyan/*
174158559Snyan * Print information about disks
175158559Snyan */
176158559Snyanstatic void
177158559Snyanbc_print(int verbose)
178158559Snyan{
179190146Snyan	char line[80];
180158559Snyan	int i;
181190146Snyan
182300117Simp	pager_open();
183158559Snyan	for (i = 0; i < nbcinfo; i++) {
184158559Snyan		sprintf(line, "    cd%d: Device 0x%x\n", i,
185158559Snyan		    bcinfo[i].bc_sp.sp_devicespec);
186300117Simp		if (pager_output(line))
187300117Simp			break;
188158559Snyan	}
189300117Simp	pager_close();
190158559Snyan}
191158559Snyan
192158559Snyan/*
193158559Snyan * Attempt to open the disk described by (dev) for use by (f).
194158559Snyan */
195158559Snyanstatic int
196158559Snyanbc_open(struct open_file *f, ...)
197158559Snyan{
198158559Snyan	va_list ap;
199158559Snyan	struct i386_devdesc *dev;
200158559Snyan
201158559Snyan	va_start(ap, f);
202158559Snyan	dev = va_arg(ap, struct i386_devdesc *);
203158559Snyan	va_end(ap);
204163897Smarcel	if (dev->d_unit >= nbcinfo) {
205158559Snyan		DEBUG("attempt to open nonexistent disk");
206158559Snyan		return(ENXIO);
207158559Snyan	}
208158559Snyan
209298230Sallanjude	BC(dev).bc_open++;
210298230Sallanjude	if (BC(dev).bc_bcache == NULL)
211298230Sallanjude		BC(dev).bc_bcache = bcache_allocate();
212158559Snyan	return(0);
213158559Snyan}
214158559Snyan
215158559Snyanstatic int
216158559Snyanbc_close(struct open_file *f)
217158559Snyan{
218298230Sallanjude	struct i386_devdesc *dev;
219158559Snyan
220298230Sallanjude	dev = (struct i386_devdesc *)f->f_devdata;
221298230Sallanjude	BC(dev).bc_open--;
222298230Sallanjude	if (BC(dev).bc_open == 0) {
223298230Sallanjude		bcache_free(BC(dev).bc_bcache);
224298230Sallanjude		BC(dev).bc_bcache = NULL;
225298230Sallanjude	}
226158559Snyan	return(0);
227158559Snyan}
228158559Snyan
229298230Sallanjudestatic int
230313355Stsoomebc_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
231298230Sallanjude    char *buf, size_t *rsize)
232298230Sallanjude{
233298230Sallanjude	struct bcache_devdata bcd;
234298230Sallanjude	struct i386_devdesc *dev;
235298230Sallanjude
236298230Sallanjude	dev = (struct i386_devdesc *)devdata;
237298230Sallanjude	bcd.dv_strategy = bc_realstrategy;
238298230Sallanjude	bcd.dv_devdata = devdata;
239298230Sallanjude	bcd.dv_cache = BC(dev).bc_bcache;
240298230Sallanjude
241313355Stsoome	return (bcache_strategy(&bcd, rw, dblk, size, buf, rsize));
242298230Sallanjude}
243298230Sallanjude
244158559Snyanstatic int
245313355Stsoomebc_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
246298230Sallanjude    char *buf, size_t *rsize)
247158559Snyan{
248158559Snyan	struct i386_devdesc *dev;
249158559Snyan	int unit;
250158559Snyan	int blks;
251158559Snyan#ifdef BD_SUPPORT_FRAGS
252158559Snyan	char fragbuf[BIOSCD_SECSIZE];
253158559Snyan	size_t fragsize;
254158559Snyan
255158559Snyan	fragsize = size % BIOSCD_SECSIZE;
256158559Snyan#else
257158559Snyan	if (size % BIOSCD_SECSIZE)
258158559Snyan		return (EINVAL);
259158559Snyan#endif
260158559Snyan
261158559Snyan	if (rw != F_READ)
262158559Snyan		return(EROFS);
263158559Snyan	dev = (struct i386_devdesc *)devdata;
264163897Smarcel	unit = dev->d_unit;
265158559Snyan	blks = size / BIOSCD_SECSIZE;
266158559Snyan	if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
267158559Snyan		return (EINVAL);
268158559Snyan	dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
269190146Snyan	DEBUG("read %d from %lld to %p", blks, dblk, buf);
270158559Snyan
271158559Snyan	if (rsize)
272158559Snyan		*rsize = 0;
273158559Snyan	if (blks && bc_read(unit, dblk, blks, buf)) {
274158559Snyan		DEBUG("read error");
275158559Snyan		return (EIO);
276158559Snyan	}
277158559Snyan#ifdef BD_SUPPORT_FRAGS
278190146Snyan	DEBUG("frag read %d from %lld+%d to %p",
279158559Snyan	    fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
280190146Snyan	if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) {
281158559Snyan		DEBUG("frag read error");
282158559Snyan		return(EIO);
283158559Snyan	}
284158559Snyan	bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
285158559Snyan#endif
286158559Snyan	if (rsize)
287158559Snyan		*rsize = size;
288158559Snyan	return (0);
289158559Snyan}
290158559Snyan
291190146Snyan/* Max number of sectors to bounce-buffer at a time. */
292190146Snyan#define	CD_BOUNCEBUF	8
293190146Snyan
294158559Snyanstatic int
295158559Snyanbc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
296158559Snyan{
297190146Snyan	u_int maxfer, resid, result, retry, x;
298190146Snyan	caddr_t bbuf, p, xp;
299158559Snyan	int biosdev;
300158559Snyan#ifdef DISK_DEBUG
301158559Snyan	int error;
302158559Snyan#endif
303158559Snyan
304158559Snyan	/* Just in case some idiot actually tries to read -1 blocks... */
305158559Snyan	if (blks < 0)
306158559Snyan		return (-1);
307158559Snyan
308158559Snyan	/* If nothing to do, just return succcess. */
309158559Snyan	if (blks == 0)
310158559Snyan		return (0);
311158559Snyan
312190146Snyan	/* Decide whether we have to bounce */
313190146Snyan	if (VTOP(dest) >> 20 != 0) {
314190146Snyan		/*
315190146Snyan		 * The destination buffer is above first 1MB of
316190146Snyan		 * physical memory so we have to arrange a suitable
317190146Snyan		 * bounce buffer.
318190146Snyan		 */
319190146Snyan		x = min(CD_BOUNCEBUF, (unsigned)blks);
320190146Snyan		bbuf = alloca(x * BIOSCD_SECSIZE);
321190146Snyan		maxfer = x;
322190146Snyan	} else {
323190146Snyan		bbuf = NULL;
324190146Snyan		maxfer = 0;
325190146Snyan	}
326190146Snyan
327158559Snyan	biosdev = bc_unit2bios(unit);
328190146Snyan	resid = blks;
329190146Snyan	p = dest;
330190146Snyan
331190146Snyan	while (resid > 0) {
332190146Snyan		if (bbuf)
333190146Snyan			xp = bbuf;
334190146Snyan		else
335190146Snyan			xp = p;
336190146Snyan		x = resid;
337190146Snyan		if (maxfer > 0)
338190146Snyan			x = min(x, maxfer);
339190146Snyan
340190146Snyan		/*
341190146Snyan		 * Loop retrying the operation a couple of times.  The BIOS
342190146Snyan		 * may also retry.
343190146Snyan		 */
344190146Snyan		for (retry = 0; retry < 3; retry++) {
345190146Snyan			/* If retrying, reset the drive */
346190146Snyan			if (retry > 0) {
347190146Snyan				v86.ctl = V86_FLAGS;
348190146Snyan				v86.addr = 0x1b;
349190146Snyan				v86.eax = 0x0300 | biosdev;
350190146Snyan				v86int();
351190146Snyan			}
352190146Snyan
353158559Snyan			v86.ctl = V86_FLAGS;
354158559Snyan			v86.addr = 0x1b;
355190146Snyan			v86.eax = 0x0600 | (biosdev & 0x7f);
356190147Snyan			v86.ebx = x * BIOSCD_SECSIZE;
357190146Snyan			v86.ecx = dblk & 0xffff;
358190146Snyan			v86.edx = (dblk >> 16) & 0xffff;
359190147Snyan			v86.ebp = VTOPOFF(xp);
360190147Snyan			v86.es = VTOPSEG(xp);
361158559Snyan			v86int();
362226746Sjhb			result = V86_CY(v86.efl);
363190146Snyan			if (result == 0)
364190146Snyan				break;
365158559Snyan		}
366158559Snyan
367158559Snyan#ifdef DISK_DEBUG
368190146Snyan		error = (v86.eax >> 8) & 0xff;
369158559Snyan#endif
370190146Snyan		DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p,
371190146Snyan		    VTOP(p), result ? "failed" : "ok");
372190146Snyan		DEBUG("unit %d  status 0x%x", unit, error);
373190146Snyan		if (bbuf != NULL)
374190146Snyan			bcopy(bbuf, p, x * BIOSCD_SECSIZE);
375190146Snyan		p += (x * BIOSCD_SECSIZE);
376190146Snyan		dblk += x;
377190146Snyan		resid -= x;
378190146Snyan	}
379158559Snyan
380158559Snyan/*	hexdump(dest, (blks * BIOSCD_SECSIZE)); */
381158559Snyan	return(0);
382158559Snyan}
383158559Snyan
384158559Snyan/*
385158559Snyan * Return a suitable dev_t value for (dev).
386158559Snyan */
387158559Snyanint
388158559Snyanbc_getdev(struct i386_devdesc *dev)
389158559Snyan{
390158559Snyan    int biosdev, unit, device;
391158559Snyan    int major;
392158559Snyan    int rootdev;
393158559Snyan
394163897Smarcel    unit = dev->d_unit;
395158559Snyan    biosdev = bc_unit2bios(unit);
396158559Snyan    DEBUG("unit %d BIOS device %d", unit, biosdev);
397158559Snyan    if (biosdev == -1)				/* not a BIOS device */
398158559Snyan	return(-1);
399158559Snyan
400158559Snyan    device = biosdev & 0xf0;
401158559Snyan    if (device == 0x80)
402158559Snyan	major = ACDMAJOR;
403158559Snyan    else if (device == 0xa0)
404158559Snyan	major = CDMAJOR;
405158559Snyan    else
406158559Snyan	return (-1);
407158559Snyan
408158559Snyan    unit = 0;	/* XXX */
409158559Snyan
410158559Snyan    /* XXX: Assume partition 'a'. */
411172921Sjhb    rootdev = MAKEBOOTDEV(major, 0, unit, 0);
412158559Snyan    DEBUG("dev is 0x%x\n", rootdev);
413158559Snyan    return(rootdev);
414158559Snyan}
415