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