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