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