xref: /haiku/src/system/kernel/syscalls.cpp (revision fc1ca2da5cfcb00ffdf791606d5ae97fdd58a638)
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 <sem.h>
20 #include <port.h>
21 #include <cpu.h>
22 #include <arch_config.h>
23 #include <disk_device_manager/ddm_userland_interface.h>
24 #include <sys/resource.h>
25 #include <fs/fd.h>
26 #include <fs/node_monitor.h>
27 #include <kimage.h>
28 #include <ksignal.h>
29 #include <real_time_clock.h>
30 #include <safemode.h>
31 #include <system_info.h>
32 #include <tracing.h>
33 #include <user_atomic.h>
34 #include <arch/system_info.h>
35 #include <messaging.h>
36 #include <frame_buffer_console.h>
37 #include <usergroup.h>
38 #include <wait_for_objects.h>
39 
40 #include <malloc.h>
41 #include <string.h>
42 
43 #include "syscall_numbers.h"
44 
45 
46 typedef struct generic_syscall generic_syscall;
47 
48 struct generic_syscall {
49 	list_link		link;
50 	char			subsystem[B_FILE_NAME_LENGTH];
51 	syscall_hook	hook;
52 	uint32			version;
53 	uint32			flags;
54 	generic_syscall	*previous;
55 };
56 
57 static struct mutex sGenericSyscallLock;
58 static struct list sGenericSyscalls;
59 
60 
61 static int dump_syscall_tracing(int argc, char** argv);
62 
63 
64 static generic_syscall *
65 find_generic_syscall(const char *subsystem)
66 {
67 	generic_syscall *syscall = NULL;
68 
69 	ASSERT_LOCKED_MUTEX(&sGenericSyscallLock);
70 
71 	while ((syscall = (generic_syscall*)list_get_next_item(&sGenericSyscalls,
72 			syscall)) != NULL) {
73 		if (!strcmp(syscall->subsystem, subsystem))
74 			return syscall;
75 	}
76 
77 	return NULL;
78 }
79 
80 
81 /**	Calls the generic syscall subsystem if any.
82  *	Also handles the special generic syscall function \c B_SYSCALL_INFO.
83  *	Returns \c B_NAME_NOT_FOUND if either the subsystem was not found, or
84  *	the subsystem does not support the requested function.
85  *	All other return codes are depending on the generic syscall implementation.
86  */
87 
88 static inline status_t
89 _user_generic_syscall(const char *userSubsystem, uint32 function,
90 	void *buffer, size_t bufferSize)
91 {
92 	char subsystem[B_FILE_NAME_LENGTH];
93 	generic_syscall *syscall;
94 	status_t status = B_NAME_NOT_FOUND;
95 
96 	if (!IS_USER_ADDRESS(userSubsystem)
97 		|| user_strlcpy(subsystem, userSubsystem, sizeof(subsystem)) < B_OK)
98 		return B_BAD_ADDRESS;
99 
100 	//dprintf("generic_syscall(subsystem = \"%s\", function = %lu)\n", subsystem, function);
101 
102 	mutex_lock(&sGenericSyscallLock);
103 
104 	syscall = find_generic_syscall(subsystem);
105 	if (syscall == NULL)
106 		goto out;
107 
108 	if (function >= B_RESERVED_SYSCALL_BASE) {
109 		if (function != B_SYSCALL_INFO) {
110 			// this is all we know
111 			status = B_NAME_NOT_FOUND;
112 			goto out;
113 		}
114 
115 		// special info syscall
116 		if (bufferSize != sizeof(uint32))
117 			status = B_BAD_VALUE;
118 		else {
119 			uint32 requestedVersion;
120 
121 			// retrieve old version
122 			status = user_memcpy(&requestedVersion, buffer, sizeof(uint32));
123 			if (status == B_OK && requestedVersion != 0 && requestedVersion < syscall->version)
124 				status = B_BAD_TYPE;
125 
126 			// return current version
127 			if (status == B_OK)
128 				status = user_memcpy(buffer, &syscall->version, sizeof(uint32));
129 		}
130 	} else {
131 		while (syscall != NULL) {
132 			generic_syscall *next;
133 
134 			mutex_unlock(&sGenericSyscallLock);
135 
136 			status = syscall->hook(subsystem, function, buffer, bufferSize);
137 
138 			mutex_lock(&sGenericSyscallLock);
139 			if (status != B_BAD_HANDLER)
140 				break;
141 
142 			// the syscall may have been removed in the mean time
143 			next = find_generic_syscall(subsystem);
144 			if (next == syscall)
145 				syscall = syscall->previous;
146 			else
147 				syscall = next;
148 		}
149 
150 		if (syscall == NULL)
151 			status = B_NAME_NOT_FOUND;
152 	}
153 
154 out:
155 	mutex_unlock(&sGenericSyscallLock);
156 	return status;
157 }
158 
159 
160 static inline int
161 _user_is_computer_on(void)
162 {
163 	return 1;
164 }
165 
166 // map to the arch specific call
167 
168 static inline int64
169 _user_restore_signal_frame()
170 {
171 	syscall_64_bit_return_value();
172 
173 	return arch_restore_signal_frame();
174 }
175 
176 
177 //	#pragma mark -
178 
179 
180 int32
181 syscall_dispatcher(uint32 call_num, void *args, uint64 *call_ret)
182 {
183 	bigtime_t startTime;
184 
185 //	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",
186 //		thread_get_current_thread_id(), call_num, arg0, arg1, arg2, arg3, arg4);
187 
188 	user_debug_pre_syscall(call_num, args);
189 
190 	startTime = system_time();
191 
192 	switch (call_num) {
193 		// the cases are auto-generated
194 		#include "syscall_dispatcher.h"
195 
196 		default:
197 			*call_ret = (uint64)B_BAD_VALUE;
198 	}
199 
200 	user_debug_post_syscall(call_num, args, *call_ret, startTime);
201 
202 //	dprintf("syscall_dispatcher: done with syscall 0x%x\n", call_num);
203 
204 	return B_HANDLED_INTERRUPT;
205 }
206 
207 
208 status_t
209 generic_syscall_init(void)
210 {
211 	list_init(&sGenericSyscalls);
212 	if (mutex_init(&sGenericSyscallLock, "generic syscall") != B_OK) {
213 		panic("generic_syscall_init(): mutex init failed");
214 		return B_ERROR;
215 	}
216 
217 #if	ENABLE_TRACING && defined(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 #ifdef 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 #ifdef 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 #ifdef 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