1 /* $NetBSD: ofdisk.c,v 1.43 2009/05/12 14:39:22 cegger Exp $ */ 2 3 /* 4 * Copyright (C) 1995, 1996 Wolfgang Solfrank. 5 * Copyright (C) 1995, 1996 TooLs GmbH. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by TooLs GmbH. 19 * 4. The name of TooLs GmbH 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 TOOLS GMBH ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: ofdisk.c,v 1.43 2009/05/12 14:39:22 cegger Exp $"); 36 37 #include <sys/param.h> 38 #include <sys/buf.h> 39 #include <sys/device.h> 40 #include <sys/conf.h> 41 #include <sys/disklabel.h> 42 #include <sys/disk.h> 43 #include <sys/fcntl.h> 44 #include <sys/ioctl.h> 45 #include <sys/stat.h> 46 #include <sys/systm.h> 47 #include <sys/proc.h> 48 49 #include <dev/ofw/openfirm.h> 50 51 struct ofdisk_softc { 52 struct device sc_dev; 53 int sc_phandle; 54 int sc_unit; 55 int sc_flags; 56 struct disk sc_dk; 57 int sc_ihandle; 58 u_long max_transfer; 59 }; 60 61 /* sc_flags */ 62 #define OFDF_ISFLOPPY 0x01 /* we are a floppy drive */ 63 64 #define OFDISK_FLOPPY_P(of) ((of)->sc_flags & OFDF_ISFLOPPY) 65 66 static int ofdisk_match (device_t, cfdata_t, void *); 67 static void ofdisk_attach (device_t, device_t, void *); 68 69 CFATTACH_DECL(ofdisk, sizeof(struct ofdisk_softc), 70 ofdisk_match, ofdisk_attach, NULL, NULL); 71 72 extern struct cfdriver ofdisk_cd; 73 74 dev_type_open(ofdisk_open); 75 dev_type_close(ofdisk_close); 76 dev_type_read(ofdisk_read); 77 dev_type_write(ofdisk_write); 78 dev_type_ioctl(ofdisk_ioctl); 79 dev_type_strategy(ofdisk_strategy); 80 dev_type_dump(ofdisk_dump); 81 dev_type_size(ofdisk_size); 82 83 const struct bdevsw ofdisk_bdevsw = { 84 ofdisk_open, ofdisk_close, ofdisk_strategy, ofdisk_ioctl, 85 ofdisk_dump, ofdisk_size, D_DISK 86 }; 87 88 const struct cdevsw ofdisk_cdevsw = { 89 ofdisk_open, ofdisk_close, ofdisk_read, ofdisk_write, ofdisk_ioctl, 90 nostop, notty, nopoll, nommap, nokqfilter, D_DISK 91 }; 92 93 static void ofminphys(struct buf *); 94 95 struct dkdriver ofdisk_dkdriver = { ofdisk_strategy, ofminphys }; 96 97 void ofdisk_getdefaultlabel (struct ofdisk_softc *, struct disklabel *); 98 void ofdisk_getdisklabel (dev_t); 99 100 static int 101 ofdisk_match(device_t parent, cfdata_t match, void *aux) 102 { 103 struct ofbus_attach_args *oba = aux; 104 char type[8]; 105 int l; 106 107 if (strcmp(oba->oba_busname, "ofw")) 108 return (0); 109 if ((l = OF_getprop(oba->oba_phandle, "device_type", type, 110 sizeof type - 1)) < 0) 111 return 0; 112 if (l >= sizeof type) 113 return 0; 114 type[l] = 0; 115 return !strcmp(type, "block"); 116 } 117 118 static void 119 ofdisk_attach(device_t parent, device_t self, void *aux) 120 { 121 struct ofdisk_softc *of = device_private(self); 122 struct ofbus_attach_args *oba = aux; 123 char child[64]; 124 int l; 125 126 if ((l = OF_getprop(oba->oba_phandle, "name", child, 127 sizeof child - 1)) < 0) 128 panic("device without name?"); 129 if (l >= sizeof child) 130 l = sizeof child - 1; 131 child[l] = 0; 132 133 of->sc_flags = 0; 134 of->sc_phandle = oba->oba_phandle; 135 of->sc_unit = oba->oba_unit; 136 of->sc_ihandle = 0; 137 disk_init(&of->sc_dk, device_xname(&of->sc_dev), &ofdisk_dkdriver); 138 disk_attach(&of->sc_dk); 139 printf("\n"); 140 141 if (strcmp(child, "floppy") == 0) 142 of->sc_flags |= OFDF_ISFLOPPY; 143 else { 144 /* Discover wedges on this disk. */ 145 dkwedge_discover(&of->sc_dk); 146 } 147 } 148 149 int 150 ofdisk_open(dev_t dev, int flags, int fmt, struct lwp *lwp) 151 { 152 struct ofdisk_softc *of; 153 char path[256]; 154 int error, l, part; 155 156 of = device_lookup_private(&ofdisk_cd, DISKUNIT(dev)); 157 if (of == NULL) 158 return ENXIO; 159 160 part = DISKPART(dev); 161 162 mutex_enter(&of->sc_dk.dk_openlock); 163 164 /* 165 * If there are wedges, and this is not RAW_PART, then we 166 * need to fail. 167 */ 168 if (of->sc_dk.dk_nwedges != 0 && part != RAW_PART) { 169 error = EBUSY; 170 goto bad1; 171 } 172 173 if (!of->sc_ihandle) { 174 if ((l = OF_package_to_path(of->sc_phandle, path, 175 sizeof path - 3)) < 0 || 176 l >= sizeof path - 3) { 177 error = ENXIO; 178 goto bad1; 179 } 180 path[l] = 0; 181 182 /* 183 * XXX This is for the benefit of SCSI/IDE disks that don't 184 * XXX have all their childs in the device tree. 185 * XXX YES, I DO THINK THIS IS A BUG IN OPENFIRMWARE!!! 186 * XXX And yes, this is a very gross hack! 187 * XXX See also ofscsi.c 188 */ 189 if (!strcmp(path + l - 4, "disk")) { 190 path[l++] = '@'; 191 path[l++] = '0' + of->sc_unit; 192 path[l] = 0; 193 } 194 195 strlcat(path, ":0", sizeof(path)); 196 197 if ((of->sc_ihandle = OF_open(path)) == -1) { 198 error = ENXIO; 199 goto bad1; 200 } 201 202 /* 203 * Try to get characteristics of the disk. 204 */ 205 of->max_transfer = OF_call_method_1("max-transfer", 206 of->sc_ihandle, 0); 207 if (of->max_transfer > MAXPHYS) 208 of->max_transfer = MAXPHYS; 209 210 ofdisk_getdisklabel(dev); 211 } 212 213 switch (fmt) { 214 case S_IFCHR: 215 of->sc_dk.dk_copenmask |= 1 << part; 216 break; 217 case S_IFBLK: 218 of->sc_dk.dk_bopenmask |= 1 << part; 219 break; 220 } 221 of->sc_dk.dk_openmask = 222 of->sc_dk.dk_copenmask | of->sc_dk.dk_bopenmask; 223 224 225 error = 0; 226 bad1: 227 mutex_exit(&of->sc_dk.dk_openlock); 228 return (error); 229 } 230 231 int 232 ofdisk_close(dev_t dev, int flags, int fmt, struct lwp *l) 233 { 234 struct ofdisk_softc *of = 235 device_lookup_private(&ofdisk_cd, DISKUNIT(dev)); 236 237 mutex_enter(&of->sc_dk.dk_openlock); 238 239 switch (fmt) { 240 case S_IFCHR: 241 of->sc_dk.dk_copenmask &= ~(1 << DISKPART(dev)); 242 break; 243 case S_IFBLK: 244 of->sc_dk.dk_bopenmask &= ~(1 << DISKPART(dev)); 245 break; 246 } 247 of->sc_dk.dk_openmask = of->sc_dk.dk_copenmask | of->sc_dk.dk_bopenmask; 248 249 #ifdef FIRMWORKSBUGS 250 /* 251 * This is a hack to get the firmware to flush its buffers. 252 */ 253 OF_seek(of->sc_ihandle, 0); 254 #endif 255 if (!of->sc_dk.dk_openmask) { 256 OF_close(of->sc_ihandle); 257 of->sc_ihandle = 0; 258 } 259 260 mutex_exit(&of->sc_dk.dk_openlock); 261 return 0; 262 } 263 264 void 265 ofdisk_strategy(struct buf *bp) 266 { 267 struct ofdisk_softc *of = 268 device_lookup_private(&ofdisk_cd, DISKUNIT(bp->b_dev)); 269 struct partition *p; 270 u_quad_t off; 271 int read; 272 int (*OF_io)(int, void *, int); 273 daddr_t blkno = bp->b_blkno; 274 275 bp->b_resid = 0; 276 if (bp->b_bcount == 0) 277 goto done; 278 279 OF_io = bp->b_flags & B_READ ? OF_read : 280 (int(*)(int, void*, int))OF_write; 281 282 if (DISKPART(bp->b_dev) != RAW_PART) { 283 if (bounds_check_with_label(&of->sc_dk, bp, 0) <= 0) { 284 bp->b_resid = bp->b_bcount; 285 goto done; 286 } 287 p = &of->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)]; 288 blkno = bp->b_blkno + p->p_offset; 289 } 290 291 disk_busy(&of->sc_dk); 292 293 off = (u_quad_t)blkno * DEV_BSIZE; 294 read = -1; 295 do { 296 if (OF_seek(of->sc_ihandle, off) < 0) 297 break; 298 read = OF_io(of->sc_ihandle, bp->b_data, bp->b_bcount); 299 } while (read == -2); 300 301 if (read < 0) { 302 bp->b_error = EIO; 303 bp->b_resid = bp->b_bcount; 304 } else 305 bp->b_resid = bp->b_bcount - read; 306 307 disk_unbusy(&of->sc_dk, bp->b_bcount - bp->b_resid, 308 (bp->b_flags & B_READ)); 309 310 done: 311 biodone(bp); 312 } 313 314 static void 315 ofminphys(struct buf *bp) 316 { 317 struct ofdisk_softc *of = 318 device_lookup_private(&ofdisk_cd, DISKUNIT(bp->b_dev)); 319 320 if (bp->b_bcount > of->max_transfer) 321 bp->b_bcount = of->max_transfer; 322 } 323 324 int 325 ofdisk_read(dev_t dev, struct uio *uio, int flags) 326 { 327 return physio(ofdisk_strategy, NULL, dev, B_READ, ofminphys, uio); 328 } 329 330 int 331 ofdisk_write(dev_t dev, struct uio *uio, int flags) 332 { 333 return physio(ofdisk_strategy, NULL, dev, B_WRITE, ofminphys, uio); 334 } 335 336 int 337 ofdisk_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 338 { 339 struct ofdisk_softc *of = 340 device_lookup_private(&ofdisk_cd, DISKUNIT(dev)); 341 int error; 342 #ifdef __HAVE_OLD_DISKLABEL 343 struct disklabel newlabel; 344 #endif 345 346 switch (cmd) { 347 case DIOCGDINFO: 348 *(struct disklabel *)data = *of->sc_dk.dk_label; 349 return 0; 350 #ifdef __HAVE_OLD_DISKLABEL 351 case ODIOCGDINFO: 352 newlabel = *of->sc_dk.dk_label; 353 if (newlabel.d_npartitions > OLDMAXPARTITIONS) 354 return ENOTTY; 355 memcpy(data, &newlabel, sizeof (struct olddisklabel)); 356 return 0; 357 #endif 358 359 case DIOCGPART: 360 ((struct partinfo *)data)->disklab = of->sc_dk.dk_label; 361 ((struct partinfo *)data)->part = 362 &of->sc_dk.dk_label->d_partitions[DISKPART(dev)]; 363 return 0; 364 365 case DIOCWDINFO: 366 case DIOCSDINFO: 367 #ifdef __HAVE_OLD_DISKLABEL 368 case ODIOCWDINFO: 369 case ODIOCSDINFO: 370 #endif 371 { 372 struct disklabel *lp; 373 374 #ifdef __HAVE_OLD_DISKLABEL 375 if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) { 376 memset(&newlabel, 0, sizeof newlabel); 377 memcpy(&newlabel, data, sizeof (struct olddisklabel)); 378 lp = &newlabel; 379 } else 380 #endif 381 lp = (struct disklabel *)data; 382 383 if ((flag & FWRITE) == 0) 384 return EBADF; 385 386 mutex_enter(&of->sc_dk.dk_openlock); 387 388 error = setdisklabel(of->sc_dk.dk_label, 389 lp, /*of->sc_dk.dk_openmask */0, 390 of->sc_dk.dk_cpulabel); 391 if (error == 0 && cmd == DIOCWDINFO 392 #ifdef __HAVE_OLD_DISKLABEL 393 || xfer == ODIOCWDINFO 394 #endif 395 ) 396 error = writedisklabel(MAKEDISKDEV(major(dev), 397 DISKUNIT(dev), RAW_PART), ofdisk_strategy, 398 of->sc_dk.dk_label, of->sc_dk.dk_cpulabel); 399 400 mutex_exit(&of->sc_dk.dk_openlock); 401 402 return error; 403 } 404 405 case DIOCGDEFLABEL: 406 ofdisk_getdefaultlabel(of, (struct disklabel *)data); 407 return 0; 408 #ifdef __HAVE_OLD_DISKLABEL 409 case DIOCGDEFLABEL: 410 ofdisk_getdefaultlabel(of, &newlabel); 411 if (newlabel.d_npartitions > OLDMAXPARTITIONS) 412 return ENOTTY; 413 memcpy(data, &newlabel, sizeof (struct olddisklabel)); 414 return 0; 415 #endif 416 417 case DIOCAWEDGE: 418 { 419 struct dkwedge_info *dkw = (void *) data; 420 421 if (OFDISK_FLOPPY_P(of)) 422 return (ENOTTY); 423 424 if ((flag & FWRITE) == 0) 425 return (EBADF); 426 427 /* If the ioctl happens here, the parent is us. */ 428 strlcpy(dkw->dkw_parent, device_xname(&of->sc_dev), 429 sizeof(dkw->dkw_parent)); 430 return (dkwedge_add(dkw)); 431 } 432 433 case DIOCDWEDGE: 434 { 435 struct dkwedge_info *dkw = (void *) data; 436 437 if (OFDISK_FLOPPY_P(of)) 438 return (ENOTTY); 439 440 if ((flag & FWRITE) == 0) 441 return (EBADF); 442 443 /* If the ioctl happens here, the parent is us. */ 444 strlcpy(dkw->dkw_parent, device_xname(&of->sc_dev), 445 sizeof(dkw->dkw_parent)); 446 return (dkwedge_del(dkw)); 447 } 448 449 case DIOCLWEDGES: 450 { 451 struct dkwedge_list *dkwl = (void *) data; 452 453 if (OFDISK_FLOPPY_P(of)) 454 return (ENOTTY); 455 456 return (dkwedge_list(&of->sc_dk, dkwl, l)); 457 } 458 459 default: 460 return ENOTTY; 461 } 462 } 463 464 int 465 ofdisk_dump(dev_t dev, daddr_t blkno, void *va, size_t size) 466 { 467 return EINVAL; 468 } 469 470 int 471 ofdisk_size(dev_t dev) 472 { 473 struct ofdisk_softc *of; 474 struct disklabel *lp; 475 int size, part, omask; 476 477 of = device_lookup_private(&ofdisk_cd, DISKUNIT(dev)); 478 if (of == NULL) 479 return ENXIO; 480 481 part = DISKPART(dev); 482 omask = of->sc_dk.dk_openmask & (1 << part); 483 lp = of->sc_dk.dk_label; 484 485 if (omask == 0 && ofdisk_open(dev, 0, S_IFBLK, curlwp) != 0) 486 return -1; 487 488 if (lp->d_partitions[part].p_fstype != FS_SWAP) 489 size = -1; 490 else 491 size = lp->d_partitions[part].p_size * 492 (lp->d_secsize / DEV_BSIZE); 493 494 if (omask == 0 && ofdisk_close(dev, 0, S_IFBLK, curlwp) != 0) 495 return -1; 496 497 return size; 498 } 499 500 void 501 ofdisk_getdefaultlabel(struct ofdisk_softc *of, struct disklabel *lp) 502 { 503 504 memset(lp, 0, sizeof *lp); 505 506 /* 507 * XXX Firmware bug? Asking for block size gives a 508 * XXX ridiculous number! So we use what the boot program 509 * XXX uses. 510 */ 511 lp->d_secsize = DEV_BSIZE; 512 513 lp->d_secperunit = OF_call_method_1("#blocks", 514 of->sc_ihandle, 0); 515 if (lp->d_secperunit == (u_int32_t)-1) 516 lp->d_secperunit = 0x7fffffff; 517 518 lp->d_secpercyl = 1; 519 lp->d_nsectors = 1; 520 lp->d_ntracks = 1; 521 lp->d_ncylinders = lp->d_secperunit; 522 523 lp->d_partitions[RAW_PART].p_offset = 0; 524 lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; 525 lp->d_npartitions = RAW_PART + 1; 526 527 lp->d_magic = DISKMAGIC; 528 lp->d_magic2 = DISKMAGIC; 529 lp->d_checksum = dkcksum(lp); 530 } 531 532 void 533 ofdisk_getdisklabel(dev_t dev) 534 { 535 int unit = DISKUNIT(dev); 536 struct ofdisk_softc *of = 537 device_lookup_private(&ofdisk_cd, unit); 538 struct disklabel *lp = of->sc_dk.dk_label; 539 const char *errmes; 540 int l; 541 542 ofdisk_getdefaultlabel(of, lp); 543 544 /* 545 * Don't read the disklabel on a floppy; simply 546 * assign all partitions the same size/offset as 547 * RAW_PART. (This is essentially what the ISA 548 * floppy driver does, but we don't deal with 549 * density stuff.) 550 */ 551 if (OFDISK_FLOPPY_P(of)) { 552 lp->d_npartitions = MAXPARTITIONS; 553 for (l = 0; l < lp->d_npartitions; l++) { 554 if (l == RAW_PART) 555 continue; 556 /* struct copy */ 557 lp->d_partitions[l] = 558 lp->d_partitions[RAW_PART]; 559 } 560 lp->d_checksum = dkcksum(lp); 561 } else { 562 errmes = readdisklabel(MAKEDISKDEV(major(dev), 563 unit, RAW_PART), ofdisk_strategy, lp, 564 of->sc_dk.dk_cpulabel); 565 if (errmes != NULL) 566 printf("%s: %s\n", device_xname(&of->sc_dev), errmes); 567 } 568 } 569