patch-2.1.44 linux/arch/sparc64/kernel/signal.c

Next file: linux/arch/sparc64/kernel/signal32.c
Previous file: linux/arch/sparc64/kernel/setup.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.43/linux/arch/sparc64/kernel/signal.c linux/arch/sparc64/kernel/signal.c
@@ -1,4 +1,4 @@
-/*  $Id: signal.c,v 1.6 1997/05/29 12:44:48 jj Exp $
+/*  $Id: signal.c,v 1.17 1997/07/05 09:52:31 davem Exp $
  *  arch/sparc64/kernel/signal.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -8,6 +8,7 @@
  *  Copyright (C) 1997 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
  */
 
+#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/signal.h>
@@ -23,6 +24,7 @@
 #include <asm/svr4.h>
 #include <asm/pgtable.h>
 #include <asm/fpumacro.h>
+#include <asm/uctx.h>
 #include <asm/smp_lock.h>
 
 #define _S(nr) (1<<((nr)-1))
@@ -38,6 +40,124 @@
 /* This turned off for production... */
 /* #define DEBUG_SIGNALS 1 */
 
+/* {set, get}context() needed for 64-bit SparcLinux userland. */
+asmlinkage void sparc64_set_context(struct pt_regs *regs)
+{
+	struct ucontext *ucp = (struct ucontext *) regs->u_regs[UREG_I0];
+	struct thread_struct *tp = &current->tss;
+	mc_gregset_t *grp;
+	unsigned long pc, npc, tstate;
+	unsigned long fp, i7;
+	unsigned char fenab;
+
+	__asm__ __volatile__("flushw");
+	if(tp->w_saved						||
+	   (((unsigned long)ucp) & (sizeof(unsigned long)-1))	||
+	   (!__access_ok((unsigned long)ucp, sizeof(*ucp))))
+		do_exit(SIGSEGV);
+	grp = &ucp->uc_mcontext.mc_gregs;
+	__get_user(pc, &((*grp)[MC_PC]));
+	__get_user(npc, &((*grp)[MC_NPC]));
+	if((pc | npc) & 3)
+		do_exit(SIGSEGV);
+	if(regs->u_regs[UREG_I1]) {
+		__get_user(current->blocked, &ucp->uc_sigmask);
+		current->blocked &= _BLOCKABLE;
+	}
+	regs->tpc = pc;
+	regs->tnpc = npc;
+	__get_user(regs->y, &((*grp)[MC_Y]));
+	__get_user(tstate, &((*grp)[MC_Y]));
+	regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC);
+	regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_XCC));
+	__get_user(regs->u_regs[UREG_G1], (&(*grp)[MC_G1]));
+	__get_user(regs->u_regs[UREG_G2], (&(*grp)[MC_G2]));
+	__get_user(regs->u_regs[UREG_G3], (&(*grp)[MC_G3]));
+	__get_user(regs->u_regs[UREG_G4], (&(*grp)[MC_G4]));
+	__get_user(regs->u_regs[UREG_G5], (&(*grp)[MC_G5]));
+	__get_user(regs->u_regs[UREG_G6], (&(*grp)[MC_G6]));
+	__get_user(regs->u_regs[UREG_G7], (&(*grp)[MC_G7]));
+	__get_user(regs->u_regs[UREG_I0], (&(*grp)[MC_O0]));
+	__get_user(regs->u_regs[UREG_I1], (&(*grp)[MC_O1]));
+	__get_user(regs->u_regs[UREG_I2], (&(*grp)[MC_O2]));
+	__get_user(regs->u_regs[UREG_I3], (&(*grp)[MC_O3]));
+	__get_user(regs->u_regs[UREG_I4], (&(*grp)[MC_O4]));
+	__get_user(regs->u_regs[UREG_I5], (&(*grp)[MC_O5]));
+	__get_user(regs->u_regs[UREG_I6], (&(*grp)[MC_O6]));
+	__get_user(regs->u_regs[UREG_I7], (&(*grp)[MC_O7]));
+
+	__get_user(fp, &(ucp->uc_mcontext.mc_fp));
+	__get_user(i7, &(ucp->uc_mcontext.mc_i7));
+	__put_user(fp, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[6])));
+	__put_user(i7, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[7])));
+
+	__get_user(fenab, &(ucp->uc_mcontext.mc_fpregs.mcfpu_enab));
+	if(fenab) {
+		unsigned long *fpregs = (unsigned long *)(regs+1);
+		copy_from_user(fpregs, &(ucp->uc_mcontext.mc_fpregs.mcfpu_fregs),
+			       (sizeof(unsigned long) * 32));
+		__get_user(fpregs[32], &(ucp->uc_mcontext.mc_fpregs.mcfpu_fsr));
+		__get_user(fpregs[33], &(ucp->uc_mcontext.mc_fpregs.mcfpu_gsr));
+		regs->fprs = FPRS_FEF;
+	}
+}
+
+asmlinkage void sparc64_get_context(struct pt_regs *regs)
+{
+	struct ucontext *ucp = (struct ucontext *) regs->u_regs[UREG_I0];
+	struct thread_struct *tp = &current->tss;
+	mc_gregset_t *grp;
+	mcontext_t *mcp;
+	unsigned long fp, i7;
+	unsigned char fenab = (current->flags & PF_USEDFPU);
+
+	synchronize_user_stack();
+	if(tp->w_saved || clear_user(ucp, sizeof(*ucp)))
+		do_exit(SIGSEGV);
+	mcp = &ucp->uc_mcontext;
+	grp = &mcp->mc_gregs;
+
+	/* Skip over the trap instruction, first. */
+	regs->tpc   = regs->tnpc;
+	regs->tnpc += 4;
+
+	__put_user(current->blocked, &ucp->uc_sigmask);
+	__put_user(regs->tstate, &((*grp)[MC_TSTATE]));
+	__put_user(regs->tpc, &((*grp)[MC_PC]));
+	__put_user(regs->tnpc, &((*grp)[MC_NPC]));
+	__put_user(regs->y, &((*grp)[MC_Y]));
+	__put_user(regs->u_regs[UREG_G1], &((*grp)[MC_G1]));
+	__put_user(regs->u_regs[UREG_G2], &((*grp)[MC_G2]));
+	__put_user(regs->u_regs[UREG_G3], &((*grp)[MC_G3]));
+	__put_user(regs->u_regs[UREG_G4], &((*grp)[MC_G4]));
+	__put_user(regs->u_regs[UREG_G5], &((*grp)[MC_G5]));
+	__put_user(regs->u_regs[UREG_G6], &((*grp)[MC_G6]));
+	__put_user(regs->u_regs[UREG_G6], &((*grp)[MC_G7]));
+	__put_user(regs->u_regs[UREG_I0], &((*grp)[MC_O0]));
+	__put_user(regs->u_regs[UREG_I1], &((*grp)[MC_O1]));
+	__put_user(regs->u_regs[UREG_I2], &((*grp)[MC_O2]));
+	__put_user(regs->u_regs[UREG_I3], &((*grp)[MC_O3]));
+	__put_user(regs->u_regs[UREG_I4], &((*grp)[MC_O4]));
+	__put_user(regs->u_regs[UREG_I5], &((*grp)[MC_O5]));
+	__put_user(regs->u_regs[UREG_I6], &((*grp)[MC_O6]));
+	__put_user(regs->u_regs[UREG_I7], &((*grp)[MC_O7]));
+
+	__get_user(fp, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[6])));
+	__get_user(i7, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[7])));
+	__put_user(fp, &(mcp->mc_fp));
+	__put_user(i7, &(mcp->mc_i7));
+
+	__put_user(fenab, &(mcp->mc_fpregs.mcfpu_enab));
+	if(fenab) {
+		unsigned long *fpregs = (unsigned long *)(regs+1);
+		copy_to_user(&(mcp->mc_fpregs.mcfpu_fregs), fpregs,
+			     (sizeof(unsigned long) * 32));
+		__put_user(fpregs[32], &(mcp->mc_fpregs.mcfpu_fsr));
+		__put_user(fpregs[33], &(mcp->mc_fpregs.mcfpu_gsr));
+		__put_user(FPRS_FEF, &(mcp->mc_fpregs.mcfpu_fprs));
+	}
+}
+
 /* 
  * The new signal frame, intended to be used for Linux applications only
  * (we have enough in there to work with clone).
@@ -65,7 +185,8 @@
 
 #ifdef CONFIG_SPARC32_COMPAT
 	if (current->tss.flags & SPARC_FLAG_32BIT) {
-		extern asmlinkage void _sigpause32_common(unsigned int, struct pt_regs *);
+		extern asmlinkage void _sigpause32_common(unsigned int,
+							  struct pt_regs *);
 		_sigpause32_common(set, regs);
 		return;
 	}
@@ -111,22 +232,12 @@
 static inline void
 restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu)
 {
-#ifdef __SMP__
-	if (current->flags & PF_USEDFPU)
-		regs->tstate &= ~(TSTATE_PEF);
-#else
-	if (current == last_task_used_math) {
-		last_task_used_math = 0;
-		regs->tstate &= ~(TSTATE_PEF);
-	}
-#endif
-	current->used_math = 1;
-	current->flags &= ~PF_USEDFPU;
-
-	copy_from_user(&current->tss.float_regs[0],
-		       &fpu->si_float_regs[0],
+	unsigned long *fpregs = (unsigned long *)(regs+1);
+	copy_from_user(fpregs, &fpu->si_float_regs[0],
 		       (sizeof(unsigned int) * 64));
-	__get_user(current->tss.fsr, &fpu->si_fsr);
+	__get_user(fpregs[32], &fpu->si_fsr);
+	__get_user(fpregs[33], &fpu->si_gsr);
+	regs->fprs = FPRS_FEF;
 }
 
 void do_sigreturn(struct pt_regs *regs)
@@ -139,24 +250,25 @@
 #ifdef CONFIG_SPARC32_COMPAT
 	if (current->tss.flags & SPARC_FLAG_32BIT) {
 		extern asmlinkage void do_sigreturn32(struct pt_regs *);
-		do_sigreturn32(regs);
-		return;
+		return do_sigreturn32(regs);
 	}
 #endif	
 	synchronize_user_stack ();
-	sf = (struct new_signal_frame *) regs->u_regs [UREG_FP];
+	sf = (struct new_signal_frame *)
+		(regs->u_regs [UREG_FP] + STACK_BIAS);
+
 	/* 1. Make sure we are not getting garbage from the user */
