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