svn commit: r263876 - in stable/9/sys: amd64/amd64 amd64/include kern sys vm

Konstantin Belousov kib at FreeBSD.org
Fri Mar 28 15:38:56 UTC 2014


Author: kib
Date: Fri Mar 28 15:38:54 2014
New Revision: 263876
URL: http://svnweb.freebsd.org/changeset/base/263876

Log:
  MFC r263475:
  Fix two issues with /dev/mem access on amd64, both causing kernel page
  faults.
  
  First, for accesses to direct map region should check for the limit by
  which direct map is instantiated.
  
  Second, for accesses to the kernel map, use a new thread private flag
  TDP_DEVMEMIO, which instructs vm_fault() to return error when fault
  happens on the MAP_ENTRY_NOFAULT entry, instead of panicing.
  
  MFC r263498:
  Add change forgotten in r263475.  Make dmaplimit accessible outside
  amd64/pmap.c.

Modified:
  stable/9/sys/amd64/amd64/mem.c
  stable/9/sys/amd64/amd64/pmap.c
  stable/9/sys/amd64/amd64/trap.c
  stable/9/sys/amd64/include/pmap.h
  stable/9/sys/kern/subr_trap.c
  stable/9/sys/sys/proc.h
  stable/9/sys/vm/vm_fault.c
Directory Properties:
  stable/9/sys/   (props changed)
  stable/9/sys/sys/   (props changed)

Modified: stable/9/sys/amd64/amd64/mem.c
==============================================================================
--- stable/9/sys/amd64/amd64/mem.c	Fri Mar 28 15:38:38 2014	(r263875)
+++ stable/9/sys/amd64/amd64/mem.c	Fri Mar 28 15:38:54 2014	(r263876)
@@ -76,14 +76,16 @@ MALLOC_DEFINE(M_MEMDESC, "memdesc", "mem
 int
 memrw(struct cdev *dev, struct uio *uio, int flags)
 {
-	int o;
-	u_long c = 0, v;
 	struct iovec *iov;
-	int error = 0;
+	u_long c, v;
+	int error, o, sflags;
 	vm_offset_t addr, eaddr;
 
 	GIANT_REQUIRED;
 
+	error = 0;
+	c = 0;
+	sflags = curthread_pflags_set(TDP_DEVMEMIO);
 	while (uio->uio_resid > 0 && error == 0) {
 		iov = uio->uio_iov;
 		if (iov->iov_len == 0) {
@@ -98,7 +100,15 @@ memrw(struct cdev *dev, struct uio *uio,
 kmemphys:
 			o = v & PAGE_MASK;
 			c = min(uio->uio_resid, (u_int)(PAGE_SIZE - o));
-			error = uiomove((void *)PHYS_TO_DMAP(v), (int)c, uio);
+			v = PHYS_TO_DMAP(v);
+			if (v < DMAP_MIN_ADDRESS ||
+			    (v > DMAP_MIN_ADDRESS + dmaplimit &&
+			    v <= DMAP_MAX_ADDRESS) ||
+			    pmap_kextract(v) == 0) {
+				error = EFAULT;
+				goto ret;
+			}
+			error = uiomove((void *)v, (int)c, uio);
 			continue;
 		}
 		else if (dev2unit(dev) == CDEV_MINOR_KMEM) {
@@ -119,22 +129,30 @@ kmemphys:
 			addr = trunc_page(v);
 			eaddr = round_page(v + c);
 
-			if (addr < VM_MIN_KERNEL_ADDRESS)
-				return (EFAULT);
-			for (; addr < eaddr; addr += PAGE_SIZE) 
-				if (pmap_extract(kernel_pmap, addr) == 0)
-					return (EFAULT);
-
+			if (addr < VM_MIN_KERNEL_ADDRESS) {
+				error = EFAULT;
+				goto ret;
+			}
+			for (; addr < eaddr; addr += PAGE_SIZE) {
+				if (pmap_extract(kernel_pmap, addr) == 0) {
+					error = EFAULT;
+					goto ret;
+				}
+			}
 			if (!kernacc((caddr_t)(long)v, c,
 			    uio->uio_rw == UIO_READ ? 
-			    VM_PROT_READ : VM_PROT_WRITE))
-				return (EFAULT);
+			    VM_PROT_READ : VM_PROT_WRITE)) {
+				error = EFAULT;
+				goto ret;
+			}
 
 			error = uiomove((caddr_t)(long)v, (int)c, uio);
 			continue;
 		}
 		/* else panic! */
 	}
+ret:
+	curthread_pflags_restore(sflags);
 	return (error);
 }
 

