bioscd.c revision 226746
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/pc98/libpc98/bioscd.c 226746 2011-10-25 19:45:12Z jhb $"); 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 "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 66struct 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 */ 84static struct bcinfo { 85 int bc_unit; /* BIOS unit number */ 86 struct specification_packet bc_sp; 87} bcinfo [MAXBCDEV]; 88static int nbcinfo = 0; 89 90static int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest); 91static int bc_init(void); 92static int bc_strategy(void *devdata, int flag, daddr_t dblk, 93 size_t size, char *buf, size_t *rsize); 94static int bc_open(struct open_file *f, ...); 95static int bc_close(struct open_file *f); 96static void bc_print(int verbose); 97 98struct 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 */ 113int 114bc_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 127int 128bc_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 */ 140static int 141bc_init(void) 142{ 143 144 return (0); 145} 146 147int 148bc_add(int biosdev) 149{ 150 151 if (nbcinfo >= MAXBCDEV) 152 return (-1); 153 bcinfo[nbcinfo].bc_unit = biosdev; 154 155 /* SCSI CD-ROM only */ 156 if ((biosdev & 0xf0) != 0xa0) 157 return (-1); 158 if ((((uint32_t *)PTOV(0xA1460))[biosdev & 0x0f] & 0x1f) != 5) 159 return (-1); 160 161 printf("BIOS CD is cd%d\n", nbcinfo); 162 nbcinfo++; 163 return(0); 164} 165 166/* 167 * Print information about disks 168 */ 169static void 170bc_print(int verbose) 171{ 172 char line[80]; 173 int i; 174 175 for (i = 0; i < nbcinfo; i++) { 176 sprintf(line, " cd%d: Device 0x%x\n", i, 177 bcinfo[i].bc_sp.sp_devicespec); 178 pager_output(line); 179 } 180} 181 182/* 183 * Attempt to open the disk described by (dev) for use by (f). 184 */ 185static int 186bc_open(struct open_file *f, ...) 187{ 188 va_list ap; 189 struct i386_devdesc *dev; 190 191 va_start(ap, f); 192 dev = va_arg(ap, struct i386_devdesc *); 193 va_end(ap); 194 if (dev->d_unit >= nbcinfo) { 195 DEBUG("attempt to open nonexistent disk"); 196 return(ENXIO); 197 } 198 199 return(0); 200} 201 202static int 203bc_close(struct open_file *f) 204{ 205 206 return(0); 207} 208 209static int 210bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, 211 size_t *rsize) 212{ 213 struct i386_devdesc *dev; 214 int unit; 215 int blks; 216#ifdef BD_SUPPORT_FRAGS 217 char fragbuf[BIOSCD_SECSIZE]; 218 size_t fragsize; 219 220 fragsize = size % BIOSCD_SECSIZE; 221#else 222 if (size % BIOSCD_SECSIZE) 223 return (EINVAL); 224#endif 225 226 if (rw != F_READ) 227 return(EROFS); 228 dev = (struct i386_devdesc *)devdata; 229 unit = dev->d_unit; 230 blks = size / BIOSCD_SECSIZE; 231 if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0) 232 return (EINVAL); 233 dblk /= (BIOSCD_SECSIZE / DEV_BSIZE); 234 DEBUG("read %d from %lld to %p", blks, dblk, buf); 235 236 if (rsize) 237 *rsize = 0; 238 if (blks && bc_read(unit, dblk, blks, buf)) { 239 DEBUG("read error"); 240 return (EIO); 241 } 242#ifdef BD_SUPPORT_FRAGS 243 DEBUG("frag read %d from %lld+%d to %p", 244 fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE)); 245 if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) { 246 DEBUG("frag read error"); 247 return(EIO); 248 } 249 bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize); 250#endif 251 if (rsize) 252 *rsize = size; 253 return (0); 254} 255 256/* Max number of sectors to bounce-buffer at a time. */ 257#define CD_BOUNCEBUF 8 258 259static int 260bc_read(int unit, daddr_t dblk, int blks, caddr_t dest) 261{ 262 u_int maxfer, resid, result, retry, x; 263 caddr_t bbuf, p, xp; 264 int biosdev; 265#ifdef DISK_DEBUG 266 int error; 267#endif 268 269 /* Just in case some idiot actually tries to read -1 blocks... */ 270 if (blks < 0) 271 return (-1); 272 273 /* If nothing to do, just return succcess. */ 274 if (blks == 0) 275 return (0); 276 277 /* Decide whether we have to bounce */ 278 if (VTOP(dest) >> 20 != 0) { 279 /* 280 * The destination buffer is above first 1MB of 281 * physical memory so we have to arrange a suitable 282 * bounce buffer. 283 */ 284 x = min(CD_BOUNCEBUF, (unsigned)blks); 285 bbuf = alloca(x * BIOSCD_SECSIZE); 286 maxfer = x; 287 } else { 288 bbuf = NULL; 289 maxfer = 0; 290 } 291 292 biosdev = bc_unit2bios(unit); 293 resid = blks; 294 p = dest; 295 296 while (resid > 0) { 297 if (bbuf) 298 xp = bbuf; 299 else 300 xp = p; 301 x = resid; 302 if (maxfer > 0) 303 x = min(x, maxfer); 304 305 /* 306 * Loop retrying the operation a couple of times. The BIOS 307 * may also retry. 308 */ 309 for (retry = 0; retry < 3; retry++) { 310 /* If retrying, reset the drive */ 311 if (retry > 0) { 312 v86.ctl = V86_FLAGS; 313 v86.addr = 0x1b; 314 v86.eax = 0x0300 | biosdev; 315 v86int(); 316 } 317 318 v86.ctl = V86_FLAGS; 319 v86.addr = 0x1b; 320 v86.eax = 0x0600 | (biosdev & 0x7f); 321 v86.ebx = x * BIOSCD_SECSIZE; 322 v86.ecx = dblk & 0xffff; 323 v86.edx = (dblk >> 16) & 0xffff; 324 v86.ebp = VTOPOFF(xp); 325 v86.es = VTOPSEG(xp); 326 v86int(); 327 result = V86_CY(v86.efl); 328 if (result == 0) 329 break; 330 } 331 332#ifdef DISK_DEBUG 333 error = (v86.eax >> 8) & 0xff; 334#endif 335 DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p, 336 VTOP(p), result ? "failed" : "ok"); 337 DEBUG("unit %d status 0x%x", unit, error); 338 if (bbuf != NULL) 339 bcopy(bbuf, p, x * BIOSCD_SECSIZE); 340 p += (x * BIOSCD_SECSIZE); 341 dblk += x; 342 resid -= x; 343 } 344 345/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */ 346 return(0); 347} 348 349/* 350 * Return a suitable dev_t value for (dev). 351 */ 352int 353bc_getdev(struct i386_devdesc *dev) 354{ 355 int biosdev, unit, device; 356 int major; 357 int rootdev; 358 359 unit = dev->d_unit; 360 biosdev = bc_unit2bios(unit); 361 DEBUG("unit %d BIOS device %d", unit, biosdev); 362 if (biosdev == -1) /* not a BIOS device */ 363 return(-1); 364 365 device = biosdev & 0xf0; 366 if (device == 0x80) 367 major = ACDMAJOR; 368 else if (device == 0xa0) 369 major = CDMAJOR; 370 else 371 return (-1); 372 373 unit = 0; /* XXX */ 374 375 /* XXX: Assume partition 'a'. */ 376 rootdev = MAKEBOOTDEV(major, 0, unit, 0); 377 DEBUG("dev is 0x%x\n", rootdev); 378 return(rootdev); 379} 380