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