123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- /*
- * arch/v850/kernel/process.c -- Arch-dependent process handling
- *
- * Copyright (C) 2001,02,03 NEC Electronics Corporation
- * Copyright (C) 2001,02,03 Miles Bader <miles@gnu.org>
- *
- * This file is subject to the terms and conditions of the GNU General
- * Public License. See the file COPYING in the main directory of this
- * archive for more details.
- *
- * Written by Miles Bader <miles@gnu.org>
- */
- #include <linux/config.h>
- #include <linux/errno.h>
- #include <linux/sched.h>
- #include <linux/kernel.h>
- #include <linux/mm.h>
- #include <linux/smp.h>
- #include <linux/smp_lock.h>
- #include <linux/stddef.h>
- #include <linux/unistd.h>
- #include <linux/ptrace.h>
- #include <linux/slab.h>
- #include <linux/user.h>
- #include <linux/a.out.h>
- #include <linux/reboot.h>
- #include <asm/uaccess.h>
- #include <asm/system.h>
- #include <asm/pgtable.h>
- extern void ret_from_fork (void);
- /* The idle loop. */
- void default_idle (void)
- {
- while (1) {
- while (! need_resched ())
- asm ("halt; nop; nop; nop; nop; nop" ::: "cc");
- schedule ();
- }
- }
- void (*idle)(void) = default_idle;
- /*
- * The idle thread. There's no useful work to be
- * done, so just try to conserve power and have a
- * low exit latency (ie sit in a loop waiting for
- * somebody to say that they'd like to reschedule)
- */
- void cpu_idle (void)
- {
- /* endless idle loop with no priority at all */
- (*idle) ();
- }
- /*
- * This is the mechanism for creating a new kernel thread.
- *
- * NOTE! Only a kernel-only process (ie the swapper or direct descendants who
- * haven't done an "execve()") should use this: it will work within a system
- * call from a "real" process, but the process memory space will not be free'd
- * until both the parent and the child have exited.
- */
- int kernel_thread (int (*fn)(void *), void *arg, unsigned long flags)
- {
- register mm_segment_t fs = get_fs ();
- register unsigned long syscall asm (SYSCALL_NUM);
- register unsigned long arg0 asm (SYSCALL_ARG0);
- register unsigned long ret asm (SYSCALL_RET);
- set_fs (KERNEL_DS);
- /* Clone this thread. Note that we don't pass the clone syscall's
- second argument -- it's ignored for calls from kernel mode (the
- child's SP is always set to the top of the kernel stack). */
- arg0 = flags | CLONE_VM;
- syscall = __NR_clone;
- asm volatile ("trap " SYSCALL_SHORT_TRAP
- : "=r" (ret), "=r" (syscall)
- : "1" (syscall), "r" (arg0)
- : SYSCALL_SHORT_CLOBBERS);
- if (ret == 0) {
- /* In child thread, call FN and exit. */
- arg0 = (*fn) (arg);
- syscall = __NR_exit;
- asm volatile ("trap " SYSCALL_SHORT_TRAP
- : "=r" (ret), "=r" (syscall)
- : "1" (syscall), "r" (arg0)
- : SYSCALL_SHORT_CLOBBERS);
- }
- /* In parent. */
- set_fs (fs);
- return ret;
- }
- void flush_thread (void)
- {
- set_fs (USER_DS);
- }
- int copy_thread (int nr, unsigned long clone_flags,
- unsigned long stack_start, unsigned long stack_size,
- struct task_struct *p, struct pt_regs *regs)
- {
- /* Start pushing stuff from the top of the child's kernel stack. */
- unsigned long orig_ksp = (unsigned long)p->thread_info + THREAD_SIZE;
- unsigned long ksp = orig_ksp;
- /* We push two `state save' stack fames (see entry.S) on the new
- kernel stack:
- 1) The innermost one is what switch_thread would have
- pushed, and is used when we context switch to the child
- thread for the first time. It's set up to return to
- ret_from_fork in entry.S.
- 2) The outermost one (nearest the top) is what a syscall
- trap would have pushed, and is set up to return to the
- same location as the parent thread, but with a return
- value of 0. */
- struct pt_regs *child_switch_regs, *child_trap_regs;
- /* Trap frame. */
- ksp -= STATE_SAVE_SIZE;
- child_trap_regs = (struct pt_regs *)(ksp + STATE_SAVE_PT_OFFSET);
- /* Switch frame. */
- ksp -= STATE_SAVE_SIZE;
- child_switch_regs = (struct pt_regs *)(ksp + STATE_SAVE_PT_OFFSET);
- /* First copy parent's register state to child. */
- *child_switch_regs = *regs;
- *child_trap_regs = *regs;
- /* switch_thread returns to the restored value of the lp
- register (r31), so we make that the place where we want to
- jump when the child thread begins running. */
- child_switch_regs->gpr[GPR_LP] = (v850_reg_t)ret_from_fork;
- if (regs->kernel_mode)
- /* Since we're returning to kernel-mode, make sure the child's
- stored kernel stack pointer agrees with what the actual
- stack pointer will be at that point (the trap return code
- always restores the SP, even when returning to
- kernel-mode). */
- child_trap_regs->gpr[GPR_SP] = orig_ksp;
- else
- /* Set the child's user-mode stack-pointer (the name
- `stack_start' is a misnomer, it's just the initial SP
- value). */
- child_trap_regs->gpr[GPR_SP] = stack_start;
- /* Thread state for the child (everything else is on the stack). */
- p->thread.ksp = ksp;
- return 0;
- }
- /*
- * fill in the user structure for a core dump..
- */
- void dump_thread (struct pt_regs *regs, struct user *dump)
- {
- #if 0 /* Later. XXX */
- dump->magic = CMAGIC;
- dump->start_code = 0;
- dump->start_stack = regs->gpr[GPR_SP];
- dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT;
- dump->u_dsize = ((unsigned long) (current->mm->brk +
- (PAGE_SIZE-1))) >> PAGE_SHIFT;
- dump->u_dsize -= dump->u_tsize;
- dump->u_ssize = 0;
- if (dump->start_stack < TASK_SIZE)
- dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT;
- dump->u_ar0 = (struct user_regs_struct *)((int)&dump->regs - (int)dump);
- dump->regs = *regs;
- dump->u_fpvalid = 0;
- #endif
- }
- /*
- * sys_execve() executes a new program.
- */
- int sys_execve (char *name, char **argv, char **envp, struct pt_regs *regs)
- {
- char *filename = getname (name);
- int error = PTR_ERR (filename);
- if (! IS_ERR (filename)) {
- error = do_execve (filename, argv, envp, regs);
- putname (filename);
- }
- return error;
- }
- /*
- * These bracket the sleeping functions..
- */
- #define first_sched ((unsigned long)__sched_text_start)
- #define last_sched ((unsigned long)__sched_text_end)
- unsigned long get_wchan (struct task_struct *p)
- {
- #if 0 /* Barf. Figure out the stack-layout later. XXX */
- unsigned long fp, pc;
- int count = 0;
- if (!p || p == current || p->state == TASK_RUNNING)
- return 0;
- pc = thread_saved_pc (p);
- /* This quite disgusting function walks up the stack, following
- saved return address, until it something that's out of bounds
- (as defined by `first_sched' and `last_sched'). It then
- returns the last PC that was in-bounds. */
- do {
- if (fp < stack_page + sizeof (struct task_struct) ||
- fp >= 8184+stack_page)
- return 0;
- pc = ((unsigned long *)fp)[1];
- if (pc < first_sched || pc >= last_sched)
- return pc;
- fp = *(unsigned long *) fp;
- } while (count++ < 16);
- #endif
- return 0;
- }
|