bioscd.c revision 119482
186091Sjhb/*-
286091Sjhb * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
386091Sjhb * Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org>
486091Sjhb * All rights reserved.
586091Sjhb *
686091Sjhb * Redistribution and use in source and binary forms, with or without
786091Sjhb * modification, are permitted provided that the following conditions
886091Sjhb * are met:
986091Sjhb * 1. Redistributions of source code must retain the above copyright
1086091Sjhb *    notice, this list of conditions and the following disclaimer.
1186091Sjhb * 2. Redistributions in binary form must reproduce the above copyright
1286091Sjhb *    notice, this list of conditions and the following disclaimer in the
1386091Sjhb *    documentation and/or other materials provided with the distribution.
1486091Sjhb *
1586091Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1686091Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1786091Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1886091Sjhb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1986091Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2086091Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2186091Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2286091Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2386091Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2486091Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2586091Sjhb * SUCH DAMAGE.
2686091Sjhb */
2786091Sjhb
28119482Sobrien#include <sys/cdefs.h>
29119482Sobrien__FBSDID("$FreeBSD: head/sys/boot/i386/libi386/bioscd.c 119482 2003-08-25 23:28:32Z obrien $");
30119482Sobrien
3186091Sjhb/*
3286091Sjhb * BIOS CD device handling for CD's that have been booted off of via no
3386091Sjhb * emulation booting as defined in the El Torito standard.
3486091Sjhb *
3586091Sjhb * Ideas and algorithms from:
3686091Sjhb *
3786091Sjhb * - FreeBSD libi386/biosdisk.c
3886091Sjhb *
3986091Sjhb */
4086091Sjhb
4186091Sjhb#include <stand.h>
4286091Sjhb
4396654Sjhay#include <sys/param.h>
44113083Sphk#include <machine/bootinfo.h>
4586091Sjhb#include <machine/psl.h>
4686091Sjhb
4786091Sjhb#include <stdarg.h>
4886091Sjhb
4986091Sjhb#include <bootstrap.h>
5086091Sjhb#include <btxv86.h>
5186091Sjhb#include "libi386.h"
5286091Sjhb
5386091Sjhb#define BIOSCD_SECSIZE		2048
5486091Sjhb#define BUFSIZE			(1 * BIOSCD_SECSIZE)
5586091Sjhb#define	MAXBCDEV		1
5686091Sjhb
5786091Sjhb/* Major numbers for devices we frontend for. */
5886091Sjhb#define ACDMAJOR		117
5986091Sjhb#define	CDMAJOR			15
6086091Sjhb
6186091Sjhb#ifdef DISK_DEBUG
6287599Sobrien# define DEBUG(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
6386091Sjhb#else
6486091Sjhb# define DEBUG(fmt, args...)
6586091Sjhb#endif
6686091Sjhb
6786091Sjhbstruct specification_packet {
6886091Sjhb	u_char	sp_size;
6986091Sjhb	u_char	sp_bootmedia;
7086091Sjhb	u_char	sp_drive;
7186091Sjhb	u_char	sp_controller;
7286091Sjhb	u_int	sp_lba;
7386091Sjhb	u_short	sp_devicespec;
7486091Sjhb	u_short	sp_buffersegment;
7586091Sjhb	u_short	sp_loadsegment;
7686091Sjhb	u_short	sp_sectorcount;
7786091Sjhb	u_short	sp_cylsec;
7886091Sjhb	u_char	sp_head;
7986091Sjhb};
8086091Sjhb
8186091Sjhb/*
8286091Sjhb * List of BIOS devices, translation from disk unit number to
8386091Sjhb * BIOS unit number.
8486091Sjhb */
8586091Sjhbstatic struct bcinfo {
8686091Sjhb	int	bc_unit;		/* BIOS unit number */
8786091Sjhb	struct specification_packet bc_sp;
8886091Sjhb} bcinfo [MAXBCDEV];
8986091Sjhbstatic int nbcinfo = 0;
9086091Sjhb
9186091Sjhbstatic int	bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
9286091Sjhbstatic int	bc_init(void);
9386091Sjhbstatic int	bc_strategy(void *devdata, int flag, daddr_t dblk,
9486091Sjhb		    size_t size, char *buf, size_t *rsize);
9586091Sjhbstatic int	bc_realstrategy(void *devdata, int flag, daddr_t dblk,
9686091Sjhb		    size_t size, char *buf, size_t *rsize);
9786091Sjhbstatic int	bc_open(struct open_file *f, ...);
9886091Sjhbstatic int	bc_close(struct open_file *f);
9986091Sjhbstatic void	bc_print(int verbose);
10086091Sjhb
10186091Sjhbstruct devsw bioscd = {
10286091Sjhb	"cd",
10386091Sjhb	DEVT_CD,
10486091Sjhb	bc_init,
10586091Sjhb	bc_strategy,
10686091Sjhb	bc_open,
10786091Sjhb	bc_close,
10886091Sjhb	noioctl,
10986091Sjhb	bc_print,
11086091Sjhb	NULL
11186091Sjhb};
11286091Sjhb
11386091Sjhb/*
11486091Sjhb * Translate between BIOS device numbers and our private unit numbers.
11586091Sjhb */
11686091Sjhbint
11786091Sjhbbc_bios2unit(int biosdev)
11886091Sjhb{
11986091Sjhb	int i;
12086091Sjhb
12186091Sjhb	DEBUG("looking for bios device 0x%x", biosdev);
12286091Sjhb	for (i = 0; i < nbcinfo; i++) {
12386091Sjhb		DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
12486091Sjhb		if (bcinfo[i].bc_unit == biosdev)
12586091Sjhb			return(i);
12686091Sjhb	}
12786091Sjhb	return(-1);
12886091Sjhb}
12986091Sjhb
13086091Sjhbint
13186091Sjhbbc_unit2bios(int unit)
13286091Sjhb{
13386091Sjhb	if ((unit >= 0) && (unit < nbcinfo))
13486091Sjhb		return(bcinfo[unit].bc_unit);
13586091Sjhb	return(-1);
13686091Sjhb}
13786091Sjhb
13886091Sjhb/*
13986091Sjhb * We can't quiz, we have to be told what device to use, so this functoin
14086091Sjhb * doesn't do anything.  Instead, the loader calls bc_add() with the BIOS
14186091Sjhb * device number to add.
14286091Sjhb */
14386091Sjhbstatic int
14486091Sjhbbc_init(void)
14586091Sjhb{
14686091Sjhb
14786091Sjhb	return (0);
14886091Sjhb}
14986091Sjhb
15086091Sjhbint
15186091Sjhbbc_add(int biosdev)
15286091Sjhb{
15386091Sjhb
15486091Sjhb	if (nbcinfo >= MAXBCDEV)
15586091Sjhb		return (-1);
15686091Sjhb	bcinfo[nbcinfo].bc_unit = biosdev;
15786091Sjhb	v86.ctl = V86_FLAGS;
15886091Sjhb	v86.addr = 0x13;
15986091Sjhb	v86.eax = 0x4b01;
16086091Sjhb	v86.edx = biosdev;
16186091Sjhb	v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
16286091Sjhb	v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
16386091Sjhb	v86int();
16486091Sjhb	if ((v86.eax & 0xff00) != 0)
16586091Sjhb		return (-1);
16686091Sjhb
16786091Sjhb	printf("BIOS CD is cd%d\n", nbcinfo);
16886091Sjhb	nbcinfo++;
16986091Sjhb	return(0);
17086091Sjhb}
17186091Sjhb
17286091Sjhb/*
17386091Sjhb * Print information about disks
17486091Sjhb */
17586091Sjhbstatic void
17686091Sjhbbc_print(int verbose)
17786091Sjhb{
17886091Sjhb	int i;
17986091Sjhb	char line[80];
18086091Sjhb
18186091Sjhb	for (i = 0; i < nbcinfo; i++) {
18286091Sjhb		sprintf(line, "    cd%d: Device 0x%x\n", i,
18386091Sjhb		    bcinfo[i].bc_sp.sp_devicespec);
18486091Sjhb		pager_output(line);
18586091Sjhb	}
18686091Sjhb}
18786091Sjhb
18886091Sjhb/*
18986091Sjhb * Attempt to open the disk described by (dev) for use by (f).
19086091Sjhb */
19186091Sjhbstatic int
19286091Sjhbbc_open(struct open_file *f, ...)
19386091Sjhb{
19486091Sjhb	va_list ap;
19586091Sjhb	struct i386_devdesc *dev;
19686091Sjhb	int error;
19786091Sjhb
19886091Sjhb	va_start(ap, f);
19986091Sjhb	dev = va_arg(ap, struct i386_devdesc *);
20086091Sjhb	va_end(ap);
20186091Sjhb	if (dev->d_kind.bioscd.unit >= nbcinfo) {
20286091Sjhb		DEBUG("attempt to open nonexistent disk");
20386091Sjhb		return(ENXIO);
20486091Sjhb	}
20586091Sjhb
20686091Sjhb	return(0);
20786091Sjhb}
20886091Sjhb
20986091Sjhbstatic int
21086091Sjhbbc_close(struct open_file *f)
21186091Sjhb{
21286091Sjhb
21386091Sjhb	return(0);
21486091Sjhb}
21586091Sjhb
21686091Sjhbstatic int
21786091Sjhbbc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
21886091Sjhb    size_t *rsize)
21986091Sjhb{
22086091Sjhb	struct i386_devdesc *dev;
22186091Sjhb	int unit;
22286091Sjhb	int blks;
22386091Sjhb#ifdef BD_SUPPORT_FRAGS
22486091Sjhb	char fragbuf[BIOSCD_SECSIZE];
22586091Sjhb	size_t fragsize;
22686091Sjhb
22786091Sjhb	fragsize = size % BIOSCD_SECSIZE;
22886091Sjhb#else
22986091Sjhb	if (size % BIOSCD_SECSIZE)
23086091Sjhb		return (EINVAL);
23186091Sjhb#endif
23286091Sjhb
23386091Sjhb	if (rw != F_READ)
23486091Sjhb		return(EROFS);
23586091Sjhb	dev = (struct i386_devdesc *)devdata;
23686091Sjhb	unit = dev->d_kind.bioscd.unit;
23786091Sjhb	blks = size / BIOSCD_SECSIZE;
23886091Sjhb	if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
23986091Sjhb		return (EINVAL);
24086091Sjhb	dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
24186091Sjhb	DEBUG("read %d from %d to %p", blks, dblk, buf);
24286091Sjhb
24386091Sjhb	if (rsize)
24486091Sjhb		*rsize = 0;
24586091Sjhb	if (blks && bc_read(unit, dblk, blks, buf)) {
24686091Sjhb		DEBUG("read error");
24786091Sjhb		return (EIO);
24886091Sjhb	}
24986091Sjhb#ifdef BD_SUPPORT_FRAGS
25086091Sjhb	DEBUG("bc_strategy: frag read %d from %d+%d to %p",
25186091Sjhb	    fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
25286091Sjhb	if (fragsize && bc_read(unit, dblk + blks, 1, fragsize)) {
25386091Sjhb		DEBUG("frag read error");
25486091Sjhb		return(EIO);
25586091Sjhb	}
25686091Sjhb	bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
25786091Sjhb#endif
25886091Sjhb	if (rsize)
25986091Sjhb		*rsize = size;
26086091Sjhb	return (0);
26186091Sjhb}
26286091Sjhb
26386091Sjhbstatic int
26486091Sjhbbc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
26586091Sjhb{
26686091Sjhb	u_int result, resid, retry;
26786091Sjhb	static unsigned short packet[8];
26886091Sjhb	int biosdev;
26986091Sjhb#ifdef DISK_DEBUG
27086091Sjhb	int error;
27186091Sjhb#endif
27286091Sjhb
27386091Sjhb	/* Just in case some idiot actually tries to read -1 blocks... */
27486091Sjhb	if (blks < 0)
27586091Sjhb		return (-1);
27686091Sjhb
27786091Sjhb	/* If nothing to do, just return succcess. */
27886091Sjhb	if (blks == 0)
27986091Sjhb		return (0);
28086091Sjhb
28186091Sjhb	biosdev = bc_unit2bios(unit);
28286091Sjhb	/*
28386091Sjhb	 * Loop retrying the operation a couple of times.  The BIOS
28486091Sjhb	 * may also retry.
28586091Sjhb	 */
28686091Sjhb	for (retry = 0; retry < 3; retry++) {
28786091Sjhb		/* If retrying, reset the drive */
28886091Sjhb		if (retry > 0) {
28986091Sjhb			v86.ctl = V86_FLAGS;
29086091Sjhb			v86.addr = 0x13;
29186091Sjhb			v86.eax = 0;
29286091Sjhb			v86.edx = biosdev;
29386091Sjhb			v86int();
29486091Sjhb		}
29586091Sjhb
29686091Sjhb		packet[0] = 0x10;
29786091Sjhb		packet[1] = blks;
29886091Sjhb		packet[2] = VTOPOFF(dest);
29986091Sjhb		packet[3] = VTOPSEG(dest);
30086091Sjhb		packet[4] = dblk & 0xffff;
30186091Sjhb		packet[5] = dblk >> 16;
30286091Sjhb		packet[6] = 0;
30386091Sjhb		packet[7] = 0;
30486091Sjhb		v86.ctl = V86_FLAGS;
30586091Sjhb		v86.addr = 0x13;
30686091Sjhb		v86.eax = 0x4200;
30786091Sjhb		v86.edx = biosdev;
30886091Sjhb		v86.ds = VTOPSEG(packet);
30986091Sjhb		v86.esi = VTOPOFF(packet);
31086091Sjhb		v86int();
31186091Sjhb		result = (v86.efl & PSL_C);
31286091Sjhb		if (result == 0)
31386091Sjhb			break;
31486091Sjhb	}
31586091Sjhb
31686091Sjhb#ifdef DISK_DEBUG
31786091Sjhb	error = (v86.eax >> 8) & 0xff;
31886091Sjhb#endif
31986091Sjhb	DEBUG("%d sectors from %ld to %p (0x%x) %s", blks, dblk, dest,
32086091Sjhb	    VTOP(dest), result ? "failed" : "ok");
32186091Sjhb	DEBUG("unit %d  status 0x%x",  unit, error);
32286091Sjhb
32386091Sjhb/*	hexdump(dest, (blks * BIOSCD_SECSIZE)); */
32486091Sjhb	return(0);
32586091Sjhb}
32686091Sjhb
32786091Sjhb/*
32886091Sjhb * Return a suitable dev_t value for (dev).
32986091Sjhb */
33086091Sjhbint
33186091Sjhbbc_getdev(struct i386_devdesc *dev)
33286091Sjhb{
33386091Sjhb    int biosdev, unit;
33486091Sjhb    int major;
33586091Sjhb    int rootdev;
33686091Sjhb
33786091Sjhb    unit = dev->d_kind.bioscd.unit;
33886091Sjhb    biosdev = bc_unit2bios(unit);
33986091Sjhb    DEBUG("unit %d BIOS device %d", unit, biosdev);
34086091Sjhb    if (biosdev == -1)				/* not a BIOS device */
34186091Sjhb	return(-1);
34286091Sjhb
34386091Sjhb    /*
34486091Sjhb     * XXX: Need to examine device spec here to figure out if SCSI or
34586091Sjhb     * ATAPI.  No idea on how to figure out device number.  All we can
34686091Sjhb     * really pass to the kernel is what bus and device on which bus we
34786091Sjhb     * were booted from, which dev_t isn't well suited to since those
34886091Sjhb     * number don't match to unit numbers very well.  We may just need
34986091Sjhb     * to engage in a hack where we pass -C to the boot args if we are
35086091Sjhb     * the boot device.
35186091Sjhb     */
35286091Sjhb    major = ACDMAJOR;
35386091Sjhb    unit = 0;	/* XXX */
35486091Sjhb
35586091Sjhb    /* XXX: Assume partition 'a'. */
35686091Sjhb    rootdev = MAKEBOOTDEV(major, 0, 0, unit, 0);
35786091Sjhb    DEBUG("dev is 0x%x\n", rootdev);
35886091Sjhb    return(rootdev);
35986091Sjhb}
360