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