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$");
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 <edd.h>
51#include "libi386.h"
52
53#define BIOSCD_SECSIZE		2048
54#define BUFSIZE			(1 * BIOSCD_SECSIZE)
55#define	MAXBCDEV		1
56
57/* Major numbers for devices we frontend for. */
58#define ACDMAJOR		117
59#define	CDMAJOR			15
60
61#ifdef DISK_DEBUG
62# define DEBUG(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
63#else
64# define DEBUG(fmt, args...)
65#endif
66
67struct specification_packet {
68	u_char	sp_size;
69	u_char	sp_bootmedia;
70	u_char	sp_drive;
71	u_char	sp_controller;
72	u_int	sp_lba;
73	u_short	sp_devicespec;
74	u_short	sp_buffersegment;
75	u_short	sp_loadsegment;
76	u_short	sp_sectorcount;
77	u_short	sp_cylsec;
78	u_char	sp_head;
79};
80
81/*
82 * List of BIOS devices, translation from disk unit number to
83 * BIOS unit number.
84 */
85static struct bcinfo {
86	int	bc_unit;		/* BIOS unit number */
87	struct specification_packet bc_sp;
88} bcinfo [MAXBCDEV];
89static int nbcinfo = 0;
90
91static int	bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
92static int	bc_init(void);
93static int	bc_strategy(void *devdata, int flag, daddr_t dblk,
94		    size_t size, char *buf, size_t *rsize);
95static int	bc_open(struct open_file *f, ...);
96static int	bc_close(struct open_file *f);
97static void	bc_print(int verbose);
98
99struct devsw bioscd = {
100	"cd",
101	DEVT_CD,
102	bc_init,
103	bc_strategy,
104	bc_open,
105	bc_close,
106	noioctl,
107	bc_print,
108	NULL
109};
110
111/*
112 * Translate between BIOS device numbers and our private unit numbers.
113 */
114int
115bc_bios2unit(int biosdev)
116{
117	int i;
118
119	DEBUG("looking for bios device 0x%x", biosdev);
120	for (i = 0; i < nbcinfo; i++) {
121		DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
122		if (bcinfo[i].bc_unit == biosdev)
123			return(i);
124	}
125	return(-1);
126}
127
128int
129bc_unit2bios(int unit)
130{
131	if ((unit >= 0) && (unit < nbcinfo))
132		return(bcinfo[unit].bc_unit);
133	return(-1);
134}
135
136/*
137 * We can't quiz, we have to be told what device to use, so this functoin
138 * doesn't do anything.  Instead, the loader calls bc_add() with the BIOS
139 * device number to add.
140 */
141static int
142bc_init(void)
143{
144
145	return (0);
146}
147
148int
149bc_add(int biosdev)
150{
151
152	if (nbcinfo >= MAXBCDEV)
153		return (-1);
154	bcinfo[nbcinfo].bc_unit = biosdev;
155	v86.ctl = V86_FLAGS;
156	v86.addr = 0x13;
157	v86.eax = 0x4b01;
158	v86.edx = biosdev;
159	v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
160	v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
161	v86int();
162	if ((v86.eax & 0xff00) != 0)
163		return (-1);
164
165	printf("BIOS CD is cd%d\n", nbcinfo);
166	nbcinfo++;
167	return(0);
168}
169
170/*
171 * Print information about disks
172 */
173static void
174bc_print(int verbose)
175{
176	char line[80];
177	int i;
178
179	for (i = 0; i < nbcinfo; i++) {
180		sprintf(line, "    cd%d: Device 0x%x\n", i,
181		    bcinfo[i].bc_sp.sp_devicespec);
182		pager_output(line);
183	}
184}
185
186/*
187 * Attempt to open the disk described by (dev) for use by (f).
188 */
189static int
190bc_open(struct open_file *f, ...)
191{
192	va_list ap;
193	struct i386_devdesc *dev;
194
195	va_start(ap, f);
196	dev = va_arg(ap, struct i386_devdesc *);
197	va_end(ap);
198	if (dev->d_unit >= nbcinfo) {
199		DEBUG("attempt to open nonexistent disk");
200		return(ENXIO);
201	}
202
203	return(0);
204}
205
206static int
207bc_close(struct open_file *f)
208{
209
210	return(0);
211}
212
213static int
214bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
215    size_t *rsize)
216{
217	struct i386_devdesc *dev;
218	int unit;
219	int blks;
220#ifdef BD_SUPPORT_FRAGS
221	char fragbuf[BIOSCD_SECSIZE];
222	size_t fragsize;
223
224	fragsize = size % BIOSCD_SECSIZE;
225#else
226	if (size % BIOSCD_SECSIZE)
227		return (EINVAL);
228#endif
229
230	if (rw != F_READ)
231		return(EROFS);
232	dev = (struct i386_devdesc *)devdata;
233	unit = dev->d_unit;
234	blks = size / BIOSCD_SECSIZE;
235	if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
236		return (EINVAL);
237	dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
238	DEBUG("read %d from %lld to %p", blks, dblk, buf);
239
240	if (rsize)
241		*rsize = 0;
242	if (blks && bc_read(unit, dblk, blks, buf)) {
243		DEBUG("read error");
244		return (EIO);
245	}
246#ifdef BD_SUPPORT_FRAGS
247	DEBUG("frag read %d from %lld+%d to %p",
248	    fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
249	if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) {
250		DEBUG("frag read error");
251		return(EIO);
252	}
253	bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
254#endif
255	if (rsize)
256		*rsize = size;
257	return (0);
258}
259
260/* Max number of sectors to bounce-buffer at a time. */
261#define	CD_BOUNCEBUF	8
262
263static int
264bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
265{
266	u_int maxfer, resid, result, retry, x;
267	caddr_t bbuf, p, xp;
268	static struct edd_packet packet;
269	int biosdev;
270#ifdef DISK_DEBUG
271	int error;
272#endif
273
274	/* Just in case some idiot actually tries to read -1 blocks... */
275	if (blks < 0)
276		return (-1);
277
278	/* If nothing to do, just return succcess. */
279	if (blks == 0)
280		return (0);
281
282	/* Decide whether we have to bounce */
283	if (VTOP(dest) >> 20 != 0) {
284		/*
285		 * The destination buffer is above first 1MB of
286		 * physical memory so we have to arrange a suitable
287		 * bounce buffer.
288		 */
289		x = min(CD_BOUNCEBUF, (unsigned)blks);
290		bbuf = alloca(x * BIOSCD_SECSIZE);
291		maxfer = x;
292	} else {
293		bbuf = NULL;
294		maxfer = 0;
295	}
296
297	biosdev = bc_unit2bios(unit);
298	resid = blks;
299	p = dest;
300
301	while (resid > 0) {
302		if (bbuf)
303			xp = bbuf;
304		else
305			xp = p;
306		x = resid;
307		if (maxfer > 0)
308			x = min(x, maxfer);
309
310		/*
311		 * Loop retrying the operation a couple of times.  The BIOS
312		 * may also retry.
313		 */
314		for (retry = 0; retry < 3; retry++) {
315			/* If retrying, reset the drive */
316			if (retry > 0) {
317				v86.ctl = V86_FLAGS;
318				v86.addr = 0x13;
319				v86.eax = 0;
320				v86.edx = biosdev;
321				v86int();
322			}
323
324			packet.len = sizeof(struct edd_packet);
325			packet.count = x;
326			packet.off = VTOPOFF(xp);
327			packet.seg = VTOPSEG(xp);
328			packet.lba = dblk;
329			v86.ctl = V86_FLAGS;
330			v86.addr = 0x13;
331			v86.eax = 0x4200;
332			v86.edx = biosdev;
333			v86.ds = VTOPSEG(&packet);
334			v86.esi = VTOPOFF(&packet);
335			v86int();
336			result = V86_CY(v86.efl);
337			if (result == 0)
338				break;
339		}
340
341#ifdef DISK_DEBUG
342		error = (v86.eax >> 8) & 0xff;
343#endif
344		DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p,
345		    VTOP(p), result ? "failed" : "ok");
346		DEBUG("unit %d  status 0x%x", unit, error);
347		if (bbuf != NULL)
348			bcopy(bbuf, p, x * BIOSCD_SECSIZE);
349		p += (x * BIOSCD_SECSIZE);
350		dblk += x;
351		resid -= x;
352	}
353
354/*	hexdump(dest, (blks * BIOSCD_SECSIZE)); */
355	return(0);
356}
357
358/*
359 * Return a suitable dev_t value for (dev).
360 */
361int
362bc_getdev(struct i386_devdesc *dev)
363{
364    int biosdev, unit;
365    int major;
366    int rootdev;
367
368    unit = dev->d_unit;
369    biosdev = bc_unit2bios(unit);
370    DEBUG("unit %d BIOS device %d", unit, biosdev);
371    if (biosdev == -1)				/* not a BIOS device */
372	return(-1);
373
374    /*
375     * XXX: Need to examine device spec here to figure out if SCSI or
376     * ATAPI.  No idea on how to figure out device number.  All we can
377     * really pass to the kernel is what bus and device on which bus we
378     * were booted from, which dev_t isn't well suited to since those
379     * number don't match to unit numbers very well.  We may just need
380     * to engage in a hack where we pass -C to the boot args if we are
381     * the boot device.
382     */
383    major = ACDMAJOR;
384    unit = 0;	/* XXX */
385
386    /* XXX: Assume partition 'a'. */
387    rootdev = MAKEBOOTDEV(major, 0, unit, 0);
388    DEBUG("dev is 0x%x\n", rootdev);
389    return(rootdev);
390}
391