/*- * Copyright (C) 1997,1998 Julian Elischer. All rights reserved. * julian@freebsd.org * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: slice_device.c,v 1.6 1998/06/07 18:44:03 sos Exp $ */ #define DIAGNOSTIC 1 #include "opt_hw_wdog.h" #include #include #include /* SYSINIT stuff */ #include /* cdevsw stuff */ #include /* malloc region definitions */ #include /* bufs for describing IO */ #include /* file open modes etc. */ #include /* standard queue macros */ #include /* S_IFBLK, S_IFMT etc. */ #include /* DEVFS defintitions */ #include /* temporary location */ #include #include #include /* Function prototypes (these should all be static except for slicenew()) */ static d_open_t slcdevopen; static d_read_t slcdevread; static d_write_t slcdevwrite; static d_close_t slcdevclose; static d_ioctl_t slcdevioctl; static d_dump_t slcdevdump; static d_psize_t slcdevsize; static d_strategy_t slcdevstrategy; #define BDEV_MAJOR 14 #define CDEV_MAJOR 20 static struct cdevsw slice_cdevsw = { slcdevopen, slcdevclose, slcdevread, slcdevwrite, slcdevioctl, nostop, nullreset, nodevtotty, seltrue, nommap, slcdevstrategy, "slice", NULL, -1, slcdevdump, slcdevsize, D_DISK, 0, -1 }; #define UNIT_HASH_SIZE 64 LIST_HEAD(slice_bucket, slice) hash_table[UNIT_HASH_SIZE - 1]; /* * Now for some driver initialisation. Occurs ONCE during boot (very early). */ static void slice_drvinit(void *unused) { int i; /* * add bdevsw and cdevsw entries */ cdevsw_add_generic(BDEV_MAJOR, CDEV_MAJOR, &slice_cdevsw); /* * clear out the hash table */ for (i = 0; i < UNIT_HASH_SIZE; i++) { LIST_INIT(hash_table + i); } } SYSINIT(slicedev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR, slice_drvinit, NULL); static int nextunit = 0; void slice_add_device(sl_p slice) { int unit = nextunit++; char *name = slice->name; RR; slice->minor = makedev(0, (((unit << 8) & 0xffff0000) | (unit & 0x000000ff))); /* * put it on the hash chain for it's bucket so we can find it again * later. */ LIST_INSERT_HEAD(hash_table + (slice->minor % UNIT_HASH_SIZE), slice, hash_list); /* * Add an entry in the devfs for it. Possibly should happen later. */ slice->devfs_ctoken = devfs_add_devswf(&slice_cdevsw, unit, DV_CHR, UID_ROOT, GID_OPERATOR, 0600, "r%s", name ? name : "-"); slice->devfs_btoken = devfs_add_devswf(&slice_cdevsw, unit, DV_BLK, UID_ROOT, GID_OPERATOR, 0600, "%s", name ? name : "-"); /* XXX link this node into upper list of caller */ } /* * Given a minor number, find the slice which the operations are destined. * When DEVFS DDEV devices are enabled this is bypassed entirely. */ static struct slice * minor_to_slice(unsigned int minor) { int hash = minor % UNIT_HASH_SIZE; struct slice *slice; slice = (hash_table + hash)->lh_first; while (slice) { if (slice->minor == minor) { return (slice); } slice = slice->hash_list.le_next; } return (NULL); } int slcdevioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc * p) { sl_p slice = minor_to_slice(minor(dev)); int error = 0; RR; /* * Look for only some generic "inherrited" ioctls that apply to all * disk-like devices otherwise pass it down to the previous handler */ switch (cmd) { /* * At present there are none, but eventually there would be * something that returns the basic partition parameters. * Whether this would be in the form of a disklabel or * similar I have not yet decided. */ default: if (slice->handler_down->ioctl) { error = (*slice->handler_down->ioctl) (slice->private_down, cmd, data, flag, p); } else { error = ENOTTY; } if (error) { /* * If no disklabel was returned, let's make * up something that will satisfy the system's * need for a disklabel to mount an ffs on. * Don't overwrite error unless we get a dummy. * let the called routine decide * if it can handle any ioctl. */ if (dkl_dummy_ioctl(slice, cmd, data, flag, p) == 0) { error = 0; } } break; } return (error); } /* * You also need read, write, open, close routines. This should get you * started. * The open MIGHT allow the caller to proceed if it is a READ * mode, and it is open at a higher layer. * All Accesses would have to be checked for READ * as the system doesn't enforce this at this time. */ static int slcdevopen(dev_t dev, int flags, int mode, struct proc * p) { sl_p slice = minor_to_slice(minor(dev)); int error; RR; if (slice == NULL) return (ENXIO); #if 1 /* the hack */ if ((mode & S_IFMT) == S_IFBLK) { /* * XXX Because a mount -u does not re-open the device * The hack here, is to always open block devices * in full read/write mode. Eventually, if DEVFS * becomes ubiquitous, VOP to do a file upgrade * might be implemented. Other Filesystems need * not implement it.. * THIS SHOULD BE DONE IN slice_device.c */ flags |= FWRITE; } #endif /* the hack */ return (sliceopen(slice, flags, mode, p, SLW_DEVICE)); } static int slcdevclose(dev_t dev, int flags, int mode, struct proc * p) { sl_p slice = minor_to_slice(minor(dev)); RR; #ifdef DIAGNOSTICX if ((flags & (FWRITE | FREAD)) != 0) { printf("sliceclose called with non 0 flags\n"); } #endif /* * Close is just an open for non-read/nonwrite in this context. */ sliceopen(slice, 0, mode, p, SLW_DEVICE); return(0); } static int slcdevsize(dev_t dev) { sl_p slice = minor_to_slice(minor(dev)); RR; if (slice == NULL) return (-1); #if 0 return (slice->limits.slicesize / slice->limits.blksize); #else return (slice->limits.slicesize / 512); #endif } static int slcdevread(dev_t dev, struct uio *uio, int ioflag) { return (physio(slcdevstrategy, NULL, dev, 1, minphys, uio)); } static int slcdevwrite(dev_t dev, struct uio *uio, int ioflag) { return (physio(slcdevstrategy, NULL, dev, 0, minphys, uio)); } static dev_t cdevnum, bdevnum; /* * Read/write routine for a buffer. Finds the proper unit, range checks * arguments, and schedules the transfer. Does not wait for the transfer to * complete. Multi-page transfers are supported. All I/O requests must be a * multiple of a sector in length. */ void slcdevstrategy(struct buf * bp) { sl_p slice = minor_to_slice(minor(bp->b_dev)); u_int64_t start, end; u_int32_t blksize; daddr_t blkno; int s; RR; if (slice == NULL) { bp->b_error = ENXIO; goto bad; } blksize = slice->limits.blksize; /* Check we are going to be able to do this kind of transfer */ /* Check the start point too if DEV_BSIZE != reallity */ if (bp->b_blkno < 0) { Debugger("Slice code got negative blocknumber"); bp->b_error = EINVAL; goto bad; } start = (u_int64_t)bp->b_blkno * DEV_BSIZE; if (blksize != DEV_BSIZE) { if ((start % blksize) != 0) { Debugger("slice: request not on block boundary."); bp->b_error = EINVAL; goto bad; } blkno = start / blksize; } else { blkno = bp->b_blkno; } if ((bp->b_bcount % blksize) != 0) { printf("bcount = %d, blksize= %d(%d)\n", bp->b_bcount, blksize, slice->limits.blksize); Debugger("slice: request not multile of blocksize."); bp->b_error = EINVAL; goto bad; } /* * Do bounds checking, adjust transfer, and set b_pblkno. */ bp->b_pblkno = blkno; end = start + (u_int64_t)bp->b_bcount; /* first byte BEYOND the IO */ /* * Handle the cases near or beyond the end of the slice. Assumes IO * is < 2^63 bytes long. (pretty safe) */ if (end > slice->limits.slicesize) { int64_t size; size = slice->limits.slicesize - start; /* * if exactly on end of slice, return EOF */ if ((size == 0) && (bp->b_flags & B_READ)) { printf("slice: at end of slice."); bp->b_resid = bp->b_bcount; goto done; } if (size <= 0) { printf("slice: beyond end of slice."); bp->b_error = EINVAL; goto bad; } bp->b_bcount = size; } sliceio(slice, bp, SLW_DEVICE); return; done: s = splbio(); /* toss transfer, we're done early */ biodone(bp); splx(s); return; bad: bp->b_flags |= B_ERROR; goto done; } void slice_remove_device(sl_p slice) { /* * Remove the devfs entry, which revokes the vnode etc. XXX if * handler has madde more, we should tell it too. e.g. floppy driver * does this. */ RR; devfs_remove_dev(slice->devfs_btoken); devfs_remove_dev(slice->devfs_ctoken); /* * Remove it from the hashtable. */ LIST_REMOVE(slice, hash_list); } static int slcdevdump(dev_t dev) { sl_p slice = minor_to_slice(minor(dev)); static int slcdoingdump = 0; int32_t num, nblocks, lo; RR; if (!slice || !(slice->flags & SLF_OPEN_DEV_WR) || !slice->handler_down->dump) return (ENXIO); /* Toss any characters present prior to dump. */ while (cncheckc() != -1) ; if (slcdoingdump) return (EFAULT); num = (int32_t)(Maxmem * PAGE_SIZE / slice->limits.blksize); nblocks = (int32_t)(slice->limits.slicesize / slice->limits.blksize); lo = dumplo * DEV_BSIZE / slice->limits.blksize; if (lo < 0 || lo + num > nblocks) return (EINVAL); slcdoingdump = 1; return (*slice->handler_down->dump)(slice->private_down, lo, num); }