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: stable/11/stand/pc98/libpc98/bioscd.c 333049 2018-04-27 02:39:36Z nyan $"); 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; 87298230Sallanjude int bc_open; /* reference counter */ 88298230Sallanjude void *bc_bcache; /* buffer cache data */ 89158559Snyan} bcinfo [MAXBCDEV]; 90158559Snyanstatic int nbcinfo = 0; 91158559Snyan 92332154Skevans#define BC(dev) (bcinfo[(dev)->dd.d_unit]) 93298230Sallanjude 94158559Snyanstatic int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest); 95158559Snyanstatic int bc_init(void); 96158559Snyanstatic int bc_strategy(void *devdata, int flag, daddr_t dblk, 97333049Snyan size_t size, char *buf, size_t *rsize); 98298230Sallanjudestatic int bc_realstrategy(void *devdata, int flag, daddr_t dblk, 99333049Snyan size_t size, char *buf, size_t *rsize); 100158559Snyanstatic int bc_open(struct open_file *f, ...); 101158559Snyanstatic int bc_close(struct open_file *f); 102328889Skevansstatic int bc_print(int verbose); 103158559Snyan 104158559Snyanstruct devsw bioscd = { 105158559Snyan "cd", 106158559Snyan DEVT_CD, 107158559Snyan bc_init, 108158559Snyan bc_strategy, 109158559Snyan bc_open, 110158559Snyan bc_close, 111158559Snyan noioctl, 112158559Snyan bc_print, 113158559Snyan NULL 114158559Snyan}; 115158559Snyan 116158559Snyan/* 117158559Snyan * Translate between BIOS device numbers and our private unit numbers. 118158559Snyan */ 119158559Snyanint 120158559Snyanbc_bios2unit(int biosdev) 121158559Snyan{ 122158559Snyan int i; 123158559Snyan 124158559Snyan DEBUG("looking for bios device 0x%x", biosdev); 125158559Snyan for (i = 0; i < nbcinfo; i++) { 126158559Snyan DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit); 127158559Snyan if (bcinfo[i].bc_unit == biosdev) 128158559Snyan return(i); 129158559Snyan } 130158559Snyan return(-1); 131158559Snyan} 132158559Snyan 133158559Snyanint 134158559Snyanbc_unit2bios(int unit) 135158559Snyan{ 136158559Snyan if ((unit >= 0) && (unit < nbcinfo)) 137158559Snyan return(bcinfo[unit].bc_unit); 138158559Snyan return(-1); 139158559Snyan} 140158559Snyan 141158559Snyan/* 142158559Snyan * We can't quiz, we have to be told what device to use, so this functoin 143158559Snyan * doesn't do anything. Instead, the loader calls bc_add() with the BIOS 144158559Snyan * device number to add. 145158559Snyan */ 146158559Snyanstatic int 147158559Snyanbc_init(void) 148158559Snyan{ 149158559Snyan 150158559Snyan return (0); 151158559Snyan} 152158559Snyan 153158559Snyanint 154158559Snyanbc_add(int biosdev) 155158559Snyan{ 156158559Snyan 157158559Snyan if (nbcinfo >= MAXBCDEV) 158158559Snyan return (-1); 159158559Snyan bcinfo[nbcinfo].bc_unit = biosdev; 160158559Snyan 161158559Snyan /* SCSI CD-ROM only */ 162158559Snyan if ((biosdev & 0xf0) != 0xa0) 163158559Snyan return (-1); 164158559Snyan if ((((uint32_t *)PTOV(0xA1460))[biosdev & 0x0f] & 0x1f) != 5) 165158559Snyan return (-1); 166158559Snyan 167158559Snyan printf("BIOS CD is cd%d\n", nbcinfo); 168158559Snyan nbcinfo++; 169298230Sallanjude bcache_add_dev(nbcinfo); /* register cd device in bcache */ 170158559Snyan return(0); 171158559Snyan} 172158559Snyan 173158559Snyan/* 174158559Snyan * Print information about disks 175158559Snyan */ 176328889Skevansstatic int 177158559Snyanbc_print(int verbose) 178158559Snyan{ 179190146Snyan char line[80]; 180328889Skevans int i, ret = 0; 181190146Snyan 182328889Skevans if (nbcinfo == 0) 183328889Skevans return (0); 184328889Skevans 185328889Skevans printf("%s devices:", bioscd.dv_name); 186328889Skevans if ((ret = pager_output("\n")) != 0) 187328889Skevans return (ret); 188328889Skevans 189158559Snyan for (i = 0; i < nbcinfo; i++) { 190158559Snyan sprintf(line, " cd%d: Device 0x%x\n", i, 191158559Snyan bcinfo[i].bc_sp.sp_devicespec); 192328889Skevans if ((ret = pager_output(line)) != 0) 193300117Simp break; 194158559Snyan } 195328889Skevans return (ret); 196158559Snyan} 197158559Snyan 198158559Snyan/* 199158559Snyan * Attempt to open the disk described by (dev) for use by (f). 200158559Snyan */ 201158559Snyanstatic int 202158559Snyanbc_open(struct open_file *f, ...) 203158559Snyan{ 204158559Snyan va_list ap; 205158559Snyan struct i386_devdesc *dev; 206158559Snyan 207158559Snyan va_start(ap, f); 208158559Snyan dev = va_arg(ap, struct i386_devdesc *); 209158559Snyan va_end(ap); 210332154Skevans if (dev->dd.d_unit >= nbcinfo) { 211158559Snyan DEBUG("attempt to open nonexistent disk"); 212158559Snyan return(ENXIO); 213158559Snyan } 214158559Snyan 215298230Sallanjude BC(dev).bc_open++; 216298230Sallanjude if (BC(dev).bc_bcache == NULL) 217298230Sallanjude BC(dev).bc_bcache = bcache_allocate(); 218158559Snyan return(0); 219158559Snyan} 220158559Snyan 221158559Snyanstatic int 222158559Snyanbc_close(struct open_file *f) 223158559Snyan{ 224298230Sallanjude struct i386_devdesc *dev; 225158559Snyan 226298230Sallanjude dev = (struct i386_devdesc *)f->f_devdata; 227298230Sallanjude BC(dev).bc_open--; 228298230Sallanjude if (BC(dev).bc_open == 0) { 229298230Sallanjude bcache_free(BC(dev).bc_bcache); 230298230Sallanjude BC(dev).bc_bcache = NULL; 231298230Sallanjude } 232158559Snyan return(0); 233158559Snyan} 234158559Snyan 235298230Sallanjudestatic int 236313355Stsoomebc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, 237298230Sallanjude char *buf, size_t *rsize) 238298230Sallanjude{ 239298230Sallanjude struct bcache_devdata bcd; 240298230Sallanjude struct i386_devdesc *dev; 241298230Sallanjude 242298230Sallanjude dev = (struct i386_devdesc *)devdata; 243298230Sallanjude bcd.dv_strategy = bc_realstrategy; 244298230Sallanjude bcd.dv_devdata = devdata; 245298230Sallanjude bcd.dv_cache = BC(dev).bc_bcache; 246298230Sallanjude 247313355Stsoome return (bcache_strategy(&bcd, rw, dblk, size, buf, rsize)); 248298230Sallanjude} 249298230Sallanjude 250158559Snyanstatic int 251313355Stsoomebc_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, 252298230Sallanjude char *buf, size_t *rsize) 253158559Snyan{ 254158559Snyan struct i386_devdesc *dev; 255158559Snyan int unit; 256158559Snyan int blks; 257158559Snyan#ifdef BD_SUPPORT_FRAGS 258158559Snyan char fragbuf[BIOSCD_SECSIZE]; 259158559Snyan size_t fragsize; 260158559Snyan 261158559Snyan fragsize = size % BIOSCD_SECSIZE; 262158559Snyan#else 263158559Snyan if (size % BIOSCD_SECSIZE) 264158559Snyan return (EINVAL); 265158559Snyan#endif 266158559Snyan 267158559Snyan if (rw != F_READ) 268158559Snyan return(EROFS); 269158559Snyan dev = (struct i386_devdesc *)devdata; 270332154Skevans unit = dev->dd.d_unit; 271158559Snyan blks = size / BIOSCD_SECSIZE; 272158559Snyan if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0) 273158559Snyan return (EINVAL); 274158559Snyan dblk /= (BIOSCD_SECSIZE / DEV_BSIZE); 275190146Snyan DEBUG("read %d from %lld to %p", blks, dblk, buf); 276158559Snyan 277158559Snyan if (rsize) 278158559Snyan *rsize = 0; 279158559Snyan if (blks && bc_read(unit, dblk, blks, buf)) { 280158559Snyan DEBUG("read error"); 281158559Snyan return (EIO); 282158559Snyan } 283158559Snyan#ifdef BD_SUPPORT_FRAGS 284190146Snyan DEBUG("frag read %d from %lld+%d to %p", 285158559Snyan fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE)); 286190146Snyan if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) { 287158559Snyan DEBUG("frag read error"); 288158559Snyan return(EIO); 289158559Snyan } 290158559Snyan bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize); 291158559Snyan#endif 292158559Snyan if (rsize) 293158559Snyan *rsize = size; 294158559Snyan return (0); 295158559Snyan} 296158559Snyan 297190146Snyan/* Max number of sectors to bounce-buffer at a time. */ 298190146Snyan#define CD_BOUNCEBUF 8 299190146Snyan 300158559Snyanstatic int 301158559Snyanbc_read(int unit, daddr_t dblk, int blks, caddr_t dest) 302158559Snyan{ 303190146Snyan u_int maxfer, resid, result, retry, x; 304190146Snyan caddr_t bbuf, p, xp; 305158559Snyan int biosdev; 306158559Snyan#ifdef DISK_DEBUG 307158559Snyan int error; 308158559Snyan#endif 309158559Snyan 310158559Snyan /* Just in case some idiot actually tries to read -1 blocks... */ 311158559Snyan if (blks < 0) 312158559Snyan return (-1); 313158559Snyan 314158559Snyan /* If nothing to do, just return succcess. */ 315158559Snyan if (blks == 0) 316158559Snyan return (0); 317158559Snyan 318190146Snyan /* Decide whether we have to bounce */ 319190146Snyan if (VTOP(dest) >> 20 != 0) { 320190146Snyan /* 321190146Snyan * The destination buffer is above first 1MB of 322190146Snyan * physical memory so we have to arrange a suitable 323190146Snyan * bounce buffer. 324190146Snyan */ 325190146Snyan x = min(CD_BOUNCEBUF, (unsigned)blks); 326190146Snyan bbuf = alloca(x * BIOSCD_SECSIZE); 327190146Snyan maxfer = x; 328190146Snyan } else { 329190146Snyan bbuf = NULL; 330190146Snyan maxfer = 0; 331190146Snyan } 332190146Snyan 333158559Snyan biosdev = bc_unit2bios(unit); 334190146Snyan resid = blks; 335190146Snyan p = dest; 336190146Snyan 337190146Snyan while (resid > 0) { 338190146Snyan if (bbuf) 339190146Snyan xp = bbuf; 340190146Snyan else 341190146Snyan xp = p; 342190146Snyan x = resid; 343190146Snyan if (maxfer > 0) 344190146Snyan x = min(x, maxfer); 345190146Snyan 346190146Snyan /* 347190146Snyan * Loop retrying the operation a couple of times. The BIOS 348190146Snyan * may also retry. 349190146Snyan */ 350190146Snyan for (retry = 0; retry < 3; retry++) { 351190146Snyan /* If retrying, reset the drive */ 352190146Snyan if (retry > 0) { 353190146Snyan v86.ctl = V86_FLAGS; 354190146Snyan v86.addr = 0x1b; 355190146Snyan v86.eax = 0x0300 | biosdev; 356190146Snyan v86int(); 357190146Snyan } 358190146Snyan 359158559Snyan v86.ctl = V86_FLAGS; 360158559Snyan v86.addr = 0x1b; 361190146Snyan v86.eax = 0x0600 | (biosdev & 0x7f); 362190147Snyan v86.ebx = x * BIOSCD_SECSIZE; 363190146Snyan v86.ecx = dblk & 0xffff; 364190146Snyan v86.edx = (dblk >> 16) & 0xffff; 365190147Snyan v86.ebp = VTOPOFF(xp); 366190147Snyan v86.es = VTOPSEG(xp); 367158559Snyan v86int(); 368226746Sjhb result = V86_CY(v86.efl); 369190146Snyan if (result == 0) 370190146Snyan break; 371158559Snyan } 372158559Snyan 373158559Snyan#ifdef DISK_DEBUG 374190146Snyan error = (v86.eax >> 8) & 0xff; 375158559Snyan#endif 376190146Snyan DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p, 377190146Snyan VTOP(p), result ? "failed" : "ok"); 378190146Snyan DEBUG("unit %d status 0x%x", unit, error); 379190146Snyan if (bbuf != NULL) 380190146Snyan bcopy(bbuf, p, x * BIOSCD_SECSIZE); 381190146Snyan p += (x * BIOSCD_SECSIZE); 382190146Snyan dblk += x; 383190146Snyan resid -= x; 384190146Snyan } 385158559Snyan 386158559Snyan/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */ 387158559Snyan return(0); 388158559Snyan} 389158559Snyan 390158559Snyan/* 391158559Snyan * Return a suitable dev_t value for (dev). 392158559Snyan */ 393158559Snyanint 394158559Snyanbc_getdev(struct i386_devdesc *dev) 395158559Snyan{ 396158559Snyan int biosdev, unit, device; 397158559Snyan int major; 398158559Snyan int rootdev; 399158559Snyan 400332154Skevans unit = dev->dd.d_unit; 401158559Snyan biosdev = bc_unit2bios(unit); 402158559Snyan DEBUG("unit %d BIOS device %d", unit, biosdev); 403158559Snyan if (biosdev == -1) /* not a BIOS device */ 404158559Snyan return(-1); 405158559Snyan 406158559Snyan device = biosdev & 0xf0; 407158559Snyan if (device == 0x80) 408158559Snyan major = ACDMAJOR; 409158559Snyan else if (device == 0xa0) 410158559Snyan major = CDMAJOR; 411158559Snyan else 412158559Snyan return (-1); 413158559Snyan 414158559Snyan unit = 0; /* XXX */ 415158559Snyan 416158559Snyan /* XXX: Assume partition 'a'. */ 417172921Sjhb rootdev = MAKEBOOTDEV(major, 0, unit, 0); 418158559Snyan DEBUG("dev is 0x%x\n", rootdev); 419158559Snyan return(rootdev); 420158559Snyan} 421