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 * $FreeBSD: src/sys/boot/i386/libi386/bioscd.c,v 1.5 2003/08/25 23:28:31 obrien Exp $ 28 * $DragonFly: src/sys/boot/pc32/libi386/bioscd.c,v 1.6 2006/09/10 01:26:31 dillon Exp $ 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 #include <machine/psl.h> 45 46 #include <stdarg.h> 47 48 #include <bootstrap.h> 49 #include <btxv86.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 } bcinfo [MAXBCDEV]; 88 static int nbcinfo = 0; 89 90 static int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest); 91 static int bc_init(void); 92 static int bc_strategy(void *devdata, int flag, daddr_t dblk, 93 size_t size, char *buf, size_t *rsize); 94 static int bc_open(struct open_file *f, ...); 95 static int bc_close(struct open_file *f); 96 static void bc_print(int verbose); 97 98 struct devsw bioscd = { 99 "cd", 100 DEVT_CD, 101 bc_init, 102 bc_strategy, 103 bc_open, 104 bc_close, 105 noioctl, 106 bc_print, 107 NULL 108 }; 109 110 /* 111 * Translate between BIOS device numbers and our private unit numbers. 112 */ 113 int 114 bc_bios2unit(int biosdev) 115 { 116 int i; 117 118 DEBUG("looking for bios device 0x%x", biosdev); 119 for (i = 0; i < nbcinfo; i++) { 120 DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit); 121 if (bcinfo[i].bc_unit == biosdev) 122 return(i); 123 } 124 return(-1); 125 } 126 127 int 128 bc_unit2bios(int unit) 129 { 130 if ((unit >= 0) && (unit < nbcinfo)) 131 return(bcinfo[unit].bc_unit); 132 return(-1); 133 } 134 135 /* 136 * We can't quiz, we have to be told what device to use, so this functoin 137 * doesn't do anything. Instead, the loader calls bc_add() with the BIOS 138 * device number to add. 139 */ 140 static int 141 bc_init(void) 142 { 143 144 return (0); 145 } 146 147 int 148 bc_add(int biosdev) 149 { 150 151 if (nbcinfo >= MAXBCDEV) 152 return (-1); 153 bcinfo[nbcinfo].bc_unit = biosdev; 154 v86.ctl = V86_FLAGS; 155 v86.addr = 0x13; 156 v86.eax = 0x4b01; 157 v86.edx = biosdev; 158 v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp); 159 v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp); 160 v86int(); 161 if ((v86.eax & 0xff00) != 0) 162 return (-1); 163 164 printf("BIOS CD is cd%d\n", nbcinfo); 165 nbcinfo++; 166 return(0); 167 } 168 169 /* 170 * Print information about disks 171 */ 172 static void 173 bc_print(int verbose) 174 { 175 int i; 176 char line[80]; 177 178 for (i = 0; i < nbcinfo; i++) { 179 sprintf(line, " cd%d: Device 0x%x\n", i, 180 bcinfo[i].bc_sp.sp_devicespec); 181 pager_output(line); 182 } 183 } 184 185 /* 186 * Attempt to open the disk described by (dev) for use by (f). 187 */ 188 static int 189 bc_open(struct open_file *f, ...) 190 { 191 va_list ap; 192 struct i386_devdesc *dev; 193 194 va_start(ap, f); 195 dev = va_arg(ap, struct i386_devdesc *); 196 va_end(ap); 197 if (dev->d_kind.bioscd.unit >= nbcinfo) { 198 DEBUG("attempt to open nonexistent disk"); 199 return(ENXIO); 200 } 201 202 return(0); 203 } 204 205 static int 206 bc_close(struct open_file *f) 207 { 208 209 return(0); 210 } 211 212 static int 213 bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, 214 size_t *rsize) 215 { 216 struct i386_devdesc *dev; 217 int unit; 218 int blks; 219 #ifdef BD_SUPPORT_FRAGS 220 #error "xxx broken code xxx" 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_kind.bioscd.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 %d 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("bc_strategy: frag read %d from %d+%d to %p", 248 fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE)); 249 if (fragsize && bc_read(unit, dblk + blks, 1, fragsize)) { 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 static int 261 bc_read(int unit, daddr_t dblk, int blks, caddr_t dest) 262 { 263 u_int result, retry; 264 static unsigned short packet[8]; 265 int biosdev; 266 int n; 267 #ifdef DISK_DEBUG 268 int error; 269 #endif 270 271 /* Just in case some idiot actually tries to read -1 blocks... */ 272 if (blks < 0) 273 return (-1); 274 275 /* If nothing to do, just return succcess. */ 276 if (blks == 0) 277 return (0); 278 279 biosdev = bc_unit2bios(unit); 280 /* 281 * Loop retrying the operation a couple of times. The BIOS 282 * may also retry. 283 */ 284 for (retry = 0; retry < 3; retry++) { 285 /* If retrying, reset the drive */ 286 if (retry > 0) { 287 v86.ctl = V86_FLAGS; 288 v86.addr = 0x13; 289 v86.eax = 0; 290 v86.edx = biosdev; 291 v86int(); 292 } 293 294 n = BOUNCEBUF_SIZE / BIOSCD_SECSIZE; 295 if (n > blks) 296 n = blks; 297 298 packet[0] = 0x10; 299 packet[1] = n; 300 packet[2] = VTOPOFF(bounce_base); 301 packet[3] = VTOPSEG(bounce_base); 302 packet[4] = dblk & 0xffff; 303 packet[5] = dblk >> 16; 304 packet[6] = 0; 305 packet[7] = 0; 306 v86.ctl = V86_FLAGS; 307 v86.addr = 0x13; 308 v86.eax = 0x4200; 309 v86.edx = biosdev; 310 v86.ds = VTOPSEG(packet); 311 v86.esi = VTOPOFF(packet); 312 v86int(); 313 result = (v86.efl & PSL_C); 314 if (result == 0) { 315 bcopy(bounce_base, dest, n * BIOSCD_SECSIZE); 316 blks -= n; 317 dest += n * BIOSCD_SECSIZE; 318 if (blks == 0) 319 break; 320 retry = 0; 321 } 322 } 323 324 #ifdef DISK_DEBUG 325 error = (v86.eax >> 8) & 0xff; 326 #endif 327 DEBUG("%d sectors from %ld to %p (0x%x) %s", blks, dblk, dest, 328 VTOP(dest), result ? "failed" : "ok"); 329 DEBUG("unit %d status 0x%x", unit, error); 330 331 /* hexdump(dest, (blks * BIOSCD_SECSIZE)); */ 332 return(0); 333 } 334 335 /* 336 * Return a suitable cdev_t value for (dev). 337 */ 338 int 339 bc_getdev(struct i386_devdesc *dev) 340 { 341 int biosdev, unit; 342 int major; 343 int rootdev; 344 345 unit = dev->d_kind.bioscd.unit; 346 biosdev = bc_unit2bios(unit); 347 DEBUG("unit %d BIOS device %d", unit, biosdev); 348 if (biosdev == -1) /* not a BIOS device */ 349 return(-1); 350 351 /* 352 * XXX: Need to examine device spec here to figure out if SCSI or 353 * ATAPI. No idea on how to figure out device number. All we can 354 * really pass to the kernel is what bus and device on which bus we 355 * were booted from, which cdev_t isn't well suited to since those 356 * number don't match to unit numbers very well. We may just need 357 * to engage in a hack where we pass -C to the boot args if we are 358 * the boot device. 359 */ 360 major = ACDMAJOR; 361 unit = 0; /* XXX */ 362 363 /* XXX: Assume partition 'a'. */ 364 rootdev = MAKEBOOTDEV(major, 0, unit, 0); 365 DEBUG("dev is 0x%x\n", rootdev); 366 return(rootdev); 367 } 368