186091Sjhb/*- 286091Sjhb * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 386091Sjhb * Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org> 486091Sjhb * All rights reserved. 586091Sjhb * 686091Sjhb * Redistribution and use in source and binary forms, with or without 786091Sjhb * modification, are permitted provided that the following conditions 886091Sjhb * are met: 986091Sjhb * 1. Redistributions of source code must retain the above copyright 1086091Sjhb * notice, this list of conditions and the following disclaimer. 1186091Sjhb * 2. Redistributions in binary form must reproduce the above copyright 1286091Sjhb * notice, this list of conditions and the following disclaimer in the 1386091Sjhb * documentation and/or other materials provided with the distribution. 1486091Sjhb * 1586091Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1686091Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1786091Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1886091Sjhb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1986091Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2086091Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2186091Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2286091Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2386091Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2486091Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2586091Sjhb * SUCH DAMAGE. 2686091Sjhb */ 2786091Sjhb 28119482Sobrien#include <sys/cdefs.h> 29119482Sobrien__FBSDID("$FreeBSD$"); 30119482Sobrien 3186091Sjhb/* 3286091Sjhb * BIOS CD device handling for CD's that have been booted off of via no 3386091Sjhb * emulation booting as defined in the El Torito standard. 3486091Sjhb * 3586091Sjhb * Ideas and algorithms from: 3686091Sjhb * 3786091Sjhb * - FreeBSD libi386/biosdisk.c 3886091Sjhb * 3986091Sjhb */ 4086091Sjhb 4186091Sjhb#include <stand.h> 4286091Sjhb 4396654Sjhay#include <sys/param.h> 44113083Sphk#include <machine/bootinfo.h> 4586091Sjhb 4686091Sjhb#include <stdarg.h> 4786091Sjhb 4886091Sjhb#include <bootstrap.h> 4986091Sjhb#include <btxv86.h> 50227400Sjhb#include <edd.h> 5186091Sjhb#include "libi386.h" 5286091Sjhb 5386091Sjhb#define BIOSCD_SECSIZE 2048 5486091Sjhb#define BUFSIZE (1 * BIOSCD_SECSIZE) 5586091Sjhb#define MAXBCDEV 1 5686091Sjhb 5786091Sjhb/* Major numbers for devices we frontend for. */ 5886091Sjhb#define ACDMAJOR 117 5986091Sjhb#define CDMAJOR 15 6086091Sjhb 6186091Sjhb#ifdef DISK_DEBUG 6287599Sobrien# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 6386091Sjhb#else 6486091Sjhb# define DEBUG(fmt, args...) 6586091Sjhb#endif 6686091Sjhb 6786091Sjhbstruct specification_packet { 6886091Sjhb u_char sp_size; 6986091Sjhb u_char sp_bootmedia; 7086091Sjhb u_char sp_drive; 7186091Sjhb u_char sp_controller; 7286091Sjhb u_int sp_lba; 7386091Sjhb u_short sp_devicespec; 7486091Sjhb u_short sp_buffersegment; 7586091Sjhb u_short sp_loadsegment; 7686091Sjhb u_short sp_sectorcount; 7786091Sjhb u_short sp_cylsec; 7886091Sjhb u_char sp_head; 7986091Sjhb}; 8086091Sjhb 8186091Sjhb/* 8286091Sjhb * List of BIOS devices, translation from disk unit number to 8386091Sjhb * BIOS unit number. 8486091Sjhb */ 8586091Sjhbstatic struct bcinfo { 8686091Sjhb int bc_unit; /* BIOS unit number */ 8786091Sjhb struct specification_packet bc_sp; 8886091Sjhb} bcinfo [MAXBCDEV]; 8986091Sjhbstatic int nbcinfo = 0; 9086091Sjhb 9186091Sjhbstatic int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest); 9286091Sjhbstatic int bc_init(void); 9386091Sjhbstatic int bc_strategy(void *devdata, int flag, daddr_t dblk, 9486091Sjhb size_t size, char *buf, size_t *rsize); 9586091Sjhbstatic int bc_open(struct open_file *f, ...); 9686091Sjhbstatic int bc_close(struct open_file *f); 9786091Sjhbstatic void bc_print(int verbose); 9886091Sjhb 9986091Sjhbstruct devsw bioscd = { 10086091Sjhb "cd", 10186091Sjhb DEVT_CD, 10286091Sjhb bc_init, 10386091Sjhb bc_strategy, 10486091Sjhb bc_open, 10586091Sjhb bc_close, 10686091Sjhb noioctl, 10786091Sjhb bc_print, 10886091Sjhb NULL 10986091Sjhb}; 11086091Sjhb 11186091Sjhb/* 11286091Sjhb * Translate between BIOS device numbers and our private unit numbers. 11386091Sjhb */ 11486091Sjhbint 11586091Sjhbbc_bios2unit(int biosdev) 11686091Sjhb{ 11786091Sjhb int i; 11886091Sjhb 11986091Sjhb DEBUG("looking for bios device 0x%x", biosdev); 12086091Sjhb for (i = 0; i < nbcinfo; i++) { 12186091Sjhb DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit); 12286091Sjhb if (bcinfo[i].bc_unit == biosdev) 12386091Sjhb return(i); 12486091Sjhb } 12586091Sjhb return(-1); 12686091Sjhb} 12786091Sjhb 12886091Sjhbint 12986091Sjhbbc_unit2bios(int unit) 13086091Sjhb{ 13186091Sjhb if ((unit >= 0) && (unit < nbcinfo)) 13286091Sjhb return(bcinfo[unit].bc_unit); 13386091Sjhb return(-1); 13486091Sjhb} 13586091Sjhb 13686091Sjhb/* 13786091Sjhb * We can't quiz, we have to be told what device to use, so this functoin 13886091Sjhb * doesn't do anything. Instead, the loader calls bc_add() with the BIOS 13986091Sjhb * device number to add. 14086091Sjhb */ 14186091Sjhbstatic int 14286091Sjhbbc_init(void) 14386091Sjhb{ 14486091Sjhb 14586091Sjhb return (0); 14686091Sjhb} 14786091Sjhb 14886091Sjhbint 14986091Sjhbbc_add(int biosdev) 15086091Sjhb{ 15186091Sjhb 15286091Sjhb if (nbcinfo >= MAXBCDEV) 15386091Sjhb return (-1); 15486091Sjhb bcinfo[nbcinfo].bc_unit = biosdev; 15586091Sjhb v86.ctl = V86_FLAGS; 15686091Sjhb v86.addr = 0x13; 15786091Sjhb v86.eax = 0x4b01; 15886091Sjhb v86.edx = biosdev; 15986091Sjhb v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp); 16086091Sjhb v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp); 16186091Sjhb v86int(); 162228187Sjhb if ((v86.eax & 0xff00) != 0) 16386091Sjhb return (-1); 16486091Sjhb 16586091Sjhb printf("BIOS CD is cd%d\n", nbcinfo); 16686091Sjhb nbcinfo++; 16786091Sjhb return(0); 16886091Sjhb} 16986091Sjhb 17086091Sjhb/* 17186091Sjhb * Print information about disks 17286091Sjhb */ 17386091Sjhbstatic void 17486091Sjhbbc_print(int verbose) 17586091Sjhb{ 176189749Sjhb char line[80]; 17786091Sjhb int i; 178189749Sjhb 17986091Sjhb for (i = 0; i < nbcinfo; i++) { 18086091Sjhb sprintf(line, " cd%d: Device 0x%x\n", i, 18186091Sjhb bcinfo[i].bc_sp.sp_devicespec); 18286091Sjhb pager_output(line); 18386091Sjhb } 18486091Sjhb} 18586091Sjhb 18686091Sjhb/* 18786091Sjhb * Attempt to open the disk described by (dev) for use by (f). 18886091Sjhb */ 18986091Sjhbstatic int 19086091Sjhbbc_open(struct open_file *f, ...) 19186091Sjhb{ 19286091Sjhb va_list ap; 19386091Sjhb struct i386_devdesc *dev; 19486091Sjhb 19586091Sjhb va_start(ap, f); 19686091Sjhb dev = va_arg(ap, struct i386_devdesc *); 19786091Sjhb va_end(ap); 198163897Smarcel if (dev->d_unit >= nbcinfo) { 19986091Sjhb DEBUG("attempt to open nonexistent disk"); 20086091Sjhb return(ENXIO); 20186091Sjhb } 20286091Sjhb 20386091Sjhb return(0); 20486091Sjhb} 20586091Sjhb 20686091Sjhbstatic int 20786091Sjhbbc_close(struct open_file *f) 20886091Sjhb{ 20986091Sjhb 21086091Sjhb return(0); 21186091Sjhb} 21286091Sjhb 21386091Sjhbstatic int 21486091Sjhbbc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, 21586091Sjhb size_t *rsize) 21686091Sjhb{ 21786091Sjhb struct i386_devdesc *dev; 21886091Sjhb int unit; 21986091Sjhb int blks; 22086091Sjhb#ifdef BD_SUPPORT_FRAGS 22186091Sjhb char fragbuf[BIOSCD_SECSIZE]; 22286091Sjhb size_t fragsize; 22386091Sjhb 22486091Sjhb fragsize = size % BIOSCD_SECSIZE; 22586091Sjhb#else 22686091Sjhb if (size % BIOSCD_SECSIZE) 22786091Sjhb return (EINVAL); 22886091Sjhb#endif 22986091Sjhb 23086091Sjhb if (rw != F_READ) 23186091Sjhb return(EROFS); 23286091Sjhb dev = (struct i386_devdesc *)devdata; 233163897Smarcel unit = dev->d_unit; 23486091Sjhb blks = size / BIOSCD_SECSIZE; 23586091Sjhb if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0) 23686091Sjhb return (EINVAL); 23786091Sjhb dblk /= (BIOSCD_SECSIZE / DEV_BSIZE); 238189749Sjhb DEBUG("read %d from %lld to %p", blks, dblk, buf); 23986091Sjhb 24086091Sjhb if (rsize) 24186091Sjhb *rsize = 0; 24286091Sjhb if (blks && bc_read(unit, dblk, blks, buf)) { 24386091Sjhb DEBUG("read error"); 24486091Sjhb return (EIO); 24586091Sjhb } 24686091Sjhb#ifdef BD_SUPPORT_FRAGS 247189749Sjhb DEBUG("frag read %d from %lld+%d to %p", 24886091Sjhb fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE)); 249189749Sjhb if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) { 25086091Sjhb DEBUG("frag read error"); 25186091Sjhb return(EIO); 25286091Sjhb } 25386091Sjhb bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize); 25486091Sjhb#endif 25586091Sjhb if (rsize) 25686091Sjhb *rsize = size; 25786091Sjhb return (0); 25886091Sjhb} 25986091Sjhb 260189749Sjhb/* Max number of sectors to bounce-buffer at a time. */ 261189749Sjhb#define CD_BOUNCEBUF 8 262189749Sjhb 26386091Sjhbstatic int 26486091Sjhbbc_read(int unit, daddr_t dblk, int blks, caddr_t dest) 26586091Sjhb{ 266189749Sjhb u_int maxfer, resid, result, retry, x; 267189749Sjhb caddr_t bbuf, p, xp; 268189749Sjhb static struct edd_packet packet; 26986091Sjhb int biosdev; 27086091Sjhb#ifdef DISK_DEBUG 27186091Sjhb int error; 27286091Sjhb#endif 27386091Sjhb 27486091Sjhb /* Just in case some idiot actually tries to read -1 blocks... */ 27586091Sjhb if (blks < 0) 27686091Sjhb return (-1); 27786091Sjhb 27886091Sjhb /* If nothing to do, just return succcess. */ 27986091Sjhb if (blks == 0) 28086091Sjhb return (0); 28186091Sjhb 282189749Sjhb /* Decide whether we have to bounce */ 283189749Sjhb if (VTOP(dest) >> 20 != 0) { 284189749Sjhb /* 285189749Sjhb * The destination buffer is above first 1MB of 286189749Sjhb * physical memory so we have to arrange a suitable 287189749Sjhb * bounce buffer. 288189749Sjhb */ 289189749Sjhb x = min(CD_BOUNCEBUF, (unsigned)blks); 290189749Sjhb bbuf = alloca(x * BIOSCD_SECSIZE); 291189749Sjhb maxfer = x; 292189749Sjhb } else { 293189749Sjhb bbuf = NULL; 294189749Sjhb maxfer = 0; 295189749Sjhb } 296189749Sjhb 29786091Sjhb biosdev = bc_unit2bios(unit); 298189749Sjhb resid = blks; 299189749Sjhb p = dest; 300189749Sjhb 301189749Sjhb while (resid > 0) { 302189749Sjhb if (bbuf) 303189749Sjhb xp = bbuf; 304189749Sjhb else 305189749Sjhb xp = p; 306189749Sjhb x = resid; 307189749Sjhb if (maxfer > 0) 308189749Sjhb x = min(x, maxfer); 309189749Sjhb 310189749Sjhb /* 311189749Sjhb * Loop retrying the operation a couple of times. The BIOS 312189749Sjhb * may also retry. 313189749Sjhb */ 314189749Sjhb for (retry = 0; retry < 3; retry++) { 315189749Sjhb /* If retrying, reset the drive */ 316189749Sjhb if (retry > 0) { 317189749Sjhb v86.ctl = V86_FLAGS; 318189749Sjhb v86.addr = 0x13; 319189749Sjhb v86.eax = 0; 320189749Sjhb v86.edx = biosdev; 321189749Sjhb v86int(); 322189749Sjhb } 323189749Sjhb 324227400Sjhb packet.len = sizeof(struct edd_packet); 325189749Sjhb packet.count = x; 326227400Sjhb packet.off = VTOPOFF(xp); 327189749Sjhb packet.seg = VTOPSEG(xp); 328189749Sjhb packet.lba = dblk; 32986091Sjhb v86.ctl = V86_FLAGS; 33086091Sjhb v86.addr = 0x13; 331189749Sjhb v86.eax = 0x4200; 33286091Sjhb v86.edx = biosdev; 333189749Sjhb v86.ds = VTOPSEG(&packet); 334189749Sjhb v86.esi = VTOPOFF(&packet); 33586091Sjhb v86int(); 336229501Sjhb result = V86_CY(v86.efl); 337189749Sjhb if (result == 0) 338189749Sjhb break; 33986091Sjhb } 34086091Sjhb 34186091Sjhb#ifdef DISK_DEBUG 342189749Sjhb error = (v86.eax >> 8) & 0xff; 34386091Sjhb#endif 344189749Sjhb DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p, 345189749Sjhb VTOP(p), result ? "failed" : "ok"); 346189749Sjhb DEBUG("unit %d status 0x%x", unit, error); 347189749Sjhb if (bbuf != NULL) 348189749Sjhb bcopy(bbuf, p, x * BIOSCD_SECSIZE); 349189749Sjhb p += (x * BIOSCD_SECSIZE); 350189749Sjhb dblk += x; 351189749Sjhb resid -= x; 352189749Sjhb } 35386091Sjhb 35486091Sjhb/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */ 35586091Sjhb return(0); 35686091Sjhb} 35786091Sjhb 35886091Sjhb/* 359130603Sphk * Return a suitable dev_t value for (dev). 36086091Sjhb */ 36186091Sjhbint 36286091Sjhbbc_getdev(struct i386_devdesc *dev) 36386091Sjhb{ 36486091Sjhb int biosdev, unit; 36586091Sjhb int major; 36686091Sjhb int rootdev; 36786091Sjhb 368163897Smarcel unit = dev->d_unit; 36986091Sjhb biosdev = bc_unit2bios(unit); 37086091Sjhb DEBUG("unit %d BIOS device %d", unit, biosdev); 37186091Sjhb if (biosdev == -1) /* not a BIOS device */ 37286091Sjhb return(-1); 37386091Sjhb 37486091Sjhb /* 37586091Sjhb * XXX: Need to examine device spec here to figure out if SCSI or 37686091Sjhb * ATAPI. No idea on how to figure out device number. All we can 37786091Sjhb * really pass to the kernel is what bus and device on which bus we 378130603Sphk * were booted from, which dev_t isn't well suited to since those 37986091Sjhb * number don't match to unit numbers very well. We may just need 38086091Sjhb * to engage in a hack where we pass -C to the boot args if we are 38186091Sjhb * the boot device. 38286091Sjhb */ 38386091Sjhb major = ACDMAJOR; 38486091Sjhb unit = 0; /* XXX */ 38586091Sjhb 38686091Sjhb /* XXX: Assume partition 'a'. */ 387172921Sjhb rootdev = MAKEBOOTDEV(major, 0, unit, 0); 38886091Sjhb DEBUG("dev is 0x%x\n", rootdev); 38986091Sjhb return(rootdev); 39086091Sjhb} 391