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