Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 19 Jan 2015 11:02:23 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r277377 - stable/10/sys/amd64/amd64
Message-ID:  <201501191102.t0JB2NV3053423@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Mon Jan 19 11:02:23 2015
New Revision: 277377
URL: https://svnweb.freebsd.org/changeset/base/277377

Log:
  MFC r277051:
  Fix several issues with /dev/mem and /dev/kmem devices on amd64.

Modified:
  stable/10/sys/amd64/amd64/mem.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/amd64/amd64/mem.c
==============================================================================
--- stable/10/sys/amd64/amd64/mem.c	Mon Jan 19 10:58:52 2015	(r277376)
+++ stable/10/sys/amd64/amd64/mem.c	Mon Jan 19 11:02:23 2015	(r277377)
@@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/systm.h>
 #include <sys/uio.h>
 
+#include <machine/md_var.h>
 #include <machine/specialreg.h>
 #include <machine/vmparam.h>
 
@@ -77,13 +78,15 @@ int
 memrw(struct cdev *dev, struct uio *uio, int flags)
 {
 	struct iovec *iov;
-	u_long c, v, vd;
-	int error, o, sflags;
-	vm_offset_t addr, eaddr;
+	void *p;
+	ssize_t orig_resid;
+	u_long v, vd;
+	u_int c;
+	int error, sflags;
 
 	error = 0;
-	c = 0;
 	sflags = curthread_pflags_set(TDP_DEVMEMIO);
+	orig_resid = uio->uio_resid;
 	while (uio->uio_resid > 0 && error == 0) {
 		iov = uio->uio_iov;
 		if (iov->iov_len == 0) {
@@ -93,63 +96,68 @@ memrw(struct cdev *dev, struct uio *uio,
 				panic("memrw");
 			continue;
 		}
-		if (dev2unit(dev) == CDEV_MINOR_MEM) {
-			v = uio->uio_offset;
-kmemphys:
-			o = v & PAGE_MASK;
-			c = min(uio->uio_resid, (u_int)(PAGE_SIZE - o));
-			vd = PHYS_TO_DMAP(v);
-			if (vd < DMAP_MIN_ADDRESS ||
-			    (vd > DMAP_MIN_ADDRESS + dmaplimit &&
-			    vd <= DMAP_MAX_ADDRESS) ||
-			    (pmap_kextract(vd) == 0 && (v & PG_FRAME) != 0)) {
-				error = EFAULT;
-				goto ret;
-			}
-			error = uiomove((void *)vd, (int)c, uio);
-			continue;
-		} else if (dev2unit(dev) == CDEV_MINOR_KMEM) {
-			v = uio->uio_offset;
+		v = uio->uio_offset;
+		c = ulmin(iov->iov_len, PAGE_SIZE - (u_int)(v & PAGE_MASK));
 
-			if (v >= DMAP_MIN_ADDRESS && v < DMAP_MAX_ADDRESS) {
-				v = DMAP_TO_PHYS(v);
-				goto kmemphys;
+		switch (dev2unit(dev)) {
+		case CDEV_MINOR_KMEM:
+			/*
+			 * Since c is clamped to be less or equal than
+			 * PAGE_SIZE, the uiomove() call does not
+			 * access past the end of the direct map.
+			 */
+			if (v >= DMAP_MIN_ADDRESS &&
+			    v < DMAP_MIN_ADDRESS + dmaplimit) {
+				error = uiomove((void *)v, c, uio);
+				break;
 			}
 
-			c = iov->iov_len;
+			if (!kernacc((void *)v, c, uio->uio_rw == UIO_READ ?
+			    VM_PROT_READ : VM_PROT_WRITE)) {
+				error = EFAULT;
+				break;
+			}
 
 			/*
-			 * Make sure that all of the pages are currently
-			 * resident so that we don't create any zero-fill
-			 * pages.
+			 * If the extracted address is not accessible
+			 * through the direct map, then we make a
+			 * private (uncached) mapping because we can't
+			 * depend on the existing kernel mapping
+			 * remaining valid until the completion of
+			 * uiomove().
+			 *
+			 * XXX We cannot provide access to the
+			 * physical page 0 mapped into KVA.
 			 */
-			addr = trunc_page(v);
-			eaddr = round_page(v + c);
-
-			if (addr < VM_MIN_KERNEL_ADDRESS) {
+			v = pmap_extract(kernel_pmap, v);
+			if (v == 0) {
 				error = EFAULT;
-				goto ret;
+				break;
 			}
-			for (; addr < eaddr; addr += PAGE_SIZE) {
-				if (pmap_extract(kernel_pmap, addr) == 0) {
-					error = EFAULT;
-					goto ret;
-				}
+			/* FALLTHROUGH */
+		case CDEV_MINOR_MEM:
+			if (v < dmaplimit) {
+				vd = PHYS_TO_DMAP(v);
+				error = uiomove((void *)vd, c, uio);
+				break;
 			}
-			if (!kernacc((caddr_t)(long)v, c,
-			    uio->uio_rw == UIO_READ ? 
-			    VM_PROT_READ : VM_PROT_WRITE)) {
+			if (v >= (1ULL << cpu_maxphyaddr)) {
 				error = EFAULT;
-				goto ret;
+				break;
 			}
-
-			error = uiomove((caddr_t)(long)v, (int)c, uio);
-			continue;
+			p = pmap_mapdev(v, PAGE_SIZE);
+			error = uiomove(p, c, uio);
+			pmap_unmapdev((vm_offset_t)p, PAGE_SIZE);
+			break;
 		}
-		/* else panic! */
 	}
-ret:
 	curthread_pflags_restore(sflags);
+	/*
+	 * Don't return error if any byte was written.  Read and write
+	 * can return error only if no i/o was performed.
+	 */
+	if (uio->uio_resid != orig_resid)
+		error = 0;
 	return (error);
 }
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201501191102.t0JB2NV3053423>