diff options
Diffstat (limited to 'arch/hexagon/kernel/process.c')
-rw-r--r-- | arch/hexagon/kernel/process.c | 102 |
1 files changed, 32 insertions, 70 deletions
diff --git a/arch/hexagon/kernel/process.c b/arch/hexagon/kernel/process.c index 9f6d7411b574..cfbc52bd5227 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,7 +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) | 91 | struct pt_regs *regs) |
119 | { | 92 | { |
120 | struct thread_info *ti = task_thread_info(p); | 93 | struct thread_info *ti = task_thread_info(p); |
@@ -125,61 +98,50 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, | |||
125 | childregs = (struct pt_regs *) (((unsigned long) ti + THREAD_SIZE) - | 98 | childregs = (struct pt_regs *) (((unsigned long) ti + THREAD_SIZE) - |
126 | sizeof(*childregs)); | 99 | sizeof(*childregs)); |
127 | 100 | ||
128 | memcpy(childregs, regs, sizeof(*childregs)); | ||
129 | ti->regs = childregs; | 101 | ti->regs = childregs; |
130 | 102 | ||
131 | /* | 103 | /* |
132 | * Establish kernel stack pointer and initial PC for new thread | 104 | * Establish kernel stack pointer and initial PC for new thread |
105 | * Note that unlike the usual situation, we do not copy the | ||
106 | * parent's callee-saved here; those are in pt_regs and whatever | ||
107 | * we leave here will be overridden on return to userland. | ||
133 | */ | 108 | */ |
134 | ss = (struct hexagon_switch_stack *) ((unsigned long) childregs - | 109 | ss = (struct hexagon_switch_stack *) ((unsigned long) childregs - |
135 | sizeof(*ss)); | 110 | sizeof(*ss)); |
136 | ss->lr = (unsigned long)ret_from_fork; | 111 | ss->lr = (unsigned long)ret_from_fork; |
137 | p->thread.switch_sp = ss; | 112 | p->thread.switch_sp = ss; |
138 | 113 | if (unlikely(p->flags & PF_KTHREAD)) { | |
139 | /* If User mode thread, set pt_reg stack pointer as per parameter */ | 114 | memset(childregs, 0, sizeof(struct pt_regs)); |
140 | if (user_mode(childregs)) { | 115 | /* r24 <- fn, r25 <- arg */ |
141 | pt_set_rte_sp(childregs, usp); | 116 | ss->r2524 = usp | ((u64)arg << 32); |
142 | 117 | pt_set_kmode(childregs); | |
143 | /* Child sees zero return value */ | 118 | return 0; |
144 | childregs->r00 = 0; | ||
145 | |||
146 | /* | ||
147 | * The clone syscall has the C signature: | ||
148 | * int [r0] clone(int flags [r0], | ||
149 | * void *child_frame [r1], | ||
150 | * void *parent_tid [r2], | ||
151 | * void *child_tid [r3], | ||
152 | * void *thread_control_block [r4]); | ||
153 | * ugp is used to provide TLS support. | ||
154 | */ | ||
155 | if (clone_flags & CLONE_SETTLS) | ||
156 | 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 | } | 119 | } |
120 | memcpy(childregs, regs, sizeof(*childregs)); | ||
121 | ss->r2524 = 0; | ||
122 | |||
123 | pt_set_rte_sp(childregs, usp); | ||
124 | |||
125 | /* Child sees zero return value */ | ||
126 | childregs->r00 = 0; | ||
127 | |||
128 | /* | ||
129 | * The clone syscall has the C signature: | ||
130 | * int [r0] clone(int flags [r0], | ||
131 | * void *child_frame [r1], | ||
132 | * void *parent_tid [r2], | ||
133 | * void *child_tid [r3], | ||
134 | * void *thread_control_block [r4]); | ||
135 | * ugp is used to provide TLS support. | ||
136 | */ | ||
137 | if (clone_flags & CLONE_SETTLS) | ||
138 | childregs->ugp = childregs->r04; | ||
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 | } |