1 /* $NetBSD: biosdisk.c,v 1.28 2008/01/05 15:28:43 dsl 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 in sector %u\n", sector); 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 #ifdef DISK_DEBUG 232 printf("ptn type %d in sector %u\n", typ, sector); 233 #endif 234 if (typ == MBR_PTYPE_NETBSD) { 235 error = check_label(d, sector); 236 if (error >= 0) 237 return error; 238 } 239 if (MBR_IS_EXTENDED(typ)) { 240 next_ext = mbr[i].mbrp_start; 241 continue; 242 } 243 #ifdef COMPAT_386BSD_MBRPART 244 if (this_ext == 0 && typ == MBR_PTYPE_386BSD) 245 sector_386bsd = sector; 246 #endif 247 if (this_ext != 0) { 248 if (dflt_lbl.d_npartitions >= MAXPARTITIONS) 249 continue; 250 p = &dflt_lbl.d_partitions[dflt_lbl.d_npartitions++]; 251 } else 252 p = &dflt_lbl.d_partitions[i]; 253 p->p_offset = sector; 254 p->p_size = mbr[i].mbrp_size; 255 p->p_fstype = xlat_mbr_fstype(typ); 256 } 257 if (next_ext == 0) 258 break; 259 if (ext_base == 0) { 260 ext_base = next_ext; 261 next_ext = 0; 262 } 263 } 264 265 sector = 0; 266 #ifdef COMPAT_386BSD_MBRPART 267 if (sector_386bsd != -1) { 268 printf("old BSD partition ID!\n"); 269 sector = sector_386bsd; 270 } 271 #endif 272 273 /* 274 * One of two things: 275 * 1. no MBR 276 * 2. no NetBSD partition in MBR 277 * 278 * We simply default to "start of disk" in this case and 279 * press on. 280 */ 281 error = check_label(d, sector); 282 if (error >= 0) 283 return error; 284 285 /* 286 * Nothing at start of disk, return info from mbr partitions. 287 */ 288 /* XXX fill it to make checksum match kernel one */ 289 dflt_lbl.d_checksum = dkcksum(&dflt_lbl); 290 memcpy(d->buf, &dflt_lbl, sizeof(dflt_lbl)); 291 return 0; 292 } 293 #endif /* NO_DISKLABEL */ 294 295 /* Determine likely partition for possible sector number of dos 296 * partition. 297 */ 298 299 int 300 biosdisk_findpartition(int biosdev, u_int sector) 301 { 302 #ifdef NO_DISKLABEL 303 return 0; 304 #else 305 struct biosdisk *d; 306 int partition = 0; 307 struct disklabel *lp; 308 #ifdef DISK_DEBUG 309 printf("looking for partition device %x, sector %u\n", biosdev, sector); 310 #endif 311 312 /* Look for netbsd partition that is the dos boot one */ 313 d = alloc_biosdisk(biosdev); 314 if (d == NULL) 315 return 0; 316 317 if (read_label(d) == 0) { 318 lp = (struct disklabel *)(d->buf + LABELOFFSET); 319 for (partition = lp->d_npartitions; --partition;){ 320 if (lp->d_partitions[partition].p_fstype == FS_UNUSED) 321 continue; 322 if (lp->d_partitions[partition].p_offset == sector) 323 break; 324 } 325 } 326 327 dealloc(d, sizeof(*d)); 328 return partition; 329 #endif /* NO_DISKLABEL */ 330 } 331 332 int 333 biosdisk_open(struct open_file *f, ...) 334 /* struct open_file *f, int biosdev, int partition */ 335 { 336 va_list ap; 337 struct biosdisk *d; 338 int biosdev; 339 int partition; 340 #ifndef NO_DISKLABEL 341 struct disklabel *lp; 342 #endif 343 int error = 0; 344 345 va_start(ap, f); 346 biosdev = va_arg(ap, int); 347 d = alloc_biosdisk(biosdev); 348 if (d == NULL) { 349 error = ENXIO; 350 goto out; 351 } 352 353 partition = va_arg(ap, int); 354 #ifdef _STANDALONE 355 bi_disk.biosdev = d->ll.dev; 356 bi_disk.partition = partition; 357 bi_disk.labelsector = -1; 358 359 bi_wedge.biosdev = d->ll.dev; 360 bi_wedge.matchblk = -1; 361 #endif 362 363 #ifndef NO_DISKLABEL 364 if (partition == RAW_PART) 365 goto nolabel; 366 error = read_label(d); 367 if (error == -1) { 368 error = 0; 369 goto nolabel; 370 } 371 if (error) 372 goto out; 373 374 lp = (struct disklabel *) (d->buf + LABELOFFSET); 375 if (partition >= lp->d_npartitions || 376 lp->d_partitions[partition].p_fstype == FS_UNUSED) { 377 #ifdef DISK_DEBUG 378 printf("illegal partition\n"); 379 #endif 380 error = EPART; 381 goto out; 382 } 383 #ifdef _STANDALONE 384 bi_disk.labelsector = d->boff + LABELSECTOR; 385 bi_disk.label.type = lp->d_type; 386 memcpy(bi_disk.label.packname, lp->d_packname, 16); 387 bi_disk.label.checksum = lp->d_checksum; 388 389 bi_wedge.startblk = lp->d_partitions[partition].p_offset; 390 bi_wedge.nblks = lp->d_partitions[partition].p_size; 391 bi_wedge.matchblk = d->boff + LABELSECTOR; 392 bi_wedge.matchnblks = 1; 393 { 394 MD5_CTX ctx; 395 396 MD5Init(&ctx); 397 MD5Update(&ctx, (void *) d->buf, 512); 398 MD5Final(bi_wedge.matchhash, &ctx); 399 } 400 #endif 401 d->boff = lp->d_partitions[partition].p_offset; 402 if (lp->d_partitions[partition].p_fstype == FS_RAID) 403 d->boff += RF_PROTECTED_SECTORS; 404 nolabel: 405 #endif /* NO_DISKLABEL */ 406 407 #ifdef DISK_DEBUG 408 printf("partition @%d\n", d->boff); 409 #endif 410 411 #ifdef _STANDALONE 412 BI_ADD(&bi_disk, BTINFO_BOOTDISK, sizeof(bi_disk)); 413 BI_ADD(&bi_wedge, BTINFO_BOOTWEDGE, sizeof(bi_wedge)); 414 #endif 415 416 f->f_devdata = d; 417 out: 418 va_end(ap); 419 if (error) 420 dealloc(d, sizeof(struct biosdisk)); 421 return error; 422 } 423 424 #ifndef LIBSA_NO_FS_CLOSE 425 int 426 biosdisk_close(struct open_file *f) 427 { 428 struct biosdisk *d = f->f_devdata; 429 430 /* let the floppy drive go off */ 431 if (d->ll.type == BIOSDISK_TYPE_FD) 432 delay(3000000); /* 2s is enough on all PCs I found */ 433 434 dealloc(d, sizeof(struct biosdisk)); 435 f->f_devdata = NULL; 436 return 0; 437 } 438 #endif 439 440 int 441 biosdisk_ioctl(struct open_file *f, u_long cmd, void *arg) 442 { 443 return EIO; 444 } 445