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