-	if (verify_area (VERIFY_READ, sf, sizeof (*sf))){
+	if (verify_area (VERIFY_READ, sf, sizeof (*sf)))
 		goto segv;
-	}
-	if (((unsigned long) sf) & 3){
+
+	if (((unsigned long) sf) & 3)
 		goto segv;
-	}
+
 	get_user(tpc, &sf->info.si_regs.tpc);
 	__get_user(tnpc, &sf->info.si_regs.tnpc);
-	if ((tpc | tnpc) & 3){
+	if ((tpc | tnpc) & 3)
 		goto segv;
-	}
+
 	regs->tpc = tpc;
 	regs->tnpc = tnpc;
 
@@ -165,9 +277,9 @@
 	__get_user(tstate, &sf->info.si_regs.tstate);
 	copy_from_user(regs->u_regs, sf->info.si_regs.u_regs, sizeof(regs->u_regs));
 
-	/* User can only change condition codes and FPU enabling in %tstate. */
-	regs->tstate &= ~(TSTATE_ICC | TSTATE_PEF);
-	regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_PEF));
+	/* User can only change condition codes in %tstate. */
+	regs->tstate &= ~(TSTATE_ICC);
+	regs->tstate |= (tstate & TSTATE_ICC);
 
 	__get_user(fpu_save, &sf->fpu_save);
 	if (fpu_save)
