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 30 /* 31 * BIOS CD device handling for CD's that have been booted off of via no 32 * emulation booting as defined in the El Torito standard. 33 * 34 * Ideas and algorithms from: 35 * 36 * - FreeBSD libi386/biosdisk.c 37 * 38 */ 39 40 #include <stand.h> 41 42 #include <sys/param.h> 43 #include <machine/bootinfo.h> 44 45 #include <stdarg.h> 46 47 #include <bootstrap.h> 48 #include <btxv86.h> 49 #include <edd.h> 50 #include "libi386.h" 51 52 #define BIOSCD_SECSIZE 2048 53 #define BUFSIZE (1 * BIOSCD_SECSIZE) 54 #define MAXBCDEV 1 55 56 /* Major numbers for devices we frontend for. */ 57 #define ACDMAJOR 117 58 #define CDMAJOR 15 59 60 #ifdef DISK_DEBUG 61 # define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 62 #else 63 # define DEBUG(fmt, args...) 64 #endif 65 66 struct specification_packet { 67 u_char sp_size; 68 u_char sp_bootmedia; 69 u_char sp_drive; 70 u_char sp_controller; 71 u_int sp_lba; 72 u_short sp_devicespec; 73 u_short sp_buffersegment; 74 u_short sp_loadsegment; 75 u_short sp_sectorcount; 76 u_short sp_cylsec; 77 u_char sp_head; 78 }; 79 80 /* 81 * List of BIOS devices, translation from disk unit number to 82 * BIOS unit number. 83 */ 84 static struct bcinfo { 85 int bc_unit; /* BIOS unit number */ 86 struct specification_packet bc_sp; 87 int bc_open; /* reference counter */ 88 void *bc_bcache; /* buffer cache data */ 89 } bcinfo [MAXBCDEV]; 90 static int nbcinfo = 0; 91 92 #define BC(dev) (bcinfo[(dev)->d_unit]) 93 94 static int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest); 95 static int bc_init(void); 96 static int bc_strategy(void *devdata, int flag, daddr_t dblk, 97 size_t size, char *buf, size_t *rsize); 98 static int bc_realstrategy(void *devdata, int flag, daddr_t dblk, 99 size_t size, char *buf, size_t *rsize); 100 static int bc_open(struct open_file *f, ...); 101 static int bc_close(struct open_file *f); 102 static int bc_print(int verbose); 103 104 struct devsw bioscd = { 105 "cd", 106 DEVT_CD, 107 bc_init, 108 bc_strategy, 109 bc_open, 110 bc_close, 111 noioctl, 112 bc_print, 113 NULL 114 }; 115 116 /* 117 * Translate between BIOS device numbers and our private unit numbers. 118 */ 119 int 120 bc_bios2unit(int biosdev) 121 { 122 int i; 123 124 DEBUG("looking for bios device 0x%x", biosdev); 125 for (i = 0; i < nbcinfo; i++) { 126 DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit); 127 if (bcinfo[i].bc_unit == biosdev) 128 return(i); 129 } 130 return(-1); 131 } 132 133 int 134 bc_unit2bios(int unit) 135 { 136 if ((unit >= 0) && (unit < nbcinfo)) 137 return(bcinfo[unit].bc_unit); 138 return(-1); 139 } 140 141 /* 142 * We can't quiz, we have to be told what device to use, so this functoin 143 * doesn't do anything. Instead, the loader calls bc_add() with the BIOS 144 * device number to add. 145 */ 146 static int 147 bc_init(void) 148 { 149 150 return (0); 151 } 152 153 int 154 bc_add(int biosdev) 155 { 156 157 if (nbcinfo >= MAXBCDEV) 158 return (-1); 159 bcinfo[nbcinfo].bc_unit = biosdev; 160 v86.ctl = V86_FLAGS; 161 v86.addr = 0x13; 162 v86.eax = 0x4b01; 163 v86.edx = biosdev; 164 v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp); 165 v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp); 166 v86int(); 167 if ((v86.eax & 0xff00) != 0) 168 return (-1); 169 170 printf("BIOS CD is cd%d\n", nbcinfo); 171 nbcinfo++; 172 bcache_add_dev(nbcinfo); /* register cd device in bcache */ 173 return(0); 174 } 175 176 /* 177 * Print information about disks 178 */ 179 static int 180 bc_print(int verbose) 181 { 182 char line[80]; 183 int i, ret = 0; 184 185 if (nbcinfo == 0) 186 return (0); 187 188 printf("%s devices:", bioscd.dv_name); 189 if ((ret = pager_output("\n")) != 0) 190 return (ret); 191 192 for (i = 0; i < nbcinfo; i++) { 193 sprintf(line, " cd%d: Device 0x%x\n", i, 194 bcinfo[i].bc_sp.sp_devicespec); 195 ret = pager_output(line); 196 if (ret != 0) 197 return (ret); 198 } 199 return (ret); 200 } 201 202 /* 203 * Attempt to open the disk described by (dev) for use by (f). 204 */ 205 static int 206 bc_open(struct open_file *f, ...) 207 { 208 va_list ap; 209 struct i386_devdesc *dev; 210 211 va_start(ap, f); 212 dev = va_arg(ap, struct i386_devdesc *); 213 va_end(ap); 214 if (dev->d_unit >= nbcinfo) { 215 DEBUG("attempt to open nonexistent disk"); 216 return(ENXIO); 217 } 218 219 BC(dev).bc_open++; 220 if (BC(dev).bc_bcache == NULL) 221 BC(dev).bc_bcache = bcache_allocate(); 222 return(0); 223 } 224 225 static int 226 bc_close(struct open_file *f) 227 { 228 struct i386_devdesc *dev; 229 230 dev = (struct i386_devdesc *)f->f_devdata; 231 BC(dev).bc_open--; 232 if (BC(dev).bc_open == 0) { 233 bcache_free(BC(dev).bc_bcache); 234 BC(dev).bc_bcache = NULL; 235 } 236 return(0); 237 } 238 239 static int 240 bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, 241 char *buf, size_t *rsize) 242 { 243 struct bcache_devdata bcd; 244 struct i386_devdesc *dev; 245 246 dev = (struct i386_devdesc *)devdata; 247 bcd.dv_strategy = bc_realstrategy; 248 bcd.dv_devdata = devdata; 249 bcd.dv_cache = BC(dev).bc_bcache; 250 251 return (bcache_strategy(&bcd, rw, dblk, size, buf, rsize)); 252 } 253 254 static int 255 bc_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, 256 char *buf, size_t *rsize) 257 { 258 struct i386_devdesc *dev; 259 int unit; 260 int blks; 261 #ifdef BD_SUPPORT_FRAGS 262 char fragbuf[BIOSCD_SECSIZE]; 263 size_t fragsize; 264 265 fragsize = size % BIOSCD_SECSIZE; 266 #else 267 if (size % BIOSCD_SECSIZE) 268 return (EINVAL); 269 #endif 270 271 if ((rw & F_MASK) != F_READ) 272 return(EROFS); 273 dev = (struct i386_devdesc *)devdata; 274 unit = dev->d_unit; 275 blks = size / BIOSCD_SECSIZE; 276 if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0) 277 return (EINVAL); 278 dblk /= (BIOSCD_SECSIZE / DEV_BSIZE); 279 DEBUG("read %d from %lld to %p", blks, dblk, buf); 280 281 if (rsize) 282 *rsize = 0; 283 if ((blks = bc_read(unit, dblk, blks, buf)) < 0) { 284 DEBUG("read error"); 285 return (EIO); 286 } else { 287 if (size / BIOSCD_SECSIZE > blks) { 288 if (rsize) 289 *rsize = blks * BIOSCD_SECSIZE; 290 return (0); 291 } 292 } 293 #ifdef BD_SUPPORT_FRAGS 294 DEBUG("frag read %d from %lld+%d to %p", 295 fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE)); 296 if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf) != 1) { 297 if (blks) { 298 if (rsize) 299 *rsize = blks * BIOSCD_SECSIZE; 300 return (0); 301 } 302 DEBUG("frag read error"); 303 return(EIO); 304 } 305 bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize); 306 #endif 307 if (rsize) 308 *rsize = size; 309 return (0); 310 } 311 312 /* return negative value for an error, otherwise blocks read */ 313 static int 314 bc_read(int unit, daddr_t dblk, int blks, caddr_t dest) 315 { 316 u_int maxfer, resid, result, retry, x; 317 caddr_t bbuf, p, xp; 318 static struct edd_packet packet; 319 int biosdev; 320 #ifdef DISK_DEBUG 321 int error; 322 #endif 323 324 /* Just in case some idiot actually tries to read -1 blocks... */ 325 if (blks < 0) 326 return (-1); 327 328 /* If nothing to do, just return succcess. */ 329 if (blks == 0) 330 return (0); 331 332 /* Decide whether we have to bounce */ 333 if (VTOP(dest) >> 20 != 0) { 334 /* 335 * The destination buffer is above first 1MB of 336 * physical memory so we have to arrange a suitable 337 * bounce buffer. 338 */ 339 x = V86_IO_BUFFER_SIZE / BIOSCD_SECSIZE; 340 if (x == 0) 341 panic("BUG: Real mode buffer is too small\n"); 342 x = min(x, (unsigned)blks); 343 bbuf = PTOV(V86_IO_BUFFER); 344 maxfer = x; 345 } else { 346 bbuf = NULL; 347 maxfer = 0; 348 } 349 350 biosdev = bc_unit2bios(unit); 351 resid = blks; 352 p = dest; 353 354 while (resid > 0) { 355 if (bbuf) 356 xp = bbuf; 357 else 358 xp = p; 359 x = resid; 360 if (maxfer > 0) 361 x = min(x, maxfer); 362 363 /* 364 * Loop retrying the operation a couple of times. The BIOS 365 * may also retry. 366 */ 367 for (retry = 0; retry < 3; retry++) { 368 /* If retrying, reset the drive */ 369 if (retry > 0) { 370 v86.ctl = V86_FLAGS; 371 v86.addr = 0x13; 372 v86.eax = 0; 373 v86.edx = biosdev; 374 v86int(); 375 } 376 377 packet.len = sizeof(struct edd_packet); 378 packet.count = x; 379 packet.off = VTOPOFF(xp); 380 packet.seg = VTOPSEG(xp); 381 packet.lba = dblk; 382 v86.ctl = V86_FLAGS; 383 v86.addr = 0x13; 384 v86.eax = 0x4200; 385 v86.edx = biosdev; 386 v86.ds = VTOPSEG(&packet); 387 v86.esi = VTOPOFF(&packet); 388 v86int(); 389 result = V86_CY(v86.efl); 390 if (result == 0) 391 break; 392 /* fall back to 1 sector read */ 393 x = 1; 394 } 395 396 #ifdef DISK_DEBUG 397 error = (v86.eax >> 8) & 0xff; 398 #endif 399 DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p, 400 VTOP(p), result ? "failed" : "ok"); 401 DEBUG("unit %d status 0x%x", unit, error); 402 403 /* still an error? break off */ 404 if (result != 0) 405 break; 406 407 if (bbuf != NULL) 408 bcopy(bbuf, p, x * BIOSCD_SECSIZE); 409 p += (x * BIOSCD_SECSIZE); 410 dblk += x; 411 resid -= x; 412 } 413 414 /* hexdump(dest, (blks * BIOSCD_SECSIZE)); */ 415 416 if (blks - resid == 0) 417 return (-1); /* read failed */ 418 419 return (blks - resid); 420 } 421 422 /* 423 * Return a suitable dev_t value for (dev). 424 */ 425 int 426 bc_getdev(struct i386_devdesc *dev) 427 { 428 int biosdev, unit; 429 int major; 430 int rootdev; 431 432 unit = dev->d_unit; 433 biosdev = bc_unit2bios(unit); 434 DEBUG("unit %d BIOS device %d", unit, biosdev); 435 if (biosdev == -1) /* not a BIOS device */ 436 return(-1); 437 438 /* 439 * XXX: Need to examine device spec here to figure out if SCSI or 440 * ATAPI. No idea on how to figure out device number. All we can 441 * really pass to the kernel is what bus and device on which bus we 442 * were booted from, which dev_t isn't well suited to since those 443 * number don't match to unit numbers very well. We may just need 444 * to engage in a hack where we pass -C to the boot args if we are 445 * the boot device. 446 */ 447 major = ACDMAJOR; 448 unit = 0; /* XXX */ 449 450 /* XXX: Assume partition 'a'. */ 451 rootdev = MAKEBOOTDEV(major, 0, unit, 0); 452 DEBUG("dev is 0x%x\n", rootdev); 453 return(rootdev); 454 } 455