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