@@ -191,27 +303,12 @@
 static inline void
 save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu)
 {
-#ifdef __SMP__
-	if (current->flags & PF_USEDFPU) {
-		fprs_write(FPRS_FEF);
-		fpsave((unsigned long *)&current->tss.float_regs[0],
-		       &current->tss.fsr);
-		regs->tstate &= ~(TSTATE_PEF);
-		current->flags &= ~(PF_USEDFPU);
-	}
-#else
-	if (current == last_task_used_math) {
-		fprs_write(FPRS_FEF);
-		fpsave((unsigned long *)&current->tss.float_regs[0],
-		       &current->tss.fsr);
-		last_task_used_math = 0;
-		regs->tstate &= ~(TSTATE_PEF);
-	}
-#endif
-	copy_to_user(&fpu->si_float_regs[0], &current->tss.float_regs[0],
+	unsigned long *fpregs = (unsigned long *)(regs+1);
+	copy_to_user(&fpu->si_float_regs[0], fpregs,
 		     (sizeof(unsigned int) * 64));
-	__put_user(current->tss.fsr, &fpu->si_fsr);
-	current->used_math = 0;
+	__put_user(fpregs[32], &fpu->si_fsr);
+	__put_user(fpregs[33], &fpu->si_gsr);
+	regs->fprs = 0;
 }
 
 static inline void
@@ -226,17 +323,18 @@
 	/* 1. Make sure everything is clean */
 	synchronize_user_stack();
 	sigframe_size = NF_ALIGNEDSZ;
-	if (!current->used_math)
+	if (!(current->flags & PF_USEDFPU))
 		sigframe_size -= sizeof(__siginfo_fpu_t);
 
-	sf = (struct new_signal_frame *)(regs->u_regs[UREG_FP] - sigframe_size);
+	sf = (struct new_signal_frame *)
+		(regs->u_regs[UREG_FP] + STACK_BIAS - sigframe_size);
 	
-	if (invalid_frame_pointer (sf, sigframe_size)){
+	if (invalid_frame_pointer (sf, sigframe_size)) {
 		lock_kernel ();
 		do_exit(SIGILL);
 	}
 
-	if (current->tss.w_saved != 0){
+	if (current->tss.w_saved != 0) {
 		printk ("%s[%d]: Invalid user stack frame for "
 			"signal delivery.\n", current->comm, current->pid);
 		lock_kernel ();
@@ -246,7 +344,7 @@
 	/* 2. Save the current process state */
 	copy_to_user(&sf->info.si_regs, regs, sizeof (*regs));
 
-	if (current->used_math) {
+	if (current->flags & PF_USEDFPU) {
 		save_fpu_state(regs, &sf->fpu_state);
 		__put_user((u64)&sf->fpu_state, &sf->fpu_save);
 	} else {
@@ -254,8 +352,8 @@
 	}
 
 	__put_user(oldmask, &sf->info.si_mask);
-	for (i = 0; i < sizeof(struct reg_window)/8; i++) {
-		__get_user(tmp, (((u64 *)regs->u_regs[UREG_FP])+i));
+	for (i = 0; i < sizeof(struct reg_window)/sizeof(u64); i++) {
+		__get_user(tmp, (((u64 *)(regs->u_regs[UREG_FP]+STACK_BIAS))+i));
 		__put_user(tmp, (((u64 *)sf)+i));
 	}
 	
@@ -264,7 +362,7 @@
 	__put_user(0x91d02011, &sf->insns[1]); /* t 0x11 */
 
 	/* 4. signal handler back-trampoline and parameters */
-	regs->u_regs[UREG_FP] = (unsigned long) sf;
+	regs->u_regs[UREG_FP] = ((unsigned long) sf) - STACK_BIAS;
 	regs->u_regs[UREG_I0] = signo;
 	regs->u_regs[UREG_I1] = (unsigned long) &sf->info;
 	regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2);
@@ -274,15 +372,22 @@
 	regs->tnpc = (regs->tpc + 4);
 
 	/* Flush instruction space. */
-	__asm__ __volatile__("
-	membar		#StoreStore
-	stxa		%%g0, [%0] %2
-	stxa		%%g0, [%1] %2
-	flush		%%g4
-	" : /* no outputs */
-	  : "r" (((unsigned long)&(sf->insns[0])) & ~(PAGE_MASK)),
-	    "r" ((((unsigned long)&(sf->insns[0])) & ~(PAGE_MASK)) + PAGE_SIZE),
-	    "i" (ASI_IC_TAG));
+	{
+		unsigned long address = ((unsigned long)&(sf->insns[0]));
+		pgd_t *pgdp = pgd_offset(current->mm, address);
+		pmd_t *pmdp = pmd_offset(pgdp, address);
+		pte_t *ptep = pte_offset(pmdp, address);
+
+		if(pte_present(*ptep)) {
+			unsigned long page = pte_page(*ptep);
+
+			__asm__ __volatile__("
+			membar	#StoreStore
+			flush	%0 + %1"
+			: : "r" (page), "r" (address & (PAGE_SIZE - 1))
+			: "memory");
+		}
+	}
 }
 
 static inline void handle_signal(unsigned long signr, struct sigaction *sa,

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov