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