aboutsummaryrefslogtreecommitdiffstats
path: root/arch/tile/kernel
diff options
context:
space:
mode:
authorChris Metcalf <cmetcalf@tilera.com>2012-10-19 16:25:12 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2012-10-20 13:13:29 -0400
commit0f8b983812f5ff588d7e3459b203714e4e58a9b2 (patch)
treec3daaff762ca64667dc4d412509e9472b4f3d72d /arch/tile/kernel
parent733deca197143857f938b41d671cd7ce9c53c4bc (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.S11
-rw-r--r--arch/tile/kernel/intvec_32.S15
-rw-r--r--arch/tile/kernel/intvec_64.S15
-rw-r--r--arch/tile/kernel/process.c103
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 */
35STD_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
1294STD_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
1153STD_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
1155STD_ENTRY_LOCAL(_kernel_double_fault) 1170STD_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)
157static void save_arch_state(struct thread_struct *t); 157static void save_arch_state(struct thread_struct *t);
158 158
159int copy_thread(unsigned long clone_flags, unsigned long sp, 159int 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, &regs->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, &regs->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 */
659static 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 */
667int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
668{
669 struct pt_regs regs;
670
671 memset(&regs, 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, &regs,
680 0, NULL, NULL);
681}
682EXPORT_SYMBOL(kernel_thread);
683
684/* Flush thread state. */ 659/* Flush thread state. */
685void flush_thread(void) 660void flush_thread(void)
686{ 661{