1 /* $NetBSD: biosdisk.c,v 1.17 2003/07/07 13:32:42 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 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed for the NetBSD Project 18 * by Matthias Drochner. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 */ 34 35 /* 36 * raw BIOS disk device for libsa. 37 * needs lowlevel parts from bios_disk.S and biosdisk_ll.c 38 * partly from netbsd:sys/arch/i386/boot/disk.c 39 * no bad144 handling! 40 * 41 * A lot of this must match sys/kern/subr_disk_mbr.c 42 */ 43 44 /* 45 * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 46 * 47 * Mach Operating System 48 * Copyright (c) 1992, 1991 Carnegie Mellon University 49 * All Rights Reserved. 50 * 51 * Permission to use, copy, modify and distribute this software and its 52 * documentation is hereby granted, provided that both the copyright 53 * notice and this permission notice appear in all copies of the 54 * software, derivative works or modified versions, and any portions 55 * thereof, and that both notices appear in supporting documentation. 56 * 57 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 58 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 59 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 60 * 61 * Carnegie Mellon requests users of this software to return to 62 * 63 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 64 * School of Computer Science 65 * Carnegie Mellon University 66 * Pittsburgh PA 15213-3890 67 * 68 * any improvements or extensions that they make and grant Carnegie Mellon 69 * the rights to redistribute these changes. 70 */ 71 72 #include <sys/types.h> 73 #include <sys/disklabel.h> 74 75 #include <lib/libsa/stand.h> 76 #include <lib/libsa/saerrno.h> 77 #include <machine/stdarg.h> 78 79 #include "libi386.h" 80 #include "biosdisk_ll.h" 81 #include "biosdisk.h" 82 #ifdef _STANDALONE 83 #include "bootinfo.h" 84 #endif 85 86 extern void memset(void *, int, size_t); 87 88 #define BUFSIZE (1 * BIOSDISK_SECSIZE) 89 90 struct biosdisk { 91 struct biosdisk_ll ll; 92 int boff; 93 char buf[BUFSIZE]; 94 }; 95 96 #ifdef _STANDALONE 97 static struct btinfo_bootdisk bi_disk; 98 #endif 99 100 #define RF_PROTECTED_SECTORS 64 /* XXX refer to <.../rf_optnames.h> */ 101 102 int boot_biossector; /* disk sector partition might have started in */ 103 104 int 105 biosdiskstrategy(devdata, flag, dblk, size, buf, rsize) 106 void *devdata; 107 int flag; 108 daddr_t dblk; 109 size_t size; 110 void *buf; 111 size_t *rsize; 112 { 113 struct biosdisk *d; 114 int blks, frag; 115 116 if (flag != F_READ) 117 return (EROFS); 118 119 d = (struct biosdisk *) devdata; 120 121 dblk += d->boff; 122 123 blks = size / BIOSDISK_SECSIZE; 124 if (blks && readsects(&d->ll, dblk, blks, buf, 0)) { 125 if (rsize) 126 *rsize = 0; 127 return (EIO); 128 } 129 /* do we really need this? */ 130 frag = size % BIOSDISK_SECSIZE; 131 if (frag) { 132 if (readsects(&d->ll, dblk + blks, 1, d->buf, 0)) { 133 if (rsize) 134 *rsize = blks * BIOSDISK_SECSIZE; 135 return (EIO); 136 } 137 memcpy(buf + blks * BIOSDISK_SECSIZE, d->buf, frag); 138 } 139 if (rsize) 140 *rsize = size; 141 return (0); 142 } 143 144 static struct biosdisk * 145 alloc_biosdisk(int dev) 146 { 147 struct biosdisk *d; 148 149 d = (struct biosdisk *)alloc(sizeof *d); 150 if (!d) 151 return NULL; 152 memset(d, 0, sizeof *d); 153 154 d->ll.dev = dev;; 155 if (set_geometry(&d->ll, NULL)) { 156 #ifdef DISK_DEBUG 157 printf("no geometry information\n"); 158 #endif 159 free(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\n"); 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[NMBRPART]; 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.dev & 0x80)) /* floppy */ 210 /* No label on floppy */ 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, ((mbr_sector_t *)d->buf)->mbr_parts, sizeof mbr); 229 /* Look for NetBSD partition ID */ 230 for (i = 0; i < NMBRPART; i++) { 231 typ = mbr[i].mbrp_typ; 232 if (typ == 0) 233 continue; 234 sector = this_ext + mbr[i].mbrp_start; 235 if (typ == MBR_PTYPE_NETBSD) { 236 error = check_label(d, sector); 237 if (error >= 0) 238 return error; 239 } 240 if (MBR_IS_EXTENDED(typ)) { 241 next_ext = mbr[i].mbrp_start; 242 continue; 243 } 244 #ifdef COMPAT_386BSD_MBRPART 245 if (this_ext == 0 && typ == MBR_PTYPE_386BSD) 246 sector_386bsd = sector; 247 #endif 248 if (this_ext != 0) { 249 if (dflt_lbl.d_npartitions >= MAXPARTITIONS) 250 continue; 251 p = &dflt_lbl.d_partitions[dflt_lbl.d_npartitions++]; 252 } else 253 p = &dflt_lbl.d_partitions[i]; 254 p->p_offset = sector; 255 p->p_size = mbr[i].mbrp_size; 256 p->p_fstype = xlat_mbr_fstype(typ); 257 } 258 if (next_ext == 0) 259 break; 260 if (ext_base == 0) { 261 ext_base = next_ext; 262 next_ext = 0; 263 } 264 } 265 266 sector = 0; 267 #ifdef COMPAT_386BSD_MBRPART 268 if (sector_386bsd != -1) { 269 printf("old BSD partition ID!\n"); 270 sector = sector_386bsd; 271 } 272 #endif 273 274 /* 275 * One of two things: 276 * 1. no MBR 277 * 2. no NetBSD partition in MBR 278 * 279 * We simply default to "start of disk" in this case and 280 * press on. 281 */ 282 error = check_label(d, sector); 283 if (error >= 0) 284 return error; 285 286 /* 287 * Nothing at start of disk, return info from mbr partitions. 288 */ 289 /* XXX fill it to make checksum match kernel one */ 290 dflt_lbl.d_checksum = dkcksum(&dflt_lbl); 291 memcpy(d->buf, &dflt_lbl, sizeof dflt_lbl); 292 return -1; 293 } 294 #endif /* NO_DISKLABEL */ 295 296 /* Determine likely partition for possible sector number of dos 297 * partition. 298 */ 299 300 u_int 301 biosdiskfindptn(int biosdev, u_int sector) 302 { 303 #ifdef NO_DISKLABEL 304 return 0; 305 #else 306 struct biosdisk *d; 307 u_int partition = 0; 308 struct disklabel *lp; 309 310 /* Look for netbsd partition that is the dos boot one */ 311 d = alloc_biosdisk(biosdev); 312 if (d == NULL) 313 return 0; 314 315 if (read_label(d) == 0) { 316 lp = (struct disklabel *)(d->buf + LABELOFFSET); 317 for (partition = lp->d_npartitions; --partition;){ 318 if (lp->d_partitions[partition].p_fstype == FS_UNUSED) 319 continue; 320 if (lp->d_partitions[partition].p_offset == sector) 321 break; 322 } 323 } 324 325 free(d, sizeof *d); 326 return partition; 327 } 328 #endif /* NO_DISKLABEL */ 329 330 int 331 biosdiskopen(struct open_file *f, ...) 332 /* file, biosdev, partition */ 333 { 334 va_list ap; 335 struct biosdisk *d; 336 int partition; 337 #ifndef NO_DISKLABEL 338 struct disklabel *lp; 339 #endif 340 int error = 0; 341 342 va_start(ap, f); 343 d = alloc_biosdisk(va_arg(ap, int)); 344 if (!d) { 345 error = ENXIO; 346 goto out; 347 } 348 349 partition = va_arg(ap, int); 350 #ifdef _STANDALONE 351 bi_disk.biosdev = d->ll.dev; 352 bi_disk.partition = partition; 353 bi_disk.labelsector = -1; 354 #endif 355 356 #ifndef NO_DISKLABEL 357 if (partition == RAW_PART) 358 goto nolabel; 359 error = read_label(d); 360 if (error == -1) { 361 error = 0; 362 goto nolabel; 363 } 364 if (error) 365 goto out; 366 367 lp = (struct disklabel *) (d->buf + LABELOFFSET); 368 if (partition >= lp->d_npartitions || 369 lp->d_partitions[partition].p_fstype == FS_UNUSED) { 370 #ifdef DISK_DEBUG 371 printf("illegal partition\n"); 372 #endif 373 error = EPART; 374 goto out; 375 } 376 #ifdef _STANDALONE 377 bi_disk.labelsector = d->boff + LABELSECTOR; 378 bi_disk.label.type = lp->d_type; 379 memcpy(bi_disk.label.packname, lp->d_packname, 16); 380 bi_disk.label.checksum = lp->d_checksum; 381 #endif 382 d->boff = lp->d_partitions[partition].p_offset; 383 if (lp->d_partitions[partition].p_fstype == FS_RAID) 384 d->boff += RF_PROTECTED_SECTORS; 385 nolabel: 386 #endif /* NO_DISKLABEL */ 387 388 #ifdef DISK_DEBUG 389 printf("partition @%d\n", d->boff); 390 #endif 391 392 #ifdef _STANDALONE 393 BI_ADD(&bi_disk, BTINFO_BOOTDISK, sizeof(bi_disk)); 394 #endif 395 396 f->f_devdata = d; 397 out: 398 va_end(ap); 399 if (error) 400 free(d, sizeof(struct biosdisk)); 401 return (error); 402 } 403 404 #ifndef LIBSA_NO_FS_CLOSE 405 int 406 biosdiskclose(f) 407 struct open_file *f; 408 { 409 struct biosdisk *d = f->f_devdata; 410 411 if (!(d->ll.dev & 0x80))/* let the floppy drive go off */ 412 delay(3000000); /* 2s is enough on all PCs I found */ 413 414 free(d, sizeof(struct biosdisk)); 415 f->f_devdata = NULL; 416 return (0); 417 } 418 #endif 419 420 int 421 biosdiskioctl(f, cmd, arg) 422 struct open_file *f; 423 u_long cmd; 424 void *arg; 425 { 426 return EIO; 427 } 428