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