patch-1.3.71 linux/arch/sparc/kernel/process.c

Next file: linux/arch/sparc/kernel/rtrap.S
Previous file: linux/arch/sparc/kernel/probe.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.70/linux/arch/sparc/kernel/process.c linux/arch/sparc/kernel/process.c
@@ -1,4 +1,4 @@
-/*  $Id: process.c,v 1.29 1995/11/25 00:58:17 davem Exp $
+/*  $Id: process.c,v 1.42 1996/02/20 07:45:08 davem Exp $
  *  linux/arch/sparc/kernel/process.c
  *
  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -26,10 +26,13 @@
 #include <asm/system.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
+#include <asm/delay.h>
 #include <asm/processor.h>
 #include <asm/psr.h>
 
-int current_user_segment = USER_DS; /* the return value from get_fs */
+extern void fpsave(unsigned long *, unsigned long *, void *, unsigned long *);
+
+int active_ds = USER_DS;
 
 /*
  * the idle loop on a Sparc... ;)
@@ -46,9 +49,15 @@
 	}
 }
 
+extern char saved_command_line[];
+
 void hard_reset_now(void)
 {
-	prom_halt();
+	sti();
+	udelay(8000);
+	cli();
+	prom_feval("reset");
+	panic("Reboot failed!");
 }
 
 void show_regwindow(struct reg_window *rw)
@@ -84,9 +93,15 @@
  */
 void exit_thread(void)
 {
-	if(last_task_used_math == current)
+	flush_user_windows();
+	if(last_task_used_math == current) {
+		/* Keep process from leaving FPU in a bogon state. */
+		put_psr(get_psr() | PSR_EF);
+		fpsave(&current->tss.float_regs[0], &current->tss.fsr,
+		       &current->tss.fpqueue[0], &current->tss.fpqdepth);
 		last_task_used_math = NULL;
-	mmu_exit_hook(current);
+	}
+	mmu_exit_hook();
 }
 
 /*
@@ -94,29 +109,33 @@
  */
 void release_thread(struct task_struct *dead_task)
 {
-	mmu_release_hook(dead_task);
 }
 
 void flush_thread(void)
 {
 	/* Make sure old user windows don't get in the way. */
-	mmu_flush_hook(current);
 	flush_user_windows();
-	current->signal &= ~(1<<(SIGILL-1));
 	current->tss.w_saved = 0;
 	current->tss.uwinmask = 0;
-
 	current->tss.sig_address = 0;
 	current->tss.sig_desc = 0;
-
-	/* Signal stack state does not inherit. XXX Really? XXX */
 	current->tss.sstk_info.cur_status = 0;
 	current->tss.sstk_info.the_stack = 0;
 
+	if(last_task_used_math == current) {
+		/* Clean the fpu. */
+		put_psr(get_psr() | PSR_EF);
+		fpsave(&current->tss.float_regs[0], &current->tss.fsr,
+		       &current->tss.fpqueue[0], &current->tss.fpqdepth);
+	}
+
 	memset(&current->tss.reg_window[0], 0,
 	       (sizeof(struct reg_window) * NSWINS));
 	memset(&current->tss.rwbuf_stkptrs[0], 0,
 	       (sizeof(unsigned long) * NSWINS));
+	mmu_flush_hook();
+	/* Now, this task is no longer a kernel thread. */
+	current->tss.flags &= ~SPARC_FLAG_KTHREAD;
 }
 
 /*
@@ -125,7 +144,12 @@
  * Parent -->  %o0 == childs  pid, %o1 == 0
  * Child  -->  %o0 == parents pid, %o1 == 1
  *
- * I'm feeling sick...
+ * NOTE: We have a seperate fork kpsr/kwim because
+ *       the parent could change these values between
+ *       sys_fork invocation and when we reach here
+ *       if the parent should sleep while trying to
+ *       allocate the task_struct and kernel stack in
+ *       do_fork().
  */
 extern void ret_sys_call(void);
 
