1*0616c1c3SMichael Corcoran /*
2*0616c1c3SMichael Corcoran  * CDDL HEADER START
3*0616c1c3SMichael Corcoran  *
4*0616c1c3SMichael Corcoran  * The contents of this file are subject to the terms of the
5*0616c1c3SMichael Corcoran  * Common Development and Distribution License (the "License").
6*0616c1c3SMichael Corcoran  * You may not use this file except in compliance with the License.
7*0616c1c3SMichael Corcoran  *
8*0616c1c3SMichael Corcoran  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*0616c1c3SMichael Corcoran  * or http://www.opensolaris.org/os/licensing.
10*0616c1c3SMichael Corcoran  * See the License for the specific language governing permissions
11*0616c1c3SMichael Corcoran  * and limitations under the License.
12*0616c1c3SMichael Corcoran  *
13*0616c1c3SMichael Corcoran  * When distributing Covered Code, include this CDDL HEADER in each
14*0616c1c3SMichael Corcoran  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*0616c1c3SMichael Corcoran  * If applicable, add the following below this CDDL HEADER, with the
16*0616c1c3SMichael Corcoran  * fields enclosed by brackets "[]" replaced with your own identifying
17*0616c1c3SMichael Corcoran  * information: Portions Copyright [yyyy] [name of copyright owner]
18*0616c1c3SMichael Corcoran  *
19*0616c1c3SMichael Corcoran  * CDDL HEADER END
20*0616c1c3SMichael Corcoran  */
21*0616c1c3SMichael Corcoran /*
22*0616c1c3SMichael Corcoran  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23*0616c1c3SMichael Corcoran  * Use is subject to license terms.
24*0616c1c3SMichael Corcoran  */
25*0616c1c3SMichael Corcoran 
26*0616c1c3SMichael Corcoran #include <sys/types.h>
27*0616c1c3SMichael Corcoran #include <sys/errno.h>
28*0616c1c3SMichael Corcoran #include <sys/mman.h>
29*0616c1c3SMichael Corcoran #include <sys/cred.h>
30*0616c1c3SMichael Corcoran #include <sys/model.h>
31*0616c1c3SMichael Corcoran #include <sys/vnode.h>
32*0616c1c3SMichael Corcoran #include <sys/systm.h>
33*0616c1c3SMichael Corcoran #include <sys/kmem.h>
34*0616c1c3SMichael Corcoran #include <sys/file.h>
35*0616c1c3SMichael Corcoran #include <sys/vfs.h>
36*0616c1c3SMichael Corcoran #include <sys/sysmacros.h>
37*0616c1c3SMichael Corcoran #include <sys/mmapobj.h>
38*0616c1c3SMichael Corcoran 
39*0616c1c3SMichael Corcoran /*
40*0616c1c3SMichael Corcoran  * We will "allocate" this many mmapobj_result_t segments on the stack
41*0616c1c3SMichael Corcoran  * in an attempt to avoid the need to call kmem_alloc. This value should
42*0616c1c3SMichael Corcoran  * cover 99% of the known ELF libraries as well as AOUT (4.x) libraries.
43*0616c1c3SMichael Corcoran  */
44*0616c1c3SMichael Corcoran #define	MOBJ_STACK_SEGS	6
45*0616c1c3SMichael Corcoran 
46*0616c1c3SMichael Corcoran static void
mmapobj_copy_64to32(mmapobj_result_t * source,mmapobj_result32_t * dest,int num)47*0616c1c3SMichael Corcoran mmapobj_copy_64to32(mmapobj_result_t *source, mmapobj_result32_t *dest, int num)
48*0616c1c3SMichael Corcoran {
49*0616c1c3SMichael Corcoran 	int i;
50*0616c1c3SMichael Corcoran 
51*0616c1c3SMichael Corcoran 	for (i = 0; i < num; i++) {
52*0616c1c3SMichael Corcoran 		dest[i].mr_addr = (caddr32_t)(uintptr_t)source[i].mr_addr;
53*0616c1c3SMichael Corcoran 		dest[i].mr_msize = (size32_t)source[i].mr_msize;
54*0616c1c3SMichael Corcoran 		dest[i].mr_fsize = (size32_t)source[i].mr_fsize;
55*0616c1c3SMichael Corcoran 		dest[i].mr_offset = (size32_t)source[i].mr_offset;
56*0616c1c3SMichael Corcoran 		dest[i].mr_prot = source[i].mr_prot;
57*0616c1c3SMichael Corcoran 		dest[i].mr_flags = source[i].mr_flags;
58*0616c1c3SMichael Corcoran 	}
59*0616c1c3SMichael Corcoran }
60*0616c1c3SMichael Corcoran 
61*0616c1c3SMichael Corcoran int
mmapobjsys(int fd,uint_t flags,mmapobj_result_t * storage,uint_t * elements,void * arg)62*0616c1c3SMichael Corcoran mmapobjsys(int fd, uint_t flags, mmapobj_result_t *storage,
63*0616c1c3SMichael Corcoran     uint_t *elements, void *arg)
64*0616c1c3SMichael Corcoran {
65*0616c1c3SMichael Corcoran 	uint_t num_mapped;
66*0616c1c3SMichael Corcoran 	uint_t num_in;
67*0616c1c3SMichael Corcoran 	int error;
68*0616c1c3SMichael Corcoran 	int old_error;
69*0616c1c3SMichael Corcoran 	size_t padding = 0;
70*0616c1c3SMichael Corcoran 	mmapobj_result_t stack_mr[MOBJ_STACK_SEGS];
71*0616c1c3SMichael Corcoran 	mmapobj_result_t *mrp = stack_mr;
72*0616c1c3SMichael Corcoran 	struct file *fp;
73*0616c1c3SMichael Corcoran 	struct vnode *vp;
74*0616c1c3SMichael Corcoran 	model_t model;
75*0616c1c3SMichael Corcoran 	int convert_64to32 = 0;
76*0616c1c3SMichael Corcoran 	uint_t alloc_num = 0;
77*0616c1c3SMichael Corcoran 
78*0616c1c3SMichael Corcoran 
79*0616c1c3SMichael Corcoran 	/* Verify flags */
80*0616c1c3SMichael Corcoran 	if ((flags & ~MMOBJ_ALL_FLAGS) != 0) {
81*0616c1c3SMichael Corcoran 		return (set_errno(EINVAL));
82*0616c1c3SMichael Corcoran 	}
83*0616c1c3SMichael Corcoran 
84*0616c1c3SMichael Corcoran 	if (((flags & MMOBJ_PADDING) == 0) && arg != NULL) {
85*0616c1c3SMichael Corcoran 		return (set_errno(EINVAL));
86*0616c1c3SMichael Corcoran 	}
87*0616c1c3SMichael Corcoran 
88*0616c1c3SMichael Corcoran 	fp = getf(fd);
89*0616c1c3SMichael Corcoran 	if (fp == NULL) {
90*0616c1c3SMichael Corcoran 		return (set_errno(EBADF));
91*0616c1c3SMichael Corcoran 	}
92*0616c1c3SMichael Corcoran 	vp = fp->f_vnode;
93*0616c1c3SMichael Corcoran 
94*0616c1c3SMichael Corcoran 	if ((fp->f_flag & FREAD) == 0) {
95*0616c1c3SMichael Corcoran 		error = EACCES;
96*0616c1c3SMichael Corcoran 		goto out;
97*0616c1c3SMichael Corcoran 	}
98*0616c1c3SMichael Corcoran 
99*0616c1c3SMichael Corcoran 	error = copyin(elements, &num_mapped, sizeof (uint_t));
100*0616c1c3SMichael Corcoran 	if (error) {
101*0616c1c3SMichael Corcoran 		error = EFAULT;
102*0616c1c3SMichael Corcoran 		goto out;
103*0616c1c3SMichael Corcoran 	}
104*0616c1c3SMichael Corcoran 
105*0616c1c3SMichael Corcoran 	num_in = num_mapped;
106*0616c1c3SMichael Corcoran 	model = get_udatamodel();
107*0616c1c3SMichael Corcoran 	if (model != DATAMODEL_NATIVE) {
108*0616c1c3SMichael Corcoran 		ASSERT(model == DATAMODEL_ILP32);
109*0616c1c3SMichael Corcoran 		convert_64to32 = 1;
110*0616c1c3SMichael Corcoran 	}
111*0616c1c3SMichael Corcoran 
112*0616c1c3SMichael Corcoran 	if (flags & MMOBJ_PADDING) {
113*0616c1c3SMichael Corcoran 		if (convert_64to32) {
114*0616c1c3SMichael Corcoran 			size32_t padding32;
115*0616c1c3SMichael Corcoran 			error = copyin(arg, &padding32, sizeof (padding32));
116*0616c1c3SMichael Corcoran 			padding = padding32;
117*0616c1c3SMichael Corcoran 		} else {
118*0616c1c3SMichael Corcoran 			error = copyin(arg, &padding, sizeof (padding));
119*0616c1c3SMichael Corcoran 		}
120*0616c1c3SMichael Corcoran 		if (error) {
121*0616c1c3SMichael Corcoran 			error = EFAULT;
122*0616c1c3SMichael Corcoran 			goto out;
123*0616c1c3SMichael Corcoran 		}
124*0616c1c3SMichael Corcoran 
125*0616c1c3SMichael Corcoran 		/*
126*0616c1c3SMichael Corcoran 		 * Need to catch overflow here for the 64 bit case.  For the
127*0616c1c3SMichael Corcoran 		 * 32 bit case, overflow would round up to 4G which would
128*0616c1c3SMichael Corcoran 		 * not be able to fit in any address space and thus ENOMEM
129*0616c1c3SMichael Corcoran 		 * would be returned after calling into mmapobj.
130*0616c1c3SMichael Corcoran 		 */
131*0616c1c3SMichael Corcoran 		if (padding) {
132*0616c1c3SMichael Corcoran 			padding = P2ROUNDUP(padding, PAGESIZE);
133*0616c1c3SMichael Corcoran 			if (padding == 0) {
134*0616c1c3SMichael Corcoran 				error = ENOMEM;
135*0616c1c3SMichael Corcoran 				goto out;
136*0616c1c3SMichael Corcoran 			}
137*0616c1c3SMichael Corcoran 		}
138*0616c1c3SMichael Corcoran 		/* turn off padding if no bytes were requested */
139*0616c1c3SMichael Corcoran 		if (padding == 0) {
140*0616c1c3SMichael Corcoran 			flags = flags & (~MMOBJ_PADDING);
141*0616c1c3SMichael Corcoran 		}
142*0616c1c3SMichael Corcoran 	}
143*0616c1c3SMichael Corcoran 
144*0616c1c3SMichael Corcoran 	if (num_mapped > MOBJ_STACK_SEGS) {
145*0616c1c3SMichael Corcoran 		num_mapped = MOBJ_STACK_SEGS;
146*0616c1c3SMichael Corcoran 	}
147*0616c1c3SMichael Corcoran retry:
148*0616c1c3SMichael Corcoran 	error = mmapobj(vp, flags, mrp, &num_mapped, padding, fp->f_cred);
149*0616c1c3SMichael Corcoran 
150*0616c1c3SMichael Corcoran 	if (error == E2BIG && alloc_num == 0) {
151*0616c1c3SMichael Corcoran 		if (num_mapped > MOBJ_STACK_SEGS && num_mapped <= num_in) {
152*0616c1c3SMichael Corcoran 			mrp = kmem_alloc(sizeof (mmapobj_result_t) * num_mapped,
153*0616c1c3SMichael Corcoran 			    KM_SLEEP);
154*0616c1c3SMichael Corcoran 			alloc_num = num_mapped;
155*0616c1c3SMichael Corcoran 			goto retry;
156*0616c1c3SMichael Corcoran 		}
157*0616c1c3SMichael Corcoran 	}
158*0616c1c3SMichael Corcoran 
159*0616c1c3SMichael Corcoran 	old_error = error;
160*0616c1c3SMichael Corcoran 	if (error == 0 || error == E2BIG) {
161*0616c1c3SMichael Corcoran 		error = copyout(&num_mapped, elements, sizeof (uint_t));
162*0616c1c3SMichael Corcoran 		if (error) {
163*0616c1c3SMichael Corcoran 			error = EFAULT;
164*0616c1c3SMichael Corcoran 			/*
165*0616c1c3SMichael Corcoran 			 * We only mapped in segments if the mmapobj call
166*0616c1c3SMichael Corcoran 			 * succeeded, so only unmap for that case.
167*0616c1c3SMichael Corcoran 			 */
168*0616c1c3SMichael Corcoran 			if (old_error == 0) {
169*0616c1c3SMichael Corcoran 				mmapobj_unmap(mrp, num_mapped, num_mapped, 0);
170*0616c1c3SMichael Corcoran 			}
171*0616c1c3SMichael Corcoran 		} else if (num_in < num_mapped) {
172*0616c1c3SMichael Corcoran 			ASSERT(old_error == E2BIG);
173*0616c1c3SMichael Corcoran 			error = E2BIG;
174*0616c1c3SMichael Corcoran 		} else {
175*0616c1c3SMichael Corcoran 			if (convert_64to32) {
176*0616c1c3SMichael Corcoran 				mmapobj_result32_t *mrp32;
177*0616c1c3SMichael Corcoran 				/* Need to translate from 64bit to 32bit */
178*0616c1c3SMichael Corcoran 				mrp32 = kmem_alloc(num_mapped * sizeof (*mrp32),
179*0616c1c3SMichael Corcoran 				    KM_SLEEP);
180*0616c1c3SMichael Corcoran 				mmapobj_copy_64to32(mrp, mrp32, num_mapped);
181*0616c1c3SMichael Corcoran 				error = copyout(mrp32, (void *)storage,
182*0616c1c3SMichael Corcoran 				    num_mapped * sizeof (mmapobj_result32_t));
183*0616c1c3SMichael Corcoran 				kmem_free(mrp32, num_mapped * sizeof (*mrp32));
184*0616c1c3SMichael Corcoran 			} else {
185*0616c1c3SMichael Corcoran 				error = copyout(mrp, (void *)storage,
186*0616c1c3SMichael Corcoran 				    num_mapped * sizeof (mmapobj_result_t));
187*0616c1c3SMichael Corcoran 			}
188*0616c1c3SMichael Corcoran 			if (error) {
189*0616c1c3SMichael Corcoran 				error = EFAULT;
190*0616c1c3SMichael Corcoran 				mmapobj_unmap(mrp, num_mapped, num_mapped, 0);
191*0616c1c3SMichael Corcoran 			}
192*0616c1c3SMichael Corcoran 		}
193*0616c1c3SMichael Corcoran 	}
194*0616c1c3SMichael Corcoran 
195*0616c1c3SMichael Corcoran 	/*
196*0616c1c3SMichael Corcoran 	 * If stack_mr was not large enough, then we had to allocate
197*0616c1c3SMichael Corcoran 	 * a larger piece of memory to hold the mmapobj_result array.
198*0616c1c3SMichael Corcoran 	 */
199*0616c1c3SMichael Corcoran 	if (alloc_num != 0) {
200*0616c1c3SMichael Corcoran 		ASSERT(mrp != stack_mr);
201*0616c1c3SMichael Corcoran 		ASSERT(num_mapped > MOBJ_STACK_SEGS);
202*0616c1c3SMichael Corcoran 		kmem_free(mrp,
203*0616c1c3SMichael Corcoran 		    alloc_num * sizeof (mmapobj_result_t));
204*0616c1c3SMichael Corcoran 	}
205*0616c1c3SMichael Corcoran 
206*0616c1c3SMichael Corcoran out:
207*0616c1c3SMichael Corcoran 	releasef(fd);
208*0616c1c3SMichael Corcoran 	if (error) {
209*0616c1c3SMichael Corcoran 		return (set_errno(error));
210*0616c1c3SMichael Corcoran 	} else {
211*0616c1c3SMichael Corcoran 		return (0);
212*0616c1c3SMichael Corcoran 	}
213*0616c1c3SMichael Corcoran }
214