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