biosdisk.c revision 344421
1/*- 2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: stable/11/stand/pc98/libpc98/biosdisk.c 344421 2019-02-21 06:02:51Z kevans $"); 29 30/* 31 * BIOS disk device handling. 32 * 33 * Ideas and algorithms from: 34 * 35 * - NetBSD libi386/biosdisk.c 36 * - FreeBSD biosboot/disk.c 37 * 38 */ 39 40#include <sys/disk.h> 41#include <sys/limits.h> 42#include <stand.h> 43#include <machine/bootinfo.h> 44#include <stdarg.h> 45 46#include <sys/disklabel.h> 47#include <sys/diskpc98.h> 48 49#include <bootstrap.h> 50#include <btxv86.h> 51#include "disk.h" 52#include "libi386.h" 53 54#define BIOS_NUMDRIVES 0x475 55#define BIOSDISK_SECSIZE 512 56#define BUFSIZE (1 * BIOSDISK_SECSIZE) 57 58#define DT_ATAPI 0x10 /* disk type for ATAPI floppies */ 59#define WDMAJOR 0 /* major numbers for devices we frontend for */ 60#define WFDMAJOR 1 61#define FDMAJOR 2 62#define DAMAJOR 4 63 64#ifdef DISK_DEBUG 65# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 66#else 67# define DEBUG(fmt, args...) 68#endif 69 70/* 71 * List of BIOS devices, translation from disk unit number to 72 * BIOS unit number. 73 */ 74static struct bdinfo 75{ 76 int bd_unit; /* BIOS unit number */ 77 int bd_cyl; /* BIOS geometry */ 78 int bd_hds; 79 int bd_sec; 80 int bd_flags; 81#define BD_MODEINT13 0x0000 82#define BD_MODEEDD1 0x0001 83#define BD_MODEEDD3 0x0002 84#define BD_MODEMASK 0x0003 85#define BD_FLOPPY 0x0004 86#define BD_LABELOK 0x0008 87#define BD_PARTTABOK 0x0010 88#define BD_OPTICAL 0x0020 89 int bd_type; /* BIOS 'drive type' (floppy only) */ 90 uint16_t bd_sectorsize; /* Sector size */ 91 uint64_t bd_sectors; /* Disk size */ 92 int bd_da_unit; /* kernel unit number for da */ 93 int bd_open; /* reference counter */ 94 void *bd_bcache; /* buffer cache data */ 95} bdinfo [MAXBDDEV]; 96static int nbdinfo = 0; 97 98#define BD(dev) (bdinfo[(dev)->dd.d_unit]) 99 100static int bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks, 101 caddr_t dest); 102static int bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks, 103 caddr_t dest); 104static int bd_int13probe(struct bdinfo *bd); 105 106static int bd_init(void); 107static int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size, 108 char *buf, size_t *rsize); 109static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size, 110 char *buf, size_t *rsize); 111static int bd_open(struct open_file *f, ...); 112static int bd_close(struct open_file *f); 113static int bd_ioctl(struct open_file *f, u_long cmd, void *data); 114static int bd_print(int verbose); 115 116struct devsw biosdisk = { 117 "disk", 118 DEVT_DISK, 119 bd_init, 120 bd_strategy, 121 bd_open, 122 bd_close, 123 bd_ioctl, 124 bd_print, 125 NULL 126}; 127 128/* 129 * Translate between BIOS device numbers and our private unit numbers. 130 */ 131int 132bd_bios2unit(int biosdev) 133{ 134 int i; 135 136 DEBUG("looking for bios device 0x%x", biosdev); 137 for (i = 0; i < nbdinfo; i++) { 138 DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit); 139 if (bdinfo[i].bd_unit == biosdev) 140 return (i); 141 } 142 return (-1); 143} 144 145int 146bd_unit2bios(int unit) 147{ 148 149 if ((unit >= 0) && (unit < nbdinfo)) 150 return (bdinfo[unit].bd_unit); 151 return (-1); 152} 153 154/* 155 * Quiz the BIOS for disk devices, save a little info about them. 156 */ 157static int 158bd_init(void) 159{ 160 int base, unit; 161 int da_drive=0, n=-0x10; 162 163 /* sequence 0x90, 0x80, 0xa0 */ 164 for (base = 0x90; base <= 0xa0; base += n, n += 0x30) { 165 for (unit = base; (nbdinfo < MAXBDDEV) || ((unit & 0x0f) < 4); 166 unit++) { 167 bdinfo[nbdinfo].bd_open = 0; 168 bdinfo[nbdinfo].bd_bcache = NULL; 169 bdinfo[nbdinfo].bd_unit = unit; 170 bdinfo[nbdinfo].bd_flags = 171 (unit & 0xf0) == 0x90 ? BD_FLOPPY : 0; 172 if (!bd_int13probe(&bdinfo[nbdinfo])) { 173 if (((unit & 0xf0) == 0x90 && 174 (unit & 0x0f) < 4) || 175 ((unit & 0xf0) == 0xa0 && 176 (unit & 0x0f) < 6)) 177 /* Target IDs are not contiguous. */ 178 continue; 179 else 180 break; 181 } 182 183 if (bdinfo[nbdinfo].bd_flags & BD_FLOPPY) { 184 /* available 1.44MB access? */ 185 if (*(u_char *)PTOV(0xA15AE) & 186 (1<<(unit & 0xf))) { 187 /* boot media 1.2MB FD? */ 188 if ((*(u_char *)PTOV(0xA1584) & 189 0xf0) != 0x90) 190 bdinfo[nbdinfo].bd_unit = 191 0x30 + (unit & 0xf); 192 } 193 } else { 194 if ((unit & 0xF0) == 0xA0) /* SCSI HD or MO */ 195 bdinfo[nbdinfo].bd_da_unit = 196 da_drive++; 197 } 198 /* XXX we need "disk aliases" to make this simpler */ 199 printf("BIOS drive %c: is disk%d\n", 200 'A' + nbdinfo, nbdinfo); 201 nbdinfo++; 202 } 203 } 204 bcache_add_dev(nbdinfo); 205 return(0); 206} 207 208/* 209 * Try to detect a device supported by the legacy int13 BIOS 210 */ 211static int 212bd_int13probe(struct bdinfo *bd) 213{ 214 int addr; 215 216 if (bd->bd_flags & BD_FLOPPY) { 217 addr = 0xa155c; 218 } else { 219 if ((bd->bd_unit & 0xf0) == 0x80) 220 addr = 0xa155d; 221 else 222 addr = 0xa1482; 223 } 224 if ( *(u_char *)PTOV(addr) & (1<<(bd->bd_unit & 0x0f))) { 225 bd->bd_flags |= BD_MODEINT13; 226 return (1); 227 } 228 if ((bd->bd_unit & 0xF0) == 0xA0) { 229 int media = 230 ((unsigned *)PTOV(0xA1460))[bd->bd_unit & 0x0F] & 0x1F; 231 232 if (media == 7) { /* MO */ 233 bd->bd_flags |= BD_MODEINT13 | BD_OPTICAL; 234 return(1); 235 } 236 } 237 return (0); 238} 239 240/* 241 * Print information about disks 242 */ 243static int 244bd_print(int verbose) 245{ 246 char line[80]; 247 struct disk_devdesc dev; 248 int i, ret = 0; 249 struct pc98_partition *dptr; 250 251 if (nbdinfo == 0) 252 return (0); 253 254 printf("%s devices:", biosdisk.dv_name); 255 if ((ret = pager_output("\n")) != 0) 256 return (ret); 257 258 for (i = 0; i < nbdinfo; i++) { 259 snprintf(line, sizeof(line), 260 " disk%d: BIOS drive %c (%ju X %u):\n", i, 261 (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit): 262 ('C' + bdinfo[i].bd_unit - 0x80), 263 (uintmax_t)bdinfo[i].bd_sectors, 264 bdinfo[i].bd_sectorsize); 265 if ((ret = pager_output(line)) != 0) 266 break; 267 268 /* try to open the whole disk */ 269 dev.dd.d_dev = &biosdisk; 270 dev.dd.d_unit = i; 271 dev.d_slice = -1; 272 dev.d_partition = -1; 273 if (disk_open(&dev, 274 bdinfo[i].bd_sectorsize * bdinfo[i].bd_sectors, 275 bdinfo[i].bd_sectorsize) == 0) { 276 snprintf(line, sizeof(line), " disk%d", i); 277 ret = disk_print(&dev, line, verbose); 278 disk_close(&dev); 279 if (ret != 0) 280 return (ret); 281 } 282 } 283 return (ret); 284} 285 286/* Given a size in 512 byte sectors, convert it to a human-readable number. */ 287static char * 288display_size(uint64_t size) 289{ 290 static char buf[80]; 291 char unit; 292 293 size /= 2; 294 unit = 'K'; 295 if (size >= 10485760000LL) { 296 size /= 1073741824; 297 unit = 'T'; 298 } else if (size >= 10240000) { 299 size /= 1048576; 300 unit = 'G'; 301 } else if (size >= 10000) { 302 size /= 1024; 303 unit = 'M'; 304 } 305 sprintf(buf, "%6ld%cB", (long)size, unit); 306 return (buf); 307} 308 309/* 310 * Attempt to open the disk described by (dev) for use by (f). 311 * 312 * Note that the philosophy here is "give them exactly what 313 * they ask for". This is necessary because being too "smart" 314 * about what the user might want leads to complications. 315 * (eg. given no slice or partition value, with a disk that is 316 * sliced - are they after the first BSD slice, or the DOS 317 * slice before it?) 318 */ 319static int 320bd_open(struct open_file *f, ...) 321{ 322 va_list ap; 323 struct disk_devdesc *dev; 324 struct disk_devdesc disk; 325 int err; 326 uint64_t size; 327 328 va_start(ap, f); 329 dev = va_arg(ap, struct disk_devdesc *); 330 va_end(ap); 331 332 if (dev->dd.d_unit < 0 || dev->dd.d_unit >= nbdinfo) 333 return (EIO); 334 BD(dev).bd_open++; 335 if (BD(dev).bd_bcache == NULL) 336 BD(dev).bd_bcache = bcache_allocate(); 337 338 /* 339 * Read disk size from partition. 340 * This is needed to work around buggy BIOS systems returning 341 * wrong (truncated) disk media size. 342 * During bd_probe() we tested if the mulitplication of bd_sectors 343 * would overflow so it should be safe to perform here. 344 */ 345 disk.dd.d_dev = dev->dd.d_dev; 346 disk.dd.d_unit = dev->dd.d_unit; 347 disk.d_slice = -1; 348 disk.d_partition = -1; 349 disk.d_offset = 0; 350 if (disk_open(&disk, BD(dev).bd_sectors * BD(dev).bd_sectorsize, 351 BD(dev).bd_sectorsize) == 0) { 352 353 if (disk_ioctl(&disk, DIOCGMEDIASIZE, &size) == 0) { 354 size /= BD(dev).bd_sectorsize; 355 if (size > BD(dev).bd_sectors) 356 BD(dev).bd_sectors = size; 357 } 358 disk_close(&disk); 359 } 360 361 err = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize, 362 BD(dev).bd_sectorsize); 363 return(err); 364} 365 366static int 367bd_close(struct open_file *f) 368{ 369 struct disk_devdesc *dev; 370 371 dev = (struct disk_devdesc *)f->f_devdata; 372 BD(dev).bd_open--; 373 if (BD(dev).bd_open == 0) { 374 bcache_free(BD(dev).bd_bcache); 375 BD(dev).bd_bcache = NULL; 376 } 377 return (disk_close(dev)); 378} 379 380static int 381bd_ioctl(struct open_file *f, u_long cmd, void *data) 382{ 383 struct disk_devdesc *dev; 384 int rc; 385 386 dev = (struct disk_devdesc *)f->f_devdata; 387 388 rc = disk_ioctl(dev, cmd, data); 389 if (rc != ENOTTY) 390 return (rc); 391 392 switch (cmd) { 393 case DIOCGSECTORSIZE: 394 *(u_int *)data = BD(dev).bd_sectorsize; 395 break; 396 case DIOCGMEDIASIZE: 397 *(uint64_t *)data = BD(dev).bd_sectors * BD(dev).bd_sectorsize; 398 break; 399 default: 400 return (ENOTTY); 401 } 402 return (0); 403} 404 405static int 406bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, 407 char *buf, size_t *rsize) 408{ 409 struct bcache_devdata bcd; 410 struct disk_devdesc *dev; 411 412 dev = (struct disk_devdesc *)devdata; 413 bcd.dv_strategy = bd_realstrategy; 414 bcd.dv_devdata = devdata; 415 bcd.dv_cache = BD(dev).bd_bcache; 416 return (bcache_strategy(&bcd, rw, dblk + dev->d_offset, 417 size, buf, rsize)); 418} 419 420static int 421bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, 422 char *buf, size_t *rsize) 423{ 424 struct disk_devdesc *dev = (struct disk_devdesc *)devdata; 425 uint64_t disk_blocks; 426 int blks, rc; 427#ifdef BD_SUPPORT_FRAGS /* XXX: sector size */ 428 char fragbuf[BIOSDISK_SECSIZE]; 429 size_t fragsize; 430 431 fragsize = size % BIOSDISK_SECSIZE; 432#else 433 if (size % BD(dev).bd_sectorsize) 434 panic("bd_strategy: %d bytes I/O not multiple of block size", size); 435#endif 436 437 DEBUG("open_disk %p", dev); 438 439 /* 440 * Check the value of the size argument. We do have quite small 441 * heap (64MB), but we do not know good upper limit, so we check against 442 * INT_MAX here. This will also protect us against possible overflows 443 * while translating block count to bytes. 444 */ 445 if (size > INT_MAX) { 446 DEBUG("too large read: %zu bytes", size); 447 return (EIO); 448 } 449 450 blks = size / BD(dev).bd_sectorsize; 451 if (dblk > dblk + blks) 452 return (EIO); 453 454 if (rsize) 455 *rsize = 0; 456 457 /* Get disk blocks, this value is either for whole disk or for partition */ 458 if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks)) { 459 /* DIOCGMEDIASIZE does return bytes. */ 460 disk_blocks /= BD(dev).bd_sectorsize; 461 } else { 462 /* We should not get here. Just try to survive. */ 463 disk_blocks = BD(dev).bd_sectors - dev->d_offset; 464 } 465 466 /* Validate source block address. */ 467 if (dblk < dev->d_offset || dblk >= dev->d_offset + disk_blocks) 468 return (EIO); 469 470 /* 471 * Truncate if we are crossing disk or partition end. 472 */ 473 if (dblk + blks >= dev->d_offset + disk_blocks) { 474 blks = dev->d_offset + disk_blocks - dblk; 475 size = blks * BD(dev).bd_sectorsize; 476 DEBUG("short read %d", blks); 477 } 478 479 switch (rw & F_MASK) { 480 case F_READ: 481 DEBUG("read %d from %lld to %p", blks, dblk, buf); 482 483 if (blks && (rc = bd_read(dev, dblk, blks, buf))) { 484 /* Filter out floppy controller errors */ 485 if (BD(dev).bd_flags != BD_FLOPPY || rc != 0x20) { 486 printf("read %d from %lld to %p, error: 0x%x", blks, dblk, 487 buf, rc); 488 } 489 return (EIO); 490 } 491#ifdef BD_SUPPORT_FRAGS /* XXX: sector size */ 492 DEBUG("bd_strategy: frag read %d from %d+%d to %p", 493 fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE)); 494 if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) { 495 DEBUG("frag read error"); 496 return(EIO); 497 } 498 bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize); 499#endif 500 break; 501 case F_WRITE : 502 DEBUG("write %d from %d to %p", blks, dblk, buf); 503 504 if (blks && bd_write(dev, dblk, blks, buf)) { 505 DEBUG("write error"); 506 return (EIO); 507 } 508#ifdef BD_SUPPORT_FRAGS 509 if(fragsize) { 510 DEBUG("Attempted to write a frag"); 511 return (EIO); 512 } 513#endif 514 break; 515 default: 516 /* DO NOTHING */ 517 return (EROFS); 518 } 519 520 if (rsize) 521 *rsize = size; 522 return (0); 523} 524 525/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */ 526#define FLOPPY_BOUNCEBUF 18 527 528static int 529bd_chs_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, 530 int write) 531{ 532 u_int x, bpc, cyl, hd, sec; 533 534 bpc = BD(dev).bd_sec * BD(dev).bd_hds; /* blocks per cylinder */ 535 x = dblk; 536 cyl = x / bpc; /* block # / blocks per cylinder */ 537 x %= bpc; /* block offset into cylinder */ 538 hd = x / BD(dev).bd_sec; /* offset / blocks per track */ 539 sec = x % BD(dev).bd_sec; /* offset into track */ 540 541 v86.ctl = V86_FLAGS; 542 v86.addr = 0x1b; 543 if (write) 544 v86.eax = 0x0500 | BD(dev).bd_unit; 545 else 546 v86.eax = 0x0600 | BD(dev).bd_unit; 547 if (BD(dev).bd_flags & BD_FLOPPY) { 548 v86.eax |= 0xd000; 549 v86.ecx = 0x0200 | (cyl & 0xff); 550 v86.edx = (hd << 8) | (sec + 1); 551 } else if (BD(dev).bd_flags & BD_OPTICAL) { 552 v86.eax &= 0xFF7F; 553 v86.ecx = dblk & 0xFFFF; 554 v86.edx = dblk >> 16; 555 } else { 556 v86.ecx = cyl; 557 v86.edx = (hd << 8) | sec; 558 } 559 v86.ebx = blks * BIOSDISK_SECSIZE; 560 v86.es = VTOPSEG(dest); 561 v86.ebp = VTOPOFF(dest); 562 v86int(); 563 return (V86_CY(v86.efl)); 564} 565 566static int 567bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, int write) 568{ 569 u_int x, sec, result, resid, retry, maxfer; 570 caddr_t p, xp, bbuf; 571 572 /* Just in case some idiot actually tries to read/write -1 blocks... */ 573 if (blks < 0) 574 return (-1); 575 576 resid = blks; 577 p = dest; 578 579 /* Decide whether we have to bounce */ 580 if (VTOP(dest) >> 20 != 0 || (BD(dev).bd_unit < 0x80 && 581 (VTOP(dest) >> 16) != (VTOP(dest + 582 blks * BD(dev).bd_sectorsize) >> 16))) { 583 584 /* 585 * There is a 64k physical boundary somewhere in the 586 * destination buffer, or the destination buffer is above 587 * first 1MB of physical memory so we have to arrange a 588 * suitable bounce buffer. Allocate a buffer twice as large 589 * as we need to. Use the bottom half unless there is a break 590 * there, in which case we use the top half. 591 */ 592 x = V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize; 593 x = min(x, (unsigned)blks); 594 bbuf = PTOV(V86_IO_BUFFER); 595 maxfer = x; /* limit transfers to bounce region size */ 596 } else { 597 bbuf = NULL; 598 maxfer = 0; 599 } 600 601 while (resid > 0) { 602 /* 603 * Play it safe and don't cross track boundaries. 604 * (XXX this is probably unnecessary) 605 */ 606 sec = dblk % BD(dev).bd_sec; /* offset into track */ 607 x = min(BD(dev).bd_sec - sec, resid); 608 if (maxfer > 0) 609 x = min(x, maxfer); /* fit bounce buffer */ 610 611 /* where do we transfer to? */ 612 xp = bbuf == NULL ? p : bbuf; 613 614 /* 615 * Put your Data In, Put your Data out, 616 * Put your Data In, and shake it all about 617 */ 618 if (write && bbuf != NULL) 619 bcopy(p, bbuf, x * BD(dev).bd_sectorsize); 620 621 /* 622 * Loop retrying the operation a couple of times. The BIOS 623 * may also retry. 624 */ 625 for (retry = 0; retry < 3; retry++) { 626 /* if retrying, reset the drive */ 627 if (retry > 0) { 628 v86.ctl = V86_FLAGS; 629 v86.addr = 0x1b; 630 v86.eax = 0x0300 | BD(dev).bd_unit; 631 v86int(); 632 } 633 634 result = bd_chs_io(dev, dblk, x, xp, write); 635 if (result == 0) 636 break; 637 } 638 639 if (write) 640 DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x, 641 p, VTOP(p), dblk, result ? "failed" : "ok"); 642 else 643 DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x, 644 dblk, p, VTOP(p), result ? "failed" : "ok"); 645 if (result) { 646 return (result); 647 } 648 if (!write && bbuf != NULL) 649 bcopy(bbuf, p, x * BD(dev).bd_sectorsize); 650 p += (x * BD(dev).bd_sectorsize); 651 dblk += x; 652 resid -= x; 653 } 654 655/* hexdump(dest, (blks * BD(dev).bd_sectorsize)); */ 656 return(0); 657} 658 659static int 660bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks, 661 caddr_t dest) 662{ 663 664 return (bd_io(dev, dblk, blks, dest, 0)); 665} 666 667static int 668bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks, 669 caddr_t dest) 670{ 671 672 return (bd_io(dev, dblk, blks, dest, 1)); 673} 674 675#if 0 676static int 677bd_getgeom(struct open_disk *od) 678{ 679 680 if (od->od_flags & BD_FLOPPY) { 681 od->od_cyl = 79; 682 od->od_hds = 2; 683 od->od_sec = (od->od_unit & 0xf0) == 0x30 ? 18 : 15; 684 } else if (od->od_flags & BD_OPTICAL) { 685 od->od_cyl = 0xFFFE; 686 od->od_hds = 8; 687 od->od_sec = 32; 688 } else { 689 v86.ctl = V86_FLAGS; 690 v86.addr = 0x1b; 691 v86.eax = 0x8400 | od->od_unit; 692 v86int(); 693 694 od->od_cyl = v86.ecx; 695 od->od_hds = (v86.edx >> 8) & 0xff; 696 od->od_sec = v86.edx & 0xff; 697 if (V86_CY(v86.efl)) 698 return(1); 699 } 700 701 DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec); 702 return(0); 703} 704#endif 705 706/* 707 * Return the BIOS geometry of a given "fixed drive" in a format 708 * suitable for the legacy bootinfo structure. Since the kernel is 709 * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we 710 * prefer to get the information directly, rather than rely on being 711 * able to put it together from information already maintained for 712 * different purposes and for a probably different number of drives. 713 * 714 * For valid drives, the geometry is expected in the format (31..0) 715 * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are 716 * indicated by returning the geometry of a "1.2M" PC-format floppy 717 * disk. And, incidentally, what is returned is not the geometry as 718 * such but the highest valid cylinder, head, and sector numbers. 719 */ 720u_int32_t 721bd_getbigeom(int bunit) 722{ 723 int hds = 0; 724 int unit = 0x80; /* IDE HDD */ 725 u_int addr = 0xA155d; 726 727 while (unit < 0xa7) { 728 if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f))) 729 if (hds++ == bunit) 730 break; 731 732 if (unit >= 0xA0) { 733 int media = ((unsigned *)PTOV(0xA1460))[unit & 0x0F] & 0x1F; 734 735 if (media == 7 && hds++ == bunit) /* SCSI MO */ 736 return(0xFFFE0820); /* C:65535 H:8 S:32 */ 737 } 738 if (++unit == 0x84) { 739 unit = 0xA0; /* SCSI HDD */ 740 addr = 0xA1482; 741 } 742 } 743 if (unit == 0xa7) 744 return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */ 745 v86.ctl = V86_FLAGS; 746 v86.addr = 0x1b; 747 v86.eax = 0x8400 | unit; 748 v86int(); 749 if (V86_CY(v86.efl)) 750 return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */ 751 return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff); 752} 753 754/* 755 * Return a suitable dev_t value for (dev). 756 * 757 * In the case where it looks like (dev) is a SCSI disk, we allow the number of 758 * IDE disks to be specified in $num_ide_disks. There should be a Better Way. 759 */ 760int 761bd_getdev(struct i386_devdesc *d) 762{ 763 struct disk_devdesc *dev; 764 int biosdev; 765 int major; 766 int rootdev; 767 char *nip, *cp; 768 int unitofs = 0, i, unit; 769 770 dev = (struct disk_devdesc *)d; 771 biosdev = bd_unit2bios(dev->dd.d_unit); 772 DEBUG("unit %d BIOS device %d", dev->dd.d_unit, biosdev); 773 if (biosdev == -1) /* not a BIOS device */ 774 return(-1); 775 if (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize, 776 BD(dev).bd_sectorsize) != 0) /* oops, not a viable device */ 777 return (-1); 778 else 779 disk_close(dev); 780 781 if ((biosdev & 0xf0) == 0x90 || (biosdev & 0xf0) == 0x30) { 782 /* floppy (or emulated floppy) or ATAPI device */ 783 if (BD(dev).bd_type == DT_ATAPI) { 784 /* is an ATAPI disk */ 785 major = WFDMAJOR; 786 } else { 787 /* is a floppy disk */ 788 major = FDMAJOR; 789 } 790 } else { 791 /* harddisk */ 792 if ((BD(dev).bd_flags & BD_LABELOK) && 0) { 793// (BD(dev).bd_disklabel.d_type == DTYPE_SCSI)) { 794 /* label OK, disk labelled as SCSI */ 795 major = DAMAJOR; 796 /* check for unit number correction hint, now deprecated */ 797 if ((nip = getenv("num_ide_disks")) != NULL) { 798 i = strtol(nip, &cp, 0); 799 /* check for parse error */ 800 if ((cp != nip) && (*cp == 0)) 801 unitofs = i; 802 } 803 } else { 804 /* assume an IDE disk */ 805 major = WDMAJOR; 806 } 807 } 808 /* default root disk unit number */ 809 if ((biosdev & 0xf0) == 0xa0) 810 unit = BD(dev).bd_da_unit; 811 else 812 unit = biosdev & 0xf; 813 814 /* XXX a better kludge to set the root disk unit number */ 815 if ((nip = getenv("root_disk_unit")) != NULL) { 816 i = strtol(nip, &cp, 0); 817 /* check for parse error */ 818 if ((cp != nip) && (*cp == 0)) 819 unit = i; 820 } 821 822 rootdev = MAKEBOOTDEV(major, dev->d_slice + 1, unit, dev->d_partition); 823 DEBUG("dev is 0x%x\n", rootdev); 824 return(rootdev); 825} 826