/*
 *  linux/arch/m68k/kernel/process.c
 *
 *  Copyright (C) 1995  Hamish Macdonald
 */

/*
 * This file handles the architecture-dependent parts of process handling..
 */

#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/malloc.h>
#include <linux/user.h>
#include <linux/a.out.h>

#include <asm/segment.h>
#include <asm/system.h>
#include <asm/traps.h>

asmlinkage void ret_from_exception(void);

/*
 * The idle loop on an m68k..
 */
asmlinkage int sys_idle(void)
{
	if (current->pid != 0)
		return -EPERM;

	/* endless idle loop with no priority at all */
	current->counter = -100;
	for (;;)
		schedule();
}

void hard_reset_now(void)
{
	if (mach_reset)
		mach_reset();
}

void show_regs(struct pt_regs * regs)
{
	struct intframe *ifp = (struct intframe *)regs;
	unsigned long usp;

	printk("\n");
	printk("PC: %08lx SR: %04x", ifp->pc, ifp->sr);
	printk(" FMT: %01x VEC: %x\n", ifp->format, ifp->vector);
	if (!(ifp->sr & PS_S)) {
		__asm__ __volatile__ ("movec %/usp,%0"
				      : "=d" (usp));
		printk("USP: %08lx\n", usp);
	}
}

/*
 * Free current thread data structures etc..
 */
void exit_thread(void)
{
}

void flush_thread(void)
{
	set_fs(USER_DS);
	current->tss.fs = USER_DS;
}

void copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
		 struct task_struct * p, struct pt_regs * regs)
{
	struct pt_regs * childregs;

#ifdef MASTER_STACK
	p->tss.sr = 0x3000;
#else
	p->tss.sr = 0x2000;
#endif
	if (current->pid != 0 && pt_user_mode(regs))
		p->tss.fs = USER_DS;

	/* Copy the current fpu state */
	asm volatile ("fsave %0" : : "m" (p->tss.fpstate[0]) : "memory");
	if (p->tss.fpstate[0])
	  asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t"
			"fmoveml %/fpiar/%/fpcr/%/fpsr,%1"
			: : "m" (p->tss.fpregs[0]), "m" (p->tss.fpcntl[0])
			: "memory");
	/* Restore the state in case the fpu was busy */
	asm volatile ("frestore %0" : : "m" (p->tss.fpstate[0]));

	childregs = ((struct pt_regs *) (p->kernel_stack_page + PAGE_SIZE)) - 1;
	*childregs = *regs;
	childregs->d0 = 0;
	childregs->usp = usp;
	p->tss.regs[11] = (unsigned long)childregs - 4;
	*(void (**)(void))p->tss.regs[11] = ret_from_exception;
}

/* Fill in the fpu structure for a core dump.  */

int dump_fpu (struct user_m68kfp_struct *fpu)
{
  char fpustate[216];

  /* First dump the fpu context to avoid protocol violation.  */
  asm volatile ("fsave %0" :: "m" (fpustate[0]) : "memory");
  if (!fpustate[0])
    return 0;

  asm volatile ("fmovem %/fpiar/%/fpcr/%/fpsr,%0"
		:: "m" (fpu->fpcntl[0])
		: "memory");
  asm volatile ("fmovemx %/fp0-%/fp7,%0"
		:: "m" (fpu->fpregs[0])
		: "memory");
  return 1;
}

/*
 * fill in the user structure for a core dump..
 */
void dump_thread(struct pt_regs * regs, struct user * dump)
{
/* changed the size calculations - should hopefully work better. lbt */
	dump->magic = CMAGIC;
	dump->start_code = 0;
	dump->start_stack = regs->usp & ~(PAGE_SIZE - 1);
	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 pt_regs *)(((int)(&dump->regs)) -((int)(dump)));
	dump->regs = *regs;
	/* dump floating point stuff */
	dump->u_fpvalid = dump_fpu (&dump->m68kfp);
}

/*
 * sys_execve() executes a new program.
 */
asmlinkage int sys_execve(struct pt_regs regs)
{
	int error;
	char * filename;

	error = getname((char *) regs.d1, &filename);
	if (error)
		return error;
	error = do_execve(filename, (char **) regs.d2, (char **) regs.d3, &regs);
	putname(filename);
	return error;
}

/*
 * sys_fork() does the obvious thing, but not the obvious way.
 * See sys_execve() above.
 */
asmlinkage int sys_fork(struct pt_regs regs)
{
	return do_fork(COPYVM | SIGCHLD, regs.usp, &regs);
}

asmlinkage int sys_clone(struct pt_regs regs)
{
	unsigned long clone_flags;
	unsigned long newsp;

	newsp = regs.d1;
	clone_flags = regs.d2;
	if (newsp == regs.usp)
		clone_flags |= COPYVM;
	return do_fork(clone_flags, newsp, &regs);
}
