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$");
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
4686091Sjhb#include <stdarg.h>
4786091Sjhb
4886091Sjhb#include <bootstrap.h>
4986091Sjhb#include <btxv86.h>
50227400Sjhb#include <edd.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_open(struct open_file *f, ...);
9686091Sjhbstatic int	bc_close(struct open_file *f);
9786091Sjhbstatic void	bc_print(int verbose);
9886091Sjhb
9986091Sjhbstruct devsw bioscd = {
10086091Sjhb	"cd",
10186091Sjhb	DEVT_CD,
10286091Sjhb	bc_init,
10386091Sjhb	bc_strategy,
10486091Sjhb	bc_open,
10586091Sjhb	bc_close,
10686091Sjhb	noioctl,
10786091Sjhb	bc_print,
10886091Sjhb	NULL
10986091Sjhb};
11086091Sjhb
11186091Sjhb/*
11286091Sjhb * Translate between BIOS device numbers and our private unit numbers.
11386091Sjhb */
11486091Sjhbint
11586091Sjhbbc_bios2unit(int biosdev)
11686091Sjhb{
11786091Sjhb	int i;
11886091Sjhb
11986091Sjhb	DEBUG("looking for bios device 0x%x", biosdev);
12086091Sjhb	for (i = 0; i < nbcinfo; i++) {
12186091Sjhb		DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
12286091Sjhb		if (bcinfo[i].bc_unit == biosdev)
12386091Sjhb			return(i);
12486091Sjhb	}
12586091Sjhb	return(-1);
12686091Sjhb}
12786091Sjhb
12886091Sjhbint
12986091Sjhbbc_unit2bios(int unit)
13086091Sjhb{
13186091Sjhb	if ((unit >= 0) && (unit < nbcinfo))
13286091Sjhb		return(bcinfo[unit].bc_unit);
13386091Sjhb	return(-1);
13486091Sjhb}
13586091Sjhb
13686091Sjhb/*
13786091Sjhb * We can't quiz, we have to be told what device to use, so this functoin
13886091Sjhb * doesn't do anything.  Instead, the loader calls bc_add() with the BIOS
13986091Sjhb * device number to add.
14086091Sjhb */
14186091Sjhbstatic int
14286091Sjhbbc_init(void)
14386091Sjhb{
14486091Sjhb
14586091Sjhb	return (0);
14686091Sjhb}
14786091Sjhb
14886091Sjhbint
14986091Sjhbbc_add(int biosdev)
15086091Sjhb{
15186091Sjhb
15286091Sjhb	if (nbcinfo >= MAXBCDEV)
15386091Sjhb		return (-1);
15486091Sjhb	bcinfo[nbcinfo].bc_unit = biosdev;
15586091Sjhb	v86.ctl = V86_FLAGS;
15686091Sjhb	v86.addr = 0x13;
15786091Sjhb	v86.eax = 0x4b01;
15886091Sjhb	v86.edx = biosdev;
15986091Sjhb	v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
16086091Sjhb	v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
16186091Sjhb	v86int();
162228187Sjhb	if ((v86.eax & 0xff00) != 0)
16386091Sjhb		return (-1);
16486091Sjhb
16586091Sjhb	printf("BIOS CD is cd%d\n", nbcinfo);
16686091Sjhb	nbcinfo++;
16786091Sjhb	return(0);
16886091Sjhb}
16986091Sjhb
17086091Sjhb/*
17186091Sjhb * Print information about disks
17286091Sjhb */
17386091Sjhbstatic void
17486091Sjhbbc_print(int verbose)
17586091Sjhb{
176189749Sjhb	char line[80];
17786091Sjhb	int i;
178189749Sjhb
17986091Sjhb	for (i = 0; i < nbcinfo; i++) {
18086091Sjhb		sprintf(line, "    cd%d: Device 0x%x\n", i,
18186091Sjhb		    bcinfo[i].bc_sp.sp_devicespec);
18286091Sjhb		pager_output(line);
18386091Sjhb	}
18486091Sjhb}
18586091Sjhb
18686091Sjhb/*
18786091Sjhb * Attempt to open the disk described by (dev) for use by (f).
18886091Sjhb */
18986091Sjhbstatic int
19086091Sjhbbc_open(struct open_file *f, ...)
19186091Sjhb{
19286091Sjhb	va_list ap;
19386091Sjhb	struct i386_devdesc *dev;
19486091Sjhb
19586091Sjhb	va_start(ap, f);
19686091Sjhb	dev = va_arg(ap, struct i386_devdesc *);
19786091Sjhb	va_end(ap);
198163897Smarcel	if (dev->d_unit >= nbcinfo) {
19986091Sjhb		DEBUG("attempt to open nonexistent disk");
20086091Sjhb		return(ENXIO);
20186091Sjhb	}
20286091Sjhb
20386091Sjhb	return(0);
20486091Sjhb}
20586091Sjhb
20686091Sjhbstatic int
20786091Sjhbbc_close(struct open_file *f)
20886091Sjhb{
20986091Sjhb
21086091Sjhb	return(0);
21186091Sjhb}
21286091Sjhb
21386091Sjhbstatic int
21486091Sjhbbc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf,
21586091Sjhb    size_t *rsize)
21686091Sjhb{
21786091Sjhb	struct i386_devdesc *dev;
21886091Sjhb	int unit;
21986091Sjhb	int blks;
22086091Sjhb#ifdef BD_SUPPORT_FRAGS
22186091Sjhb	char fragbuf[BIOSCD_SECSIZE];
22286091Sjhb	size_t fragsize;
22386091Sjhb
22486091Sjhb	fragsize = size % BIOSCD_SECSIZE;
22586091Sjhb#else
22686091Sjhb	if (size % BIOSCD_SECSIZE)
22786091Sjhb		return (EINVAL);
22886091Sjhb#endif
22986091Sjhb
23086091Sjhb	if (rw != F_READ)
23186091Sjhb		return(EROFS);
23286091Sjhb	dev = (struct i386_devdesc *)devdata;
233163897Smarcel	unit = dev->d_unit;
23486091Sjhb	blks = size / BIOSCD_SECSIZE;
23586091Sjhb	if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
23686091Sjhb		return (EINVAL);
23786091Sjhb	dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
238189749Sjhb	DEBUG("read %d from %lld to %p", blks, dblk, buf);
23986091Sjhb
24086091Sjhb	if (rsize)
24186091Sjhb		*rsize = 0;
24286091Sjhb	if (blks && bc_read(unit, dblk, blks, buf)) {
24386091Sjhb		DEBUG("read error");
24486091Sjhb		return (EIO);
24586091Sjhb	}
24686091Sjhb#ifdef BD_SUPPORT_FRAGS
247189749Sjhb	DEBUG("frag read %d from %lld+%d to %p",
24886091Sjhb	    fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
249189749Sjhb	if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) {
25086091Sjhb		DEBUG("frag read error");
25186091Sjhb		return(EIO);
25286091Sjhb	}
25386091Sjhb	bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
25486091Sjhb#endif
25586091Sjhb	if (rsize)
25686091Sjhb		*rsize = size;
25786091Sjhb	return (0);
25886091Sjhb}
25986091Sjhb
260189749Sjhb/* Max number of sectors to bounce-buffer at a time. */
261189749Sjhb#define	CD_BOUNCEBUF	8
262189749Sjhb
26386091Sjhbstatic int
26486091Sjhbbc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
26586091Sjhb{
266189749Sjhb	u_int maxfer, resid, result, retry, x;
267189749Sjhb	caddr_t bbuf, p, xp;
268189749Sjhb	static struct edd_packet packet;
26986091Sjhb	int biosdev;
27086091Sjhb#ifdef DISK_DEBUG
27186091Sjhb	int error;
27286091Sjhb#endif
27386091Sjhb
27486091Sjhb	/* Just in case some idiot actually tries to read -1 blocks... */
27586091Sjhb	if (blks < 0)
27686091Sjhb		return (-1);
27786091Sjhb
27886091Sjhb	/* If nothing to do, just return succcess. */
27986091Sjhb	if (blks == 0)
28086091Sjhb		return (0);
28186091Sjhb
282189749Sjhb	/* Decide whether we have to bounce */
283189749Sjhb	if (VTOP(dest) >> 20 != 0) {
284189749Sjhb		/*
285189749Sjhb		 * The destination buffer is above first 1MB of
286189749Sjhb		 * physical memory so we have to arrange a suitable
287189749Sjhb		 * bounce buffer.
288189749Sjhb		 */
289189749Sjhb		x = min(CD_BOUNCEBUF, (unsigned)blks);
290189749Sjhb		bbuf = alloca(x * BIOSCD_SECSIZE);
291189749Sjhb		maxfer = x;
292189749Sjhb	} else {
293189749Sjhb		bbuf = NULL;
294189749Sjhb		maxfer = 0;
295189749Sjhb	}
296189749Sjhb
29786091Sjhb	biosdev = bc_unit2bios(unit);
298189749Sjhb	resid = blks;
299189749Sjhb	p = dest;
300189749Sjhb
301189749Sjhb	while (resid > 0) {
302189749Sjhb		if (bbuf)
303189749Sjhb			xp = bbuf;
304189749Sjhb		else
305189749Sjhb			xp = p;
306189749Sjhb		x = resid;
307189749Sjhb		if (maxfer > 0)
308189749Sjhb			x = min(x, maxfer);
309189749Sjhb
310189749Sjhb		/*
311189749Sjhb		 * Loop retrying the operation a couple of times.  The BIOS
312189749Sjhb		 * may also retry.
313189749Sjhb		 */
314189749Sjhb		for (retry = 0; retry < 3; retry++) {
315189749Sjhb			/* If retrying, reset the drive */
316189749Sjhb			if (retry > 0) {
317189749Sjhb				v86.ctl = V86_FLAGS;
318189749Sjhb				v86.addr = 0x13;
319189749Sjhb				v86.eax = 0;
320189749Sjhb				v86.edx = biosdev;
321189749Sjhb				v86int();
322189749Sjhb			}
323189749Sjhb
324227400Sjhb			packet.len = sizeof(struct edd_packet);
325189749Sjhb			packet.count = x;
326227400Sjhb			packet.off = VTOPOFF(xp);
327189749Sjhb			packet.seg = VTOPSEG(xp);
328189749Sjhb			packet.lba = dblk;
32986091Sjhb			v86.ctl = V86_FLAGS;
33086091Sjhb			v86.addr = 0x13;
331189749Sjhb			v86.eax = 0x4200;
33286091Sjhb			v86.edx = biosdev;
333189749Sjhb			v86.ds = VTOPSEG(&packet);
334189749Sjhb			v86.esi = VTOPOFF(&packet);
33586091Sjhb			v86int();
336229501Sjhb			result = V86_CY(v86.efl);
337189749Sjhb			if (result == 0)
338189749Sjhb				break;
33986091Sjhb		}
34086091Sjhb
34186091Sjhb#ifdef DISK_DEBUG
342189749Sjhb		error = (v86.eax >> 8) & 0xff;
34386091Sjhb#endif
344189749Sjhb		DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p,
345189749Sjhb		    VTOP(p), result ? "failed" : "ok");
346189749Sjhb		DEBUG("unit %d  status 0x%x", unit, error);
347189749Sjhb		if (bbuf != NULL)
348189749Sjhb			bcopy(bbuf, p, x * BIOSCD_SECSIZE);
349189749Sjhb		p += (x * BIOSCD_SECSIZE);
350189749Sjhb		dblk += x;
351189749Sjhb		resid -= x;
352189749Sjhb	}
35386091Sjhb
35486091Sjhb/*	hexdump(dest, (blks * BIOSCD_SECSIZE)); */
35586091Sjhb	return(0);
35686091Sjhb}
35786091Sjhb
35886091Sjhb/*
359130603Sphk * Return a suitable dev_t value for (dev).
36086091Sjhb */
36186091Sjhbint
36286091Sjhbbc_getdev(struct i386_devdesc *dev)
36386091Sjhb{
36486091Sjhb    int biosdev, unit;
36586091Sjhb    int major;
36686091Sjhb    int rootdev;
36786091Sjhb
368163897Smarcel    unit = dev->d_unit;
36986091Sjhb    biosdev = bc_unit2bios(unit);
37086091Sjhb    DEBUG("unit %d BIOS device %d", unit, biosdev);
37186091Sjhb    if (biosdev == -1)				/* not a BIOS device */
37286091Sjhb	return(-1);
37386091Sjhb
37486091Sjhb    /*
37586091Sjhb     * XXX: Need to examine device spec here to figure out if SCSI or
37686091Sjhb     * ATAPI.  No idea on how to figure out device number.  All we can
37786091Sjhb     * really pass to the kernel is what bus and device on which bus we
378130603Sphk     * were booted from, which dev_t isn't well suited to since those
37986091Sjhb     * number don't match to unit numbers very well.  We may just need
38086091Sjhb     * to engage in a hack where we pass -C to the boot args if we are
38186091Sjhb     * the boot device.
38286091Sjhb     */
38386091Sjhb    major = ACDMAJOR;
38486091Sjhb    unit = 0;	/* XXX */
38586091Sjhb
38686091Sjhb    /* XXX: Assume partition 'a'. */
387172921Sjhb    rootdev = MAKEBOOTDEV(major, 0, unit, 0);
38886091Sjhb    DEBUG("dev is 0x%x\n", rootdev);
38986091Sjhb    return(rootdev);
39086091Sjhb}
391