1 /* $NetBSD: biosdisk.c,v 1.36 2011/01/05 22:06:59 jakllsch Exp $ */ 2 3 /* 4 * Copyright (c) 1996, 1998 5 * Matthias Drochner. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 27 */ 28 29 /* 30 * raw BIOS disk device for libsa. 31 * needs lowlevel parts from bios_disk.S and biosdisk_ll.c 32 * partly from netbsd:sys/arch/i386/boot/disk.c 33 * no bad144 handling! 34 * 35 * A lot of this must match sys/kern/subr_disk_mbr.c 36 */ 37 38 /* 39 * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 40 * 41 * Mach Operating System 42 * Copyright (c) 1992, 1991 Carnegie Mellon University 43 * All Rights Reserved. 44 * 45 * Permission to use, copy, modify and distribute this software and its 46 * documentation is hereby granted, provided that both the copyright 47 * notice and this permission notice appear in all copies of the 48 * software, derivative works or modified versions, and any portions 49 * thereof, and that both notices appear in supporting documentation. 50 * 51 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 52 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 53 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 54 * 55 * Carnegie Mellon requests users of this software to return to 56 * 57 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 58 * School of Computer Science 59 * Carnegie Mellon University 60 * Pittsburgh PA 15213-3890 61 * 62 * any improvements or extensions that they make and grant Carnegie Mellon 63 * the rights to redistribute these changes. 64 */ 65 66 #if !defined(NO_DISKLABEL) || !defined(NO_GPT) 67 #define FSTYPENAMES 68 #endif 69 70 #include <lib/libkern/libkern.h> 71 #include <lib/libsa/stand.h> 72 73 #include <sys/types.h> 74 #include <sys/md5.h> 75 #include <sys/param.h> 76 #include <sys/disklabel.h> 77 #include <sys/disklabel_gpt.h> 78 #include <sys/uuid.h> 79 80 #include <fs/cd9660/iso.h> 81 82 #include <lib/libsa/saerrno.h> 83 #include <machine/stdarg.h> 84 #include <machine/cpu.h> 85 86 #include "libi386.h" 87 #include "biosdisk_ll.h" 88 #include "biosdisk.h" 89 #ifdef _STANDALONE 90 #include "bootinfo.h" 91 #endif 92 93 #define BUFSIZE 2048 /* must be large enough for a CD sector */ 94 95 #define BIOSDISKNPART 26 96 97 struct biosdisk { 98 struct biosdisk_ll ll; 99 daddr_t boff; 100 char buf[BUFSIZE]; 101 #if !defined(NO_DISKLABEL) || !defined(NO_GPT) 102 struct { 103 daddr_t offset; 104 daddr_t size; 105 int fstype; 106 } part[BIOSDISKNPART]; 107 #endif 108 }; 109 110 #ifndef NO_GPT 111 const struct uuid GET_nbsd_raid = GPT_ENT_TYPE_NETBSD_RAIDFRAME; 112 const struct uuid GET_nbsd_ffs = GPT_ENT_TYPE_NETBSD_FFS; 113 const struct uuid GET_nbsd_lfs = GPT_ENT_TYPE_NETBSD_LFS; 114 const struct uuid GET_nbsd_swap = GPT_ENT_TYPE_NETBSD_SWAP; 115 #endif /* NO_GPT */ 116 117 #ifdef _STANDALONE 118 static struct btinfo_bootdisk bi_disk; 119 static struct btinfo_bootwedge bi_wedge; 120 #endif 121 122 #define RF_PROTECTED_SECTORS 64 /* XXX refer to <.../rf_optnames.h> */ 123 124 int 125 biosdisk_strategy(void *devdata, int flag, daddr_t dblk, size_t size, 126 void *buf, size_t *rsize) 127 { 128 struct biosdisk *d; 129 int blks, frag; 130 131 if (flag != F_READ) 132 return EROFS; 133 134 d = (struct biosdisk *) devdata; 135 136 if (d->ll.type == BIOSDISK_TYPE_CD) 137 dblk = dblk * DEV_BSIZE / ISO_DEFAULT_BLOCK_SIZE; 138 139 dblk += d->boff; 140 141 blks = size / d->ll.secsize; 142 if (blks && readsects(&d->ll, dblk, blks, buf, 0)) { 143 if (rsize) 144 *rsize = 0; 145 return EIO; 146 } 147 148 /* needed for CD */ 149 frag = size % d->ll.secsize; 150 if (frag) { 151 if (readsects(&d->ll, dblk + blks, 1, d->buf, 0)) { 152 if (rsize) 153 *rsize = blks * d->ll.secsize; 154 return EIO; 155 } 156 memcpy(buf + blks * d->ll.secsize, d->buf, frag); 157 } 158 159 if (rsize) 160 *rsize = size; 161 return 0; 162 } 163 164 static struct biosdisk * 165 alloc_biosdisk(int biosdev) 166 { 167 struct biosdisk *d; 168 169 d = alloc(sizeof(*d)); 170 if (d == NULL) 171 return NULL; 172 memset(d, 0, sizeof(*d)); 173 174 d->ll.dev = biosdev; 175 if (set_geometry(&d->ll, NULL)) { 176 #ifdef DISK_DEBUG 177 printf("no geometry information\n"); 178 #endif 179 dealloc(d, sizeof(*d)); 180 return NULL; 181 } 182 return d; 183 } 184 185 #if !defined(NO_DISKLABEL) || !defined(NO_GPT) 186 static void 187 md5(void *hash, const void *data, size_t len) 188 { 189 MD5_CTX ctx; 190 191 MD5Init(&ctx); 192 MD5Update(&ctx, data, len); 193 MD5Final(hash, &ctx); 194 195 return; 196 } 197 #endif 198 199 #ifndef NO_GPT 200 static bool 201 guid_is_nil(const struct uuid *u) 202 { 203 static const struct uuid nil = { .time_low = 0 }; 204 return (memcmp(u, &nil, sizeof(*u)) == 0 ? true : false); 205 } 206 207 static bool 208 guid_is_equal(const struct uuid *a, const struct uuid *b) 209 { 210 return (memcmp(a, b, sizeof(*a)) == 0 ? true : false); 211 } 212 213 static int 214 check_gpt(struct biosdisk *d, daddr_t sector) 215 { 216 struct gpt_hdr gpth; 217 const struct gpt_ent *ep; 218 const struct uuid *u; 219 daddr_t entblk; 220 size_t size; 221 uint32_t crc; 222 int sectors; 223 int entries; 224 int entry; 225 int i, j; 226 227 /* read in gpt_hdr sector */ 228 if (readsects(&d->ll, sector, 1, d->buf, 1)) { 229 #ifdef DISK_DEBUG 230 printf("Error reading GPT header at %"PRId64"\n", sector); 231 #endif 232 return EIO; 233 } 234 235 gpth = *(const struct gpt_hdr *)d->buf; 236 237 if (memcmp(GPT_HDR_SIG, gpth.hdr_sig, sizeof(gpth.hdr_sig))) 238 return -1; 239 240 crc = gpth.hdr_crc_self; 241 gpth.hdr_crc_self = 0; 242 gpth.hdr_crc_self = crc32(0, (const void *)&gpth, GPT_HDR_SIZE); 243 if (gpth.hdr_crc_self != crc) { 244 return -1; 245 } 246 247 if (gpth.hdr_lba_self != sector) 248 return -1; 249 250 #ifdef _STANDALONE 251 bi_wedge.matchblk = sector; 252 bi_wedge.matchnblks = 1; 253 254 md5(bi_wedge.matchhash, d->buf, d->ll.secsize); 255 #endif 256 257 sectors = sizeof(d->buf)/d->ll.secsize; /* sectors per buffer */ 258 entries = sizeof(d->buf)/gpth.hdr_entsz; /* entries per buffer */ 259 entblk = gpth.hdr_lba_table; 260 crc = crc32(0, NULL, 0); 261 262 j = 0; 263 ep = (const struct gpt_ent *)d->buf; 264 265 for (entry = 0; entry < gpth.hdr_entries; entry += entries) { 266 size = MIN(sizeof(d->buf), 267 (gpth.hdr_entries - entry) * gpth.hdr_entsz); 268 entries = size / gpth.hdr_entsz; 269 sectors = roundup(size, d->ll.secsize) / d->ll.secsize; 270 if (readsects(&d->ll, entblk, sectors, d->buf, 1)) 271 return -1; 272 entblk += sectors; 273 crc = crc32(crc, (const void *)d->buf, size); 274 275 for (i = 0; j < BIOSDISKNPART && i < entries; i++, j++) { 276 u = (const struct uuid *)ep[i].ent_type; 277 if (!guid_is_nil(u)) { 278 d->part[j].offset = ep[i].ent_lba_start; 279 d->part[j].size = ep[i].ent_lba_end - 280 ep[i].ent_lba_start + 1; 281 if (guid_is_equal(u, &GET_nbsd_ffs)) 282 d->part[j].fstype = FS_BSDFFS; 283 else if (guid_is_equal(u, &GET_nbsd_lfs)) 284 d->part[j].fstype = FS_BSDLFS; 285 else if (guid_is_equal(u, &GET_nbsd_raid)) 286 d->part[j].fstype = FS_RAID; 287 else if (guid_is_equal(u, &GET_nbsd_swap)) 288 d->part[j].fstype = FS_SWAP; 289 else 290 d->part[j].fstype = FS_OTHER; 291 } 292 } 293 294 } 295 296 if (crc != gpth.hdr_crc_table) { 297 #ifdef DISK_DEBUG 298 printf("GPT table CRC invalid\n"); 299 #endif 300 return -1; 301 } 302 303 return 0; 304 } 305 306 static int 307 read_gpt(struct biosdisk *d) 308 { 309 struct biosdisk_extinfo ed; 310 daddr_t gptsector[2]; 311 int i, error; 312 313 gptsector[0] = GPT_HDR_BLKNO; 314 if (set_geometry(&d->ll, &ed) == 0 && d->ll.flags & BIOSDISK_INT13EXT) { 315 gptsector[1] = ed.totsec - 1; 316 d->ll.secsize = ed.sbytes; 317 } else { 318 #ifdef DISK_DEBUG 319 printf("Unable to determine extended disk geometry - " 320 "using CHS\n"); 321 #endif 322 /* at least try some other reasonable values then */ 323 gptsector[1] = d->ll.chs_sectors - 1; 324 } 325 326 /* 327 * Use any valid GPT available, do not require both GPTs to be valid 328 */ 329 for (i = 0; i < __arraycount(gptsector); i++) { 330 error = check_gpt(d, gptsector[i]); 331 if (error == 0) 332 break; 333 } 334 335 if (i >= __arraycount(gptsector)) { 336 memset(d->part, 0, sizeof(d->part)); 337 return -1; 338 } 339 340 #ifdef DISK_DEBUG 341 printf("using %s GPT\n", (i == 0) ? "primary" : "secondary"); 342 #endif 343 return 0; 344 } 345 #endif /* !NO_GPT */ 346 347 #ifndef NO_DISKLABEL 348 static int 349 check_label(struct biosdisk *d, daddr_t sector) 350 { 351 struct disklabel *lp; 352 int part; 353 354 /* find partition in NetBSD disklabel */ 355 if (readsects(&d->ll, sector + LABELSECTOR, 1, d->buf, 0)) { 356 #ifdef DISK_DEBUG 357 printf("Error reading disklabel\n"); 358 #endif 359 return EIO; 360 } 361 lp = (struct disklabel *) (d->buf + LABELOFFSET); 362 if (lp->d_magic != DISKMAGIC || dkcksum(lp)) { 363 #ifdef DISK_DEBUG 364 printf("warning: no disklabel in sector %"PRId64"\n", sector); 365 #endif 366 return -1; 367 } 368 369 memset(d->part, 0, sizeof(d->part)); 370 for (part = 0; part < lp->d_npartitions; part++) { 371 if (lp->d_partitions[part].p_size == 0) 372 continue; 373 if (lp->d_partitions[part].p_fstype == FS_UNUSED) 374 continue; 375 d->part[part].fstype = lp->d_partitions[part].p_fstype; 376 d->part[part].offset = lp->d_partitions[part].p_offset; 377 d->part[part].size = lp->d_partitions[part].p_size; 378 } 379 380 d->boff = sector; 381 382 #ifdef _STANDALONE 383 bi_disk.labelsector = d->boff + LABELSECTOR; 384 bi_disk.label.type = lp->d_type; 385 memcpy(bi_disk.label.packname, lp->d_packname, 16); 386 bi_disk.label.checksum = lp->d_checksum; 387 388 bi_wedge.matchblk = d->boff + LABELSECTOR; 389 bi_wedge.matchnblks = 1; 390 391 md5(bi_wedge.matchhash, d->buf, d->ll.secsize); 392 #endif 393 394 return 0; 395 } 396 397 static int 398 read_label(struct biosdisk *d) 399 { 400 struct disklabel dflt_lbl; 401 struct mbr_partition mbr[MBR_PART_COUNT]; 402 struct partition *p; 403 int sector, i; 404 int error; 405 int typ; 406 int ext_base, this_ext, next_ext; 407 #ifdef COMPAT_386BSD_MBRPART 408 int sector_386bsd = -1; 409 #endif 410 411 memset(&dflt_lbl, 0, sizeof(dflt_lbl)); 412 dflt_lbl.d_npartitions = 8; 413 414 d->boff = 0; 415 416 if (d->ll.type != BIOSDISK_TYPE_HD) 417 /* No label on floppy and CD */ 418 return -1; 419 420 /* 421 * find NetBSD Partition in DOS partition table 422 * XXX check magic??? 423 */ 424 ext_base = 0; 425 next_ext = 0; 426 for (;;) { 427 this_ext = ext_base + next_ext; 428 next_ext = 0; 429 if (readsects(&d->ll, this_ext, 1, d->buf, 0)) { 430 #ifdef DISK_DEBUG 431 printf("error reading MBR sector %d\n", this_ext); 432 #endif 433 return EIO; 434 } 435 memcpy(&mbr, ((struct mbr_sector *)d->buf)->mbr_parts, 436 sizeof(mbr)); 437 /* Look for NetBSD partition ID */ 438 for (i = 0; i < MBR_PART_COUNT; i++) { 439 typ = mbr[i].mbrp_type; 440 if (typ == 0) 441 continue; 442 sector = this_ext + mbr[i].mbrp_start; 443 #ifdef DISK_DEBUG 444 printf("ptn type %d in sector %d\n", typ, sector); 445 #endif 446 if (typ == MBR_PTYPE_NETBSD) { 447 error = check_label(d, sector); 448 if (error >= 0) 449 return error; 450 } 451 if (MBR_IS_EXTENDED(typ)) { 452 next_ext = mbr[i].mbrp_start; 453 continue; 454 } 455 #ifdef COMPAT_386BSD_MBRPART 456 if (this_ext == 0 && typ == MBR_PTYPE_386BSD) 457 sector_386bsd = sector; 458 #endif 459 if (this_ext != 0) { 460 if (dflt_lbl.d_npartitions >= MAXPARTITIONS) 461 continue; 462 p = &dflt_lbl.d_partitions[dflt_lbl.d_npartitions++]; 463 } else 464 p = &dflt_lbl.d_partitions[i]; 465 p->p_offset = sector; 466 p->p_size = mbr[i].mbrp_size; 467 p->p_fstype = xlat_mbr_fstype(typ); 468 } 469 if (next_ext == 0) 470 break; 471 if (ext_base == 0) { 472 ext_base = next_ext; 473 next_ext = 0; 474 } 475 } 476 477 sector = 0; 478 #ifdef COMPAT_386BSD_MBRPART 479 if (sector_386bsd != -1) { 480 printf("old BSD partition ID!\n"); 481 sector = sector_386bsd; 482 } 483 #endif 484 485 /* 486 * One of two things: 487 * 1. no MBR 488 * 2. no NetBSD partition in MBR 489 * 490 * We simply default to "start of disk" in this case and 491 * press on. 492 */ 493 error = check_label(d, sector); 494 if (error >= 0) 495 return error; 496 497 /* 498 * Nothing at start of disk, return info from mbr partitions. 499 */ 500 /* XXX fill it to make checksum match kernel one */ 501 dflt_lbl.d_checksum = dkcksum(&dflt_lbl); 502 memcpy(d->buf, &dflt_lbl, sizeof(dflt_lbl)); 503 return 0; 504 } 505 #endif /* NO_DISKLABEL */ 506 507 #if !defined(NO_DISKLABEL) || !defined(NO_GPT) 508 static int 509 read_partitions(struct biosdisk *d) 510 { 511 int error; 512 513 error = -1; 514 515 #ifndef NO_GPT 516 error = read_gpt(d); 517 if (error == 0) 518 return 0; 519 520 #endif 521 #ifndef NO_DISKLABEL 522 error = read_label(d); 523 524 #endif 525 return error; 526 } 527 #endif 528 529 void 530 biosdisk_probe(void) 531 { 532 struct biosdisk d; 533 struct biosdisk_extinfo ed; 534 uint64_t size; 535 int first; 536 int i; 537 #if !defined(NO_DISKLABEL) || !defined(NO_GPT) 538 int part; 539 #endif 540 541 for (i = 0; i < MAX_BIOSDISKS + 2; i++) { 542 first = 1; 543 memset(&d, 0, sizeof(d)); 544 memset(&ed, 0, sizeof(ed)); 545 if (i >= MAX_BIOSDISKS) 546 d.ll.dev = 0x00 + i - MAX_BIOSDISKS; /* fd */ 547 else 548 d.ll.dev = 0x80 + i; /* hd/cd */ 549 if (set_geometry(&d.ll, &ed)) 550 continue; 551 printf("disk "); 552 switch (d.ll.type) { 553 case BIOSDISK_TYPE_CD: 554 printf("cd0\n cd0a\n"); 555 break; 556 case BIOSDISK_TYPE_FD: 557 printf("fd%d\n", d.ll.dev & 0x7f); 558 printf(" fd%da\n", d.ll.dev & 0x7f); 559 break; 560 case BIOSDISK_TYPE_HD: 561 printf("hd%d", d.ll.dev & 0x7f); 562 if (d.ll.flags & BIOSDISK_INT13EXT) { 563 printf(" size "); 564 size = ed.totsec * ed.sbytes; 565 if (size >= (10ULL * 1024 * 1024 * 1024)) 566 printf("%"PRIu64" GB", 567 size / (1024 * 1024 * 1024)); 568 else 569 printf("%"PRIu64" MB", 570 size / (1024 * 1024)); 571 } 572 printf("\n"); 573 break; 574 } 575 #if !defined(NO_DISKLABEL) || !defined(NO_GPT) 576 if (d.ll.type != BIOSDISK_TYPE_HD) 577 continue; 578 579 if (read_partitions(&d) != 0) 580 continue; 581 582 for (part = 0; part < BIOSDISKNPART; part++) { 583 if (d.part[part].size == 0) 584 continue; 585 if (d.part[part].fstype == FS_UNUSED) 586 continue; 587 if (first) { 588 printf(" "); 589 first = 0; 590 } 591 printf(" hd%d%c(", d.ll.dev & 0x7f, part + 'a'); 592 if (d.part[part].fstype < FSMAXTYPES) 593 printf("%s", 594 fstypenames[d.part[part].fstype]); 595 else 596 printf("%d", d.part[part].fstype); 597 printf(")"); 598 } 599 #endif 600 if (first == 0) 601 printf("\n"); 602 } 603 } 604 605 /* Determine likely partition for possible sector number of dos 606 * partition. 607 */ 608 609 int 610 biosdisk_findpartition(int biosdev, daddr_t sector) 611 { 612 #if defined(NO_DISKLABEL) && defined(NO_GPT) 613 return 0; 614 #else 615 struct biosdisk *d; 616 int partition = 0; 617 #ifdef DISK_DEBUG 618 printf("looking for partition device %x, sector %"PRId64"\n", biosdev, sector); 619 #endif 620 621 /* Look for netbsd partition that is the dos boot one */ 622 d = alloc_biosdisk(biosdev); 623 if (d == NULL) 624 return 0; 625 626 if (read_partitions(d) == 0) { 627 for (partition = (BIOSDISKNPART-1); --partition;) { 628 if (d->part[partition].fstype == FS_UNUSED) 629 continue; 630 if (d->part[partition].offset == sector) 631 break; 632 } 633 } 634 635 dealloc(d, sizeof(*d)); 636 return partition; 637 #endif /* NO_DISKLABEL && NO_GPT */ 638 } 639 640 #ifdef _STANDALONE 641 static void 642 add_biosdisk_bootinfo(void) 643 { 644 static bool done; 645 646 if (bootinfo == NULL) { 647 done = false; 648 return; 649 } 650 651 if (done) 652 return; 653 654 BI_ADD(&bi_disk, BTINFO_BOOTDISK, sizeof(bi_disk)); 655 BI_ADD(&bi_wedge, BTINFO_BOOTWEDGE, sizeof(bi_wedge)); 656 657 done = true; 658 659 return; 660 } 661 662 #endif 663 664 int 665 biosdisk_open(struct open_file *f, ...) 666 /* struct open_file *f, int biosdev, int partition */ 667 { 668 va_list ap; 669 struct biosdisk *d; 670 int biosdev; 671 int partition; 672 int error = 0; 673 674 va_start(ap, f); 675 biosdev = va_arg(ap, int); 676 d = alloc_biosdisk(biosdev); 677 if (d == NULL) { 678 error = ENXIO; 679 goto out; 680 } 681 682 partition = va_arg(ap, int); 683 #ifdef _STANDALONE 684 bi_disk.biosdev = d->ll.dev; 685 bi_disk.partition = partition; 686 bi_disk.labelsector = -1; 687 688 bi_wedge.biosdev = d->ll.dev; 689 bi_wedge.matchblk = -1; 690 #endif 691 692 #if !defined(NO_DISKLABEL) || !defined(NO_GPT) 693 error = read_partitions(d); 694 if (error == -1) { 695 error = 0; 696 goto nolabel; 697 } 698 if (error) 699 goto out; 700 701 if (partition >= BIOSDISKNPART || 702 d->part[partition].fstype == FS_UNUSED) { 703 #ifdef DISK_DEBUG 704 printf("illegal partition\n"); 705 #endif 706 error = EPART; 707 goto out; 708 } 709 710 d->boff = d->part[partition].offset; 711 712 if (d->part[partition].fstype == FS_RAID) 713 d->boff += RF_PROTECTED_SECTORS; 714 715 #ifdef _STANDALONE 716 bi_wedge.startblk = d->part[partition].offset; 717 bi_wedge.nblks = d->part[partition].size; 718 #endif 719 720 nolabel: 721 #endif 722 #ifdef DISK_DEBUG 723 printf("partition @%"PRId64"\n", d->boff); 724 #endif 725 726 #ifdef _STANDALONE 727 add_biosdisk_bootinfo(); 728 #endif 729 730 f->f_devdata = d; 731 out: 732 va_end(ap); 733 if (error) 734 dealloc(d, sizeof(*d)); 735 return error; 736 } 737 738 #ifndef LIBSA_NO_FS_CLOSE 739 int 740 biosdisk_close(struct open_file *f) 741 { 742 struct biosdisk *d = f->f_devdata; 743 744 /* let the floppy drive go off */ 745 if (d->ll.type == BIOSDISK_TYPE_FD) 746 wait_sec(3); /* 2s is enough on all PCs I found */ 747 748 dealloc(d, sizeof(*d)); 749 f->f_devdata = NULL; 750 return 0; 751 } 752 #endif 753 754 int 755 biosdisk_ioctl(struct open_file *f, u_long cmd, void *arg) 756 { 757 return EIO; 758 } 759