diff options
Diffstat (limited to 'arch/hexagon/kernel/process.c')
-rw-r--r-- | arch/hexagon/kernel/process.c | 100 |
1 files changed, 31 insertions, 69 deletions
diff --git a/arch/hexagon/kernel/process.c b/arch/hexagon/kernel/process.c index 9f6d7411b574..06ae9ffcabd5 100644 --- a/arch/hexagon/kernel/process.c +++ b/arch/hexagon/kernel/process.c | |||
@@ -26,33 +26,6 @@ | |||
26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
27 | 27 | ||
28 | /* | 28 | /* |
29 | * Kernel thread creation. The desired kernel function is "wrapped" | ||
30 | * in the kernel_thread_helper function, which does cleanup | ||
31 | * afterwards. | ||
32 | */ | ||
33 | static void __noreturn kernel_thread_helper(void *arg, int (*fn)(void *)) | ||
34 | { | ||
35 | do_exit(fn(arg)); | ||
36 | } | ||
37 | |||
38 | int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) | ||
39 | { | ||
40 | struct pt_regs regs; | ||
41 | |||
42 | memset(®s, 0, sizeof(regs)); | ||
43 | /* | ||
44 | * Yes, we're exploting illicit knowledge of the ABI here. | ||
45 | */ | ||
46 | regs.r00 = (unsigned long) arg; | ||
47 | regs.r01 = (unsigned long) fn; | ||
48 | pt_set_elr(®s, (unsigned long)kernel_thread_helper); | ||
49 | pt_set_kmode(®s); | ||
50 | |||
51 | return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); | ||
52 | } | ||
53 | EXPORT_SYMBOL(kernel_thread); | ||
54 | |||
55 | /* | ||
56 | * Program thread launch. Often defined as a macro in processor.h, | 29 | * Program thread launch. Often defined as a macro in processor.h, |
57 | * but we're shooting for a small footprint and it's not an inner-loop | 30 | * but we're shooting for a small footprint and it's not an inner-loop |
58 | * performance-critical operation. | 31 | * performance-critical operation. |
@@ -114,8 +87,7 @@ unsigned long thread_saved_pc(struct task_struct *tsk) | |||
114 | * Copy architecture-specific thread state | 87 | * Copy architecture-specific thread state |
115 | */ | 88 | */ |
116 | int copy_thread(unsigned long clone_flags, unsigned long usp, | 89 | int copy_thread(unsigned long clone_flags, unsigned long usp, |
117 | unsigned long unused, struct task_struct *p, | 90 | unsigned long arg, struct task_struct *p) |
118 | struct pt_regs *regs) | ||
119 | { | 91 | { |
120 | struct thread_info *ti = task_thread_info(p); | 92 | struct thread_info *ti = task_thread_info(p); |
121 | struct hexagon_switch_stack *ss; | 93 | struct hexagon_switch_stack *ss; |
@@ -125,61 +97,51 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, | |||
125 | childregs = (struct pt_regs *) (((unsigned long) ti + THREAD_SIZE) - | 97 | childregs = (struct pt_regs *) (((unsigned long) ti + THREAD_SIZE) - |
126 | sizeof(*childregs)); | 98 | sizeof(*childregs)); |
127 | 99 | ||
128 | memcpy(childregs, regs, sizeof(*childregs)); | ||
129 | ti->regs = childregs; | 100 | ti->regs = childregs; |
130 | 101 | ||
131 | /* | 102 | /* |
132 | * Establish kernel stack pointer and initial PC for new thread | 103 | * Establish kernel stack pointer and initial PC for new thread |
104 | * Note that unlike the usual situation, we do not copy the | ||
105 | * parent's callee-saved here; those are in pt_regs and whatever | ||
106 | * we leave here will be overridden on return to userland. | ||
133 | */ | 107 | */ |
134 | ss = (struct hexagon_switch_stack *) ((unsigned long) childregs - | 108 | ss = (struct hexagon_switch_stack *) ((unsigned long) childregs - |
135 | sizeof(*ss)); | 109 | sizeof(*ss)); |
136 | ss->lr = (unsigned long)ret_from_fork; | 110 | ss->lr = (unsigned long)ret_from_fork; |
137 | p->thread.switch_sp = ss; | 111 | p->thread.switch_sp = ss; |
112 | if (unlikely(p->flags & PF_KTHREAD)) { | ||
113 | memset(childregs, 0, sizeof(struct pt_regs)); | ||
114 | /* r24 <- fn, r25 <- arg */ | ||
115 | ss->r2524 = usp | ((u64)arg << 32); | ||
116 | pt_set_kmode(childregs); | ||
117 | return 0; | ||
118 | } | ||
119 | memcpy(childregs, current_pt_regs(), sizeof(*childregs)); | ||
120 | ss->r2524 = 0; | ||
138 | 121 | ||
139 | /* If User mode thread, set pt_reg stack pointer as per parameter */ | 122 | if (usp) |
140 | if (user_mode(childregs)) { | ||
141 | pt_set_rte_sp(childregs, usp); | 123 | pt_set_rte_sp(childregs, usp); |
142 | 124 | ||
143 | /* Child sees zero return value */ | 125 | /* Child sees zero return value */ |
144 | childregs->r00 = 0; | 126 | childregs->r00 = 0; |
145 | 127 | ||
146 | /* | 128 | /* |
147 | * The clone syscall has the C signature: | 129 | * The clone syscall has the C signature: |
148 | * int [r0] clone(int flags [r0], | 130 | * int [r0] clone(int flags [r0], |
149 | * void *child_frame [r1], | 131 | * void *child_frame [r1], |
150 | * void *parent_tid [r2], | 132 | * void *parent_tid [r2], |
151 | * void *child_tid [r3], | 133 | * void *child_tid [r3], |
152 | * void *thread_control_block [r4]); | 134 | * void *thread_control_block [r4]); |
153 | * ugp is used to provide TLS support. | 135 | * ugp is used to provide TLS support. |
154 | */ | 136 | */ |
155 | if (clone_flags & CLONE_SETTLS) | 137 | if (clone_flags & CLONE_SETTLS) |
156 | childregs->ugp = childregs->r04; | 138 | childregs->ugp = childregs->r04; |
157 | |||
158 | /* | ||
159 | * Parent sees new pid -- not necessary, not even possible at | ||
160 | * this point in the fork process | ||
161 | * Might also want to set things like ti->addr_limit | ||
162 | */ | ||
163 | } else { | ||
164 | /* | ||
165 | * If kernel thread, resume stack is kernel stack base. | ||
166 | * Note that this is pointer arithmetic on pt_regs * | ||
167 | */ | ||
168 | pt_set_rte_sp(childregs, (unsigned long)(childregs + 1)); | ||
169 | /* | ||
170 | * We need the current thread_info fast path pointer | ||
171 | * set up in pt_regs. The register to be used is | ||
172 | * parametric for assembler code, but the mechanism | ||
173 | * doesn't drop neatly into C. Needs to be fixed. | ||
174 | */ | ||
175 | childregs->THREADINFO_REG = (unsigned long) ti; | ||
176 | } | ||
177 | 139 | ||
178 | /* | 140 | /* |
179 | * thread_info pointer is pulled out of task_struct "stack" | 141 | * Parent sees new pid -- not necessary, not even possible at |
180 | * field on switch_to. | 142 | * this point in the fork process |
143 | * Might also want to set things like ti->addr_limit | ||
181 | */ | 144 | */ |
182 | p->stack = (void *)ti; | ||
183 | 145 | ||
184 | return 0; | 146 | return 0; |
185 | } | 147 | } |