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