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