@@ -133,34 +157,37 @@
 		 struct task_struct *p, struct pt_regs *regs)
 {
 	struct pt_regs *childregs;
-	struct sparc_stackf *old_stack, *new_stack;
-	unsigned long stack_offset, kthread_usp = 0;
+	struct reg_window *old_stack, *new_stack;
+	unsigned long stack_offset;
 
-	mmu_task_cacheflush(current);
-	p->tss.context = -1;
+	if(last_task_used_math == current) {
+		put_psr(get_psr() | PSR_EF);
+		fpsave(&p->tss.float_regs[0], &p->tss.fsr,
+		       &p->tss.fpqueue[0], &p->tss.fpqdepth);
+	}
 
 	/* Calculate offset to stack_frame & pt_regs */
-	stack_offset = (PAGE_SIZE - TRACEREG_SZ);
+	stack_offset = ((PAGE_SIZE*2) - TRACEREG_SZ);
+	if(regs->psr & PSR_PS)
+		stack_offset -= REGWIN_SZ;
 	childregs = ((struct pt_regs *) (p->kernel_stack_page + stack_offset));
 	*childregs = *regs;
-	new_stack = (((struct sparc_stackf *) childregs) - 1);
-	old_stack = (((struct sparc_stackf *) regs) - 1);
+	new_stack = (((struct reg_window *) childregs) - 1);
+	old_stack = (((struct reg_window *) regs) - 1);
 	*new_stack = *old_stack;
-	p->tss.ksp = (unsigned long) new_stack;
+	p->tss.ksp = p->saved_kernel_stack = (unsigned long) new_stack;
 	p->tss.kpc = (((unsigned long) ret_sys_call) - 0x8);
+	p->tss.kpsr = current->tss.fork_kpsr;
+	p->tss.kwim = current->tss.fork_kwim;
+	p->tss.kregs = childregs;
+	childregs->u_regs[UREG_FP] = sp;
 
-	/* As a special case, if this is a kernel fork we need
-	 * to give the child a new fresh stack for when it returns
-	 * from the syscall. (ie. the "user" stack)  This happens
-	 * only once and we count on the page acquisition happening
-	 * successfully.
-	 */
 	if(regs->psr & PSR_PS) {
-		 unsigned long n_stack = get_free_page(GFP_KERNEL);
-		 childregs->u_regs[UREG_FP] = (n_stack | (sp & 0xfff));
-		 memcpy((char *)n_stack,(char *)(sp & PAGE_MASK),PAGE_SIZE);
-		 kthread_usp = n_stack;
-	}
+		stack_offset += TRACEREG_SZ;
+		childregs->u_regs[UREG_FP] = p->kernel_stack_page + stack_offset;
+		p->tss.flags |= SPARC_FLAG_KTHREAD;
+	} else
+		p->tss.flags &= ~SPARC_FLAG_KTHREAD;
 
 	/* Set the return value for the child. */
 	childregs->u_regs[UREG_I0] = current->pid;
@@ -168,8 +195,6 @@
 
 	/* Set the return value for the parent. */
 	regs->u_regs[UREG_I1] = 0;
-
-	mmu_fork_hook(p, kthread_usp);
 }
 
 /*
@@ -177,6 +202,31 @@
  */
 void dump_thread(struct pt_regs * regs, struct user * dump)
 {
+	unsigned long first_stack_page;
+
+	dump->magic = SUNOS_CORE_MAGIC;
+	dump->len = sizeof(struct user);
+	dump->regs.psr = regs->psr;
+	dump->regs.pc = regs->pc;
+	dump->regs.npc = regs->npc;
+	dump->regs.y = regs->y;
+	/* fuck me plenty */
+	memcpy(&dump->regs.regs[0], &regs->u_regs[1], (sizeof(unsigned long) * 15));
+	dump->uexec = current->tss.core_exec;
+	dump->u_tsize = (((unsigned long) current->mm->end_code) -
+		((unsigned long) current->mm->start_code)) & ~(PAGE_SIZE - 1);
+	dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1)));
+	dump->u_dsize -= dump->u_tsize;
+	dump->u_dsize &= ~(PAGE_SIZE - 1);
+	first_stack_page = (regs->u_regs[UREG_FP] & ~(PAGE_SIZE - 1));
+	dump->u_ssize = (TASK_SIZE - first_stack_page) & ~(PAGE_SIZE - 1);
+	memcpy(&dump->fpu.fpstatus.fregs.regs[0], &current->tss.float_regs[0], (sizeof(unsigned long) * 32));
+	dump->fpu.fpstatus.fsr = current->tss.fsr;
+	dump->fpu.fpstatus.flags = dump->fpu.fpstatus.extra = 0;
+	dump->fpu.fpstatus.fpq_count = current->tss.fpqdepth;
+	memcpy(&dump->fpu.fpstatus.fpq[0], &current->tss.fpqueue[0],
+	       ((sizeof(unsigned long) * 2) * 16));
+	dump->sigcode = current->tss.sig_desc;
 }
 
 /*
@@ -198,7 +248,6 @@
 	char *filename;
 
 	flush_user_windows();
-	mmu_task_cacheflush(current);
 	error = getname((char *) regs->u_regs[UREG_I0], &filename);
 	if(error)
 		return error;
@@ -206,28 +255,4 @@
 			  (char **) regs->u_regs[UREG_I2], regs);
 	putname(filename);
 	return error;
-}
-
-void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
-{
-	unsigned long saved_psr = (regs->psr & (PSR_CWP)) | PSR_S;
-
-	memset(regs, 0, sizeof(struct pt_regs));
-	regs->pc = ((pc & (~3)) - 4); /* whee borken a.out header fields... */
-	regs->npc = regs->pc + 4;
-	regs->psr = saved_psr;
-	regs->u_regs[UREG_G1] = sp; /* Base of arg/env stack area */
-
-	/* XXX More mysterious netbsd garbage... XXX */
-	regs->u_regs[UREG_G2] = regs->u_regs[UREG_G7] = regs->npc;
-
-	/* Allocate one reg window because the first jump into
-	 * user mode will restore one register window by definition
-	 * of the 'rett' instruction.  Also, SunOS crt.o code
-	 * depends upon the arg/envp area being _exactly_ one
-	 * register window above %sp when the process begins
-	 * execution.
-	 */
-	sp -= REGWIN_SZ;
-	regs->u_regs[UREG_FP] = sp;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this