Subversion Repositories HelenOS

Rev

Rev 1029 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1027 palkovsky 1
/*
2
 * Copyright (C) 2006 Ondrej Palkovsky
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 *
9
 * - Redistributions of source code must retain the above copyright
10
 *   notice, this list of conditions and the following disclaimer.
11
 * - Redistributions in binary form must reproduce the above copyright
12
 *   notice, this list of conditions and the following disclaimer in the
13
 *   documentation and/or other materials provided with the distribution.
14
 * - The name of the author may not be used to endorse or promote products
15
 *   derived from this software without specific prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 */
28
 
29
#include <arch.h>
30
#include <proc/task.h>
31
 
32
#include <errno.h>
33
#include <mm/page.h>
34
#include <memstr.h>
35
#include <debug.h>
36
#include <ipc/ipc.h>
37
#include <ipc/sysipc.h>
38
#include <print.h>
39
 
40
/* TODO: multi-threaded connect-to-me can cause race condition
41
 * on phone, add counter + thread_kill??
42
 *
43
 */
44
 
1040 palkovsky 45
/** Return true if the method is a system method */
46
static inline int is_system_method(__native method)
47
{
48
	if (method <= IPC_M_LAST_SYSTEM)
49
		return 1;
50
	return 0;
51
}
52
 
53
/** Return true if the message with this method is forwardable
54
 *
55
 * - some system messages may be forwarded, for some of them
56
 *   it is useless
57
 */
58
static inline int is_forwardable(__native method)
59
{
60
	return 1;
61
}
62
 
63
/** Find call_t * in call table according to callid
64
 *
65
 * @return NULL on not found, otherwise pointer to call structure
66
 */
67
static inline call_t * get_call(__native callid)
68
{
69
	/* TODO: Traverse list of dispatched calls and find one */
70
	/* TODO: locking of call, ripping it from dispatched calls etc.  */
71
	return (call_t *) callid;
72
}
73
 
1027 palkovsky 74
/** Return pointer to phone identified by phoneid or NULL if non-existent */
75
static phone_t * get_phone(__native phoneid)
76
{
77
	phone_t *phone;
78
 
79
	if (phoneid >= IPC_MAX_PHONES)
80
		return NULL;
81
 
82
	phone = &TASK->phones[phoneid];
83
	if (!phone->callee)
84
		return NULL;
85
	return phone;
86
}
87
 
88
/** Allocate new phone slot in current TASK structure */
1040 palkovsky 89
static int phone_alloc(void)
1027 palkovsky 90
{
91
	int i;
92
 
93
	spinlock_lock(&TASK->lock);
94
 
95
	for (i=0; i < IPC_MAX_PHONES; i++) {
1040 palkovsky 96
		if (!TASK->phones[i].busy) {
97
			TASK->phones[i].busy = 1;
1027 palkovsky 98
			break;
99
		}
100
	}
101
	spinlock_unlock(&TASK->lock);
102
 
103
	if (i >= IPC_MAX_PHONES)
104
		return -1;
105
	return i;
106
}
107
 
108
/** Disconnect phone */
109
static void phone_dealloc(int phoneid)
110
{
1029 palkovsky 111
	spinlock_lock(&TASK->lock);
1027 palkovsky 112
 
1040 palkovsky 113
	ASSERT(TASK->phones[phoneid].busy);
1027 palkovsky 114
 
1040 palkovsky 115
	if (TASK->phones[phoneid].callee)
116
		ipc_phone_destroy(&TASK->phones[phoneid]);
117
 
118
	TASK->phones[phoneid].busy = 0;
1029 palkovsky 119
	spinlock_unlock(&TASK->lock);
1027 palkovsky 120
}
121
 
1040 palkovsky 122
static void phone_connect(int phoneid, answerbox_t *box)
123
{
124
	phone_t *phone = &TASK->phones[phoneid];
125
 
126
	ipc_phone_connect(phone, box);
127
}
128
 
1027 palkovsky 129
/****************************************************/
130
/* Functions that preprocess answer before sending 
131
 * it to the recepient
132
 */
133
 
134
/** Return true if the caller (ipc_answer) should save
135
 * the old call contents and call answer_preprocess
136
 */
137
static inline int answer_will_preprocess(call_t *call)
138
{
139
	if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME)
140
		return 1;
141
	return 0;
142
}
143
 
