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