io.c 9.43 KB
Newer Older
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
1
2
3
/*
 * Copyright (C) 2005 Jan Kiszka <jan.kiszka@web.de>.
 * Copyright (C) 2005 Joerg Langenberg <joerg.langenberg@gmx.net>.
4
 * Copyright (C) 2008 Gilles Chanteperdrix <gilles.chanteperdrix@xenomai.org>
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *
 * Xenomai is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Xenomai is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Xenomai; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
20
#include <linux/err.h>
21
#include <linux/fs.h>
22
#include <cobalt/kernel/compat.h>
23
#include <cobalt/kernel/ppd.h>
24
#include <xenomai/rtdm/internal.h>
25
#include "process.h"
26
#include "internal.h"
27
#include "clock.h"
28
#include "io.h"
Philippe Gerum's avatar
Philippe Gerum committed
29

30
COBALT_SYSCALL(open, lostage,
31
	       (const char __user *u_path, int oflag))
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
32
{
33
	struct filename *filename;
34
	int ufd;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
35

36
37
38
	filename = getname(u_path);
	if (IS_ERR(filename))
		return PTR_ERR(filename);
39

40
	ufd = __rtdm_dev_open(filename->name, oflag);
41
42
43
	putname(filename);

	return ufd;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
44
45
}

46
COBALT_SYSCALL(socket, lostage,
47
	       (int protocol_family, int socket_type, int protocol))
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
48
{
49
	return __rtdm_dev_socket(protocol_family, socket_type, protocol);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
50
51
}

52
COBALT_SYSCALL(close, lostage, (int fd))
53
{
54
	return rtdm_fd_close(fd, 0);
55
56
}

57
COBALT_SYSCALL(fcntl, current, (int fd, int cmd, long arg))
58
59
60
61
{
	return rtdm_fd_fcntl(fd, cmd, arg);
}

62
COBALT_SYSCALL(ioctl, handover,
63
	       (int fd, unsigned int request, void __user *arg))
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
64
{
65
	return rtdm_fd_ioctl(fd, request, arg);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
66
67
}

68
COBALT_SYSCALL(read, handover,
69
	       (int fd, void __user *buf, size_t size))
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
70
{
71
	return rtdm_fd_read(fd, buf, size);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
72
73
}

74
COBALT_SYSCALL(write, handover,
75
	       (int fd, const void __user *buf, size_t size))
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
76
{
77
	return rtdm_fd_write(fd, buf, size);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
78
79
}

80
COBALT_SYSCALL(recvmsg, handover,
81
	       (int fd, struct user_msghdr __user *umsg, int flags))
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
82
{
83
	struct user_msghdr m;
84
85
	ssize_t ret;

86
	ret = cobalt_copy_from_user(&m, umsg, sizeof(m));
87
88
	if (ret)
		return ret;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
89

90
	ret = rtdm_fd_recvmsg(fd, &m, flags);
91
	if (ret < 0)
92
93
		return ret;

94
	return cobalt_copy_to_user(umsg, &m, sizeof(*umsg)) ?: ret;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
95
96
}

97
static int get_timespec(struct timespec64 *ts,
98
99
			const void __user *u_ts)
{
100
	return cobalt_get_u_timespec(ts, u_ts);
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
}

static int get_mmsg(struct mmsghdr *mmsg, void __user *u_mmsg)
{
	return cobalt_copy_from_user(mmsg, u_mmsg, sizeof(*mmsg));
}

static int put_mmsg(void __user **u_mmsg_p, const struct mmsghdr *mmsg)
{
	struct mmsghdr __user **p = (struct mmsghdr **)u_mmsg_p,
		*q __user = (*p)++;

	return cobalt_copy_to_user(q, mmsg, sizeof(*q));
}

COBALT_SYSCALL(recvmmsg, primary,
	       (int fd, struct mmsghdr __user *u_msgvec, unsigned int vlen,
118
		unsigned int flags, struct __user_old_timespec __user *u_timeout))
119
120
121
122
123
{
	return __rtdm_fd_recvmmsg(fd, u_msgvec, vlen, flags, u_timeout,
				  get_mmsg, put_mmsg, get_timespec);
}

124
COBALT_SYSCALL(sendmsg, handover,
125
	       (int fd, struct user_msghdr __user *umsg, int flags))
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
126
{
127
	struct user_msghdr m;
128
	int ret;
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
129

130
	ret = cobalt_copy_from_user(&m, umsg, sizeof(m));
131
132

	return ret ?: rtdm_fd_sendmsg(fd, &m, flags);
133
134
}

135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
static int put_mmsglen(void __user **u_mmsg_p, const struct mmsghdr *mmsg)
{
	struct mmsghdr __user **p = (struct mmsghdr **)u_mmsg_p,
		*q __user = (*p)++;

	return __xn_put_user(mmsg->msg_len, &q->msg_len);
}

COBALT_SYSCALL(sendmmsg, primary,
	       (int fd, struct mmsghdr __user *u_msgvec,
		unsigned int vlen, unsigned int flags))
{
	return __rtdm_fd_sendmmsg(fd, u_msgvec, vlen, flags,
				  get_mmsg, put_mmsglen);
}

151
COBALT_SYSCALL(mmap, lostage,
152
153
	       (int fd, struct _rtdm_mmap_request __user *u_rma,
	        void __user **u_addrp))
154
155
{
	struct _rtdm_mmap_request rma;
156
	void *u_addr = NULL;
157
158
	int ret;

159
	ret = cobalt_copy_from_user(&rma, u_rma, sizeof(rma));
160
161
	if (ret)
		return ret;
162

163
	ret = rtdm_fd_mmap(fd, &rma, &u_addr);
164
165
166
	if (ret)
		return ret;

167
	return cobalt_copy_to_user(u_addrp, &u_addr, sizeof(u_addr));
168
}
169

170
static int __cobalt_first_fd_valid_p(fd_set *fds[XNSELECT_MAX_TYPES], int nfds)
171
172
173
174
175
176
177
178
179
180
181
182
183
{
	int i, fd;

	for (i = 0; i < XNSELECT_MAX_TYPES; i++)
		if (fds[i]
		    && (fd = find_first_bit(fds[i]->fds_bits, nfds)) < nfds)
			return rtdm_fd_valid_p(fd);

	/* All empty is correct, used as a "sleep" mechanism by strange
	   applications. */
	return 1;
}