144
/** Interpret process answer as control information */
1040 palkovsky 145
static inline void answer_preprocess(call_t *answer, ipc_data_t *olddata)
1027 palkovsky 146
{
147
	int phoneid;
148
 
1040 palkovsky 149
	if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECTTOME) {
150
		phoneid = IPC_GET_ARG3(*olddata);
1027 palkovsky 151
		if (IPC_GET_RETVAL(answer->data)) {
152
			/* The connection was not accepted */
153
			phone_dealloc(phoneid);
1040 palkovsky 154
		} else {
155
			/* The connection was accepted */
156
			phone_connect(phoneid,&answer->sender->answerbox);
1027 palkovsky 157
		}
158
	}
159
}
160
 
161
/****************************************************/
162
/* Functions called to process received call/answer 
163
 * before passing to uspace
164
 */
165
 
166
/** Do basic kernel processing of received call answer */
167
static int process_answer(answerbox_t *box,call_t *call)
168
{
169
	return 0;
170
}
171
 
172
/** Do basic kernel processing of received call request
173
 *
174
 * @return 0 - the call should be passed to userspace, 1 - ignore call
175
 */
176
static int process_request(answerbox_t *box,call_t *call)
177
{
178
	int phoneid;
179
 
180
	if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME) {
1040 palkovsky 181
		phoneid = phone_alloc();
1027 palkovsky 182
		if (phoneid < 0) { /* Failed to allocate phone */
183
			IPC_SET_RETVAL(call->data, ELIMIT);
184
			ipc_answer(box,call);
185
			return -1;
186
		}
187
		IPC_SET_ARG3(call->data, phoneid);
188
	} 
189
	return 0;
190
}
191
 
192
/** Send a call over IPC, wait for reply, return to user
193
 *
194
 * @return Call identification, returns -1 on fatal error, 
195
           -2 on 'Too many async request, handle answers first
196
 */
197
__native sys_ipc_call_sync_fast(__native phoneid, __native method, 
198
				__native arg1, __native *data)
199
{
200
	call_t call;
201
	phone_t *phone;
202
 
203
	phone = get_phone(phoneid);
204
	if (!phone)
1040 palkovsky 205
		return ENOENT;
1027 palkovsky 206
 
1040 palkovsky 207
	if (is_system_method(method))
208
		return EPERM;
209
 
1027 palkovsky 210
	ipc_call_init(&call);
211
	IPC_SET_METHOD(call.data, method);
212
	IPC_SET_ARG1(call.data, arg1);
213
 
214
	ipc_call_sync(phone, &call);
215
 
216
	copy_to_uspace(data, &call.data, sizeof(call.data));
217
 
218
	return 0;
219
}
220
 
221
/** Synchronous IPC call allowing to send whole message */
222
__native sys_ipc_call_sync(__native phoneid, __native *question, 
223
			   __native *reply)
224
{
225
	call_t call;
226
	phone_t *phone;
227
 
228
	phone = get_phone(phoneid);
229
	if (!phone)
1040 palkovsky 230
		return ENOENT;
1027 palkovsky 231
 
232
	ipc_call_init(&call);
233
	copy_from_uspace(&call.data, question, sizeof(call.data));
1040 palkovsky 234
 
235
	if (is_system_method(IPC_GET_METHOD(call.data)))
236
		return EPERM;
1027 palkovsky 237
 
238
	ipc_call_sync(phone, &call);
239
 
240
	copy_to_uspace(reply, &call.data, sizeof(call.data));
241
 
242
	return 0;
243
}
244
 
245
/** Check that the task did not exceed allowed limit
246
 *
247
 * @return 0 - Limit OK,   -1 - limit exceeded
248
 */
249
static int check_call_limit(void)
250
{
251
	if (atomic_preinc(&TASK->active_calls) > IPC_MAX_ASYNC_CALLS) {
252
		atomic_dec(&TASK->active_calls);
253
		return -1;
254
	}
255
	return 0;
256
}
257
 
258
/** Send an asynchronous call over ipc
259
 *
260
 * @return Call identification, returns -1 on fatal error, 
261
           -2 on 'Too many async request, handle answers first
262
 */
263
__native sys_ipc_call_async_fast(__native phoneid, __native method, 
264
				 __native arg1, __native arg2)
