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