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