1158559Snyan/*- 2158559Snyan * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3158559Snyan * Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org> 4158559Snyan * All rights reserved. 5158559Snyan * 6158559Snyan * Redistribution and use in source and binary forms, with or without 7158559Snyan * modification, are permitted provided that the following conditions 8158559Snyan * are met: 9158559Snyan * 1. Redistributions of source code must retain the above copyright 10158559Snyan * notice, this list of conditions and the following disclaimer. 11158559Snyan * 2. Redistributions in binary form must reproduce the above copyright 12158559Snyan * notice, this list of conditions and the following disclaimer in the 13158559Snyan * documentation and/or other materials provided with the distribution. 14158559Snyan * 15158559Snyan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16158559Snyan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17158559Snyan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18158559Snyan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19158559Snyan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20158559Snyan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21158559Snyan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22158559Snyan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23158559Snyan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24158559Snyan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25158559Snyan * SUCH DAMAGE. 26158559Snyan */ 27158559Snyan 28158559Snyan#include <sys/cdefs.h> 29158559Snyan__FBSDID("$FreeBSD$"); 30158559Snyan 31158559Snyan/* 32158559Snyan * BIOS CD device handling for CD's that have been booted off of via no 33158559Snyan * emulation booting as defined in the El Torito standard. 34158559Snyan * 35158559Snyan * Ideas and algorithms from: 36158559Snyan * 37158559Snyan * - FreeBSD libi386/biosdisk.c 38158559Snyan * 39158559Snyan */ 40158559Snyan 41158559Snyan#include <stand.h> 42158559Snyan 43158559Snyan#include <sys/param.h> 44158559Snyan#include <machine/bootinfo.h> 45158559Snyan 46158559Snyan#include <stdarg.h> 47158559Snyan 48158559Snyan#include <bootstrap.h> 49158559Snyan#include <btxv86.h> 50158559Snyan#include "libi386.h" 51158559Snyan 52158559Snyan#define BIOSCD_SECSIZE 2048 53158559Snyan#define BUFSIZE (1 * BIOSCD_SECSIZE) 54158559Snyan#define MAXBCDEV 1 55158559Snyan 56158559Snyan/* Major numbers for devices we frontend for. */ 57158559Snyan#define ACDMAJOR 117 58158559Snyan#define CDMAJOR 15 59158559Snyan 60158559Snyan#ifdef DISK_DEBUG 61158559Snyan# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 62158559Snyan#else 63158559Snyan# define DEBUG(fmt, args...) 64158559Snyan#endif 65158559Snyan 66158559Snyanstruct specification_packet { 67158559Snyan u_char sp_size; 68158559Snyan u_char sp_bootmedia; 69158559Snyan u_char sp_drive; 70158559Snyan u_char sp_controller; 71158559Snyan u_int sp_lba; 72158559Snyan u_short sp_devicespec; 73158559Snyan u_short sp_buffersegment; 74158559Snyan u_short sp_loadsegment; 75158559Snyan u_short sp_sectorcount; 76158559Snyan u_short sp_cylsec; 77158559Snyan u_char sp_head; 78158559Snyan}; 79158559Snyan 80158559Snyan/* 81158559Snyan * List of BIOS devices, translation from disk unit number to 82158559Snyan * BIOS unit number. 83158559Snyan */ 84158559Snyanstatic struct bcinfo { 85158559Snyan int bc_unit; /* BIOS unit number */ 86158559Snyan struct specification_packet bc_sp; 87158559Snyan} bcinfo [MAXBCDEV]; 88158559Snyanstatic int nbcinfo = 0; 89158559Snyan 90158559Snyanstatic int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest); 91158559Snyanstatic int bc_init(void); 92158559Snyanstatic int bc_strategy(void *devdata, int flag, daddr_t dblk, 93158559Snyan size_t size, char *buf, size_t *rsize); 94158559Snyanstatic int bc_open(struct open_file *f, ...); 95158559Snyanstatic int bc_close(struct open_file *f); 96158559Snyanstatic void bc_print(int verbose); 97158559Snyan 98158559Snyanstruct devsw bioscd = { 99158559Snyan "cd", 100158559Snyan DEVT_CD, 101158559Snyan bc_init, 102158559Snyan bc_strategy, 103158559Snyan bc_open, 104158559Snyan bc_close, 105158559Snyan noioctl, 106158559Snyan bc_print, 107158559Snyan NULL 108158559Snyan}; 109158559Snyan 110158559Snyan/* 111158559Snyan * Translate between BIOS device numbers and our private unit numbers. 112158559Snyan */ 113158559Snyanint 114158559Snyanbc_bios2unit(int biosdev) 115158559Snyan{ 116158559Snyan int i; 117158559Snyan 118158559Snyan DEBUG("looking for bios device 0x%x", biosdev); 119158559Snyan for (i = 0; i < nbcinfo; i++) { 120158559Snyan DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit); 121158559Snyan if (bcinfo[i].bc_unit == biosdev) 122158559Snyan return(i); 123158559Snyan } 124158559Snyan return(-1); 125158559Snyan} 126158559Snyan 127158559Snyanint 128158559Snyanbc_unit2bios(int unit) 129158559Snyan{ 130158559Snyan if ((unit >= 0) && (unit < nbcinfo)) 131158559Snyan return(bcinfo[unit].bc_unit); 132158559Snyan return(-1); 133158559Snyan} 134158559Snyan 135158559Snyan/* 136158559Snyan * We can't quiz, we have to be told what device to use, so this functoin 137158559Snyan * doesn't do anything. Instead, the loader calls bc_add() with the BIOS 138158559Snyan * device number to add. 139158559Snyan */ 140158559Snyanstatic int 141158559Snyanbc_init(void) 142158559Snyan{ 143158559Snyan 144158559Snyan return (0); 145158559Snyan} 146158559Snyan 147158559Snyanint 148158559Snyanbc_add(int biosdev) 149158559Snyan{ 150158559Snyan 151158559Snyan if (nbcinfo >= MAXBCDEV) 152158559Snyan return (-1); 153158559Snyan bcinfo[nbcinfo].bc_unit = biosdev; 154158559Snyan 155158559Snyan /* SCSI CD-ROM only */ 156158559Snyan if ((biosdev & 0xf0) != 0xa0) 157158559Snyan return (-1); 158158559Snyan if ((((uint32_t *)PTOV(0xA1460))[biosdev & 0x0f] & 0x1f) != 5) 159158559Snyan return (-1); 160158559Snyan 161158559Snyan printf("BIOS CD is cd%d\n", nbcinfo); 162158559Snyan nbcinfo++; 163158559Snyan return(0); 164158559Snyan} 165158559Snyan 166158559Snyan/* 167158559Snyan * Print information about disks 168158559Snyan */ 169158559Snyanstatic void 170158559Snyanbc_print(int verbose) 171158559Snyan{ 172190146Snyan char line[80]; 173158559Snyan int i; 174190146Snyan 175158559Snyan for (i = 0; i < nbcinfo; i++) { 176158559Snyan sprintf(line, " cd%d: Device 0x%x\n", i, 177158559Snyan bcinfo[i].bc_sp.sp_devicespec); 178158559Snyan pager_output(line); 179158559Snyan } 180158559Snyan} 181158559Snyan 182158559Snyan/* 183158559Snyan * Attempt to open the disk described by (dev) for use by (f). 184158559Snyan */ 185158559Snyanstatic int 186158559Snyanbc_open(struct open_file *f, ...) 187158559Snyan{ 188158559Snyan va_list ap; 189158559Snyan struct i386_devdesc *dev; 190158559Snyan 191158559Snyan va_start(ap, f); 192158559Snyan dev = va_arg(ap, struct i386_devdesc *); 193158559Snyan va_end(ap); 194163897Smarcel if (dev->d_unit >= nbcinfo) { 195158559Snyan DEBUG("attempt to open nonexistent disk"); 196158559Snyan return(ENXIO); 197158559Snyan } 198158559Snyan 199158559Snyan return(0); 200158559Snyan} 201158559Snyan 202158559Snyanstatic int 203158559Snyanbc_close(struct open_file *f) 204158559Snyan{ 205158559Snyan 206158559Snyan return(0); 207158559Snyan} 208158559Snyan 209158559Snyanstatic int 210158559Snyanbc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, 211158559Snyan size_t *rsize) 212158559Snyan{ 213158559Snyan struct i386_devdesc *dev; 214158559Snyan int unit; 215158559Snyan int blks; 216158559Snyan#ifdef BD_SUPPORT_FRAGS 217158559Snyan char fragbuf[BIOSCD_SECSIZE]; 218158559Snyan size_t fragsize; 219158559Snyan 220158559Snyan fragsize = size % BIOSCD_SECSIZE; 221158559Snyan#else 222158559Snyan if (size % BIOSCD_SECSIZE) 223158559Snyan return (EINVAL); 224158559Snyan#endif 225158559Snyan 226158559Snyan if (rw != F_READ) 227158559Snyan return(EROFS); 228158559Snyan dev = (struct i386_devdesc *)devdata; 229163897Smarcel unit = dev->d_unit; 230158559Snyan blks = size / BIOSCD_SECSIZE; 231158559Snyan if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0) 232158559Snyan return (EINVAL); 233158559Snyan dblk /= (BIOSCD_SECSIZE / DEV_BSIZE); 234190146Snyan DEBUG("read %d from %lld to %p", blks, dblk, buf); 235158559Snyan 236158559Snyan if (rsize) 237158559Snyan *rsize = 0; 238158559Snyan if (blks && bc_read(unit, dblk, blks, buf)) { 239158559Snyan DEBUG("read error"); 240158559Snyan return (EIO); 241158559Snyan } 242158559Snyan#ifdef BD_SUPPORT_FRAGS 243190146Snyan DEBUG("frag read %d from %lld+%d to %p", 244158559Snyan fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE)); 245190146Snyan if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) { 246158559Snyan DEBUG("frag read error"); 247158559Snyan return(EIO); 248158559Snyan } 249158559Snyan bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize); 250158559Snyan#endif 251158559Snyan if (rsize) 252158559Snyan *rsize = size; 253158559Snyan return (0); 254158559Snyan} 255158559Snyan 256190146Snyan/* Max number of sectors to bounce-buffer at a time. */ 257190146Snyan#define CD_BOUNCEBUF 8 258190146Snyan 259158559Snyanstatic int 260158559Snyanbc_read(int unit, daddr_t dblk, int blks, caddr_t dest) 261158559Snyan{ 262190146Snyan u_int maxfer, resid, result, retry, x; 263190146Snyan caddr_t bbuf, p, xp; 264158559Snyan int biosdev; 265158559Snyan#ifdef DISK_DEBUG 266158559Snyan int error; 267158559Snyan#endif 268158559Snyan 269158559Snyan /* Just in case some idiot actually tries to read -1 blocks... */ 270158559Snyan if (blks < 0) 271158559Snyan return (-1); 272158559Snyan 273158559Snyan /* If nothing to do, just return succcess. */ 274158559Snyan if (blks == 0) 275158559Snyan return (0); 276158559Snyan 277190146Snyan /* Decide whether we have to bounce */ 278190146Snyan if (VTOP(dest) >> 20 != 0) { 279190146Snyan /* 280190146Snyan * The destination buffer is above first 1MB of 281190146Snyan * physical memory so we have to arrange a suitable 282190146Snyan * bounce buffer. 283190146Snyan */ 284190146Snyan x = min(CD_BOUNCEBUF, (unsigned)blks); 285190146Snyan bbuf = alloca(x * BIOSCD_SECSIZE); 286190146Snyan maxfer = x; 287190146Snyan } else { 288190146Snyan bbuf = NULL; 289190146Snyan maxfer = 0; 290190146Snyan } 291190146Snyan 292158559Snyan biosdev = bc_unit2bios(unit); 293190146Snyan resid = blks; 294190146Snyan p = dest; 295190146Snyan 296190146Snyan while (resid > 0) { 297190146Snyan if (bbuf) 298190146Snyan xp = bbuf; 299190146Snyan else 300190146Snyan xp = p; 301190146Snyan x = resid; 302190146Snyan if (maxfer > 0) 303190146Snyan x = min(x, maxfer); 304190146Snyan 305190146Snyan /* 306190146Snyan * Loop retrying the operation a couple of times. The BIOS 307190146Snyan * may also retry. 308190146Snyan */ 309190146Snyan for (retry = 0; retry < 3; retry++) { 310190146Snyan /* If retrying, reset the drive */ 311190146Snyan if (retry > 0) { 312190146Snyan v86.ctl = V86_FLAGS; 313190146Snyan v86.addr = 0x1b; 314190146Snyan v86.eax = 0x0300 | biosdev; 315190146Snyan v86int(); 316190146Snyan } 317190146Snyan 318158559Snyan v86.ctl = V86_FLAGS; 319158559Snyan v86.addr = 0x1b; 320190146Snyan v86.eax = 0x0600 | (biosdev & 0x7f); 321190147Snyan v86.ebx = x * BIOSCD_SECSIZE; 322190146Snyan v86.ecx = dblk & 0xffff; 323190146Snyan v86.edx = (dblk >> 16) & 0xffff; 324190147Snyan v86.ebp = VTOPOFF(xp); 325190147Snyan v86.es = VTOPSEG(xp); 326158559Snyan v86int(); 327226746Sjhb result = V86_CY(v86.efl); 328190146Snyan if (result == 0) 329190146Snyan break; 330158559Snyan } 331158559Snyan 332158559Snyan#ifdef DISK_DEBUG 333190146Snyan error = (v86.eax >> 8) & 0xff; 334158559Snyan#endif 335190146Snyan DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p, 336190146Snyan VTOP(p), result ? "failed" : "ok"); 337190146Snyan DEBUG("unit %d status 0x%x", unit, error); 338190146Snyan if (bbuf != NULL) 339190146Snyan bcopy(bbuf, p, x * BIOSCD_SECSIZE); 340190146Snyan p += (x * BIOSCD_SECSIZE); 341190146Snyan dblk += x; 342190146Snyan resid -= x; 343190146Snyan } 344158559Snyan 345158559Snyan/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */ 346158559Snyan return(0); 347158559Snyan} 348158559Snyan 349158559Snyan/* 350158559Snyan * Return a suitable dev_t value for (dev). 351158559Snyan */ 352158559Snyanint 353158559Snyanbc_getdev(struct i386_devdesc *dev) 354158559Snyan{ 355158559Snyan int biosdev, unit, device; 356158559Snyan int major; 357158559Snyan int rootdev; 358158559Snyan 359163897Smarcel unit = dev->d_unit; 360158559Snyan biosdev = bc_unit2bios(unit); 361158559Snyan DEBUG("unit %d BIOS device %d", unit, biosdev); 362158559Snyan if (biosdev == -1) /* not a BIOS device */ 363158559Snyan return(-1); 364158559Snyan 365158559Snyan device = biosdev & 0xf0; 366158559Snyan if (device == 0x80) 367158559Snyan major = ACDMAJOR; 368158559Snyan else if (device == 0xa0) 369158559Snyan major = CDMAJOR; 370158559Snyan else 371158559Snyan return (-1); 372158559Snyan 373158559Snyan unit = 0; /* XXX */ 374158559Snyan 375158559Snyan /* XXX: Assume partition 'a'. */ 376172921Sjhb rootdev = MAKEBOOTDEV(major, 0, unit, 0); 377158559Snyan DEBUG("dev is 0x%x\n", rootdev); 378158559Snyan return(rootdev); 379158559Snyan} 380