1208926Smjacob/*- 2208926Smjacob * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3208926Smjacob * Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org> 4208926Smjacob * All rights reserved. 5208926Smjacob * 6208926Smjacob * Redistribution and use in source and binary forms, with or without 7208926Smjacob * 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$"); 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 46#include <stdarg.h> 47 48#include <bootstrap.h> 49#include <btxv86.h> 50#include <edd.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_open(struct open_file *f, ...); 96static int bc_close(struct open_file *f); 97static void bc_print(int verbose); 98 99struct devsw bioscd = { 100 "cd", 101 DEVT_CD, 102 bc_init, 103 bc_strategy, 104 bc_open, 105 bc_close, 106 noioctl, 107 bc_print, 108 NULL 109}; 110 111/* 112 * Translate between BIOS device numbers and our private unit numbers. 113 */ 114int 115bc_bios2unit(int biosdev) 116{ 117 int i; 118 119 DEBUG("looking for bios device 0x%x", biosdev); 120 for (i = 0; i < nbcinfo; i++) { 121 DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit); 122 if (bcinfo[i].bc_unit == biosdev) 123 return(i); 124 } 125 return(-1); 126} 127 128int 129bc_unit2bios(int unit) 130{ 131 if ((unit >= 0) && (unit < nbcinfo)) 132 return(bcinfo[unit].bc_unit); 133 return(-1); 134} 135 136/* 137 * We can't quiz, we have to be told what device to use, so this functoin 138 * doesn't do anything. Instead, the loader calls bc_add() with the BIOS 139 * device number to add. 140 */ 141static int 142bc_init(void) 143{ 144 145 return (0); 146} 147 148int 149bc_add(int biosdev) 150{ 151 152 if (nbcinfo >= MAXBCDEV) 153 return (-1); 154 bcinfo[nbcinfo].bc_unit = biosdev; 155 v86.ctl = V86_FLAGS; 156 v86.addr = 0x13; 157 v86.eax = 0x4b01; 158 v86.edx = biosdev; 159 v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp); 160 v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp); 161 v86int(); 162 if ((v86.eax & 0xff00) != 0) 163 return (-1); 164 165 printf("BIOS CD is cd%d\n", nbcinfo); 166 nbcinfo++; 167 return(0); 168} 169 170/* 171 * Print information about disks 172 */ 173static void 174bc_print(int verbose) 175{ 176 char line[80]; 177 int i; 178 179 for (i = 0; i < nbcinfo; i++) { 180 sprintf(line, " cd%d: Device 0x%x\n", i, 181 bcinfo[i].bc_sp.sp_devicespec); 182 pager_output(line); 183 } 184} 185 186/* 187 * Attempt to open the disk described by (dev) for use by (f). 188 */ 189static int 190bc_open(struct open_file *f, ...) 191{ 192 va_list ap; 193 struct i386_devdesc *dev; 194 195 va_start(ap, f); 196 dev = va_arg(ap, struct i386_devdesc *); 197 va_end(ap); 198 if (dev->d_unit >= nbcinfo) { 199 DEBUG("attempt to open nonexistent disk"); 200 return(ENXIO); 201 } 202 203 return(0); 204} 205 206static int 207bc_close(struct open_file *f) 208{ 209 210 return(0); 211} 212 213static int 214bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, 215 size_t *rsize) 216{ 217 struct i386_devdesc *dev; 218 int unit; 219 int blks; 220#ifdef BD_SUPPORT_FRAGS 221 char fragbuf[BIOSCD_SECSIZE]; 222 size_t fragsize; 223 224 fragsize = size % BIOSCD_SECSIZE; 225#else 226 if (size % BIOSCD_SECSIZE) 227 return (EINVAL); 228#endif 229 230 if (rw != F_READ) 231 return(EROFS); 232 dev = (struct i386_devdesc *)devdata; 233 unit = dev->d_unit; 234 blks = size / BIOSCD_SECSIZE; 235 if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0) 236 return (EINVAL); 237 dblk /= (BIOSCD_SECSIZE / DEV_BSIZE); 238 DEBUG("read %d from %lld to %p", blks, dblk, buf); 239 240 if (rsize) 241 *rsize = 0; 242 if (blks && bc_read(unit, dblk, blks, buf)) { 243 DEBUG("read error"); 244 return (EIO); 245 } 246#ifdef BD_SUPPORT_FRAGS 247 DEBUG("frag read %d from %lld+%d to %p", 248 fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE)); 249 if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) { 250 DEBUG("frag read error"); 251 return(EIO); 252 } 253 bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize); 254#endif 255 if (rsize) 256 *rsize = size; 257 return (0); 258} 259 260/* Max number of sectors to bounce-buffer at a time. */ 261#define CD_BOUNCEBUF 8 262 263static int 264bc_read(int unit, daddr_t dblk, int blks, caddr_t dest) 265{ 266 u_int maxfer, resid, result, retry, x; 267 caddr_t bbuf, p, xp; 268 static struct edd_packet packet; 269 int biosdev; 270#ifdef DISK_DEBUG 271 int error; 272#endif 273 274 /* Just in case some idiot actually tries to read -1 blocks... */ 275 if (blks < 0) 276 return (-1); 277 278 /* If nothing to do, just return succcess. */ 279 if (blks == 0) 280 return (0); 281 282 /* Decide whether we have to bounce */ 283 if (VTOP(dest) >> 20 != 0) { 284 /* 285 * The destination buffer is above first 1MB of 286 * physical memory so we have to arrange a suitable 287 * bounce buffer. 288 */ 289 x = min(CD_BOUNCEBUF, (unsigned)blks); 290 bbuf = alloca(x * BIOSCD_SECSIZE); 291 maxfer = x; 292 } else { 293 bbuf = NULL; 294 maxfer = 0; 295 } 296 297 biosdev = bc_unit2bios(unit); 298 resid = blks; 299 p = dest; 300 301 while (resid > 0) { 302 if (bbuf) 303 xp = bbuf; 304 else 305 xp = p; 306 x = resid; 307 if (maxfer > 0) 308 x = min(x, maxfer); 309 310 /* 311 * Loop retrying the operation a couple of times. The BIOS 312 * may also retry. 313 */ 314 for (retry = 0; retry < 3; retry++) { 315 /* If retrying, reset the drive */ 316 if (retry > 0) { 317 v86.ctl = V86_FLAGS; 318 v86.addr = 0x13; 319 v86.eax = 0; 320 v86.edx = biosdev; 321 v86int(); 322 } 323 324 packet.len = sizeof(struct edd_packet); 325 packet.count = x; 326 packet.off = VTOPOFF(xp); 327 packet.seg = VTOPSEG(xp); 328 packet.lba = dblk; 329 v86.ctl = V86_FLAGS; 330 v86.addr = 0x13; 331 v86.eax = 0x4200; 332 v86.edx = biosdev; 333 v86.ds = VTOPSEG(&packet); 334 v86.esi = VTOPOFF(&packet); 335 v86int(); 336 result = V86_CY(v86.efl); 337 if (result == 0) 338 break; 339 } 340 341#ifdef DISK_DEBUG 342 error = (v86.eax >> 8) & 0xff; 343#endif 344 DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p, 345 VTOP(p), result ? "failed" : "ok"); 346 DEBUG("unit %d status 0x%x", unit, error); 347 if (bbuf != NULL) 348 bcopy(bbuf, p, x * BIOSCD_SECSIZE); 349 p += (x * BIOSCD_SECSIZE); 350 dblk += x; 351 resid -= x; 352 } 353 354/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */ 355 return(0); 356} 357 358/* 359 * Return a suitable dev_t value for (dev). 360 */ 361int 362bc_getdev(struct i386_devdesc *dev) 363{ 364 int biosdev, unit; 365 int major; 366 int rootdev; 367 368 unit = dev->d_unit; 369 biosdev = bc_unit2bios(unit); 370 DEBUG("unit %d BIOS device %d", unit, biosdev); 371 if (biosdev == -1) /* not a BIOS device */ 372 return(-1); 373 374 /* 375 * XXX: Need to examine device spec here to figure out if SCSI or 376 * ATAPI. No idea on how to figure out device number. All we can 377 * really pass to the kernel is what bus and device on which bus we 378 * were booted from, which dev_t isn't well suited to since those 379 * number don't match to unit numbers very well. We may just need 380 * to engage in a hack where we pass -C to the boot args if we are 381 * the boot device. 382 */ 383 major = ACDMAJOR; 384 unit = 0; /* XXX */ 385 386 /* XXX: Assume partition 'a'. */ 387 rootdev = MAKEBOOTDEV(major, 0, unit, 0); 388 DEBUG("dev is 0x%x\n", rootdev); 389 return(rootdev); 390} 391