commit d20373d3bfb8872cae43a8f2804e8a4d7aeb2e73 Author: Conrad Meyer Date: Tue Oct 10 13:43:50 2017 -0700 mem: Hack to allow libkvm to read device registers diff --git a/sys/amd64/amd64/mem.c b/sys/amd64/amd64/mem.c index 067da04dfc4f..9fb90767688f 100644 --- a/sys/amd64/amd64/mem.c +++ b/sys/amd64/amd64/mem.c @@ -73,6 +73,94 @@ __FBSDID("$FreeBSD$"); */ MALLOC_DEFINE(M_MEMDESC, "memdesc", "memory range descriptors"); +static bool +is_word_aligned(u_int len, u_long addr) +{ + return ((len == 8 && (addr & 7) == 0) || (len == 4 && (addr & 3) == 0) + || (len == 2 && (addr & 1) == 0)); +} + +static int +alignmove(void *cp, u_int len, struct uio *uio) +{ + struct iovec *iov; + size_t cnt; + union { + uint64_t u64; + uint32_t u32; + uint16_t u16; + } devmem; + void *memp; + int error; + + switch (len) { + case 8: + memp = &devmem.u64; + break; + case 4: + memp = &devmem.u32; + break; + case 2: + memp = &devmem.u16; + break; + default: + return (EINVAL); + } + + error = 0; + iov = uio->uio_iov; + cnt = iov->iov_len; + if (cnt > len) + cnt = len; + + if (uio->uio_rw == UIO_WRITE) { + if (uio->uio_segflg == UIO_USERSPACE) { + error = copyin(iov->iov_base, memp, cnt); + if (error != 0) + goto out; + } else if (uio->uio_segflg == UIO_SYSSPACE) + bcopy(iov->iov_base, memp, cnt); + + switch (len) { + case 8: + *(volatile uint64_t *)cp = devmem.u64; + break; + case 4: + *(volatile uint32_t *)cp = devmem.u32; + break; + case 2: + *(volatile uint16_t *)cp = devmem.u16; + break; + } + } else { + switch (len) { + case 8: + devmem.u64 = *(volatile uint64_t *)cp; + break; + case 4: + devmem.u32 = *(volatile uint32_t *)cp; + break; + case 2: + devmem.u16 = *(volatile uint16_t *)cp; + break; + } + + if (uio->uio_segflg == UIO_USERSPACE) { + error = copyout(memp, iov->iov_base, cnt); + if (error != 0) + goto out; + } else if (uio->uio_segflg == UIO_SYSSPACE) + bcopy(memp, iov->iov_base, cnt); + } + + iov->iov_base = (char *)iov->iov_base + cnt; + iov->iov_len -= cnt; + uio->uio_resid -= cnt; + uio->uio_offset += cnt; +out: + return (error); +} + /* ARGSUSED */ int memrw(struct cdev *dev, struct uio *uio, int flags) @@ -107,7 +195,10 @@ memrw(struct cdev *dev, struct uio *uio, int flags) */ if (v >= DMAP_MIN_ADDRESS && v < DMAP_MIN_ADDRESS + dmaplimit) { - error = uiomove((void *)v, c, uio); + if (is_word_aligned(c, v)) + error = alignmove((void *)v, c, uio); + else + error = uiomove((void *)v, c, uio); break; } @@ -137,7 +228,10 @@ memrw(struct cdev *dev, struct uio *uio, int flags) case CDEV_MINOR_MEM: if (v < dmaplimit) { vd = PHYS_TO_DMAP(v); - error = uiomove((void *)vd, c, uio); + if (is_word_aligned(c, vd)) + error = alignmove((void *)vd, c, uio); + else + error = uiomove((void *)vd, c, uio); break; } if (v > cpu_getmaxphyaddr()) { @@ -145,7 +239,10 @@ memrw(struct cdev *dev, struct uio *uio, int flags) break; } p = pmap_mapdev(v, PAGE_SIZE); - error = uiomove(p, c, uio); + if (is_word_aligned(c, (u_long)p)) + error = alignmove(p, c, uio); + else + error = uiomove(p, c, uio); pmap_unmapdev((vm_offset_t)p, PAGE_SIZE); break; }