1 /* $NetBSD: biosdisk.c,v 1.30 2009/10/20 14:49:03 jmcneill 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 #ifndef NO_DISKLABEL 67 #define FSTYPENAMES 68 #endif 69 70 #include <sys/types.h> 71 #include <sys/md5.h> 72 #include <sys/param.h> 73 #include <sys/disklabel.h> 74 75 #include <fs/cd9660/iso.h> 76 77 #include <lib/libsa/stand.h> 78 #include <lib/libsa/saerrno.h> 79 #include <machine/stdarg.h> 80 #include <machine/cpu.h> 81 82 #include "libi386.h" 83 #include "biosdisk_ll.h" 84 #include "biosdisk.h" 85 #ifdef _STANDALONE 86 #include "bootinfo.h" 87 #endif 88 89 #define BUFSIZE 2048 /* must be large enough for a CD sector */ 90 91 struct biosdisk { 92 struct biosdisk_ll ll; 93 int boff; 94 char buf[BUFSIZE]; 95 }; 96 97 #ifdef _STANDALONE 98 static struct btinfo_bootdisk bi_disk; 99 static struct btinfo_bootwedge bi_wedge; 100 #endif 101 102 #define RF_PROTECTED_SECTORS 64 /* XXX refer to <.../rf_optnames.h> */ 103 104 int 105 biosdisk_strategy(void *devdata, int flag, daddr_t dblk, size_t size, 106 void *buf, size_t *rsize) 107 { 108 struct biosdisk *d; 109 int blks, frag; 110 111 if (flag != F_READ) 112 return EROFS; 113 114 d = (struct biosdisk *) devdata; 115 116 if (d->ll.type == BIOSDISK_TYPE_CD) 117 dblk = dblk * DEV_BSIZE / ISO_DEFAULT_BLOCK_SIZE; 118 119 dblk += d->boff; 120 121 blks = size / d->ll.secsize; 122 if (blks && readsects(&d->ll, dblk, blks, buf, 0)) { 123 if (rsize) 124 *rsize = 0; 125 return EIO; 126 } 127 128 /* needed for CD */ 129 frag = size % d->ll.secsize; 130 if (frag) { 131 if (readsects(&d->ll, dblk + blks, 1, d->buf, 0)) { 132 if (rsize) 133 *rsize = blks * d->ll.secsize; 134 return EIO; 135 } 136 memcpy(buf + blks * d->ll.secsize, d->buf, frag); 137 } 138 139 if (rsize) 140 *rsize = size; 141 return 0; 142 } 143 144 static struct biosdisk * 145 alloc_biosdisk(int biosdev) 146 { 147 struct biosdisk *d; 148 149 d = alloc(sizeof(*d)); 150 if (d == NULL) 151 return NULL; 152 memset(d, 0, sizeof(*d)); 153 154 d->ll.dev = biosdev; 155 if (set_geometry(&d->ll, NULL)) { 156 #ifdef DISK_DEBUG 157 printf("no geometry information\n"); 158 #endif 159 dealloc(d, sizeof(*d)); 160 return NULL; 161 } 162 return d; 163 } 164 165 #ifndef NO_DISKLABEL 166 static int 167 check_label(struct biosdisk *d, int sector) 168 { 169 struct disklabel *lp; 170 171 /* find partition in NetBSD disklabel */ 172 if (readsects(&d->ll, sector + LABELSECTOR, 1, d->buf, 0)) { 173 #ifdef DISK_DEBUG 174 printf("Error reading disklabel\n"); 175 #endif 176 return EIO; 177 } 178 lp = (struct disklabel *) (d->buf + LABELOFFSET); 179 if (lp->d_magic != DISKMAGIC || dkcksum(lp)) { 180 #ifdef DISK_DEBUG 181 printf("warning: no disklabel in sector %u\n", sector); 182 #endif 183 return -1; 184 } 185 186 d->boff = sector; 187 return 0; 188 } 189 190 static int 191 read_label(struct biosdisk *d) 192 { 193 struct disklabel dflt_lbl; 194 struct mbr_partition mbr[MBR_PART_COUNT]; 195 struct partition *p; 196 int sector, i; 197 int error; 198 int typ; 199 int ext_base, this_ext, next_ext; 200 #ifdef COMPAT_386BSD_MBRPART 201 int sector_386bsd = -1; 202 #endif 203 204 memset(&dflt_lbl, 0, sizeof(dflt_lbl)); 205 dflt_lbl.d_npartitions = 8; 206 207 d->boff = 0; 208 209 if (d->ll.type != BIOSDISK_TYPE_HD) 210 /* No label on floppy and CD */ 211 return -1; 212 213 /* 214 * find NetBSD Partition in DOS partition table 215 * XXX check magic??? 216 */ 217 ext_base = 0; 218 next_ext = 0; 219 for (;;) { 220 this_ext = ext_base + next_ext; 221 next_ext = 0; 222 if (readsects(&d->ll, this_ext, 1, d->buf, 0)) { 223 #ifdef DISK_DEBUG 224 printf("error reading MBR sector %d\n", this_ext); 225 #endif 226 return EIO; 227 } 228 memcpy(&mbr, ((struct mbr_sector *)d->buf)->mbr_parts, 229 sizeof(mbr)); 230 /* Look for NetBSD partition ID */ 231 for (i = 0; i < MBR_PART_COUNT; i++) { 232 typ = mbr[i].mbrp_type; 233 if (typ == 0) 234 continue; 235 sector = this_ext + mbr[i].mbrp_start; 236 #ifdef DISK_DEBUG 237 printf("ptn type %d in sector %u\n", typ, sector); 238 #endif 239 if (typ == MBR_PTYPE_NETBSD) { 240 error = check_label(d, sector); 241 if (error >= 0) 242 return error; 243 } 244 if (MBR_IS_EXTENDED(typ)) { 245 next_ext = mbr[i].mbrp_start; 246 continue; 247 } 248 #ifdef COMPAT_386BSD_MBRPART 249 if (this_ext == 0 && typ == MBR_PTYPE_386BSD) 250 sector_386bsd = sector; 251 #endif 252 if (this_ext != 0) { 253 if (dflt_lbl.d_npartitions >= MAXPARTITIONS) 254 continue; 255 p = &dflt_lbl.d_partitions[dflt_lbl.d_npartitions++]; 256 } else 257 p = &dflt_lbl.d_partitions[i]; 258 p->p_offset = sector; 259 p->p_size = mbr[i].mbrp_size; 260 p->p_fstype = xlat_mbr_fstype(typ); 261 } 262 if (next_ext == 0) 263 break; 264 if (ext_base == 0) { 265 ext_base = next_ext; 266 next_ext = 0; 267 } 268 } 269 270 sector = 0; 271 #ifdef COMPAT_386BSD_MBRPART 272 if (sector_386bsd != -1) { 273 printf("old BSD partition ID!\n"); 274 sector = sector_386bsd; 275 } 276 #endif 277 278 /* 279 * One of two things: 280 * 1. no MBR 281 * 2. no NetBSD partition in MBR 282 * 283 * We simply default to "start of disk" in this case and 284 * press on. 285 */ 286 error = check_label(d, sector); 287 if (error >= 0) 288 return error; 289 290 /* 291 * Nothing at start of disk, return info from mbr partitions. 292 */ 293 /* XXX fill it to make checksum match kernel one */ 294 dflt_lbl.d_checksum = dkcksum(&dflt_lbl); 295 memcpy(d->buf, &dflt_lbl, sizeof(dflt_lbl)); 296 return 0; 297 } 298 #endif /* NO_DISKLABEL */ 299 300 void 301 biosdisk_probe(void) 302 { 303 #ifndef NO_DISKLABEL 304 struct disklabel *lp; 305 int first, part; 306 #endif 307 struct biosdisk d; 308 struct biosdisk_extinfo ed; 309 uint64_t size; 310 int i; 311 312 for (i = 0; i < MAX_BIOSDISKS + 2; i++) { 313 first = 1; 314 memset(&d, 0, sizeof(d)); 315 memset(&ed, 0, sizeof(ed)); 316 if (i >= MAX_BIOSDISKS) 317 d.ll.dev = 0x00 + i - MAX_BIOSDISKS; /* fd */ 318 else 319 d.ll.dev = 0x80 + i; /* hd/cd */ 320 if (set_geometry(&d.ll, &ed)) 321 continue; 322 printf("disk "); 323 switch (d.ll.type) { 324 case BIOSDISK_TYPE_CD: 325 printf("cd0\n cd0a\n"); 326 break; 327 case BIOSDISK_TYPE_FD: 328 printf("fd%d\n", d.ll.dev & 0x7f); 329 printf(" fd%da\n", d.ll.dev & 0x7f); 330 break; 331 case BIOSDISK_TYPE_HD: 332 printf("hd%d", d.ll.dev & 0x7f); 333 if (d.ll.flags & BIOSDISK_INT13EXT) { 334 printf(" size "); 335 size = ed.totsec * ed.sbytes; 336 if (size >= (10ULL * 1024 * 1024 * 1024)) 337 printf("%llu GB", 338 size / (1024 * 1024 * 1024)); 339 else 340 printf("%llu MB", 341 size / (1024 * 1024)); 342 } 343 printf("\n"); 344 break; 345 } 346 #ifndef NO_DISKLABEL 347 if (d.ll.type != BIOSDISK_TYPE_HD) 348 continue; 349 if (read_label(&d) == -1) 350 break; 351 lp = (struct disklabel *)(d.buf + LABELOFFSET); 352 for (part = 0; part < lp->d_npartitions; part++) { 353 if (lp->d_partitions[part].p_size == 0) 354 continue; 355 if (lp->d_partitions[part].p_fstype == FS_UNUSED) 356 continue; 357 if (first) { 358 printf(" "); 359 first = 0; 360 } 361 printf(" hd%d%c(", d.ll.dev & 0x7f, part + 'a'); 362 if (lp->d_partitions[part].p_fstype < FSMAXTYPES) 363 printf("%s", 364 fstypenames[lp->d_partitions[part].p_fstype]); 365 else 366 printf("%d", lp->d_partitions[part].p_fstype); 367 printf(")"); 368 } 369 if (first == 0) 370 printf("\n"); 371 #endif 372 } 373 } 374 375 /* Determine likely partition for possible sector number of dos 376 * partition. 377 */ 378 379 int 380 biosdisk_findpartition(int biosdev, u_int sector) 381 { 382 #ifdef NO_DISKLABEL 383 return 0; 384 #else 385 struct biosdisk *d; 386 int partition = 0; 387 struct disklabel *lp; 388 #ifdef DISK_DEBUG 389 printf("looking for partition device %x, sector %u\n", biosdev, sector); 390 #endif 391 392 /* Look for netbsd partition that is the dos boot one */ 393 d = alloc_biosdisk(biosdev); 394 if (d == NULL) 395 return 0; 396 397 if (read_label(d) == 0) { 398 lp = (struct disklabel *)(d->buf + LABELOFFSET); 399 for (partition = lp->d_npartitions; --partition;){ 400 if (lp->d_partitions[partition].p_fstype == FS_UNUSED) 401 continue; 402 if (lp->d_partitions[partition].p_offset == sector) 403 break; 404 } 405 } 406 407 dealloc(d, sizeof(*d)); 408 return partition; 409 #endif /* NO_DISKLABEL */ 410 } 411 412 int 413 biosdisk_open(struct open_file *f, ...) 414 /* struct open_file *f, int biosdev, int partition */ 415 { 416 va_list ap; 417 struct biosdisk *d; 418 int biosdev; 419 int partition; 420 #ifndef NO_DISKLABEL 421 struct disklabel *lp; 422 #endif 423 int error = 0; 424 425 va_start(ap, f); 426 biosdev = va_arg(ap, int); 427 d = alloc_biosdisk(biosdev); 428 if (d == NULL) { 429 error = ENXIO; 430 goto out; 431 } 432 433 partition = va_arg(ap, int); 434 #ifdef _STANDALONE 435 bi_disk.biosdev = d->ll.dev; 436 bi_disk.partition = partition; 437 bi_disk.labelsector = -1; 438 439 bi_wedge.biosdev = d->ll.dev; 440 bi_wedge.matchblk = -1; 441 #endif 442 443 #ifndef NO_DISKLABEL 444 if (partition == RAW_PART) 445 goto nolabel; 446 error = read_label(d); 447 if (error == -1) { 448 error = 0; 449 goto nolabel; 450 } 451 if (error) 452 goto out; 453 454 lp = (struct disklabel *) (d->buf + LABELOFFSET); 455 if (partition >= lp->d_npartitions || 456 lp->d_partitions[partition].p_fstype == FS_UNUSED) { 457 #ifdef DISK_DEBUG 458 printf("illegal partition\n"); 459 #endif 460 error = EPART; 461 goto out; 462 } 463 #ifdef _STANDALONE 464 bi_disk.labelsector = d->boff + LABELSECTOR; 465 bi_disk.label.type = lp->d_type; 466 memcpy(bi_disk.label.packname, lp->d_packname, 16); 467 bi_disk.label.checksum = lp->d_checksum; 468 469 bi_wedge.startblk = lp->d_partitions[partition].p_offset; 470 bi_wedge.nblks = lp->d_partitions[partition].p_size; 471 bi_wedge.matchblk = d->boff + LABELSECTOR; 472 bi_wedge.matchnblks = 1; 473 { 474 MD5_CTX ctx; 475 476 MD5Init(&ctx); 477 MD5Update(&ctx, (void *) d->buf, 512); 478 MD5Final(bi_wedge.matchhash, &ctx); 479 } 480 #endif 481 d->boff = lp->d_partitions[partition].p_offset; 482 if (lp->d_partitions[partition].p_fstype == FS_RAID) 483 d->boff += RF_PROTECTED_SECTORS; 484 nolabel: 485 #endif /* NO_DISKLABEL */ 486 487 #ifdef DISK_DEBUG 488 printf("partition @%d\n", d->boff); 489 #endif 490 491 #ifdef _STANDALONE 492 BI_ADD(&bi_disk, BTINFO_BOOTDISK, sizeof(bi_disk)); 493 BI_ADD(&bi_wedge, BTINFO_BOOTWEDGE, sizeof(bi_wedge)); 494 #endif 495 496 f->f_devdata = d; 497 out: 498 va_end(ap); 499 if (error) 500 dealloc(d, sizeof(struct biosdisk)); 501 return error; 502 } 503 504 #ifndef LIBSA_NO_FS_CLOSE 505 int 506 biosdisk_close(struct open_file *f) 507 { 508 struct biosdisk *d = f->f_devdata; 509 510 /* let the floppy drive go off */ 511 if (d->ll.type == BIOSDISK_TYPE_FD) 512 delay(3000000); /* 2s is enough on all PCs I found */ 513 514 dealloc(d, sizeof(struct biosdisk)); 515 f->f_devdata = NULL; 516 return 0; 517 } 518 #endif 519 520 int 521 biosdisk_ioctl(struct open_file *f, u_long cmd, void *arg) 522 { 523 return EIO; 524 } 525