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