143561Skato/*- 243561Skato * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 343561Skato * All rights reserved. 443561Skato * 543561Skato * Redistribution and use in source and binary forms, with or without 643561Skato * modification, are permitted provided that the following conditions 743561Skato * are met: 843561Skato * 1. Redistributions of source code must retain the above copyright 943561Skato * notice, this list of conditions and the following disclaimer. 1043561Skato * 2. Redistributions in binary form must reproduce the above copyright 1143561Skato * notice, this list of conditions and the following disclaimer in the 1243561Skato * documentation and/or other materials provided with the distribution. 1343561Skato * 1443561Skato * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1543561Skato * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1643561Skato * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1743561Skato * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1843561Skato * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1943561Skato * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2043561Skato * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2143561Skato * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2243561Skato * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2343561Skato * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2443561Skato * SUCH DAMAGE. 2543561Skato */ 2643561Skato 27119880Sobrien#include <sys/cdefs.h> 28119880Sobrien__FBSDID("$FreeBSD: stable/11/stand/pc98/libpc98/biosdisk.c 344421 2019-02-21 06:02:51Z kevans $"); 29119880Sobrien 3043561Skato/* 3143561Skato * BIOS disk device handling. 3243561Skato * 3343561Skato * Ideas and algorithms from: 3443561Skato * 3543561Skato * - NetBSD libi386/biosdisk.c 3643561Skato * - FreeBSD biosboot/disk.c 3743561Skato * 3843561Skato */ 3943561Skato 40339406Simp#include <sys/disk.h> 41339406Simp#include <sys/limits.h> 4243561Skato#include <stand.h> 43339406Simp#include <machine/bootinfo.h> 44339406Simp#include <stdarg.h> 4543561Skato 4643561Skato#include <sys/disklabel.h> 47104621Snyan#include <sys/diskpc98.h> 4843561Skato 4943561Skato#include <bootstrap.h> 5043561Skato#include <btxv86.h> 51339406Simp#include "disk.h" 5243561Skato#include "libi386.h" 5343561Skato 5463101Snyan#define BIOS_NUMDRIVES 0x475 5543561Skato#define BIOSDISK_SECSIZE 512 5643561Skato#define BUFSIZE (1 * BIOSDISK_SECSIZE) 5743561Skato 5843561Skato#define DT_ATAPI 0x10 /* disk type for ATAPI floppies */ 5943561Skato#define WDMAJOR 0 /* major numbers for devices we frontend for */ 6043561Skato#define WFDMAJOR 1 6143561Skato#define FDMAJOR 2 6243561Skato#define DAMAJOR 4 6343561Skato 6443561Skato#ifdef DISK_DEBUG 6587599Sobrien# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 6643561Skato#else 6743561Skato# define DEBUG(fmt, args...) 6843561Skato#endif 6943561Skato 70339406Simp/* 71339406Simp * List of BIOS devices, translation from disk unit number to 72339406Simp * BIOS unit number. 73339406Simp */ 74339406Simpstatic struct bdinfo 75339406Simp{ 76339406Simp int bd_unit; /* BIOS unit number */ 77339406Simp int bd_cyl; /* BIOS geometry */ 78339406Simp int bd_hds; 79339406Simp int bd_sec; 80339406Simp int bd_flags; 8159777Snyan#define BD_MODEINT13 0x0000 8259777Snyan#define BD_MODEEDD1 0x0001 8359777Snyan#define BD_MODEEDD3 0x0002 8463101Snyan#define BD_MODEMASK 0x0003 8559777Snyan#define BD_FLOPPY 0x0004 8659777Snyan#define BD_LABELOK 0x0008 8759777Snyan#define BD_PARTTABOK 0x0010 88108791Snyan#define BD_OPTICAL 0x0020 89333049Snyan int bd_type; /* BIOS 'drive type' (floppy only) */ 90339406Simp uint16_t bd_sectorsize; /* Sector size */ 91339406Simp uint64_t bd_sectors; /* Disk size */ 92333049Snyan int bd_da_unit; /* kernel unit number for da */ 93333049Snyan int bd_open; /* reference counter */ 94333049Snyan void *bd_bcache; /* buffer cache data */ 9543561Skato} bdinfo [MAXBDDEV]; 9643561Skatostatic int nbdinfo = 0; 9743561Skato 98332154Skevans#define BD(dev) (bdinfo[(dev)->dd.d_unit]) 99298230Sallanjude 100339406Simpstatic int bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks, 101333049Snyan caddr_t dest); 102339406Simpstatic int bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks, 103333049Snyan caddr_t dest); 104333049Snyanstatic int bd_int13probe(struct bdinfo *bd); 10543561Skato 106333049Snyanstatic int bd_init(void); 107333049Snyanstatic int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size, 108333049Snyan char *buf, size_t *rsize); 109333049Snyanstatic int bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size, 110333049Snyan char *buf, size_t *rsize); 111333049Snyanstatic int bd_open(struct open_file *f, ...); 112333049Snyanstatic int bd_close(struct open_file *f); 113339406Simpstatic int bd_ioctl(struct open_file *f, u_long cmd, void *data); 114333049Snyanstatic int bd_print(int verbose); 11543561Skato 11643561Skatostruct devsw biosdisk = { 117333049Snyan "disk", 118333049Snyan DEVT_DISK, 119333049Snyan bd_init, 120333049Snyan bd_strategy, 121333049Snyan bd_open, 122333049Snyan bd_close, 123339406Simp bd_ioctl, 124333049Snyan bd_print, 125333049Snyan NULL 12643561Skato}; 12743561Skato 12843561Skato/* 12943561Skato * Translate between BIOS device numbers and our private unit numbers. 13043561Skato */ 13143561Skatoint 13243561Skatobd_bios2unit(int biosdev) 13343561Skato{ 134333049Snyan int i; 135333049Snyan 136333049Snyan DEBUG("looking for bios device 0x%x", biosdev); 137333049Snyan for (i = 0; i < nbdinfo; i++) { 138333049Snyan DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit); 139333049Snyan if (bdinfo[i].bd_unit == biosdev) 140333049Snyan return (i); 141333049Snyan } 142333049Snyan return (-1); 14343561Skato} 14443561Skato 14543561Skatoint 14643561Skatobd_unit2bios(int unit) 14743561Skato{ 148333049Snyan 149333049Snyan if ((unit >= 0) && (unit < nbdinfo)) 150333049Snyan return (bdinfo[unit].bd_unit); 151333049Snyan return (-1); 15243561Skato} 15343561Skato 154333049Snyan/* 15543561Skato * Quiz the BIOS for disk devices, save a little info about them. 15643561Skato */ 15743561Skatostatic int 158333049Snyanbd_init(void) 15943561Skato{ 160333049Snyan int base, unit; 161333049Snyan int da_drive=0, n=-0x10; 16243561Skato 163333049Snyan /* sequence 0x90, 0x80, 0xa0 */ 164333049Snyan for (base = 0x90; base <= 0xa0; base += n, n += 0x30) { 165333049Snyan for (unit = base; (nbdinfo < MAXBDDEV) || ((unit & 0x0f) < 4); 166333049Snyan unit++) { 167333049Snyan bdinfo[nbdinfo].bd_open = 0; 168333049Snyan bdinfo[nbdinfo].bd_bcache = NULL; 169333049Snyan bdinfo[nbdinfo].bd_unit = unit; 170333049Snyan bdinfo[nbdinfo].bd_flags = 171333049Snyan (unit & 0xf0) == 0x90 ? BD_FLOPPY : 0; 172333049Snyan if (!bd_int13probe(&bdinfo[nbdinfo])) { 173333049Snyan if (((unit & 0xf0) == 0x90 && 174333049Snyan (unit & 0x0f) < 4) || 175333049Snyan ((unit & 0xf0) == 0xa0 && 176333049Snyan (unit & 0x0f) < 6)) 177333049Snyan /* Target IDs are not contiguous. */ 178333049Snyan continue; 179333049Snyan else 180333049Snyan break; 181333049Snyan } 18243561Skato 183333049Snyan if (bdinfo[nbdinfo].bd_flags & BD_FLOPPY) { 184333049Snyan /* available 1.44MB access? */ 185333049Snyan if (*(u_char *)PTOV(0xA15AE) & 186333049Snyan (1<<(unit & 0xf))) { 187333049Snyan /* boot media 1.2MB FD? */ 188333049Snyan if ((*(u_char *)PTOV(0xA1584) & 189333049Snyan 0xf0) != 0x90) 190333049Snyan bdinfo[nbdinfo].bd_unit = 191333049Snyan 0x30 + (unit & 0xf); 192333049Snyan } 193333049Snyan } else { 194333049Snyan if ((unit & 0xF0) == 0xA0) /* SCSI HD or MO */ 195333049Snyan bdinfo[nbdinfo].bd_da_unit = 196333049Snyan da_drive++; 197333049Snyan } 198333049Snyan /* XXX we need "disk aliases" to make this simpler */ 199333049Snyan printf("BIOS drive %c: is disk%d\n", 200333049Snyan 'A' + nbdinfo, nbdinfo); 201333049Snyan nbdinfo++; 20244467Skato } 20343561Skato } 204333049Snyan bcache_add_dev(nbdinfo); 205333049Snyan return(0); 20643561Skato} 20743561Skato 20843561Skato/* 20943561Skato * Try to detect a device supported by the legacy int13 BIOS 21043561Skato */ 21143561Skatostatic int 21243561Skatobd_int13probe(struct bdinfo *bd) 21343561Skato{ 214333049Snyan int addr; 21563101Snyan 216333049Snyan if (bd->bd_flags & BD_FLOPPY) { 217333049Snyan addr = 0xa155c; 218333049Snyan } else { 219333049Snyan if ((bd->bd_unit & 0xf0) == 0x80) 220333049Snyan addr = 0xa155d; 221333049Snyan else 222333049Snyan addr = 0xa1482; 223333049Snyan } 224333049Snyan if ( *(u_char *)PTOV(addr) & (1<<(bd->bd_unit & 0x0f))) { 225333049Snyan bd->bd_flags |= BD_MODEINT13; 226333049Snyan return (1); 227333049Snyan } 228333049Snyan if ((bd->bd_unit & 0xF0) == 0xA0) { 229333049Snyan int media = 230333049Snyan ((unsigned *)PTOV(0xA1460))[bd->bd_unit & 0x0F] & 0x1F; 231108791Snyan 232333049Snyan if (media == 7) { /* MO */ 233333049Snyan bd->bd_flags |= BD_MODEINT13 | BD_OPTICAL; 234333049Snyan return(1); 235333049Snyan } 236108791Snyan } 237333049Snyan return (0); 23843561Skato} 23943561Skato 24043561Skato/* 24143561Skato * Print information about disks 24243561Skato */ 243328889Skevansstatic int 24443561Skatobd_print(int verbose) 24543561Skato{ 246333049Snyan char line[80]; 247339406Simp struct disk_devdesc dev; 248339406Simp int i, ret = 0; 249333049Snyan struct pc98_partition *dptr; 25043561Skato 251333049Snyan if (nbdinfo == 0) 252333049Snyan return (0); 25343561Skato 254333049Snyan printf("%s devices:", biosdisk.dv_name); 255333049Snyan if ((ret = pager_output("\n")) != 0) 256333049Snyan return (ret); 257328889Skevans 258333049Snyan for (i = 0; i < nbdinfo; i++) { 259339406Simp snprintf(line, sizeof(line), 260339406Simp " disk%d: BIOS drive %c (%ju X %u):\n", i, 261339406Simp (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit): 262339406Simp ('C' + bdinfo[i].bd_unit - 0x80), 263339406Simp (uintmax_t)bdinfo[i].bd_sectors, 264339406Simp bdinfo[i].bd_sectorsize); 265333049Snyan if ((ret = pager_output(line)) != 0) 266333049Snyan break; 267328889Skevans 268333049Snyan /* try to open the whole disk */ 269339406Simp dev.dd.d_dev = &biosdisk; 270333049Snyan dev.dd.d_unit = i; 271339406Simp dev.d_slice = -1; 272339406Simp dev.d_partition = -1; 273339406Simp if (disk_open(&dev, 274339406Simp bdinfo[i].bd_sectorsize * bdinfo[i].bd_sectors, 275339406Simp bdinfo[i].bd_sectorsize) == 0) { 276339406Simp snprintf(line, sizeof(line), " disk%d", i); 277339406Simp ret = disk_print(&dev, line, verbose); 278339406Simp disk_close(&dev); 279333049Snyan if (ret != 0) 280339406Simp return (ret); 28143561Skato } 28243561Skato } 283333049Snyan return (ret); 28443561Skato} 28543561Skato 286176654Snyan/* Given a size in 512 byte sectors, convert it to a human-readable number. */ 287176654Snyanstatic char * 288176654Snyandisplay_size(uint64_t size) 289176654Snyan{ 290333049Snyan static char buf[80]; 291333049Snyan char unit; 292176654Snyan 293333049Snyan size /= 2; 294333049Snyan unit = 'K'; 295333049Snyan if (size >= 10485760000LL) { 296333049Snyan size /= 1073741824; 297333049Snyan unit = 'T'; 298333049Snyan } else if (size >= 10240000) { 299333049Snyan size /= 1048576; 300333049Snyan unit = 'G'; 301333049Snyan } else if (size >= 10000) { 302333049Snyan size /= 1024; 303333049Snyan unit = 'M'; 304333049Snyan } 305333049Snyan sprintf(buf, "%6ld%cB", (long)size, unit); 306333049Snyan return (buf); 307176654Snyan} 308176654Snyan 30959777Snyan/* 31043561Skato * Attempt to open the disk described by (dev) for use by (f). 31143561Skato * 31243561Skato * Note that the philosophy here is "give them exactly what 31343561Skato * they ask for". This is necessary because being too "smart" 31443561Skato * about what the user might want leads to complications. 31543561Skato * (eg. given no slice or partition value, with a disk that is 31643561Skato * sliced - are they after the first BSD slice, or the DOS 31743561Skato * slice before it?) 31843561Skato */ 31943561Skatostatic int 32043561Skatobd_open(struct open_file *f, ...) 32143561Skato{ 322339406Simp va_list ap; 323339406Simp struct disk_devdesc *dev; 324339406Simp struct disk_devdesc disk; 325339406Simp int err; 326339406Simp uint64_t size; 32743561Skato 328339406Simp va_start(ap, f); 329339406Simp dev = va_arg(ap, struct disk_devdesc *); 330339406Simp va_end(ap); 33143561Skato 332339406Simp if (dev->dd.d_unit < 0 || dev->dd.d_unit >= nbdinfo) 333339406Simp return (EIO); 334339406Simp BD(dev).bd_open++; 335339406Simp if (BD(dev).bd_bcache == NULL) 336339406Simp BD(dev).bd_bcache = bcache_allocate(); 337298230Sallanjude 338339406Simp /* 339339406Simp * Read disk size from partition. 340339406Simp * This is needed to work around buggy BIOS systems returning 341339406Simp * wrong (truncated) disk media size. 342339406Simp * During bd_probe() we tested if the mulitplication of bd_sectors 343339406Simp * would overflow so it should be safe to perform here. 344339406Simp */ 345339406Simp disk.dd.d_dev = dev->dd.d_dev; 346339406Simp disk.dd.d_unit = dev->dd.d_unit; 347339406Simp disk.d_slice = -1; 348339406Simp disk.d_partition = -1; 349339406Simp disk.d_offset = 0; 350339406Simp if (disk_open(&disk, BD(dev).bd_sectors * BD(dev).bd_sectorsize, 351339406Simp BD(dev).bd_sectorsize) == 0) { 35243561Skato 353339406Simp if (disk_ioctl(&disk, DIOCGMEDIASIZE, &size) == 0) { 354339406Simp size /= BD(dev).bd_sectorsize; 355339406Simp if (size > BD(dev).bd_sectors) 356339406Simp BD(dev).bd_sectors = size; 357339406Simp } 358339406Simp disk_close(&disk); 359339406Simp } 36043561Skato 361339406Simp err = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize, 362339406Simp BD(dev).bd_sectorsize); 363339406Simp return(err); 364172968Snyan} 365172968Snyan 366339406Simpstatic int 367339406Simpbd_close(struct open_file *f) 368172968Snyan{ 369339406Simp struct disk_devdesc *dev; 370172968Snyan 371339406Simp dev = (struct disk_devdesc *)f->f_devdata; 372339406Simp BD(dev).bd_open--; 373339406Simp if (BD(dev).bd_open == 0) { 374339406Simp bcache_free(BD(dev).bd_bcache); 375339406Simp BD(dev).bd_bcache = NULL; 37643561Skato } 377339406Simp return (disk_close(dev)); 37843561Skato} 37943561Skato 38043561Skatostatic int 381339406Simpbd_ioctl(struct open_file *f, u_long cmd, void *data) 38243561Skato{ 383339406Simp struct disk_devdesc *dev; 384339406Simp int rc; 38543561Skato 386339406Simp dev = (struct disk_devdesc *)f->f_devdata; 38759777Snyan 388339406Simp rc = disk_ioctl(dev, cmd, data); 389339406Simp if (rc != ENOTTY) 390339406Simp return (rc); 391339406Simp 392339406Simp switch (cmd) { 393339406Simp case DIOCGSECTORSIZE: 394339406Simp *(u_int *)data = BD(dev).bd_sectorsize; 395339406Simp break; 396339406Simp case DIOCGMEDIASIZE: 397339406Simp *(uint64_t *)data = BD(dev).bd_sectors * BD(dev).bd_sectorsize; 398339406Simp break; 399339406Simp default: 400339406Simp return (ENOTTY); 40143561Skato } 402339406Simp return (0); 40343561Skato} 40443561Skato 40543561Skatostatic int 406313355Stsoomebd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, 407298230Sallanjude char *buf, size_t *rsize) 40843561Skato{ 409339406Simp struct bcache_devdata bcd; 410339406Simp struct disk_devdesc *dev; 41158165Snyan 412339406Simp dev = (struct disk_devdesc *)devdata; 413339406Simp bcd.dv_strategy = bd_realstrategy; 414339406Simp bcd.dv_devdata = devdata; 415339406Simp bcd.dv_cache = BD(dev).bd_bcache; 416339406Simp return (bcache_strategy(&bcd, rw, dblk + dev->d_offset, 417339406Simp size, buf, rsize)); 41843561Skato} 41943561Skato 42043561Skatostatic int 421333049Snyanbd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, 422333049Snyan char *buf, size_t *rsize) 42343561Skato{ 424339406Simp struct disk_devdesc *dev = (struct disk_devdesc *)devdata; 425339406Simp uint64_t disk_blocks; 426339406Simp int blks, rc; 427339406Simp#ifdef BD_SUPPORT_FRAGS /* XXX: sector size */ 42843561Skato char fragbuf[BIOSDISK_SECSIZE]; 42943561Skato size_t fragsize; 43043561Skato 43143561Skato fragsize = size % BIOSDISK_SECSIZE; 43243561Skato#else 433339406Simp if (size % BD(dev).bd_sectorsize) 43443561Skato panic("bd_strategy: %d bytes I/O not multiple of block size", size); 43543561Skato#endif 43643561Skato 437339406Simp DEBUG("open_disk %p", dev); 438339406Simp 439339406Simp /* 440339406Simp * Check the value of the size argument. We do have quite small 441339406Simp * heap (64MB), but we do not know good upper limit, so we check against 442339406Simp * INT_MAX here. This will also protect us against possible overflows 443339406Simp * while translating block count to bytes. 444339406Simp */ 445339406Simp if (size > INT_MAX) { 446339406Simp DEBUG("too large read: %zu bytes", size); 447339406Simp return (EIO); 448339406Simp } 449339406Simp 450339406Simp blks = size / BD(dev).bd_sectorsize; 451339406Simp if (dblk > dblk + blks) 452339406Simp return (EIO); 453339406Simp 45443561Skato if (rsize) 45543561Skato *rsize = 0; 456172925Snyan 457339406Simp /* Get disk blocks, this value is either for whole disk or for partition */ 458339406Simp if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks)) { 459339406Simp /* DIOCGMEDIASIZE does return bytes. */ 460339406Simp disk_blocks /= BD(dev).bd_sectorsize; 461339406Simp } else { 462339406Simp /* We should not get here. Just try to survive. */ 463339406Simp disk_blocks = BD(dev).bd_sectors - dev->d_offset; 464339406Simp } 465339406Simp 466339406Simp /* Validate source block address. */ 467339406Simp if (dblk < dev->d_offset || dblk >= dev->d_offset + disk_blocks) 468339406Simp return (EIO); 469339406Simp 470339406Simp /* 471339406Simp * Truncate if we are crossing disk or partition end. 472339406Simp */ 473339406Simp if (dblk + blks >= dev->d_offset + disk_blocks) { 474339406Simp blks = dev->d_offset + disk_blocks - dblk; 475339406Simp size = blks * BD(dev).bd_sectorsize; 476339406Simp DEBUG("short read %d", blks); 477339406Simp } 478339406Simp 479339406Simp switch (rw & F_MASK) { 480172925Snyan case F_READ: 481339406Simp DEBUG("read %d from %lld to %p", blks, dblk, buf); 482172925Snyan 483339406Simp if (blks && (rc = bd_read(dev, dblk, blks, buf))) { 484339406Simp /* Filter out floppy controller errors */ 485339406Simp if (BD(dev).bd_flags != BD_FLOPPY || rc != 0x20) { 486339406Simp printf("read %d from %lld to %p, error: 0x%x", blks, dblk, 487339406Simp buf, rc); 488339406Simp } 489172925Snyan return (EIO); 490172925Snyan } 491339406Simp#ifdef BD_SUPPORT_FRAGS /* XXX: sector size */ 492172925Snyan DEBUG("bd_strategy: frag read %d from %d+%d to %p", 493172925Snyan fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE)); 494172925Snyan if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) { 495172925Snyan DEBUG("frag read error"); 496172925Snyan return(EIO); 497172925Snyan } 498172925Snyan bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize); 49943561Skato#endif 500172925Snyan break; 501172925Snyan case F_WRITE : 502172925Snyan DEBUG("write %d from %d to %p", blks, dblk, buf); 50387734Snyan 504339406Simp if (blks && bd_write(dev, dblk, blks, buf)) { 505172925Snyan DEBUG("write error"); 506172925Snyan return (EIO); 507172925Snyan } 50887734Snyan#ifdef BD_SUPPORT_FRAGS 50987734Snyan if(fragsize) { 510172925Snyan DEBUG("Attempted to write a frag"); 511172925Snyan return (EIO); 51287734Snyan } 51387734Snyan#endif 514172925Snyan break; 515172925Snyan default: 516172925Snyan /* DO NOTHING */ 517172925Snyan return (EROFS); 518172925Snyan } 51987734Snyan 52087734Snyan if (rsize) 52187734Snyan *rsize = size; 52287734Snyan return (0); 52343561Skato} 52443561Skato 52543561Skato/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */ 52643561Skato#define FLOPPY_BOUNCEBUF 18 52743561Skato 52843561Skatostatic int 529339406Simpbd_chs_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, 530339406Simp int write) 53143561Skato{ 532172965Snyan u_int x, bpc, cyl, hd, sec; 533172965Snyan 534339406Simp bpc = BD(dev).bd_sec * BD(dev).bd_hds; /* blocks per cylinder */ 535172965Snyan x = dblk; 536172965Snyan cyl = x / bpc; /* block # / blocks per cylinder */ 537172965Snyan x %= bpc; /* block offset into cylinder */ 538339406Simp hd = x / BD(dev).bd_sec; /* offset / blocks per track */ 539339406Simp sec = x % BD(dev).bd_sec; /* offset into track */ 540172965Snyan 541172965Snyan v86.ctl = V86_FLAGS; 542172965Snyan v86.addr = 0x1b; 543172965Snyan if (write) 544339406Simp v86.eax = 0x0500 | BD(dev).bd_unit; 545172965Snyan else 546339406Simp v86.eax = 0x0600 | BD(dev).bd_unit; 547339406Simp if (BD(dev).bd_flags & BD_FLOPPY) { 548172965Snyan v86.eax |= 0xd000; 549172965Snyan v86.ecx = 0x0200 | (cyl & 0xff); 550172965Snyan v86.edx = (hd << 8) | (sec + 1); 551339406Simp } else if (BD(dev).bd_flags & BD_OPTICAL) { 552172965Snyan v86.eax &= 0xFF7F; 553172965Snyan v86.ecx = dblk & 0xFFFF; 554172965Snyan v86.edx = dblk >> 16; 555172965Snyan } else { 556172965Snyan v86.ecx = cyl; 557172965Snyan v86.edx = (hd << 8) | sec; 558172965Snyan } 559172965Snyan v86.ebx = blks * BIOSDISK_SECSIZE; 560172965Snyan v86.es = VTOPSEG(dest); 561172965Snyan v86.ebp = VTOPOFF(dest); 562172965Snyan v86int(); 563292682Sjhb return (V86_CY(v86.efl)); 564172965Snyan} 565172965Snyan 566172965Snyanstatic int 567339406Simpbd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, int write) 568172965Snyan{ 569172965Snyan u_int x, sec, result, resid, retry, maxfer; 570339406Simp caddr_t p, xp, bbuf; 57143561Skato 572172965Snyan /* Just in case some idiot actually tries to read/write -1 blocks... */ 57368358Snyan if (blks < 0) 57468358Snyan return (-1); 57568358Snyan 57643561Skato resid = blks; 57743561Skato p = dest; 57843561Skato 57943561Skato /* Decide whether we have to bounce */ 580339406Simp if (VTOP(dest) >> 20 != 0 || (BD(dev).bd_unit < 0x80 && 581339406Simp (VTOP(dest) >> 16) != (VTOP(dest + 582339406Simp blks * BD(dev).bd_sectorsize) >> 16))) { 58343561Skato 58443561Skato /* 585172965Snyan * There is a 64k physical boundary somewhere in the 586172965Snyan * destination buffer, or the destination buffer is above 587172965Snyan * first 1MB of physical memory so we have to arrange a 588172965Snyan * suitable bounce buffer. Allocate a buffer twice as large 589172965Snyan * as we need to. Use the bottom half unless there is a break 590172965Snyan * there, in which case we use the top half. 59143561Skato */ 592339406Simp x = V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize; 593339406Simp x = min(x, (unsigned)blks); 594339406Simp bbuf = PTOV(V86_IO_BUFFER); 595172965Snyan maxfer = x; /* limit transfers to bounce region size */ 59643561Skato } else { 597339406Simp bbuf = NULL; 59843561Skato maxfer = 0; 59943561Skato } 60043561Skato 60143561Skato while (resid > 0) { 602172965Snyan /* 603172965Snyan * Play it safe and don't cross track boundaries. 604172965Snyan * (XXX this is probably unnecessary) 605172965Snyan */ 606339406Simp sec = dblk % BD(dev).bd_sec; /* offset into track */ 607339406Simp x = min(BD(dev).bd_sec - sec, resid); 60843561Skato if (maxfer > 0) 60943561Skato x = min(x, maxfer); /* fit bounce buffer */ 61043561Skato 61143561Skato /* where do we transfer to? */ 612339406Simp xp = bbuf == NULL ? p : bbuf; 61343561Skato 614172965Snyan /* 615172965Snyan * Put your Data In, Put your Data out, 616172965Snyan * Put your Data In, and shake it all about 617172965Snyan */ 618172965Snyan if (write && bbuf != NULL) 619339406Simp bcopy(p, bbuf, x * BD(dev).bd_sectorsize); 62043561Skato 621172965Snyan /* 622172965Snyan * Loop retrying the operation a couple of times. The BIOS 623172965Snyan * may also retry. 624172965Snyan */ 62543561Skato for (retry = 0; retry < 3; retry++) { 62643561Skato /* if retrying, reset the drive */ 62743561Skato if (retry > 0) { 62851586Skato v86.ctl = V86_FLAGS; 62951586Skato v86.addr = 0x1b; 630339406Simp v86.eax = 0x0300 | BD(dev).bd_unit; 63143561Skato v86int(); 63243561Skato } 633172965Snyan 634339406Simp result = bd_chs_io(dev, dblk, x, xp, write); 63543561Skato if (result == 0) 63643561Skato break; 63743561Skato } 638172965Snyan 639172965Snyan if (write) 640200631Snyan DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x, 641200631Snyan p, VTOP(p), dblk, result ? "failed" : "ok"); 642172965Snyan else 643200631Snyan DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x, 644200631Snyan dblk, p, VTOP(p), result ? "failed" : "ok"); 64543561Skato if (result) { 646339406Simp return (result); 64743561Skato } 648172965Snyan if (!write && bbuf != NULL) 649339406Simp bcopy(bbuf, p, x * BD(dev).bd_sectorsize); 650339406Simp p += (x * BD(dev).bd_sectorsize); 65143561Skato dblk += x; 65243561Skato resid -= x; 65343561Skato } 654172965Snyan 655339406Simp/* hexdump(dest, (blks * BD(dev).bd_sectorsize)); */ 65643561Skato return(0); 65743561Skato} 65843561Skato 659172965Snyanstatic int 660339406Simpbd_read(struct disk_devdesc *dev, daddr_t dblk, int blks, 661339406Simp caddr_t dest) 662172965Snyan{ 66387734Snyan 664339406Simp return (bd_io(dev, dblk, blks, dest, 0)); 665172965Snyan} 666172965Snyan 66743561Skatostatic int 668339406Simpbd_write(struct disk_devdesc *dev, daddr_t dblk, int blks, 669339406Simp caddr_t dest) 67087734Snyan{ 67187734Snyan 672339406Simp return (bd_io(dev, dblk, blks, dest, 1)); 673172965Snyan} 67487734Snyan 675339406Simp#if 0 67687734Snyanstatic int 67743561Skatobd_getgeom(struct open_disk *od) 67843561Skato{ 67943561Skato 68043561Skato if (od->od_flags & BD_FLOPPY) { 68154819Snyan od->od_cyl = 79; 68243561Skato od->od_hds = 2; 68343561Skato od->od_sec = (od->od_unit & 0xf0) == 0x30 ? 18 : 15; 684108791Snyan } else if (od->od_flags & BD_OPTICAL) { 685108791Snyan od->od_cyl = 0xFFFE; 686108791Snyan od->od_hds = 8; 687108791Snyan od->od_sec = 32; 68859777Snyan } else { 68954819Snyan v86.ctl = V86_FLAGS; 69043561Skato v86.addr = 0x1b; 69143561Skato v86.eax = 0x8400 | od->od_unit; 69243561Skato v86int(); 69343561Skato 69443561Skato od->od_cyl = v86.ecx; 69543561Skato od->od_hds = (v86.edx >> 8) & 0xff; 69643561Skato od->od_sec = v86.edx & 0xff; 697292682Sjhb if (V86_CY(v86.efl)) 69851586Skato return(1); 69943561Skato } 70043561Skato 70143561Skato DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec); 70243561Skato return(0); 70343561Skato} 704339406Simp#endif 70543561Skato 70643561Skato/* 70753207Snyan * Return the BIOS geometry of a given "fixed drive" in a format 70853207Snyan * suitable for the legacy bootinfo structure. Since the kernel is 70953207Snyan * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we 71053207Snyan * prefer to get the information directly, rather than rely on being 71153207Snyan * able to put it together from information already maintained for 71253207Snyan * different purposes and for a probably different number of drives. 71353207Snyan * 71453207Snyan * For valid drives, the geometry is expected in the format (31..0) 71553207Snyan * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are 71653207Snyan * indicated by returning the geometry of a "1.2M" PC-format floppy 71753207Snyan * disk. And, incidentally, what is returned is not the geometry as 71853207Snyan * such but the highest valid cylinder, head, and sector numbers. 71953207Snyan */ 72053207Snyanu_int32_t 72153207Snyanbd_getbigeom(int bunit) 72253207Snyan{ 72354819Snyan int hds = 0; 72455339Snyan int unit = 0x80; /* IDE HDD */ 72554819Snyan u_int addr = 0xA155d; 72655339Snyan 72754819Snyan while (unit < 0xa7) { 72855339Snyan if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f))) 72955339Snyan if (hds++ == bunit) 73054819Snyan break; 731108791Snyan 732108791Snyan if (unit >= 0xA0) { 733108791Snyan int media = ((unsigned *)PTOV(0xA1460))[unit & 0x0F] & 0x1F; 734108791Snyan 735108791Snyan if (media == 7 && hds++ == bunit) /* SCSI MO */ 736108791Snyan return(0xFFFE0820); /* C:65535 H:8 S:32 */ 737108791Snyan } 73854819Snyan if (++unit == 0x84) { 739108791Snyan unit = 0xA0; /* SCSI HDD */ 74054819Snyan addr = 0xA1482; 74154819Snyan } 74254819Snyan } 74354819Snyan if (unit == 0xa7) 744108791Snyan return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */ 74553207Snyan v86.ctl = V86_FLAGS; 74654819Snyan v86.addr = 0x1b; 74754819Snyan v86.eax = 0x8400 | unit; 74854819Snyan v86int(); 749292682Sjhb if (V86_CY(v86.efl)) 750108791Snyan return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */ 75154819Snyan return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff); 75253207Snyan} 75353207Snyan 75453207Snyan/* 755130603Sphk * Return a suitable dev_t value for (dev). 75643561Skato * 75743561Skato * In the case where it looks like (dev) is a SCSI disk, we allow the number of 75843561Skato * IDE disks to be specified in $num_ide_disks. There should be a Better Way. 75943561Skato */ 76043561Skatoint 761339406Simpbd_getdev(struct i386_devdesc *d) 76243561Skato{ 763339406Simp struct disk_devdesc *dev; 76443561Skato int biosdev; 76543561Skato int major; 76643561Skato int rootdev; 76743561Skato char *nip, *cp; 76843561Skato int unitofs = 0, i, unit; 76943561Skato 770339406Simp dev = (struct disk_devdesc *)d; 771332154Skevans biosdev = bd_unit2bios(dev->dd.d_unit); 772332154Skevans DEBUG("unit %d BIOS device %d", dev->dd.d_unit, biosdev); 77343561Skato if (biosdev == -1) /* not a BIOS device */ 77443561Skato return(-1); 775339406Simp if (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize, 776339406Simp BD(dev).bd_sectorsize) != 0) /* oops, not a viable device */ 777339406Simp return (-1); 778339406Simp else 779339406Simp disk_close(dev); 78043561Skato 78143561Skato if ((biosdev & 0xf0) == 0x90 || (biosdev & 0xf0) == 0x30) { 78243561Skato /* floppy (or emulated floppy) or ATAPI device */ 783339406Simp if (BD(dev).bd_type == DT_ATAPI) { 78443561Skato /* is an ATAPI disk */ 78543561Skato major = WFDMAJOR; 78643561Skato } else { 78743561Skato /* is a floppy disk */ 78843561Skato major = FDMAJOR; 78943561Skato } 79043561Skato } else { 79143561Skato /* harddisk */ 792339406Simp if ((BD(dev).bd_flags & BD_LABELOK) && 0) { 793339406Simp// (BD(dev).bd_disklabel.d_type == DTYPE_SCSI)) { 79443561Skato /* label OK, disk labelled as SCSI */ 79543561Skato major = DAMAJOR; 79643561Skato /* check for unit number correction hint, now deprecated */ 79743561Skato if ((nip = getenv("num_ide_disks")) != NULL) { 79843561Skato i = strtol(nip, &cp, 0); 79943561Skato /* check for parse error */ 80043561Skato if ((cp != nip) && (*cp == 0)) 80143561Skato unitofs = i; 80243561Skato } 80343561Skato } else { 80443561Skato /* assume an IDE disk */ 80543561Skato major = WDMAJOR; 80643561Skato } 80743561Skato } 80868358Snyan /* default root disk unit number */ 80968358Snyan if ((biosdev & 0xf0) == 0xa0) 810339406Simp unit = BD(dev).bd_da_unit; 81168358Snyan else 81268358Snyan unit = biosdev & 0xf; 81368358Snyan 81443561Skato /* XXX a better kludge to set the root disk unit number */ 81543561Skato if ((nip = getenv("root_disk_unit")) != NULL) { 81643561Skato i = strtol(nip, &cp, 0); 81743561Skato /* check for parse error */ 81843561Skato if ((cp != nip) && (*cp == 0)) 81943561Skato unit = i; 82043561Skato } 82143561Skato 822339406Simp rootdev = MAKEBOOTDEV(major, dev->d_slice + 1, unit, dev->d_partition); 82343561Skato DEBUG("dev is 0x%x\n", rootdev); 82443561Skato return(rootdev); 82543561Skato} 826