/* * Copyright (c) 1994 John S. Dyson * All rights reserved. * * 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 immediately at the beginning of the file, without modification, * 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. * 3. Absolutely no warranty of function or purpose is made by the author * John S. Dyson. * 4. Modifications may be freely made to this file if the above conditions * are met. */ #include #include #include #include #include #include #include #include #include #include static int physio(cdev_t dev, struct uio *uio, int ioflag) { int i; int error; int saflags; size_t iolen; size_t bcount; caddr_t ubase; struct buf *bp; if (uio->uio_segflg == UIO_USERSPACE) bp = getpbuf_mem(NULL); else bp = getpbuf_kva(NULL); saflags = bp->b_flags; error = 0; /* XXX: sanity check */ if (dev->si_iosize_max < PAGE_SIZE) { kprintf("WARNING: %s si_iosize_max=%d, using MAXPHYS.\n", devtoname(dev), dev->si_iosize_max); dev->si_iosize_max = MAXPHYS; } /* Must be a real uio */ KKASSERT(uio->uio_segflg != UIO_NOCOPY); for (i = 0; i < uio->uio_iovcnt; i++) { while (uio->uio_iov[i].iov_len) { if (uio->uio_rw == UIO_READ) bp->b_cmd = BUF_CMD_READ; else bp->b_cmd = BUF_CMD_WRITE; bp->b_flags = saflags; bcount = uio->uio_iov[i].iov_len; reinitbufbio(bp); /* clear translation cache */ bp->b_bio1.bio_offset = uio->uio_offset; bp->b_bio1.bio_done = biodone_sync; bp->b_bio1.bio_flags |= BIO_SYNC; /* * Setup for mapping the request into kernel memory. * * We can only write as much as fits in a pbuf, * which is MAXPHYS, and no larger then the device's * ability. * * If not using bounce pages the base address of the * user mapping into the pbuf may be offset, further * reducing how much will actually fit in the pbuf. */ if (bcount > dev->si_iosize_max) bcount = dev->si_iosize_max; ubase = uio->uio_iov[i].iov_base; iolen = ((vm_offset_t)ubase) & PAGE_MASK; if (bcount > bp->b_kvasize) bcount = bp->b_kvasize; /* * If we have to use a bounce buffer allocate kernel * memory and copyin/copyout. Otherwise map the * user buffer directly into kernel memory without * copying. */ if (uio->uio_segflg == UIO_USERSPACE) { bp->b_bcount = bcount; if (uio->uio_rw == UIO_WRITE) { error = copyin(ubase, bp->b_data, bcount); if (error) goto doerror; } } else { bp->b_data = uio->uio_iov[i].iov_base; bp->b_bcount = bcount; } dev_dstrategy(dev, &bp->b_bio1); biowait(&bp->b_bio1, "physstr"); iolen = bp->b_bcount - bp->b_resid; if (uio->uio_segflg == UIO_USERSPACE) { if (uio->uio_rw == UIO_READ && iolen) { error = copyout(bp->b_data, ubase, iolen); if (error) { bp->b_flags |= B_ERROR; bp->b_error = error; } } } if (iolen == 0 && !(bp->b_flags & B_ERROR)) goto doerror; /* EOF */ uio->uio_iov[i].iov_len -= iolen; uio->uio_iov[i].iov_base = (char *)uio->uio_iov[i].iov_base + iolen; uio->uio_resid -= iolen; uio->uio_offset += iolen; if (bp->b_flags & B_ERROR) { error = bp->b_error; goto doerror; } } } doerror: relpbuf(bp, NULL); return (error); } int physread(struct dev_read_args *ap) { return(physio(ap->a_head.a_dev, ap->a_uio, ap->a_ioflag)); } int physwrite(struct dev_write_args *ap) { return(physio(ap->a_head.a_dev, ap->a_uio, ap->a_ioflag)); }