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
61typedef struct generic_syscall generic_syscall;
62
63struct 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
74typedef DoublyLinkedList<generic_syscall> GenericSyscallList;
75
76
77static mutex sGenericSyscallLock = MUTEX_INITIALIZER("generic syscall");
78static GenericSyscallList sGenericSyscalls;
79
80
81status_t _user_generic_syscall(const char* userSubsystem, uint32 function,
82	void* buffer, size_t bufferSize);
83int _user_is_computer_on(void);
84
85
86#if SYSCALL_TRACING
87static int dump_syscall_tracing(int argc, char** argv);
88#endif
89
90
91static generic_syscall*
92find_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*/
113status_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
184int
185_user_is_computer_on(void)
186{
187	return 1;
188}
189
190
191//	#pragma mark -
192
193
194int32
195syscall_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
222status_t
223generic_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
245status_t
246register_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
286status_t
287unregister_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
329namespace SyscallTracing {
330
331
332static const char*
333get_syscall_name(uint32 syscall)
334{
335	if (syscall >= (uint32)kSyscallCount)
336		return "<invalid syscall number>";
337
338	return kExtendedSyscallInfos[syscall].name;
339}
340
341
342class 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
437class 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
469extern "C" void trace_pre_syscall(uint32 syscallNumber, const void* parameters);
470
471void
472trace_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
483extern "C" void trace_post_syscall(int syscallNumber, uint64 returnValue);
484
485void
486trace_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
498using namespace SyscallTracing;
499
500class SyscallWrapperTraceFilter : public WrapperTraceFilter {
501public:
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
555private:
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
612static SyscallWrapperTraceFilter sFilter;
613
614static int
615dump_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