1 /* $NetBSD: biosdisk.c,v 1.29 2009/09/13 22:45:27 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 switch (d.ll.type) { 323 case BIOSDISK_TYPE_CD: 324 printf("disk cd0\n"); 325 printf(" cd0a(unknown)\n"); 326 break; 327 case BIOSDISK_TYPE_FD: 328 printf("disk fd%d\n", d.ll.dev & 0x7f); 329 printf(" fd%da(unknown)\n", d.ll.dev & 0x7f); 330 break; 331 case BIOSDISK_TYPE_HD: 332 printf("disk 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 (read_label(&d) == -1) 348 break; 349 lp = (struct disklabel *)(d.buf + LABELOFFSET); 350 for (part = 0; part < lp->d_npartitions; part++) { 351 if (lp->d_partitions[part].p_size == 0) 352 continue; 353 if (lp->d_partitions[part].p_fstype == FS_UNUSED) 354 continue; 355 if (first) { 356 printf(" "); 357 first = 0; 358 } 359 printf(" hd%d%c(", d.ll.dev & 0x7f, part + 'a'); 360 if (lp->d_partitions[part].p_fstype < FSMAXTYPES) 361 printf("%s", 362 fstypenames[lp->d_partitions[part].p_fstype]); 363 else 364 printf("%d", lp->d_partitions[part].p_fstype); 365 printf(")"); 366 } 367 if (first == 0) 368 printf("\n"); 369 #endif 370 } 371 } 372 373 /* Determine likely partition for possible sector number of dos 374 * partition. 375 */ 376 377 int 378 biosdisk_findpartition(int biosdev, u_int sector) 379 { 380 #ifdef NO_DISKLABEL 381 return 0; 382 #else 383 struct biosdisk *d; 384 int partition = 0; 385 struct disklabel *lp; 386 #ifdef DISK_DEBUG 387 printf("looking for partition device %x, sector %u\n", biosdev, sector); 388 #endif 389 390 /* Look for netbsd partition that is the dos boot one */ 391 d = alloc_biosdisk(biosdev); 392 if (d == NULL) 393 return 0; 394 395 if (read_label(d) == 0) { 396 lp = (struct disklabel *)(d->buf + LABELOFFSET); 397 for (partition = lp->d_npartitions; --partition;){ 398 if (lp->d_partitions[partition].p_fstype == FS_UNUSED) 399 continue; 400 if (lp->d_partitions[partition].p_offset == sector) 401 break; 402 } 403 } 404 405 dealloc(d, sizeof(*d)); 406 return partition; 407 #endif /* NO_DISKLABEL */ 408 } 409 410 int 411 biosdisk_open(struct open_file *f, ...) 412 /* struct open_file *f, int biosdev, int partition */ 413 { 414 va_list ap; 415 struct biosdisk *d; 416 int biosdev; 417 int partition; 418 #ifndef NO_DISKLABEL 419 struct disklabel *lp; 420 #endif 421 int error = 0; 422 423 va_start(ap, f); 424 biosdev = va_arg(ap, int); 425 d = alloc_biosdisk(biosdev); 426 if (d == NULL) { 427 error = ENXIO; 428 goto out; 429 } 430 431 partition = va_arg(ap, int); 432 #ifdef _STANDALONE 433 bi_disk.biosdev = d->ll.dev; 434 bi_disk.partition = partition; 435 bi_disk.labelsector = -1; 436 437 bi_wedge.biosdev = d->ll.dev; 438 bi_wedge.matchblk = -1; 439 #endif 440 441 #ifndef NO_DISKLABEL 442 if (partition == RAW_PART) 443 goto nolabel; 444 error = read_label(d); 445 if (error == -1) { 446 error = 0; 447 goto nolabel; 448 } 449 if (error) 450 goto out; 451 452 lp = (struct disklabel *) (d->buf + LABELOFFSET); 453 if (partition >= lp->d_npartitions || 454 lp->d_partitions[partition].p_fstype == FS_UNUSED) { 455 #ifdef DISK_DEBUG 456 printf("illegal partition\n"); 457 #endif 458 error = EPART; 459 goto out; 460 } 461 #ifdef _STANDALONE 462 bi_disk.labelsector = d->boff + LABELSECTOR; 463 bi_disk.label.type = lp->d_type; 464 memcpy(bi_disk.label.packname, lp->d_packname, 16); 465 bi_disk.label.checksum = lp->d_checksum; 466 467 bi_wedge.startblk = lp->d_partitions[partition].p_offset; 468 bi_wedge.nblks = lp->d_partitions[partition].p_size; 469 bi_wedge.matchblk = d->boff + LABELSECTOR; 470 bi_wedge.matchnblks = 1; 471 { 472 MD5_CTX ctx; 473 474 MD5Init(&ctx); 475 MD5Update(&ctx, (void *) d->buf, 512); 476 MD5Final(bi_wedge.matchhash, &ctx); 477 } 478 #endif 479 d->boff = lp->d_partitions[partition].p_offset; 480 if (lp->d_partitions[partition].p_fstype == FS_RAID) 481 d->boff += RF_PROTECTED_SECTORS; 482 nolabel: 483 #endif /* NO_DISKLABEL */ 484 485 #ifdef DISK_DEBUG 486 printf("partition @%d\n", d->boff); 487 #endif 488 489 #ifdef _STANDALONE 490 BI_ADD(&bi_disk, BTINFO_BOOTDISK, sizeof(bi_disk)); 491 BI_ADD(&bi_wedge, BTINFO_BOOTWEDGE, sizeof(bi_wedge)); 492 #endif 493 494 f->f_devdata = d; 495 out: 496 va_end(ap); 497 if (error) 498 dealloc(d, sizeof(struct biosdisk)); 499 return error; 500 } 501 502 #ifndef LIBSA_NO_FS_CLOSE 503 int 504 biosdisk_close(struct open_file *f) 505 { 506 struct biosdisk *d = f->f_devdata; 507 508 /* let the floppy drive go off */ 509 if (d->ll.type == BIOSDISK_TYPE_FD) 510 delay(3000000); /* 2s is enough on all PCs I found */ 511 512 dealloc(d, sizeof(struct biosdisk)); 513 f->f_devdata = NULL; 514 return 0; 515 } 516 #endif 517 518 int 519 biosdisk_ioctl(struct open_file *f, u_long cmd, void *arg) 520 { 521 return EIO; 522 } 523