diff options
author | Chris Metcalf <cmetcalf@tilera.com> | 2012-10-19 16:25:12 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2012-10-20 13:13:29 -0400 |
commit | 0f8b983812f5ff588d7e3459b203714e4e58a9b2 (patch) | |
tree | c3daaff762ca64667dc4d412509e9472b4f3d72d /arch/tile/kernel | |
parent | 733deca197143857f938b41d671cd7ce9c53c4bc (diff) |
tile: support GENERIC_KERNEL_THREAD and GENERIC_KERNEL_EXECVE
Also provide an optimized current_pt_regs() while we're at it.
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'arch/tile/kernel')
-rw-r--r-- | arch/tile/kernel/entry.S | 11 | ||||
-rw-r--r-- | arch/tile/kernel/intvec_32.S | 15 | ||||
-rw-r--r-- | arch/tile/kernel/intvec_64.S | 15 | ||||
-rw-r--r-- | arch/tile/kernel/process.c | 103 |
4 files changed, 69 insertions, 75 deletions
diff --git a/arch/tile/kernel/entry.S b/arch/tile/kernel/entry.S index c31637baff28..f116cb0bce20 100644 --- a/arch/tile/kernel/entry.S +++ b/arch/tile/kernel/entry.S | |||
@@ -28,17 +28,6 @@ STD_ENTRY(current_text_addr) | |||
28 | STD_ENDPROC(current_text_addr) | 28 | STD_ENDPROC(current_text_addr) |
29 | 29 | ||
30 | /* | 30 | /* |
31 | * Implement execve(). The i386 code has a note that forking from kernel | ||
32 | * space results in no copy on write until the execve, so we should be | ||
33 | * careful not to write to the stack here. | ||
34 | */ | ||
35 | STD_ENTRY(kernel_execve) | ||
36 | moveli TREG_SYSCALL_NR_NAME, __NR_execve | ||
37 | swint1 | ||
38 | jrp lr | ||
39 | STD_ENDPROC(kernel_execve) | ||
40 | |||
41 | /* | ||
42 | * We don't run this function directly, but instead copy it to a page | 31 | * We don't run this function directly, but instead copy it to a page |
43 | * we map into every user process. See vdso_setup(). | 32 | * we map into every user process. See vdso_setup(). |
44 | * | 33 | * |
diff --git a/arch/tile/kernel/intvec_32.S b/arch/tile/kernel/intvec_32.S index 6943515100f8..58aad519b7d6 100644 --- a/arch/tile/kernel/intvec_32.S +++ b/arch/tile/kernel/intvec_32.S | |||
@@ -1291,6 +1291,21 @@ STD_ENTRY(ret_from_fork) | |||
1291 | } | 1291 | } |
1292 | STD_ENDPROC(ret_from_fork) | 1292 | STD_ENDPROC(ret_from_fork) |
1293 | 1293 | ||
1294 | STD_ENTRY(ret_from_kernel_thread) | ||
1295 | jal sim_notify_fork | ||
1296 | jal schedule_tail | ||
1297 | FEEDBACK_REENTER(ret_from_fork) | ||
1298 | { | ||
1299 | move r0, r31 | ||
1300 | jalr r30 | ||
1301 | } | ||
1302 | FEEDBACK_REENTER(ret_from_kernel_thread) | ||
1303 | { | ||
1304 | movei r30, 0 /* not an NMI */ | ||
1305 | j .Lresume_userspace /* jump into middle of interrupt_return */ | ||
1306 | } | ||
1307 | STD_ENDPROC(ret_from_kernel_thread) | ||
1308 | |||
1294 | /* | 1309 | /* |
1295 | * Code for ill interrupt. | 1310 | * Code for ill interrupt. |
1296 | */ | 1311 | */ |
diff --git a/arch/tile/kernel/intvec_64.S b/arch/tile/kernel/intvec_64.S index 73f6c0a648c4..f66bd5f67d44 100644 --- a/arch/tile/kernel/intvec_64.S +++ b/arch/tile/kernel/intvec_64.S | |||
@@ -1150,6 +1150,21 @@ STD_ENTRY(ret_from_fork) | |||
1150 | } | 1150 | } |
1151 | STD_ENDPROC(ret_from_fork) | 1151 | STD_ENDPROC(ret_from_fork) |
1152 | 1152 | ||
1153 | STD_ENTRY(ret_from_kernel_thread) | ||
1154 | jal sim_notify_fork | ||
1155 | jal schedule_tail | ||
1156 | FEEDBACK_REENTER(ret_from_fork) | ||
1157 | { | ||
1158 | move r0, r31 | ||
1159 | jalr r30 | ||
1160 | } | ||
1161 | FEEDBACK_REENTER(ret_from_kernel_thread) | ||
1162 | { | ||
1163 | movei r30, 0 /* not an NMI */ | ||
1164 | j .Lresume_userspace /* jump into middle of interrupt_return */ | ||
1165 | } | ||
1166 | STD_ENDPROC(ret_from_kernel_thread) | ||
1167 | |||
1153 | /* Various stub interrupt handlers and syscall handlers */ | 1168 | /* Various stub interrupt handlers and syscall handlers */ |
1154 | 1169 | ||
1155 | STD_ENTRY_LOCAL(_kernel_double_fault) | 1170 | STD_ENTRY_LOCAL(_kernel_double_fault) |
diff --git a/arch/tile/kernel/process.c b/arch/tile/kernel/process.c index 9dc139127f86..da6e4d78da6e 100644 --- a/arch/tile/kernel/process.c +++ b/arch/tile/kernel/process.c | |||
@@ -157,24 +157,44 @@ void arch_release_thread_info(struct thread_info *info) | |||
157 | static void save_arch_state(struct thread_struct *t); | 157 | static void save_arch_state(struct thread_struct *t); |
158 | 158 | ||
159 | int copy_thread(unsigned long clone_flags, unsigned long sp, | 159 | int copy_thread(unsigned long clone_flags, unsigned long sp, |
160 | unsigned long stack_size, | 160 | unsigned long arg, |
161 | struct task_struct *p, struct pt_regs *regs) | 161 | struct task_struct *p, struct pt_regs *regs) |
162 | { | 162 | { |
163 | struct pt_regs *childregs; | 163 | struct pt_regs *childregs = task_pt_regs(p); |
164 | unsigned long ksp; | 164 | unsigned long ksp; |
165 | unsigned long *callee_regs; | ||
165 | 166 | ||
166 | /* | 167 | /* |
167 | * When creating a new kernel thread we pass sp as zero. | 168 | * Set up the stack and stack pointer appropriately for the |
168 | * Assign it to a reasonable value now that we have the stack. | 169 | * new child to find itself woken up in __switch_to(). |
170 | * The callee-saved registers must be on the stack to be read; | ||
171 | * the new task will then jump to assembly support to handle | ||
172 | * calling schedule_tail(), etc., and (for userspace tasks) | ||
173 | * returning to the context set up in the pt_regs. | ||
169 | */ | 174 | */ |
170 | if (sp == 0 && regs->ex1 == PL_ICS_EX1(KERNEL_PL, 0)) | 175 | ksp = (unsigned long) childregs; |
171 | sp = KSTK_TOP(p); | 176 | ksp -= C_ABI_SAVE_AREA_SIZE; /* interrupt-entry save area */ |
177 | ((long *)ksp)[0] = ((long *)ksp)[1] = 0; | ||
178 | ksp -= CALLEE_SAVED_REGS_COUNT * sizeof(unsigned long); | ||
179 | callee_regs = (unsigned long *)ksp; | ||
180 | ksp -= C_ABI_SAVE_AREA_SIZE; /* __switch_to() save area */ | ||
181 | ((long *)ksp)[0] = ((long *)ksp)[1] = 0; | ||
182 | p->thread.ksp = ksp; | ||
172 | 183 | ||
173 | /* | 184 | /* Record the pid of the task that created this one. */ |
174 | * Do not clone step state from the parent; each thread | 185 | p->thread.creator_pid = current->pid; |
175 | * must make its own lazily. | 186 | |
176 | */ | 187 | if (unlikely(!regs)) { |
177 | task_thread_info(p)->step_state = NULL; | 188 | /* kernel thread */ |
189 | memset(childregs, 0, sizeof(struct pt_regs)); | ||
190 | memset(&callee_regs[2], 0, | ||
191 | (CALLEE_SAVED_REGS_COUNT - 2) * sizeof(unsigned long)); | ||
192 | callee_regs[0] = sp; /* r30 = function */ | ||
193 | callee_regs[1] = arg; /* r31 = arg */ | ||
194 | childregs->ex1 = PL_ICS_EX1(KERNEL_PL, 0); | ||
195 | p->thread.pc = (unsigned long) ret_from_kernel_thread; | ||
196 | return 0; | ||
197 | } | ||
178 | 198 | ||
179 | /* | 199 | /* |
180 | * Start new thread in ret_from_fork so it schedules properly | 200 | * Start new thread in ret_from_fork so it schedules properly |
@@ -182,20 +202,24 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, | |||
182 | */ | 202 | */ |
183 | p->thread.pc = (unsigned long) ret_from_fork; | 203 | p->thread.pc = (unsigned long) ret_from_fork; |
184 | 204 | ||
205 | /* | ||
206 | * Do not clone step state from the parent; each thread | ||
207 | * must make its own lazily. | ||
208 | */ | ||
209 | task_thread_info(p)->step_state = NULL; | ||
210 | |||
185 | /* Save user stack top pointer so we can ID the stack vm area later. */ | 211 | /* Save user stack top pointer so we can ID the stack vm area later. */ |
186 | p->thread.usp0 = sp; | 212 | p->thread.usp0 = sp; |
187 | 213 | ||
188 | /* Record the pid of the process that created this one. */ | ||
189 | p->thread.creator_pid = current->pid; | ||
190 | |||
191 | /* | 214 | /* |
192 | * Copy the registers onto the kernel stack so the | 215 | * Copy the registers onto the kernel stack so the |
193 | * return-from-interrupt code will reload it into registers. | 216 | * return-from-interrupt code will reload it into registers. |
194 | */ | 217 | */ |
195 | childregs = task_pt_regs(p); | ||
196 | *childregs = *regs; | 218 | *childregs = *regs; |
197 | childregs->regs[0] = 0; /* return value is zero */ | 219 | childregs->regs[0] = 0; /* return value is zero */ |
198 | childregs->sp = sp; /* override with new user stack pointer */ | 220 | childregs->sp = sp; /* override with new user stack pointer */ |
221 | memcpy(callee_regs, ®s->regs[CALLEE_SAVED_FIRST_REG], | ||
222 | CALLEE_SAVED_REGS_COUNT * sizeof(unsigned long)); | ||
199 | 223 | ||
200 | /* | 224 | /* |
201 | * If CLONE_SETTLS is set, set "tp" in the new task to "r4", | 225 | * If CLONE_SETTLS is set, set "tp" in the new task to "r4", |
@@ -204,24 +228,6 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, | |||
204 | if (clone_flags & CLONE_SETTLS) | 228 | if (clone_flags & CLONE_SETTLS) |
205 | childregs->tp = regs->regs[4]; | 229 | childregs->tp = regs->regs[4]; |
206 | 230 | ||
207 | /* | ||
208 | * Copy the callee-saved registers from the passed pt_regs struct | ||
209 | * into the context-switch callee-saved registers area. | ||
210 | * This way when we start the interrupt-return sequence, the | ||
211 | * callee-save registers will be correctly in registers, which | ||
212 | * is how we assume the compiler leaves them as we start doing | ||
213 | * the normal return-from-interrupt path after calling C code. | ||
214 | * Zero out the C ABI save area to mark the top of the stack. | ||
215 | */ | ||
216 | ksp = (unsigned long) childregs; | ||
217 | ksp -= C_ABI_SAVE_AREA_SIZE; /* interrupt-entry save area */ | ||
218 | ((long *)ksp)[0] = ((long *)ksp)[1] = 0; | ||
219 | ksp -= CALLEE_SAVED_REGS_COUNT * sizeof(unsigned long); | ||
220 | memcpy((void *)ksp, ®s->regs[CALLEE_SAVED_FIRST_REG], | ||
221 | CALLEE_SAVED_REGS_COUNT * sizeof(unsigned long)); | ||
222 | ksp -= C_ABI_SAVE_AREA_SIZE; /* __switch_to() save area */ | ||
223 | ((long *)ksp)[0] = ((long *)ksp)[1] = 0; | ||
224 | p->thread.ksp = ksp; | ||
225 | 231 | ||
226 | #if CHIP_HAS_TILE_DMA() | 232 | #if CHIP_HAS_TILE_DMA() |
227 | /* | 233 | /* |
@@ -650,37 +656,6 @@ unsigned long get_wchan(struct task_struct *p) | |||
650 | return 0; | 656 | return 0; |
651 | } | 657 | } |
652 | 658 | ||
653 | /* | ||
654 | * We pass in lr as zero (cleared in kernel_thread) and the caller | ||
655 | * part of the backtrace ABI on the stack also zeroed (in copy_thread) | ||
656 | * so that backtraces will stop with this function. | ||
657 | * Note that we don't use r0, since copy_thread() clears it. | ||
658 | */ | ||
659 | static void start_kernel_thread(int dummy, int (*fn)(int), int arg) | ||
660 | { | ||
661 | do_exit(fn(arg)); | ||
662 | } | ||
663 | |||
664 | /* | ||
665 | * Create a kernel thread | ||
666 | */ | ||
667 | int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) | ||
668 | { | ||
669 | struct pt_regs regs; | ||
670 | |||
671 | memset(®s, 0, sizeof(regs)); | ||
672 | regs.ex1 = PL_ICS_EX1(KERNEL_PL, 0); /* run at kernel PL, no ICS */ | ||
673 | regs.pc = (long) start_kernel_thread; | ||
674 | regs.flags = PT_FLAGS_CALLER_SAVES; /* need to restore r1 and r2 */ | ||
675 | regs.regs[1] = (long) fn; /* function pointer */ | ||
676 | regs.regs[2] = (long) arg; /* parameter register */ | ||
677 | |||
678 | /* Ok, create the new process.. */ | ||
679 | return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, | ||
680 | 0, NULL, NULL); | ||
681 | } | ||
682 | EXPORT_SYMBOL(kernel_thread); | ||
683 | |||
684 | /* Flush thread state. */ | 659 | /* Flush thread state. */ |
685 | void flush_thread(void) | 660 | void flush_thread(void) |
686 | { | 661 | { |