bioscd.c revision 130603
1254885Sdumbbell/*-
2254885Sdumbbell * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3254885Sdumbbell * Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org>
4254885Sdumbbell * All rights reserved.
5254885Sdumbbell *
6254885Sdumbbell * Redistribution and use in source and binary forms, with or without
7254885Sdumbbell * modification, are permitted provided that the following conditions
8254885Sdumbbell * are met:
9254885Sdumbbell * 1. Redistributions of source code must retain the above copyright
10254885Sdumbbell *    notice, this list of conditions and the following disclaimer.
11254885Sdumbbell * 2. Redistributions in binary form must reproduce the above copyright
12254885Sdumbbell *    notice, this list of conditions and the following disclaimer in the
13254885Sdumbbell *    documentation and/or other materials provided with the distribution.
14254885Sdumbbell *
15254885Sdumbbell * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16254885Sdumbbell * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17254885Sdumbbell * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18254885Sdumbbell * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19254885Sdumbbell * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20254885Sdumbbell * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21254885Sdumbbell * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22254885Sdumbbell * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23254885Sdumbbell * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24254885Sdumbbell * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25254885Sdumbbell * SUCH DAMAGE.
26254885Sdumbbell */
27254885Sdumbbell
28254885Sdumbbell#include <sys/cdefs.h>
29254885Sdumbbell__FBSDID("$FreeBSD: head/sys/boot/i386/libi386/bioscd.c 130603 2004-06-16 18:21:22Z phk $");
30254885Sdumbbell
31254885Sdumbbell/*
32254885Sdumbbell * BIOS CD device handling for CD's that have been booted off of via no
33254885Sdumbbell * emulation booting as defined in the El Torito standard.
34254885Sdumbbell *
35254885Sdumbbell * Ideas and algorithms from:
36254885Sdumbbell *
37254885Sdumbbell * - FreeBSD libi386/biosdisk.c
38254885Sdumbbell *
39254885Sdumbbell */
40254885Sdumbbell
41254885Sdumbbell#include <stand.h>
42254885Sdumbbell
43254885Sdumbbell#include <sys/param.h>
44263817Sray#include <machine/bootinfo.h>
45254885Sdumbbell#include <machine/psl.h>
46254885Sdumbbell
47254885Sdumbbell#include <stdarg.h>
48254885Sdumbbell
49254885Sdumbbell#include <bootstrap.h>
50254885Sdumbbell#include <btxv86.h>
51254885Sdumbbell#include "libi386.h"
52254885Sdumbbell
53254885Sdumbbell#define BIOSCD_SECSIZE		2048
54254885Sdumbbell#define BUFSIZE			(1 * BIOSCD_SECSIZE)
55254885Sdumbbell#define	MAXBCDEV		1
56254885Sdumbbell
57254885Sdumbbell/* Major numbers for devices we frontend for. */
58254885Sdumbbell#define ACDMAJOR		117
59254885Sdumbbell#define	CDMAJOR			15
60254885Sdumbbell
61254885Sdumbbell#ifdef DISK_DEBUG
62254885Sdumbbell# define DEBUG(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
63254885Sdumbbell#else
64254885Sdumbbell# define DEBUG(fmt, args...)
65254885Sdumbbell#endif
66254885Sdumbbell
67254885Sdumbbellstruct specification_packet {
68254885Sdumbbell	u_char	sp_size;
69254885Sdumbbell	u_char	sp_bootmedia;
70254885Sdumbbell	u_char	sp_drive;
71254885Sdumbbell	u_char	sp_controller;
72254885Sdumbbell	u_int	sp_lba;
73254885Sdumbbell	u_short	sp_devicespec;
74254885Sdumbbell	u_short	sp_buffersegment;
75254885Sdumbbell	u_short	sp_loadsegment;
76254885Sdumbbell	u_short	sp_sectorcount;
77254885Sdumbbell	u_short	sp_cylsec;
78254885Sdumbbell	u_char	sp_head;
79254885Sdumbbell};
80254885Sdumbbell
81254885Sdumbbell/*
82254885Sdumbbell * List of BIOS devices, translation from disk unit number to
83254885Sdumbbell * BIOS unit number.
84254885Sdumbbell */
85254885Sdumbbellstatic struct bcinfo {
86254885Sdumbbell	int	bc_unit;		/* BIOS unit number */
87254885Sdumbbell	struct specification_packet bc_sp;
88267430Sdumbbell} bcinfo [MAXBCDEV];
89267430Sdumbbellstatic int nbcinfo = 0;
90267430Sdumbbell
91267430Sdumbbellstatic int	bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
92254885Sdumbbellstatic int	bc_init(void);
93254885Sdumbbellstatic int	bc_strategy(void *devdata, int flag, daddr_t dblk,
94254885Sdumbbell		    size_t size, char *buf, size_t *rsize);
95254885Sdumbbellstatic int	bc_realstrategy(void *devdata, int flag, daddr_t dblk,
96254885Sdumbbell		    size_t size, char *buf, size_t *rsize);
97254885Sdumbbellstatic int	bc_open(struct open_file *f, ...);
98254885Sdumbbellstatic int	bc_close(struct open_file *f);
99254885Sdumbbellstatic void	bc_print(int verbose);
100254885Sdumbbell
101254885Sdumbbellstruct devsw bioscd = {
102254885Sdumbbell	"cd",
103254885Sdumbbell	DEVT_CD,
104254885Sdumbbell	bc_init,
105254885Sdumbbell	bc_strategy,
106254885Sdumbbell	bc_open,
107254885Sdumbbell	bc_close,
108254885Sdumbbell	noioctl,
109254885Sdumbbell	bc_print,
110254885Sdumbbell	NULL
111254885Sdumbbell};
112254885Sdumbbell
113254885Sdumbbell/*
114254885Sdumbbell * Translate between BIOS device numbers and our private unit numbers.
115254885Sdumbbell */
116254885Sdumbbellint
117254885Sdumbbellbc_bios2unit(int biosdev)
118254885Sdumbbell{
119254885Sdumbbell	int i;
120254885Sdumbbell
121254885Sdumbbell	DEBUG("looking for bios device 0x%x", biosdev);
122254885Sdumbbell	for (i = 0; i < nbcinfo; i++) {
123254885Sdumbbell		DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
124254885Sdumbbell		if (bcinfo[i].bc_unit == biosdev)
125254885Sdumbbell			return(i);
126254885Sdumbbell	}
127254885Sdumbbell	return(-1);
128254885Sdumbbell}
129254885Sdumbbell
130254885Sdumbbellint
131254885Sdumbbellbc_unit2bios(int unit)
132254885Sdumbbell{
133254885Sdumbbell	if ((unit >= 0) && (unit < nbcinfo))
134254885Sdumbbell		return(bcinfo[unit].bc_unit);
135254885Sdumbbell	return(-1);
136254885Sdumbbell}
137254885Sdumbbell
138254885Sdumbbell/*
139254885Sdumbbell * We can't quiz, we have to be told what device to use, so this functoin
140254885Sdumbbell * doesn't do anything.  Instead, the loader calls bc_add() with the BIOS
141254885Sdumbbell * device number to add.
142254885Sdumbbell */
143254885Sdumbbellstatic int
144254885Sdumbbellbc_init(void)
145254885Sdumbbell{
146254885Sdumbbell
147254885Sdumbbell	return (0);
148254885Sdumbbell}
149254885Sdumbbell
150254885Sdumbbellint
151254885Sdumbbellbc_add(int biosdev)
152254885Sdumbbell{
153254885Sdumbbell
154254885Sdumbbell	if (nbcinfo >= MAXBCDEV)
155254885Sdumbbell		return (-1);
156254885Sdumbbell	bcinfo[nbcinfo].bc_unit = biosdev;
157254885Sdumbbell	v86.ctl = V86_FLAGS;
158254885Sdumbbell	v86.addr = 0x13;
159254885Sdumbbell	v86.eax = 0x4b01;
160254885Sdumbbell	v86.edx = biosdev;
161254885Sdumbbell	v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
162254885Sdumbbell	v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
163254885Sdumbbell	v86int();
164254885Sdumbbell	if ((v86.eax & 0xff00) != 0)
165254885Sdumbbell		return (-1);
166254885Sdumbbell
167254885Sdumbbell	printf("BIOS CD is cd%d\n", nbcinfo);
168254885Sdumbbell	nbcinfo++;
169254885Sdumbbell	return(0);
170254885Sdumbbell}
171254885Sdumbbell
172254885Sdumbbell/*
173254885Sdumbbell * Print information about disks
174254885Sdumbbell */
175254885Sdumbbellstatic void
176254885Sdumbbellbc_print(int verbose)
177254885Sdumbbell{
178254885Sdumbbell	int i;
179254885Sdumbbell	char line[80];
180254885Sdumbbell
181254885Sdumbbell	for (i = 0; i < nbcinfo; i++) {
182254885Sdumbbell		sprintf(line, "    cd%d: Device 0x%x\n", i,
183254885Sdumbbell		    bcinfo[i].bc_sp.sp_devicespec);
184254885Sdumbbell		pager_output(line);
185254885Sdumbbell	}
186254885Sdumbbell}
187254885Sdumbbell
188254885Sdumbbell/*
189254885Sdumbbell * Attempt to open the disk described by (dev) for use by (f).
190254885Sdumbbell */
191254885Sdumbbellstatic int
192254885Sdumbbellbc_open(struct open_file *f, ...)
193254885Sdumbbell{
194254885Sdumbbell	va_list ap;
195254885Sdumbbell	struct i386_devdesc *dev;
196254885Sdumbbell	int error;
197254885Sdumbbell
198254885Sdumbbell	va_start(ap, f);
199254885Sdumbbell	dev = va_arg(ap, struct i386_devdesc *);
200254885Sdumbbell	va_end(ap);
201254885Sdumbbell	if (dev->d_kind.bioscd.unit >= nbcinfo) {
202254885Sdumbbell		DEBUG("attempt to open nonexistent disk");
203254885Sdumbbell		return(ENXIO);
204254885Sdumbbell	}
205254885Sdumbbell
206254885Sdumbbell	return(0);
207254885Sdumbbell}
208254885Sdumbbell
209254885Sdumbbellstatic int
210254885Sdumbbellbc_close(struct open_file *f)
211254885Sdumbbell{
212254885Sdumbbell
213254885Sdumbbell	return(0);
214254885Sdumbbell}
215254885Sdumbbell
216254885Sdumbbellstatic int
217254885Sdumbbellbc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
218254885Sdumbbell    size_t *rsize)
219254885Sdumbbell{
220254885Sdumbbell	struct i386_devdesc *dev;
221254885Sdumbbell	int unit;
222254885Sdumbbell	int blks;
223254885Sdumbbell#ifdef BD_SUPPORT_FRAGS
224254885Sdumbbell	char fragbuf[BIOSCD_SECSIZE];
225254885Sdumbbell	size_t fragsize;
226254885Sdumbbell
227254885Sdumbbell	fragsize = size % BIOSCD_SECSIZE;
228254885Sdumbbell#else
229254885Sdumbbell	if (size % BIOSCD_SECSIZE)
230254885Sdumbbell		return (EINVAL);
231254885Sdumbbell#endif
232254885Sdumbbell
233254885Sdumbbell	if (rw != F_READ)
234254885Sdumbbell		return(EROFS);
235254885Sdumbbell	dev = (struct i386_devdesc *)devdata;
236254885Sdumbbell	unit = dev->d_kind.bioscd.unit;
237254885Sdumbbell	blks = size / BIOSCD_SECSIZE;
238254885Sdumbbell	if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
239254885Sdumbbell		return (EINVAL);
240254885Sdumbbell	dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
241254885Sdumbbell	DEBUG("read %d from %d to %p", blks, dblk, buf);
242254885Sdumbbell
243254885Sdumbbell	if (rsize)
244254885Sdumbbell		*rsize = 0;
245254885Sdumbbell	if (blks && bc_read(unit, dblk, blks, buf)) {
246254885Sdumbbell		DEBUG("read error");
247254885Sdumbbell		return (EIO);
248254885Sdumbbell	}
249254885Sdumbbell#ifdef BD_SUPPORT_FRAGS
250254885Sdumbbell	DEBUG("bc_strategy: frag read %d from %d+%d to %p",
251254885Sdumbbell	    fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
252254885Sdumbbell	if (fragsize && bc_read(unit, dblk + blks, 1, fragsize)) {
253254885Sdumbbell		DEBUG("frag read error");
254254885Sdumbbell		return(EIO);
255254885Sdumbbell	}
256254885Sdumbbell	bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
257254885Sdumbbell#endif
258254885Sdumbbell	if (rsize)
259254885Sdumbbell		*rsize = size;
260254885Sdumbbell	return (0);
261254885Sdumbbell}
262254885Sdumbbell
263254885Sdumbbellstatic int
264254885Sdumbbellbc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
265254885Sdumbbell{
266254885Sdumbbell	u_int result, resid, retry;
267254885Sdumbbell	static unsigned short packet[8];
268254885Sdumbbell	int biosdev;
269254885Sdumbbell#ifdef DISK_DEBUG
270254885Sdumbbell	int error;
271254885Sdumbbell#endif
272254885Sdumbbell
273254885Sdumbbell	/* Just in case some idiot actually tries to read -1 blocks... */
274254885Sdumbbell	if (blks < 0)
275254885Sdumbbell		return (-1);
276254885Sdumbbell
277254885Sdumbbell	/* If nothing to do, just return succcess. */
278254885Sdumbbell	if (blks == 0)
279254885Sdumbbell		return (0);
280254885Sdumbbell
281254885Sdumbbell	biosdev = bc_unit2bios(unit);
282254885Sdumbbell	/*
283254885Sdumbbell	 * Loop retrying the operation a couple of times.  The BIOS
284254885Sdumbbell	 * may also retry.
285254885Sdumbbell	 */
286254885Sdumbbell	for (retry = 0; retry < 3; retry++) {
287254885Sdumbbell		/* If retrying, reset the drive */
288254885Sdumbbell		if (retry > 0) {
289254885Sdumbbell			v86.ctl = V86_FLAGS;
290254885Sdumbbell			v86.addr = 0x13;
291254885Sdumbbell			v86.eax = 0;
292254885Sdumbbell			v86.edx = biosdev;
293254885Sdumbbell			v86int();
294254885Sdumbbell		}
295254885Sdumbbell
296254885Sdumbbell		packet[0] = 0x10;
297254885Sdumbbell		packet[1] = blks;
298254885Sdumbbell		packet[2] = VTOPOFF(dest);
299254885Sdumbbell		packet[3] = VTOPSEG(dest);
300254885Sdumbbell		packet[4] = dblk & 0xffff;
301254885Sdumbbell		packet[5] = dblk >> 16;
302254885Sdumbbell		packet[6] = 0;
303254885Sdumbbell		packet[7] = 0;
304254885Sdumbbell		v86.ctl = V86_FLAGS;
305254885Sdumbbell		v86.addr = 0x13;
306254885Sdumbbell		v86.eax = 0x4200;
307254885Sdumbbell		v86.edx = biosdev;
308254885Sdumbbell		v86.ds = VTOPSEG(packet);
309254885Sdumbbell		v86.esi = VTOPOFF(packet);
310254885Sdumbbell		v86int();
311254885Sdumbbell		result = (v86.efl & PSL_C);
312254885Sdumbbell		if (result == 0)
313254885Sdumbbell			break;
314254885Sdumbbell	}
315254885Sdumbbell
316254885Sdumbbell#ifdef DISK_DEBUG
317254885Sdumbbell	error = (v86.eax >> 8) & 0xff;
318254885Sdumbbell#endif
319254885Sdumbbell	DEBUG("%d sectors from %ld to %p (0x%x) %s", blks, dblk, dest,
320254885Sdumbbell	    VTOP(dest), result ? "failed" : "ok");
321254885Sdumbbell	DEBUG("unit %d  status 0x%x",  unit, error);
322254885Sdumbbell
323254885Sdumbbell/*	hexdump(dest, (blks * BIOSCD_SECSIZE)); */
324254885Sdumbbell	return(0);
325254885Sdumbbell}
326254885Sdumbbell
327254885Sdumbbell/*
328254885Sdumbbell * Return a suitable dev_t value for (dev).
329254885Sdumbbell */
330254885Sdumbbellint
331254885Sdumbbellbc_getdev(struct i386_devdesc *dev)
332254885Sdumbbell{
333254885Sdumbbell    int biosdev, unit;
334254885Sdumbbell    int major;
335254885Sdumbbell    int rootdev;
336254885Sdumbbell
337254885Sdumbbell    unit = dev->d_kind.bioscd.unit;
338254885Sdumbbell    biosdev = bc_unit2bios(unit);
339254885Sdumbbell    DEBUG("unit %d BIOS device %d", unit, biosdev);
340254885Sdumbbell    if (biosdev == -1)				/* not a BIOS device */
341254885Sdumbbell	return(-1);
342254885Sdumbbell
343254885Sdumbbell    /*
344254885Sdumbbell     * XXX: Need to examine device spec here to figure out if SCSI or
345254885Sdumbbell     * ATAPI.  No idea on how to figure out device number.  All we can
346263170Sdumbbell     * really pass to the kernel is what bus and device on which bus we
347263170Sdumbbell     * were booted from, which dev_t isn't well suited to since those
348263170Sdumbbell     * number don't match to unit numbers very well.  We may just need
349263170Sdumbbell     * to engage in a hack where we pass -C to the boot args if we are
350263170Sdumbbell     * the boot device.
351263170Sdumbbell     */
352254885Sdumbbell    major = ACDMAJOR;
353254885Sdumbbell    unit = 0;	/* XXX */
354254885Sdumbbell
355254885Sdumbbell    /* XXX: Assume partition 'a'. */
356254885Sdumbbell    rootdev = MAKEBOOTDEV(major, 0, 0, unit, 0);
357254885Sdumbbell    DEBUG("dev is 0x%x\n", rootdev);
358254885Sdumbbell    return(rootdev);
359254885Sdumbbell}
360254885Sdumbbell