1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
/*
* clone syscall for OpenRISC
*
* Copyright (c) 2010 Jonas Bonn <jonas@southpole.se>
* Copyright (C) 2003 John Williams <jwilliams@itee.uq.edu.au>
* Copyright (C) 2002,03 NEC Electronics Corporation
* Copyright (C) 2002,03 Miles Bader <miles@gnu.org>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License. See the file COPYING.LIB in the main
* directory of this archive for more details.
*
* OpenRISC port by Jonas Bonn <jonas@southpole.se>
*/
#include <errno.h>
#include <sys/syscall.h>
#include <sched.h>
#include <unistd.h>
/* The userland implementation is:
int clone (int (*fn)(void *arg), void *child_stack, int flags, void *arg, ...)
the kernel entry is:
int clone (long flags, void *child_stack)
*/
int
clone (int (*fn)(void *arg), void *child_stack, int flags, void *arg, ...)
{
int err;
/* OK, here's the skinny on this one...
* OR1K GCC does weird things with varargs functions... the last
* parameter is NEVER passed on the stack -- i.e. arg, in this case.
* So we need to push at least 'arg' onto the child stack so that
* the new thread can find it. Just to be totally safe, we'll
* push both 'fn' and 'arg'; that way we don't need to care what
* GCC does with parameters, whether they are passed in registers
* or on stack.
*/
/* Put 'fn' and 'arg' on child stack */
__asm__ __volatile__ (
"l.sw -4(%0),%1;"
"l.sw -8(%0),%2;"
:
: "r" (child_stack), "r" (fn), "r" (arg)
);
/* Sanity check the arguments */
err = -EINVAL;
if (!fn)
goto syscall_error;
if (!child_stack)
goto syscall_error;
err = INLINE_SYSCALL(clone, 2, flags, child_stack);
/* NB: from here you are in child thread or parent thread.
*
* Do not use any functions here that may write data _up_
* onto the stack because they will overwrite the child's
* thread descriptor... i.e. don't use printf
*/
if (err < 0)
goto syscall_error;
else if (err != 0) {
return err;
}
/* NB: from here you exclusively in child thread */
/* Grab 'fn' and 'arg' from child stack */
__asm__ __volatile__ (
"l.lwz %0,-4(%2);"
"l.lwz %1,-8(%2);"
: "=&r" (fn), "=r" (arg)
: "r" (child_stack)
: "0", "1"
);
_exit(fn(arg));
syscall_error:
__set_errno (-err);
return -1;
}
|