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