bioscd.c revision 119482
1/*- 2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3 * Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/sys/boot/i386/libi386/bioscd.c 119482 2003-08-25 23:28:32Z obrien $"); 30 31/* 32 * BIOS CD device handling for CD's that have been booted off of via no 33 * emulation booting as defined in the El Torito standard. 34 * 35 * Ideas and algorithms from: 36 * 37 * - FreeBSD libi386/biosdisk.c 38 * 39 */ 40 41#include <stand.h> 42 43#include <sys/param.h> 44#include <machine/bootinfo.h> 45#include <machine/psl.h> 46 47#include <stdarg.h> 48 49#include <bootstrap.h> 50#include <btxv86.h> 51#include "libi386.h" 52 53#define BIOSCD_SECSIZE 2048 54#define BUFSIZE (1 * BIOSCD_SECSIZE) 55#define MAXBCDEV 1 56 57/* Major numbers for devices we frontend for. */ 58#define ACDMAJOR 117 59#define CDMAJOR 15 60 61#ifdef DISK_DEBUG 62# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 63#else 64# define DEBUG(fmt, args...) 65#endif 66 67struct specification_packet { 68 u_char sp_size; 69 u_char sp_bootmedia; 70 u_char sp_drive; 71 u_char sp_controller; 72 u_int sp_lba; 73 u_short sp_devicespec; 74 u_short sp_buffersegment; 75 u_short sp_loadsegment; 76 u_short sp_sectorcount; 77 u_short sp_cylsec; 78 u_char sp_head; 79}; 80 81/* 82 * List of BIOS devices, translation from disk unit number to 83 * BIOS unit number. 84 */ 85static struct bcinfo { 86 int bc_unit; /* BIOS unit number */ 87 struct specification_packet bc_sp; 88} bcinfo [MAXBCDEV]; 89static int nbcinfo = 0; 90 91static int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest); 92static int bc_init(void); 93static int bc_strategy(void *devdata, int flag, daddr_t dblk, 94 size_t size, char *buf, size_t *rsize); 95static int bc_realstrategy(void *devdata, int flag, daddr_t dblk, 96 size_t size, char *buf, size_t *rsize); 97static int bc_open(struct open_file *f, ...); 98static int bc_close(struct open_file *f); 99static void bc_print(int verbose); 100 101struct devsw bioscd = { 102 "cd", 103 DEVT_CD, 104 bc_init, 105 bc_strategy, 106 bc_open, 107 bc_close, 108 noioctl, 109 bc_print, 110 NULL 111}; 112 113/* 114 * Translate between BIOS device numbers and our private unit numbers. 115 */ 116int 117bc_bios2unit(int biosdev) 118{ 119 int i; 120 121 DEBUG("looking for bios device 0x%x", biosdev); 122 for (i = 0; i < nbcinfo; i++) { 123 DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit); 124 if (bcinfo[i].bc_unit == biosdev) 125 return(i); 126 } 127 return(-1); 128} 129 130int 131bc_unit2bios(int unit) 132{ 133 if ((unit >= 0) && (unit < nbcinfo)) 134 return(bcinfo[unit].bc_unit); 135 return(-1); 136} 137 138/* 139 * We can't quiz, we have to be told what device to use, so this functoin 140 * doesn't do anything. Instead, the loader calls bc_add() with the BIOS 141 * device number to add. 142 */ 143static int 144bc_init(void) 145{ 146 147 return (0); 148} 149 150int 151bc_add(int biosdev) 152{ 153 154 if (nbcinfo >= MAXBCDEV) 155 return (-1); 156 bcinfo[nbcinfo].bc_unit = biosdev; 157 v86.ctl = V86_FLAGS; 158 v86.addr = 0x13; 159 v86.eax = 0x4b01; 160 v86.edx = biosdev; 161 v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp); 162 v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp); 163 v86int(); 164 if ((v86.eax & 0xff00) != 0) 165 return (-1); 166 167 printf("BIOS CD is cd%d\n", nbcinfo); 168 nbcinfo++; 169 return(0); 170} 171 172/* 173 * Print information about disks 174 */ 175static void 176bc_print(int verbose) 177{ 178 int i; 179 char line[80]; 180 181 for (i = 0; i < nbcinfo; i++) { 182 sprintf(line, " cd%d: Device 0x%x\n", i, 183 bcinfo[i].bc_sp.sp_devicespec); 184 pager_output(line); 185 } 186} 187 188/* 189 * Attempt to open the disk described by (dev) for use by (f). 190 */ 191static int 192bc_open(struct open_file *f, ...) 193{ 194 va_list ap; 195 struct i386_devdesc *dev; 196 int error; 197 198 va_start(ap, f); 199 dev = va_arg(ap, struct i386_devdesc *); 200 va_end(ap); 201 if (dev->d_kind.bioscd.unit >= nbcinfo) { 202 DEBUG("attempt to open nonexistent disk"); 203 return(ENXIO); 204 } 205 206 return(0); 207} 208 209static int 210bc_close(struct open_file *f) 211{ 212 213 return(0); 214} 215 216static int 217bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, 218 size_t *rsize) 219{ 220 struct i386_devdesc *dev; 221 int unit; 222 int blks; 223#ifdef BD_SUPPORT_FRAGS 224 char fragbuf[BIOSCD_SECSIZE]; 225 size_t fragsize; 226 227 fragsize = size % BIOSCD_SECSIZE; 228#else 229 if (size % BIOSCD_SECSIZE) 230 return (EINVAL); 231#endif 232 233 if (rw != F_READ) 234 return(EROFS); 235 dev = (struct i386_devdesc *)devdata; 236 unit = dev->d_kind.bioscd.unit; 237 blks = size / BIOSCD_SECSIZE; 238 if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0) 239 return (EINVAL); 240 dblk /= (BIOSCD_SECSIZE / DEV_BSIZE); 241 DEBUG("read %d from %d to %p", blks, dblk, buf); 242 243 if (rsize) 244 *rsize = 0; 245 if (blks && bc_read(unit, dblk, blks, buf)) { 246 DEBUG("read error"); 247 return (EIO); 248 } 249#ifdef BD_SUPPORT_FRAGS 250 DEBUG("bc_strategy: frag read %d from %d+%d to %p", 251 fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE)); 252 if (fragsize && bc_read(unit, dblk + blks, 1, fragsize)) { 253 DEBUG("frag read error"); 254 return(EIO); 255 } 256 bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize); 257#endif 258 if (rsize) 259 *rsize = size; 260 return (0); 261} 262 263static int 264bc_read(int unit, daddr_t dblk, int blks, caddr_t dest) 265{ 266 u_int result, resid, retry; 267 static unsigned short packet[8]; 268 int biosdev; 269#ifdef DISK_DEBUG 270 int error; 271#endif 272 273 /* Just in case some idiot actually tries to read -1 blocks... */ 274 if (blks < 0) 275 return (-1); 276 277 /* If nothing to do, just return succcess. */ 278 if (blks == 0) 279 return (0); 280 281 biosdev = bc_unit2bios(unit); 282 /* 283 * Loop retrying the operation a couple of times. The BIOS 284 * may also retry. 285 */ 286 for (retry = 0; retry < 3; retry++) { 287 /* If retrying, reset the drive */ 288 if (retry > 0) { 289 v86.ctl = V86_FLAGS; 290 v86.addr = 0x13; 291 v86.eax = 0; 292 v86.edx = biosdev; 293 v86int(); 294 } 295 296 packet[0] = 0x10; 297 packet[1] = blks; 298 packet[2] = VTOPOFF(dest); 299 packet[3] = VTOPSEG(dest); 300 packet[4] = dblk & 0xffff; 301 packet[5] = dblk >> 16; 302 packet[6] = 0; 303 packet[7] = 0; 304 v86.ctl = V86_FLAGS; 305 v86.addr = 0x13; 306 v86.eax = 0x4200; 307 v86.edx = biosdev; 308 v86.ds = VTOPSEG(packet); 309 v86.esi = VTOPOFF(packet); 310 v86int(); 311 result = (v86.efl & PSL_C); 312 if (result == 0) 313 break; 314 } 315 316#ifdef DISK_DEBUG 317 error = (v86.eax >> 8) & 0xff; 318#endif 319 DEBUG("%d sectors from %ld to %p (0x%x) %s", blks, dblk, dest, 320 VTOP(dest), result ? "failed" : "ok"); 321 DEBUG("unit %d status 0x%x", unit, error); 322 323/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */ 324 return(0); 325} 326 327/* 328 * Return a suitable dev_t value for (dev). 329 */ 330int 331bc_getdev(struct i386_devdesc *dev) 332{ 333 int biosdev, unit; 334 int major; 335 int rootdev; 336 337 unit = dev->d_kind.bioscd.unit; 338 biosdev = bc_unit2bios(unit); 339 DEBUG("unit %d BIOS device %d", unit, biosdev); 340 if (biosdev == -1) /* not a BIOS device */ 341 return(-1); 342 343 /* 344 * XXX: Need to examine device spec here to figure out if SCSI or 345 * ATAPI. No idea on how to figure out device number. All we can 346 * really pass to the kernel is what bus and device on which bus we 347 * were booted from, which dev_t isn't well suited to since those 348 * number don't match to unit numbers very well. We may just need 349 * to engage in a hack where we pass -C to the boot args if we are 350 * the boot device. 351 */ 352 major = ACDMAJOR; 353 unit = 0; /* XXX */ 354 355 /* XXX: Assume partition 'a'. */ 356 rootdev = MAKEBOOTDEV(major, 0, 0, unit, 0); 357 DEBUG("dev is 0x%x\n", rootdev); 358 return(rootdev); 359} 360