xref: /haiku/src/system/kernel/syscalls.cpp (revision c237c4ce593ee823d9867fd997e51e4c447f5623)
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 	bigtime_t startTime;
198 
199 //	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",
200 //		thread_get_current_thread_id(), call_num, arg0, arg1, arg2, arg3, arg4);
201 
202 	user_debug_pre_syscall(callIndex, args);
203 
204 	startTime = system_time();
205 
206 	switch (callIndex) {
207 		// the cases are auto-generated
208 		#include "syscall_dispatcher.h"
209 
210 		default:
211 			*_returnValue = (uint64)B_BAD_VALUE;
212 	}
213 
214 	user_debug_post_syscall(callIndex, args, *_returnValue, startTime);
215 
216 //	dprintf("syscall_dispatcher: done with syscall 0x%x\n", callIndex);
217 
218 	return B_HANDLED_INTERRUPT;
219 }
220 
221 
222 status_t
223 generic_syscall_init(void)
224 {
225 	new(&sGenericSyscalls) GenericSyscallList;
226 
227 #if	SYSCALL_TRACING
228 	add_debugger_command_etc("straced", &dump_syscall_tracing,
229 		"Dump recorded syscall trace entries",
230 		"Prints recorded trace entries. It is wrapper for the \"traced\"\n"
231 		"command and supports all of its command line options (though\n"
232 		"backward tracing doesn't really work). The difference is that if a\n"
233 		"pre syscall trace entry is encountered, the corresponding post\n"
234 		"syscall traced entry is also printed, even if it doesn't match the\n"
235 		"given filter.\n", 0);
236 #endif	// ENABLE_TRACING
237 
238 	return B_OK;
239 }
240 
241 
242 // #pragma mark - public API
243 
244 
245 status_t
246 register_generic_syscall(const char* subsystem, syscall_hook hook,
247 	uint32 version, uint32 flags)
248 {
249 	if (hook == NULL)
250 		return B_BAD_VALUE;
251 
252 	MutexLocker _(sGenericSyscallLock);
253 
254 	generic_syscall* previous = find_generic_syscall(subsystem);
255 	if (previous != NULL) {
256 		if ((flags & B_DO_NOT_REPLACE_SYSCALL) != 0
257 			|| version < previous->version) {
258 			return B_NAME_IN_USE;
259 		}
260 		if ((previous->flags & B_SYSCALL_NOT_REPLACEABLE) != 0)
261 			return B_NOT_ALLOWED;
262 	}
263 
264 	generic_syscall* syscall = new(std::nothrow) generic_syscall;
265 	if (syscall == NULL)
266 		return B_NO_MEMORY;
267 
268 	strlcpy(syscall->subsystem, subsystem, sizeof(syscall->subsystem));
269 	syscall->hook = hook;
270 	syscall->version = version;
271 	syscall->flags = flags;
272 	syscall->use_count = 0;
273 	syscall->valid = true;
274 	syscall->previous = previous;
275 	syscall->unused_condition.Init(syscall, "syscall unused");
276 
277 	sGenericSyscalls.Add(syscall);
278 
279 	if (previous != NULL)
280 		sGenericSyscalls.Remove(previous);
281 
282 	return B_OK;
283 }
284 
285 
286 status_t
287 unregister_generic_syscall(const char* subsystem, uint32 version)
288 {
289 	// TODO: we should only remove the syscall with the matching version
290 
291 	while (true) {
292 		MutexLocker locker(sGenericSyscallLock);
293 
294 		generic_syscall* syscall = find_generic_syscall(subsystem);
295 		if (syscall == NULL)
296 			return B_NAME_NOT_FOUND;
297 
298 		syscall->valid = false;
299 
300 		if (syscall->use_count != 0) {
301 			// Wait until the syscall isn't in use anymore
302 			ConditionVariableEntry entry;
303 			syscall->unused_condition.Add(&entry);
304 
305 			locker.Unlock();
306 
307 			entry.Wait();
308 			continue;
309 		}
310 
311 		if (syscall->previous != NULL) {
312 			// reestablish the old syscall
313 			sGenericSyscalls.Add(syscall->previous);
314 		}
315 
316 		sGenericSyscalls.Remove(syscall);
317 		delete syscall;
318 
319 		return B_OK;
320 	}
321 }
322 
323 
324 // #pragma mark - syscall tracing
325 
326 
327 #if SYSCALL_TRACING
328 
329 namespace SyscallTracing {
330 
331 
332 static const char*
333 get_syscall_name(uint32 syscall)
334 {
335 	if (syscall >= (uint32)kSyscallCount)
336 		return "<invalid syscall number>";
337 
338 	return kExtendedSyscallInfos[syscall].name;
339 }
340 
341 
342 class PreSyscall : public AbstractTraceEntry {
343 	public:
344 		PreSyscall(uint32 syscall, const void* parameters)
345 			:
346 			fSyscall(syscall),
347 			fParameters(NULL)
348 		{
349 			if (syscall < (uint32)kSyscallCount) {
350 				fParameters = alloc_tracing_buffer_memcpy(parameters,
351 					kSyscallInfos[syscall].parameter_size, false);
352 
353 				// copy string parameters, if any
354 				if (fParameters != NULL && syscall != SYSCALL_KTRACE_OUTPUT) {
355 					int32 stringIndex = 0;
356 					const extended_syscall_info& syscallInfo
357 						= kExtendedSyscallInfos[fSyscall];
358 					for (int i = 0; i < syscallInfo.parameter_count; i++) {
359 						const syscall_parameter_info& paramInfo
360 							= syscallInfo.parameters[i];
361 						if (paramInfo.type != B_STRING_TYPE)
362 							continue;
363 
364 						const uint8* data
365 							= (uint8*)fParameters + paramInfo.offset;
366 						if (stringIndex < MAX_PARAM_STRINGS) {
367 							fParameterStrings[stringIndex++]
368 								= alloc_tracing_buffer_strcpy(
369 									*(const char**)data, 64, true);
370 						}
371 					}
372 				}
373 			}
374 
375 			Initialized();
376 		}
377 
378 		virtual void AddDump(TraceOutput& out)
379 		{
380 			out.Print("syscall pre:  %s(", get_syscall_name(fSyscall));
381 
382 			if (fParameters != NULL) {
383 				int32 stringIndex = 0;
384 				const extended_syscall_info& syscallInfo
385 					= kExtendedSyscallInfos[fSyscall];
386 				for (int i = 0; i < syscallInfo.parameter_count; i++) {
387 					const syscall_parameter_info& paramInfo
388 						= syscallInfo.parameters[i];
389 					const uint8* data = (uint8*)fParameters + paramInfo.offset;
390 					uint64 value = 0;
391 					bool printValue = true;
392 					switch (paramInfo.type) {
393 						case B_INT8_TYPE:
394 							value = *(uint8*)data;
395 							break;
396 						case B_INT16_TYPE:
397 							value = *(uint16*)data;
398 							break;
399 						case B_INT32_TYPE:
400 							value = *(uint32*)data;
401 							break;
402 						case B_INT64_TYPE:
403 							value = *(uint64*)data;
404 							break;
405 						case B_POINTER_TYPE:
406 							value = (uint64)*(void**)data;
407 							break;
408 						case B_STRING_TYPE:
409 							if (stringIndex < MAX_PARAM_STRINGS
410 								&& fSyscall != SYSCALL_KTRACE_OUTPUT) {
411 								out.Print("%s\"%s\"",
412 									(i == 0 ? "" : ", "),
413 									fParameterStrings[stringIndex++]);
414 								printValue = false;
415 							} else
416 								value = (uint64)*(void**)data;
417 							break;
418 					}
419 
420 					if (printValue)
421 						out.Print("%s%#" B_PRIx64, (i == 0 ? "" : ", "), value);
422 				}
423 			}
424 
425 			out.Print(")");
426 		}
427 
428 	private:
429 		enum { MAX_PARAM_STRINGS = 3 };
430 
431 		uint32		fSyscall;
432 		void*		fParameters;
433 		const char*	fParameterStrings[MAX_PARAM_STRINGS];
434 };
435 
436 
437 class PostSyscall : public AbstractTraceEntry {
438 	public:
439 		PostSyscall(uint32 syscall, uint64 returnValue)
440 			:
441 			fSyscall(syscall),
442 			fReturnValue(returnValue)
443 		{
444 			Initialized();
445 #if 0
446 			if (syscall < (uint32)kSyscallCount
447 				&&  returnValue != (returnValue & 0xffffffff)
448 				&& kExtendedSyscallInfos[syscall].return_type.size <= 4) {
449 				panic("syscall return value 64 bit although it should be 32 "
450 					"bit");
451 			}
452 #endif
453 		}
454 
455 		virtual void AddDump(TraceOutput& out)
456 		{
457 			out.Print("syscall post: %s() -> %#" B_PRIx64,
458 				get_syscall_name(fSyscall), fReturnValue);
459 		}
460 
461 	private:
462 		uint32	fSyscall;
463 		uint64	fReturnValue;
464 };
465 
466 }	// namespace SyscallTracing
467 
468 
469 extern "C" void trace_pre_syscall(uint32 syscallNumber, const void* parameters);
470 
471 void
472 trace_pre_syscall(uint32 syscallNumber, const void* parameters)
473 {
474 #if SYSCALL_TRACING_IGNORE_KTRACE_OUTPUT
475 	if (syscallNumber != SYSCALL_KTRACE_OUTPUT)
476 #endif
477 	{
478 		new(std::nothrow) SyscallTracing::PreSyscall(syscallNumber, parameters);
479 	}
480 }
481 
482 
483 extern "C" void trace_post_syscall(int syscallNumber, uint64 returnValue);
484 
485 void
486 trace_post_syscall(int syscallNumber, uint64 returnValue)
487 {
488 #if SYSCALL_TRACING_IGNORE_KTRACE_OUTPUT
489 	if (syscallNumber != SYSCALL_KTRACE_OUTPUT)
490 #endif
491 	{
492 		new(std::nothrow) SyscallTracing::PostSyscall(syscallNumber,
493 			returnValue);
494 	}
495 }
496 
497 
498 using namespace SyscallTracing;
499 
500 class SyscallWrapperTraceFilter : public WrapperTraceFilter {
501 public:
502 	virtual void Init(TraceFilter* filter, int direction, bool continued)
503 	{
504 		fFilter = filter;
505 		fHitThreadLimit = false;
506 		fDirection = direction;
507 
508 		if (!continued)
509 			fPendingThreadCount = 0;
510 	}
511 
512 	virtual bool Filter(const TraceEntry* _entry, LazyTraceOutput& out)
513 	{
514 		if (fFilter == NULL)
515 			return true;
516 
517 		if (fDirection < 0)
518 			return fFilter->Filter(_entry, out);
519 
520 		if (const PreSyscall* entry = dynamic_cast<const PreSyscall*>(_entry)) {
521 			_RemovePendingThread(entry->ThreadID());
522 
523 			bool accepted = fFilter->Filter(entry, out);
524 			if (accepted)
525 				_AddPendingThread(entry->ThreadID());
526 			return accepted;
527 
528 		} else if (const PostSyscall* entry
529 				= dynamic_cast<const PostSyscall*>(_entry)) {
530 			bool wasPending = _RemovePendingThread(entry->ThreadID());
531 
532 			return wasPending || fFilter->Filter(entry, out);
533 
534 		} else if (const AbstractTraceEntry* entry
535 				= dynamic_cast<const AbstractTraceEntry*>(_entry)) {
536 			bool isPending = _IsPendingThread(entry->ThreadID());
537 
538 			return isPending || fFilter->Filter(entry, out);
539 
540 		} else {
541 			return fFilter->Filter(_entry, out);
542 		}
543 	}
544 
545 	bool HitThreadLimit() const
546 	{
547 		return fHitThreadLimit;
548 	}
549 
550 	int Direction() const
551 	{
552 		return fDirection;
553 	}
554 
555 private:
556 	enum {
557 		MAX_PENDING_THREADS = 32
558 	};
559 
560 	bool _AddPendingThread(thread_id thread)
561 	{
562 		int32 index = _PendingThreadIndex(thread);
563 		if (index >= 0)
564 			return true;
565 
566 		if (fPendingThreadCount == MAX_PENDING_THREADS) {
567 			fHitThreadLimit = true;
568 			return false;
569 		}
570 
571 		fPendingThreads[fPendingThreadCount++] = thread;
572 		return true;
573 	}
574 
575 	bool _RemovePendingThread(thread_id thread)
576 	{
577 		int32 index = _PendingThreadIndex(thread);
578 		if (index < 0)
579 			return false;
580 
581 		if (index + 1 < fPendingThreadCount) {
582 			memmove(fPendingThreads + index, fPendingThreads + index + 1,
583 				fPendingThreadCount - index - 1);
584 		}
585 
586 		fPendingThreadCount--;
587 		return true;
588 	}
589 
590 	bool _IsPendingThread(thread_id thread)
591 	{
592 		return _PendingThreadIndex(thread) >= 0;
593 	}
594 
595 	int32 _PendingThreadIndex(thread_id thread)
596 	{
597 		for (int32 i = 0; i < fPendingThreadCount; i++) {
598 			if (fPendingThreads[i] == thread)
599 				return i;
600 		}
601 		return -1;
602 	}
603 
604 	TraceFilter*	fFilter;
605 	thread_id		fPendingThreads[MAX_PENDING_THREADS];
606 	int32			fPendingThreadCount;
607 	int				fDirection;
608 	bool			fHitThreadLimit;
609 };
610 
611 
612 static SyscallWrapperTraceFilter sFilter;
613 
614 static int
615 dump_syscall_tracing(int argc, char** argv)
616 {
617 	new(&sFilter) SyscallWrapperTraceFilter;
618 	int result = dump_tracing(argc, argv, &sFilter);
619 
620 	if (sFilter.HitThreadLimit()) {
621 		kprintf("Warning: The thread buffer was too small to track all "
622 			"threads!\n");
623 	} else if (sFilter.HitThreadLimit()) {
624 		kprintf("Warning: Can't track syscalls backwards!\n");
625 	}
626 
627 	return result;
628 }
629 
630 
631 #endif	// SYSCALL_TRACING
632 
633 
634 /*
635  * kSyscallCount and kSyscallInfos here
636  */
637 // generated by gensyscalls
638 #include "syscall_table.h"
639