Modified: stable/9/sys/amd64/amd64/pmap.c
==============================================================================
--- stable/9/sys/amd64/amd64/pmap.c	Fri Mar 28 15:38:38 2014	(r263875)
+++ stable/9/sys/amd64/amd64/pmap.c	Fri Mar 28 15:38:54 2014	(r263876)
@@ -210,7 +210,7 @@ vm_offset_t virtual_avail;	/* VA of firs
 vm_offset_t virtual_end;	/* VA of last avail page (end of kernel AS) */
 
 static int ndmpdp;
-static vm_paddr_t dmaplimit;
+vm_paddr_t dmaplimit;
 vm_offset_t kernel_vm_end = VM_MIN_KERNEL_ADDRESS;
 pt_entry_t pg_nx;
 

Modified: stable/9/sys/amd64/amd64/trap.c
==============================================================================
--- stable/9/sys/amd64/amd64/trap.c	Fri Mar 28 15:38:38 2014	(r263875)
+++ stable/9/sys/amd64/amd64/trap.c	Fri Mar 28 15:38:54 2014	(r263876)
@@ -785,6 +785,12 @@ nogo:
 			frame->tf_rip = (long)curpcb->pcb_onfault;
 			return (0);
 		}
+		if ((td->td_pflags & TDP_DEVMEMIO) != 0) {
+			KASSERT(curpcb->pcb_onfault != NULL,
+			    ("/dev/mem without pcb_onfault"));
+			frame->tf_rip = (long)curpcb->pcb_onfault;
+			return (0);
+		}
 		trap_fatal(frame, eva);
 		return (-1);
 	}

Modified: stable/9/sys/amd64/include/pmap.h
==============================================================================
--- stable/9/sys/amd64/include/pmap.h	Fri Mar 28 15:38:38 2014	(r263875)
+++ stable/9/sys/amd64/include/pmap.h	Fri Mar 28 15:38:54 2014	(r263876)
@@ -280,6 +280,7 @@ extern vm_paddr_t phys_avail[];
 extern vm_paddr_t dump_avail[];
 extern vm_offset_t virtual_avail;
 extern vm_offset_t virtual_end;
+extern vm_paddr_t dmaplimit;
 
 #define	pmap_page_get_memattr(m)	((vm_memattr_t)(m)->md.pat_mode)
 #define	pmap_page_is_write_mapped(m)	(((m)->aflags & PGA_WRITEABLE) != 0)

Modified: stable/9/sys/kern/subr_trap.c
==============================================================================
--- stable/9/sys/kern/subr_trap.c	Fri Mar 28 15:38:38 2014	(r263875)
+++ stable/9/sys/kern/subr_trap.c	Fri Mar 28 15:38:54 2014	(r263876)
@@ -139,6 +139,8 @@ userret(struct thread *td, struct trapfr
 	sched_userret(td);
 	KASSERT(td->td_locks == 0,
 	    ("userret: Returning with %d locks held.", td->td_locks));
+	KASSERT((td->td_pflags & TDP_DEVMEMIO) == 0,
+	    ("userret: Returning with /dev/mem i/o leaked"));
 	KASSERT(td->td_vp_reserv == 0,
 	    ("userret: Returning while holding vnode reservation"));
 	KASSERT((td->td_flags & TDF_SBDRY) == 0,

Modified: stable/9/sys/sys/proc.h
==============================================================================
--- stable/9/sys/sys/proc.h	Fri Mar 28 15:38:38 2014	(r263875)
+++ stable/9/sys/sys/proc.h	Fri Mar 28 15:38:54 2014	(r263876)
@@ -425,6 +425,7 @@ do {									\
 #define	TDP_RESETSPUR	0x04000000 /* Reset spurious page fault history. */
 #define	TDP_NERRNO	0x08000000 /* Last errno is already in td_errno */
 #define	TDP_UIOHELD	0x10000000 /* Current uio has pages held in td_ma */
+#define	TDP_DEVMEMIO	0x20000000 /* Accessing memory for /dev/mem */
 
 /*
  * Reasons that the current thread can not be run yet.

Modified: stable/9/sys/vm/vm_fault.c
==============================================================================
--- stable/9/sys/vm/vm_fault.c	Fri Mar 28 15:38:38 2014	(r263875)
+++ stable/9/sys/vm/vm_fault.c	Fri Mar 28 15:38:54 2014	(r263876)
@@ -282,6 +282,10 @@ RetryFault:;
 	map_generation = fs.map->timestamp;
 
 	if (fs.entry->eflags & MAP_ENTRY_NOFAULT) {
+		if ((curthread->td_pflags & TDP_DEVMEMIO) != 0) {
+			vm_map_unlock_read(fs.map);
+			return (KERN_FAILURE);
+		}
 		panic("vm_fault: fault on nofault entry, addr: %lx",
 		    (u_long)vaddr);
 	}


More information about the svn-src-all mailing list