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
60typedef struct generic_syscall generic_syscall;
61
62struct 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
73typedef DoublyLinkedList<generic_syscall> GenericSyscallList;
74
75
76static mutex sGenericSyscallLock = MUTEX_INITIALIZER("generic syscall");
77static GenericSyscallList sGenericSyscalls;
78
79
80#if SYSCALL_TRACING
81static int dump_syscall_tracing(int argc, char** argv);
82#endif
83
84
85static generic_syscall*
86find_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*/
107static 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
178static inline int
179_user_is_computer_on(void)
180{
181	return 1;
182}
183
184
185//	#pragma mark -
186
187
188int32
189syscall_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
216status_t
217generic_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
239status_t
240register_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
280status_t
281unregister_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
323namespace SyscallTracing {
324
325
326static const char*
327get_syscall_name(uint32 syscall)
328{
329	if (syscall >= (uint32)kSyscallCount)
330		return "<invalid syscall number>";
331
332	return kExtendedSyscallInfos[syscall].name;
333}
334
335
336class 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
431class 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
463extern "C" void trace_pre_syscall(uint32 syscallNumber, const void* parameters);
464
465void
466trace_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
477extern "C" void trace_post_syscall(int syscallNumber, uint64 returnValue);
478
479void
480trace_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
492using namespace SyscallTracing;
493
494class SyscallWrapperTraceFilter : public WrapperTraceFilter {
495public:
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
549private:
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
606static SyscallWrapperTraceFilter sFilter;
607
608static int
609dump_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