bioscd.c revision 130603
1254885Sdumbbell/*- 2254885Sdumbbell * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3254885Sdumbbell * Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org> 4254885Sdumbbell * All rights reserved. 5254885Sdumbbell * 6254885Sdumbbell * Redistribution and use in source and binary forms, with or without 7254885Sdumbbell * modification, are permitted provided that the following conditions 8254885Sdumbbell * are met: 9254885Sdumbbell * 1. Redistributions of source code must retain the above copyright 10254885Sdumbbell * notice, this list of conditions and the following disclaimer. 11254885Sdumbbell * 2. Redistributions in binary form must reproduce the above copyright 12254885Sdumbbell * notice, this list of conditions and the following disclaimer in the 13254885Sdumbbell * documentation and/or other materials provided with the distribution. 14254885Sdumbbell * 15254885Sdumbbell * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16254885Sdumbbell * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17254885Sdumbbell * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18254885Sdumbbell * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19254885Sdumbbell * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20254885Sdumbbell * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21254885Sdumbbell * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22254885Sdumbbell * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23254885Sdumbbell * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24254885Sdumbbell * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25254885Sdumbbell * SUCH DAMAGE. 26254885Sdumbbell */ 27254885Sdumbbell 28254885Sdumbbell#include <sys/cdefs.h> 29254885Sdumbbell__FBSDID("$FreeBSD: head/sys/boot/i386/libi386/bioscd.c 130603 2004-06-16 18:21:22Z phk $"); 30254885Sdumbbell 31254885Sdumbbell/* 32254885Sdumbbell * BIOS CD device handling for CD's that have been booted off of via no 33254885Sdumbbell * emulation booting as defined in the El Torito standard. 34254885Sdumbbell * 35254885Sdumbbell * Ideas and algorithms from: 36254885Sdumbbell * 37254885Sdumbbell * - FreeBSD libi386/biosdisk.c 38254885Sdumbbell * 39254885Sdumbbell */ 40254885Sdumbbell 41254885Sdumbbell#include <stand.h> 42254885Sdumbbell 43254885Sdumbbell#include <sys/param.h> 44263817Sray#include <machine/bootinfo.h> 45254885Sdumbbell#include <machine/psl.h> 46254885Sdumbbell 47254885Sdumbbell#include <stdarg.h> 48254885Sdumbbell 49254885Sdumbbell#include <bootstrap.h> 50254885Sdumbbell#include <btxv86.h> 51254885Sdumbbell#include "libi386.h" 52254885Sdumbbell 53254885Sdumbbell#define BIOSCD_SECSIZE 2048 54254885Sdumbbell#define BUFSIZE (1 * BIOSCD_SECSIZE) 55254885Sdumbbell#define MAXBCDEV 1 56254885Sdumbbell 57254885Sdumbbell/* Major numbers for devices we frontend for. */ 58254885Sdumbbell#define ACDMAJOR 117 59254885Sdumbbell#define CDMAJOR 15 60254885Sdumbbell 61254885Sdumbbell#ifdef DISK_DEBUG 62254885Sdumbbell# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 63254885Sdumbbell#else 64254885Sdumbbell# define DEBUG(fmt, args...) 65254885Sdumbbell#endif 66254885Sdumbbell 67254885Sdumbbellstruct specification_packet { 68254885Sdumbbell u_char sp_size; 69254885Sdumbbell u_char sp_bootmedia; 70254885Sdumbbell u_char sp_drive; 71254885Sdumbbell u_char sp_controller; 72254885Sdumbbell u_int sp_lba; 73254885Sdumbbell u_short sp_devicespec; 74254885Sdumbbell u_short sp_buffersegment; 75254885Sdumbbell u_short sp_loadsegment; 76254885Sdumbbell u_short sp_sectorcount; 77254885Sdumbbell u_short sp_cylsec; 78254885Sdumbbell u_char sp_head; 79254885Sdumbbell}; 80254885Sdumbbell 81254885Sdumbbell/* 82254885Sdumbbell * List of BIOS devices, translation from disk unit number to 83254885Sdumbbell * BIOS unit number. 84254885Sdumbbell */ 85254885Sdumbbellstatic struct bcinfo { 86254885Sdumbbell int bc_unit; /* BIOS unit number */ 87254885Sdumbbell struct specification_packet bc_sp; 88267430Sdumbbell} bcinfo [MAXBCDEV]; 89267430Sdumbbellstatic int nbcinfo = 0; 90267430Sdumbbell 91267430Sdumbbellstatic int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest); 92254885Sdumbbellstatic int bc_init(void); 93254885Sdumbbellstatic int bc_strategy(void *devdata, int flag, daddr_t dblk, 94254885Sdumbbell size_t size, char *buf, size_t *rsize); 95254885Sdumbbellstatic int bc_realstrategy(void *devdata, int flag, daddr_t dblk, 96254885Sdumbbell size_t size, char *buf, size_t *rsize); 97254885Sdumbbellstatic int bc_open(struct open_file *f, ...); 98254885Sdumbbellstatic int bc_close(struct open_file *f); 99254885Sdumbbellstatic void bc_print(int verbose); 100254885Sdumbbell 101254885Sdumbbellstruct devsw bioscd = { 102254885Sdumbbell "cd", 103254885Sdumbbell DEVT_CD, 104254885Sdumbbell bc_init, 105254885Sdumbbell bc_strategy, 106254885Sdumbbell bc_open, 107254885Sdumbbell bc_close, 108254885Sdumbbell noioctl, 109254885Sdumbbell bc_print, 110254885Sdumbbell NULL 111254885Sdumbbell}; 112254885Sdumbbell 113254885Sdumbbell/* 114254885Sdumbbell * Translate between BIOS device numbers and our private unit numbers. 115254885Sdumbbell */ 116254885Sdumbbellint 117254885Sdumbbellbc_bios2unit(int biosdev) 118254885Sdumbbell{ 119254885Sdumbbell int i; 120254885Sdumbbell 121254885Sdumbbell DEBUG("looking for bios device 0x%x", biosdev); 122254885Sdumbbell for (i = 0; i < nbcinfo; i++) { 123254885Sdumbbell DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit); 124254885Sdumbbell if (bcinfo[i].bc_unit == biosdev) 125254885Sdumbbell return(i); 126254885Sdumbbell } 127254885Sdumbbell return(-1); 128254885Sdumbbell} 129254885Sdumbbell 130254885Sdumbbellint 131254885Sdumbbellbc_unit2bios(int unit) 132254885Sdumbbell{ 133254885Sdumbbell if ((unit >= 0) && (unit < nbcinfo)) 134254885Sdumbbell return(bcinfo[unit].bc_unit); 135254885Sdumbbell return(-1); 136254885Sdumbbell} 137254885Sdumbbell 138254885Sdumbbell/* 139254885Sdumbbell * We can't quiz, we have to be told what device to use, so this functoin 140254885Sdumbbell * doesn't do anything. Instead, the loader calls bc_add() with the BIOS 141254885Sdumbbell * device number to add. 142254885Sdumbbell */ 143254885Sdumbbellstatic int 144254885Sdumbbellbc_init(void) 145254885Sdumbbell{ 146254885Sdumbbell 147254885Sdumbbell return (0); 148254885Sdumbbell} 149254885Sdumbbell 150254885Sdumbbellint 151254885Sdumbbellbc_add(int biosdev) 152254885Sdumbbell{ 153254885Sdumbbell 154254885Sdumbbell if (nbcinfo >= MAXBCDEV) 155254885Sdumbbell return (-1); 156254885Sdumbbell bcinfo[nbcinfo].bc_unit = biosdev; 157254885Sdumbbell v86.ctl = V86_FLAGS; 158254885Sdumbbell v86.addr = 0x13; 159254885Sdumbbell v86.eax = 0x4b01; 160254885Sdumbbell v86.edx = biosdev; 161254885Sdumbbell v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp); 162254885Sdumbbell v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp); 163254885Sdumbbell v86int(); 164254885Sdumbbell if ((v86.eax & 0xff00) != 0) 165254885Sdumbbell return (-1); 166254885Sdumbbell 167254885Sdumbbell printf("BIOS CD is cd%d\n", nbcinfo); 168254885Sdumbbell nbcinfo++; 169254885Sdumbbell return(0); 170254885Sdumbbell} 171254885Sdumbbell 172254885Sdumbbell/* 173254885Sdumbbell * Print information about disks 174254885Sdumbbell */ 175254885Sdumbbellstatic void 176254885Sdumbbellbc_print(int verbose) 177254885Sdumbbell{ 178254885Sdumbbell int i; 179254885Sdumbbell char line[80]; 180254885Sdumbbell 181254885Sdumbbell for (i = 0; i < nbcinfo; i++) { 182254885Sdumbbell sprintf(line, " cd%d: Device 0x%x\n", i, 183254885Sdumbbell bcinfo[i].bc_sp.sp_devicespec); 184254885Sdumbbell pager_output(line); 185254885Sdumbbell } 186254885Sdumbbell} 187254885Sdumbbell 188254885Sdumbbell/* 189254885Sdumbbell * Attempt to open the disk described by (dev) for use by (f). 190254885Sdumbbell */ 191254885Sdumbbellstatic int 192254885Sdumbbellbc_open(struct open_file *f, ...) 193254885Sdumbbell{ 194254885Sdumbbell va_list ap; 195254885Sdumbbell struct i386_devdesc *dev; 196254885Sdumbbell int error; 197254885Sdumbbell 198254885Sdumbbell va_start(ap, f); 199254885Sdumbbell dev = va_arg(ap, struct i386_devdesc *); 200254885Sdumbbell va_end(ap); 201254885Sdumbbell if (dev->d_kind.bioscd.unit >= nbcinfo) { 202254885Sdumbbell DEBUG("attempt to open nonexistent disk"); 203254885Sdumbbell return(ENXIO); 204254885Sdumbbell } 205254885Sdumbbell 206254885Sdumbbell return(0); 207254885Sdumbbell} 208254885Sdumbbell 209254885Sdumbbellstatic int 210254885Sdumbbellbc_close(struct open_file *f) 211254885Sdumbbell{ 212254885Sdumbbell 213254885Sdumbbell return(0); 214254885Sdumbbell} 215254885Sdumbbell 216254885Sdumbbellstatic int 217254885Sdumbbellbc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, 218254885Sdumbbell size_t *rsize) 219254885Sdumbbell{ 220254885Sdumbbell struct i386_devdesc *dev; 221254885Sdumbbell int unit; 222254885Sdumbbell int blks; 223254885Sdumbbell#ifdef BD_SUPPORT_FRAGS 224254885Sdumbbell char fragbuf[BIOSCD_SECSIZE]; 225254885Sdumbbell size_t fragsize; 226254885Sdumbbell 227254885Sdumbbell fragsize = size % BIOSCD_SECSIZE; 228254885Sdumbbell#else 229254885Sdumbbell if (size % BIOSCD_SECSIZE) 230254885Sdumbbell return (EINVAL); 231254885Sdumbbell#endif 232254885Sdumbbell 233254885Sdumbbell if (rw != F_READ) 234254885Sdumbbell return(EROFS); 235254885Sdumbbell dev = (struct i386_devdesc *)devdata; 236254885Sdumbbell unit = dev->d_kind.bioscd.unit; 237254885Sdumbbell blks = size / BIOSCD_SECSIZE; 238254885Sdumbbell if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0) 239254885Sdumbbell return (EINVAL); 240254885Sdumbbell dblk /= (BIOSCD_SECSIZE / DEV_BSIZE); 241254885Sdumbbell DEBUG("read %d from %d to %p", blks, dblk, buf); 242254885Sdumbbell 243254885Sdumbbell if (rsize) 244254885Sdumbbell *rsize = 0; 245254885Sdumbbell if (blks && bc_read(unit, dblk, blks, buf)) { 246254885Sdumbbell DEBUG("read error"); 247254885Sdumbbell return (EIO); 248254885Sdumbbell } 249254885Sdumbbell#ifdef BD_SUPPORT_FRAGS 250254885Sdumbbell DEBUG("bc_strategy: frag read %d from %d+%d to %p", 251254885Sdumbbell fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE)); 252254885Sdumbbell if (fragsize && bc_read(unit, dblk + blks, 1, fragsize)) { 253254885Sdumbbell DEBUG("frag read error"); 254254885Sdumbbell return(EIO); 255254885Sdumbbell } 256254885Sdumbbell bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize); 257254885Sdumbbell#endif 258254885Sdumbbell if (rsize) 259254885Sdumbbell *rsize = size; 260254885Sdumbbell return (0); 261254885Sdumbbell} 262254885Sdumbbell 263254885Sdumbbellstatic int 264254885Sdumbbellbc_read(int unit, daddr_t dblk, int blks, caddr_t dest) 265254885Sdumbbell{ 266254885Sdumbbell u_int result, resid, retry; 267254885Sdumbbell static unsigned short packet[8]; 268254885Sdumbbell int biosdev; 269254885Sdumbbell#ifdef DISK_DEBUG 270254885Sdumbbell int error; 271254885Sdumbbell#endif 272254885Sdumbbell 273254885Sdumbbell /* Just in case some idiot actually tries to read -1 blocks... */ 274254885Sdumbbell if (blks < 0) 275254885Sdumbbell return (-1); 276254885Sdumbbell 277254885Sdumbbell /* If nothing to do, just return succcess. */ 278254885Sdumbbell if (blks == 0) 279254885Sdumbbell return (0); 280254885Sdumbbell 281254885Sdumbbell biosdev = bc_unit2bios(unit); 282254885Sdumbbell /* 283254885Sdumbbell * Loop retrying the operation a couple of times. The BIOS 284254885Sdumbbell * may also retry. 285254885Sdumbbell */ 286254885Sdumbbell for (retry = 0; retry < 3; retry++) { 287254885Sdumbbell /* If retrying, reset the drive */ 288254885Sdumbbell if (retry > 0) { 289254885Sdumbbell v86.ctl = V86_FLAGS; 290254885Sdumbbell v86.addr = 0x13; 291254885Sdumbbell v86.eax = 0; 292254885Sdumbbell v86.edx = biosdev; 293254885Sdumbbell v86int(); 294254885Sdumbbell } 295254885Sdumbbell 296254885Sdumbbell packet[0] = 0x10; 297254885Sdumbbell packet[1] = blks; 298254885Sdumbbell packet[2] = VTOPOFF(dest); 299254885Sdumbbell packet[3] = VTOPSEG(dest); 300254885Sdumbbell packet[4] = dblk & 0xffff; 301254885Sdumbbell packet[5] = dblk >> 16; 302254885Sdumbbell packet[6] = 0; 303254885Sdumbbell packet[7] = 0; 304254885Sdumbbell v86.ctl = V86_FLAGS; 305254885Sdumbbell v86.addr = 0x13; 306254885Sdumbbell v86.eax = 0x4200; 307254885Sdumbbell v86.edx = biosdev; 308254885Sdumbbell v86.ds = VTOPSEG(packet); 309254885Sdumbbell v86.esi = VTOPOFF(packet); 310254885Sdumbbell v86int(); 311254885Sdumbbell result = (v86.efl & PSL_C); 312254885Sdumbbell if (result == 0) 313254885Sdumbbell break; 314254885Sdumbbell } 315254885Sdumbbell 316254885Sdumbbell#ifdef DISK_DEBUG 317254885Sdumbbell error = (v86.eax >> 8) & 0xff; 318254885Sdumbbell#endif 319254885Sdumbbell DEBUG("%d sectors from %ld to %p (0x%x) %s", blks, dblk, dest, 320254885Sdumbbell VTOP(dest), result ? "failed" : "ok"); 321254885Sdumbbell DEBUG("unit %d status 0x%x", unit, error); 322254885Sdumbbell 323254885Sdumbbell/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */ 324254885Sdumbbell return(0); 325254885Sdumbbell} 326254885Sdumbbell 327254885Sdumbbell/* 328254885Sdumbbell * Return a suitable dev_t value for (dev). 329254885Sdumbbell */ 330254885Sdumbbellint 331254885Sdumbbellbc_getdev(struct i386_devdesc *dev) 332254885Sdumbbell{ 333254885Sdumbbell int biosdev, unit; 334254885Sdumbbell int major; 335254885Sdumbbell int rootdev; 336254885Sdumbbell 337254885Sdumbbell unit = dev->d_kind.bioscd.unit; 338254885Sdumbbell biosdev = bc_unit2bios(unit); 339254885Sdumbbell DEBUG("unit %d BIOS device %d", unit, biosdev); 340254885Sdumbbell if (biosdev == -1) /* not a BIOS device */ 341254885Sdumbbell return(-1); 342254885Sdumbbell 343254885Sdumbbell /* 344254885Sdumbbell * XXX: Need to examine device spec here to figure out if SCSI or 345254885Sdumbbell * ATAPI. No idea on how to figure out device number. All we can 346263170Sdumbbell * really pass to the kernel is what bus and device on which bus we 347263170Sdumbbell * were booted from, which dev_t isn't well suited to since those 348263170Sdumbbell * number don't match to unit numbers very well. We may just need 349263170Sdumbbell * to engage in a hack where we pass -C to the boot args if we are 350263170Sdumbbell * the boot device. 351263170Sdumbbell */ 352254885Sdumbbell major = ACDMAJOR; 353254885Sdumbbell unit = 0; /* XXX */ 354254885Sdumbbell 355254885Sdumbbell /* XXX: Assume partition 'a'. */ 356254885Sdumbbell rootdev = MAKEBOOTDEV(major, 0, 0, unit, 0); 357254885Sdumbbell DEBUG("dev is 0x%x\n", rootdev); 358254885Sdumbbell return(rootdev); 359254885Sdumbbell} 360254885Sdumbbell