bioscd.c revision 226746
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: head/sys/boot/pc98/libpc98/bioscd.c 226746 2011-10-25 19:45:12Z jhb $");
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} bcinfo [MAXBCDEV];
88static int nbcinfo = 0;
89
90static int	bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
91static int	bc_init(void);
92static int	bc_strategy(void *devdata, int flag, daddr_t dblk,
93		    size_t size, char *buf, size_t *rsize);
94static int	bc_open(struct open_file *f, ...);
95static int	bc_close(struct open_file *f);
96static void	bc_print(int verbose);
97
98struct devsw bioscd = {
99	"cd",
100	DEVT_CD,
101	bc_init,
102	bc_strategy,
103	bc_open,
104	bc_close,
105	noioctl,
106	bc_print,
107	NULL
108};
109
110/*
111 * Translate between BIOS device numbers and our private unit numbers.
112 */
113int
114bc_bios2unit(int biosdev)
115{
116	int i;
117
118	DEBUG("looking for bios device 0x%x", biosdev);
119	for (i = 0; i < nbcinfo; i++) {
120		DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
121		if (bcinfo[i].bc_unit == biosdev)
122			return(i);
123	}
124	return(-1);
125}
126
127int
128bc_unit2bios(int unit)
129{
130	if ((unit >= 0) && (unit < nbcinfo))
131		return(bcinfo[unit].bc_unit);
132	return(-1);
133}
134
135/*
136 * We can't quiz, we have to be told what device to use, so this functoin
137 * doesn't do anything.  Instead, the loader calls bc_add() with the BIOS
138 * device number to add.
139 */
140static int
141bc_init(void)
142{
143
144	return (0);
145}
146
147int
148bc_add(int biosdev)
149{
150
151	if (nbcinfo >= MAXBCDEV)
152		return (-1);
153	bcinfo[nbcinfo].bc_unit = biosdev;
154
155	/* SCSI CD-ROM only */
156	if ((biosdev & 0xf0) != 0xa0)
157		return (-1);
158	if ((((uint32_t *)PTOV(0xA1460))[biosdev & 0x0f] & 0x1f) != 5)
159		return (-1);
160
161	printf("BIOS CD is cd%d\n", nbcinfo);
162	nbcinfo++;
163	return(0);
164}
165
166/*
167 * Print information about disks
168 */
169static void
170bc_print(int verbose)
171{
172	char line[80];
173	int i;
174
175	for (i = 0; i < nbcinfo; i++) {
176		sprintf(line, "    cd%d: Device 0x%x\n", i,
177		    bcinfo[i].bc_sp.sp_devicespec);
178		pager_output(line);
179	}
180}
181
182/*
183 * Attempt to open the disk described by (dev) for use by (f).
184 */
185static int
186bc_open(struct open_file *f, ...)
187{
188	va_list ap;
189	struct i386_devdesc *dev;
190
191	va_start(ap, f);
192	dev = va_arg(ap, struct i386_devdesc *);
193	va_end(ap);
194	if (dev->d_unit >= nbcinfo) {
195		DEBUG("attempt to open nonexistent disk");
196		return(ENXIO);
197	}
198
199	return(0);
200}
201
202static int
203bc_close(struct open_file *f)
204{
205
206	return(0);
207}
208
209static int
210bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
211    size_t *rsize)
212{
213	struct i386_devdesc *dev;
214	int unit;
215	int blks;
216#ifdef BD_SUPPORT_FRAGS
217	char fragbuf[BIOSCD_SECSIZE];
218	size_t fragsize;
219
220	fragsize = size % BIOSCD_SECSIZE;
221#else
222	if (size % BIOSCD_SECSIZE)
223		return (EINVAL);
224#endif
225
226	if (rw != F_READ)
227		return(EROFS);
228	dev = (struct i386_devdesc *)devdata;
229	unit = dev->d_unit;
230	blks = size / BIOSCD_SECSIZE;
231	if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
232		return (EINVAL);
233	dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
234	DEBUG("read %d from %lld to %p", blks, dblk, buf);
235
236	if (rsize)
237		*rsize = 0;
238	if (blks && bc_read(unit, dblk, blks, buf)) {
239		DEBUG("read error");
240		return (EIO);
241	}
242#ifdef BD_SUPPORT_FRAGS
243	DEBUG("frag read %d from %lld+%d to %p",
244	    fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
245	if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) {
246		DEBUG("frag read error");
247		return(EIO);
248	}
249	bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
250#endif
251	if (rsize)
252		*rsize = size;
253	return (0);
254}
255
256/* Max number of sectors to bounce-buffer at a time. */
257#define	CD_BOUNCEBUF	8
258
259static int
260bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
261{
262	u_int maxfer, resid, result, retry, x;
263	caddr_t bbuf, p, xp;
264	int biosdev;
265#ifdef DISK_DEBUG
266	int error;
267#endif
268
269	/* Just in case some idiot actually tries to read -1 blocks... */
270	if (blks < 0)
271		return (-1);
272
273	/* If nothing to do, just return succcess. */
274	if (blks == 0)
275		return (0);
276
277	/* Decide whether we have to bounce */
278	if (VTOP(dest) >> 20 != 0) {
279		/*
280		 * The destination buffer is above first 1MB of
281		 * physical memory so we have to arrange a suitable
282		 * bounce buffer.
283		 */
284		x = min(CD_BOUNCEBUF, (unsigned)blks);
285		bbuf = alloca(x * BIOSCD_SECSIZE);
286		maxfer = x;
287	} else {
288		bbuf = NULL;
289		maxfer = 0;
290	}
291
292	biosdev = bc_unit2bios(unit);
293	resid = blks;
294	p = dest;
295
296	while (resid > 0) {
297		if (bbuf)
298			xp = bbuf;
299		else
300			xp = p;
301		x = resid;
302		if (maxfer > 0)
303			x = min(x, maxfer);
304
305		/*
306		 * Loop retrying the operation a couple of times.  The BIOS
307		 * may also retry.
308		 */
309		for (retry = 0; retry < 3; retry++) {
310			/* If retrying, reset the drive */
311			if (retry > 0) {
312				v86.ctl = V86_FLAGS;
313				v86.addr = 0x1b;
314				v86.eax = 0x0300 | biosdev;
315				v86int();
316			}
317
318			v86.ctl = V86_FLAGS;
319			v86.addr = 0x1b;
320			v86.eax = 0x0600 | (biosdev & 0x7f);
321			v86.ebx = x * BIOSCD_SECSIZE;
322			v86.ecx = dblk & 0xffff;
323			v86.edx = (dblk >> 16) & 0xffff;
324			v86.ebp = VTOPOFF(xp);
325			v86.es = VTOPSEG(xp);
326			v86int();
327			result = V86_CY(v86.efl);
328			if (result == 0)
329				break;
330		}
331
332#ifdef DISK_DEBUG
333		error = (v86.eax >> 8) & 0xff;
334#endif
335		DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p,
336		    VTOP(p), result ? "failed" : "ok");
337		DEBUG("unit %d  status 0x%x", unit, error);
338		if (bbuf != NULL)
339			bcopy(bbuf, p, x * BIOSCD_SECSIZE);
340		p += (x * BIOSCD_SECSIZE);
341		dblk += x;
342		resid -= x;
343	}
344
345/*	hexdump(dest, (blks * BIOSCD_SECSIZE)); */
346	return(0);
347}
348
349/*
350 * Return a suitable dev_t value for (dev).
351 */
352int
353bc_getdev(struct i386_devdesc *dev)
354{
355    int biosdev, unit, device;
356    int major;
357    int rootdev;
358
359    unit = dev->d_unit;
360    biosdev = bc_unit2bios(unit);
361    DEBUG("unit %d BIOS device %d", unit, biosdev);
362    if (biosdev == -1)				/* not a BIOS device */
363	return(-1);
364
365    device = biosdev & 0xf0;
366    if (device == 0x80)
367	major = ACDMAJOR;
368    else if (device == 0xa0)
369	major = CDMAJOR;
370    else
371	return (-1);
372
373    unit = 0;	/* XXX */
374
375    /* XXX: Assume partition 'a'. */
376    rootdev = MAKEBOOTDEV(major, 0, unit, 0);
377    DEBUG("dev is 0x%x\n", rootdev);
378    return(rootdev);
379}
380