xref: /haiku/src/system/kernel/syscalls.cpp (revision ed6250c95736c0b55da79d6e9dd01369532260c0)
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