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