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