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