bioscd.c revision 86091
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 * $FreeBSD: head/sys/boot/i386/libi386/bioscd.c 86091 2001-11-05 18:58:33Z jhb $
2886091Sjhb */
2986091Sjhb
3086091Sjhb/*
3186091Sjhb * BIOS CD device handling for CD's that have been booted off of via no
3286091Sjhb * emulation booting as defined in the El Torito standard.
3386091Sjhb *
3486091Sjhb * Ideas and algorithms from:
3586091Sjhb *
3686091Sjhb * - FreeBSD libi386/biosdisk.c
3786091Sjhb *
3886091Sjhb */
3986091Sjhb
4086091Sjhb#include <stand.h>
4186091Sjhb
4286091Sjhb#include <sys/reboot.h>
4386091Sjhb#include <machine/param.h>
4486091Sjhb#include <machine/psl.h>
4586091Sjhb
4686091Sjhb#include <stdarg.h>
4786091Sjhb
4886091Sjhb#include <bootstrap.h>
4986091Sjhb#include <btxv86.h>
5086091Sjhb#include "libi386.h"
5186091Sjhb
5286091Sjhb#define BIOSCD_SECSIZE		2048
5386091Sjhb#define BUFSIZE			(1 * BIOSCD_SECSIZE)
5486091Sjhb#define	MAXBCDEV		1
5586091Sjhb
5686091Sjhb/* Major numbers for devices we frontend for. */
5786091Sjhb#define ACDMAJOR		117
5886091Sjhb#define	CDMAJOR			15
5986091Sjhb
6086091Sjhb#ifdef DISK_DEBUG
6186091Sjhb# define DEBUG(fmt, args...)	printf("%s: " fmt "\n" , __FUNCTION__ , ## args)
6286091Sjhb#else
6386091Sjhb# define DEBUG(fmt, args...)
6486091Sjhb#endif
6586091Sjhb
6686091Sjhbstruct specification_packet {
6786091Sjhb	u_char	sp_size;
6886091Sjhb	u_char	sp_bootmedia;
6986091Sjhb	u_char	sp_drive;
7086091Sjhb	u_char	sp_controller;
7186091Sjhb	u_int	sp_lba;
7286091Sjhb	u_short	sp_devicespec;
7386091Sjhb	u_short	sp_buffersegment;
7486091Sjhb	u_short	sp_loadsegment;
7586091Sjhb	u_short	sp_sectorcount;
7686091Sjhb	u_short	sp_cylsec;
7786091Sjhb	u_char	sp_head;
7886091Sjhb};
7986091Sjhb
8086091Sjhb/*
8186091Sjhb * List of BIOS devices, translation from disk unit number to
8286091Sjhb * BIOS unit number.
8386091Sjhb */
8486091Sjhbstatic struct bcinfo {
8586091Sjhb	int	bc_unit;		/* BIOS unit number */
8686091Sjhb	struct specification_packet bc_sp;
8786091Sjhb} bcinfo [MAXBCDEV];
8886091Sjhbstatic int nbcinfo = 0;
8986091Sjhb
9086091Sjhbstatic int	bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
9186091Sjhbstatic int	bc_init(void);
9286091Sjhbstatic int	bc_strategy(void *devdata, int flag, daddr_t dblk,
9386091Sjhb		    size_t size, char *buf, size_t *rsize);
9486091Sjhbstatic int	bc_realstrategy(void *devdata, int flag, daddr_t dblk,
9586091Sjhb		    size_t size, char *buf, size_t *rsize);
9686091Sjhbstatic int	bc_open(struct open_file *f, ...);
9786091Sjhbstatic int	bc_close(struct open_file *f);
9886091Sjhbstatic void	bc_print(int verbose);
9986091Sjhb
10086091Sjhbstruct devsw bioscd = {
10186091Sjhb	"cd",
10286091Sjhb	DEVT_CD,
10386091Sjhb	bc_init,
10486091Sjhb	bc_strategy,
10586091Sjhb	bc_open,
10686091Sjhb	bc_close,
10786091Sjhb	noioctl,
10886091Sjhb	bc_print,
10986091Sjhb	NULL
11086091Sjhb};
11186091Sjhb
11286091Sjhb/*
11386091Sjhb * Translate between BIOS device numbers and our private unit numbers.
11486091Sjhb */
11586091Sjhbint
11686091Sjhbbc_bios2unit(int biosdev)
11786091Sjhb{
11886091Sjhb	int i;
11986091Sjhb
12086091Sjhb	DEBUG("looking for bios device 0x%x", biosdev);
12186091Sjhb	for (i = 0; i < nbcinfo; i++) {
12286091Sjhb		DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
12386091Sjhb		if (bcinfo[i].bc_unit == biosdev)
12486091Sjhb			return(i);
12586091Sjhb	}
12686091Sjhb	return(-1);
12786091Sjhb}
12886091Sjhb
12986091Sjhbint
13086091Sjhbbc_unit2bios(int unit)
13186091Sjhb{
13286091Sjhb	if ((unit >= 0) && (unit < nbcinfo))
13386091Sjhb		return(bcinfo[unit].bc_unit);
13486091Sjhb	return(-1);
13586091Sjhb}
13686091Sjhb
13786091Sjhb/*
13886091Sjhb * We can't quiz, we have to be told what device to use, so this functoin
13986091Sjhb * doesn't do anything.  Instead, the loader calls bc_add() with the BIOS
14086091Sjhb * device number to add.
14186091Sjhb */
14286091Sjhbstatic int
14386091Sjhbbc_init(void)
14486091Sjhb{
14586091Sjhb
14686091Sjhb	return (0);
14786091Sjhb}
14886091Sjhb
14986091Sjhbint
15086091Sjhbbc_add(int biosdev)
15186091Sjhb{
15286091Sjhb
15386091Sjhb	if (nbcinfo >= MAXBCDEV)
15486091Sjhb		return (-1);
15586091Sjhb	bcinfo[nbcinfo].bc_unit = biosdev;
15686091Sjhb	v86.ctl = V86_FLAGS;
15786091Sjhb	v86.addr = 0x13;
15886091Sjhb	v86.eax = 0x4b01;
15986091Sjhb	v86.edx = biosdev;
16086091Sjhb	v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
16186091Sjhb	v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
16286091Sjhb	v86int();
16386091Sjhb	if ((v86.eax & 0xff00) != 0)
16486091Sjhb		return (-1);
16586091Sjhb
16686091Sjhb	printf("BIOS CD is cd%d\n", nbcinfo);
16786091Sjhb	nbcinfo++;
16886091Sjhb	return(0);
16986091Sjhb}
17086091Sjhb
17186091Sjhb/*
17286091Sjhb * Print information about disks
17386091Sjhb */
17486091Sjhbstatic void
17586091Sjhbbc_print(int verbose)
17686091Sjhb{
17786091Sjhb	int i;
17886091Sjhb	char line[80];
17986091Sjhb
18086091Sjhb	for (i = 0; i < nbcinfo; i++) {
18186091Sjhb		sprintf(line, "    cd%d: Device 0x%x\n", i,
18286091Sjhb		    bcinfo[i].bc_sp.sp_devicespec);
18386091Sjhb		pager_output(line);
18486091Sjhb	}
18586091Sjhb}
18686091Sjhb
18786091Sjhb/*
18886091Sjhb * Attempt to open the disk described by (dev) for use by (f).
18986091Sjhb */
19086091Sjhbstatic int
19186091Sjhbbc_open(struct open_file *f, ...)
19286091Sjhb{
19386091Sjhb	va_list ap;
19486091Sjhb	struct i386_devdesc *dev;
19586091Sjhb	int error;
19686091Sjhb
19786091Sjhb	va_start(ap, f);
19886091Sjhb	dev = va_arg(ap, struct i386_devdesc *);
19986091Sjhb	va_end(ap);
20086091Sjhb	if (dev->d_kind.bioscd.unit >= nbcinfo) {
20186091Sjhb		DEBUG("attempt to open nonexistent disk");
20286091Sjhb		return(ENXIO);
20386091Sjhb	}
20486091Sjhb
20586091Sjhb	return(0);
20686091Sjhb}
20786091Sjhb
20886091Sjhbstatic int
20986091Sjhbbc_close(struct open_file *f)
21086091Sjhb{
21186091Sjhb
21286091Sjhb	return(0);
21386091Sjhb}
21486091Sjhb
21586091Sjhbstatic int
21686091Sjhbbc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
21786091Sjhb    size_t *rsize)
21886091Sjhb{
21986091Sjhb	struct i386_devdesc *dev;
22086091Sjhb	int unit;
22186091Sjhb	int blks;
22286091Sjhb#ifdef BD_SUPPORT_FRAGS
22386091Sjhb	char fragbuf[BIOSCD_SECSIZE];
22486091Sjhb	size_t fragsize;
22586091Sjhb
22686091Sjhb	fragsize = size % BIOSCD_SECSIZE;
22786091Sjhb#else
22886091Sjhb	if (size % BIOSCD_SECSIZE)
22986091Sjhb		return (EINVAL);
23086091Sjhb#endif
23186091Sjhb
23286091Sjhb	if (rw != F_READ)
23386091Sjhb		return(EROFS);
23486091Sjhb	dev = (struct i386_devdesc *)devdata;
23586091Sjhb	unit = dev->d_kind.bioscd.unit;
23686091Sjhb	blks = size / BIOSCD_SECSIZE;
23786091Sjhb	if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
23886091Sjhb		return (EINVAL);
23986091Sjhb	dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
24086091Sjhb	DEBUG("read %d from %d to %p", blks, dblk, buf);
24186091Sjhb
24286091Sjhb	if (rsize)
24386091Sjhb		*rsize = 0;
24486091Sjhb	if (blks && bc_read(unit, dblk, blks, buf)) {
24586091Sjhb		DEBUG("read error");
24686091Sjhb		return (EIO);
24786091Sjhb	}
24886091Sjhb#ifdef BD_SUPPORT_FRAGS
24986091Sjhb	DEBUG("bc_strategy: frag read %d from %d+%d to %p",
25086091Sjhb	    fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
25186091Sjhb	if (fragsize && bc_read(unit, dblk + blks, 1, fragsize)) {
25286091Sjhb		DEBUG("frag read error");
25386091Sjhb		return(EIO);
25486091Sjhb	}
25586091Sjhb	bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
25686091Sjhb#endif
25786091Sjhb	if (rsize)
25886091Sjhb		*rsize = size;
25986091Sjhb	return (0);
26086091Sjhb}
26186091Sjhb
26286091Sjhbstatic int
26386091Sjhbbc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
26486091Sjhb{
26586091Sjhb	u_int result, resid, retry;
26686091Sjhb	static unsigned short packet[8];
26786091Sjhb	int biosdev;
26886091Sjhb#ifdef DISK_DEBUG
26986091Sjhb	int error;
27086091Sjhb#endif
27186091Sjhb
27286091Sjhb	/* Just in case some idiot actually tries to read -1 blocks... */
27386091Sjhb	if (blks < 0)
27486091Sjhb		return (-1);
27586091Sjhb
27686091Sjhb	/* If nothing to do, just return succcess. */
27786091Sjhb	if (blks == 0)
27886091Sjhb		return (0);
27986091Sjhb
28086091Sjhb	biosdev = bc_unit2bios(unit);
28186091Sjhb	/*
28286091Sjhb	 * Loop retrying the operation a couple of times.  The BIOS
28386091Sjhb	 * may also retry.
28486091Sjhb	 */
28586091Sjhb	for (retry = 0; retry < 3; retry++) {
28686091Sjhb		/* If retrying, reset the drive */
28786091Sjhb		if (retry > 0) {
28886091Sjhb			v86.ctl = V86_FLAGS;
28986091Sjhb			v86.addr = 0x13;
29086091Sjhb			v86.eax = 0;
29186091Sjhb			v86.edx = biosdev;
29286091Sjhb			v86int();
29386091Sjhb		}
29486091Sjhb
29586091Sjhb		packet[0] = 0x10;
29686091Sjhb		packet[1] = blks;
29786091Sjhb		packet[2] = VTOPOFF(dest);
29886091Sjhb		packet[3] = VTOPSEG(dest);
29986091Sjhb		packet[4] = dblk & 0xffff;
30086091Sjhb		packet[5] = dblk >> 16;
30186091Sjhb		packet[6] = 0;
30286091Sjhb		packet[7] = 0;
30386091Sjhb		v86.ctl = V86_FLAGS;
30486091Sjhb		v86.addr = 0x13;
30586091Sjhb		v86.eax = 0x4200;
30686091Sjhb		v86.edx = biosdev;
30786091Sjhb		v86.ds = VTOPSEG(packet);
30886091Sjhb		v86.esi = VTOPOFF(packet);
30986091Sjhb		v86int();
31086091Sjhb		result = (v86.efl & PSL_C);
31186091Sjhb		if (result == 0)
31286091Sjhb			break;
31386091Sjhb	}
31486091Sjhb
31586091Sjhb#ifdef DISK_DEBUG
31686091Sjhb	error = (v86.eax >> 8) & 0xff;
31786091Sjhb#endif
31886091Sjhb	DEBUG("%d sectors from %ld to %p (0x%x) %s", blks, dblk, dest,
31986091Sjhb	    VTOP(dest), result ? "failed" : "ok");
32086091Sjhb	DEBUG("unit %d  status 0x%x",  unit, error);
32186091Sjhb
32286091Sjhb/*	hexdump(dest, (blks * BIOSCD_SECSIZE)); */
32386091Sjhb	return(0);
32486091Sjhb}
32586091Sjhb
32686091Sjhb/*
32786091Sjhb * Return a suitable dev_t value for (dev).
32886091Sjhb */
32986091Sjhbint
33086091Sjhbbc_getdev(struct i386_devdesc *dev)
33186091Sjhb{
33286091Sjhb    int biosdev, unit;
33386091Sjhb    int major;
33486091Sjhb    int rootdev;
33586091Sjhb
33686091Sjhb    unit = dev->d_kind.bioscd.unit;
33786091Sjhb    biosdev = bc_unit2bios(unit);
33886091Sjhb    DEBUG("unit %d BIOS device %d", unit, biosdev);
33986091Sjhb    if (biosdev == -1)				/* not a BIOS device */
34086091Sjhb	return(-1);
34186091Sjhb
34286091Sjhb    /*
34386091Sjhb     * XXX: Need to examine device spec here to figure out if SCSI or
34486091Sjhb     * ATAPI.  No idea on how to figure out device number.  All we can
34586091Sjhb     * really pass to the kernel is what bus and device on which bus we
34686091Sjhb     * were booted from, which dev_t isn't well suited to since those
34786091Sjhb     * number don't match to unit numbers very well.  We may just need
34886091Sjhb     * to engage in a hack where we pass -C to the boot args if we are
34986091Sjhb     * the boot device.
35086091Sjhb     */
35186091Sjhb    major = ACDMAJOR;
35286091Sjhb    unit = 0;	/* XXX */
35386091Sjhb
35486091Sjhb    /* XXX: Assume partition 'a'. */
35586091Sjhb    rootdev = MAKEBOOTDEV(major, 0, 0, unit, 0);
35686091Sjhb    DEBUG("dev is 0x%x\n", rootdev);
35786091Sjhb    return(rootdev);
35886091Sjhb}
359