master
h 366 lines 8.43 KB
Raw
1 /*
2 * miscellaneous BSD system call shims
3 *
4 * Copyright (c) 2013 Stacey D. Son
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #ifndef BSD_MISC_H
10 #define BSD_MISC_H
11
12 #include <sys/types.h>
13 #include <sys/ipc.h>
14 #include <sys/msg.h>
15 #include <sys/sem.h>
16 #include <sys/uuid.h>
17
18 #include "qemu-bsd.h"
19
20 static int bsd_msgmax;
21
22 /* quotactl(2) */
23 static inline abi_long do_bsd_quotactl(abi_ulong path, abi_long cmd,
24 __unused abi_ulong target_addr)
25 {
26 qemu_log("qemu: Unsupported syscall quotactl()\n");
27 return -TARGET_ENOSYS;
28 }
29
30 /* reboot(2) */
31 static inline abi_long do_bsd_reboot(abi_long how)
32 {
33 qemu_log("qemu: Unsupported syscall reboot()\n");
34 return -TARGET_ENOSYS;
35 }
36
37 /* uuidgen(2) */
38 static inline abi_long do_bsd_uuidgen(abi_ulong target_addr, int count)
39 {
40 int i;
41 abi_long ret;
42 g_autofree struct uuid *host_uuid = NULL;
43
44 /*
45 * 2048 is the kernel limit, but there's no #define for it, nor any sysctl
46 * to query it.
47 */
48 if (count < 1 || count > 2048) {
49 return -TARGET_EINVAL;
50 }
51
52 host_uuid = g_malloc(count * sizeof(struct uuid));
53
54 ret = get_errno(uuidgen(host_uuid, count));
55 if (is_error(ret)) {
56 goto out;
57 }
58 for (i = 0; i < count; i++) {
59 ret = host_to_target_uuid(target_addr +
60 (abi_ulong)(sizeof(struct target_uuid) * i), &host_uuid[i]);
61 if (is_error(ret)) {
62 break;
63 }
64 }
65
66 out:
67 return ret;
68 }
69
70 /*
71 * System V Semaphores
72 */
73
74 /* semget(2) */
75 static inline abi_long do_bsd_semget(abi_long key, int nsems,
76 int target_flags)
77 {
78 return get_errno(semget(key, nsems,
79 target_to_host_bitmask(target_flags, ipc_flags_tbl)));
80 }
81
82 /* semop(2) */
83 static inline abi_long do_bsd_semop(int semid, abi_long ptr, unsigned nsops)
84 {
85 g_autofree struct sembuf *sops = g_malloc(nsops * sizeof(struct sembuf));
86 struct target_sembuf *target_sembuf;
87 int i;
88
89 target_sembuf = lock_user(VERIFY_READ, ptr,
90 nsops * sizeof(struct target_sembuf), 1);
91 if (target_sembuf == NULL) {
92 return -TARGET_EFAULT;
93 }
94 for (i = 0; i < nsops; i++) {
95 __get_user(sops[i].sem_num, &target_sembuf[i].sem_num);
96 __get_user(sops[i].sem_op, &target_sembuf[i].sem_op);
97 __get_user(sops[i].sem_flg, &target_sembuf[i].sem_flg);
98 }
99 unlock_user(target_sembuf, ptr, 0);
100
101 return semop(semid, sops, nsops);
102 }
103
104 /* __semctl(2) */
105 static inline abi_long do_bsd___semctl(int semid, int semnum, int target_cmd,
106 abi_ptr un_ptr)
107 {
108 void *target_un;
109 union semun arg;
110 struct semid_ds dsarg;
111 unsigned short *array = NULL;
112 int host_cmd;
113 abi_long ret = 0;
114 abi_ulong target_array, target_buffer;
115
116 switch (target_cmd) {
117 case TARGET_GETVAL:
118 host_cmd = GETVAL;
119 break;
120
121 case TARGET_SETVAL:
122 host_cmd = SETVAL;
123 break;
124
125 case TARGET_GETALL:
126 host_cmd = GETALL;
127 break;
128
129 case TARGET_SETALL:
130 host_cmd = SETALL;
131 break;
132
133 case TARGET_IPC_STAT:
134 host_cmd = IPC_STAT;
135 break;
136
137 case TARGET_IPC_SET:
138 host_cmd = IPC_SET;
139 break;
140
141 case TARGET_IPC_RMID:
142 host_cmd = IPC_RMID;
143 break;
144
145 case TARGET_GETPID:
146 host_cmd = GETPID;
147 break;
148
149 case TARGET_GETNCNT:
150 host_cmd = GETNCNT;
151 break;
152
153 case TARGET_GETZCNT:
154 host_cmd = GETZCNT;
155 break;
156
157 default:
158 return -TARGET_EINVAL;
159 }
160
161 /*
162 * Unlike Linux and the semctl system call, we take a pointer
163 * to the union arg here.
164 */
165 target_un = lock_user(VERIFY_READ, un_ptr, sizeof(union target_semun), 1);
166
167 switch (host_cmd) {
168 case GETVAL:
169 case SETVAL:
170 __get_user(arg.val, (abi_int *)target_un);
171 ret = get_errno(semctl(semid, semnum, host_cmd, arg));
172 break;
173
174 case GETALL:
175 case SETALL:
176 __get_user(target_array, (abi_ulong *)target_un);
177 ret = target_to_host_semarray(semid, &array, target_array);
178 if (is_error(ret)) {
179 goto out;
180 }
181 arg.array = array;
182 ret = get_errno(semctl(semid, semnum, host_cmd, arg));
183 if (!is_error(ret)) {
184 ret = host_to_target_semarray(semid, target_array, &array);
185 }
186 break;
187
188 case IPC_STAT:
189 case IPC_SET:
190 __get_user(target_buffer, (abi_ulong *)target_un);
191 ret = target_to_host_semid_ds(&dsarg, target_buffer);
192 if (is_error(ret)) {
193 goto out;
194 }
195 arg.buf = &dsarg;
196 ret = get_errno(semctl(semid, semnum, host_cmd, arg));
197 if (!is_error(ret)) {
198 ret = host_to_target_semid_ds(target_buffer, &dsarg);
199 }
200 break;
201
202 case IPC_RMID:
203 case GETPID:
204 case GETNCNT:
205 case GETZCNT:
206 ret = get_errno(semctl(semid, semnum, host_cmd, NULL));
207 break;
208
209 default:
210 ret = -TARGET_EINVAL;
211 break;
212 }
213 out:
214 unlock_user(target_un, un_ptr, 1);
215 return ret;
216 }
217
218 /* msgctl(2) */
219 static inline abi_long do_bsd_msgctl(int msgid, int target_cmd, abi_long ptr)
220 {
221 struct msqid_ds dsarg;
222 abi_long ret = -TARGET_EINVAL;
223 int host_cmd;
224
225 switch (target_cmd) {
226 case TARGET_IPC_STAT:
227 host_cmd = IPC_STAT;
228 break;
229
230 case TARGET_IPC_SET:
231 host_cmd = IPC_SET;
232 break;
233
234 case TARGET_IPC_RMID:
235 host_cmd = IPC_RMID;
236 break;
237
238 default:
239 return -TARGET_EINVAL;
240 }
241
242 switch (host_cmd) {
243 case IPC_STAT:
244 case IPC_SET:
245 if (target_to_host_msqid_ds(&dsarg, ptr)) {
246 return -TARGET_EFAULT;
247 }
248 ret = get_errno(msgctl(msgid, host_cmd, &dsarg));
249 if (host_to_target_msqid_ds(ptr, &dsarg)) {
250 return -TARGET_EFAULT;
251 }
252 break;
253
254 case IPC_RMID:
255 ret = get_errno(msgctl(msgid, host_cmd, NULL));
256 break;
257
258 default:
259 ret = -TARGET_EINVAL;
260 break;
261 }
262 return ret;
263 }
264
265 struct kern_mymsg {
266 long mtype;
267 char mtext[1];
268 };
269
270 static inline abi_long bsd_validate_msgsz(abi_ulong msgsz)
271 {
272 /* Fetch msgmax the first time we need it. */
273 if (bsd_msgmax == 0) {
274 size_t len = sizeof(bsd_msgmax);
275
276 if (sysctlbyname("kern.ipc.msgmax", &bsd_msgmax, &len, NULL, 0) == -1) {
277 return -TARGET_EINVAL;
278 }
279 }
280
281 if (msgsz > bsd_msgmax) {
282 return -TARGET_EINVAL;
283 }
284 return 0;
285 }
286
287 /* msgsnd(2) */
288 static inline abi_long do_bsd_msgsnd(int msqid, abi_long msgp,
289 abi_ulong msgsz, int msgflg)
290 {
291 struct target_msgbuf *target_mb;
292 struct kern_mymsg *host_mb;
293 abi_long ret;
294
295 ret = bsd_validate_msgsz(msgsz);
296 if (is_error(ret)) {
297 return ret;
298 }
299 if (!lock_user_struct(VERIFY_READ, target_mb, msgp, 0)) {
300 return -TARGET_EFAULT;
301 }
302 host_mb = g_malloc(msgsz + sizeof(long));
303 host_mb->mtype = (abi_long) tswapal(target_mb->mtype);
304 memcpy(host_mb->mtext, target_mb->mtext, msgsz);
305 ret = get_errno(msgsnd(msqid, host_mb, msgsz, msgflg));
306 g_free(host_mb);
307 unlock_user_struct(target_mb, msgp, 0);
308
309 return ret;
310 }
311
312 /* msgget(2) */
313 static inline abi_long do_bsd_msgget(abi_long key, abi_long msgflag)
314 {
315 abi_long ret;
316
317 ret = get_errno(msgget(key, msgflag));
318 return ret;
319 }
320
321 /* msgrcv(2) */
322 static inline abi_long do_bsd_msgrcv(int msqid, abi_long msgp,
323 abi_ulong msgsz, abi_long msgtyp, int msgflg)
324 {
325 struct target_msgbuf *target_mb = NULL;
326 char *target_mtext;
327 struct kern_mymsg *host_mb;
328 abi_long ret = 0;
329
330 ret = bsd_validate_msgsz(msgsz);
331 if (is_error(ret)) {
332 return ret;
333 }
334 if (!lock_user_struct(VERIFY_WRITE, target_mb, msgp, 0)) {
335 return -TARGET_EFAULT;
336 }
337 host_mb = g_malloc(msgsz + sizeof(long));
338 ret = get_errno(msgrcv(msqid, host_mb, msgsz, tswapal(msgtyp), msgflg));
339 if (ret > 0) {
340 abi_ulong target_mtext_addr = msgp + sizeof(abi_ulong);
341 target_mtext = lock_user(VERIFY_WRITE, target_mtext_addr, ret, 0);
342 if (target_mtext == NULL) {
343 ret = -TARGET_EFAULT;
344 goto end;
345 }
346 memcpy(target_mb->mtext, host_mb->mtext, ret);
347 unlock_user(target_mtext, target_mtext_addr, ret);
348 }
349 if (!is_error(ret)) {
350 target_mb->mtype = tswapal(host_mb->mtype);
351 }
352 end:
353 if (target_mb != NULL) {
354 unlock_user_struct(target_mb, msgp, 1);
355 }
356 g_free(host_mb);
357 return ret;
358 }
359
360 /* getdtablesize(2) */
361 static inline abi_long do_bsd_getdtablesize(void)
362 {
363 return get_errno(getdtablesize());
364 }
365
366 #endif /* BSD_MISC_H */