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.
8 /*! Big case statement for dispatching syscalls, as well as the generic
18 #include <TypeConstants.h>
20 #include <arch_config.h>
21 #include <arch/system_info.h>
24 #include <disk_device_manager/ddm_userland_interface.h>
26 #include <frame_buffer_console.h>
28 #include <fs/node_monitor.h>
29 #include <generic_syscall.h>
34 #include <ksyscalls.h>
35 #include <ksystem_info.h>
36 #include <messaging.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>
44 #include <sys/resource.h>
45 #include <system_profiler.h>
48 #include <user_atomic.h>
49 #include <user_mutex.h>
50 #include <usergroup.h>
51 #include <UserTimer.h>
52 #include <util/AutoLock.h>
55 #include <wait_for_objects.h>
57 #include "syscall_numbers.h"
60 typedef struct generic_syscall generic_syscall
;
62 struct generic_syscall
: DoublyLinkedListLinkImpl
<generic_syscall
> {
63 char subsystem
[B_FILE_NAME_LENGTH
];
69 ConditionVariable unused_condition
;
70 generic_syscall
* previous
;
73 typedef DoublyLinkedList
<generic_syscall
> GenericSyscallList
;
76 static mutex sGenericSyscallLock
= MUTEX_INITIALIZER("generic syscall");
77 static GenericSyscallList sGenericSyscalls
;
81 static int dump_syscall_tracing(int argc
, char** argv
);
85 static generic_syscall
*
86 find_generic_syscall(const char* subsystem
)
88 ASSERT_LOCKED_MUTEX(&sGenericSyscallLock
);
90 GenericSyscallList::Iterator iterator
= sGenericSyscalls
.GetIterator();
92 while (generic_syscall
* syscall
= iterator
.Next()) {
93 if (!strcmp(syscall
->subsystem
, subsystem
))
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.
107 static inline status_t
108 _user_generic_syscall(const char* userSubsystem
, uint32 function
,
109 void* buffer
, size_t bufferSize
)
111 char subsystem
[B_FILE_NAME_LENGTH
];
113 if (!IS_USER_ADDRESS(userSubsystem
)
114 || user_strlcpy(subsystem
, userSubsystem
, sizeof(subsystem
)) < B_OK
)
115 return B_BAD_ADDRESS
;
117 //dprintf("generic_syscall(subsystem = \"%s\", function = %lu)\n", subsystem, function);
119 MutexLocker
locker(sGenericSyscallLock
);
121 generic_syscall
* syscall
= find_generic_syscall(subsystem
);
123 return B_NAME_NOT_FOUND
;
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
;
131 // special info syscall
132 if (bufferSize
!= sizeof(uint32
))
135 uint32 requestedVersion
;
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
)
143 // return current version
144 return user_memcpy(buffer
, &syscall
->version
, sizeof(uint32
));
147 while (syscall
!= NULL
) {
148 generic_syscall
* next
;
150 if (syscall
->valid
) {
151 syscall
->use_count
++;
155 = syscall
->hook(subsystem
, function
, buffer
, bufferSize
);
159 if (--syscall
->use_count
== 0)
160 syscall
->unused_condition
.NotifyAll();
162 if (status
!= B_BAD_HANDLER
)
166 // the syscall may have been removed in the mean time
167 next
= find_generic_syscall(subsystem
);
169 syscall
= syscall
->previous
;
174 return B_NAME_NOT_FOUND
;
179 _user_is_computer_on(void)
189 syscall_dispatcher(uint32 callIndex
, void* args
, uint64
* _returnValue
)
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);
196 user_debug_pre_syscall(callIndex
, args
);
198 startTime
= system_time();
201 // the cases are auto-generated
202 #include "syscall_dispatcher.h"
205 *_returnValue
= (uint64
)B_BAD_VALUE
;
208 user_debug_post_syscall(callIndex
, args
, *_returnValue
, startTime
);
210 // dprintf("syscall_dispatcher: done with syscall 0x%x\n", callIndex);
212 return B_HANDLED_INTERRUPT
;
217 generic_syscall_init(void)
219 new(&sGenericSyscalls
) GenericSyscallList
;
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
236 // #pragma mark - public API
240 register_generic_syscall(const char* subsystem
, syscall_hook hook
,
241 uint32 version
, uint32 flags
)
246 MutexLocker
_(sGenericSyscallLock
);
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
;
254 if ((previous
->flags
& B_SYSCALL_NOT_REPLACEABLE
) != 0)
255 return B_NOT_ALLOWED
;
258 generic_syscall
* syscall
= new(std::nothrow
) generic_syscall
;
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");
271 sGenericSyscalls
.Add(syscall
);
273 if (previous
!= NULL
)
274 sGenericSyscalls
.Remove(previous
);
281 unregister_generic_syscall(const char* subsystem
, uint32 version
)
283 // TODO: we should only remove the syscall with the matching version
286 MutexLocker
locker(sGenericSyscallLock
);
288 generic_syscall
* syscall
= find_generic_syscall(subsystem
);
290 return B_NAME_NOT_FOUND
;
292 syscall
->valid
= false;
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
);
305 if (syscall
->previous
!= NULL
) {
306 // reestablish the old syscall
307 sGenericSyscalls
.Add(syscall
->previous
);
310 sGenericSyscalls
.Remove(syscall
);
318 // #pragma mark - syscall tracing
323 namespace SyscallTracing
{
327 get_syscall_name(uint32 syscall
)
329 if (syscall
>= (uint32
)kSyscallCount
)
330 return "<invalid syscall number>";
332 return kExtendedSyscallInfos
[syscall
].name
;
336 class PreSyscall
: public AbstractTraceEntry
{
338 PreSyscall(uint32 syscall
, const void* parameters
)
343 if (syscall
< (uint32
)kSyscallCount
) {
344 fParameters
= alloc_tracing_buffer_memcpy(parameters
,
345 kSyscallInfos
[syscall
].parameter_size
, false);
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
)
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);
372 virtual void AddDump(TraceOutput
& out
)
374 out
.Print("syscall pre: %s(", get_syscall_name(fSyscall
));
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
;
385 bool printValue
= true;
386 switch (paramInfo
.type
) {
388 value
= *(uint8
*)data
;
391 value
= *(uint16
*)data
;
394 value
= *(uint32
*)data
;
397 value
= *(uint64
*)data
;
400 value
= (uint64
)*(void**)data
;
403 if (stringIndex
< MAX_PARAM_STRINGS
404 && fSyscall
!= SYSCALL_KTRACE_OUTPUT
) {
405 out
.Print("%s\"%s\"",
406 (i
== 0 ? "" : ", "),
407 fParameterStrings
[stringIndex
++]);
410 value
= (uint64
)*(void**)data
;
415 out
.Print("%s%#" B_PRIx64
, (i
== 0 ? "" : ", "), value
);
423 enum { MAX_PARAM_STRINGS
= 3 };
427 const char* fParameterStrings
[MAX_PARAM_STRINGS
];
431 class PostSyscall
: public AbstractTraceEntry
{
433 PostSyscall(uint32 syscall
, uint64 returnValue
)
436 fReturnValue(returnValue
)
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 "
449 virtual void AddDump(TraceOutput
& out
)
451 out
.Print("syscall post: %s() -> %#" B_PRIx64
,
452 get_syscall_name(fSyscall
), fReturnValue
);
460 } // namespace SyscallTracing
463 extern "C" void trace_pre_syscall(uint32 syscallNumber
, const void* parameters
);
466 trace_pre_syscall(uint32 syscallNumber
, const void* parameters
)
468 #if SYSCALL_TRACING_IGNORE_KTRACE_OUTPUT
469 if (syscallNumber
!= SYSCALL_KTRACE_OUTPUT
)
472 new(std::nothrow
) SyscallTracing::PreSyscall(syscallNumber
, parameters
);
477 extern "C" void trace_post_syscall(int syscallNumber
, uint64 returnValue
);
480 trace_post_syscall(int syscallNumber
, uint64 returnValue
)
482 #if SYSCALL_TRACING_IGNORE_KTRACE_OUTPUT
483 if (syscallNumber
!= SYSCALL_KTRACE_OUTPUT
)
486 new(std::nothrow
) SyscallTracing::PostSyscall(syscallNumber
,
492 using namespace SyscallTracing
;
494 class SyscallWrapperTraceFilter
: public WrapperTraceFilter
{
496 virtual void Init(TraceFilter
* filter
, int direction
, bool continued
)
499 fHitThreadLimit
= false;
500 fDirection
= direction
;
503 fPendingThreadCount
= 0;
506 virtual bool Filter(const TraceEntry
* _entry
, LazyTraceOutput
& out
)
512 return fFilter
->Filter(_entry
, out
);
514 if (const PreSyscall
* entry
= dynamic_cast<const PreSyscall
*>(_entry
)) {
515 _RemovePendingThread(entry
->ThreadID());
517 bool accepted
= fFilter
->Filter(entry
, out
);
519 _AddPendingThread(entry
->ThreadID());
522 } else if (const PostSyscall
* entry
523 = dynamic_cast<const PostSyscall
*>(_entry
)) {
524 bool wasPending
= _RemovePendingThread(entry
->ThreadID());
526 return wasPending
|| fFilter
->Filter(entry
, out
);
528 } else if (const AbstractTraceEntry
* entry
529 = dynamic_cast<const AbstractTraceEntry
*>(_entry
)) {
530 bool isPending
= _IsPendingThread(entry
->ThreadID());
532 return isPending
|| fFilter
->Filter(entry
, out
);
535 return fFilter
->Filter(_entry
, out
);
539 bool HitThreadLimit() const
541 return fHitThreadLimit
;
544 int Direction() const
551 MAX_PENDING_THREADS
= 32
554 bool _AddPendingThread(thread_id thread
)
556 int32 index
= _PendingThreadIndex(thread
);
560 if (fPendingThreadCount
== MAX_PENDING_THREADS
) {
561 fHitThreadLimit
= true;
565 fPendingThreads
[fPendingThreadCount
++] = thread
;
569 bool _RemovePendingThread(thread_id thread
)
571 int32 index
= _PendingThreadIndex(thread
);
575 if (index
+ 1 < fPendingThreadCount
) {
576 memmove(fPendingThreads
+ index
, fPendingThreads
+ index
+ 1,
577 fPendingThreadCount
- index
- 1);
580 fPendingThreadCount
--;
584 bool _IsPendingThread(thread_id thread
)
586 return _PendingThreadIndex(thread
) >= 0;
589 int32
_PendingThreadIndex(thread_id thread
)
591 for (int32 i
= 0; i
< fPendingThreadCount
; i
++) {
592 if (fPendingThreads
[i
] == thread
)
598 TraceFilter
* fFilter
;
599 thread_id fPendingThreads
[MAX_PENDING_THREADS
];
600 int32 fPendingThreadCount
;
602 bool fHitThreadLimit
;
606 static SyscallWrapperTraceFilter sFilter
;
609 dump_syscall_tracing(int argc
, char** argv
)
611 new(&sFilter
) SyscallWrapperTraceFilter
;
612 int result
= dump_tracing(argc
, argv
, &sFilter
);
614 if (sFilter
.HitThreadLimit()) {
615 kprintf("Warning: The thread buffer was too small to track all "
617 } else if (sFilter
.HitThreadLimit()) {
618 kprintf("Warning: Can't track syscalls backwards!\n");
625 #endif // SYSCALL_TRACING
629 * kSyscallCount and kSyscallInfos here
631 // generated by gensyscalls
632 #include "syscall_table.h"