1 /* $NetBSD: ofdisk.c,v 1.8 1997/10/08 23:35:41 thorpej 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/param.h> 35 #include <sys/buf.h> 36 #include <sys/device.h> 37 #include <sys/disklabel.h> 38 #include <sys/disk.h> 39 #include <sys/fcntl.h> 40 #include <sys/ioctl.h> 41 #include <sys/stat.h> 42 #include <sys/systm.h> 43 #include <sys/proc.h> 44 45 #include <dev/ofw/openfirm.h> 46 47 struct ofd_softc { 48 struct device sc_dev; 49 int sc_phandle; 50 int sc_unit; 51 int sc_flags; 52 struct disk sc_dk; 53 int sc_ihandle; 54 u_long max_transfer; 55 char sc_name[16]; 56 }; 57 58 /* sc_flags */ 59 #define OFDF_ISFLOPPY 0x01 /* we are a floppy drive */ 60 61 static int ofdprobe __P((struct device *, struct cfdata *, void *)); 62 static void ofdattach __P((struct device *, struct device *, void *)); 63 64 struct cfattach ofdisk_ca = { 65 sizeof(struct ofd_softc), ofdprobe, ofdattach 66 }; 67 68 struct cfdriver ofdisk_cd = { 69 NULL, "ofdisk", DV_DISK 70 }; 71 72 void ofdstrategy __P((struct buf *)); 73 74 struct dkdriver ofdkdriver = { ofdstrategy }; 75 76 void ofdgetdefaultlabel __P((struct ofd_softc *, struct disklabel *)); 77 void ofdgetdisklabel __P((dev_t)); 78 79 static int 80 ofdprobe(parent, match, aux) 81 struct device *parent; 82 struct cfdata *match; 83 void *aux; 84 { 85 struct ofprobe *ofp = aux; 86 char type[8]; 87 int l; 88 89 if ((l = OF_getprop(ofp->phandle, "device_type", type, 90 sizeof type - 1)) < 0) 91 return 0; 92 if (l >= sizeof type) 93 return 0; 94 type[l] = 0; 95 return !strcmp(type, "block"); 96 } 97 98 static void 99 ofdattach(parent, self, aux) 100 struct device *parent, *self; 101 void *aux; 102 { 103 struct ofd_softc *of = (void *)self; 104 struct ofprobe *ofp = aux; 105 char child[64]; 106 int l; 107 108 if ((l = OF_getprop(ofp->phandle, "name", child, 109 sizeof child - 1)) < 0) 110 panic("device without name?"); 111 if (l >= sizeof child) 112 l = sizeof child - 1; 113 child[l] = 0; 114 115 of->sc_flags = 0; 116 of->sc_phandle = ofp->phandle; 117 of->sc_unit = ofp->unit; 118 of->sc_ihandle = 0; 119 of->sc_dk.dk_driver = &ofdkdriver; 120 of->sc_dk.dk_name = of->sc_name; 121 strcpy(of->sc_name, of->sc_dev.dv_xname); 122 disk_attach(&of->sc_dk); 123 dk_establish(&of->sc_dk, self); /* XXX */ 124 printf("\n"); 125 126 if (strcmp(child, "floppy") == 0) 127 of->sc_flags |= OFDF_ISFLOPPY; 128 } 129 130 int 131 ofdopen(dev, flags, fmt, p) 132 dev_t dev; 133 int flags; 134 int fmt; 135 struct proc *p; 136 { 137 int unit = DISKUNIT(dev); 138 struct ofd_softc *of; 139 char path[256]; 140 struct disklabel *lp; 141 int l; 142 143 if (unit >= ofdisk_cd.cd_ndevs) 144 return ENXIO; 145 if (!(of = ofdisk_cd.cd_devs[unit])) 146 return ENXIO; 147 148 if (!of->sc_ihandle) { 149 if ((l = OF_package_to_path(of->sc_phandle, path, 150 sizeof path - 3)) < 0) 151 return ENXIO; 152 if (l >= sizeof path - 3) 153 return ENXIO; 154 path[l] = 0; 155 156 /* 157 * XXX This is for the benefit of SCSI/IDE disks that don't 158 * XXX have all their childs in the device tree. 159 * XXX YES, I DO THINK THIS IS A BUG IN OPENFIRMWARE!!! 160 * XXX And yes, this is a very gross hack! 161 * XXX See also ofscsi.c 162 */ 163 if (!strcmp(path + l - 4, "disk")) { 164 path[l++] = '@'; 165 path[l++] = '0' + of->sc_unit; 166 path[l] = 0; 167 } 168 169 strcat(path, ":0"); 170 171 if (!(of->sc_ihandle = OF_open(path))) 172 return ENXIO; 173 174 /* 175 * Try to get characteristics of the disk. 176 */ 177 of->max_transfer = OF_call_method_1("max-transfer", 178 of->sc_ihandle, 0); 179 if (of->max_transfer > MAXPHYS) 180 of->max_transfer = MAXPHYS; 181 182 ofdgetdisklabel(dev); 183 } 184 185 switch (fmt) { 186 case S_IFCHR: 187 of->sc_dk.dk_copenmask |= 1 << DISKPART(dev); 188 break; 189 case S_IFBLK: 190 of->sc_dk.dk_bopenmask |= 1 << DISKPART(dev); 191 break; 192 } 193 of->sc_dk.dk_openmask = 194 of->sc_dk.dk_copenmask | of->sc_dk.dk_bopenmask; 195 196 return 0; 197 } 198 199 int 200 ofdclose(dev, flags, fmt, p) 201 dev_t dev; 202 int flags; 203 int fmt; 204 struct proc *p; 205 { 206 struct ofd_softc *of = ofdisk_cd.cd_devs[DISKUNIT(dev)]; 207 208 switch (fmt) { 209 case S_IFCHR: 210 of->sc_dk.dk_copenmask &= ~(1 << DISKPART(dev)); 211 break; 212 case S_IFBLK: 213 of->sc_dk.dk_bopenmask &= ~(1 << DISKPART(dev)); 214 break; 215 } 216 of->sc_dk.dk_openmask = of->sc_dk.dk_copenmask | of->sc_dk.dk_bopenmask; 217 218 #ifdef FIRMWORKSBUGS 219 /* 220 * This is a hack to get the firmware to flush its buffers. 221 */ 222 OF_seek(of->sc_ihandle, 0); 223 #endif 224 if (!of->sc_dk.dk_openmask) { 225 OF_close(of->sc_ihandle); 226 of->sc_ihandle = 0; 227 } 228 229 return 0; 230 } 231 232 void 233 ofdstrategy(bp) 234 struct buf *bp; 235 { 236 struct ofd_softc *of = ofdisk_cd.cd_devs[DISKUNIT(bp->b_dev)]; 237 struct partition *p; 238 u_quad_t off; 239 int read; 240 int (*OF_io)(int, void *, int); 241 daddr_t blkno = bp->b_blkno; 242 243 bp->b_resid = 0; 244 if (bp->b_bcount == 0) 245 goto done; 246 247 OF_io = bp->b_flags & B_READ ? OF_read : OF_write; 248 249 if (DISKPART(bp->b_dev) != RAW_PART) { 250 if (bounds_check_with_label(bp, of->sc_dk.dk_label, 0) <= 0) { 251 bp->b_resid = bp->b_bcount; 252 goto done; 253 } 254 p = &of->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)]; 255 blkno = bp->b_blkno + p->p_offset; 256 } 257 258 disk_busy(&of->sc_dk); 259 260 off = (u_quad_t)blkno * DEV_BSIZE; 261 read = -1; 262 do { 263 if (OF_seek(of->sc_ihandle, off) < 0) 264 break; 265 read = OF_io(of->sc_ihandle, bp->b_data, bp->b_bcount); 266 } while (read == -2); 267 268 if (read < 0) { 269 bp->b_error = EIO; 270 bp->b_flags |= B_ERROR; 271 bp->b_resid = bp->b_bcount; 272 } else 273 bp->b_resid = bp->b_bcount - read; 274 275 disk_unbusy(&of->sc_dk, bp->b_bcount - bp->b_resid); 276 277 done: 278 biodone(bp); 279 } 280 281 static void 282 ofminphys(bp) 283 struct buf *bp; 284 { 285 struct ofd_softc *of = ofdisk_cd.cd_devs[DISKUNIT(bp->b_dev)]; 286 287 if (bp->b_bcount > of->max_transfer) 288 bp->b_bcount = of->max_transfer; 289 } 290 291 int 292 ofdread(dev, uio) 293 dev_t dev; 294 struct uio *uio; 295 { 296 return physio(ofdstrategy, NULL, dev, B_READ, ofminphys, uio); 297 } 298 299 int 300 ofdwrite(dev, uio) 301 dev_t dev; 302 struct uio *uio; 303 { 304 return physio(ofdstrategy, NULL, dev, B_WRITE, ofminphys, uio); 305 } 306 307 int 308 ofdioctl(dev, cmd, data, flag, p) 309 dev_t dev; 310 u_long cmd; 311 caddr_t data; 312 int flag; 313 struct proc *p; 314 { 315 struct ofd_softc *of = ofdisk_cd.cd_devs[DISKUNIT(dev)]; 316 int error; 317 318 switch (cmd) { 319 case DIOCGDINFO: 320 *(struct disklabel *)data = *of->sc_dk.dk_label; 321 return 0; 322 323 case DIOCGPART: 324 ((struct partinfo *)data)->disklab = of->sc_dk.dk_label; 325 ((struct partinfo *)data)->part = 326 &of->sc_dk.dk_label->d_partitions[DISKPART(dev)]; 327 return 0; 328 329 case DIOCWDINFO: 330 case DIOCSDINFO: 331 if ((flag & FWRITE) == 0) 332 return EBADF; 333 334 error = setdisklabel(of->sc_dk.dk_label, 335 (struct disklabel *)data, /*of->sc_dk.dk_openmask */0, 336 of->sc_dk.dk_cpulabel); 337 if (error == 0 && cmd == DIOCWDINFO) 338 error = writedisklabel(MAKEDISKDEV(major(dev), 339 DISKUNIT(dev), RAW_PART), ofdstrategy, 340 of->sc_dk.dk_label, of->sc_dk.dk_cpulabel); 341 342 return error; 343 344 case DIOCGDEFLABEL: 345 ofdgetdefaultlabel(of, (struct disklabel *)data); 346 return 0; 347 348 default: 349 return ENOTTY; 350 } 351 } 352 353 int 354 ofddump(dev, blkno, va, size) 355 dev_t dev; 356 daddr_t blkno; 357 caddr_t va; 358 size_t size; 359 { 360 return EINVAL; 361 } 362 363 int 364 ofdsize(dev) 365 dev_t dev; 366 { 367 struct ofd_softc *of; 368 struct disklabel *lp; 369 int size, part, omask, unit; 370 371 unit = DISKUNIT(dev); 372 if (unit >= ofdisk_cd.cd_ndevs || 373 (of = ofdisk_cd.cd_devs[unit]) == NULL) 374 return -1; 375 376 part = DISKPART(dev); 377 omask = of->sc_dk.dk_openmask & (1 << part); 378 lp = of->sc_dk.dk_label; 379 380 if (omask == 0 && ofdopen(dev, 0, S_IFBLK, curproc) != 0) 381 return -1; 382 383 if (lp->d_partitions[part].p_fstype != FS_SWAP) 384 size = -1; 385 else 386 size = lp->d_partitions[part].p_size * 387 (lp->d_secsize / DEV_BSIZE); 388 389 if (omask == 0 && ofdclose(dev, 0, S_IFBLK, curproc) != 0) 390 return -1; 391 392 return size; 393 } 394 395 void 396 ofdgetdefaultlabel(of, lp) 397 struct ofd_softc *of; 398 struct disklabel *lp; 399 { 400 401 bzero(lp, sizeof *lp); 402 403 /* 404 * XXX Firmware bug? Asking for block size gives a 405 * XXX rediculous number! So we use what the boot program 406 * XXX uses. 407 */ 408 lp->d_secsize = DEV_BSIZE; 409 410 lp->d_secperunit = OF_call_method_1("#blocks", 411 of->sc_ihandle, 0); 412 if (lp->d_secperunit == (u_int32_t)-1) 413 lp->d_secperunit = 0x7fffffff; 414 415 lp->d_secpercyl = 1; 416 lp->d_nsectors = 1; 417 lp->d_ntracks = 1; 418 lp->d_ncylinders = lp->d_secperunit; 419 420 lp->d_partitions[RAW_PART].p_offset = 0; 421 lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; 422 lp->d_npartitions = RAW_PART + 1; 423 424 lp->d_magic = DISKMAGIC; 425 lp->d_magic2 = DISKMAGIC; 426 lp->d_checksum = dkcksum(lp); 427 } 428 429 void 430 ofdgetdisklabel(dev) 431 dev_t dev; 432 { 433 int unit = DISKUNIT(dev); 434 struct ofd_softc *of = ofdisk_cd.cd_devs[unit]; 435 struct disklabel *lp = of->sc_dk.dk_label; 436 char *errmes; 437 int l; 438 439 ofdgetdefaultlabel(of, lp); 440 441 /* 442 * Don't read the disklabel on a floppy; simply 443 * assign all partitions the same size/offset as 444 * RAW_PART. (This is essentially what the ISA 445 * floppy driver does, but we don't deal with 446 * density stuff.) 447 */ 448 if (of->sc_flags & OFDF_ISFLOPPY) { 449 lp->d_npartitions = MAXPARTITIONS; 450 for (l = 0; l < lp->d_npartitions; l++) { 451 if (l == RAW_PART) 452 continue; 453 /* struct copy */ 454 lp->d_partitions[l] = 455 lp->d_partitions[RAW_PART]; 456 } 457 lp->d_checksum = dkcksum(lp); 458 } else { 459 errmes = readdisklabel(MAKEDISKDEV(major(dev), 460 unit, RAW_PART), ofdstrategy, lp, 461 of->sc_dk.dk_cpulabel); 462 if (errmes != NULL) 463 printf("%s: %s\n", errmes); 464 } 465 } 466