biosdisk.c revision 130603
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: head/sys/boot/pc98/libpc98/biosdisk.c 130603 2004-06-16 18:21:22Z phk $"); 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/diskpc98.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#ifdef PC98 85#define BD_OPTICAL 0x0020 86#endif 87 struct disklabel od_disklabel; 88 int od_nslices; /* slice count */ 89 struct pc98_partition od_slicetab[NDOSPART]; 90}; 91 92/* 93 * List of BIOS devices, translation from disk unit number to 94 * BIOS unit number. 95 */ 96static struct bdinfo 97{ 98 int bd_unit; /* BIOS unit number */ 99 int bd_flags; 100 int bd_type; /* BIOS 'drive type' (floppy only) */ 101#ifdef PC98 102 int bd_da_unit; /* kernel unit number for da */ 103#endif 104} bdinfo [MAXBDDEV]; 105static int nbdinfo = 0; 106 107static int bd_getgeom(struct open_disk *od); 108static int bd_read(struct open_disk *od, daddr_t dblk, int blks, 109 caddr_t dest); 110static int bd_write(struct open_disk *od, daddr_t dblk, int blks, 111 caddr_t dest); 112 113static int bd_int13probe(struct bdinfo *bd); 114 115static void bd_printslice(struct open_disk *od, struct pc98_partition *dp, 116 char *prefix, int verbose); 117static void bd_printbsdslice(struct open_disk *od, daddr_t offset, 118 char *prefix, int verbose); 119 120static int bd_init(void); 121static int bd_strategy(void *devdata, int flag, daddr_t dblk, 122 size_t size, char *buf, size_t *rsize); 123static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, 124 size_t size, char *buf, size_t *rsize); 125static int bd_open(struct open_file *f, ...); 126static int bd_close(struct open_file *f); 127static void bd_print(int verbose); 128 129struct devsw biosdisk = { 130 "disk", 131 DEVT_DISK, 132 bd_init, 133 bd_strategy, 134 bd_open, 135 bd_close, 136 noioctl, 137 bd_print, 138 NULL 139}; 140 141static int bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev); 142static void bd_closedisk(struct open_disk *od); 143static int bd_bestslice(struct open_disk *od); 144static void bd_checkextended(struct open_disk *od, int slicenum); 145 146/* 147 * Translate between BIOS device numbers and our private unit numbers. 148 */ 149int 150bd_bios2unit(int biosdev) 151{ 152 int i; 153 154 DEBUG("looking for bios device 0x%x", biosdev); 155 for (i = 0; i < nbdinfo; i++) { 156 DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit); 157 if (bdinfo[i].bd_unit == biosdev) 158 return(i); 159 } 160 return(-1); 161} 162 163int 164bd_unit2bios(int unit) 165{ 166 if ((unit >= 0) && (unit < nbdinfo)) 167 return(bdinfo[unit].bd_unit); 168 return(-1); 169} 170 171/* 172 * Quiz the BIOS for disk devices, save a little info about them. 173 */ 174static int 175bd_init(void) 176{ 177#ifdef PC98 178 int base, unit; 179 int da_drive=0, n=-0x10; 180 181 /* sequence 0x90, 0x80, 0xa0 */ 182 for (base = 0x90; base <= 0xa0; base += n, n += 0x30) { 183 for (unit = base; (nbdinfo < MAXBDDEV) || ((unit & 0x0f) < 4); unit++) { 184 bdinfo[nbdinfo].bd_unit = unit; 185 bdinfo[nbdinfo].bd_flags = (unit & 0xf0) == 0x90 ? BD_FLOPPY : 0; 186 187 if (!bd_int13probe(&bdinfo[nbdinfo])){ 188 if (((unit & 0xf0) == 0x90 && (unit & 0x0f) < 4) || 189 ((unit & 0xf0) == 0xa0 && (unit & 0x0f) < 6)) 190 continue; /* Target IDs are not contiguous. */ 191 else 192 break; 193 } 194 195 if (bdinfo[nbdinfo].bd_flags & BD_FLOPPY){ 196 /* available 1.44MB access? */ 197 if (*(u_char *)PTOV(0xA15AE) & (1<<(unit & 0xf))) { 198 /* boot media 1.2MB FD? */ 199 if ((*(u_char *)PTOV(0xA1584) & 0xf0) != 0x90) 200 bdinfo[nbdinfo].bd_unit = 0x30 + (unit & 0xf); 201 } 202 } 203 else { 204 if ((unit & 0xF0) == 0xA0) /* SCSI HD or MO */ 205 bdinfo[nbdinfo].bd_da_unit = da_drive++; 206 } 207 /* XXX we need "disk aliases" to make this simpler */ 208 printf("BIOS drive %c: is disk%d\n", 209 'A' + nbdinfo, nbdinfo); 210 nbdinfo++; 211 } 212 } 213#else 214 int base, unit, nfd = 0; 215 216 /* sequence 0, 0x80 */ 217 for (base = 0; base <= 0x80; base += 0x80) { 218 for (unit = base; (nbdinfo < MAXBDDEV); unit++) { 219 /* check the BIOS equipment list for number of fixed disks */ 220 if((base == 0x80) && 221 (nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES))) 222 break; 223 224 bdinfo[nbdinfo].bd_unit = unit; 225 bdinfo[nbdinfo].bd_flags = (unit < 0x80) ? BD_FLOPPY : 0; 226 227 if (!bd_int13probe(&bdinfo[nbdinfo])) 228 break; 229 230 /* XXX we need "disk aliases" to make this simpler */ 231 printf("BIOS drive %c: is disk%d\n", 232 (unit < 0x80) ? ('A' + unit) : ('C' + unit - 0x80), nbdinfo); 233 nbdinfo++; 234 if (base == 0x80) 235 nfd++; 236 } 237 } 238#endif 239 return(0); 240} 241 242/* 243 * Try to detect a device supported by the legacy int13 BIOS 244 */ 245static int 246bd_int13probe(struct bdinfo *bd) 247{ 248#ifdef PC98 249 int addr; 250 251 if (bd->bd_flags & BD_FLOPPY) { 252 addr = 0xa155c; 253 } else { 254 if ((bd->bd_unit & 0xf0) == 0x80) 255 addr = 0xa155d; 256 else 257 addr = 0xa1482; 258 } 259 if ( *(u_char *)PTOV(addr) & (1<<(bd->bd_unit & 0x0f))) { 260 bd->bd_flags |= BD_MODEINT13; 261 return(1); 262 } 263 if ((bd->bd_unit & 0xF0) == 0xA0) { 264 int media = ((unsigned *)PTOV(0xA1460))[bd->bd_unit & 0x0F] & 0x1F; 265 266 if (media == 7) { /* MO */ 267 bd->bd_flags |= BD_MODEINT13 | BD_OPTICAL; 268 return(1); 269 } 270 } 271 return(0); 272#else 273 v86.ctl = V86_FLAGS; 274 v86.addr = 0x13; 275 v86.eax = 0x800; 276 v86.edx = bd->bd_unit; 277 v86int(); 278 279 if (!(v86.efl & 0x1) && /* carry clear */ 280 ((v86.edx & 0xff) > ((unsigned)bd->bd_unit & 0x7f))) { /* unit # OK */ 281 bd->bd_flags |= BD_MODEINT13; 282 bd->bd_type = v86.ebx & 0xff; 283 284 /* Determine if we can use EDD with this device. */ 285 v86.eax = 0x4100; 286 v86.edx = bd->bd_unit; 287 v86.ebx = 0x55aa; 288 v86int(); 289 if (!(v86.efl & 0x1) && /* carry clear */ 290 ((v86.ebx & 0xffff) == 0xaa55) && /* signature */ 291 (v86.ecx & 0x1)) { /* packets mode ok */ 292 bd->bd_flags |= BD_MODEEDD1; 293 if((v86.eax & 0xff00) > 0x300) 294 bd->bd_flags |= BD_MODEEDD3; 295 } 296 return(1); 297 } 298 return(0); 299#endif 300} 301 302/* 303 * Print information about disks 304 */ 305static void 306bd_print(int verbose) 307{ 308 int i, j; 309 char line[80]; 310 struct i386_devdesc dev; 311 struct open_disk *od; 312 struct pc98_partition *dptr; 313 314 for (i = 0; i < nbdinfo; i++) { 315#ifdef PC98 316 sprintf(line, " disk%d: BIOS drive %c:\n", i, 'A' + i); 317#else 318 sprintf(line, " disk%d: BIOS drive %c:\n", i, 319 (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit) : ('C' + bdinfo[i].bd_unit - 0x80)); 320#endif 321 pager_output(line); 322 323 /* try to open the whole disk */ 324 dev.d_kind.biosdisk.unit = i; 325 dev.d_kind.biosdisk.slice = -1; 326 dev.d_kind.biosdisk.partition = -1; 327 328 if (!bd_opendisk(&od, &dev)) { 329 330 /* Do we have a partition table? */ 331 if (od->od_flags & BD_PARTTABOK) { 332 dptr = &od->od_slicetab[0]; 333 334 /* Check for a "dedicated" disk */ 335#ifdef PC98 336 for (j = 0; j < od->od_nslices; j++) { 337 switch(dptr[j].dp_mid) { 338 case DOSMID_386BSD: 339 sprintf(line, " disk%ds%d", i, j + 1); 340 bd_printbsdslice(od, 341 dptr[j].dp_scyl * od->od_hds * od->od_sec + 342 dptr[j].dp_shd * od->od_sec + dptr[j].dp_ssect, 343 line, verbose); 344 break; 345 default: 346 break; 347 } 348 } 349#else 350 if ((dptr[3].dp_typ == DOSPTYP_386BSD) && 351 (dptr[3].dp_start == 0) && 352 (dptr[3].dp_size == 50000)) { 353 sprintf(line, " disk%d", i); 354 bd_printbsdslice(od, 0, line, verbose); 355 } else { 356 for (j = 0; j < od->od_nslices; j++) { 357 sprintf(line, " disk%ds%d", i, j + 1); 358 bd_printslice(od, &dptr[j], line, verbose); 359 } 360 } 361#endif 362 } 363 bd_closedisk(od); 364 } 365 } 366} 367 368#ifndef PC98 369/* 370 * Print information about slices on a disk. For the size calculations we 371 * assume a 512 byte sector. 372 */ 373static void 374bd_printslice(struct open_disk *od, struct dos_partition *dp, char *prefix, 375 int verbose) 376{ 377 char line[80]; 378 379 switch (dp->dp_typ) { 380 case DOSPTYP_386BSD: 381 bd_printbsdslice(od, (daddr_t)dp->dp_start, prefix, verbose); 382 return; 383 case DOSPTYP_LINSWP: 384 if (verbose) 385 sprintf(line, "%s: Linux swap %.6dMB (%d - %d)\n", 386 prefix, dp->dp_size / 2048, 387 dp->dp_start, dp->dp_start + dp->dp_size); 388 else 389 sprintf(line, "%s: Linux swap\n", prefix); 390 break; 391 case DOSPTYP_LINUX: 392 /* 393 * XXX 394 * read the superblock to confirm this is an ext2fs partition? 395 */ 396 if (verbose) 397 sprintf(line, "%s: ext2fs %.6dMB (%d - %d)\n", prefix, 398 dp->dp_size / 2048, dp->dp_start, 399 dp->dp_start + dp->dp_size); 400 else 401 sprintf(line, "%s: ext2fs\n", prefix); 402 break; 403 case 0x00: /* unused partition */ 404 case DOSPTYP_EXT: 405 return; 406 case 0x01: 407 if (verbose) 408 sprintf(line, "%s: FAT-12 %.6dMB (%d - %d)\n", prefix, 409 dp->dp_size / 2048, dp->dp_start, 410 dp->dp_start + dp->dp_size); 411 else 412 sprintf(line, "%s: FAT-12\n", prefix); 413 break; 414 case 0x04: 415 case 0x06: 416 case 0x0e: 417 if (verbose) 418 sprintf(line, "%s: FAT-16 %.6dMB (%d - %d)\n", prefix, 419 dp->dp_size / 2048, dp->dp_start, 420 dp->dp_start + dp->dp_size); 421 else 422 sprintf(line, "%s: FAT-16\n", prefix); 423 break; 424 case 0x0b: 425 case 0x0c: 426 if (verbose) 427 sprintf(line, "%s: FAT-32 %.6dMB (%d - %d)\n", prefix, 428 dp->dp_size / 2048, dp->dp_start, 429 dp->dp_start + dp->dp_size); 430 else 431 sprintf(line, "%s: FAT-32\n", prefix); 432 break; 433 default: 434 if (verbose) 435 sprintf(line, "%s: Unknown fs: 0x%x %.6dMB (%d - %d)\n", 436 prefix, dp->dp_typ, dp->dp_size / 2048, 437 dp->dp_start, dp->dp_start + dp->dp_size); 438 else 439 sprintf(line, "%s: Unknown fs: 0x%x\n", prefix, 440 dp->dp_typ); 441 } 442 pager_output(line); 443} 444#endif 445 446/* 447 * Print out each valid partition in the disklabel of a FreeBSD slice. 448 * For size calculations, we assume a 512 byte sector size. 449 */ 450static void 451bd_printbsdslice(struct open_disk *od, daddr_t offset, char *prefix, 452 int verbose) 453{ 454 char line[80]; 455 char buf[BIOSDISK_SECSIZE]; 456 struct disklabel *lp; 457 int i; 458 459 /* read disklabel */ 460 if (bd_read(od, offset + LABELSECTOR, 1, buf)) 461 return; 462 lp =(struct disklabel *)(&buf[0]); 463 if (lp->d_magic != DISKMAGIC) { 464 sprintf(line, "%s: FFS bad disklabel\n", prefix); 465 pager_output(line); 466 return; 467 } 468 469 /* Print partitions */ 470 for (i = 0; i < lp->d_npartitions; i++) { 471 /* 472 * For each partition, make sure we know what type of fs it is. If 473 * not, then skip it. However, since floppies often have bogus 474 * fstypes, print the 'a' partition on a floppy even if it is marked 475 * unused. 476 */ 477 if ((lp->d_partitions[i].p_fstype == FS_BSDFFS) || 478 (lp->d_partitions[i].p_fstype == FS_SWAP) || 479 (lp->d_partitions[i].p_fstype == FS_VINUM) || 480 ((lp->d_partitions[i].p_fstype == FS_UNUSED) && 481 (od->od_flags & BD_FLOPPY) && (i == 0))) { 482 483 /* Only print out statistics in verbose mode */ 484 if (verbose) 485 sprintf(line, " %s%c: %s %.6dMB (%d - %d)\n", prefix, 'a' + i, 486 (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : 487 (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" : 488 "FFS", 489 lp->d_partitions[i].p_size / 2048, 490 lp->d_partitions[i].p_offset, 491 lp->d_partitions[i].p_offset + lp->d_partitions[i].p_size); 492 else 493 sprintf(line, " %s%c: %s\n", prefix, 'a' + i, 494 (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : 495 (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" : 496 "FFS"); 497 pager_output(line); 498 } 499 } 500} 501 502 503/* 504 * Attempt to open the disk described by (dev) for use by (f). 505 * 506 * Note that the philosophy here is "give them exactly what 507 * they ask for". This is necessary because being too "smart" 508 * about what the user might want leads to complications. 509 * (eg. given no slice or partition value, with a disk that is 510 * sliced - are they after the first BSD slice, or the DOS 511 * slice before it?) 512 */ 513static int 514bd_open(struct open_file *f, ...) 515{ 516 va_list ap; 517 struct i386_devdesc *dev; 518 struct open_disk *od; 519 int error; 520 521 va_start(ap, f); 522 dev = va_arg(ap, struct i386_devdesc *); 523 va_end(ap); 524 if ((error = bd_opendisk(&od, dev))) 525 return(error); 526 527 /* 528 * Save our context 529 */ 530 ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od; 531 DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff); 532 return(0); 533} 534 535static int 536bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev) 537{ 538 struct pc98_partition *dptr; 539 struct disklabel *lp; 540 struct open_disk *od; 541 int sector, slice, i; 542 int error; 543 char buf[BUFSIZE]; 544 545 if (dev->d_kind.biosdisk.unit >= nbdinfo) { 546 DEBUG("attempt to open nonexistent disk"); 547 return(ENXIO); 548 } 549 550 od = (struct open_disk *)malloc(sizeof(struct open_disk)); 551 if (!od) { 552 DEBUG("no memory"); 553 return (ENOMEM); 554 } 555 556 /* Look up BIOS unit number, intialise open_disk structure */ 557 od->od_dkunit = dev->d_kind.biosdisk.unit; 558 od->od_unit = bdinfo[od->od_dkunit].bd_unit; 559 od->od_flags = bdinfo[od->od_dkunit].bd_flags; 560 od->od_boff = 0; 561 od->od_nslices = 0; 562 error = 0; 563 DEBUG("open '%s', unit 0x%x slice %d partition %c", 564 i386_fmtdev(dev), dev->d_kind.biosdisk.unit, 565 dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition + 'a'); 566 567 /* Get geometry for this open (removable device may have changed) */ 568 if (bd_getgeom(od)) { 569 DEBUG("can't get geometry"); 570 error = ENXIO; 571 goto out; 572 } 573 574 /* 575 * Following calculations attempt to determine the correct value 576 * for d->od_boff by looking for the slice and partition specified, 577 * or searching for reasonable defaults. 578 */ 579 580 /* 581 * Find the slice in the DOS slice table. 582 */ 583#ifdef PC98 584 if (od->od_flags & BD_FLOPPY) { 585 sector = 0; 586 goto unsliced; 587 } 588#endif 589 if (bd_read(od, 0, 1, buf)) { 590 DEBUG("error reading MBR"); 591 error = EIO; 592 goto out; 593 } 594 595 /* 596 * Check the slice table magic. 597 */ 598 if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { 599 /* If a slice number was explicitly supplied, this is an error */ 600 if (dev->d_kind.biosdisk.slice > 0) { 601 DEBUG("no slice table/MBR (no magic)"); 602 error = ENOENT; 603 goto out; 604 } 605 sector = 0; 606 goto unsliced; /* may be a floppy */ 607 } 608#ifdef PC98 609 if (bd_read(od, 1, 1, buf)) { 610 DEBUG("error reading MBR"); 611 error = EIO; 612 goto out; 613 } 614#endif 615 616 /* 617 * copy the partition table, then pick up any extended partitions. 618 */ 619 bcopy(buf + DOSPARTOFF, &od->od_slicetab, 620 sizeof(struct pc98_partition) * NDOSPART); 621#ifdef PC98 622 od->od_nslices = NDOSPART; /* extended slices start here */ 623#else 624 od->od_nslices = 4; /* extended slices start here */ 625 for (i = 0; i < NDOSPART; i++) 626 bd_checkextended(od, i); 627#endif 628 od->od_flags |= BD_PARTTABOK; 629 dptr = &od->od_slicetab[0]; 630 631 /* Is this a request for the whole disk? */ 632 if (dev->d_kind.biosdisk.slice == -1) { 633 sector = 0; 634 goto unsliced; 635 } 636 637 /* 638 * if a slice number was supplied but not found, this is an error. 639 */ 640 if (dev->d_kind.biosdisk.slice > 0) { 641 slice = dev->d_kind.biosdisk.slice - 1; 642 if (slice >= od->od_nslices) { 643 DEBUG("slice %d not found", slice); 644 error = ENOENT; 645 goto out; 646 } 647 } 648 649#ifndef PC98 650 /* 651 * Check for the historically bogus MBR found on true dedicated disks 652 */ 653 if ((dptr[3].dp_typ == DOSPTYP_386BSD) && 654 (dptr[3].dp_start == 0) && 655 (dptr[3].dp_size == 50000)) { 656 sector = 0; 657 goto unsliced; 658 } 659#endif 660 661 /* Try to auto-detect the best slice; this should always give a slice number */ 662 if (dev->d_kind.biosdisk.slice == 0) { 663 slice = bd_bestslice(od); 664 if (slice == -1) { 665 error = ENOENT; 666 goto out; 667 } 668 dev->d_kind.biosdisk.slice = slice; 669 } 670 671 dptr = &od->od_slicetab[0]; 672 /* 673 * Accept the supplied slice number unequivocally (we may be looking 674 * at a DOS partition). 675 */ 676 dptr += (dev->d_kind.biosdisk.slice - 1); /* we number 1-4, offsets are 0-3 */ 677#ifdef PC98 678 sector = dptr->dp_scyl * od->od_hds * od->od_sec + 679 dptr->dp_shd * od->od_sec + dptr->dp_ssect; 680 { 681 int end = dptr->dp_ecyl * od->od_hds * od->od_sec + 682 dptr->dp_ehd * od->od_sec + dptr->dp_esect; 683 DEBUG("slice entry %d at %d, %d sectors", 684 dev->d_kind.biosdisk.slice - 1, sector, end-sector); 685 } 686#else 687 sector = dptr->dp_start; 688 DEBUG("slice entry %d at %d, %d sectors", dev->d_kind.biosdisk.slice - 1, sector, dptr->dp_size); 689#endif 690 691 /* 692 * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition 693 */ 694#ifdef PC98 695 if ((dptr->dp_mid == DOSMID_386BSD) && (dev->d_kind.biosdisk.partition < 0)) 696#else 697 if ((dptr->dp_typ == DOSPTYP_386BSD) && (dev->d_kind.biosdisk.partition < 0)) 698#endif 699 dev->d_kind.biosdisk.partition = 0; 700 701 unsliced: 702 /* 703 * Now we have the slice offset, look for the partition in the disklabel if we have 704 * a partition to start with. 705 * 706 * XXX we might want to check the label checksum. 707 */ 708 if (dev->d_kind.biosdisk.partition < 0) { 709 od->od_boff = sector; /* no partition, must be after the slice */ 710 DEBUG("opening raw slice"); 711 } else { 712 713 if (bd_read(od, sector + LABELSECTOR, 1, buf)) { 714 DEBUG("error reading disklabel"); 715 error = EIO; 716 goto out; 717 } 718 DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel); 719 bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel)); 720 lp = &od->od_disklabel; 721 od->od_flags |= BD_LABELOK; 722 723 if (lp->d_magic != DISKMAGIC) { 724 DEBUG("no disklabel"); 725 error = ENOENT; 726 goto out; 727 } 728 if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) { 729 DEBUG("partition '%c' exceeds partitions in table (a-'%c')", 730 'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions); 731 error = EPART; 732 goto out; 733 734 } 735 736#ifdef DISK_DEBUG 737 /* Complain if the partition is unused unless this is a floppy. */ 738 if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) && 739 !(od->od_flags & BD_FLOPPY)) 740 DEBUG("warning, partition marked as unused"); 741#endif 742 743 od->od_boff = lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset; 744 } 745 746 out: 747 if (error) { 748 free(od); 749 } else { 750 *odp = od; /* return the open disk */ 751 } 752 return(error); 753} 754 755#ifndef PC98 756static void 757bd_checkextended(struct open_disk *od, int slicenum) 758{ 759 char buf[BIOSDISK_SECSIZE]; 760 struct dos_partition *dp; 761 u_int base; 762 int i, start, end; 763 764 dp = &od->od_slicetab[slicenum]; 765 start = od->od_nslices; 766 767 if (dp->dp_size == 0) 768 goto done; 769 if (dp->dp_typ != DOSPTYP_EXT) 770 goto done; 771 if (bd_read(od, (daddr_t)dp->dp_start, 1, buf)) 772 goto done; 773 if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { 774 DEBUG("no magic in extended table"); 775 goto done; 776 } 777 base = dp->dp_start; 778 dp = (struct dos_partition *)(&buf[DOSPARTOFF]); 779 for (i = 0; i < NDOSPART; i++, dp++) { 780 if (dp->dp_size == 0) 781 continue; 782 if (od->od_nslices == NDOSPART) 783 goto done; 784 dp->dp_start += base; 785 bcopy(dp, &od->od_slicetab[od->od_nslices], sizeof(*dp)); 786 od->od_nslices++; 787 } 788 end = od->od_nslices; 789 790 /* 791 * now, recursively check the slices we just added 792 */ 793 for (i = start; i < end; i++) 794 bd_checkextended(od, i); 795done: 796 return; 797} 798#endif 799 800/* 801 * Search for a slice with the following preferences: 802 * 803 * 1: Active FreeBSD slice 804 * 2: Non-active FreeBSD slice 805 * 3: Active Linux slice 806 * 4: non-active Linux slice 807 * 5: Active FAT/FAT32 slice 808 * 6: non-active FAT/FAT32 slice 809 */ 810#define PREF_RAWDISK 0 811#define PREF_FBSD_ACT 1 812#define PREF_FBSD 2 813#define PREF_LINUX_ACT 3 814#define PREF_LINUX 4 815#define PREF_DOS_ACT 5 816#define PREF_DOS 6 817#define PREF_NONE 7 818 819/* 820 * slicelimit is in the range 0 .. NDOSPART 821 */ 822static int 823bd_bestslice(struct open_disk *od) 824{ 825 struct pc98_partition *dp; 826 int pref, preflevel; 827 int i, prefslice; 828 829 prefslice = 0; 830 preflevel = PREF_NONE; 831 832 dp = &od->od_slicetab[0]; 833 for (i = 0; i < od->od_nslices; i++, dp++) { 834 835#ifdef PC98 836 switch(dp->dp_mid & 0x7f) { 837 case DOSMID_386BSD & 0x7f: /* FreeBSD */ 838 if ((dp->dp_mid & 0x80) && 839 (preflevel > PREF_FBSD_ACT)) { 840 pref = i; 841 preflevel = PREF_FBSD_ACT; 842 } else if (preflevel > PREF_FBSD) { 843 pref = i; 844 preflevel = PREF_FBSD; 845 } 846 break; 847 848 case 0x11: /* DOS/Windows */ 849 case 0x20: 850 case 0x21: 851 case 0x22: 852 case 0x23: 853 case 0x63: 854 if ((dp->dp_mid & 0x80) && 855 (preflevel > PREF_DOS_ACT)) { 856 pref = i; 857 preflevel = PREF_DOS_ACT; 858 } else if (preflevel > PREF_DOS) { 859 pref = i; 860 preflevel = PREF_DOS; 861 } 862 break; 863 } 864#else 865 switch (dp->dp_typ) { 866 case DOSPTYP_386BSD: /* FreeBSD */ 867 pref = dp->dp_flag & 0x80 ? PREF_FBSD_ACT : PREF_FBSD; 868 break; 869 870 case DOSPTYP_LINUX: 871 pref = dp->dp_flag & 0x80 ? PREF_LINUX_ACT : PREF_LINUX; 872 break; 873 874 case 0x01: /* DOS/Windows */ 875 case 0x04: 876 case 0x06: 877 case 0x0b: 878 case 0x0c: 879 case 0x0e: 880 pref = dp->dp_flag & 0x80 ? PREF_DOS_ACT : PREF_DOS; 881 break; 882 883 default: 884 pref = PREF_NONE; 885 } 886 if (pref < preflevel) { 887 preflevel = pref; 888 prefslice = i + 1; 889 } 890#endif 891 } 892 return (prefslice); 893} 894 895static int 896bd_close(struct open_file *f) 897{ 898 struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data); 899 900 bd_closedisk(od); 901 return(0); 902} 903 904static void 905bd_closedisk(struct open_disk *od) 906{ 907 DEBUG("open_disk %p", od); 908#if 0 909 /* XXX is this required? (especially if disk already open...) */ 910 if (od->od_flags & BD_FLOPPY) 911 delay(3000000); 912#endif 913 free(od); 914} 915 916static int 917bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) 918{ 919 struct bcache_devdata bcd; 920 struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data); 921 922 bcd.dv_strategy = bd_realstrategy; 923 bcd.dv_devdata = devdata; 924 return(bcache_strategy(&bcd, od->od_unit, rw, dblk+od->od_boff, size, buf, rsize)); 925} 926 927static int 928bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) 929{ 930 struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data); 931 int blks; 932#ifdef BD_SUPPORT_FRAGS 933 char fragbuf[BIOSDISK_SECSIZE]; 934 size_t fragsize; 935 936 fragsize = size % BIOSDISK_SECSIZE; 937#else 938 if (size % BIOSDISK_SECSIZE) 939 panic("bd_strategy: %d bytes I/O not multiple of block size", size); 940#endif 941 942 DEBUG("open_disk %p", od); 943 944 945 switch(rw){ 946 case F_READ: 947 blks = size / BIOSDISK_SECSIZE; 948 DEBUG("read %d from %d to %p", blks, dblk, buf); 949 950 if (rsize) 951 *rsize = 0; 952 if (blks && bd_read(od, dblk, blks, buf)) { 953 DEBUG("read error"); 954 return (EIO); 955 } 956#ifdef BD_SUPPORT_FRAGS 957 DEBUG("bd_strategy: frag read %d from %d+%d to %p", 958 fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE)); 959 if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) { 960 DEBUG("frag read error"); 961 return(EIO); 962 } 963 bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize); 964#endif 965 if (rsize) 966 *rsize = size; 967 return (0); 968 break; 969 970 case F_WRITE : 971 blks = size / BIOSDISK_SECSIZE; 972 DEBUG("write %d from %d to %p", blks, dblk, buf); 973 974 if (rsize) 975 *rsize = 0; 976 if (blks && bd_write(od, dblk, blks, buf)) { 977 DEBUG("write error"); 978 return (EIO); 979 } 980#ifdef BD_SUPPORT_FRAGS 981 if(fragsize) { 982 DEBUG("Attempted to write a frag"); 983 return (EIO); 984 } 985#endif 986 987 if (rsize) 988 *rsize = size; 989 return (0); 990 default: 991 /* DO NOTHING */ 992 beak; 993 } 994 995 return EROFS; 996} 997 998/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */ 999#define FLOPPY_BOUNCEBUF 18 1000 1001static int 1002bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) 1003{ 1004 u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer; 1005 caddr_t p, xp, bbuf, breg; 1006 1007 /* Just in case some idiot actually tries to read -1 blocks... */ 1008 if (blks < 0) 1009 return (-1); 1010 1011 bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ 1012 resid = blks; 1013 p = dest; 1014 1015 /* Decide whether we have to bounce */ 1016#ifdef PC98 1017 if ( 1018#else 1019 if ((od->od_unit < 0x80) && 1020#endif 1021 ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) { 1022 1023 /* 1024 * There is a 64k physical boundary somewhere in the destination buffer, so we have 1025 * to arrange a suitable bounce buffer. Allocate a buffer twice as large as we 1026 * need to. Use the bottom half unless there is a break there, in which case we 1027 * use the top half. 1028 */ 1029#ifdef PC98 1030 x = min(od->od_sec, (unsigned)blks); 1031#else 1032 x = min(FLOPPY_BOUNCEBUF, (unsigned)blks); 1033#endif 1034 bbuf = malloc(x * 2 * BIOSDISK_SECSIZE); 1035 if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(dest + x * BIOSDISK_SECSIZE) & 0xffff0000)) { 1036 breg = bbuf; 1037 } else { 1038 breg = bbuf + x * BIOSDISK_SECSIZE; 1039 } 1040 maxfer = x; /* limit transfers to bounce region size */ 1041 } else { 1042 breg = bbuf = NULL; 1043 maxfer = 0; 1044 } 1045 1046 while (resid > 0) { 1047 x = dblk; 1048 cyl = x / bpc; /* block # / blocks per cylinder */ 1049 x %= bpc; /* block offset into cylinder */ 1050 hd = x / od->od_sec; /* offset / blocks per track */ 1051 sec = x % od->od_sec; /* offset into track */ 1052 1053 /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */ 1054 x = min(od->od_sec - sec, resid); 1055 if (maxfer > 0) 1056 x = min(x, maxfer); /* fit bounce buffer */ 1057 1058 /* where do we transfer to? */ 1059 xp = bbuf == NULL ? p : breg; 1060 1061 /* correct sector number for 1-based BIOS numbering */ 1062#ifdef PC98 1063 if ((od->od_unit & 0xf0) == 0x30 || (od->od_unit & 0xf0) == 0x90) 1064 sec++; 1065#else 1066 sec++; 1067#endif 1068 1069 /* Loop retrying the operation a couple of times. The BIOS may also retry. */ 1070 for (retry = 0; retry < 3; retry++) { 1071 /* if retrying, reset the drive */ 1072 if (retry > 0) { 1073#ifdef PC98 1074 v86.ctl = V86_FLAGS; 1075 v86.addr = 0x1b; 1076 v86.eax = 0x0300 | od->od_unit; 1077#else 1078 v86.ctl = V86_FLAGS; 1079 v86.addr = 0x13; 1080 v86.eax = 0; 1081 v86.edx = od->od_unit; 1082#endif 1083 v86int(); 1084 } 1085 1086#ifdef PC98 1087 v86.ctl = V86_FLAGS; 1088 v86.addr = 0x1b; 1089 if (od->od_flags & BD_FLOPPY) { 1090 v86.eax = 0xd600 | od->od_unit; 1091 v86.ecx = 0x0200 | (cyl & 0xff); 1092 } 1093 else { 1094 v86.eax = 0x0600 | od->od_unit; 1095 v86.ecx = cyl; 1096 } 1097 if (od->od_flags & BD_OPTICAL) { 1098 v86.eax &= 0xFF7F; 1099 v86.ecx = dblk & 0xFFFF; 1100 v86.edx = dblk >> 16; 1101 } else { 1102 v86.edx = (hd << 8) | sec; 1103 } 1104 v86.ebx = x * BIOSDISK_SECSIZE; 1105 v86.es = VTOPSEG(xp); 1106 v86.ebp = VTOPOFF(xp); 1107 v86int(); 1108 result = (v86.efl & 0x1); 1109 if (result == 0) 1110 break; 1111#else 1112 if(cyl > 1023) { 1113 /* use EDD if the disk supports it, otherwise, return error */ 1114 if(od->od_flags & BD_MODEEDD1) { 1115 static unsigned short packet[8]; 1116 1117 packet[0] = 0x10; 1118 packet[1] = x; 1119 packet[2] = VTOPOFF(xp); 1120 packet[3] = VTOPSEG(xp); 1121 packet[4] = dblk & 0xffff; 1122 packet[5] = dblk >> 16; 1123 packet[6] = 0; 1124 packet[7] = 0; 1125 v86.ctl = V86_FLAGS; 1126 v86.addr = 0x13; 1127 v86.eax = 0x4200; 1128 v86.edx = od->od_unit; 1129 v86.ds = VTOPSEG(packet); 1130 v86.esi = VTOPOFF(packet); 1131 v86int(); 1132 result = (v86.efl & 0x1); 1133 if(result == 0) 1134 break; 1135 } else { 1136 result = 1; 1137 break; 1138 } 1139 } else { 1140 /* Use normal CHS addressing */ 1141 v86.ctl = V86_FLAGS; 1142 v86.addr = 0x13; 1143 v86.eax = 0x200 | x; 1144 v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; 1145 v86.edx = (hd << 8) | od->od_unit; 1146 v86.es = VTOPSEG(xp); 1147 v86.ebx = VTOPOFF(xp); 1148 v86int(); 1149 result = (v86.efl & 0x1); 1150 if (result == 0) 1151 break; 1152 } 1153#endif 1154 } 1155 1156#ifdef PC98 1157 DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, od->od_flags & BD_FLOPPY ? sec - 1 : sec, p, VTOP(p), result ? "failed" : "ok"); 1158 /* BUG here, cannot use v86 in printf because putchar uses it too */ 1159 DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 1160 od->od_flags & BD_FLOPPY ? 0xd600 | od->od_unit : 0x0600 | od->od_unit, 1161 od->od_flags & BD_FLOPPY ? 0x0200 | cyl : cyl, (hd << 8) | sec, 1162 (v86.eax >> 8) & 0xff); 1163#else 1164 DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok"); 1165 /* BUG here, cannot use v86 in printf because putchar uses it too */ 1166 DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 1167 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff); 1168#endif 1169 if (result) { 1170 if (bbuf != NULL) 1171 free(bbuf); 1172 return(-1); 1173 } 1174 if (bbuf != NULL) 1175 bcopy(breg, p, x * BIOSDISK_SECSIZE); 1176 p += (x * BIOSDISK_SECSIZE); 1177 dblk += x; 1178 resid -= x; 1179 } 1180 1181/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */ 1182 if (bbuf != NULL) 1183 free(bbuf); 1184 return(0); 1185} 1186 1187 1188static int 1189bd_write(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) 1190{ 1191 u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer; 1192 caddr_t p, xp, bbuf, breg; 1193 1194 /* Just in case some idiot actually tries to read -1 blocks... */ 1195 if (blks < 0) 1196 return (-1); 1197 1198 bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ 1199 resid = blks; 1200 p = dest; 1201 1202 /* Decide whether we have to bounce */ 1203#ifdef PC98 1204 if ( 1205#else 1206 if ((od->od_unit < 0x80) && 1207#endif 1208 ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) { 1209 1210 /* 1211 * There is a 64k physical boundary somewhere in the destination buffer, so we have 1212 * to arrange a suitable bounce buffer. Allocate a buffer twice as large as we 1213 * need to. Use the bottom half unless there is a break there, in which case we 1214 * use the top half. 1215 */ 1216 1217#ifdef PC98 1218 x = min(od->od_sec, (unsigned)blks); 1219#else 1220 x = min(FLOPPY_BOUNCEBUF, (unsigned)blks); 1221#endif 1222 bbuf = malloc(x * 2 * BIOSDISK_SECSIZE); 1223 if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(dest + x * BIOSDISK_SECSIZE) & 0xffff0000)) { 1224 breg = bbuf; 1225 } else { 1226 breg = bbuf + x * BIOSDISK_SECSIZE; 1227 } 1228 maxfer = x; /* limit transfers to bounce region size */ 1229 } else { 1230 breg = bbuf = NULL; 1231 maxfer = 0; 1232 } 1233 1234 while (resid > 0) { 1235 x = dblk; 1236 cyl = x / bpc; /* block # / blocks per cylinder */ 1237 x %= bpc; /* block offset into cylinder */ 1238 hd = x / od->od_sec; /* offset / blocks per track */ 1239 sec = x % od->od_sec; /* offset into track */ 1240 1241 /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */ 1242 x = min(od->od_sec - sec, resid); 1243 if (maxfer > 0) 1244 x = min(x, maxfer); /* fit bounce buffer */ 1245 1246 /* where do we transfer to? */ 1247 xp = bbuf == NULL ? p : breg; 1248 1249 /* correct sector number for 1-based BIOS numbering */ 1250#ifdef PC98 1251 if ((od->od_unit & 0xf0) == 0x30 || (od->od_unit & 0xf0) == 0x90) 1252 sec++; 1253#else 1254 sec++; 1255#endif 1256 1257 1258 /* Put your Data In, Put your Data out, 1259 Put your Data In, and shake it all about 1260 */ 1261 if (bbuf != NULL) 1262 bcopy(p, breg, x * BIOSDISK_SECSIZE); 1263 p += (x * BIOSDISK_SECSIZE); 1264 dblk += x; 1265 resid -= x; 1266 1267 /* Loop retrying the operation a couple of times. The BIOS may also retry. */ 1268 for (retry = 0; retry < 3; retry++) { 1269 /* if retrying, reset the drive */ 1270 if (retry > 0) { 1271#ifdef PC98 1272 v86.ctl = V86_FLAGS; 1273 v86.addr = 0x1b; 1274 v86.eax = 0x0300 | od->od_unit; 1275#else 1276 v86.ctl = V86_FLAGS; 1277 v86.addr = 0x13; 1278 v86.eax = 0; 1279 v86.edx = od->od_unit; 1280#endif 1281 v86int(); 1282 } 1283 1284#ifdef PC98 1285 v86.ctl = V86_FLAGS; 1286 v86.addr = 0x1b; 1287 if (od->od_flags & BD_FLOPPY) { 1288 v86.eax = 0xd500 | od->od_unit; 1289 v86.ecx = 0x0200 | (cyl & 0xff); 1290 } else { 1291 v86.eax = 0x0500 | od->od_unit; 1292 v86.ecx = cyl; 1293 } 1294 v86.edx = (hd << 8) | sec; 1295 v86.ebx = x * BIOSDISK_SECSIZE; 1296 v86.es = VTOPSEG(xp); 1297 v86.ebp = VTOPOFF(xp); 1298 v86int(); 1299 result = (v86.efl & 0x1); 1300 if (result == 0) 1301 break; 1302#else 1303 if(cyl > 1023) { 1304 /* use EDD if the disk supports it, otherwise, return error */ 1305 if(od->od_flags & BD_MODEEDD1) { 1306 static unsigned short packet[8]; 1307 1308 packet[0] = 0x10; 1309 packet[1] = x; 1310 packet[2] = VTOPOFF(xp); 1311 packet[3] = VTOPSEG(xp); 1312 packet[4] = dblk & 0xffff; 1313 packet[5] = dblk >> 16; 1314 packet[6] = 0; 1315 packet[7] = 0; 1316 v86.ctl = V86_FLAGS; 1317 v86.addr = 0x13; 1318 /* Should we Write with verify ?? 0x4302 ? */ 1319 v86.eax = 0x4300; 1320 v86.edx = od->od_unit; 1321 v86.ds = VTOPSEG(packet); 1322 v86.esi = VTOPOFF(packet); 1323 v86int(); 1324 result = (v86.efl & 0x1); 1325 if(result == 0) 1326 break; 1327 } else { 1328 result = 1; 1329 break; 1330 } 1331 } else { 1332 /* Use normal CHS addressing */ 1333 v86.ctl = V86_FLAGS; 1334 v86.addr = 0x13; 1335 v86.eax = 0x300 | x; 1336 v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; 1337 v86.edx = (hd << 8) | od->od_unit; 1338 v86.es = VTOPSEG(xp); 1339 v86.ebx = VTOPOFF(xp); 1340 v86int(); 1341 result = (v86.efl & 0x1); 1342 if (result == 0) 1343 break; 1344 } 1345#endif 1346 } 1347 1348#ifdef PC98 1349 DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, 1350 od->od_flags & BD_FLOPPY ? sec - 1 : sec, p, VTOP(p), 1351 result ? "failed" : "ok"); 1352 /* BUG here, cannot use v86 in printf because putchar uses it too */ 1353 DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 1354 od->od_flags & BD_FLOPPY ? 0xd600 | od->od_unit : 0x0600 | od->od_unit, 1355 od->od_flags & BD_FLOPPY ? 0x0200 | cyl : cyl, (hd << 8) | sec, 1356 (v86.eax >> 8) & 0xff); 1357#else 1358 DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok"); 1359 /* BUG here, cannot use v86 in printf because putchar uses it too */ 1360 DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", 1361 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff); 1362#endif 1363 if (result) { 1364 if (bbuf != NULL) 1365 free(bbuf); 1366 return(-1); 1367 } 1368 } 1369 1370/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */ 1371 if (bbuf != NULL) 1372 free(bbuf); 1373 return(0); 1374} 1375static int 1376bd_getgeom(struct open_disk *od) 1377{ 1378 1379#ifdef PC98 1380 if (od->od_flags & BD_FLOPPY) { 1381 od->od_cyl = 79; 1382 od->od_hds = 2; 1383 od->od_sec = (od->od_unit & 0xf0) == 0x30 ? 18 : 15; 1384 } else if (od->od_flags & BD_OPTICAL) { 1385 od->od_cyl = 0xFFFE; 1386 od->od_hds = 8; 1387 od->od_sec = 32; 1388 } else { 1389 v86.ctl = V86_FLAGS; 1390 v86.addr = 0x1b; 1391 v86.eax = 0x8400 | od->od_unit; 1392 v86int(); 1393 1394 od->od_cyl = v86.ecx; 1395 od->od_hds = (v86.edx >> 8) & 0xff; 1396 od->od_sec = v86.edx & 0xff; 1397 if (v86.efl & 0x1) 1398 return(1); 1399 } 1400#else 1401 v86.ctl = V86_FLAGS; 1402 v86.addr = 0x13; 1403 v86.eax = 0x800; 1404 v86.edx = od->od_unit; 1405 v86int(); 1406 1407 if ((v86.efl & 0x1) || /* carry set */ 1408 ((v86.edx & 0xff) <= (unsigned)(od->od_unit & 0x7f))) /* unit # bad */ 1409 return(1); 1410 1411 /* convert max cyl # -> # of cylinders */ 1412 od->od_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1; 1413 /* convert max head # -> # of heads */ 1414 od->od_hds = ((v86.edx & 0xff00) >> 8) + 1; 1415 od->od_sec = v86.ecx & 0x3f; 1416#endif 1417 1418 DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec); 1419 return(0); 1420} 1421 1422/* 1423 * Return the BIOS geometry of a given "fixed drive" in a format 1424 * suitable for the legacy bootinfo structure. Since the kernel is 1425 * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we 1426 * prefer to get the information directly, rather than rely on being 1427 * able to put it together from information already maintained for 1428 * different purposes and for a probably different number of drives. 1429 * 1430 * For valid drives, the geometry is expected in the format (31..0) 1431 * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are 1432 * indicated by returning the geometry of a "1.2M" PC-format floppy 1433 * disk. And, incidentally, what is returned is not the geometry as 1434 * such but the highest valid cylinder, head, and sector numbers. 1435 */ 1436u_int32_t 1437bd_getbigeom(int bunit) 1438{ 1439 1440#ifdef PC98 1441 int hds = 0; 1442 int unit = 0x80; /* IDE HDD */ 1443 u_int addr = 0xA155d; 1444 1445 while (unit < 0xa7) { 1446 if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f))) 1447 if (hds++ == bunit) 1448 break; 1449 1450 if (unit >= 0xA0) { 1451 int media = ((unsigned *)PTOV(0xA1460))[unit & 0x0F] & 0x1F; 1452 1453 if (media == 7 && hds++ == bunit) /* SCSI MO */ 1454 return(0xFFFE0820); /* C:65535 H:8 S:32 */ 1455 } 1456 if (++unit == 0x84) { 1457 unit = 0xA0; /* SCSI HDD */ 1458 addr = 0xA1482; 1459 } 1460 } 1461 if (unit == 0xa7) 1462 return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */ 1463 v86.ctl = V86_FLAGS; 1464 v86.addr = 0x1b; 1465 v86.eax = 0x8400 | unit; 1466 v86int(); 1467 if (v86.efl & 0x1) 1468 return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */ 1469 return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff); 1470#else 1471 v86.ctl = V86_FLAGS; 1472 v86.addr = 0x13; 1473 v86.eax = 0x800; 1474 v86.edx = 0x80 + bunit; 1475 v86int(); 1476 if (v86.efl & 0x1) 1477 return 0x4f010f; 1478 return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) | 1479 (v86.edx & 0xff00) | (v86.ecx & 0x3f); 1480#endif 1481} 1482 1483/* 1484 * Return a suitable dev_t value for (dev). 1485 * 1486 * In the case where it looks like (dev) is a SCSI disk, we allow the number of 1487 * IDE disks to be specified in $num_ide_disks. There should be a Better Way. 1488 */ 1489int 1490bd_getdev(struct i386_devdesc *dev) 1491{ 1492 struct open_disk *od; 1493 int biosdev; 1494 int major; 1495 int rootdev; 1496 char *nip, *cp; 1497 int unitofs = 0, i, unit; 1498 1499 biosdev = bd_unit2bios(dev->d_kind.biosdisk.unit); 1500 DEBUG("unit %d BIOS device %d", dev->d_kind.biosdisk.unit, biosdev); 1501 if (biosdev == -1) /* not a BIOS device */ 1502 return(-1); 1503 if (bd_opendisk(&od, dev) != 0) /* oops, not a viable device */ 1504 return(-1); 1505 1506#ifdef PC98 1507 if ((biosdev & 0xf0) == 0x90 || (biosdev & 0xf0) == 0x30) { 1508#else 1509 if (biosdev < 0x80) { 1510#endif 1511 /* floppy (or emulated floppy) or ATAPI device */ 1512 if (bdinfo[dev->d_kind.biosdisk.unit].bd_type == DT_ATAPI) { 1513 /* is an ATAPI disk */ 1514 major = WFDMAJOR; 1515 } else { 1516 /* is a floppy disk */ 1517 major = FDMAJOR; 1518 } 1519 } else { 1520 /* harddisk */ 1521 if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) { 1522 /* label OK, disk labelled as SCSI */ 1523 major = DAMAJOR; 1524 /* check for unit number correction hint, now deprecated */ 1525 if ((nip = getenv("num_ide_disks")) != NULL) { 1526 i = strtol(nip, &cp, 0); 1527 /* check for parse error */ 1528 if ((cp != nip) && (*cp == 0)) 1529 unitofs = i; 1530 } 1531 } else { 1532 /* assume an IDE disk */ 1533 major = WDMAJOR; 1534 } 1535 } 1536 /* default root disk unit number */ 1537#ifdef PC98 1538 if ((biosdev & 0xf0) == 0xa0) 1539 unit = bdinfo[dev->d_kind.biosdisk.unit].bd_da_unit; 1540 else 1541 unit = biosdev & 0xf; 1542#else 1543 unit = (biosdev & 0x7f) - unitofs; 1544#endif 1545 1546 /* XXX a better kludge to set the root disk unit number */ 1547 if ((nip = getenv("root_disk_unit")) != NULL) { 1548 i = strtol(nip, &cp, 0); 1549 /* check for parse error */ 1550 if ((cp != nip) && (*cp == 0)) 1551 unit = i; 1552 } 1553 1554 rootdev = MAKEBOOTDEV(major, 1555 (dev->d_kind.biosdisk.slice + 1) >> 4, /* XXX slices may be wrong here */ 1556 (dev->d_kind.biosdisk.slice + 1) & 0xf, 1557 unit, 1558 dev->d_kind.biosdisk.partition); 1559 DEBUG("dev is 0x%x\n", rootdev); 1560 return(rootdev); 1561} 1562