>Number:         155903
>Category:       amd64
>Synopsis:       FreeBSD32 emulation patch to support i386 X11 Server
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-amd64
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Mar 24 05:20:00 UTC 2011
>Closed-Date:
>Last-Modified:
>Originator:     John Wehle
>Release:        8.2
>Organization:
>Environment:
8.2-RELEASE amd64
>Description:
The enclosed lightly tested patch extends the FreeBSD32 emulation
so that the i386 X11 server runs on amd64.  Specifically tested:

  a) i386 pciconf -v -l

  b) i386 X11 Server 1.9.5 using the VESA driver.

Changes:

  1) Fix fd count leakage in the freebsd32_ioctl routines.

     freebsd32_ioctl called fget for a fd and called a subroutine to handle
     each specific ioctl.  It was expected that the subroutine would call
     fdrop when done.  However many of the subroutines would exit out early
     if copyin encountered an error resulting in fdrop never being called.

  2) Extend freebsd32_ioctl to handle MEMRANGE_GET, MEMRANGE_SET, and
     PCIOCGETCONF.

  3) Promote ksyms_map / ksyms_unmap to copyout_map / copyout_unmap
     as discussed on one of the mailing lists.  Necessary in order to
     handle PCIOCGETCONF.

  4) Modify copyout_map / copyout_unmap to handle size == 0.

-- John

>How-To-Repeat:
Try running i386 X11 server on amd64 ... the X11 server will fail
to find any video cards since the PCIOCGETCONF doesn't work from
32 bit binaries on amd64.  It also has problems with mtrr.

You can also try running i386 pciconf -v -l which will fail to find
any hardware.
>Fix:
Apply the supplied patch.

Patch attached with submission follows:

--- ./compat/freebsd32/freebsd32_ioctl.h.ORIGINAL       2010-12-21 
12:09:25.000000000 -0500
+++ ./compat/freebsd32/freebsd32_ioctl.h        2011-03-23 02:26:12.000000000 
-0400
@@ -67,6 +67,51 @@ struct fiodgname_arg32 {
        caddr_t32       buf;
 };
 
+struct mem_range_op32
+{
+       caddr_t32       mo_desc;
+       int             mo_arg[2];
+};
+
+struct pci_conf32 {
+       struct pcisel   pc_sel;         /* domain+bus+slot+function */
+       u_int8_t        pc_hdr;         /* PCI header type */
+       u_int16_t       pc_subvendor;   /* card vendor ID */
+       u_int16_t       pc_subdevice;   /* card device ID, assigned by 
+                                          card vendor */
+       u_int16_t       pc_vendor;      /* chip vendor ID */
+       u_int16_t       pc_device;      /* chip device ID, assigned by 
+                                          chip vendor */
+       u_int8_t        pc_class;       /* chip PCI class */
+       u_int8_t        pc_subclass;    /* chip PCI subclass */
+       u_int8_t        pc_progif;      /* chip PCI programming interface */
+       u_int8_t        pc_revid;       /* chip revision ID */
+       char            pd_name[PCI_MAXNAMELEN + 1];  /* device name */
+       u_int32_t       pd_unit;        /* device unit number */
+};
+
+struct pci_match_conf32 {
+       struct pcisel           pc_sel;         /* domain+bus+slot+function */
+       char                    pd_name[PCI_MAXNAMELEN + 1];  /* device name */
+       u_int32_t               pd_unit;        /* Unit number */
+       u_int16_t               pc_vendor;      /* PCI Vendor ID */
+       u_int16_t               pc_device;      /* PCI Device ID */
+       u_int8_t                pc_class;       /* PCI class */
+       pci_getconf_flags       flags;          /* Matching expression */
+};
+
+struct pci_conf_io32 {
+       u_int32_t               pat_buf_len;    /* pattern buffer length */
+       u_int32_t               num_patterns;   /* number of patterns */
+       caddr_t32               patterns;       /* struct pci_match_conf ptr */
+       u_int32_t               match_buf_len;  /* match buffer length */
+       u_int32_t               num_matches;    /* number of matches returned */
+       caddr_t32               matches;        /* struct pci_conf ptr */
+       u_int32_t               offset;         /* offset into device list */
+       u_int32_t               generation;     /* device list generation */
+       pci_getconf_status      status;         /* request status */
+};
+
 #define        CDIOREADTOCENTRYS_32 _IOWR('c', 5, struct ioc_read_toc_entry32)
 #define        CDIOREADTOCHEADER_32 _IOR('c', 4, struct ioc_toc_header32)
 #define        MDIOCATTACH_32  _IOC(IOC_INOUT, 'm', 0, sizeof(struct 
