xref: /openbmc/qemu/bsd-user/bsd-mem.h (revision 51464c56)
1 /*
2  *  memory management system call shims and definitions
3  *
4  *  Copyright (c) 2013-15 Stacey D. Son
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 /*
21  * Copyright (c) 1982, 1986, 1993
22  *      The Regents of the University of California.  All rights reserved.
23  *
24  * Redistribution and use in source and binary forms, with or without
25  * modification, are permitted provided that the following conditions
26  * are met:
27  * 1. Redistributions of source code must retain the above copyright
28  *    notice, this list of conditions and the following disclaimer.
29  * 2. Redistributions in binary form must reproduce the above copyright
30  *    notice, this list of conditions and the following disclaimer in the
31  *    documentation and/or other materials provided with the distribution.
32  * 4. Neither the name of the University nor the names of its contributors
33  *    may be used to endorse or promote products derived from this software
34  *    without specific prior written permission.
35  *
36  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
37  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
40  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
42  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  */
48 
49 #ifndef BSD_USER_BSD_MEM_H
50 #define BSD_USER_BSD_MEM_H
51 
52 #include <sys/types.h>
53 #include <sys/ipc.h>
54 #include <sys/mman.h>
55 #include <sys/shm.h>
56 #include <fcntl.h>
57 
58 #include "qemu-bsd.h"
59 
60 extern struct bsd_shm_regions bsd_shm_regions[];
61 extern abi_ulong target_brk;
62 extern abi_ulong initial_target_brk;
63 
64 /* mmap(2) */
65 static inline abi_long do_bsd_mmap(void *cpu_env, abi_long arg1, abi_long arg2,
66     abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6, abi_long arg7,
67     abi_long arg8)
68 {
69     if (regpairs_aligned(cpu_env) != 0) {
70         arg6 = arg7;
71         arg7 = arg8;
72     }
73     return get_errno(target_mmap(arg1, arg2, arg3,
74                                  target_to_host_bitmask(arg4, mmap_flags_tbl),
75                                  arg5, target_arg64(arg6, arg7)));
76 }
77 
78 /* munmap(2) */
79 static inline abi_long do_bsd_munmap(abi_long arg1, abi_long arg2)
80 {
81     return get_errno(target_munmap(arg1, arg2));
82 }
83 
84 /* mprotect(2) */
85 static inline abi_long do_bsd_mprotect(abi_long arg1, abi_long arg2,
86         abi_long arg3)
87 {
88     return get_errno(target_mprotect(arg1, arg2, arg3));
89 }
90 
91 /* msync(2) */
92 static inline abi_long do_bsd_msync(abi_long addr, abi_long len, abi_long flags)
93 {
94     if (!guest_range_valid_untagged(addr, len)) {
95         /* It seems odd, but POSIX wants this to be ENOMEM */
96         return -TARGET_ENOMEM;
97     }
98 
99     return get_errno(msync(g2h_untagged(addr), len, flags));
100 }
101 
102 /* mlock(2) */
103 static inline abi_long do_bsd_mlock(abi_long arg1, abi_long arg2)
104 {
105     if (!guest_range_valid_untagged(arg1, arg2)) {
106         return -TARGET_EINVAL;
107     }
108     return get_errno(mlock(g2h_untagged(arg1), arg2));
109 }
110 
111 /* munlock(2) */
112 static inline abi_long do_bsd_munlock(abi_long arg1, abi_long arg2)
113 {
114     if (!guest_range_valid_untagged(arg1, arg2)) {
115         return -TARGET_EINVAL;
116     }
117     return get_errno(munlock(g2h_untagged(arg1), arg2));
118 }
119 
120 /* mlockall(2) */
121 static inline abi_long do_bsd_mlockall(abi_long arg1)
122 {
123     return get_errno(mlockall(arg1));
124 }
125 
126 /* munlockall(2) */
127 static inline abi_long do_bsd_munlockall(void)
128 {
129     return get_errno(munlockall());
130 }
131 
132 /* madvise(2) */
133 static inline abi_long do_bsd_madvise(abi_long arg1, abi_long arg2,
134         abi_long arg3)
135 {
136     abi_ulong len;
137     int ret = 0;
138     abi_long start = arg1;
139     abi_long len_in = arg2;
140     abi_long advice = arg3;
141 
142     if (start & ~TARGET_PAGE_MASK) {
143         return -TARGET_EINVAL;
144     }
145     if (len_in == 0) {
146         return 0;
147     }
148     len = TARGET_PAGE_ALIGN(len_in);
149     if (len == 0 || !guest_range_valid_untagged(start, len)) {
150         return -TARGET_EINVAL;
151     }
152 
153     /*
154      * Most advice values are hints, so ignoring and returning success is ok.
155      *
156      * However, some advice values such as MADV_DONTNEED, are not hints and
157      * need to be emulated.
158      *
159      * A straight passthrough for those may not be safe because qemu sometimes
160      * turns private file-backed mappings into anonymous mappings.
161      * If all guest pages have PAGE_PASSTHROUGH set, mappings have the
162      * same semantics for the host as for the guest.
163      *
164      * MADV_DONTNEED is passed through, if possible.
165      * If passthrough isn't possible, we nevertheless (wrongly!) return
166      * success, which is broken but some userspace programs fail to work
167      * otherwise. Completely implementing such emulation is quite complicated
168      * though.
169      */
170     mmap_lock();
171     switch (advice) {
172     case MADV_DONTNEED:
173         if (page_check_range(start, len, PAGE_PASSTHROUGH)) {
174             ret = get_errno(madvise(g2h_untagged(start), len, advice));
175             if (ret == 0) {
176                 page_reset_target_data(start, start + len - 1);
177             }
178         }
179     }
180     mmap_unlock();
181 
182     return ret;
183 }
184 
185 /* minherit(2) */
186 static inline abi_long do_bsd_minherit(abi_long addr, abi_long len,
187         abi_long inherit)
188 {
189     return get_errno(minherit(g2h_untagged(addr), len, inherit));
190 }
191 
192 /* mincore(2) */
193 static inline abi_long do_bsd_mincore(abi_ulong target_addr, abi_ulong len,
194         abi_ulong target_vec)
195 {
196     abi_long ret;
197     void *p;
198     abi_ulong vec_len = DIV_ROUND_UP(len, TARGET_PAGE_SIZE);
199 
200     if (!guest_range_valid_untagged(target_addr, len)
201         || !page_check_range(target_addr, len, PAGE_VALID)) {
202         return -TARGET_EFAULT;
203     }
204 
205     p = lock_user(VERIFY_WRITE, target_vec, vec_len, 0);
206     if (p == NULL) {
207         return -TARGET_EFAULT;
208     }
209     ret = get_errno(mincore(g2h_untagged(target_addr), len, p));
210     unlock_user(p, target_vec, vec_len);
211 
212     return ret;
213 }
214 
215 /* do_brk() must return target values and target errnos. */
216 static inline abi_long do_obreak(abi_ulong brk_val)
217 {
218     abi_long mapped_addr;
219     abi_ulong new_brk;
220     abi_ulong old_brk;
221 
222     /* brk pointers are always untagged */
223 
224     /* do not allow to shrink below initial brk value */
225     if (brk_val < initial_target_brk) {
226         return target_brk;
227     }
228 
229     new_brk = TARGET_PAGE_ALIGN(brk_val);
230     old_brk = TARGET_PAGE_ALIGN(target_brk);
231 
232     /* new and old target_brk might be on the same page */
233     if (new_brk == old_brk) {
234         target_brk = brk_val;
235         return target_brk;
236     }
237 
238     /* Release heap if necessary */
239     if (new_brk < old_brk) {
240         target_munmap(new_brk, old_brk - new_brk);
241 
242         target_brk = brk_val;
243         return target_brk;
244     }
245 
246     mapped_addr = target_mmap(old_brk, new_brk - old_brk,
247                               PROT_READ | PROT_WRITE,
248                               MAP_FIXED | MAP_EXCL | MAP_ANON | MAP_PRIVATE,
249                               -1, 0);
250 
251     if (mapped_addr == old_brk) {
252         target_brk = brk_val;
253         return target_brk;
254     }
255 
256     /* For everything else, return the previous break. */
257     return target_brk;
258 }
259 
260 /* shm_open(2) */
261 static inline abi_long do_bsd_shm_open(abi_ulong arg1, abi_long arg2,
262         abi_long arg3)
263 {
264     int ret;
265     void *p;
266 
267     if (arg1 == (uintptr_t)SHM_ANON) {
268         p = SHM_ANON;
269     } else {
270         p = lock_user_string(arg1);
271         if (p == NULL) {
272             return -TARGET_EFAULT;
273         }
274     }
275     ret = get_errno(shm_open(p, target_to_host_bitmask(arg2, fcntl_flags_tbl),
276                              arg3));
277 
278     if (p != SHM_ANON) {
279         unlock_user(p, arg1, 0);
280     }
281 
282     return ret;
283 }
284 
285 /* shm_unlink(2) */
286 static inline abi_long do_bsd_shm_unlink(abi_ulong arg1)
287 {
288     int ret;
289     void *p;
290 
291     p = lock_user_string(arg1);
292     if (p == NULL) {
293         return -TARGET_EFAULT;
294     }
295     ret = get_errno(shm_unlink(p)); /* XXX path(p)? */
296     unlock_user(p, arg1, 0);
297 
298     return ret;
299 }
300 
301 /* shmget(2) */
302 static inline abi_long do_bsd_shmget(abi_long arg1, abi_ulong arg2,
303         abi_long arg3)
304 {
305     return get_errno(shmget(arg1, arg2, arg3));
306 }
307 
308 /* shmctl(2) */
309 static inline abi_long do_bsd_shmctl(abi_long shmid, abi_long cmd,
310         abi_ulong buff)
311 {
312     struct shmid_ds dsarg;
313     abi_long ret = -TARGET_EINVAL;
314 
315     cmd &= 0xff;
316 
317     switch (cmd) {
318     case IPC_STAT:
319         if (target_to_host_shmid_ds(&dsarg, buff)) {
320             return -TARGET_EFAULT;
321         }
322         ret = get_errno(shmctl(shmid, cmd, &dsarg));
323         if (host_to_target_shmid_ds(buff, &dsarg)) {
324             return -TARGET_EFAULT;
325         }
326         break;
327 
328     case IPC_SET:
329         if (target_to_host_shmid_ds(&dsarg, buff)) {
330             return -TARGET_EFAULT;
331         }
332         ret = get_errno(shmctl(shmid, cmd, &dsarg));
333         break;
334 
335     case IPC_RMID:
336         ret = get_errno(shmctl(shmid, cmd, NULL));
337         break;
338 
339     default:
340         ret = -TARGET_EINVAL;
341         break;
342     }
343 
344     return ret;
345 }
346 
347 /* shmat(2) */
348 static inline abi_long do_bsd_shmat(int shmid, abi_ulong shmaddr, int shmflg)
349 {
350     abi_ulong raddr;
351     abi_long ret;
352     struct shmid_ds shm_info;
353 
354     /* Find out the length of the shared memory segment. */
355     ret = get_errno(shmctl(shmid, IPC_STAT, &shm_info));
356     if (is_error(ret)) {
357         /* Can't get the length */
358         return ret;
359     }
360 
361     if (!guest_range_valid_untagged(shmaddr, shm_info.shm_segsz)) {
362         return -TARGET_EINVAL;
363     }
364 
365     WITH_MMAP_LOCK_GUARD() {
366         void *host_raddr;
367 
368         if (shmaddr) {
369             host_raddr = shmat(shmid, (void *)g2h_untagged(shmaddr), shmflg);
370         } else {
371             abi_ulong mmap_start;
372 
373             mmap_start = mmap_find_vma(0, shm_info.shm_segsz);
374 
375             if (mmap_start == -1) {
376                 return -TARGET_ENOMEM;
377             }
378             host_raddr = shmat(shmid, g2h_untagged(mmap_start),
379                                shmflg | SHM_REMAP);
380         }
381 
382         if (host_raddr == (void *)-1) {
383             return get_errno(-1);
384         }
385         raddr = h2g(host_raddr);
386 
387         page_set_flags(raddr, raddr + shm_info.shm_segsz - 1,
388                        PAGE_VALID | PAGE_RESET | PAGE_READ |
389                        (shmflg & SHM_RDONLY ? 0 : PAGE_WRITE));
390 
391         for (int i = 0; i < N_BSD_SHM_REGIONS; i++) {
392             if (bsd_shm_regions[i].start == 0) {
393                 bsd_shm_regions[i].start = raddr;
394                 bsd_shm_regions[i].size = shm_info.shm_segsz;
395                 break;
396             }
397         }
398     }
399 
400     return raddr;
401 }
402 
403 /* shmdt(2) */
404 static inline abi_long do_bsd_shmdt(abi_ulong shmaddr)
405 {
406     abi_long ret;
407 
408     WITH_MMAP_LOCK_GUARD() {
409         int i;
410 
411         for (i = 0; i < N_BSD_SHM_REGIONS; ++i) {
412             if (bsd_shm_regions[i].start == shmaddr) {
413                 break;
414             }
415         }
416 
417         if (i == N_BSD_SHM_REGIONS) {
418             return -TARGET_EINVAL;
419         }
420 
421         ret = get_errno(shmdt(g2h_untagged(shmaddr)));
422         if (ret == 0) {
423             abi_ulong size = bsd_shm_regions[i].size;
424 
425             bsd_shm_regions[i].start = 0;
426             page_set_flags(shmaddr, shmaddr + size - 1, 0);
427             mmap_reserve(shmaddr, size);
428         }
429     }
430 
431     return ret;
432 }
433 
434 static inline abi_long do_bsd_vadvise(void)
435 {
436     /* See sys_ovadvise() in vm_unix.c */
437     return -TARGET_EINVAL;
438 }
439 
440 static inline abi_long do_bsd_sbrk(void)
441 {
442     /* see sys_sbrk() in vm_mmap.c */
443     return -TARGET_EOPNOTSUPP;
444 }
445 
446 static inline abi_long do_bsd_sstk(void)
447 {
448     /* see sys_sstk() in vm_mmap.c */
449     return -TARGET_EOPNOTSUPP;
450 }
451 
452 #endif /* BSD_USER_BSD_MEM_H */
453