bioscd.c revision 113083
1303233Sdim/*- 2303233Sdim * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3353358Sdim * Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org> 4353358Sdim * All rights reserved. 5353358Sdim * 6303233Sdim * Redistribution and use in source and binary forms, with or without 7303233Sdim * modification, are permitted provided that the following conditions 8303233Sdim * are met: 9303233Sdim * 1. Redistributions of source code must retain the above copyright 10303233Sdim * notice, this list of conditions and the following disclaimer. 11303233Sdim * 2. Redistributions in binary form must reproduce the above copyright 12303233Sdim * notice, this list of conditions and the following disclaimer in the 13303233Sdim * documentation and/or other materials provided with the distribution. 14314564Sdim * 15303233Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16303233Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17303233Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18303233Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19303233Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20303233Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21303233Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22303233Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23303233Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24303233Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25303233Sdim * SUCH DAMAGE. 26303233Sdim * 27303233Sdim * $FreeBSD: head/sys/boot/i386/libi386/bioscd.c 113083 2003-04-04 16:35:16Z phk $ 28303233Sdim */ 29303233Sdim 30303233Sdim/* 31303233Sdim * BIOS CD device handling for CD's that have been booted off of via no 32303233Sdim * emulation booting as defined in the El Torito standard. 33303233Sdim * 34303233Sdim * Ideas and algorithms from: 35303233Sdim * 36303233Sdim * - FreeBSD libi386/biosdisk.c 37303233Sdim * 38303233Sdim */ 39303233Sdim 40303233Sdim#include <stand.h> 41303233Sdim 42303233Sdim#include <sys/param.h> 43303233Sdim#include <machine/bootinfo.h> 44303233Sdim#include <machine/psl.h> 45303233Sdim 46303233Sdim#include <stdarg.h> 47303233Sdim 48303233Sdim#include <bootstrap.h> 49303233Sdim#include <btxv86.h> 50303233Sdim#include "libi386.h" 51303233Sdim 52303233Sdim#define BIOSCD_SECSIZE 2048 53303233Sdim#define BUFSIZE (1 * BIOSCD_SECSIZE) 54303233Sdim#define MAXBCDEV 1 55303233Sdim 56303233Sdim/* Major numbers for devices we frontend for. */ 57303233Sdim#define ACDMAJOR 117 58303233Sdim#define CDMAJOR 15 59303233Sdim 60303233Sdim#ifdef DISK_DEBUG 61303233Sdim# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 62303233Sdim#else 63303233Sdim# define DEBUG(fmt, args...) 64303233Sdim#endif 65303233Sdim 66303233Sdimstruct specification_packet { 67303233Sdim u_char sp_size; 68303233Sdim u_char sp_bootmedia; 69303233Sdim u_char sp_drive; 70303233Sdim u_char sp_controller; 71303233Sdim u_int sp_lba; 72303233Sdim u_short sp_devicespec; 73303233Sdim u_short sp_buffersegment; 74303233Sdim u_short sp_loadsegment; 75303233Sdim u_short sp_sectorcount; 76303233Sdim u_short sp_cylsec; 77303233Sdim u_char sp_head; 78303233Sdim}; 79303233Sdim 80303233Sdim/* 81303233Sdim * List of BIOS devices, translation from disk unit number to 82303233Sdim * BIOS unit number. 83303233Sdim */ 84303233Sdimstatic struct bcinfo { 85303233Sdim int bc_unit; /* BIOS unit number */ 86303233Sdim struct specification_packet bc_sp; 87303233Sdim} bcinfo [MAXBCDEV]; 88303233Sdimstatic int nbcinfo = 0; 89303233Sdim 90303233Sdimstatic int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest); 91303233Sdimstatic int bc_init(void); 92303233Sdimstatic int bc_strategy(void *devdata, int flag, daddr_t dblk, 93303233Sdim size_t size, char *buf, size_t *rsize); 94303233Sdimstatic int bc_realstrategy(void *devdata, int flag, daddr_t dblk, 95303233Sdim size_t size, char *buf, size_t *rsize); 96303233Sdimstatic int bc_open(struct open_file *f, ...); 97303233Sdimstatic int bc_close(struct open_file *f); 98303233Sdimstatic void bc_print(int verbose); 99303233Sdim 100303233Sdimstruct devsw bioscd = { 101303233Sdim "cd", 102303233Sdim DEVT_CD, 103303233Sdim bc_init, 104303233Sdim bc_strategy, 105303233Sdim bc_open, 106303233Sdim bc_close, 107303233Sdim noioctl, 108303233Sdim bc_print, 109303233Sdim NULL 110303233Sdim}; 111303233Sdim 112303233Sdim/* 113303233Sdim * Translate between BIOS device numbers and our private unit numbers. 114303233Sdim */ 115303233Sdimint 116303233Sdimbc_bios2unit(int biosdev) 117303233Sdim{ 118303233Sdim int i; 119303233Sdim 120303233Sdim DEBUG("looking for bios device 0x%x", biosdev); 121303233Sdim for (i = 0; i < nbcinfo; i++) { 122303233Sdim DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit); 123303233Sdim if (bcinfo[i].bc_unit == biosdev) 124303233Sdim return(i); 125303233Sdim } 126303233Sdim return(-1); 127303233Sdim} 128303233Sdim 129303233Sdimint 130303233Sdimbc_unit2bios(int unit) 131303233Sdim{ 132303233Sdim if ((unit >= 0) && (unit < nbcinfo)) 133303233Sdim return(bcinfo[unit].bc_unit); 134303233Sdim return(-1); 135303233Sdim} 136303233Sdim 137303233Sdim/* 138303233Sdim * We can't quiz, we have to be told what device to use, so this functoin 139303233Sdim * doesn't do anything. Instead, the loader calls bc_add() with the BIOS 140303233Sdim * device number to add. 141303233Sdim */ 142303233Sdimstatic int 143303233Sdimbc_init(void) 144303233Sdim{ 145303233Sdim 146303233Sdim return (0); 147303233Sdim} 148303233Sdim 149303233Sdimint 150303233Sdimbc_add(int biosdev) 151303233Sdim{ 152303233Sdim 153303233Sdim if (nbcinfo >= MAXBCDEV) 154303233Sdim return (-1); 155303233Sdim bcinfo[nbcinfo].bc_unit = biosdev; 156303233Sdim v86.ctl = V86_FLAGS; 157303233Sdim v86.addr = 0x13; 158303233Sdim v86.eax = 0x4b01; 159303233Sdim v86.edx = biosdev; 160303233Sdim v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp); 161303233Sdim v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp); 162303233Sdim v86int(); 163303233Sdim if ((v86.eax & 0xff00) != 0) 164303233Sdim return (-1); 165303233Sdim 166303233Sdim printf("BIOS CD is cd%d\n", nbcinfo); 167303233Sdim nbcinfo++; 168303233Sdim return(0); 169303233Sdim} 170303233Sdim 171303233Sdim/* 172303233Sdim * Print information about disks 173303233Sdim */ 174303233Sdimstatic void 175303233Sdimbc_print(int verbose) 176303233Sdim{ 177303233Sdim int i; 178303233Sdim char line[80]; 179303233Sdim 180303233Sdim for (i = 0; i < nbcinfo; i++) { 181303233Sdim sprintf(line, " cd%d: Device 0x%x\n", i, 182303233Sdim bcinfo[i].bc_sp.sp_devicespec); 183303233Sdim pager_output(line); 184303233Sdim } 185303233Sdim} 186303233Sdim 187303233Sdim/* 188303233Sdim * Attempt to open the disk described by (dev) for use by (f). 189303233Sdim */ 190303233Sdimstatic int 191303233Sdimbc_open(struct open_file *f, ...) 192303233Sdim{ 193303233Sdim va_list ap; 194303233Sdim struct i386_devdesc *dev; 195303233Sdim int error; 196303233Sdim 197303233Sdim va_start(ap, f); 198303233Sdim dev = va_arg(ap, struct i386_devdesc *); 199303233Sdim va_end(ap); 200303233Sdim if (dev->d_kind.bioscd.unit >= nbcinfo) { 201303233Sdim DEBUG("attempt to open nonexistent disk"); 202303233Sdim return(ENXIO); 203303233Sdim } 204303233Sdim 205303233Sdim return(0); 206303233Sdim} 207303233Sdim 208303233Sdimstatic int 209303233Sdimbc_close(struct open_file *f) 210303233Sdim{ 211303233Sdim 212303233Sdim return(0); 213303233Sdim} 214303233Sdim 215303233Sdimstatic int 216303233Sdimbc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, 217303233Sdim size_t *rsize) 218303233Sdim{ 219303233Sdim struct i386_devdesc *dev; 220303233Sdim int unit; 221303233Sdim int blks; 222303233Sdim#ifdef BD_SUPPORT_FRAGS 223303233Sdim char fragbuf[BIOSCD_SECSIZE]; 224303233Sdim size_t fragsize; 225303233Sdim 226303233Sdim fragsize = size % BIOSCD_SECSIZE; 227303233Sdim#else 228303233Sdim if (size % BIOSCD_SECSIZE) 229303233Sdim return (EINVAL); 230303233Sdim#endif 231303233Sdim 232303233Sdim if (rw != F_READ) 233303233Sdim return(EROFS); 234303233Sdim dev = (struct i386_devdesc *)devdata; 235303233Sdim unit = dev->d_kind.bioscd.unit; 236303233Sdim blks = size / BIOSCD_SECSIZE; 237303233Sdim if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0) 238303233Sdim return (EINVAL); 239303233Sdim dblk /= (BIOSCD_SECSIZE / DEV_BSIZE); 240303233Sdim DEBUG("read %d from %d to %p", blks, dblk, buf); 241303233Sdim 242303233Sdim if (rsize) 243303233Sdim *rsize = 0; 244303233Sdim if (blks && bc_read(unit, dblk, blks, buf)) { 245303233Sdim DEBUG("read error"); 246303233Sdim return (EIO); 247303233Sdim } 248303233Sdim#ifdef BD_SUPPORT_FRAGS 249303233Sdim DEBUG("bc_strategy: frag read %d from %d+%d to %p", 250303233Sdim fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE)); 251303233Sdim if (fragsize && bc_read(unit, dblk + blks, 1, fragsize)) { 252303233Sdim DEBUG("frag read error"); 253303233Sdim return(EIO); 254303233Sdim } 255303233Sdim bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize); 256303233Sdim#endif 257303233Sdim if (rsize) 258303233Sdim *rsize = size; 259303233Sdim return (0); 260303233Sdim} 261303233Sdim 262303233Sdimstatic int 263303233Sdimbc_read(int unit, daddr_t dblk, int blks, caddr_t dest) 264303233Sdim{ 265303233Sdim u_int result, resid, retry; 266303233Sdim static unsigned short packet[8]; 267303233Sdim int biosdev; 268303233Sdim#ifdef DISK_DEBUG 269303233Sdim int error; 270303233Sdim#endif 271303233Sdim 272303233Sdim /* Just in case some idiot actually tries to read -1 blocks... */ 273303233Sdim if (blks < 0) 274303233Sdim return (-1); 275303233Sdim 276303233Sdim /* If nothing to do, just return succcess. */ 277303233Sdim if (blks == 0) 278303233Sdim return (0); 279303233Sdim 280303233Sdim biosdev = bc_unit2bios(unit); 281303233Sdim /* 282303233Sdim * Loop retrying the operation a couple of times. The BIOS 283303233Sdim * may also retry. 284 */ 285 for (retry = 0; retry < 3; retry++) { 286 /* If retrying, reset the drive */ 287 if (retry > 0) { 288 v86.ctl = V86_FLAGS; 289 v86.addr = 0x13; 290 v86.eax = 0; 291 v86.edx = biosdev; 292 v86int(); 293 } 294 295 packet[0] = 0x10; 296 packet[1] = blks; 297 packet[2] = VTOPOFF(dest); 298 packet[3] = VTOPSEG(dest); 299 packet[4] = dblk & 0xffff; 300 packet[5] = dblk >> 16; 301 packet[6] = 0; 302 packet[7] = 0; 303 v86.ctl = V86_FLAGS; 304 v86.addr = 0x13; 305 v86.eax = 0x4200; 306 v86.edx = biosdev; 307 v86.ds = VTOPSEG(packet); 308 v86.esi = VTOPOFF(packet); 309 v86int(); 310 result = (v86.efl & PSL_C); 311 if (result == 0) 312 break; 313 } 314 315#ifdef DISK_DEBUG 316 error = (v86.eax >> 8) & 0xff; 317#endif 318 DEBUG("%d sectors from %ld to %p (0x%x) %s", blks, dblk, dest, 319 VTOP(dest), result ? "failed" : "ok"); 320 DEBUG("unit %d status 0x%x", unit, error); 321 322/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */ 323 return(0); 324} 325 326/* 327 * Return a suitable dev_t value for (dev). 328 */ 329int 330bc_getdev(struct i386_devdesc *dev) 331{ 332 int biosdev, unit; 333 int major; 334 int rootdev; 335 336 unit = dev->d_kind.bioscd.unit; 337 biosdev = bc_unit2bios(unit); 338 DEBUG("unit %d BIOS device %d", unit, biosdev); 339 if (biosdev == -1) /* not a BIOS device */ 340 return(-1); 341 342 /* 343 * XXX: Need to examine device spec here to figure out if SCSI or 344 * ATAPI. No idea on how to figure out device number. All we can 345 * really pass to the kernel is what bus and device on which bus we 346 * were booted from, which dev_t isn't well suited to since those 347 * number don't match to unit numbers very well. We may just need 348 * to engage in a hack where we pass -C to the boot args if we are 349 * the boot device. 350 */ 351 major = ACDMAJOR; 352 unit = 0; /* XXX */ 353 354 /* XXX: Assume partition 'a'. */ 355 rootdev = MAKEBOOTDEV(major, 0, 0, unit, 0); 356 DEBUG("dev is 0x%x\n", rootdev); 357 return(rootdev); 358} 359