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