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