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 <sem.h> 20 #include <port.h> 21 #include <cpu.h> 22 #include <arch_config.h> 23 #include <disk_device_manager/ddm_userland_interface.h> 24 #include <sys/resource.h> 25 #include <fs/fd.h> 26 #include <fs/node_monitor.h> 27 #include <kimage.h> 28 #include <ksignal.h> 29 #include <real_time_clock.h> 30 #include <safemode.h> 31 #include <system_info.h> 32 #include <tracing.h> 33 #include <user_atomic.h> 34 #include <arch/system_info.h> 35 #include <messaging.h> 36 #include <frame_buffer_console.h> 37 #include <usergroup.h> 38 #include <wait_for_objects.h> 39 40 #include <malloc.h> 41 #include <string.h> 42 43 #include "syscall_numbers.h" 44 45 46 typedef struct generic_syscall generic_syscall; 47 48 struct generic_syscall { 49 list_link link; 50 char subsystem[B_FILE_NAME_LENGTH]; 51 syscall_hook hook; 52 uint32 version; 53 uint32 flags; 54 generic_syscall *previous; 55 }; 56 57 static struct mutex sGenericSyscallLock; 58 static struct list sGenericSyscalls; 59 60 61 static generic_syscall * 62 find_generic_syscall(const char *subsystem) 63 { 64 generic_syscall *syscall = NULL; 65 66 ASSERT_LOCKED_MUTEX(&sGenericSyscallLock); 67 68 while ((syscall = (generic_syscall*)list_get_next_item(&sGenericSyscalls, 69 syscall)) != NULL) { 70 if (!strcmp(syscall->subsystem, subsystem)) 71 return syscall; 72 } 73 74 return NULL; 75 } 76 77 78 /** Calls the generic syscall subsystem if any. 79 * Also handles the special generic syscall function \c B_SYSCALL_INFO. 80 * Returns \c B_NAME_NOT_FOUND if either the subsystem was not found, or 81 * the subsystem does not support the requested function. 82 * All other return codes are depending on the generic syscall implementation. 83 */ 84 85 static inline status_t 86 _user_generic_syscall(const char *userSubsystem, uint32 function, 87 void *buffer, size_t bufferSize) 88 { 89 char subsystem[B_FILE_NAME_LENGTH]; 90 generic_syscall *syscall; 91 status_t status = B_NAME_NOT_FOUND; 92 93 if (!IS_USER_ADDRESS(userSubsystem) 94 || user_strlcpy(subsystem, userSubsystem, sizeof(subsystem)) < B_OK) 95 return B_BAD_ADDRESS; 96 97 //dprintf("generic_syscall(subsystem = \"%s\", function = %lu)\n", subsystem, function); 98 99 mutex_lock(&sGenericSyscallLock); 100 101 syscall = find_generic_syscall(subsystem); 102 if (syscall == NULL) 103 goto out; 104 105 if (function >= B_RESERVED_SYSCALL_BASE) { 106 if (function != B_SYSCALL_INFO) { 107 // this is all we know 108 status = B_NAME_NOT_FOUND; 109 goto out; 110 } 111 112 // special info syscall 113 if (bufferSize != sizeof(uint32)) 114 status = B_BAD_VALUE; 115 else { 116 uint32 requestedVersion; 117 118 // retrieve old version 119 status = user_memcpy(&requestedVersion, buffer, sizeof(uint32)); 120 if (status == B_OK && requestedVersion != 0 && requestedVersion < syscall->version) 121 status = B_BAD_TYPE; 122 123 // return current version 124 if (status == B_OK) 125 status = user_memcpy(buffer, &syscall->version, sizeof(uint32)); 126 } 127 } else { 128 while (syscall != NULL) { 129 generic_syscall *next; 130 131 mutex_unlock(&sGenericSyscallLock); 132 133 status = syscall->hook(subsystem, function, buffer, bufferSize); 134 135 mutex_lock(&sGenericSyscallLock); 136 if (status != B_BAD_HANDLER) 137 break; 138 139 // the syscall may have been removed in the mean time 140 next = find_generic_syscall(subsystem); 141 if (next == syscall) 142 syscall = syscall->previous; 143 else 144 syscall = next; 145 } 146 147 if (syscall == NULL) 148 status = B_NAME_NOT_FOUND; 149 } 150 151 out: 152 mutex_unlock(&sGenericSyscallLock); 153 return status; 154 } 155 156 157 static inline int 158 _user_is_computer_on(void) 159 { 160 return 1; 161 } 162 163 // map to the arch specific call 164 165 static inline int64 166 _user_restore_signal_frame() 167 { 168 syscall_64_bit_return_value(); 169 170 return arch_restore_signal_frame(); 171 } 172 173 174 // TODO: Replace when networking code is added to the build. 175 176 static inline int 177 _user_socket(int family, int type, int proto) 178 { 179 return 0; 180 } 181 182 183 // #pragma mark - 184 185 186 int32 187 syscall_dispatcher(uint32 call_num, void *args, uint64 *call_ret) 188 { 189 bigtime_t startTime; 190 191 // 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", 192 // thread_get_current_thread_id(), call_num, arg0, arg1, arg2, arg3, arg4); 193 194 user_debug_pre_syscall(call_num, args); 195 196 startTime = system_time(); 197 198 switch (call_num) { 199 // the cases are auto-generated 200 #include "syscall_dispatcher.h" 201 202 default: 203 *call_ret = (uint64)B_BAD_VALUE; 204 } 205 206 user_debug_post_syscall(call_num, args, *call_ret, startTime); 207 208 // dprintf("syscall_dispatcher: done with syscall 0x%x\n", call_num); 209 210 return B_HANDLED_INTERRUPT; 211 } 212 213 214 status_t 215 generic_syscall_init(void) 216 { 217 list_init(&sGenericSyscalls); 218 return mutex_init(&sGenericSyscallLock, "generic syscall"); 219 } 220 221 222 // #pragma mark - 223 // public API 224 225 226 status_t 227 register_generic_syscall(const char *subsystem, syscall_hook hook, 228 uint32 version, uint32 flags) 229 { 230 struct generic_syscall *previous, *syscall; 231 status_t status; 232 233 if (hook == NULL) 234 return B_BAD_VALUE; 235 236 mutex_lock(&sGenericSyscallLock); 237 238 previous = find_generic_syscall(subsystem); 239 if (previous != NULL) { 240 if ((flags & B_DO_NOT_REPLACE_SYSCALL) != 0 241 || version < previous->version) { 242 status = B_NAME_IN_USE; 243 goto out; 244 } 245 if (previous->flags & B_SYSCALL_NOT_REPLACEABLE) { 246 status = B_NOT_ALLOWED; 247 goto out; 248 } 249 } 250 251 syscall = (generic_syscall *)malloc(sizeof(struct generic_syscall)); 252 if (syscall == NULL) { 253 status = B_NO_MEMORY; 254 goto out; 255 } 256 257 strlcpy(syscall->subsystem, subsystem, sizeof(syscall->subsystem)); 258 syscall->hook = hook; 259 syscall->version = version; 260 syscall->flags = flags; 261 syscall->previous = previous; 262 list_add_item(&sGenericSyscalls, syscall); 263 264 if (previous != NULL) 265 list_remove_link(&previous->link); 266 267 status = B_OK; 268 269 out: 270 mutex_unlock(&sGenericSyscallLock); 271 return status; 272 } 273 274 275 status_t 276 unregister_generic_syscall(const char *subsystem, uint32 version) 277 { 278 // ToDo: we should only remove the syscall with the matching version 279 generic_syscall *syscall; 280 status_t status; 281 282 mutex_lock(&sGenericSyscallLock); 283 284 syscall = find_generic_syscall(subsystem); 285 if (syscall != NULL) { 286 if (syscall->previous != NULL) { 287 // reestablish the old syscall 288 list_add_item(&sGenericSyscalls, syscall->previous); 289 } 290 list_remove_link(&syscall->link); 291 free(syscall); 292 status = B_OK; 293 } else 294 status = B_NAME_NOT_FOUND; 295 296 mutex_unlock(&sGenericSyscallLock); 297 return status; 298 } 299 300 301 // #pragma mark - syscall tracing 302 303 304 #ifdef SYSCALL_TRACING 305 306 namespace SyscallTracing { 307 308 309 static const char* 310 get_syscall_name(uint32 syscall) 311 { 312 if (syscall >= (uint32)kSyscallCount) 313 return "<invalid syscall number>"; 314 315 return kExtendedSyscallInfos[syscall].name; 316 } 317 318 319 class PreSyscall : public AbstractTraceEntry { 320 public: 321 PreSyscall(uint32 syscall, const void* parameters) 322 : 323 fSyscall(syscall), 324 fParameters(NULL) 325 { 326 if (syscall < (uint32)kSyscallCount) { 327 fParameters = alloc_tracing_buffer_memcpy(parameters, 328 kSyscallInfos[syscall].parameter_size, false); 329 330 // copy string parameters, if any 331 if (fParameters != NULL && syscall != SYSCALL_KTRACE_OUTPUT) { 332 int32 stringIndex = 0; 333 const extended_syscall_info& syscallInfo 334 = kExtendedSyscallInfos[fSyscall]; 335 for (int i = 0; i < syscallInfo.parameter_count; i++) { 336 const syscall_parameter_info& paramInfo 337 = syscallInfo.parameters[i]; 338 if (paramInfo.type != B_STRING_TYPE) 339 continue; 340 341 const uint8* data 342 = (uint8*)fParameters + paramInfo.offset; 343 if (stringIndex < MAX_PARAM_STRINGS) { 344 fParameterStrings[stringIndex++] 345 = alloc_tracing_buffer_strcpy( 346 *(const char**)data, 64, true); 347 } 348 } 349 } 350 } 351 352 Initialized(); 353 } 354 355 virtual void AddDump(TraceOutput& out) 356 { 357 out.Print("syscall pre: %s(", get_syscall_name(fSyscall)); 358 359 if (fParameters != NULL) { 360 int32 stringIndex = 0; 361 const extended_syscall_info& syscallInfo 362 = kExtendedSyscallInfos[fSyscall]; 363 for (int i = 0; i < syscallInfo.parameter_count; i++) { 364 const syscall_parameter_info& paramInfo 365 = syscallInfo.parameters[i]; 366 const uint8* data = (uint8*)fParameters + paramInfo.offset; 367 uint64 value = 0; 368 bool printValue = true; 369 switch (paramInfo.type) { 370 case B_INT8_TYPE: 371 value = *(uint8*)data; 372 break; 373 case B_INT16_TYPE: 374 value = *(uint16*)data; 375 break; 376 case B_INT32_TYPE: 377 value = *(uint32*)data; 378 break; 379 case B_INT64_TYPE: 380 value = *(uint64*)data; 381 break; 382 case B_POINTER_TYPE: 383 value = (uint64)*(void**)data; 384 break; 385 case B_STRING_TYPE: 386 if (stringIndex < MAX_PARAM_STRINGS 387 && fSyscall != SYSCALL_KTRACE_OUTPUT) { 388 out.Print("%s\"%s\"", 389 (i == 0 ? "" : ", "), 390 fParameterStrings[stringIndex++]); 391 printValue = false; 392 } else 393 value = (uint64)*(void**)data; 394 break; 395 } 396 397 if (printValue) 398 out.Print("%s0x%llx", (i == 0 ? "" : ", "), value); 399 } 400 } 401 402 out.Print(")"); 403 } 404 405 private: 406 enum { MAX_PARAM_STRINGS = 3 }; 407 408 uint32 fSyscall; 409 void* fParameters; 410 const char* fParameterStrings[MAX_PARAM_STRINGS]; 411 }; 412 413 414 class PostSyscall : public AbstractTraceEntry { 415 public: 416 PostSyscall(uint32 syscall, uint64 returnValue) 417 : 418 fSyscall(syscall), 419 fReturnValue(returnValue) 420 { 421 Initialized(); 422 #if 0 423 if (syscall < (uint32)kSyscallCount 424 && returnValue != (returnValue & 0xffffffff) 425 && kExtendedSyscallInfos[syscall].return_type.size <= 4) { 426 panic("syscall return value 64 bit although it should be 32 " 427 "bit"); 428 } 429 #endif 430 } 431 432 virtual void AddDump(TraceOutput& out) 433 { 434 out.Print("syscall post: %s() -> 0x%llx", 435 get_syscall_name(fSyscall), fReturnValue); 436 } 437 438 private: 439 uint32 fSyscall; 440 uint64 fReturnValue; 441 }; 442 443 } // namespace SyscallTracing 444 445 446 extern "C" void trace_pre_syscall(uint32 syscallNumber, const void* parameters); 447 448 void 449 trace_pre_syscall(uint32 syscallNumber, const void* parameters) 450 { 451 #ifdef SYSCALL_TRACING_IGNORE_KTRACE_OUTPUT 452 if (syscallNumber != SYSCALL_KTRACE_OUTPUT) 453 #endif 454 { 455 new(std::nothrow) SyscallTracing::PreSyscall(syscallNumber, parameters); 456 } 457 } 458 459 460 extern "C" void trace_post_syscall(int syscallNumber, uint64 returnValue); 461 462 void 463 trace_post_syscall(int syscallNumber, uint64 returnValue) 464 { 465 #ifdef SYSCALL_TRACING_IGNORE_KTRACE_OUTPUT 466 if (syscallNumber != SYSCALL_KTRACE_OUTPUT) 467 #endif 468 { 469 new(std::nothrow) SyscallTracing::PostSyscall(syscallNumber, 470 returnValue); 471 } 472 } 473 474 475 #endif // SYSCALL_TRACING 476 477 478 /* 479 * kSyscallCount and kSyscallInfos here 480 */ 481 // generated by gensyscalls 482 #include "syscall_table.h" 483