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

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/10/sys/amd64/amd64/mem.c
  stable/10/sys/amd64/amd64/pmap.c
  stable/10/sys/amd64/amd64/trap.c
  stable/10/sys/amd64/include/pmap.h
  stable/10/sys/kern/subr_trap.c
  stable/10/sys/sys/proc.h
  stable/10/sys/vm/vm_fault.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/amd64/amd64/mem.c
==============================================================================
--- stable/10/sys/amd64/amd64/mem.c     Fri Mar 28 15:09:35 2014        
(r263874)
+++ stable/10/sys/amd64/amd64/mem.c     Fri Mar 28 15:38:38 2014        
(r263875)
@@ -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/10/sys/amd64/amd64/pmap.c
==============================================================================
--- stable/10/sys/amd64/amd64/pmap.c    Fri Mar 28 15:09:35 2014        
(r263874)
+++ stable/10/sys/amd64/amd64/pmap.c    Fri Mar 28 15:38:38 2014        
(r263875)
@@ -321,7 +321,7 @@ SYSCTL_INT(_machdep, OID_AUTO, nkpt, CTL
     "Number of kernel page table pages allocated on bootup");
 
 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/10/sys/amd64/amd64/trap.c
==============================================================================
--- stable/10/sys/amd64/amd64/trap.c    Fri Mar 28 15:09:35 2014        
(r263874)
+++ stable/10/sys/amd64/amd64/trap.c    Fri Mar 28 15:38:38 2014        
(r263875)
@@ -788,6 +788,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/10/sys/amd64/include/pmap.h
==============================================================================
--- stable/10/sys/amd64/include/pmap.h  Fri Mar 28 15:09:35 2014        
(r263874)
+++ stable/10/sys/amd64/include/pmap.h  Fri Mar 28 15:38:38 2014        
(r263875)
@@ -368,6 +368,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/10/sys/kern/subr_trap.c
==============================================================================
--- stable/10/sys/kern/subr_trap.c      Fri Mar 28 15:09:35 2014        
(r263874)
+++ stable/10/sys/kern/subr_trap.c      Fri Mar 28 15:38:38 2014        
(r263875)
@@ -155,6 +155,8 @@ userret(struct thread *td, struct trapfr
            ("userret: Returning with %d locks held", td->td_locks));
        KASSERT((td->td_pflags & TDP_NOFAULTING) == 0,
            ("userret: Returning with pagefaults disabled"));
+       KASSERT((td->td_pflags & TDP_DEVMEMIO) == 0,
+           ("userret: Returning with /dev/mem i/o leaked"));
        KASSERT(td->td_no_sleeping == 0,
            ("userret: Returning with sleep disabled"));
        KASSERT(td->td_pinned == 0 || (td->td_pflags & TDP_CALLCHAIN) != 0,

Modified: stable/10/sys/sys/proc.h
==============================================================================
--- stable/10/sys/sys/proc.h    Fri Mar 28 15:09:35 2014        (r263874)
+++ stable/10/sys/sys/proc.h    Fri Mar 28 15:38:38 2014        (r263875)
@@ -424,6 +424,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/10/sys/vm/vm_fault.c
==============================================================================
--- stable/10/sys/vm/vm_fault.c Fri Mar 28 15:09:35 2014        (r263874)
+++ stable/10/sys/vm/vm_fault.c Fri Mar 28 15:38:38 2014        (r263875)
@@ -276,6 +276,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);
        }
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to