amd64/155903: FreeBSD32 emulation patch to support i386 X11 Server

John Wehle john at feith.com
Thu Mar 24 05:20:01 UTC 2011


>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:


More information about the freebsd-amd64 mailing list