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 <stand.h> 41 42#include <sys/disklabel.h> 43#include <sys/diskmbr.h> 44#include <machine/bootinfo.h> 45 46#include <stdarg.h> 47 48#include <bootstrap.h> 49#include <btxv86.h> 50#include "libi386.h" 51 52#define BIOS_NUMDRIVES 0x475 53#define BIOSDISK_SECSIZE 512 54#define BUFSIZE (1 * BIOSDISK_SECSIZE) 55#define MAXBDDEV MAXDEV 56 57#define DT_ATAPI 0x10 /* disk type for ATAPI floppies */ 58#define WDMAJOR 0 /* major numbers for devices we frontend for */ 59#define WFDMAJOR 1 60#define FDMAJOR 2 61#define DAMAJOR 4 62 63#ifdef DISK_DEBUG 64# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 65#else 66# define DEBUG(fmt, args...) 67#endif 68 69struct open_disk { 70 int od_dkunit; /* disk unit number */ 71 int od_unit; /* BIOS unit number */ 72 int od_cyl; /* BIOS geometry */ 73 int od_hds; 74 int od_sec; 75 int od_boff; /* block offset from beginning of BIOS disk */ 76 int od_flags; 77#define BD_MODEINT13 0x0000 78#define BD_MODEEDD1 0x0001 79#define BD_MODEEDD3 0x0002 80#define BD_MODEMASK 0x0003 81#define BD_FLOPPY 0x0004 82#define BD_LABELOK 0x0008 83#define BD_PARTTABOK 0x0010 84 struct disklabel od_disklabel; 85 int od_nslices; /* slice count */ 86 struct dos_partition od_slicetab[NEXTDOSPART]; 87}; 88 89/* 90 * List of BIOS devices, translation from disk unit number to 91 * BIOS unit number. 92 */ 93static struct bdinfo 94{ 95 int bd_unit; /* BIOS unit number */ 96 int bd_flags; 97 int bd_type; /* BIOS 'drive type' (floppy only) */ 98} bdinfo [MAXBDDEV]; 99static int nbdinfo = 0; 100 101static int bd_getgeom(struct open_disk *od); 102static int bd_read(struct open_disk *od, daddr_t dblk, int blks, 103 caddr_t dest); 104static int bd_write(struct open_disk *od, daddr_t dblk, int blks, 105 caddr_t dest); 106 107static int bd_int13probe(struct bdinfo *bd); 108 109static void bd_printslice(struct open_disk *od, struct dos_partition *dp, 110 char *prefix, int verbose); 111static void bd_printbsdslice(struct open_disk *od, daddr_t offset, 112 char *prefix, int verbose); 113 114static int bd_init(void); 115static int bd_strategy(void *devdata, int flag, daddr_t dblk, 116 size_t size, char *buf, size_t *rsize); 117static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, 118 size_t size, char *buf, size_t *rsize); 119static int bd_open(struct open_file *f, ...); 120static int bd_close(struct open_file *f); 121static void bd_print(int verbose); 122 123struct devsw biosdisk = { 124 "disk", 125 DEVT_DISK, 126 bd_init, 127 bd_strategy, 128 bd_open, 129 bd_close, 130 noioctl, 131 bd_print, 132 NULL 133}; 134 135static int bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev); 136static void bd_closedisk(struct open_disk *od); 137static int bd_bestslice(struct open_disk *od); 138static void bd_checkextended(struct open_disk *od, int slicenum); 139 140/* 141 * Translate between BIOS device numbers and our private unit numbers. 142 */ 143int 144bd_bios2unit(int biosdev) 145{ 146 int i; 147 148 DEBUG("looking for bios device 0x%x", biosdev); 149 for (i = 0; i < nbdinfo; i++) { 150 DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit); 151 if (bdinfo[i].bd_unit == biosdev) 152 return(i); 153 } 154 return(-1); 155} 156 157int 158bd_unit2bios(int unit) 159{ 160 if ((unit >= 0) && (unit < nbdinfo)) 161 return(bdinfo[unit].bd_unit); 162 return(-1); 163} 164 165/* 166 * Quiz the BIOS for disk devices, save a little info about them. 167 */ 168static int 169bd_init(void) 170{ 171 int base, unit, nfd = 0; 172 173 /* sequence 0, 0x80 */ 174 for (base = 0; base <= 0x80; base += 0x80) { 175 for (unit = base; (nbdinfo < MAXBDDEV); unit++) { 176 /* check the BIOS equipment list for number of fixed disks */ 177 if((base == 0x80) && 178 (nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES))) 179 break; 180 181 bdinfo[nbdinfo].bd_unit = unit; 182 bdinfo[nbdinfo].bd_flags = (unit < 0x80) ? BD_FLOPPY : 0; 183 184 if (!bd_int13probe(&bdinfo[nbdinfo])) 185 break; 186 187 /* XXX we need "disk aliases" to make this simpler */ 188 printf("BIOS drive %c: is disk%d\n", 189 (unit < 0x80) ? ('A' + unit) : ('C' + unit - 0x80), nbdinfo); 190 nbdinfo++; 191 if (base == 0x80) 192 nfd++; 193 } 194 } 195 return(0); 196} 197 198/* 199 * Try to detect a device supported by the legacy int13 BIOS 200 */ 201static int 202bd_int13probe(struct bdinfo *bd) 203{ 204 v86.ctl = V86_FLAGS; 205 v86.addr = 0x13; 206 v86.eax = 0x800; 207 v86.edx = bd->bd_unit; 208 v86int(); 209 210 if (!(v86.efl & 0x1) && /* carry clear */ 211 ((v86.edx & 0xff) > ((unsigned)bd->bd_unit & 0x7f))) { /* unit # OK */ 212 if ((v86.ecx & 0x3f) == 0) { /* absurd sector size */ 213 DEBUG("Invalid geometry for unit %d", bd->bd_unit); 214 return(0); /* skip device */ 215 } 216 bd->bd_flags |= BD_MODEINT13; 217 bd->bd_type = v86.ebx & 0xff; 218 219 /* Determine if we can use EDD with this device. */ 220 v86.eax = 0x4100; 221 v86.edx = bd->bd_unit; 222 v86.ebx = 0x55aa; 223 v86int(); 224 if (!(v86.efl & 0x1) && /* carry clear */ 225 ((v86.ebx & 0xffff) == 0xaa55) && /* signature */ 226 (v86.ecx & 0x1)) { /* packets mode ok */ 227 bd->bd_flags |= BD_MODEEDD1; 228 if((v86.eax & 0xff00) > 0x300) 229 bd->bd_flags |= BD_MODEEDD3; 230 } 231 return(1); 232 } 233 return(0); 234} 235 236/* 237 * Print information about disks 238 */ 239static void 240bd_print(int verbose) 241{ 242 int i, j; 243 char line[80]; 244 struct i386_devdesc dev; 245 struct open_disk *od; 246 struct dos_partition *dptr; 247 248 for (i = 0; i < nbdinfo; i++) { 249 sprintf(line, " disk%d: BIOS drive %c:\n", i, 250 (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit) : ('C' + bdinfo[i].bd_unit - 0x80)); 251 pager_output(line); 252 253 /* try to open the whole disk */ 254 dev.d_unit = i; 255 dev.d_kind.biosdisk.slice = -1; 256 dev.d_kind.biosdisk.partition = -1; 257 258 if (!bd_opendisk(&od, &dev)) { 259 260 /* Do we have a partition table? */ 261 if (od->od_flags & BD_PARTTABOK) { 262 dptr = &od->od_slicetab[0]; 263 264 /* Check for a "dedicated" disk */ 265 if ((dptr[3].dp_typ == DOSPTYP_386BSD) && 266 (dptr[3].dp_start == 0) && 267 (dptr[3].dp_size == 50000)) { 268 sprintf(line, " disk%d", i); 269 bd_printbsdslice(od, 0, line, verbose); 270 } else { 271 for (j = 0; j < od->od_nslices; j++) { 272 sprintf(line, " disk%ds%d", i, j + 1); 273 bd_printslice(od, &dptr[j], line, verbose); 274 } 275 } 276 } 277 bd_closedisk(od); 278 } 279 } 280} 281 282/* 283 * Print information about slices on a disk. For the size calculations we 284 * assume a 512 byte sector. 285 */ 286static void 287bd_printslice(struct open_disk *od, struct dos_partition *dp, char *prefix, 288 int verbose) 289{ 290 char line[80]; 291 292 switch (dp->dp_typ) { 293 case DOSPTYP_386BSD: 294 bd_printbsdslice(od, (daddr_t)dp->dp_start, prefix, verbose); 295 return; 296 case DOSPTYP_LINSWP: 297 if (verbose) 298 sprintf(line, "%s: Linux swap %.6dMB (%d - %d)\n", 299 prefix, dp->dp_size / 2048, 300 dp->dp_start, dp->dp_start + dp->dp_size); 301 else 302 sprintf(line, "%s: Linux swap\n", prefix); 303 break; 304 case DOSPTYP_LINUX: 305 /* 306 * XXX 307 * read the superblock to confirm this is an ext2fs partition? 308 */ 309 if (verbose) 310 sprintf(line, "%s: ext2fs %.6dMB (%d - %d)\n", prefix, 311 dp->dp_size / 2048, dp->dp_start, 312 dp->dp_start + dp->dp_size); 313 else 314 sprintf(line, "%s: ext2fs\n", prefix); 315 break; 316 case 0x00: /* unused partition */ 317 case DOSPTYP_EXT: 318 return; 319 case 0x01: 320 if (verbose) 321 sprintf(line, "%s: FAT-12 %.6dMB (%d - %d)\n", prefix, 322 dp->dp_size / 2048, dp->dp_start, 323 dp->dp_start + dp->dp_size); 324 else 325 sprintf(line, "%s: FAT-12\n", prefix); 326 break; 327 case 0x04: 328 case 0x06: 329 case 0x0e: 330 if (verbose) 331 sprintf(line, "%s: FAT-16 %.6dMB (%d - %d)\n", prefix, 332 dp->dp_size / 2048, dp->dp_start, 333 dp->dp_start + dp->dp_size); 334 else 335 sprintf(line, "%s: FAT-16\n", prefix); 336 break; 337 case 0x0b: 338 case 0x0c: 339 if (verbose) 340 sprintf(line, "%s: FAT-32 %.6dMB (%d - %d)\n", prefix, 341 dp->dp_size / 2048, dp->dp_start, 342 dp->dp_start + dp->dp_size); 343 else 344 sprintf(line, "%s: FAT-32\n", prefix); 345 break; 346 default: 347 if (verbose) 348 sprintf(line, "%s: Unknown fs: 0x%x %.6dMB (%d - %d)\n", 349 prefix, dp->dp_typ, dp->dp_size / 2048, 350 dp->dp_start, dp->dp_start + dp->dp_size); 351 else 352 sprintf(line, "%s: Unknown fs: 0x%x\n", prefix, 353 dp->dp_typ); 354 } 355 pager_output(line); 356} 357 358/* 359 * Print out each valid partition in the disklabel of a FreeBSD slice. 360 * For size calculations, we assume a 512 byte sector size. 361 */ 362static void 363bd_printbsdslice(struct open_disk *od, daddr_t offset, char *prefix, 364 int verbose) 365{ 366 char line[80]; 367 char buf[BIOSDISK_SECSIZE]; 368 struct disklabel *lp; 369 int i; 370 371 /* read disklabel */ 372 if (bd_read(od, offset + LABELSECTOR, 1, buf)) 373 return; 374 lp =(struct disklabel *)(&buf[0]); 375 if (lp->d_magic != DISKMAGIC) { 376 sprintf(line, "%s: FFS bad disklabel\n", prefix); 377 pager_output(line); 378 return; 379 } 380 381 /* Print partitions */ 382 for (i = 0; i < lp->d_npartitions; i++) { 383 /* 384 * For each partition, make sure we know what type of fs it is. If 385 * not, then skip it. However, since floppies often have bogus 386 * fstypes, print the 'a' partition on a floppy even if it is marked 387 * unused. 388 */ 389 if ((lp->d_partitions[i].p_fstype == FS_BSDFFS) || 390 (lp->d_partitions[i].p_fstype == FS_SWAP) || 391 (lp->d_partitions[i].p_fstype == FS_VINUM) || 392 ((lp->d_partitions[i].p_fstype == FS_UNUSED) && 393 (od->od_flags & BD_FLOPPY) && (i == 0))) { 394 395 /* Only print out statistics in verbose mode */ 396 if (verbose) 397 sprintf(line, " %s%c: %s %.6dMB (%d - %d)\n", prefix, 'a' + i, 398 (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : 399 (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" : 400 "FFS", 401 lp->d_partitions[i].p_size / 2048, 402 lp->d_partitions[i].p_offset, 403 lp->d_partitions[i].p_offset + lp->d_partitions[i].p_size); 404 else 405 sprintf(line, " %s%c: %s\n", prefix, 'a' + i, 406 (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : 407 (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" : 408 "FFS"); 409 pager_output(line); 410 } 411 } 412} 413 414 415/* 416 * Attempt to open the disk described by (dev) for use by (f). 417 * 418 * Note that the philosophy here is "give them exactly what 419 * they ask for". This is necessary because being too "smart" 420 * about what the user might want leads to complications. 421 * (eg. given no slice or partition value, with a disk that is 422 * sliced - are they after the first BSD slice, or the DOS 423 * slice before it?) 424 */ 425static int 426bd_open(struct open_file *f, ...) 427{ 428 va_list ap; 429 struct i386_devdesc *dev; 430 struct open_disk *od; 431 int error; 432 433 va_start(ap, f); 434 dev = va_arg(ap, struct i386_devdesc *); 435 va_end(ap); 436 if ((error = bd_opendisk(&od, dev))) 437 return(error); 438 439 /* 440 * Save our context 441 */ 442 ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od; 443 DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff); 444 return(0); 445} 446 447static int 448bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev) 449{ 450 struct dos_partition *dptr; 451 struct disklabel *lp; 452 struct open_disk *od; 453 int sector, slice, i; 454 int error; 455 char buf[BUFSIZE]; 456 457 if (dev->d_unit >= nbdinfo) { 458 DEBUG("attempt to open nonexistent disk"); 459 return(ENXIO); 460 } 461 462 od = (struct open_disk *)malloc(sizeof(struct open_disk)); 463 if (!od) { 464 DEBUG("no memory"); 465 return (ENOMEM); 466 } 467 468 /* Look up BIOS unit number, intialise open_disk structure */ 469 od->od_dkunit = dev->d_unit; 470 od->od_unit = bdinfo[od->od_dkunit].bd_unit; 471 od->od_flags = bdinfo[od->od_dkunit].bd_flags; 472 od->od_boff = 0; 473 od->od_nslices = 0; 474 error = 0; 475 DEBUG("open '%s', unit 0x%x slice %d partition %c", 476 i386_fmtdev(dev), dev->d_unit, 477 dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition + 'a'); 478 479 /* Get geometry for this open (removable device may have changed) */ 480 if (bd_getgeom(od)) { 481 DEBUG("can't get geometry"); 482 error = ENXIO; 483 goto out; 484 } 485 486 /* 487 * Following calculations attempt to determine the correct value 488 * for d->od_boff by looking for the slice and partition specified, 489 * or searching for reasonable defaults. 490 */ 491 492 /* 493 * Find the slice in the DOS slice table. 494 */ 495 if (bd_read(od, 0, 1, buf)) { 496 DEBUG("error reading MBR"); 497 error = EIO; 498 goto out; 499 } 500 501 /* 502 * Check the slice table magic. 503 */ 504 if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { 505 /* If a slice number was explicitly supplied, this is an error */ 506 if (dev->d_kind.biosdisk.slice > 0) { 507 DEBUG("no slice table/MBR (no magic)"); 508 error = ENOENT; 509 goto out; 510 } 511 sector = 0; 512 goto unsliced; /* may be a floppy */ 513 } 514 515 /* 516 * copy the partition table, then pick up any extended partitions. 517 */ 518 bcopy(buf + DOSPARTOFF, &od->od_slicetab, 519 sizeof(struct dos_partition) * NDOSPART); 520 od->od_nslices = 4; /* extended slices start here */ 521 for (i = 0; i < NDOSPART; i++) 522 bd_checkextended(od, i); 523 od->od_flags |= BD_PARTTABOK; 524 dptr = &od->od_slicetab[0]; 525 526 /* Is this a request for the whole disk? */ 527 if (dev->d_kind.biosdisk.slice == -1) { 528 sector = 0; 529 goto unsliced; 530 } 531 532 /* 533 * if a slice number was supplied but not found, this is an error. 534 */ 535 if (dev->d_kind.biosdisk.slice > 0) { 536 slice = dev->d_kind.biosdisk.slice - 1; 537 if (slice >= od->od_nslices) { 538 DEBUG("slice %d not found", slice); 539 error = ENOENT; 540 goto out; 541 } 542 } 543 544 /* 545 * Check for the historically bogus MBR found on true dedicated disks 546 */ 547 if ((dptr[3].dp_typ == DOSPTYP_386BSD) && 548 (dptr[3].dp_start == 0) && 549 (dptr[3].dp_size == 50000)) { 550 sector = 0; 551 goto unsliced; 552 } 553 554 /* Try to auto-detect the best slice; this should always give a slice number */ 555 if (dev->d_kind.biosdisk.slice == 0) { 556 slice = bd_bestslice(od); 557 if (slice == -1) { 558 error = ENOENT; 559 goto out; 560 } 561 dev->d_kind.biosdisk.slice = slice; 562 } 563 564 dptr = &od->od_slicetab[0]; 565 /* 566 * Accept the supplied slice number unequivocally (we may be looking 567 * at a DOS partition). 568 */ 569 dptr += (dev->d_kind.biosdisk.slice - 1); /* we number 1-4, offsets are 0-3 */ 570 sector = dptr->dp_start; 571 DEBUG("slice entry %d at %d, %d sectors", dev->d_kind.biosdisk.slice - 1, sector, dptr->dp_size); 572 573 /* 574 * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition 575 */ 576 if ((dptr->dp_typ == DOSPTYP_386BSD) && (dev->d_kind.biosdisk.partition < 0)) 577 dev->d_kind.biosdisk.partition = 0; 578 579 unsliced: 580 /* 581 * Now we have the slice offset, look for the partition in the disklabel if we have 582 * a partition to start with. 583 * 584 * XXX we might want to check the label checksum. 585 */ 586 if (dev->d_kind.biosdisk.partition < 0) { 587 od->od_boff = sector; /* no partition, must be after the slice */ 588 DEBUG("opening raw slice"); 589 } else { 590 591 if (bd_read(od, sector + LABELSECTOR, 1, buf)) { 592 DEBUG("error reading disklabel"); 593 error = EIO; 594 goto out; 595 } 596 DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel); 597 bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel)); 598 lp = &od->od_disklabel; 599 od->od_flags |= BD_LABELOK; 600 601 if (lp->d_magic != DISKMAGIC) { 602 DEBUG("no disklabel"); 603 error = ENOENT; 604 goto out; 605 } 606 if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) { 607 DEBUG("partition '%c' exceeds partitions in table (a-'%c')", 608 'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions); 609 error = EPART; 610 goto out; 611 612 } 613 614#ifdef DISK_DEBUG 615 /* Complain if the partition is unused unless this is a floppy. */ 616 if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) && 617 !(od->od_flags & BD_FLOPPY)) 618 DEBUG("warning, partition marked as unused"); 619#endif 620 621 od->od_boff = 622 lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset - 623 lp->d_partitions[RAW_PART].p_offset + 624 sector; 625 } 626 627 out: 628 if (error) { 629 free(od); 630 } else { 631 *odp = od; /* return the open disk */ 632 } 633 return(error); 634} 635 636static void 637bd_checkextended(struct open_disk *od, int slicenum) 638{ 639 char buf[BIOSDISK_SECSIZE]; 640 struct dos_partition *dp; 641 u_int base; 642 int i, start, end; 643 644 dp = &od->od_slicetab[slicenum]; 645 start = od->od_nslices; 646 647 if (dp->dp_size == 0) 648 goto done; 649 if (dp->dp_typ != DOSPTYP_EXT) 650 goto done; 651 if (bd_read(od, (daddr_t)dp->dp_start, 1, buf)) 652 goto done; 653 if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { 654 DEBUG("no magic in extended table"); 655 goto done; 656 } 657 base = dp->dp_start; 658 dp = (struct dos_partition *)(&buf[DOSPARTOFF]); 659 for (i = 0; i < NDOSPART; i++, dp++) { 660 if (dp->dp_size == 0) 661 continue; 662 if (od->od_nslices == NEXTDOSPART) 663 goto done; 664 dp->dp_start += base; 665 bcopy(dp, &od->od_slicetab[od->od_nslices], sizeof(*dp)); 666 od->od_nslices++; 667 } 668 end = od->od_nslices; 669 670 /* 671 * now, recursively check the slices we just added 672 */ 673 for (i = start; i < end; i++) 674 bd_checkextended(od, i); 675done: 676 return; 677} 678 679/* 680 * Search for a slice with the following preferences: 681 * 682 * 1: Active FreeBSD slice 683 * 2: Non-active FreeBSD slice 684 * 3: Active Linux slice 685 * 4: non-active Linux slice 686 * 5: Active FAT/FAT32 slice 687 * 6: non-active FAT/FAT32 slice 688 */ 689#define PREF_RAWDISK 0 690#define PREF_FBSD_ACT 1 691#define PREF_FBSD 2 692#define PREF_LINUX_ACT 3 693#define PREF_LINUX 4 694#define PREF_DOS_ACT 5 695#define PREF_DOS 6 696#define PREF_NONE 7 697 698/* 699 * slicelimit is in the range 0 .. NDOSPART 700 */ 701static int 702bd_bestslice(struct open_disk *od) 703{ 704 struct dos_partition *dp; 705 int pref, preflevel; 706 int i, prefslice; 707 708 prefslice = 0; 709 preflevel = PREF_NONE; 710 711 dp = &od->od_slicetab[0]; 712 for (i = 0; i < od->od_nslices; i++, dp++) { 713 714 switch (dp->dp_typ) { 715 case DOSPTYP_386BSD: /* FreeBSD */ 716 pref = dp->dp_flag & 0x80 ? PREF_FBSD_ACT : PREF_FBSD; 717 break; 718 719 case DOSPTYP_LINUX: 720 pref = dp->dp_flag & 0x80 ? PREF_LINUX_ACT : PREF_LINUX; 721 break; 722 723 case 0x01: /* DOS/Windows */ 724 case 0x04: 725 case 0x06: 726 case 0x0b: 727 case 0x0c: 728 case 0x0e: 729 pref = dp->dp_flag & 0x80 ? PREF_DOS_ACT : PREF_DOS; 730 break; 731 732 default: 733 pref = PREF_NONE; 734 } 735 if (pref < preflevel) { 736 preflevel = pref; 737 prefslice = i + 1; 738 } 739 } 740 return (prefslice); 741} 742 743static int 744bd_close(struct open_file *f) 745{ 746 struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data); 747 748 bd_closedisk(od); 749 return(0); 750} 751 752static void 753bd_closedisk(struct open_disk *od) 754{ 755 DEBUG("open_disk %p", od); 756#if 0 757 /* XXX is this required? (especially if disk already open...) */ 758 if (od->od_flags & BD_FLOPPY) 759 delay(3000000); 760#endif 761 free(od); 762} 763 764static int 765bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) 766{ 767 struct bcache_devdata bcd; 768 struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data); 769 770 bcd.dv_strategy = bd_realstrategy; 771 bcd.dv_devdata = devdata; 772 return(bcache_strategy(&bcd, od->od_unit, rw, dblk+od->od_boff, size, buf, rsize)); 773} 774 775static int 776bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) 777{ 778 struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data); 779 int blks; 780#ifdef BD_SUPPORT_FRAGS 781 char fragbuf[BIOSDISK_SECSIZE]; 782 size_t fragsize; 783 784 fragsize = size % BIOSDISK_SECSIZE; 785#else 786 if (size % BIOSDISK_SECSIZE) 787 panic("bd_strategy: %d bytes I/O not multiple of block size", size); 788#endif 789 790 DEBUG("open_disk %p", od); 791 792 793 switch(rw){ 794 case F_READ: 795 blks = size / BIOSDISK_SECSIZE; 796 DEBUG("read %d from %d to %p", blks, dblk, buf); 797 798 if (rsize) 799 *rsize = 0; 800 if (blks && bd_read(od, dblk, blks, buf)) { 801 DEBUG("read error"); 802 return (EIO); 803 } 804#ifdef BD_SUPPORT_FRAGS 805 DEBUG("bd_strategy: frag read %d from %d+%d to %p", 806 fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE)); 807 if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) { 808 DEBUG("frag read error"); 809 return(EIO); 810 } 811 bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize); 812#endif 813 if (rsize) 814 *rsize = size; 815 return (0); 816 break; 817 818 case F_WRITE : 819 blks = size / BIOSDISK_SECSIZE; 820 DEBUG("write %d from %d to %p", blks, dblk, buf); 821 822 if (rsize) 823 *rsize = 0; 824 if (blks && bd_write(od, dblk, blks, buf)) { 825 DEBUG("write error"); 826 return (EIO); 827 } 828#ifdef BD_SUPPORT_FRAGS 829 if(fragsize) { 830 DEBUG("Attempted to write a frag"); 831 return (EIO); 832 } 833#endif 834 835 if (rsize) 836 *rsize = size; 837 return (0); 838 default: 839 /* DO NOTHING */ 840 break; 841 } 842 843 return EROFS; 844} 845 846/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */ 847#define FLOPPY_BOUNCEBUF 18 848 849static int 850bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) 851{ 852 u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer; 853 caddr_t p, xp, bbuf, breg; 854 855 /* Just in case some idiot actually tries to read -1 blocks... */ 856 if (blks < 0) 857 return (-1); 858 859 bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ 860 resid = blks; 861 p = dest; 862 863 /* Decide whether we have to bounce */ 864 if (VTOP(dest) >> 20 != 0 || ((od->od_unit < 0x80) && 865 ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16)))) { 866 867 /* 868 * There is a 64k physical boundary somewhere in the destination buffer, or the 869 * destination buffer is above first 1MB of physical memory so we have 870 * to arrange a suitable bounce buffer. Allocate a buffer twice as large as we 871 * need to. Use the bottom half unless there is a break there, in which case we 872 * use the top half. 873 */ 874 x = min(FLOPPY_BOUNCEBUF, (unsigned)blks); 875 bbuf = alloca(x * 2 * BIOSDISK_SECSIZE); 876 if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(bbuf + x * BIOSDISK_SECSIZE) & 0xffff0000)) { 877 breg = bbuf; 878 } else { 879 breg = bbuf + x * BIOSDISK_SECSIZE; 880 } 881 maxfer = x; /* limit transfers to bounce region size */ 882 } else { 883 breg = bbuf = NULL; 884 maxfer = 0; 885 } 886 887 while (resid > 0) { 888 x = dblk; 889 cyl = x / bpc; /* block # / blocks per cylinder */ 890 x %= bpc; /* block offset into cylinder */ 891 hd = x / od->od_sec; /* offset / blocks per track */ 892 sec = x % od->od_sec; /* offset into track */ 893 894 /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */ 895 x = min(od->od_sec - sec, resid); 896 if (maxfer > 0) 897 x = min(x, maxfer); /* fit bounce buffer */ 898 899 /* where do we transfer to? */ 900 xp = bbuf == NULL ? p : breg; 901 902 /* correct sector number for 1-based BIOS numbering */ 903 sec++; 904 905 /* Loop retrying the operation a couple of times. The BIOS may also retry. */ 906 for (retry = 0; retry < 3; retry++) { 907 /* if retrying, reset the drive */ 908 if (retry > 0) { 909 v86.ctl = V86_FLAGS; 910 v86.addr = 0x13; 911 v86.eax = 0; 912 v86.edx = od->od_unit; 913 v86int(); 914 } 915 916 if(cyl > 1023) { 917 /* use EDD if the disk supports it, otherwise, return error */ 918 if(od->od_flags & BD_MODEEDD1) { 919 static unsigned short packet[8]; 920 921 packet[0] = 0x10; 922 packet[1] = x; 923 packet[2] = VTOPOFF(xp); 924 packet[3] = VTOPSEG(xp); 925 packet[4] = dblk & 0xffff; 926 packet[5] = dblk >> 16; 927 packet[6] = 0; 928 packet[7] = 0; 929 v86.ctl = V86_FLAGS; 930 v86.addr = 0x13; 931 v86.eax = 0x4200; 932 v86.edx = od->od_unit; 933 v86.ds = VTOPSEG(packet); 934 v86.esi = VTOPOFF(packet); 935 v86int(); 936 result = (v86.efl & 0x1); 937 if(result == 0) 938 break; 939 } else { 940 result = 1; 941 break; 942 } 943 } else { 944 /* Use normal CHS addressing */ 945 v86.ctl = V86_FLAGS; 946 v86.addr = 0x13; 947 v86.eax = 0x200 | x; 948 v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; 949 v86.edx = (hd << 8) | od->od_unit; 950 v86.es = VTOPSEG(xp); 951 v86.ebx = VTOPOFF(xp); 952 v86int(); 953 result = (v86.efl & 0x1); 954 if (result == 0) 955 break; 956 } 957 } 958 959 DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok"); 960 /* BUG here, cannot use v86 in printf because putchar uses it too */ 961 DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 962 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff); 963 if (result) { 964 return(-1); 965 } 966 if (bbuf != NULL) 967 bcopy(breg, p, x * BIOSDISK_SECSIZE); 968 p += (x * BIOSDISK_SECSIZE); 969 dblk += x; 970 resid -= x; 971 } 972 973/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */ 974 return(0); 975} 976 977 978static int 979bd_write(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) 980{ 981 u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer; 982 caddr_t p, xp, bbuf, breg; 983 984 /* Just in case some idiot actually tries to read -1 blocks... */ 985 if (blks < 0) 986 return (-1); 987 988 bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ 989 resid = blks; 990 p = dest; 991 992 /* Decide whether we have to bounce */ 993 if (VTOP(dest) >> 20 != 0 || ((od->od_unit < 0x80) && 994 ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16)))) { 995 996 /* 997 * There is a 64k physical boundary somewhere in the destination buffer, or the 998 * destination buffer is above first 1MB of physical memory so we have 999 * to arrange a suitable bounce buffer. Allocate a buffer twice as large as we 1000 * need to. Use the bottom half unless there is a break there, in which case we 1001 * use the top half. 1002 */ 1003 x = min(FLOPPY_BOUNCEBUF, (unsigned)blks); 1004 bbuf = alloca(x * 2 * BIOSDISK_SECSIZE); 1005 if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(bbuf + x * BIOSDISK_SECSIZE) & 0xffff0000)) { 1006 breg = bbuf; 1007 } else { 1008 breg = bbuf + x * BIOSDISK_SECSIZE; 1009 } 1010 maxfer = x; /* limit transfers to bounce region size */ 1011 } else { 1012 breg = bbuf = NULL; 1013 maxfer = 0; 1014 } 1015 1016 while (resid > 0) { 1017 x = dblk; 1018 cyl = x / bpc; /* block # / blocks per cylinder */ 1019 x %= bpc; /* block offset into cylinder */ 1020 hd = x / od->od_sec; /* offset / blocks per track */ 1021 sec = x % od->od_sec; /* offset into track */ 1022 1023 /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */ 1024 x = min(od->od_sec - sec, resid); 1025 if (maxfer > 0) 1026 x = min(x, maxfer); /* fit bounce buffer */ 1027 1028 /* where do we transfer to? */ 1029 xp = bbuf == NULL ? p : breg; 1030 1031 /* correct sector number for 1-based BIOS numbering */ 1032 sec++; 1033 1034 1035 /* Put your Data In, Put your Data out, 1036 Put your Data In, and shake it all about 1037 */ 1038 if (bbuf != NULL) 1039 bcopy(p, breg, x * BIOSDISK_SECSIZE); 1040 1041 /* Loop retrying the operation a couple of times. The BIOS may also retry. */ 1042 for (retry = 0; retry < 3; retry++) { 1043 /* if retrying, reset the drive */ 1044 if (retry > 0) { 1045 v86.ctl = V86_FLAGS; 1046 v86.addr = 0x13; 1047 v86.eax = 0; 1048 v86.edx = od->od_unit; 1049 v86int(); 1050 } 1051 1052 if(cyl > 1023) { 1053 /* use EDD if the disk supports it, otherwise, return error */ 1054 if(od->od_flags & BD_MODEEDD1) { 1055 static unsigned short packet[8]; 1056 1057 packet[0] = 0x10; 1058 packet[1] = x; 1059 packet[2] = VTOPOFF(xp); 1060 packet[3] = VTOPSEG(xp); 1061 packet[4] = dblk & 0xffff; 1062 packet[5] = dblk >> 16; 1063 packet[6] = 0; 1064 packet[7] = 0; 1065 v86.ctl = V86_FLAGS; 1066 v86.addr = 0x13; 1067 /* Should we Write with verify ?? 0x4302 ? */ 1068 v86.eax = 0x4300; 1069 v86.edx = od->od_unit; 1070 v86.ds = VTOPSEG(packet); 1071 v86.esi = VTOPOFF(packet); 1072 v86int(); 1073 result = (v86.efl & 0x1); 1074 if(result == 0) 1075 break; 1076 } else { 1077 result = 1; 1078 break; 1079 } 1080 } else { 1081 /* Use normal CHS addressing */ 1082 v86.ctl = V86_FLAGS; 1083 v86.addr = 0x13; 1084 v86.eax = 0x300 | x; 1085 v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; 1086 v86.edx = (hd << 8) | od->od_unit; 1087 v86.es = VTOPSEG(xp); 1088 v86.ebx = VTOPOFF(xp); 1089 v86int(); 1090 result = (v86.efl & 0x1); 1091 if (result == 0) 1092 break; 1093 } 1094 } 1095 1096 DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok"); 1097 /* BUG here, cannot use v86 in printf because putchar uses it too */ 1098 DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 1099 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff); 1100 if (result) { 1101 return(-1); 1102 } 1103 p += (x * BIOSDISK_SECSIZE); 1104 dblk += x; 1105 resid -= x; 1106 } 1107 1108/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */ 1109 return(0); 1110} 1111static int 1112bd_getgeom(struct open_disk *od) 1113{ 1114 1115 v86.ctl = V86_FLAGS; 1116 v86.addr = 0x13; 1117 v86.eax = 0x800; 1118 v86.edx = od->od_unit; 1119 v86int(); 1120 1121 if ((v86.efl & 0x1) || /* carry set */ 1122 ((v86.edx & 0xff) <= (unsigned)(od->od_unit & 0x7f))) /* unit # bad */ 1123 return(1); 1124 1125 /* convert max cyl # -> # of cylinders */ 1126 od->od_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1; 1127 /* convert max head # -> # of heads */ 1128 od->od_hds = ((v86.edx & 0xff00) >> 8) + 1; 1129 od->od_sec = v86.ecx & 0x3f; 1130 1131 DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec); 1132 return(0); 1133} 1134 1135/* 1136 * Return the BIOS geometry of a given "fixed drive" in a format 1137 * suitable for the legacy bootinfo structure. Since the kernel is 1138 * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we 1139 * prefer to get the information directly, rather than rely on being 1140 * able to put it together from information already maintained for 1141 * different purposes and for a probably different number of drives. 1142 * 1143 * For valid drives, the geometry is expected in the format (31..0) 1144 * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are 1145 * indicated by returning the geometry of a "1.2M" PC-format floppy 1146 * disk. And, incidentally, what is returned is not the geometry as 1147 * such but the highest valid cylinder, head, and sector numbers. 1148 */ 1149u_int32_t 1150bd_getbigeom(int bunit) 1151{ 1152 1153 v86.ctl = V86_FLAGS; 1154 v86.addr = 0x13; 1155 v86.eax = 0x800; 1156 v86.edx = 0x80 + bunit; 1157 v86int(); 1158 if (v86.efl & 0x1) 1159 return 0x4f010f; 1160 return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) | 1161 (v86.edx & 0xff00) | (v86.ecx & 0x3f); 1162} 1163 1164/* 1165 * Return a suitable dev_t value for (dev). 1166 * 1167 * In the case where it looks like (dev) is a SCSI disk, we allow the number of 1168 * IDE disks to be specified in $num_ide_disks. There should be a Better Way. 1169 */ 1170int 1171bd_getdev(struct i386_devdesc *dev) 1172{ 1173 struct open_disk *od; 1174 int biosdev; 1175 int major; 1176 int rootdev; 1177 char *nip, *cp; 1178 int unitofs = 0, i, unit; 1179 1180 biosdev = bd_unit2bios(dev->d_unit); 1181 DEBUG("unit %d BIOS device %d", dev->d_unit, biosdev); 1182 if (biosdev == -1) /* not a BIOS device */ 1183 return(-1); 1184 if (bd_opendisk(&od, dev) != 0) /* oops, not a viable device */ 1185 return(-1); 1186 1187 if (biosdev < 0x80) { 1188 /* floppy (or emulated floppy) or ATAPI device */ 1189 if (bdinfo[dev->d_unit].bd_type == DT_ATAPI) { 1190 /* is an ATAPI disk */ 1191 major = WFDMAJOR; 1192 } else { 1193 /* is a floppy disk */ 1194 major = FDMAJOR; 1195 } 1196 } else { 1197 /* harddisk */ 1198 if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) { 1199 /* label OK, disk labelled as SCSI */ 1200 major = DAMAJOR; 1201 /* check for unit number correction hint, now deprecated */ 1202 if ((nip = getenv("num_ide_disks")) != NULL) { 1203 i = strtol(nip, &cp, 0); 1204 /* check for parse error */ 1205 if ((cp != nip) && (*cp == 0)) 1206 unitofs = i; 1207 } 1208 } else { 1209 /* assume an IDE disk */ 1210 major = WDMAJOR; 1211 } 1212 } 1213 /* default root disk unit number */ 1214 unit = (biosdev & 0x7f) - unitofs; 1215 1216 /* XXX a better kludge to set the root disk unit number */ 1217 if ((nip = getenv("root_disk_unit")) != NULL) { 1218 i = strtol(nip, &cp, 0); 1219 /* check for parse error */ 1220 if ((cp != nip) && (*cp == 0)) 1221 unit = i; 1222 } 1223
|