184
185
static int __cobalt_select_bind_all(struct xnselector *selector,
				    fd_set *fds[XNSELECT_MAX_TYPES], int nfds)
186
{
187
	bool first_fd = true;
188
189
190
191
192
193
194
195
196
	unsigned fd, type;
	int err;

	for (type = 0; type < XNSELECT_MAX_TYPES; type++) {
		fd_set *set = fds[type];
		if (set)
			for (fd = find_first_bit(set->fds_bits, nfds);
			     fd < nfds;
			     fd = find_next_bit(set->fds_bits, nfds, fd + 1)) {
197
				err = rtdm_fd_select(fd, selector, type);
198
199
200
201
202
203
204
				if (err) {
					/*
					 * Do not needlessly signal "retry
					 * under Linux" for mixed fd sets.
					 */
					if (err == -EADV && !first_fd)
						return -EBADF;
205
					return err;
206
207
				}
				first_fd = false;
208
209
210
211
212
213
			}
	}

	return 0;
}

214
215
int __cobalt_select(int nfds, void __user *u_rfds, void __user *u_wfds,
		    void __user *u_xfds, void __user *u_tv, bool compat)
216
{
217
	void __user *ufd_sets[XNSELECT_MAX_TYPES] = {
218
219
220
221
222
223
224
225
226
		[XNSELECT_READ] = u_rfds,
		[XNSELECT_WRITE] = u_wfds,
		[XNSELECT_EXCEPT] = u_xfds
	};
	fd_set *in_fds[XNSELECT_MAX_TYPES] = {NULL, NULL, NULL};
	fd_set *out_fds[XNSELECT_MAX_TYPES] = {NULL, NULL, NULL};
	fd_set in_fds_storage[XNSELECT_MAX_TYPES],
		out_fds_storage[XNSELECT_MAX_TYPES];
	xnticks_t timeout = XN_INFINITE;
227
	struct restart_block *restart;
228
229
230
	xntmode_t mode = XN_RELATIVE;
	struct xnselector *selector;
	struct xnthread *curr;
231
	struct __kernel_old_timeval tv;
232
233
234
235
236
237
	size_t fds_size;
	int i, err;

	curr = xnthread_current();

	if (u_tv) {
238
239
240
241
242
243
244
245
246
247
248
		if (xnthread_test_localinfo(curr, XNSYSRST)) {
			xnthread_clear_localinfo(curr, XNSYSRST);

			restart = cobalt_get_restart_block(current);
			timeout = restart->nanosleep.expires;

			if (restart->fn != cobalt_restart_syscall_placeholder) {
				err = -EINTR;
				goto out;
			}
		} else {
249
250
251
252
253
254
255
256
257
258
259
260
#ifdef CONFIG_XENO_ARCH_SYS3264
			if (compat) {
				if (sys32_get_timeval(&tv, u_tv))
					return -EFAULT;
			} else
#endif
			{
				if (!access_wok(u_tv, sizeof(tv))
				    || cobalt_copy_from_user(&tv, u_tv,
							     sizeof(tv)))
					return -EFAULT;
			}
261

262
			if (tv.tv_usec >= 1000000)
263
264
265
266
				return -EINVAL;

			timeout = clock_get_ticks(CLOCK_MONOTONIC) + tv2ns(&tv);
		}
267
268
269
270
271
272
273
274
275

		mode = XN_ABSOLUTE;
	}

	fds_size = __FDELT__(nfds + __NFDBITS__ - 1) * sizeof(long);

	for (i = 0; i < XNSELECT_MAX_TYPES; i++)
		if (ufd_sets[i]) {
			in_fds[i] = &in_fds_storage[i];
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
			out_fds[i] = &out_fds_storage[i];
#ifdef CONFIG_XENO_ARCH_SYS3264
			if (compat) {
				if (sys32_get_fdset(in_fds[i], ufd_sets[i],
						    fds_size))
					return -EFAULT;
			} else
#endif
			{
				if (!access_wok((void __user *) ufd_sets[i],
						sizeof(fd_set))
				    || cobalt_copy_from_user(in_fds[i],
							     (void __user *)ufd_sets[i],
							     fds_size))
					return -EFAULT;
			}
292
293
294
295
296
297
298
299
		}

	selector = curr->selector;
	if (!selector) {
		/* This function may be called from pure Linux fd_sets, we want
		   to avoid the xnselector allocation in this case, so, we do a
		   simple test: test if the first file descriptor we find in the
		   fd_set is an RTDM descriptor or a message queue descriptor. */
300
		if (!__cobalt_first_fd_valid_p(in_fds, nfds))
301
			return -EADV;
302
303
304
305
306
307
308
309
310

		selector = xnmalloc(sizeof(*curr->selector));
		if (selector == NULL)
			return -ENOMEM;
		xnselector_init(selector);
		curr->selector = selector;

		/* Bind directly the file descriptors, we do not need to go
		   through xnselect returning -ECHRNG */
311
312
		err = __cobalt_select_bind_all(selector, in_fds, nfds);
		if (err)
313
314
315
316
317
318
			return err;
	}

	do {
		err = xnselect(selector, out_fds, in_fds, nfds, timeout, mode);
		if (err == -ECHRNG) {
319
320
321
322
			int bind_err = __cobalt_select_bind_all(selector,
								out_fds, nfds);
			if (bind_err)
				return bind_err;
323
324
325
		}
	} while (err == -ECHRNG);

326
327
328
329
330
331
332
333
334
335
336
	if (err == -EINTR && signal_pending(current)) {
		xnthread_set_localinfo(curr, XNSYSRST);

		restart = cobalt_get_restart_block(current);
		restart->fn = cobalt_restart_syscall_placeholder;
		restart->nanosleep.expires = timeout;

		return -ERESTARTSYS;
	}

out:
337
338
339
340
341
342
343
	if (u_tv && (err > 0 || err == -EINTR)) {
		xnsticks_t diff = timeout - clock_get_ticks(CLOCK_MONOTONIC);
		if (diff > 0)
			ticks2tv(&tv, diff);
		else
			tv.tv_sec = tv.tv_usec = 0;

344
345
346
347
348
349
350
351
352
353
#ifdef CONFIG_XENO_ARCH_SYS3264
		if (compat) {
			if (sys32_put_timeval(u_tv, &tv))
				return -EFAULT;
		} else
#endif
		{
			if (cobalt_copy_to_user(u_tv, &tv, sizeof(tv)))
				return -EFAULT;
		}
354
355
356
	}

	if (err >= 0)
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
		for (i = 0; i < XNSELECT_MAX_TYPES; i++) {
			if (!ufd_sets[i])
				continue;
#ifdef CONFIG_XENO_ARCH_SYS3264
			if (compat) {
				if (sys32_put_fdset(ufd_sets[i], out_fds[i],
						    sizeof(fd_set)))
					return -EFAULT;
			} else
#endif
			{
				if (cobalt_copy_to_user((void __user *)ufd_sets[i],
							out_fds[i], sizeof(fd_set)))
					return -EFAULT;
			}
		}
373
374
	return err;
}
375
376
377
378
379
380
381
382
383
384
385

/* int select(int, fd_set *, fd_set *, fd_set *, struct __kernel_old_timeval *) */
COBALT_SYSCALL(select, primary,
	       (int nfds,
		fd_set __user *u_rfds,
		fd_set __user *u_wfds,
		fd_set __user *u_xfds,
		struct __kernel_old_timeval __user *u_tv))
{
	return __cobalt_select(nfds, u_rfds, u_wfds, u_xfds, u_tv, false);
}