xref: /haiku/src/system/kernel/syscalls.cpp (revision 746cac055adc6ac3308c7bc2d29040fb95689cc9)
1 /*
2  * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2004-2006, Haiku Inc. All rights reserved.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 /* Big case statement for dispatching syscalls */
8 
9 #include <kernel.h>
10 #include <ksyscalls.h>
11 #include <syscalls.h>
12 #include <generic_syscall.h>
13 #include <debug.h>
14 #include <int.h>
15 #include <elf.h>
16 #include <vfs.h>
17 #include <vm.h>
18 #include <thread.h>
19 #include <posix/realtime_sem.h>
20 #include <posix/xsi_message_queue.h>
21 #include <posix/xsi_semaphore.h>
22 #include <sem.h>
23 #include <port.h>
24 #include <cpu.h>
25 #include <arch_config.h>
26 #include <disk_device_manager/ddm_userland_interface.h>
27 #include <sys/resource.h>
28 #include <fs/fd.h>
29 #include <fs/node_monitor.h>
30 #include <kimage.h>
31 #include <ksignal.h>
32 #include <real_time_clock.h>
33 #include <safemode.h>
34 #include <system_info.h>
35 #include <tracing.h>
36 #include <user_atomic.h>
37 #include <arch/system_info.h>
38 #include <messaging.h>
39 #include <frame_buffer_console.h>
40 #include <usergroup.h>
41 #include <wait_for_objects.h>
42 
43 #include <malloc.h>
44 #include <string.h>
45 
46 #include "syscall_numbers.h"
47 
48 
49 typedef struct generic_syscall generic_syscall;
50 
51 struct generic_syscall {
52 	list_link		link;
53 	char			subsystem[B_FILE_NAME_LENGTH];
54 	syscall_hook	hook;
55 	uint32			version;
56 	uint32			flags;
57 	generic_syscall	*previous;
58 };
59 
60 static mutex sGenericSyscallLock = MUTEX_INITIALIZER("generic syscall");
61 static struct list sGenericSyscalls;
62 
63 
64 #if SYSCALL_TRACING
65 static int dump_syscall_tracing(int argc, char** argv);
66 #endif
67 
68 
69 static generic_syscall *
70 find_generic_syscall(const char *subsystem)
71 {
72 	generic_syscall *syscall = NULL;
73 
74 	ASSERT_LOCKED_MUTEX(&sGenericSyscallLock);
75 
76 	while ((syscall = (generic_syscall*)list_get_next_item(&sGenericSyscalls,
77 			syscall)) != NULL) {
78 		if (!strcmp(syscall->subsystem, subsystem))
79 			return syscall;
80 	}
81 
82 	return NULL;
83 }
84 
85 
86 /**	Calls the generic syscall subsystem if any.
87  *	Also handles the special generic syscall function \c B_SYSCALL_INFO.
88  *	Returns \c B_NAME_NOT_FOUND if either the subsystem was not found, or
89  *	the subsystem does not support the requested function.
90  *	All other return codes are depending on the generic syscall implementation.
91  */
92 
93 static inline status_t
94 _user_generic_syscall(const char *userSubsystem, uint32 function,
95 	void *buffer, size_t bufferSize)
96 {
97 	char subsystem[B_FILE_NAME_LENGTH];
98 	generic_syscall *syscall;
99 	status_t status = B_NAME_NOT_FOUND;
100 
101 	if (!IS_USER_ADDRESS(userSubsystem)
102 		|| user_strlcpy(subsystem, userSubsystem, sizeof(subsystem)) < B_OK)
103 		return B_BAD_ADDRESS;
104 
105 	//dprintf("generic_syscall(subsystem = \"%s\", function = %lu)\n", subsystem, function);
106 
107 	mutex_lock(&sGenericSyscallLock);
108 
109 	syscall = find_generic_syscall(subsystem);
110 	if (syscall == NULL)
111 		goto out;
112 
113 	if (function >= B_RESERVED_SYSCALL_BASE) {
114 		if (function != B_SYSCALL_INFO) {
115 			// this is all we know
116 			status = B_NAME_NOT_FOUND;
117 			goto out;
118 		}
119 
120 		// special info syscall
121 		if (bufferSize != sizeof(uint32))
122 			status = B_BAD_VALUE;
123 		else {
124 			uint32 requestedVersion;
125 
126 			// retrieve old version
127 			status = user_memcpy(&requestedVersion, buffer, sizeof(uint32));
128 			if (status == B_OK && requestedVersion != 0 && requestedVersion < syscall->version)
129 				status = B_BAD_TYPE;
130 
131 			// return current version
132 			if (status == B_OK)
133 				status = user_memcpy(buffer, &syscall->version, sizeof(uint32));
134 		}
135 	} else {
136 		while (syscall != NULL) {
137 			generic_syscall *next;
138 
139 			mutex_unlock(&sGenericSyscallLock);
140 
141 			status = syscall->hook(subsystem, function, buffer, bufferSize);
142 
143 			mutex_lock(&sGenericSyscallLock);
144 			if (status != B_BAD_HANDLER)
145 				break;
146 
147 			// the syscall may have been removed in the mean time
148 			next = find_generic_syscall(subsystem);
149 			if (next == syscall)
150 				syscall = syscall->previous;
151 			else
152 				syscall = next;
153 		}
154 
155 		if (syscall == NULL)
156 			status = B_NAME_NOT_FOUND;
157 	}
158 
159 out:
160 	mutex_unlock(&sGenericSyscallLock);
161 	return status;
162 }
163 
164 
165 static inline int
166 _user_is_computer_on(void)
167 {
168 	return 1;
169 }
170 
171 // map to the arch specific call
172 
173 static inline int64
174 _user_restore_signal_frame()
175 {
176 	syscall_64_bit_return_value();
177 
178 	return arch_restore_signal_frame();
179 }
180 
181 
182 //	#pragma mark -
183 
184 
185 int32
186 syscall_dispatcher(uint32 call_num, void *args, uint64 *call_ret)
187 {
188 	bigtime_t startTime;
189 
190 //	dprintf("syscall_dispatcher: thread 0x%x call 0x%x, arg0 0x%x, arg1 0x%x arg2 0x%x arg3 0x%x arg4 0x%x\n",
191 //		thread_get_current_thread_id(), call_num, arg0, arg1, arg2, arg3, arg4);
192 
193 	user_debug_pre_syscall(call_num, args);
194 
195 	startTime = system_time();
196 
197 	switch (call_num) {
198 		// the cases are auto-generated
199 		#include "syscall_dispatcher.h"
200 
201 		default:
202 			*call_ret = (uint64)B_BAD_VALUE;
203 	}
204 
205 	user_debug_post_syscall(call_num, args, *call_ret, startTime);
206 
207 //	dprintf("syscall_dispatcher: done with syscall 0x%x\n", call_num);
208 
209 	return B_HANDLED_INTERRUPT;
210 }
211 
212 
213 status_t
214 generic_syscall_init(void)
215 {
216 	list_init(&sGenericSyscalls);
217 
218 #if	SYSCALL_TRACING
219 	add_debugger_command_etc("straced", &dump_syscall_tracing,
220 		"Dump recorded syscall trace entries",
221 		"Prints recorded trace entries. It is wrapper for the \"traced\"\n"
222 		"command and supports all of its command line options (though\n"
223 		"backward tracing doesn't really work). The difference is that if a\n"
224 		"pre syscall trace entry is encountered, the corresponding post\n"
225 		"syscall traced entry is also printed, even if it doesn't match the\n"
226 		"given filter.\n", 0);
227 #endif	// ENABLE_TRACING
228 
229 	return B_OK;
230 }
231 
232 
233 //	#pragma mark -
234 //	public API
235 
236 
237 status_t
238 register_generic_syscall(const char *subsystem, syscall_hook hook,
239 	uint32 version, uint32 flags)
240 {
241 	struct generic_syscall *previous, *syscall;
242 	status_t status;
243 
244 	if (hook == NULL)
245 		return B_BAD_VALUE;
246 
247 	mutex_lock(&sGenericSyscallLock);
248 
249 	previous = find_generic_syscall(subsystem);
250 	if (previous != NULL) {
251 		if ((flags & B_DO_NOT_REPLACE_SYSCALL) != 0
252 			|| version < previous->version) {
253 			status = B_NAME_IN_USE;
254 			goto out;
255 		}
256 		if (previous->flags & B_SYSCALL_NOT_REPLACEABLE) {
257 			status = B_NOT_ALLOWED;
258 			goto out;
259 		}
260 	}
261 
262 	syscall = (generic_syscall *)malloc(sizeof(struct generic_syscall));
263 	if (syscall == NULL) {
264 		status = B_NO_MEMORY;
265 		goto out;
266 	}
267 
268 	strlcpy(syscall->subsystem, subsystem, sizeof(syscall->subsystem));
269 	syscall->hook = hook;
270 	syscall->version = version;
271 	syscall->flags = flags;
272 	syscall->previous = previous;
273 	list_add_item(&sGenericSyscalls, syscall);
274 
275 	if (previous != NULL)
276 		list_remove_link(&previous->link);
277 
278 	status = B_OK;
279 
280 out:
281 	mutex_unlock(&sGenericSyscallLock);
282 	return status;
283 }
284 
285 
286 status_t
287 unregister_generic_syscall(const char *subsystem, uint32 version)
288 {
289 	// ToDo: we should only remove the syscall with the matching version
290 	generic_syscall *syscall;
291 	status_t status;
292 
293 	mutex_lock(&sGenericSyscallLock);
294 
295 	syscall = find_generic_syscall(subsystem);
296 	if (syscall != NULL) {
297 		if (syscall->previous != NULL) {
298 			// reestablish the old syscall
299 			list_add_item(&sGenericSyscalls, syscall->previous);
300 		}
301 		list_remove_link(&syscall->link);
302 		free(syscall);
303 		status = B_OK;
304 	} else
305 		status = B_NAME_NOT_FOUND;
306 
307 	mutex_unlock(&sGenericSyscallLock);
308 	return status;
309 }
310 
311 
312 // #pragma mark - syscall tracing
313 
314 
315 #if SYSCALL_TRACING
316 
317 namespace SyscallTracing {
318 
319 
320 static const char*
321 get_syscall_name(uint32 syscall)
322 {
323 	if (syscall >= (uint32)kSyscallCount)
324 		return "<invalid syscall number>";
325 
326 	return kExtendedSyscallInfos[syscall].name;
327 }
328 
329 
330 class PreSyscall : public AbstractTraceEntry {
331 	public:
332 		PreSyscall(uint32 syscall, const void* parameters)
333 			:
334 			fSyscall(syscall),
335 			fParameters(NULL)
336 		{
337 			if (syscall < (uint32)kSyscallCount) {
338 				fParameters = alloc_tracing_buffer_memcpy(parameters,
339 					kSyscallInfos[syscall].parameter_size, false);
340 
341 				// copy string parameters, if any
342 				if (fParameters != NULL && syscall != SYSCALL_KTRACE_OUTPUT) {
343 					int32 stringIndex = 0;
344 					const extended_syscall_info& syscallInfo
345 						= kExtendedSyscallInfos[fSyscall];
346 					for (int i = 0; i < syscallInfo.parameter_count; i++) {
347 						const syscall_parameter_info& paramInfo
348 							= syscallInfo.parameters[i];
349 						if (paramInfo.type != B_STRING_TYPE)
350 							continue;
351 
352 						const uint8* data
353 							= (uint8*)fParameters + paramInfo.offset;
354 						if (stringIndex < MAX_PARAM_STRINGS) {
355 							fParameterStrings[stringIndex++]
356 								= alloc_tracing_buffer_strcpy(
357 									*(const char**)data, 64, true);
358 						}
359 					}
360 				}
361 			}
362 
363 			Initialized();
364 		}
365 
366 		virtual void AddDump(TraceOutput& out)
367 		{
368 			out.Print("syscall pre:  %s(", get_syscall_name(fSyscall));
369 
370 			if (fParameters != NULL) {
371 				int32 stringIndex = 0;
372 				const extended_syscall_info& syscallInfo
373 					= kExtendedSyscallInfos[fSyscall];
374 				for (int i = 0; i < syscallInfo.parameter_count; i++) {
375 					const syscall_parameter_info& paramInfo
376 						= syscallInfo.parameters[i];
377 					const uint8* data = (uint8*)fParameters + paramInfo.offset;
378 					uint64 value = 0;
379 					bool printValue = true;
380 					switch (paramInfo.type) {
381 						case B_INT8_TYPE:
382 							value = *(uint8*)data;
383 							break;
384 						case B_INT16_TYPE:
385 							value = *(uint16*)data;
386 							break;
387 						case B_INT32_TYPE:
388 							value = *(uint32*)data;
389 							break;
390 						case B_INT64_TYPE:
391 							value = *(uint64*)data;
392 							break;
393 						case B_POINTER_TYPE:
394 							value = (uint64)*(void**)data;
395 							break;
396 						case B_STRING_TYPE:
397 							if (stringIndex < MAX_PARAM_STRINGS
398 								&& fSyscall != SYSCALL_KTRACE_OUTPUT) {
399 								out.Print("%s\"%s\"",
400 									(i == 0 ? "" : ", "),
401 									fParameterStrings[stringIndex++]);
402 								printValue = false;
403 							} else
404 								value = (uint64)*(void**)data;
405 							break;
406 					}
407 
408 					if (printValue)
409 						out.Print("%s0x%llx", (i == 0 ? "" : ", "), value);
410 				}
411 			}
412 
413 			out.Print(")");
414 		}
415 
416 	private:
417 		enum { MAX_PARAM_STRINGS = 3 };
418 
419 		uint32		fSyscall;
420 		void*		fParameters;
421 		const char*	fParameterStrings[MAX_PARAM_STRINGS];
422 };
423 
424 
425 class PostSyscall : public AbstractTraceEntry {
426 	public:
427 		PostSyscall(uint32 syscall, uint64 returnValue)
428 			:
429 			fSyscall(syscall),
430 			fReturnValue(returnValue)
431 		{
432 			Initialized();
433 #if 0
434 			if (syscall < (uint32)kSyscallCount
435 				&&  returnValue != (returnValue & 0xffffffff)
436 				&& kExtendedSyscallInfos[syscall].return_type.size <= 4) {
437 				panic("syscall return value 64 bit although it should be 32 "
438 					"bit");
439 			}
440 #endif
441 		}
442 
443 		virtual void AddDump(TraceOutput& out)
444 		{
445 			out.Print("syscall post: %s() -> 0x%llx",
446 				get_syscall_name(fSyscall), fReturnValue);
447 		}
448 
449 	private:
450 		uint32	fSyscall;
451 		uint64	fReturnValue;
452 };
453 
454 }	// namespace SyscallTracing
455 
456 
457 extern "C" void trace_pre_syscall(uint32 syscallNumber, const void* parameters);
458 
459 void
460 trace_pre_syscall(uint32 syscallNumber, const void* parameters)
461 {
462 #if SYSCALL_TRACING_IGNORE_KTRACE_OUTPUT
463 	if (syscallNumber != SYSCALL_KTRACE_OUTPUT)
464 #endif
465 	{
466 		new(std::nothrow) SyscallTracing::PreSyscall(syscallNumber, parameters);
467 	}
468 }
469 
470 
471 extern "C" void trace_post_syscall(int syscallNumber, uint64 returnValue);
472 
473 void
474 trace_post_syscall(int syscallNumber, uint64 returnValue)
475 {
476 #if SYSCALL_TRACING_IGNORE_KTRACE_OUTPUT
477 	if (syscallNumber != SYSCALL_KTRACE_OUTPUT)
478 #endif
479 	{
480 		new(std::nothrow) SyscallTracing::PostSyscall(syscallNumber,
481 			returnValue);
482 	}
483 }
484 
485 
486 using namespace SyscallTracing;
487 
488 class SyscallWrapperTraceFilter : public WrapperTraceFilter {
489 public:
490 	virtual void Init(TraceFilter* filter, int direction, bool continued)
491 	{
492 		fFilter = filter;
493 		fHitThreadLimit = false;
494 		fDirection = direction;
495 
496 		if (!continued)
497 			fPendingThreadCount = 0;
498 	}
499 
500 	virtual bool Filter(const TraceEntry* _entry, LazyTraceOutput& out)
501 	{
502 		if (fFilter == NULL)
503 			return true;
504 
505 		if (fDirection < 0)
506 			return fFilter->Filter(_entry, out);
507 
508 		if (const PreSyscall* entry = dynamic_cast<const PreSyscall*>(_entry)) {
509 			_RemovePendingThread(entry->Thread());
510 
511 			bool accepted = fFilter->Filter(entry, out);
512 			if (accepted)
513 				_AddPendingThread(entry->Thread());
514 			return accepted;
515 
516 		} else if (const PostSyscall* entry
517 				= dynamic_cast<const PostSyscall*>(_entry)) {
518 			bool wasPending = _RemovePendingThread(entry->Thread());
519 
520 			return wasPending || fFilter->Filter(entry, out);
521 
522 		} else if (const AbstractTraceEntry* entry
523 				= dynamic_cast<const AbstractTraceEntry*>(_entry)) {
524 			bool isPending = _IsPendingThread(entry->Thread());
525 
526 			return isPending || fFilter->Filter(entry, out);
527 
528 		} else {
529 			return fFilter->Filter(_entry, out);
530 		}
531 	}
532 
533 	bool HitThreadLimit() const
534 	{
535 		return fHitThreadLimit;
536 	}
537 
538 	int Direction() const
539 	{
540 		return fDirection;
541 	}
542 
543 private:
544 	enum {
545 		MAX_PENDING_THREADS = 32
546 	};
547 
548 	bool _AddPendingThread(thread_id thread)
549 	{
550 		int32 index = _PendingThreadIndex(thread);
551 		if (index >= 0)
552 			return true;
553 
554 		if (fPendingThreadCount == MAX_PENDING_THREADS) {
555 			fHitThreadLimit = true;
556 			return false;
557 		}
558 
559 		fPendingThreads[fPendingThreadCount++] = thread;
560 		return true;
561 	}
562 
563 	bool _RemovePendingThread(thread_id thread)
564 	{
565 		int32 index = _PendingThreadIndex(thread);
566 		if (index < 0)
567 			return false;
568 
569 		if (index + 1 < fPendingThreadCount) {
570 			memmove(fPendingThreads + index, fPendingThreads + index + 1,
571 				fPendingThreadCount - index - 1);
572 		}
573 
574 		fPendingThreadCount--;
575 		return true;
576 	}
577 
578 	bool _IsPendingThread(thread_id thread)
579 	{
580 		return _PendingThreadIndex(thread) >= 0;
581 	}
582 
583 	int32 _PendingThreadIndex(thread_id thread)
584 	{
585 		for (int32 i = 0; i < fPendingThreadCount; i++) {
586 			if (fPendingThreads[i] == thread)
587 				return i;
588 		}
589 		return -1;
590 	}
591 
592 	TraceFilter*	fFilter;
593 	thread_id		fPendingThreads[MAX_PENDING_THREADS];
594 	int32			fPendingThreadCount;
595 	int				fDirection;
596 	bool			fHitThreadLimit;
597 };
598 
599 
600 static SyscallWrapperTraceFilter sFilter;
601 
602 static int
603 dump_syscall_tracing(int argc, char** argv)
604 {
605 	new(&sFilter) SyscallWrapperTraceFilter;
606 	int result = dump_tracing(argc, argv, &sFilter);
607 
608 	if (sFilter.HitThreadLimit()) {
609 		kprintf("Warning: The thread buffer was too small to track all "
610 			"threads!\n");
611 	} else if (sFilter.HitThreadLimit()) {
612 		kprintf("Warning: Can't track syscalls backwards!\n");
613 	}
614 
615 	return result;
616 }
617 
618 
619 #endif	// SYSCALL_TRACING
620 
621 
622 /*
623  * kSyscallCount and kSyscallInfos here
624  */
625 // generated by gensyscalls
626 #include "syscall_table.h"
627