md_ioctl32) + 4)
@@ -74,5 +119,8 @@ struct fiodgname_arg32 {
 #define        MDIOCQUERY_32   _IOC(IOC_INOUT, 'm', 2, sizeof(struct 
md_ioctl32) + 4)
 #define        MDIOCLIST_32    _IOC(IOC_INOUT, 'm', 3, sizeof(struct 
md_ioctl32) + 4)
 #define        FIODGNAME_32    _IOW('f', 120, struct fiodgname_arg32)
+#define        MEMRANGE_GET32  _IOWR('m', 50, struct mem_range_op32)
+#define        MEMRANGE_SET32  _IOW('m', 51, struct mem_range_op32)
+#define        PCIOCGETCONF_32 _IOWR('p', 5, struct pci_conf_io32)
 
 #endif /* _COMPAT_FREEBSD32_IOCTL_H_ */
--- ./compat/freebsd32/freebsd32_ioctl.c.ORIGINAL       2010-12-21 
12:09:25.000000000 -0500
+++ ./compat/freebsd32/freebsd32_ioctl.c        2011-03-24 00:26:59.000000000 
-0400
@@ -38,7 +38,10 @@ __FBSDID("$FreeBSD: src/sys/compat/freeb
 #include <sys/filio.h>
 #include <sys/file.h>
 #include <sys/ioccom.h>
+#include <sys/malloc.h>
 #include <sys/mdioctl.h>
+#include <sys/memrange.h>
+#include <sys/pciio.h>
 #include <sys/proc.h>
 #include <sys/syscall.h>
 #include <sys/syscallsubr.h>
@@ -47,6 +50,9 @@ __FBSDID("$FreeBSD: src/sys/compat/freeb
 #include <sys/sysproto.h>
 #include <sys/systm.h>
 
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+
 #include <compat/freebsd32/freebsd32.h>
 #include <compat/freebsd32/freebsd32_ioctl.h>
 #include <compat/freebsd32/freebsd32_proto.h>
@@ -55,6 +61,10 @@ __FBSDID("$FreeBSD: src/sys/compat/freeb
 CTASSERT((sizeof(struct md_ioctl32)+4) == 436);
 CTASSERT(sizeof(struct ioc_read_toc_entry32) == 8);
 CTASSERT(sizeof(struct ioc_toc_header32) == 4);
+CTASSERT(sizeof(struct mem_range_op32) == 12);
+CTASSERT(sizeof(struct pci_conf_io32) == 36);
+CTASSERT(sizeof(struct pci_match_conf32) == 44);
+CTASSERT(sizeof(struct pci_conf32) == 44);
 
 
 static int
@@ -70,7 +80,6 @@ freebsd32_ioctl_md(struct thread *td, st
                panic("%s: where is my ioctl data??", __func__);
        if (uap->com & IOC_IN) {
                if ((error = copyin(uap->data, &md32, sizeof(md32)))) {
-                       fdrop(fp, td);
                        return (error);
                }
                CP(md32, mdv, md_version);
@@ -121,7 +130,6 @@ freebsd32_ioctl_md(struct thread *td, st
                CP(mdv, md32, md_fwsectors);
                error = copyout(&md32, uap->data, sizeof(md32));
        }
-       fdrop(fp, td);
        return error;
 }
 
@@ -144,7 +152,6 @@ freebsd32_ioctl_ioc_toc_header(struct th
        CP(toch32, toch, ending_track);
        error = fo_ioctl(fp, CDIOREADTOCHEADER, (caddr_t)&toch,
            td->td_ucred, td);
-       fdrop(fp, td);
        return (error);
 }
 
@@ -175,7 +182,6 @@ freebsd32_ioctl_ioc_read_toc(struct thre
                PTROUT_CP(toce, toce32, data);
                error = copyout(&toce32, uap->data, sizeof(toce32));
        }
-       fdrop(fp, td);
        return error;
 }
 
@@ -192,7 +198,151 @@ freebsd32_ioctl_fiodgname(struct thread 
        CP(fgn32, fgn, len);
        PTRIN_CP(fgn32, fgn, buf);
        error = fo_ioctl(fp, FIODGNAME, (caddr_t)&fgn, td->td_ucred, td);
-       fdrop(fp, td);
+       return (error);
+}
+
+static int
+freebsd32_ioctl_memrange(struct thread *td,
+    struct freebsd32_ioctl_args *uap, struct file *fp)
+{
+       struct mem_range_op mro;
+       struct mem_range_op32 mro32;
+       int error;
+       u_long com;
+
+       if ((error = copyin(uap->data, &mro32, sizeof(mro32))) != 0)
+               return (error);
+
+       PTRIN_CP(mro32, mro, mo_desc);
+       CP(mro32, mro, mo_arg[0]);
+       CP(mro32, mro, mo_arg[1]);
+
+       com = 0;
+       switch (uap->com) {
+       case MEMRANGE_GET32:
+               com = MEMRANGE_GET;
+               break;
+
+       case MEMRANGE_SET32:
+               com = MEMRANGE_SET;
+               break;
+
+       default:
+               panic("%s: unknown MEMRANGE %#x", __func__, uap->com);
+       }
+
+       if ((error = fo_ioctl(fp, com, (caddr_t)&mro, td->td_ucred, td)) != 0)
+               return (error);
+
+       if ( (com & IOC_OUT) ) {
+               CP(mro, mro32, mo_arg[0]);
+               CP(mro, mro32, mo_arg[1]);
+
+               error = copyout(&mro32, uap->data, sizeof(mro32));
+       }
+
+       return (error);
+}
+
+static int
+freebsd32_ioctl_pciocgetconf(struct thread *td,
+    struct freebsd32_ioctl_args *uap, struct file *fp)
+{
+       struct pci_conf_io pci;
+       struct pci_conf_io32 pci32;
+       struct pci_match_conf32 pmc32;
+       struct pci_match_conf32 *pmc32p;
+       struct pci_match_conf pmc;
+       struct pci_match_conf *pmcp;
+       struct pci_conf32 pc32;
+       struct pci_conf32 *pc32p;
+       struct pci_conf pc;
+       struct pci_conf *pcp;
+       u_int32_t i;
+       u_int32_t npat_to_convert;
+       u_int32_t nmatch_to_convert;
+       vm_offset_t addr;
+       int error;
+
+       if ((error = copyin(uap->data, &pci32, sizeof(pci32))) != 0)
+               return (error);
+
+       CP(pci32, pci, num_patterns);
+       CP(pci32, pci, offset);
+       CP(pci32, pci, generation);
+
+       npat_to_convert = pci32.pat_buf_len / sizeof(struct pci_match_conf32);
+       pci.pat_buf_len = npat_to_convert * sizeof(struct pci_match_conf);
+       pci.patterns = NULL;
+       nmatch_to_convert = pci32.match_buf_len / sizeof(struct pci_conf32);
+       pci.match_buf_len = nmatch_to_convert * sizeof(struct pci_conf);
+       pci.matches = NULL;
+
+       if ((error = copyout_map(td, &addr, pci.pat_buf_len)) != 0)
+               goto cleanup;
+       pci.patterns = (struct pci_match_conf *)addr;
+       if ((error = copyout_map(td, &addr, pci.match_buf_len)) != 0)
+               goto cleanup;
+       pci.matches = (struct pci_conf *)addr;
+
+       npat_to_convert = min(npat_to_convert, pci.num_patterns);
+
+       for (i = 0, pmc32p = (struct pci_match_conf32 *)PTRIN(pci32.patterns),
+            pmcp = pci.patterns;
+            i < npat_to_convert; i++, pmc32p++, pmcp++) {
+               if ((error = copyin(pmc32p, &pmc32, sizeof(pmc32))) != 0)
+                       goto cleanup;
+               CP(pmc32,pmc,pc_sel);
+               strlcpy(pmc.pd_name, pmc32.pd_name, sizeof(pmc.pd_name));
+               CP(pmc32,pmc,pd_unit);
+               CP(pmc32,pmc,pc_vendor);
+               CP(pmc32,pmc,pc_device);
+               CP(pmc32,pmc,pc_class);
+               CP(pmc32,pmc,flags);
+               if ((error = copyout(&pmc, pmcp, sizeof(pmc))) != 0)
+                       goto cleanup;
+       }
+
+       if ((error = fo_ioctl(fp, PCIOCGETCONF, (caddr_t)&pci,
+                             td->td_ucred, td)) != 0)
+               goto cleanup;
+
+       nmatch_to_convert = min(nmatch_to_convert, pci.num_matches);
+
+       for (i = 0, pcp = pci.matches,
+            pc32p = (struct pci_conf32 *)PTRIN(pci32.matches);
+            i < nmatch_to_convert; i++, pcp++, pc32p++) {
+               if ((error = copyin(pcp, &pc, sizeof(pc))) != 0)
+                       goto cleanup;
+               CP(pc,pc32,pc_sel);
+               CP(pc,pc32,pc_hdr);
+               CP(pc,pc32,pc_subvendor);
+               CP(pc,pc32,pc_subdevice);
+               CP(pc,pc32,pc_vendor);
+               CP(pc,pc32,pc_device);
+               CP(pc,pc32,pc_class);
+               CP(pc,pc32,pc_subclass);
+               CP(pc,pc32,pc_progif);
+               CP(pc,pc32,pc_revid);
+               strlcpy(pc32.pd_name, pc.pd_name, sizeof(pc32.pd_name));
+               CP(pc,pc32,pd_unit);
+               if ((error = copyout(&pc32, pc32p, sizeof(pc32))) != 0)
+                       goto cleanup;
+       }
+
+       CP(pci, pci32, num_matches);
+       CP(pci, pci32, offset);
+       CP(pci, pci32, generation);
+       CP(pci, pci32, status);
+
+       error = copyout(&pci32, uap->data, sizeof(pci32));
+
+cleanup:
+       if (pci.patterns)
+               copyout_unmap(td, (vm_offset_t)pci.patterns, pci.pat_buf_len);
+       if (pci.matches)
+               copyout_unmap(td, (vm_offset_t)pci.matches, pci.match_buf_len);
+
        return (error);
 }
 
@@ -219,16 +369,29 @@ freebsd32_ioctl(struct thread *td, struc
        case MDIOCDETACH_32:    /* FALLTHROUGH */
        case MDIOCQUERY_32:     /* FALLTHROUGH */
        case MDIOCLIST_32:
-               return freebsd32_ioctl_md(td, uap, fp);
+               error = freebsd32_ioctl_md(td, uap, fp);
+               break;
 
        case CDIOREADTOCENTRYS_32:
-               return freebsd32_ioctl_ioc_read_toc(td, uap, fp);
+               error = freebsd32_ioctl_ioc_read_toc(td, uap, fp);
+               break;
 
        case CDIOREADTOCHEADER_32:
-               return freebsd32_ioctl_ioc_toc_header(td, uap, fp);
+               error = freebsd32_ioctl_ioc_toc_header(td, uap, fp);
+               break;
 
        case FIODGNAME_32:
-               return freebsd32_ioctl_fiodgname(td, uap, fp);
+               error = freebsd32_ioctl_fiodgname(td, uap, fp);
+               break;
+
+       case MEMRANGE_GET32:    /* FALLTHROUGH */
+       case MEMRANGE_SET32:
+               error = freebsd32_ioctl_memrange(td, uap, fp);
+               break;
+
+       case PCIOCGETCONF_32:
+               error = freebsd32_ioctl_pciocgetconf(td, uap, fp);
+               break;
 
        default:
                fdrop(fp, td);
@@ -237,4 +400,7 @@ freebsd32_ioctl(struct thread *td, struc
                PTRIN_CP(*uap, ap, data);
                return ioctl(td, &ap);
        }
+
+       fdrop(fp, td);
+       return error;
 }
--- ./vm/vm_extern.h.ORIGINAL   2010-12-21 12:09:25.000000000 -0500
+++ ./vm/vm_extern.h    2011-03-23 02:26:54.000000000 -0400
@@ -88,5 +88,7 @@ void vm_thread_dispose(struct thread *td
 int vm_thread_new(struct thread *td, int pages);
 void vm_thread_swapin(struct thread *td);
 void vm_thread_swapout(struct thread *td);
+int copyout_map(struct thread *td, vm_offset_t *addr, size_t sz);
+int copyout_unmap(struct thread *td, vm_offset_t addr, size_t sz);
 #endif                         /* _KERNEL */
 #endif                         /* !_VM_EXTERN_H_ */
--- ./vm/vm_glue.c.ORIGINAL     2010-12-21 12:09:25.000000000 -0500
+++ ./vm/vm_glue.c      2011-03-23 02:26:54.000000000 -0400
@@ -67,6 +67,7 @@ __FBSDID("$FreeBSD: src/sys/vm/vm_glue.c
 #include <sys/systm.h>
 #include <sys/limits.h>
 #include <sys/lock.h>
+#include <sys/mman.h>
 #include <sys/mutex.h>
 #include <sys/proc.h>
 #include <sys/resourcevar.h>
@@ -1100,3 +1101,57 @@ swapout(p)
        return (0);
 }
 #endif /* !NO_SWAPPING */
+
+/*
+ * Map some anonymous memory in user space of size sz, rounded up to the page
+ * boundary.
+ */
+int
+copyout_map(struct thread *td, vm_offset_t *addr, size_t sz)
+{
+       struct vmspace *vms = td->td_proc->p_vmspace;
+       int error;
+       vm_size_t size;
+
+       if (sz == 0) {
+               *addr = 0;
+               return 0;
+       }
+
+       /* 
+        * Map somewhere after heap in process memory.
+        */
+       PROC_LOCK(td->td_proc);
+       *addr = round_page((vm_offset_t)vms->vm_daddr + 
+           lim_max(td->td_proc, RLIMIT_DATA));
+       PROC_UNLOCK(td->td_proc);
+
+       /* round size up to page boundry */
+       size = (vm_size_t) round_page(sz);
+    
+       error = vm_mmap(&vms->vm_map, addr, size, PROT_READ | PROT_WRITE, 
+           VM_PROT_ALL, MAP_PRIVATE | MAP_ANON, OBJT_DEFAULT, NULL, 0);
+       
+       return (error);
+}
+
+/*
+ * Unmap memory in user space.
+ */
+int
+copyout_unmap(struct thread *td, vm_offset_t addr, size_t sz)
+{
+       vm_map_t map;
+       vm_size_t size;
+
+       if (sz == 0)
+               return 0;
+
+       map = &td->td_proc->p_vmspace->vm_map;
+       size = (vm_size_t) round_page(sz);      
+
+       if (!vm_map_remove(map, addr, addr + size))
+               return (EINVAL);
+
+       return (0);
+}
--- ./dev/ksyms/ksyms.c.ORIGINAL        2010-12-21 12:09:25.000000000 -0500
+++ ./dev/ksyms/ksyms.c 2011-03-23 02:27:23.000000000 -0400
@@ -360,53 +360,6 @@ ksyms_snapshot(struct tsizes *ts, vm_off
        return (error);
 }
 
-/*
- * Map some anonymous memory in user space of size sz, rounded up to the page
- * boundary.
- */
-static int
-ksyms_map(struct thread *td, vm_offset_t *addr, size_t sz)
-{
-       struct vmspace *vms = td->td_proc->p_vmspace;
-       int error;
-       vm_size_t size;
-
-       
-       /* 
-        * Map somewhere after heap in process memory.
-        */
-       PROC_LOCK(td->td_proc);
-       *addr = round_page((vm_offset_t)vms->vm_daddr + 
-           lim_max(td->td_proc, RLIMIT_DATA));
-       PROC_UNLOCK(td->td_proc);
-
-       /* round size up to page boundry */
-       size = (vm_size_t) round_page(sz);
-    
-       error = vm_mmap(&vms->vm_map, addr, size, PROT_READ | PROT_WRITE, 
-           VM_PROT_ALL, MAP_PRIVATE | MAP_ANON, OBJT_DEFAULT, NULL, 0);
-       
-       return (error);
-}
-
-/*
- * Unmap memory in user space.
- */
-static int
-ksyms_unmap(struct thread *td, vm_offset_t addr, size_t sz)
-{
-       vm_map_t map;
-       vm_size_t size;
-    
-       map = &td->td_proc->p_vmspace->vm_map;
-       size = (vm_size_t) round_page(sz);      
-
-       if (!vm_map_remove(map, addr, addr + size))
-               return (EINVAL);
-
-       return (0);
-}
-
 static void
 ksyms_cdevpriv_dtr(void *data)
 {
@@ -475,7 +428,7 @@ ksyms_open(struct cdev *dev, int flags, 
                total_elf_sz = sizeof(struct ksyms_hdr) + ts.ts_symsz + 
                        ts.ts_strsz; 
 
-               error = ksyms_map(td, &(sc->sc_uaddr), 
+               error = copyout_map(td, &(sc->sc_uaddr), 
                                (vm_size_t) total_elf_sz);
                if (error)
                        break;
@@ -488,7 +441,7 @@ ksyms_open(struct cdev *dev, int flags, 
                }
                
                /* Snapshot failed, unmap the memory and try again */ 
-               (void) ksyms_unmap(td, sc->sc_uaddr, sc->sc_usize);
+               (void) copyout_unmap(td, sc->sc_uaddr, sc->sc_usize);
        }
 
 failed:
@@ -624,7 +577,7 @@ ksyms_close(struct cdev *dev, int flags 
                return (error);
 
        /* Unmap the buffer from the process address space. */
-       error = ksyms_unmap(td, sc->sc_uaddr, sc->sc_usize);
+       error = copyout_unmap(td, sc->sc_uaddr, sc->sc_usize);
 
        devfs_clear_cdevpriv();
 


>Release-Note:
>Audit-Trail:
>Unformatted:
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-amd64
To unsubscribe, send any mail to "[email protected]"

Reply via email to