#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/vm_page.h>
#include <machine/vmparam.h>
#include <machine/md_var.h>
int
uiomove_fromphys(vm_page_t ma[], vm_offset_t offset, int n, struct uio *uio)
{
struct iovec *iov;
void *cp, *vaddr;
vm_offset_t page_offset;
size_t cnt;
int error = 0;
int save;
bool mapped;
KASSERT(uio->uio_rw == UIO_READ || uio->uio_rw == UIO_WRITE,
("uiomove_fromphys: mode"));
KASSERT(uio->uio_segflg != UIO_USERSPACE || uio->uio_td == curthread,
("uiomove_fromphys proc"));
KASSERT(uio->uio_resid >= 0,
("%s: uio %p resid underflow", __func__, uio));
save = curthread_pflags_set(TDP_DEADLKTREAT);
mapped = false;
while (n > 0 && uio->uio_resid) {
KASSERT(uio->uio_iovcnt > 0,
("%s: uio %p iovcnt underflow", __func__, uio));
iov = uio->uio_iov;
cnt = iov->iov_len;
if (cnt == 0) {
uio->uio_iov++;
uio->uio_iovcnt--;
continue;
}
if (cnt > n)
cnt = n;
page_offset = offset & PAGE_MASK;
cnt = min(cnt, PAGE_SIZE - page_offset);
if (uio->uio_segflg != UIO_NOCOPY) {
mapped = pmap_map_io_transient(
&ma[offset >> PAGE_SHIFT], &vaddr, 1, true);
cp = (char *)vaddr + page_offset;
}
error = uiomove_step(cp, iov->iov_base, cnt, uio);
if (__predict_false(mapped)) {
pmap_unmap_io_transient(&ma[offset >> PAGE_SHIFT],
&vaddr, 1, true);
mapped = false;
}
if (error != 0)
break;
iov->iov_base = (char *)iov->iov_base + cnt;
iov->iov_len -= cnt;
uio->uio_resid -= cnt;
uio->uio_offset += cnt;
offset += cnt;
n -= cnt;
}
curthread_pflags_restore(save);
return (error);
}
int
uiomove_mem(enum uiomove_mem_req req, struct uio *uio)
{
struct iovec *iov;
void *p, *vd;
ssize_t orig_resid;
vm_prot_t prot;
u_long v;
u_int c;
int error;
error = 0;
orig_resid = uio->uio_resid;
while (uio->uio_resid > 0 && error == 0) {
iov = uio->uio_iov;
if (iov->iov_len == 0) {
uio->uio_iov++;
uio->uio_iovcnt--;
if (uio->uio_iovcnt < 0)
panic("memrw");
continue;
}
v = uio->uio_offset;
c = ulmin(iov->iov_len, PAGE_SIZE - (u_int)(v & PAGE_MASK));
switch (req) {
case UIO_MEM_KMEM:
if (v >= kva_layout.dmap_low &&
v < kva_layout.dmap_high) {
error = uiomove((void *)v, c, uio);
break;
}
switch (uio->uio_rw) {
case UIO_READ:
prot = VM_PROT_READ;
break;
case UIO_WRITE:
prot = VM_PROT_WRITE;
break;
}
if (!kernacc((void *)v, c, prot)) {
error = EFAULT;
break;
}
v = pmap_extract(kernel_pmap, v);
if (v == 0) {
error = EFAULT;
break;
}
case UIO_MEM_MEM:
if (v < dmaplimit) {
vd = PHYS_TO_DMAP(v);
error = uiomove(vd, c, uio);
break;
}
if (v > cpu_getmaxphyaddr()) {
error = EFAULT;
break;
}
p = pmap_mapdev(v, PAGE_SIZE);
error = uiomove(p, c, uio);
pmap_unmapdev(p, PAGE_SIZE);
break;
}
}
if (uio->uio_resid != orig_resid)
error = 0;
return (error);
}