1 /* $OpenBSD: biosdev.c,v 1.15 2011/03/17 12:53:44 krw Exp $ */ 2 3 /* 4 * Copyright (c) 1996 Michael Shalayeff 5 * Copyright (c) 2003 Tobias Weingartner 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30 31 #include <sys/param.h> 32 #include <sys/reboot.h> 33 #include <sys/disklabel.h> 34 #include <machine/tss.h> 35 #include <machine/biosvar.h> 36 #include <lib/libsa/saerrno.h> 37 #include <isofs/cd9660/iso.h> 38 #include "disk.h" 39 #include "libsa.h" 40 #include "biosdev.h" 41 42 static const char *biosdisk_err(u_int); 43 static int biosdisk_errno(u_int); 44 45 int CHS_rw (int, int, int, int, int, int, void *); 46 static int EDD_rw (int, int, u_int32_t, u_int32_t, void *); 47 48 static u_int findopenbsd(bios_diskinfo_t *, const char **); 49 50 extern int debug; 51 int bios_bootdev; 52 int bios_cddev = -1; /* Set by srt0 if coming from CD */ 53 54 #if 0 55 struct biosdisk { 56 bios_diskinfo_t *bios_info; 57 dev_t bsddev; 58 struct disklabel disklabel; 59 }; 60 #endif 61 62 struct EDD_CB { 63 u_int8_t edd_len; /* size of packet */ 64 u_int8_t edd_res1; /* reserved */ 65 u_int8_t edd_nblk; /* # of blocks to transfer */ 66 u_int8_t edd_res2; /* reserved */ 67 u_int16_t edd_off; /* address of buffer (offset) */ 68 u_int16_t edd_seg; /* address of buffer (segment) */ 69 u_int64_t edd_daddr; /* starting block */ 70 }; 71 72 /* 73 * reset disk system 74 */ 75 static int 76 biosdreset(int dev) 77 { 78 int rv; 79 80 __asm __volatile (DOINT(0x13) "; setc %b0" : "=a" (rv) 81 : "0" (0), "d" (dev) : "%ecx", "cc"); 82 83 return ((rv & 0xff)? rv >> 8 : 0); 84 } 85 86 /* 87 * Fill out a bios_diskinfo_t for this device. 88 * Return 0 if all ok. 89 * Return 1 if not ok. 90 */ 91 int 92 bios_getdiskinfo(int dev, bios_diskinfo_t *pdi) 93 { 94 u_int rv; 95 96 /* Just reset, don't check return code */ 97 rv = biosdreset(dev); 98 99 #ifdef BIOS_DEBUG 100 if (debug) 101 printf("getinfo: try #8, 0x%x, %p\n", dev, pdi); 102 #endif 103 __asm __volatile (DOINT(0x13) "\n\t" 104 "setc %b0; movzbl %h1, %1\n\t" 105 "movzbl %%cl, %3; andb $0x3f, %b3\n\t" 106 "xchgb %%cl, %%ch; rolb $2, %%ch" 107 : "=a" (rv), "=d" (pdi->bios_heads), 108 "=c" (pdi->bios_cylinders), 109 "=b" (pdi->bios_sectors) 110 : "0" (0x0800), "1" (dev) : "cc"); 111 112 #ifdef BIOS_DEBUG 113 if (debug) { 114 printf("getinfo: got #8\n"); 115 printf("disk 0x%x: %d,%d,%d\n", dev, pdi->bios_cylinders, 116 pdi->bios_heads, pdi->bios_sectors); 117 } 118 #endif 119 if (rv & 0xff) 120 return 1; 121 122 /* Fix up info */ 123 pdi->bios_number = dev; 124 pdi->bios_heads++; 125 pdi->bios_cylinders &= 0x3ff; 126 pdi->bios_cylinders++; 127 128 /* NOTE: 129 * This currently hangs/reboots some machines 130 * The IBM ThinkPad 750ED for one. 131 * 132 * Funny that an IBM/MS extension would not be 133 * implemented by an IBM system... 134 * 135 * Future hangs (when reported) can be "fixed" 136 * with getSYSCONFaddr() and an exceptions list. 137 */ 138 if (dev & 0x80 && (dev == 0x80 || dev == 0x81 || dev == bios_bootdev)) { 139 int bm; 140 141 #ifdef BIOS_DEBUG 142 if (debug) 143 printf("getinfo: try #41, 0x%x\n", dev); 144 #endif 145 /* EDD support check */ 146 __asm __volatile(DOINT(0x13) "; setc %b0" 147 : "=a" (rv), "=c" (bm) 148 : "0" (0x4100), "b" (0x55aa), "d" (dev) : "cc"); 149 if (!(rv & 0xff) && (BIOS_regs.biosr_bx & 0xffff) == 0xaa55) 150 pdi->bios_edd = (bm & 0xffff) | ((rv & 0xff) << 16); 151 else 152 pdi->bios_edd = -1; 153 154 #ifdef BIOS_DEBUG 155 if (debug) { 156 printf("getinfo: got #41\n"); 157 printf("disk 0x%x: 0x%x\n", dev, bm); 158 } 159 #endif 160 /* 161 * If extended disk access functions are not supported 162 * there is not much point on doing EDD. 163 */ 164 if (!(pdi->bios_edd & EXT_BM_EDA)) 165 pdi->bios_edd = -1; 166 } else 167 pdi->bios_edd = -1; 168 169 /* Skip sanity check for CHS options in EDD mode. */ 170 if (pdi->bios_edd != -1) 171 return 0; 172 173 /* Sanity check */ 174 if (!pdi->bios_cylinders || !pdi->bios_heads || !pdi->bios_sectors) 175 return 1; 176 177 /* CD-ROMs sometimes return heads == 1 */ 178 if (pdi->bios_heads < 2) 179 return 1; 180 181 return 0; 182 } 183 184 /* 185 * Read/Write a block from given place using the BIOS. 186 */ 187 int 188 CHS_rw(int rw, int dev, int cyl, int head, int sect, int nsect, void *buf) 189 { 190 int rv; 191 192 rw = rw == F_READ ? 2 : 3; 193 BIOS_regs.biosr_es = (u_int32_t)buf >> 4; 194 __asm __volatile ("movb %b7, %h1\n\t" 195 "movb %b6, %%dh\n\t" 196 "andl $0xf, %4\n\t" 197 /* cylinder; the highest 2 bits of cyl is in %cl */ 198 "xchgb %%ch, %%cl\n\t" 199 "rorb $2, %%cl\n\t" 200 "orb %b5, %%cl\n\t" 201 "inc %%cx\n\t" 202 DOINT(0x13) "\n\t" 203 "setc %b0" 204 : "=a" (rv) 205 : "0" (nsect), "d" (dev), "c" (cyl), 206 "b" (buf), "m" (sect), "m" (head), 207 "m" (rw) 208 : "cc", "memory"); 209 210 return ((rv & 0xff)? rv >> 8 : 0); 211 } 212 213 static __inline int 214 EDD_rw(int rw, int dev, u_int32_t daddr, u_int32_t nblk, void *buf) 215 { 216 int rv; 217 volatile static struct EDD_CB cb; 218 219 /* Some (most?) BIOSen get confused by i/o above 2 ^ 28 - 1 sector. */ 220 if ((daddr + nblk) > BOOTBIOS_MAXSEC) 221 return (1); /* Invalid function/parameter. */ 222 223 /* Zero out reserved stuff */ 224 cb.edd_res1 = 0; 225 cb.edd_res2 = 0; 226 227 /* Fill in parameters */ 228 cb.edd_len = sizeof(cb); 229 cb.edd_nblk = nblk; 230 cb.edd_seg = ((u_int32_t)buf >> 4) & 0xffff; 231 cb.edd_off = (u_int32_t)buf & 0xf; 232 cb.edd_daddr = daddr; 233 234 /* if offset/segment are zero, punt */ 235 if (!cb.edd_seg && !cb.edd_off) 236 return 1; 237 238 /* Call extended read/write (with disk packet) */ 239 BIOS_regs.biosr_ds = (u_int32_t)&cb >> 4; 240 __asm __volatile (DOINT(0x13) "; setc %b0" : "=a" (rv) 241 : "0" ((rw == F_READ)? 0x4200: 0x4300), 242 "d" (dev), "S" ((int) (&cb) & 0xf) : "%ecx", "cc"); 243 return ((rv & 0xff)? rv >> 8 : 0); 244 } 245 246 /* 247 * Read given sector, handling retry/errors/etc. 248 */ 249 int 250 biosd_io(int rw, bios_diskinfo_t *bd, u_int off, int nsect, void *buf) 251 { 252 int dev = bd->bios_number; 253 int j, error; 254 void *bb; 255 int bbsize = nsect * DEV_BSIZE; 256 257 if (bd->flags & BDI_EL_TORITO) { /* It's a CD device */ 258 dev &= 0xff; /* Mask out this flag bit */ 259 260 /* 261 * sys/lib/libsa/cd9600.c converts 2,048-byte CD sectors 262 * to DEV_BSIZE blocks before calling the device strategy 263 * routine. However, the El Torito spec says that the 264 * BIOS will work in 2,048-byte sectors. So shift back. 265 */ 266 off >>= (ISO_DEFAULT_BLOCK_SHIFT - DEV_BSHIFT); 267 nsect >>= (ISO_DEFAULT_BLOCK_SHIFT - DEV_BSHIFT); 268 } 269 270 /* 271 * Use a bounce buffer to not cross 64k DMA boundary, and to 272 * not access 1 MB or above. 273 */ 274 if (((((u_int32_t)buf) & ~0xffff) != 275 (((u_int32_t)buf + bbsize) & ~0xffff)) || 276 (((u_int32_t)buf) >= 0x100000)) { 277 /* 278 * XXX we believe that all the io is buffered 279 * by fs routines, so no big reads anyway 280 */ 281 bb = alloca(bbsize); 282 if (rw != F_READ) 283 bcopy(buf, bb, bbsize); 284 } else 285 bb = buf; 286 287 /* Try to do operation up to 5 times */ 288 for (error = 1, j = 5; j-- && error; ) { 289 /* CHS or LBA access? */ 290 if (bd->bios_edd != -1) { 291 error = EDD_rw(rw, dev, off, nsect, bb); 292 } else { 293 int cyl, head, sect; 294 size_t i, n; 295 char *p = bb; 296 297 /* Handle track boundaries */ 298 for (error = i = 0; error == 0 && i < nsect; 299 i += n, off += n, p += n * DEV_BSIZE) { 300 301 btochs(off, cyl, head, sect, bd->bios_heads, 302 bd->bios_sectors); 303 304 if ((sect + (nsect - i)) >= bd->bios_sectors) 305 n = bd->bios_sectors - sect; 306 else 307 n = nsect - i; 308 309 error = CHS_rw(rw, dev, cyl, head, sect, n, p); 310 311 /* ECC corrected */ 312 if (error == 0x11) 313 error = 0; 314 } 315 } 316 switch (error) { 317 case 0x00: /* No errors */ 318 case 0x11: /* ECC corrected */ 319 error = 0; 320 break; 321 322 default: /* All other errors */ 323 #ifdef BIOS_DEBUG 324 if (debug) 325 printf("\nBIOS error 0x%x (%s)\n", 326 error, biosdisk_err(error)); 327 #endif 328 biosdreset(dev); 329 break; 330 } 331 } 332 333 if (bb != buf && rw == F_READ) 334 bcopy(bb, buf, bbsize); 335 336 #ifdef BIOS_DEBUG 337 if (debug) { 338 if (error != 0) 339 printf("=0x%x(%s)", error, biosdisk_err(error)); 340 putchar('\n'); 341 } 342 #endif 343 344 return error; 345 } 346 347 /* 348 * Try to read the bsd label on the given BIOS device 349 */ 350 static u_int 351 findopenbsd(bios_diskinfo_t *bd, const char **err) 352 { 353 struct dos_mbr mbr; 354 struct dos_partition *dp; 355 u_int mbroff = DOSBBSECTOR; 356 u_int mbr_eoff = DOSBBSECTOR; /* Offset of MBR extended partition. */ 357 int error, i, maxebr = DOS_MAXEBR, nextebr; 358 359 again: 360 if (!maxebr--) { 361 *err = "too many extended partitions"; 362 return (-1); 363 } 364 365 /* Read MBR */ 366 bzero(&mbr, sizeof(mbr)); 367 error = biosd_io(F_READ, bd, mbroff, 1, &mbr); 368 if (error) { 369 *err = biosdisk_err(error); 370 return (-1); 371 } 372 373 /* check mbr signature */ 374 if (mbr.dmbr_sign != DOSMBR_SIGNATURE) { 375 *err = "bad MBR signature\n"; 376 return (-1); 377 } 378 379 /* Search for OpenBSD partition */ 380 nextebr = 0; 381 for (i = 0; i < NDOSPART; i++) { 382 dp = &mbr.dmbr_parts[i]; 383 if (!dp->dp_size) 384 continue; 385 #ifdef BIOS_DEBUG 386 if (debug) 387 printf("found partition %u: " 388 "type %u (0x%x) offset %u (0x%x)\n", 389 (int)(dp - mbr.dmbr_parts), 390 dp->dp_typ, dp->dp_typ, 391 dp->dp_start, dp->dp_start); 392 #endif 393 if (dp->dp_typ == DOSPTYP_OPENBSD) { 394 if (dp->dp_start > (dp->dp_start + mbroff)) 395 continue; 396 return (dp->dp_start + mbroff); 397 } 398 399 /* 400 * Record location of next ebr if and only if this is the first 401 * extended partition in this boot record! 402 */ 403 if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND || 404 dp->dp_typ == DOSPTYP_EXTENDL)) { 405 nextebr = dp->dp_start + mbr_eoff; 406 if (nextebr < dp->dp_start) 407 nextebr = (u_int)-1; 408 if (mbr_eoff == DOSBBSECTOR) 409 mbr_eoff = dp->dp_start; 410 } 411 } 412 413 if (nextebr && nextebr != (u_int)-1) { 414 mbroff = nextebr; 415 goto again; 416 } 417 418 return (-1); 419 } 420 421 const char * 422 bios_getdisklabel(bios_diskinfo_t *bd, struct disklabel *label) 423 { 424 u_int start = 0; 425 char *buf; 426 const char *err = NULL; 427 int error; 428 429 /* Sanity check */ 430 if (bd->bios_edd == -1 && 431 (bd->bios_heads == 0 || bd->bios_sectors == 0)) 432 return "failed to read disklabel"; 433 434 /* MBR is a harddisk thing */ 435 if (bd->bios_number & 0x80) { 436 start = findopenbsd(bd, &err); 437 if (start == (u_int)-1) { 438 if (err != NULL) 439 return (err); 440 return "no OpenBSD partition\n"; 441 } 442 } 443 start = LABELSECTOR + start; 444 445 /* Load BSD disklabel */ 446 buf = alloca(DEV_BSIZE); 447 #ifdef BIOS_DEBUG 448 if (debug) 449 printf("loading disklabel @ %u\n", start); 450 #endif 451 /* read disklabel */ 452 error = biosd_io(F_READ, bd, start, 1, buf); 453 454 if (error) 455 return "failed to read disklabel"; 456 457 /* Fill in disklabel */ 458 return (getdisklabel(buf, label)); 459 } 460 461 int 462 biosopen(struct open_file *f, ...) 463 { 464 va_list ap; 465 register char *cp, **file; 466 dev_t maj, unit, part; 467 struct diskinfo *dip; 468 int biosdev; 469 470 va_start(ap, f); 471 cp = *(file = va_arg(ap, char **)); 472 va_end(ap); 473 474 #ifdef BIOS_DEBUG 475 if (debug) 476 printf("%s\n", cp); 477 #endif 478 479 f->f_devdata = NULL; 480 /* search for device specification */ 481 cp += 2; 482 if (cp[2] != ':') { 483 if (cp[3] != ':') 484 return ENOENT; 485 else 486 cp++; 487 } 488 489 for (maj = 0; maj < nbdevs && strncmp(*file, bdevs[maj], cp - *file); ) 490 maj++; 491 if (maj >= nbdevs) { 492 printf("Unknown device: "); 493 for (cp = *file; *cp != ':'; cp++) 494 putchar(*cp); 495 putchar('\n'); 496 return EADAPT; 497 } 498 499 /* get unit */ 500 if ('0' <= *cp && *cp <= '9') 501 unit = *cp++ - '0'; 502 else { 503 printf("Bad unit number\n"); 504 return EUNIT; 505 } 506 /* get partition */ 507 if ('a' <= *cp && *cp <= 'p') 508 part = *cp++ - 'a'; 509 else { 510 printf("Bad partition id\n"); 511 return EPART; 512 } 513 514 cp++; /* skip ':' */ 515 if (*cp != 0) 516 *file = cp; 517 else 518 f->f_flags |= F_RAW; 519 520 biosdev = unit; 521 switch (maj) { 522 case 0: /* wd */ 523 case 4: /* sd */ 524 case 17: /* hd */ 525 biosdev |= 0x80; 526 break; 527 case 2: /* fd */ 528 break; 529 case 6: /* cd */ 530 biosdev = bios_bootdev & 0xff; 531 break; 532 default: 533 return ENXIO; 534 } 535 536 /* Find device */ 537 bootdev_dip = dip = dklookup(biosdev); 538 539 /* Fix up bootdev */ 540 { dev_t bsd_dev; 541 bsd_dev = dip->bios_info.bsd_dev; 542 dip->bsddev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev), 543 B_CONTROLLER(bsd_dev), unit, part); 544 dip->bootdev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev), 545 B_CONTROLLER(bsd_dev), B_UNIT(bsd_dev), part); 546 } 547 548 #if 0 549 dip->bios_info.bsd_dev = dip->bootdev; 550 bootdev = dip->bootdev; 551 #endif 552 553 #ifdef BIOS_DEBUG 554 if (debug) { 555 printf("BIOS geometry: heads=%u, s/t=%u; EDD=%d\n", 556 dip->bios_info.bios_heads, dip->bios_info.bios_sectors, 557 dip->bios_info.bios_edd); 558 } 559 #endif 560 561 /* Try for disklabel again (might be removable media) */ 562 if (dip->bios_info.flags & BDI_BADLABEL) { 563 const char *st = bios_getdisklabel(&dip->bios_info, 564 &dip->disklabel); 565 #ifdef BIOS_DEBUG 566 if (debug && st) 567 printf("%s\n", st); 568 #endif 569 if (!st) { 570 dip->bios_info.flags &= ~BDI_BADLABEL; 571 dip->bios_info.flags |= BDI_GOODLABEL; 572 } else 573 return ERDLAB; 574 } 575 576 f->f_devdata = dip; 577 578 return 0; 579 } 580 581 const u_char bidos_errs[] = 582 /* ignored "\x00" "successful completion\0" */ 583 "\x01" "invalid function/parameter\0" 584 "\x02" "address mark not found\0" 585 "\x03" "write-protected\0" 586 "\x04" "sector not found\0" 587 "\x05" "reset failed\0" 588 "\x06" "disk changed\0" 589 "\x07" "drive parameter activity failed\0" 590 "\x08" "DMA overrun\0" 591 "\x09" "data boundary error\0" 592 "\x0A" "bad sector detected\0" 593 "\x0B" "bad track detected\0" 594 "\x0C" "invalid media\0" 595 "\x0E" "control data address mark detected\0" 596 "\x0F" "DMA arbitration level out of range\0" 597 "\x10" "uncorrectable CRC or ECC error on read\0" 598 /* ignored "\x11" "data ECC corrected\0" */ 599 "\x20" "controller failure\0" 600 "\x31" "no media in drive\0" 601 "\x32" "incorrect drive type in CMOS\0" 602 "\x40" "seek failed\0" 603 "\x80" "operation timed out\0" 604 "\xAA" "drive not ready\0" 605 "\xB0" "volume not locked in drive\0" 606 "\xB1" "volume locked in drive\0" 607 "\xB2" "volume not removable\0" 608 "\xB3" "volume in use\0" 609 "\xB4" "lock count exceeded\0" 610 "\xB5" "valid eject request failed\0" 611 "\xBB" "undefined error\0" 612 "\xCC" "write fault\0" 613 "\xE0" "status register error\0" 614 "\xFF" "sense operation failed\0" 615 "\x00" "\0"; 616 617 static const char * 618 biosdisk_err(u_int error) 619 { 620 register const u_char *p = bidos_errs; 621 622 while (*p && *p != error) 623 while (*p++); 624 625 return ++p; 626 } 627 628 const struct biosdisk_errors { 629 u_char error; 630 u_char errno; 631 } tab[] = { 632 { 0x01, EINVAL }, 633 { 0x03, EROFS }, 634 { 0x08, EINVAL }, 635 { 0x09, EINVAL }, 636 { 0x0A, EBSE }, 637 { 0x0B, EBSE }, 638 { 0x0C, ENXIO }, 639 { 0x0D, EINVAL }, 640 { 0x10, EECC }, 641 { 0x20, EHER }, 642 { 0x31, ENXIO }, 643 { 0x32, ENXIO }, 644 { 0x00, EIO } 645 }; 646 647 static int 648 biosdisk_errno(u_int error) 649 { 650 register const struct biosdisk_errors *p; 651 652 if (error == 0) 653 return 0; 654 655 for (p = tab; p->error && p->error != error; p++); 656 657 return p->errno; 658 } 659 660 int 661 biosstrategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf, 662 size_t *rsize) 663 { 664 struct diskinfo *dip = (struct diskinfo *)devdata; 665 bios_diskinfo_t *bd = &dip->bios_info; 666 u_int8_t error = 0; 667 size_t nsect; 668 669 nsect = (size + DEV_BSIZE-1) / DEV_BSIZE; 670 if (rsize != NULL) 671 blk += dip->disklabel. 672 d_partitions[B_PARTITION(dip->bsddev)].p_offset; 673 674 /* Read all, sub-functions handle track boundaries */ 675 if (blk < 0) 676 error = EINVAL; 677 else 678 error = biosd_io(rw, bd, blk, nsect, buf); 679 680 #ifdef BIOS_DEBUG 681 if (debug) { 682 if (error != 0) 683 printf("=0x%x(%s)", error, biosdisk_err(error)); 684 putchar('\n'); 685 } 686 #endif 687 688 if (rsize != NULL) 689 *rsize = nsect * DEV_BSIZE; 690 691 return (biosdisk_errno(error)); 692 } 693 694 int 695 biosclose(struct open_file *f) 696 { 697 f->f_devdata = NULL; 698 699 return 0; 700 } 701 702 int 703 biosioctl(struct open_file *f, u_long cmd, void *data) 704 { 705 return 0; 706 } 707