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