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