patch-2.4.21 linux-2.4.21/arch/ia64/ia32/ia32_signal.c

Next file: linux-2.4.21/arch/ia64/ia32/ia32_support.c
Previous file: linux-2.4.21/arch/ia64/ia32/ia32_entry.S
Back to the patch index
Back to the overall index

diff -urN linux-2.4.20/arch/ia64/ia32/ia32_signal.c linux-2.4.21/arch/ia64/ia32/ia32_signal.c
@@ -39,6 +39,16 @@
 #define __IA32_NR_sigreturn            119
 #define __IA32_NR_rt_sigreturn         173
 
+register double f16 asm ("f16"); register double f17 asm ("f17");
+register double f18 asm ("f18"); register double f19 asm ("f19");
+register double f20 asm ("f20"); register double f21 asm ("f21");
+register double f22 asm ("f22"); register double f23 asm ("f23");
+
+register double f24 asm ("f24"); register double f25 asm ("f25");
+register double f26 asm ("f26"); register double f27 asm ("f27");
+register double f28 asm ("f28"); register double f29 asm ("f29");
+register double f30 asm ("f30"); register double f31 asm ("f31");
+
 struct sigframe_ia32
 {
        int pretcode;
@@ -143,6 +153,304 @@
 	return err;
 }
 
+
+/*
+ *  SAVE and RESTORE of ia32 fpstate info, from ia64 current state
+ *  Used in exception handler to pass the fpstate to the user, and restore
+ *  the fpstate while returning from the exception handler.
+ *
+ *    fpstate info and their mapping to IA64 regs:
+ *    fpstate    REG(BITS)      Attribute    Comments
+ *    cw         ar.fcr(0:12)                with bits 7 and 6 not used
+ *    sw         ar.fsr(0:15)
+ *    tag        ar.fsr(16:31)               with odd numbered bits not used
+ *                                           (read returns 0, writes ignored)
+ *    ipoff      ar.fir(0:31)
+ *    cssel      ar.fir(32:47)
+ *    dataoff    ar.fdr(0:31)
+ *    datasel    ar.fdr(32:47)
+ *
+ *    _st[(0+TOS)%8]   f8
+ *    _st[(1+TOS)%8]   f9                    (f8, f9 from ptregs)
+ *      : :            :                     (f10..f15 from live reg)
+ *      : :            :
+ *    _st[(7+TOS)%8]   f15                   TOS=sw.top(bits11:13)
+ *
+ *    status     Same as sw     RO
+ *    magic      0                           as X86_FXSR_MAGIC in ia32
+ *    mxcsr      Bits(7:15)=ar.fcr(39:47)
+ *               Bits(0:5) =ar.fsr(32:37)    with bit 6 reserved
+ *    _xmm[0..7] f16..f31                    (live registers)
+ *                                           with _xmm[0]
+ *                                             Bit(64:127)=f17(0:63)
+ *                                             Bit(0:63)=f16(0:63)
+ *    All other fields unused...
+ */
+
+#define __ldfe(regnum, x)						\
+({									\
+ 	register double __f__ asm ("f"#regnum);				\
+	__asm__ __volatile__ ("ldfe %0=[%1] ;;" :"=f"(__f__): "r"(x));	\
+})
+
+#define __ldf8(regnum, x)						\
+({									\
+ 	register double __f__ asm ("f"#regnum);				\
+	__asm__ __volatile__ ("ldf8 %0=[%1] ;;" :"=f"(__f__): "r"(x));	\
+})
+
+#define __stfe(x, regnum)							\
+({										\
+ 	register double __f__ asm ("f"#regnum);					\
+	__asm__ __volatile__ ("stfe [%0]=%1" :: "r"(x), "f"(__f__) : "memory");	\
+})
+
+#define __stf8(x, regnum)							\
+({										\
+ 	register double __f__ asm ("f"#regnum);					\
+	__asm__ __volatile__ ("stf8 [%0]=%1" :: "r"(x), "f"(__f__) : "memory");	\
+})
+
+static int
+save_ia32_fpstate_live (struct _fpstate_ia32 *save)
+{
+	struct task_struct *tsk = current;
+	struct pt_regs *ptp;
+	struct _fpreg_ia32 *fpregp;
+	char buf[32];
+	unsigned long fsr, fcr, fir, fdr;
+	unsigned long new_fsr;
+	unsigned long num128[2];
+	unsigned long mxcsr=0;
+	int fp_tos, fr8_st_map;
+
+	if (!access_ok(VERIFY_WRITE, save, sizeof(*save)))
+		return -EFAULT;
+
+	/* Readin fsr, fcr, fir, fdr and copy onto fpstate */
+	asm volatile ( "mov %0=ar.fsr;" : "=r"(fsr));
+	asm volatile ( "mov %0=ar.fcr;" : "=r"(fcr));
+	asm volatile ( "mov %0=ar.fir;" : "=r"(fir));
+	asm volatile ( "mov %0=ar.fdr;" : "=r"(fdr));
+	/*
+	 * We need to clear the exception state before calling the signal handler. Clear
+	 * the bits 15, bits 0-7 in fp status word. Similar to the functionality of fnclex
+	 * instruction.
+	 */
+	new_fsr = fsr & ~0x80ff;
+	asm volatile ( "mov ar.fsr=%0;" :: "r"(new_fsr));
+
+	__put_user(fcr & 0xffff, &save->cw);
+	__put_user(fsr & 0xffff, &save->sw);
+	__put_user((fsr>>16) & 0xffff, &save->tag);
+	__put_user(fir, &save->ipoff);
+	__put_user((fir>>32) & 0xffff, &save->cssel);
+	__put_user(fdr, &save->dataoff);
+	__put_user((fdr>>32) & 0xffff, &save->datasel);
+	__put_user(fsr & 0xffff, &save->status);
+
+	mxcsr = ((fcr>>32) & 0xff80) | ((fsr>>32) & 0x3f);
+	__put_user(mxcsr & 0xffff, &save->mxcsr);
+	__put_user( 0, &save->magic); //#define X86_FXSR_MAGIC   0x0000
+
+	/*
+	 * save f8 and f9  from pt_regs
+	 * save f10..f15 from live register set
+	 */
+	/*
+	 *  Find the location where f8 has to go in fp reg stack.  This depends on
+	 *  TOP(11:13) field of sw. Other f reg continue sequentially from where f8 maps
+	 *  to.
+	 */
+	fp_tos = (fsr>>11)&0x7;
+	fr8_st_map = (8-fp_tos)&0x7;
+	ptp = ia64_task_regs(tsk);
+	fpregp = (struct _fpreg_ia32 *)(((unsigned long)buf + 15) & ~15);
+	ia64f2ia32f(fpregp, &ptp->f8);
+	copy_to_user(&save->_st[(0+fr8_st_map)&0x7], fpregp, sizeof(struct _fpreg_ia32));
+	ia64f2ia32f(fpregp, &ptp->f9);
+	copy_to_user(&save->_st[(1+fr8_st_map)&0x7], fpregp, sizeof(struct _fpreg_ia32));
+
+	__stfe(fpregp, 10);
+	copy_to_user(&save->_st[(2+fr8_st_map)&0x7], fpregp, sizeof(struct _fpreg_ia32));
+	__stfe(fpregp, 11);
+	copy_to_user(&save->_st[(3+fr8_st_map)&0x7], fpregp, sizeof(struct _fpreg_ia32));
+	__stfe(fpregp, 12);
+	copy_to_user(&save->_st[(4+fr8_st_map)&0x7], fpregp, sizeof(struct _fpreg_ia32));
+	__stfe(fpregp, 13);
+	copy_to_user(&save->_st[(5+fr8_st_map)&0x7], fpregp, sizeof(struct _fpreg_ia32));
+	__stfe(fpregp, 14);
+	copy_to_user(&save->_st[(6+fr8_st_map)&0x7], fpregp, sizeof(struct _fpreg_ia32));
+	__stfe(fpregp, 15);
+	copy_to_user(&save->_st[(7+fr8_st_map)&0x7], fpregp, sizeof(struct _fpreg_ia32));
+
+	__stf8(&num128[0], 16);
+	__stf8(&num128[1], 17);
+	copy_to_user(&save->_xmm[0], num128, sizeof(struct _xmmreg_ia32));
+
+	__stf8(&num128[0], 18);
+	__stf8(&num128[1], 19);
+	copy_to_user(&save->_xmm[1], num128, sizeof(struct _xmmreg_ia32));
+
+	__stf8(&num128[0], 20);
+	__stf8(&num128[1], 21);
+	copy_to_user(&save->_xmm[2], num128, sizeof(struct _xmmreg_ia32));
+
+	__stf8(&num128[0], 22);
+	__stf8(&num128[1], 23);
+	copy_to_user(&save->_xmm[3], num128, sizeof(struct _xmmreg_ia32));
+
+	__stf8(&num128[0], 24);
+	__stf8(&num128[1], 25);
+	copy_to_user(&save->_xmm[4], num128, sizeof(struct _xmmreg_ia32));
+
+	__stf8(&num128[0], 26);
+	__stf8(&num128[1], 27);
+	copy_to_user(&save->_xmm[5], num128, sizeof(struct _xmmreg_ia32));
+
+	__stf8(&num128[0], 28);
+	__stf8(&num128[1], 29);
+	copy_to_user(&save->_xmm[6], num128, sizeof(struct _xmmreg_ia32));
+
+	__stf8(&num128[0], 30);
+	__stf8(&num128[1], 31);
+	copy_to_user(&save->_xmm[7], num128, sizeof(struct _xmmreg_ia32));
+	return 0;
+}
+
+static int
+restore_ia32_fpstate_live (struct _fpstate_ia32 *save)
+{
+	struct task_struct *tsk = current;
+	struct pt_regs *ptp;
+	unsigned int lo, hi;
+	unsigned long num128[2];
+	unsigned long num64, mxcsr;
+	struct _fpreg_ia32 *fpregp;
+	char buf[32];
+	unsigned long fsr, fcr, fir, fdr;
+	int fp_tos, fr8_st_map;
+
+	if (!access_ok(VERIFY_READ, save, sizeof(*save)))
+		return(-EFAULT);
+
+	/*
+	 * Updating fsr, fcr, fir, fdr.
+	 * Just a bit more complicated than save.
+	 * - Need to make sure that we dont write any value other than the
+	 *   specific fpstate info
+	 * - Need to make sure that the untouched part of frs, fdr, fir, fcr
+	 *   should remain same while writing.
+	 * So, we do a read, change specific fields and write.
+	 */
+	asm volatile ( "mov %0=ar.fsr;" : "=r"(fsr));
+	asm volatile ( "mov %0=ar.fcr;" : "=r"(fcr));
+	asm volatile ( "mov %0=ar.fir;" : "=r"(fir));
+	asm volatile ( "mov %0=ar.fdr;" : "=r"(fdr));
+
+	__get_user(mxcsr, (unsigned int *)&save->mxcsr);
+	/* setting bits 0..5 8..12 with cw and 39..47 from mxcsr */
+	__get_user(lo, (unsigned int *)&save->cw);
+	num64 = mxcsr & 0xff10;
+	num64 = (num64 << 32) | (lo & 0x1f3f);
+	fcr = (fcr & (~0xff1000001f3f)) | num64;
+
+	/* setting bits 0..31 with sw and tag and 32..37 from mxcsr */
+	__get_user(lo, (unsigned int *)&save->sw);
+	/* set bits 15,7 (fsw.b, fsw.es) to reflect the current error status */
+	if ( !(lo & 0x7f) )
+		lo &= (~0x8080);
+	__get_user(hi, (unsigned int *)&save->tag);
+	num64 = mxcsr & 0x3f;
+	num64 = (num64 << 16) | (hi & 0xffff);
+	num64 = (num64 << 16) | (lo & 0xffff);
+	fsr = (fsr & (~0x3fffffffff)) | num64;
+
+	/* setting bits 0..47 with cssel and ipoff */
+	__get_user(lo, (unsigned int *)&save->ipoff);
+	__get_user(hi, (unsigned int *)&save->cssel);
+	num64 = hi & 0xffff;
+	num64 = (num64 << 32) | lo;
+	fir = (fir & (~0xffffffffffff)) | num64;
+
+	/* setting bits 0..47 with datasel and dataoff */
+	__get_user(lo, (unsigned int *)&save->dataoff);
+	__get_user(hi, (unsigned int *)&save->datasel);
+	num64 = hi & 0xffff;
+	num64 = (num64 << 32) | lo;
+	fdr = (fdr & (~0xffffffffffff)) | num64;
+
+	asm volatile ( "mov ar.fsr=%0;" :: "r"(fsr));
+	asm volatile ( "mov ar.fcr=%0;" :: "r"(fcr));
+	asm volatile ( "mov ar.fir=%0;" :: "r"(fir));
+	asm volatile ( "mov ar.fdr=%0;" :: "r"(fdr));
+
+	/*
+	 * restore f8, f9 onto pt_regs
+	 * restore f10..f15 onto live registers
+	 */
+	/*
+	 *  Find the location where f8 has to go in fp reg stack.  This depends on
+	 *  TOP(11:13) field of sw. Other f reg continue sequentially from where f8 maps
+	 *  to.
+	 */
+	fp_tos = (fsr>>11)&0x7;
+	fr8_st_map = (8-fp_tos)&0x7;
+	fpregp = (struct _fpreg_ia32 *)(((unsigned long)buf + 15) & ~15);
+
+	ptp = ia64_task_regs(tsk);
+	copy_from_user(fpregp, &save->_st[(0+fr8_st_map)&0x7], sizeof(struct _fpreg_ia32));
+	ia32f2ia64f(&ptp->f8, fpregp);
+	copy_from_user(fpregp, &save->_st[(1+fr8_st_map)&0x7], sizeof(struct _fpreg_ia32));
+	ia32f2ia64f(&ptp->f9, fpregp);
+
+	copy_from_user(fpregp, &save->_st[(2+fr8_st_map)&0x7], sizeof(struct _fpreg_ia32));
+	__ldfe(10, fpregp);
+	copy_from_user(fpregp, &save->_st[(3+fr8_st_map)&0x7], sizeof(struct _fpreg_ia32));
+	__ldfe(11, fpregp);
+	copy_from_user(fpregp, &save->_st[(4+fr8_st_map)&0x7], sizeof(struct _fpreg_ia32));
+	__ldfe(12, fpregp);
+	copy_from_user(fpregp, &save->_st[(5+fr8_st_map)&0x7], sizeof(struct _fpreg_ia32));
+	__ldfe(13, fpregp);
+	copy_from_user(fpregp, &save->_st[(6+fr8_st_map)&0x7], sizeof(struct _fpreg_ia32));
+	__ldfe(14, fpregp);
+	copy_from_user(fpregp, &save->_st[(7+fr8_st_map)&0x7], sizeof(struct _fpreg_ia32));
+	__ldfe(15, fpregp);
+
+	copy_from_user(num128, &save->_xmm[0], sizeof(struct _xmmreg_ia32));
+	__ldf8(16, &num128[0]);
+	__ldf8(17, &num128[1]);
+
+	copy_from_user(num128, &save->_xmm[1], sizeof(struct _xmmreg_ia32));
+	__ldf8(18, &num128[0]);
+	__ldf8(19, &num128[1]);
+
+	copy_from_user(num128, &save->_xmm[2], sizeof(struct _xmmreg_ia32));
+	__ldf8(20, &num128[0]);
+	__ldf8(21, &num128[1]);
+
+	copy_from_user(num128, &save->_xmm[3], sizeof(struct _xmmreg_ia32));
+	__ldf8(22, &num128[0]);
+	__ldf8(23, &num128[1]);
+
+	copy_from_user(num128, &save->_xmm[4], sizeof(struct _xmmreg_ia32));
+	__ldf8(24, &num128[0]);
+	__ldf8(25, &num128[1]);
+
+	copy_from_user(num128, &save->_xmm[5], sizeof(struct _xmmreg_ia32));
+	__ldf8(26, &num128[0]);
+	__ldf8(27, &num128[1]);
+
+	copy_from_user(num128, &save->_xmm[6], sizeof(struct _xmmreg_ia32));
+	__ldf8(28, &num128[0]);
+	__ldf8(29, &num128[1]);
+
+	copy_from_user(num128, &save->_xmm[7], sizeof(struct _xmmreg_ia32));
+	__ldf8(30, &num128[0]);
+	__ldf8(31, &num128[1]);
+	return 0;
+}
+
 static inline void
 sigact_set_handler (struct k_sigaction *sa, unsigned int handler, unsigned int restorer)
 {
@@ -371,6 +679,9 @@
 	int  err = 0;
 	unsigned long flag;
 
+	if (!access_ok(VERIFY_WRITE, sc, sizeof(*sc)))
+		return -EFAULT;
+
 	err |= __put_user((regs->r16 >> 32) & 0xffff, (unsigned int *)&sc->fs);
 	err |= __put_user((regs->r16 >> 48) & 0xffff, (unsigned int *)&sc->gs);
 	err |= __put_user((regs->r16 >> 16) & 0xffff, (unsigned int *)&sc->es);
@@ -397,6 +708,11 @@
 	err |= __put_user(regs->r12, &sc->esp_at_signal);
 	err |= __put_user((regs->r17 >> 16) & 0xffff, (unsigned int *)&sc->ss);
 
+	if ( save_ia32_fpstate_live(fpstate) < 0 )
+		err = -EFAULT;
+	else
+		err |= __put_user((u32)(u64)fpstate, &sc->fpstate);
+
 #if 0
 	tmp = save_i387(fpstate);
 	if (tmp < 0)
@@ -418,6 +734,9 @@
 {
 	unsigned int err = 0;
 
+	if (!access_ok(VERIFY_READ, sc, sizeof(*sc)))
+		return(-EFAULT);
+
 #define COPY(ia64x, ia32x)	err |= __get_user(regs->ia64x, &sc->ia32x)
 
 #define copyseg_gs(tmp)		(regs->r16 |= (unsigned long) tmp << 48)
@@ -477,6 +796,16 @@
 		regs->r1 = -1;	/* disable syscall checks, r1 is orig_eax */
 	}
 
+	{
+		struct _fpstate_ia32 *buf = NULL;
+		u32    fpstate_ptr;
+		err |= get_user(fpstate_ptr, &(sc->fpstate));
+		buf = (struct _fpstate_ia32 *)(u64)fpstate_ptr;
+		if (buf) {
+			err |= restore_ia32_fpstate_live(buf);
+		}
+	}
+
 #if 0
 	{
 		struct _fpstate * buf;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)