265
{
266
	call_t *call;
267
	phone_t *phone;
268
 
269
	phone = get_phone(phoneid);
270
	if (!phone)
271
		return IPC_CALLRET_FATAL;
272
 
1040 palkovsky 273
	if (is_system_method(method))
274
		return IPC_CALLRET_FATAL;
275
 
1027 palkovsky 276
	if (check_call_limit())
277
		return IPC_CALLRET_TEMPORARY;
278
 
279
	call = ipc_call_alloc();
280
	IPC_SET_METHOD(call->data, method);
281
	IPC_SET_ARG1(call->data, arg1);
282
	IPC_SET_ARG2(call->data, arg2);
283
 
284
	ipc_call(phone, call);
285
 
286
	return (__native) call;
287
}
288
 
289
/** Synchronous IPC call allowing to send whole message
290
 *
291
 * @return The same as sys_ipc_call_async
292
 */
293
__native sys_ipc_call_async(__native phoneid, __native *data)
294
{
295
	call_t *call;
296
	phone_t *phone;
297
 
298
	phone = get_phone(phoneid);
299
	if (!phone)
300
		return IPC_CALLRET_FATAL;
301
 
302
	if (check_call_limit())
303
		return IPC_CALLRET_TEMPORARY;
304
 
305
	call = ipc_call_alloc();
306
	copy_from_uspace(&call->data, data, sizeof(call->data));
1040 palkovsky 307
 
308
	if (is_system_method(IPC_GET_METHOD(call->data))) {
309
		ipc_call_free(call);
310
		return EPERM;
311
	}
1027 palkovsky 312
 
313
	ipc_call(phone, call);
314
 
315
	return (__native) call;
316
}
317
 
1040 palkovsky 318
/** Forward received call to another destination
319
 *
320
 * The arg1 and arg2 are changed in the forwarded message
321
 */
322
__native sys_ipc_forward_fast(__native callid, __native phoneid,
323
			      __native method, __native arg1)
324
{
325
	call_t *call;
326
	phone_t *phone;
327
 
328
	call = get_call(callid);
329
	if (!call)
330
		return ENOENT;
331
 
332
	phone = get_phone(phoneid);
333
	if (!phone) {
334
		IPC_SET_RETVAL(call->data, EFORWARD);
335
		ipc_answer(&TASK->answerbox, call);
336
		return ENOENT;
337
	}
338
 
339
	if (!is_forwardable(IPC_GET_METHOD(call->data))) {
340
		IPC_SET_RETVAL(call->data, EFORWARD);
341
		ipc_answer(&TASK->answerbox, call);
342
		return EPERM;
343
	}
344
 
345
	/* Userspace is not allowed to change method of system methods
346
	 * on forward, allow changing ARG1 and ARG2 by means of method and arg1
347
	 */
348
	if (is_system_method(IPC_GET_METHOD(call->data))) {
349
		IPC_SET_ARG1(call->data, method);
350
		IPC_SET_ARG2(call->data, arg1);
351
	} else {
352
		IPC_SET_METHOD(call->data, method);
353
		IPC_SET_ARG1(call->data, arg1);
354
	}
355
 
356
	ipc_forward(call, phone->callee, &TASK->answerbox);
357
 
358
	return 0;
359
}
360
 
1027 palkovsky 361
/** Send IPC answer */
362
__native sys_ipc_answer_fast(__native callid, __native retval, 
363
			     __native arg1, __native arg2)
364
{
365
	call_t *call;
1040 palkovsky 366
	ipc_data_t saved_data;
1027 palkovsky 367
	int preprocess = 0;
368
 
1040 palkovsky 369
	call = get_call(callid);
370
	if (!call)
371
		return ENOENT;
1027 palkovsky 372
 
373
	if (answer_will_preprocess(call)) {
1040 palkovsky 374
		memcpy(&saved_data, &call->data, sizeof(call->data));
1027 palkovsky 375
		preprocess = 1;
376
	}
377
 
378
	IPC_SET_RETVAL(call->data, retval);
379
	IPC_SET_ARG1(call->data, arg1);
380
	IPC_SET_ARG2(call->data, arg2);
1040 palkovsky 381
 
1027 palkovsky 382
	if (preprocess)
1040 palkovsky 383
		answer_preprocess(call, &saved_data);
1027 palkovsky 384
 
385
	ipc_answer(&TASK->answerbox, call);
386
	return 0;
387
}
388
 
389
/** Send IPC answer */
390
inline __native sys_ipc_answer(__native callid, __native *data)
391
{
392
	call_t *call;
1040 palkovsky 393
	ipc_data_t saved_data;
1027 palkovsky 394
	int preprocess = 0;
395
 
1040 palkovsky 396
	call = get_call(callid);
397
	if (!call)
398
		return ENOENT;
1027 palkovsky 399
 
400
	if (answer_will_preprocess(call)) {
1040 palkovsky 401
		memcpy(&saved_data, &call->data, sizeof(call->data));
1027 palkovsky 402
		preprocess = 1;
403
	}
404
	copy_from_uspace(&call->data, data, sizeof(call->data));
405
 
406
	if (preprocess)
1040 palkovsky 407
		answer_preprocess(call, &saved_data);
1027 palkovsky 408
 
409
	ipc_answer(&TASK->answerbox, call);
410
 
411
	return 0;
412
}
413
 
414
/** Ask the other side of connection to do 'callback' connection
415
 *
416
 * @return 0 if no error, error otherwise 
417
 */
418
__native sys_ipc_connect_to_me(__native phoneid, __native arg1,
419
			       __native arg2, task_id_t *taskid)
420
{
421
	call_t call;
422
	phone_t *phone;
423
 
424
	phone = get_phone(phoneid);
425
	if (!phone)
1040 palkovsky 426
		return ENOENT;
1027 palkovsky 427
 
428
	ipc_call_init(&call);
429
	IPC_SET_METHOD(call.data, IPC_M_CONNECTTOME);
430
	IPC_SET_ARG1(call.data, arg1);
431
	IPC_SET_ARG2(call.data, arg2);
432
 
433
	ipc_call_sync(phone, &call);
434
 
435
	if (!IPC_GET_RETVAL(call.data) && taskid)
436
		copy_to_uspace(taskid, 
437
			       &phone->callee->task->taskid,
438
			       sizeof(TASK->taskid));
439
	return IPC_GET_RETVAL(call.data);
440
}
441
 
1040 palkovsky 442
/** Ask target process to connect me somewhere
443
 *
444
 * @return phoneid - on success, error otherwise
445
 */
446
__native sys_ipc_connect_me_to(__native phoneid, __native arg1,
447
			       __native arg2)
448
{
449
	call_t call;
450
	phone_t *phone;
451
 
452
	phone = get_phone(phoneid);
453
	if (!phone)
454
		return ENOENT;
455
 
456
	ipc_call_init(&call);
457
	IPC_SET_METHOD(call.data, IPC_M_CONNECTMETO);
458
	IPC_SET_ARG1(call.data, arg1);
459
	IPC_SET_ARG2(call.data, arg2);
460
 
461
	ipc_call_sync(phone, &call);
462
	if (!IPC_GET_RETVAL(call.data)) {
463
		/* Everybody accepted, we should be connected by now */
464
	}
465
 
466
	return 0;
467
}
468
 
1027 palkovsky 469
/** Wait for incoming ipc call or answer
470
 *
471
 * Generic function - can serve either as inkernel or userspace call
472
 * - inside kernel does probably unnecessary copying of data (TODO)
473
 *
474
 * @param result 
475
 * @param taskid 
476
 * @param flags
477
 * @return Callid, if callid & 1, then the call is answer
478
 */
479
inline __native sys_ipc_wait_for_call(ipc_data_t *calldata, 
480
				      task_id_t *taskid,
481
				      __native flags)
482
 
483
{
484
	call_t *call;
485
 
486
restart:	
487
	call = ipc_wait_for_call(&TASK->answerbox, flags);
1040 palkovsky 488
	printf("Received call %P from sender: %P\n", call, call->sender);
1027 palkovsky 489
 
490
	if (call->flags & IPC_CALL_ANSWERED) {
491
		if (process_answer(&TASK->answerbox, call))
492
			goto restart;
493
 
494
		copy_to_uspace(calldata, &call->data, sizeof(call->data));
495
		atomic_dec(&TASK->active_calls);
496
		ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
497
		ipc_call_free(call);
498
 
499
		return ((__native)call) | IPC_CALLID_ANSWERED;
500
	}
501
	if (process_request(&TASK->answerbox, call))
502
		goto restart;
503
	copy_to_uspace(calldata, &call->data, sizeof(call->data));
504
	copy_to_uspace(taskid, (void *)&TASK->taskid, sizeof(TASK->taskid));
505
	return (__native)call;
506
}
507