Commit af59e218 authored by Anton Blanchard's avatar Anton Blanchard

ppc64: Allow user to change MSR_FE0 and MSR_FE1 - from ppc32

parent 220d2581
...@@ -1421,7 +1421,7 @@ _STATIC(load_up_fpu) ...@@ -1421,7 +1421,7 @@ _STATIC(load_up_fpu)
addi r4,r4,THREAD /* want THREAD of last_task_used_math */ addi r4,r4,THREAD /* want THREAD of last_task_used_math */
SAVE_32FPRS(0, r4) SAVE_32FPRS(0, r4)
mffs fr0 mffs fr0
stfd fr0,THREAD_FPSCR-4(r4) stfd fr0,THREAD_FPSCR(r4)
ld r5,PT_REGS(r4) ld r5,PT_REGS(r4)
ld r4,_MSR-STACK_FRAME_OVERHEAD(r5) ld r4,_MSR-STACK_FRAME_OVERHEAD(r5)
li r20,MSR_FP|MSR_FE0|MSR_FE1 li r20,MSR_FP|MSR_FE0|MSR_FE1
...@@ -1430,10 +1430,12 @@ _STATIC(load_up_fpu) ...@@ -1430,10 +1430,12 @@ _STATIC(load_up_fpu)
1: 1:
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
/* enable use of FP after return */ /* enable use of FP after return */
ori r23,r23,MSR_FP|MSR_FE0|MSR_FE1 ld r4,PACACURRENT(r13)
ld r4, PACACURRENT(r13)
addi r5,r4,THREAD /* Get THREAD */ addi r5,r4,THREAD /* Get THREAD */
lfd fr0,THREAD_FPSCR-4(r5) lwz r4,THREAD_FPEXC_MODE(r5)
ori r23,r23,MSR_FP
or r23,r23,r4
lfd fr0,THREAD_FPSCR(r5)
mtfsf 0xff,fr0 mtfsf 0xff,fr0
REST_32FPRS(0, r5) REST_32FPRS(0, r5)
#ifndef CONFIG_SMP #ifndef CONFIG_SMP
...@@ -1476,7 +1478,7 @@ _GLOBAL(giveup_fpu) ...@@ -1476,7 +1478,7 @@ _GLOBAL(giveup_fpu)
cmpi 0,r5,0 cmpi 0,r5,0
SAVE_32FPRS(0, r3) SAVE_32FPRS(0, r3)
mffs fr0 mffs fr0
stfd fr0,THREAD_FPSCR-4(r3) stfd fr0,THREAD_FPSCR(r3)
beq 1f beq 1f
ld r4,_MSR-STACK_FRAME_OVERHEAD(r5) ld r4,_MSR-STACK_FRAME_OVERHEAD(r5)
li r3,MSR_FP|MSR_FE0|MSR_FE1 li r3,MSR_FP|MSR_FE0|MSR_FE1
......
...@@ -460,21 +460,21 @@ _GLOBAL(_get_HID0) ...@@ -460,21 +460,21 @@ _GLOBAL(_get_HID0)
blr blr
_GLOBAL(cvt_fd) _GLOBAL(cvt_fd)
lfd 0,-4(r5) /* load up fpscr value */ lfd 0,0(r5) /* load up fpscr value */
mtfsf 0xff,0 mtfsf 0xff,0
lfs 0,0(r3) lfs 0,0(r3)
stfd 0,0(r4) stfd 0,0(r4)
mffs 0 /* save new fpscr value */ mffs 0 /* save new fpscr value */
stfd 0,-4(r5) stfd 0,0(r5)
blr blr
_GLOBAL(cvt_df) _GLOBAL(cvt_df)
lfd 0,-4(r5) /* load up fpscr value */ lfd 0,0(r5) /* load up fpscr value */
mtfsf 0xff,0 mtfsf 0xff,0
lfd 0,0(r3) lfd 0,0(r3)
stfs 0,0(r4) stfs 0,0(r4)
mffs 0 /* save new fpscr value */ mffs 0 /* save new fpscr value */
stfd 0,-4(r5) stfd 0,0(r5)
blr blr
/* /*
......
...@@ -51,6 +51,7 @@ main(void) ...@@ -51,6 +51,7 @@ main(void)
/* task_struct->thread */ /* task_struct->thread */
DEFINE(THREAD, offsetof(struct task_struct, thread)); DEFINE(THREAD, offsetof(struct task_struct, thread));
DEFINE(PT_REGS, offsetof(struct thread_struct, regs)); DEFINE(PT_REGS, offsetof(struct thread_struct, regs));
DEFINE(THREAD_FPEXC_MODE, offsetof(struct thread_struct, fpexc_mode));
DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0])); DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0]));
DEFINE(THREAD_FPSCR, offsetof(struct thread_struct, fpscr)); DEFINE(THREAD_FPSCR, offsetof(struct thread_struct, fpscr));
DEFINE(KSP, offsetof(struct thread_struct, ksp)); DEFINE(KSP, offsetof(struct thread_struct, ksp));
......
...@@ -163,8 +163,7 @@ release_thread(struct task_struct *t) ...@@ -163,8 +163,7 @@ release_thread(struct task_struct *t)
*/ */
int int
copy_thread(int nr, unsigned long clone_flags, unsigned long usp, copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
unsigned long unused, unsigned long unused, struct task_struct *p, struct pt_regs *regs)
struct task_struct *p, struct pt_regs *regs)
{ {
struct pt_regs *childregs, *kregs; struct pt_regs *childregs, *kregs;
extern void ret_from_fork(void); extern void ret_from_fork(void);
...@@ -208,17 +207,6 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, ...@@ -208,17 +207,6 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
*/ */
kregs->nip = *((unsigned long *)ret_from_fork); kregs->nip = *((unsigned long *)ret_from_fork);
/*
* copy fpu info - assume lazy fpu switch now always
* -- Cort
*/
if (regs->msr & MSR_FP) {
giveup_fpu(current);
childregs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1);
}
memcpy(&p->thread.fpr, &current->thread.fpr, sizeof(p->thread.fpr));
p->thread.fpscr = current->thread.fpscr;
return 0; return 0;
} }
...@@ -247,10 +235,38 @@ void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp) ...@@ -247,10 +235,38 @@ void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp)
current->thread.fpscr = 0; current->thread.fpscr = 0;
} }
/* XXX temporary */
#define PR_FP_EXC_PRECISE 3 /* precise exception mode */
int set_fpexc_mode(struct task_struct *tsk, unsigned int val)
{
struct pt_regs *regs = tsk->thread.regs;
if (val > PR_FP_EXC_PRECISE)
return -EINVAL;
tsk->thread.fpexc_mode = __pack_fe01(val);
if (regs != NULL && (regs->msr & MSR_FP) != 0)
regs->msr = (regs->msr & ~(MSR_FE0|MSR_FE1))
| tsk->thread.fpexc_mode;
return 0;
}
int get_fpexc_mode(struct task_struct *tsk, unsigned long adr)
{
unsigned int val;
val = __unpack_fe01(tsk->thread.fpexc_mode);
return put_user(val, (unsigned int *) adr);
}
int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6,
struct pt_regs *regs) struct pt_regs *regs)
{ {
struct task_struct *p; struct task_struct *p;
if (regs->msr & MSR_FP)
giveup_fpu(current);
p = do_fork(p1 & ~CLONE_IDLETASK, regs->gpr[1], regs, 0); p = do_fork(p1 & ~CLONE_IDLETASK, regs->gpr[1], regs, 0);
return IS_ERR(p) ? PTR_ERR(p) : p->pid; return IS_ERR(p) ? PTR_ERR(p) : p->pid;
} }
...@@ -259,6 +275,10 @@ int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, ...@@ -259,6 +275,10 @@ int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6,
struct pt_regs *regs) struct pt_regs *regs)
{ {
struct task_struct *p; struct task_struct *p;
if (regs->msr & MSR_FP)
giveup_fpu(current);
p = do_fork(SIGCHLD, regs->gpr[1], regs, 0); p = do_fork(SIGCHLD, regs->gpr[1], regs, 0);
return IS_ERR(p) ? PTR_ERR(p) : p->pid; return IS_ERR(p) ? PTR_ERR(p) : p->pid;
} }
...@@ -267,6 +287,10 @@ int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6, ...@@ -267,6 +287,10 @@ int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6,
struct pt_regs *regs) struct pt_regs *regs)
{ {
struct task_struct *p; struct task_struct *p;
if (regs->msr & MSR_FP)
giveup_fpu(current);
p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], regs, 0); p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], regs, 0);
return IS_ERR(p) ? PTR_ERR(p) : p->pid; return IS_ERR(p) ? PTR_ERR(p) : p->pid;
} }
......
...@@ -177,10 +177,7 @@ int sys32_ptrace(long request, long pid, unsigned long addr, unsigned long data) ...@@ -177,10 +177,7 @@ int sys32_ptrace(long request, long pid, unsigned long addr, unsigned long data)
if (numReg >= PT_FPR0) { if (numReg >= PT_FPR0) {
if (child->thread.regs->msr & MSR_FP) if (child->thread.regs->msr & MSR_FP)
giveup_fpu(child); giveup_fpu(child);
if (numReg == PT_FPSCR) tmp = ((unsigned long int *)child->thread.fpr)[numReg - PT_FPR0];
tmp = ((unsigned int *)child->thread.fpscr);
else
tmp = ((unsigned long int *)child->thread.fpr)[numReg - PT_FPR0];
} else { /* register within PT_REGS struct */ } else { /* register within PT_REGS struct */
tmp = get_reg(child, numReg); tmp = get_reg(child, numReg);
} }
......
...@@ -55,7 +55,19 @@ ...@@ -55,7 +55,19 @@
* able to continue 'til the next breakpoint from within the signal * able to continue 'til the next breakpoint from within the signal
* handler, even if the handler returns. * handler, even if the handler returns.
*/ */
#if 0
#define MSR_USERCHANGE (MSR_FE0 | MSR_FE1) #define MSR_USERCHANGE (MSR_FE0 | MSR_FE1)
#else
/*
* glibc tries to set FE0/FE1 via a signal handler. Since it only ever
* sets both bits and this is the default setting we now disable this
* behaviour. This is done to insure the new prctl which alters FE0/FE1 does
* not get overriden by glibc. Setting and clearing FE0/FE1 via signal
* handler has always been bogus since load_up_fpu used to set FE0/FE1
* unconditionally.
*/
#define MSR_USERCHANGE 0
#endif
/* /*
* When we have signals to deliver, we set up on the * When we have signals to deliver, we set up on the
......
...@@ -39,7 +39,19 @@ ...@@ -39,7 +39,19 @@
* able to continue 'til the next breakpoint from within the signal * able to continue 'til the next breakpoint from within the signal
* handler, even if the handler returns. * handler, even if the handler returns.
*/ */
#if 0
#define MSR_USERCHANGE (MSR_FE0 | MSR_FE1) #define MSR_USERCHANGE (MSR_FE0 | MSR_FE1)
#else
/*
* glibc tries to set FE0/FE1 via a signal handler. Since it only ever
* sets both bits and this is the default setting we now disable this
* behaviour. This is done to insure the new prctl which alters FE0/FE1 does
* not get overriden by glibc. Setting and clearing FE0/FE1 via signal
* handler has always been bogus since load_up_fpu used to set FE0/FE1
* unconditionally.
*/
#define MSR_USERCHANGE 0
#endif
struct timespec32 { struct timespec32 {
s32 tv_sec; s32 tv_sec;
......
...@@ -240,14 +240,12 @@ InstructionBreakpointException(struct pt_regs *regs) ...@@ -240,14 +240,12 @@ InstructionBreakpointException(struct pt_regs *regs)
static void parse_fpe(struct pt_regs *regs) static void parse_fpe(struct pt_regs *regs)
{ {
siginfo_t info; siginfo_t info;
unsigned int *tmp; unsigned long fpscr;
unsigned int fpscr;
if (regs->msr & MSR_FP) if (regs->msr & MSR_FP)
giveup_fpu(current); giveup_fpu(current);
tmp = &current->thread.fpscr; fpscr = current->thread.fpscr;
fpscr = *tmp;
/* Invalid operation */ /* Invalid operation */
if ((fpscr & FPSCR_VE) && (fpscr & FPSCR_VX)) if ((fpscr & FPSCR_VE) && (fpscr & FPSCR_VX))
......
...@@ -652,6 +652,7 @@ struct thread_struct { ...@@ -652,6 +652,7 @@ struct thread_struct {
mm_segment_t fs; /* for get_fs() validation */ mm_segment_t fs; /* for get_fs() validation */
double fpr[32]; /* Complete floating point set */ double fpr[32]; /* Complete floating point set */
unsigned long fpscr; /* Floating point status */ unsigned long fpscr; /* Floating point status */
unsigned int fpexc_mode; /* Floating-point exception mode */
}; };
#define INIT_SP (sizeof(init_stack) + (unsigned long) &init_stack) #define INIT_SP (sizeof(init_stack) + (unsigned long) &init_stack)
...@@ -661,7 +662,8 @@ struct thread_struct { ...@@ -661,7 +662,8 @@ struct thread_struct {
(struct pt_regs *)INIT_SP - 1, /* regs */ \ (struct pt_regs *)INIT_SP - 1, /* regs */ \
KERNEL_DS, /*fs*/ \ KERNEL_DS, /*fs*/ \
{0}, /* fpr */ \ {0}, /* fpr */ \
0 /* fpscr */ \ 0, /* fpscr */ \
MSR_FE0|MSR_FE1, /* fpexc_mode */ \
} }
/* /*
...@@ -689,6 +691,23 @@ unsigned long get_wchan(struct task_struct *p); ...@@ -689,6 +691,23 @@ unsigned long get_wchan(struct task_struct *p);
#define KSTK_EIP(tsk) ((tsk)->thread.regs? (tsk)->thread.regs->nip: 0) #define KSTK_EIP(tsk) ((tsk)->thread.regs? (tsk)->thread.regs->nip: 0)
#define KSTK_ESP(tsk) ((tsk)->thread.regs? (tsk)->thread.regs->gpr[1]: 0) #define KSTK_ESP(tsk) ((tsk)->thread.regs? (tsk)->thread.regs->gpr[1]: 0)
/* Get/set floating-point exception mode */
#define GET_FPEXC_CTL(tsk, adr) get_fpexc_mode((tsk), (adr))
#define SET_FPEXC_CTL(tsk, val) set_fpexc_mode((tsk), (val))
extern int get_fpexc_mode(struct task_struct *tsk, unsigned long adr);
extern int set_fpexc_mode(struct task_struct *tsk, unsigned int val);
static inline unsigned int __unpack_fe01(unsigned long msr_bits)
{
return ((msr_bits & MSR_FE0) >> 10) | ((msr_bits & MSR_FE1) >> 8);
}
static inline unsigned int __pack_fe01(unsigned int fpmode)
{
return ((fpmode << 10) & MSR_FE0) | ((fpmode << 8) & MSR_FE1);
}
#define cpu_relax() barrier() #define cpu_relax() barrier()
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment