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