bioscd.c revision 332154
1/*-
2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * Copyright (c) 2001 John H. Baldwin <jhb@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/11/stand/pc98/libpc98/bioscd.c 332154 2018-04-06 21:37:25Z kevans $");
30
31/*
32 * BIOS CD device handling for CD's that have been booted off of via no
33 * emulation booting as defined in the El Torito standard.
34 *
35 * Ideas and algorithms from:
36 *
37 * - FreeBSD libi386/biosdisk.c
38 *
39 */
40
41#include <stand.h>
42
43#include <sys/param.h>
44#include <machine/bootinfo.h>
45
46#include <stdarg.h>
47
48#include <bootstrap.h>
49#include <btxv86.h>
50#include "libi386.h"
51
52#define BIOSCD_SECSIZE		2048
53#define BUFSIZE			(1 * BIOSCD_SECSIZE)
54#define	MAXBCDEV		1
55
56/* Major numbers for devices we frontend for. */
57#define ACDMAJOR		117
58#define	CDMAJOR			15
59
60#ifdef DISK_DEBUG
61# define DEBUG(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
62#else
63# define DEBUG(fmt, args...)
64#endif
65
66struct specification_packet {
67	u_char	sp_size;
68	u_char	sp_bootmedia;
69	u_char	sp_drive;
70	u_char	sp_controller;
71	u_int	sp_lba;
72	u_short	sp_devicespec;
73	u_short	sp_buffersegment;
74	u_short	sp_loadsegment;
75	u_short	sp_sectorcount;
76	u_short	sp_cylsec;
77	u_char	sp_head;
78};
79
80/*
81 * List of BIOS devices, translation from disk unit number to
82 * BIOS unit number.
83 */
84static struct bcinfo {
85	int	bc_unit;		/* BIOS unit number */
86	struct specification_packet bc_sp;
87	int	bc_open;		/* reference counter */
88	void	*bc_bcache;		/* buffer cache data */
89} bcinfo [MAXBCDEV];
90static int nbcinfo = 0;
91
92#define	BC(dev)	(bcinfo[(dev)->dd.d_unit])
93
94static int	bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
95static int	bc_init(void);
96static int	bc_strategy(void *devdata, int flag, daddr_t dblk,
97		    size_t size, char *buf, size_t *rsize);
98static int	bc_realstrategy(void *devdata, int flag, daddr_t dblk,
99		    size_t size, char *buf, size_t *rsize);
100static int	bc_open(struct open_file *f, ...);
101static int	bc_close(struct open_file *f);
102static int	bc_print(int verbose);
103
104struct devsw bioscd = {
105	"cd",
106	DEVT_CD,
107	bc_init,
108	bc_strategy,
109	bc_open,
110	bc_close,
111	noioctl,
112	bc_print,
113	NULL
114};
115
116/*
117 * Translate between BIOS device numbers and our private unit numbers.
118 */
119int
120bc_bios2unit(int biosdev)
121{
122	int i;
123
124	DEBUG("looking for bios device 0x%x", biosdev);
125	for (i = 0; i < nbcinfo; i++) {
126		DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
127		if (bcinfo[i].bc_unit == biosdev)
128			return(i);
129	}
130	return(-1);
131}
132
133int
134bc_unit2bios(int unit)
135{
136	if ((unit >= 0) && (unit < nbcinfo))
137		return(bcinfo[unit].bc_unit);
138	return(-1);
139}
140
141/*
142 * We can't quiz, we have to be told what device to use, so this functoin
143 * doesn't do anything.  Instead, the loader calls bc_add() with the BIOS
144 * device number to add.
145 */
146static int
147bc_init(void)
148{
149
150	return (0);
151}
152
153int
154bc_add(int biosdev)
155{
156
157	if (nbcinfo >= MAXBCDEV)
158		return (-1);
159	bcinfo[nbcinfo].bc_unit = biosdev;
160
161	/* SCSI CD-ROM only */
162	if ((biosdev & 0xf0) != 0xa0)
163		return (-1);
164	if ((((uint32_t *)PTOV(0xA1460))[biosdev & 0x0f] & 0x1f) != 5)
165		return (-1);
166
167	printf("BIOS CD is cd%d\n", nbcinfo);
168	nbcinfo++;
169	bcache_add_dev(nbcinfo);	/* register cd device in bcache */
170	return(0);
171}
172
173/*
174 * Print information about disks
175 */
176static int
177bc_print(int verbose)
178{
179	char line[80];
180	int i, ret = 0;
181
182	if (nbcinfo == 0)
183		return (0);
184
185	printf("%s devices:", bioscd.dv_name);
186	if ((ret = pager_output("\n")) != 0)
187		return (ret);
188
189	for (i = 0; i < nbcinfo; i++) {
190		sprintf(line, "    cd%d: Device 0x%x\n", i,
191		    bcinfo[i].bc_sp.sp_devicespec);
192		if ((ret = pager_output(line)) != 0)
193			break;
194	}
195	return (ret);
196}
197
198/*
199 * Attempt to open the disk described by (dev) for use by (f).
200 */
201static int
202bc_open(struct open_file *f, ...)
203{
204	va_list ap;
205	struct i386_devdesc *dev;
206
207	va_start(ap, f);
208	dev = va_arg(ap, struct i386_devdesc *);
209	va_end(ap);
210	if (dev->dd.d_unit >= nbcinfo) {
211		DEBUG("attempt to open nonexistent disk");
212		return(ENXIO);
213	}
214
215	BC(dev).bc_open++;
216	if (BC(dev).bc_bcache == NULL)
217		BC(dev).bc_bcache = bcache_allocate();
218	return(0);
219}
220
221static int
222bc_close(struct open_file *f)
223{
224	struct i386_devdesc *dev;
225
226	dev = (struct i386_devdesc *)f->f_devdata;
227	BC(dev).bc_open--;
228	if (BC(dev).bc_open == 0) {
229		bcache_free(BC(dev).bc_bcache);
230		BC(dev).bc_bcache = NULL;
231	}
232	return(0);
233}
234
235static int
236bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
237    char *buf, size_t *rsize)
238{
239	struct bcache_devdata bcd;
240	struct i386_devdesc *dev;
241
242	dev = (struct i386_devdesc *)devdata;
243	bcd.dv_strategy = bc_realstrategy;
244	bcd.dv_devdata = devdata;
245	bcd.dv_cache = BC(dev).bc_bcache;
246
247	return (bcache_strategy(&bcd, rw, dblk, size, buf, rsize));
248}
249
250static int
251bc_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
252    char *buf, size_t *rsize)
253{
254	struct i386_devdesc *dev;
255	int unit;
256	int blks;
257#ifdef BD_SUPPORT_FRAGS
258	char fragbuf[BIOSCD_SECSIZE];
259	size_t fragsize;
260
261	fragsize = size % BIOSCD_SECSIZE;
262#else
263	if (size % BIOSCD_SECSIZE)
264		return (EINVAL);
265#endif
266
267	if (rw != F_READ)
268		return(EROFS);
269	dev = (struct i386_devdesc *)devdata;
270	unit = dev->dd.d_unit;
271	blks = size / BIOSCD_SECSIZE;
272	if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
273		return (EINVAL);
274	dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
275	DEBUG("read %d from %lld to %p", blks, dblk, buf);
276
277	if (rsize)
278		*rsize = 0;
279	if (blks && bc_read(unit, dblk, blks, buf)) {
280		DEBUG("read error");
281		return (EIO);
282	}
283#ifdef BD_SUPPORT_FRAGS
284	DEBUG("frag read %d from %lld+%d to %p",
285	    fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
286	if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) {
287		DEBUG("frag read error");
288		return(EIO);
289	}
290	bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
291#endif
292	if (rsize)
293		*rsize = size;
294	return (0);
295}
296
297/* Max number of sectors to bounce-buffer at a time. */
298#define	CD_BOUNCEBUF	8
299
300static int
301bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
302{
303	u_int maxfer, resid, result, retry, x;
304	caddr_t bbuf, p, xp;
305	int biosdev;
306#ifdef DISK_DEBUG
307	int error;
308#endif
309
310	/* Just in case some idiot actually tries to read -1 blocks... */
311	if (blks < 0)
312		return (-1);
313
314	/* If nothing to do, just return succcess. */
315	if (blks == 0)
316		return (0);
317
318	/* Decide whether we have to bounce */
319	if (VTOP(dest) >> 20 != 0) {
320		/*
321		 * The destination buffer is above first 1MB of
322		 * physical memory so we have to arrange a suitable
323		 * bounce buffer.
324		 */
325		x = min(CD_BOUNCEBUF, (unsigned)blks);
326		bbuf = alloca(x * BIOSCD_SECSIZE);
327		maxfer = x;
328	} else {
329		bbuf = NULL;
330		maxfer = 0;
331	}
332
333	biosdev = bc_unit2bios(unit);
334	resid = blks;
335	p = dest;
336
337	while (resid > 0) {
338		if (bbuf)
339			xp = bbuf;
340		else
341			xp = p;
342		x = resid;
343		if (maxfer > 0)
344			x = min(x, maxfer);
345
346		/*
347		 * Loop retrying the operation a couple of times.  The BIOS
348		 * may also retry.
349		 */
350		for (retry = 0; retry < 3; retry++) {
351			/* If retrying, reset the drive */
352			if (retry > 0) {
353				v86.ctl = V86_FLAGS;
354				v86.addr = 0x1b;
355				v86.eax = 0x0300 | biosdev;
356				v86int();
357			}
358
359			v86.ctl = V86_FLAGS;
360			v86.addr = 0x1b;
361			v86.eax = 0x0600 | (biosdev & 0x7f);
362			v86.ebx = x * BIOSCD_SECSIZE;
363			v86.ecx = dblk & 0xffff;
364			v86.edx = (dblk >> 16) & 0xffff;
365			v86.ebp = VTOPOFF(xp);
366			v86.es = VTOPSEG(xp);
367			v86int();
368			result = V86_CY(v86.efl);
369			if (result == 0)
370				break;
371		}
372
373#ifdef DISK_DEBUG
374		error = (v86.eax >> 8) & 0xff;
375#endif
376		DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p,
377		    VTOP(p), result ? "failed" : "ok");
378		DEBUG("unit %d  status 0x%x", unit, error);
379		if (bbuf != NULL)
380			bcopy(bbuf, p, x * BIOSCD_SECSIZE);
381		p += (x * BIOSCD_SECSIZE);
382		dblk += x;
383		resid -= x;
384	}
385
386/*	hexdump(dest, (blks * BIOSCD_SECSIZE)); */
387	return(0);
388}
389
390/*
391 * Return a suitable dev_t value for (dev).
392 */
393int
394bc_getdev(struct i386_devdesc *dev)
395{
396    int biosdev, unit, device;
397    int major;
398    int rootdev;
399
400    unit = dev->dd.d_unit;
401    biosdev = bc_unit2bios(unit);
402    DEBUG("unit %d BIOS device %d", unit, biosdev);
403    if (biosdev == -1)				/* not a BIOS device */
404	return(-1);
405
406    device = biosdev & 0xf0;
407    if (device == 0x80)
408	major = ACDMAJOR;
409    else if (device == 0xa0)
410	major = CDMAJOR;
411    else
412	return (-1);
413
414    unit = 0;	/* XXX */
415
416    /* XXX: Assume partition 'a'. */
417    rootdev = MAKEBOOTDEV(major, 0, unit, 0);
418    DEBUG("dev is 0x%x\n", rootdev);
419    return(rootdev);
420}
421