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