mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 12:42:02 +00:00
powerpc: Add VSX context save/restore, ptrace and signal support
This patch extends the floating point save and restore code to use the VSX load/stores when VSX is available. This will make FP context save/restore marginally slower on FP only code, when VSX is available, as it has to load/store 128bits rather than just 64bits. Mixing FP, VMX and VSX code will get constant architected state. The signals interface is extended to enable access to VSR 0-31 doubleword 1 after discussions with tool chain maintainers. Backward compatibility is maintained. The ptrace interface is also extended to allow access to VSR 0-31 full registers. Signed-off-by: Michael Neuling <mikey@neuling.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
This commit is contained in:
parent
72ffff5b17
commit
ce48b21007
@ -353,6 +353,11 @@ _GLOBAL(_switch)
|
||||
mflr r20 /* Return to switch caller */
|
||||
mfmsr r22
|
||||
li r0, MSR_FP
|
||||
#ifdef CONFIG_VSX
|
||||
BEGIN_FTR_SECTION
|
||||
oris r0,r0,MSR_VSX@h /* Disable VSX */
|
||||
END_FTR_SECTION_IFSET(CPU_FTR_VSX)
|
||||
#endif /* CONFIG_VSX */
|
||||
#ifdef CONFIG_ALTIVEC
|
||||
BEGIN_FTR_SECTION
|
||||
oris r0,r0,MSR_VEC@h /* Disable altivec */
|
||||
|
@ -57,6 +57,11 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX); \
|
||||
_GLOBAL(load_up_fpu)
|
||||
mfmsr r5
|
||||
ori r5,r5,MSR_FP
|
||||
#ifdef CONFIG_VSX
|
||||
BEGIN_FTR_SECTION
|
||||
oris r5,r5,MSR_VSX@h
|
||||
END_FTR_SECTION_IFSET(CPU_FTR_VSX)
|
||||
#endif
|
||||
SYNC
|
||||
MTMSRD(r5) /* enable use of fpu now */
|
||||
isync
|
||||
@ -73,7 +78,7 @@ _GLOBAL(load_up_fpu)
|
||||
beq 1f
|
||||
toreal(r4)
|
||||
addi r4,r4,THREAD /* want last_task_used_math->thread */
|
||||
SAVE_32FPRS(0, r4)
|
||||
SAVE_32FPVSRS(0, r5, r4)
|
||||
mffs fr0
|
||||
stfd fr0,THREAD_FPSCR(r4)
|
||||
PPC_LL r5,PT_REGS(r4)
|
||||
@ -100,7 +105,7 @@ _GLOBAL(load_up_fpu)
|
||||
#endif
|
||||
lfd fr0,THREAD_FPSCR(r5)
|
||||
MTFSF_L(fr0)
|
||||
REST_32FPRS(0, r5)
|
||||
REST_32FPVSRS(0, r4, r5)
|
||||
#ifndef CONFIG_SMP
|
||||
subi r4,r5,THREAD
|
||||
fromreal(r4)
|
||||
@ -119,6 +124,11 @@ _GLOBAL(load_up_fpu)
|
||||
_GLOBAL(giveup_fpu)
|
||||
mfmsr r5
|
||||
ori r5,r5,MSR_FP
|
||||
#ifdef CONFIG_VSX
|
||||
BEGIN_FTR_SECTION
|
||||
oris r5,r5,MSR_VSX@h
|
||||
END_FTR_SECTION_IFSET(CPU_FTR_VSX)
|
||||
#endif
|
||||
SYNC_601
|
||||
ISYNC_601
|
||||
MTMSRD(r5) /* enable use of fpu now */
|
||||
@ -129,7 +139,7 @@ _GLOBAL(giveup_fpu)
|
||||
addi r3,r3,THREAD /* want THREAD of task */
|
||||
PPC_LL r5,PT_REGS(r3)
|
||||
PPC_LCMPI 0,r5,0
|
||||
SAVE_32FPRS(0, r3)
|
||||
SAVE_32FPVSRS(0, r4 ,r3)
|
||||
mffs fr0
|
||||
stfd fr0,THREAD_FPSCR(r3)
|
||||
beq 1f
|
||||
|
@ -278,6 +278,9 @@ END_FTR_SECTION_IFSET(CPU_FTR_REAL_LE)
|
||||
. = 0xf20
|
||||
b altivec_unavailable_pSeries
|
||||
|
||||
. = 0xf40
|
||||
b vsx_unavailable_pSeries
|
||||
|
||||
#ifdef CONFIG_CBE_RAS
|
||||
HSTD_EXCEPTION_PSERIES(0x1200, cbe_system_error)
|
||||
#endif /* CONFIG_CBE_RAS */
|
||||
@ -297,6 +300,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_REAL_LE)
|
||||
/* moved from 0xf00 */
|
||||
STD_EXCEPTION_PSERIES(., performance_monitor)
|
||||
STD_EXCEPTION_PSERIES(., altivec_unavailable)
|
||||
STD_EXCEPTION_PSERIES(., vsx_unavailable)
|
||||
|
||||
/*
|
||||
* An interrupt came in while soft-disabled; clear EE in SRR1,
|
||||
@ -836,6 +840,67 @@ _STATIC(load_up_altivec)
|
||||
blr
|
||||
#endif /* CONFIG_ALTIVEC */
|
||||
|
||||
.align 7
|
||||
.globl vsx_unavailable_common
|
||||
vsx_unavailable_common:
|
||||
EXCEPTION_PROLOG_COMMON(0xf40, PACA_EXGEN)
|
||||
#ifdef CONFIG_VSX
|
||||
BEGIN_FTR_SECTION
|
||||
bne .load_up_vsx
|
||||
1:
|
||||
END_FTR_SECTION_IFSET(CPU_FTR_VSX)
|
||||
#endif
|
||||
bl .save_nvgprs
|
||||
addi r3,r1,STACK_FRAME_OVERHEAD
|
||||
ENABLE_INTS
|
||||
bl .vsx_unavailable_exception
|
||||
b .ret_from_except
|
||||
|
||||
#ifdef CONFIG_VSX
|
||||
/*
|
||||
* load_up_vsx(unused, unused, tsk)
|
||||
* Disable VSX for the task which had it previously,
|
||||
* and save its vector registers in its thread_struct.
|
||||
* Reuse the fp and vsx saves, but first check to see if they have
|
||||
* been saved already.
|
||||
* On entry: r13 == 'current' && last_task_used_vsx != 'current'
|
||||
*/
|
||||
_STATIC(load_up_vsx)
|
||||
/* Load FP and VSX registers if they haven't been done yet */
|
||||
andi. r5,r12,MSR_FP
|
||||
beql+ load_up_fpu /* skip if already loaded */
|
||||
andis. r5,r12,MSR_VEC@h
|
||||
beql+ load_up_altivec /* skip if already loaded */
|
||||
|
||||
#ifndef CONFIG_SMP
|
||||
ld r3,last_task_used_vsx@got(r2)
|
||||
ld r4,0(r3)
|
||||
cmpdi 0,r4,0
|
||||
beq 1f
|
||||
/* Disable VSX for last_task_used_vsx */
|
||||
addi r4,r4,THREAD
|
||||
ld r5,PT_REGS(r4)
|
||||
ld r4,_MSR-STACK_FRAME_OVERHEAD(r5)
|
||||
lis r6,MSR_VSX@h
|
||||
andc r6,r4,r6
|
||||
std r6,_MSR-STACK_FRAME_OVERHEAD(r5)
|
||||
1:
|
||||
#endif /* CONFIG_SMP */
|
||||
ld r4,PACACURRENT(r13)
|
||||
addi r4,r4,THREAD /* Get THREAD */
|
||||
li r6,1
|
||||
stw r6,THREAD_USED_VSR(r4) /* ... also set thread used vsr */
|
||||
/* enable use of VSX after return */
|
||||
oris r12,r12,MSR_VSX@h
|
||||
std r12,_MSR(r1)
|
||||
#ifndef CONFIG_SMP
|
||||
/* Update last_task_used_math to 'current' */
|
||||
ld r4,PACACURRENT(r13)
|
||||
std r4,0(r3)
|
||||
#endif /* CONFIG_SMP */
|
||||
b fast_exception_return
|
||||
#endif /* CONFIG_VSX */
|
||||
|
||||
/*
|
||||
* Hash table stuff
|
||||
*/
|
||||
|
@ -506,6 +506,39 @@ _GLOBAL(giveup_altivec)
|
||||
|
||||
#endif /* CONFIG_ALTIVEC */
|
||||
|
||||
#ifdef CONFIG_VSX
|
||||
/*
|
||||
* giveup_vsx(tsk)
|
||||
* Disable VSX for the task given as the argument,
|
||||
* and save the vector registers in its thread_struct.
|
||||
* Enables the VSX for use in the kernel on return.
|
||||
*/
|
||||
_GLOBAL(giveup_vsx)
|
||||
mfmsr r5
|
||||
oris r5,r5,MSR_VSX@h
|
||||
mtmsrd r5 /* enable use of VSX now */
|
||||
isync
|
||||
|
||||
cmpdi 0,r3,0
|
||||
beqlr- /* if no previous owner, done */
|
||||
addi r3,r3,THREAD /* want THREAD of task */
|
||||
ld r5,PT_REGS(r3)
|
||||
cmpdi 0,r5,0
|
||||
beq 1f
|
||||
ld r4,_MSR-STACK_FRAME_OVERHEAD(r5)
|
||||
lis r3,MSR_VSX@h
|
||||
andc r4,r4,r3 /* disable VSX for previous task */
|
||||
std r4,_MSR-STACK_FRAME_OVERHEAD(r5)
|
||||
1:
|
||||
#ifndef CONFIG_SMP
|
||||
li r5,0
|
||||
ld r4,last_task_used_vsx@got(r2)
|
||||
std r5,0(r4)
|
||||
#endif /* CONFIG_SMP */
|
||||
blr
|
||||
|
||||
#endif /* CONFIG_VSX */
|
||||
|
||||
/* kexec_wait(phys_cpu)
|
||||
*
|
||||
* wait for the flag to change, indicating this kernel is going away but
|
||||
|
@ -120,6 +120,7 @@ struct mcontext32 {
|
||||
elf_fpregset_t mc_fregs;
|
||||
unsigned int mc_pad[2];
|
||||
elf_vrregset_t32 mc_vregs __attribute__((__aligned__(16)));
|
||||
elf_vsrreghalf_t32 mc_vsregs __attribute__((__aligned__(16)));
|
||||
};
|
||||
|
||||
struct ucontext32 {
|
||||
|
@ -102,6 +102,9 @@ EXPORT_SYMBOL(giveup_fpu);
|
||||
#ifdef CONFIG_ALTIVEC
|
||||
EXPORT_SYMBOL(giveup_altivec);
|
||||
#endif /* CONFIG_ALTIVEC */
|
||||
#ifdef CONFIG_VSX
|
||||
EXPORT_SYMBOL(giveup_vsx);
|
||||
#endif /* CONFIG_VSX */
|
||||
#ifdef CONFIG_SPE
|
||||
EXPORT_SYMBOL(giveup_spe);
|
||||
#endif /* CONFIG_SPE */
|
||||
|
@ -53,6 +53,7 @@ extern unsigned long _get_SP(void);
|
||||
#ifndef CONFIG_SMP
|
||||
struct task_struct *last_task_used_math = NULL;
|
||||
struct task_struct *last_task_used_altivec = NULL;
|
||||
struct task_struct *last_task_used_vsx = NULL;
|
||||
struct task_struct *last_task_used_spe = NULL;
|
||||
#endif
|
||||
|
||||
@ -106,11 +107,23 @@ EXPORT_SYMBOL(enable_kernel_fp);
|
||||
|
||||
int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs)
|
||||
{
|
||||
#ifdef CONFIG_VSX
|
||||
int i;
|
||||
elf_fpreg_t *reg;
|
||||
#endif
|
||||
|
||||
if (!tsk->thread.regs)
|
||||
return 0;
|
||||
flush_fp_to_thread(current);
|
||||
|
||||
#ifdef CONFIG_VSX
|
||||
reg = (elf_fpreg_t *)fpregs;
|
||||
for (i = 0; i < ELF_NFPREG - 1; i++, reg++)
|
||||
*reg = tsk->thread.TS_FPR(i);
|
||||
memcpy(reg, &tsk->thread.fpscr, sizeof(elf_fpreg_t));
|
||||
#else
|
||||
memcpy(fpregs, &tsk->thread.TS_FPR(0), sizeof(*fpregs));
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -149,7 +162,7 @@ void flush_altivec_to_thread(struct task_struct *tsk)
|
||||
}
|
||||
}
|
||||
|
||||
int dump_task_altivec(struct task_struct *tsk, elf_vrregset_t *vrregs)
|
||||
int dump_task_altivec(struct task_struct *tsk, elf_vrreg_t *vrregs)
|
||||
{
|
||||
/* ELF_NVRREG includes the VSCR and VRSAVE which we need to save
|
||||
* separately, see below */
|
||||
@ -179,6 +192,80 @@ int dump_task_altivec(struct task_struct *tsk, elf_vrregset_t *vrregs)
|
||||
}
|
||||
#endif /* CONFIG_ALTIVEC */
|
||||
|
||||
#ifdef CONFIG_VSX
|
||||
#if 0
|
||||
/* not currently used, but some crazy RAID module might want to later */
|
||||
void enable_kernel_vsx(void)
|
||||
{
|
||||
WARN_ON(preemptible());
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
if (current->thread.regs && (current->thread.regs->msr & MSR_VSX))
|
||||
giveup_vsx(current);
|
||||
else
|
||||
giveup_vsx(NULL); /* just enable vsx for kernel - force */
|
||||
#else
|
||||
giveup_vsx(last_task_used_vsx);
|
||||
#endif /* CONFIG_SMP */
|
||||
}
|
||||
EXPORT_SYMBOL(enable_kernel_vsx);
|
||||
#endif
|
||||
|
||||
void flush_vsx_to_thread(struct task_struct *tsk)
|
||||
{
|
||||
if (tsk->thread.regs) {
|
||||
preempt_disable();
|
||||
if (tsk->thread.regs->msr & MSR_VSX) {
|
||||
#ifdef CONFIG_SMP
|
||||
BUG_ON(tsk != current);
|
||||
#endif
|
||||
giveup_vsx(tsk);
|
||||
}
|
||||
preempt_enable();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This dumps the lower half 64bits of the first 32 VSX registers.
|
||||
* This needs to be called with dump_task_fp and dump_task_altivec to
|
||||
* get all the VSX state.
|
||||
*/
|
||||
int dump_task_vsx(struct task_struct *tsk, elf_vrreg_t *vrregs)
|
||||
{
|
||||
elf_vrreg_t *reg;
|
||||
double buf[32];
|
||||
int i;
|
||||
|
||||
if (tsk == current)
|
||||
flush_vsx_to_thread(tsk);
|
||||
|
||||
reg = (elf_vrreg_t *)vrregs;
|
||||
|
||||
for (i = 0; i < 32 ; i++)
|
||||
buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET];
|
||||
memcpy(reg, buf, sizeof(buf));
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif /* CONFIG_VSX */
|
||||
|
||||
int dump_task_vector(struct task_struct *tsk, elf_vrregset_t *vrregs)
|
||||
{
|
||||
int rc = 0;
|
||||
elf_vrreg_t *regs = (elf_vrreg_t *)vrregs;
|
||||
#ifdef CONFIG_ALTIVEC
|
||||
rc = dump_task_altivec(tsk, regs);
|
||||
if (rc)
|
||||
return rc;
|
||||
regs += ELF_NVRREG;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_VSX
|
||||
rc = dump_task_vsx(tsk, regs);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPE
|
||||
|
||||
void enable_kernel_spe(void)
|
||||
@ -233,6 +320,10 @@ void discard_lazy_cpu_state(void)
|
||||
if (last_task_used_altivec == current)
|
||||
last_task_used_altivec = NULL;
|
||||
#endif /* CONFIG_ALTIVEC */
|
||||
#ifdef CONFIG_VSX
|
||||
if (last_task_used_vsx == current)
|
||||
last_task_used_vsx = NULL;
|
||||
#endif /* CONFIG_VSX */
|
||||
#ifdef CONFIG_SPE
|
||||
if (last_task_used_spe == current)
|
||||
last_task_used_spe = NULL;
|
||||
@ -297,6 +388,10 @@ struct task_struct *__switch_to(struct task_struct *prev,
|
||||
if (prev->thread.regs && (prev->thread.regs->msr & MSR_VEC))
|
||||
giveup_altivec(prev);
|
||||
#endif /* CONFIG_ALTIVEC */
|
||||
#ifdef CONFIG_VSX
|
||||
if (prev->thread.regs && (prev->thread.regs->msr & MSR_VSX))
|
||||
giveup_vsx(prev);
|
||||
#endif /* CONFIG_VSX */
|
||||
#ifdef CONFIG_SPE
|
||||
/*
|
||||
* If the previous thread used spe in the last quantum
|
||||
@ -317,6 +412,10 @@ struct task_struct *__switch_to(struct task_struct *prev,
|
||||
if (new->thread.regs && last_task_used_altivec == new)
|
||||
new->thread.regs->msr |= MSR_VEC;
|
||||
#endif /* CONFIG_ALTIVEC */
|
||||
#ifdef CONFIG_VSX
|
||||
if (new->thread.regs && last_task_used_vsx == new)
|
||||
new->thread.regs->msr |= MSR_VSX;
|
||||
#endif /* CONFIG_VSX */
|
||||
#ifdef CONFIG_SPE
|
||||
/* Avoid the trap. On smp this this never happens since
|
||||
* we don't set last_task_used_spe
|
||||
@ -417,6 +516,8 @@ static struct regbit {
|
||||
{MSR_EE, "EE"},
|
||||
{MSR_PR, "PR"},
|
||||
{MSR_FP, "FP"},
|
||||
{MSR_VEC, "VEC"},
|
||||
{MSR_VSX, "VSX"},
|
||||
{MSR_ME, "ME"},
|
||||
{MSR_IR, "IR"},
|
||||
{MSR_DR, "DR"},
|
||||
@ -534,6 +635,7 @@ void prepare_to_copy(struct task_struct *tsk)
|
||||
{
|
||||
flush_fp_to_thread(current);
|
||||
flush_altivec_to_thread(current);
|
||||
flush_vsx_to_thread(current);
|
||||
flush_spe_to_thread(current);
|
||||
}
|
||||
|
||||
@ -689,6 +791,9 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp)
|
||||
#endif
|
||||
|
||||
discard_lazy_cpu_state();
|
||||
#ifdef CONFIG_VSX
|
||||
current->thread.used_vsr = 0;
|
||||
#endif
|
||||
memset(current->thread.fpr, 0, sizeof(current->thread.fpr));
|
||||
current->thread.fpscr.val = 0;
|
||||
#ifdef CONFIG_ALTIVEC
|
||||
|
@ -350,6 +350,51 @@ static int vr_set(struct task_struct *target, const struct user_regset *regset,
|
||||
}
|
||||
#endif /* CONFIG_ALTIVEC */
|
||||
|
||||
#ifdef CONFIG_VSX
|
||||
/*
|
||||
* Currently to set and and get all the vsx state, you need to call
|
||||
* the fp and VMX calls aswell. This only get/sets the lower 32
|
||||
* 128bit VSX registers.
|
||||
*/
|
||||
|
||||
static int vsr_active(struct task_struct *target,
|
||||
const struct user_regset *regset)
|
||||
{
|
||||
flush_vsx_to_thread(target);
|
||||
return target->thread.used_vsr ? regset->n : 0;
|
||||
}
|
||||
|
||||
static int vsr_get(struct task_struct *target, const struct user_regset *regset,
|
||||
unsigned int pos, unsigned int count,
|
||||
void *kbuf, void __user *ubuf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
flush_vsx_to_thread(target);
|
||||
|
||||
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
|
||||
target->thread.fpr, 0,
|
||||
32 * sizeof(vector128));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vsr_set(struct task_struct *target, const struct user_regset *regset,
|
||||
unsigned int pos, unsigned int count,
|
||||
const void *kbuf, const void __user *ubuf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
flush_vsx_to_thread(target);
|
||||
|
||||
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
||||
target->thread.fpr, 0,
|
||||
32 * sizeof(vector128));
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_VSX */
|
||||
|
||||
#ifdef CONFIG_SPE
|
||||
|
||||
/*
|
||||
@ -426,6 +471,9 @@ enum powerpc_regset {
|
||||
#ifdef CONFIG_ALTIVEC
|
||||
REGSET_VMX,
|
||||
#endif
|
||||
#ifdef CONFIG_VSX
|
||||
REGSET_VSX,
|
||||
#endif
|
||||
#ifdef CONFIG_SPE
|
||||
REGSET_SPE,
|
||||
#endif
|
||||
@ -449,6 +497,13 @@ static const struct user_regset native_regsets[] = {
|
||||
.active = vr_active, .get = vr_get, .set = vr_set
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_VSX
|
||||
[REGSET_VSX] = {
|
||||
.n = 32,
|
||||
.size = sizeof(vector128), .align = sizeof(vector128),
|
||||
.active = vsr_active, .get = vsr_get, .set = vsr_set
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_SPE
|
||||
[REGSET_SPE] = {
|
||||
.n = 35,
|
||||
@ -849,6 +904,21 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
|
||||
sizeof(u32)),
|
||||
(const void __user *) data);
|
||||
#endif
|
||||
#ifdef CONFIG_VSX
|
||||
case PTRACE_GETVSRREGS:
|
||||
return copy_regset_to_user(child, &user_ppc_native_view,
|
||||
REGSET_VSX,
|
||||
0, (32 * sizeof(vector128) +
|
||||
sizeof(u32)),
|
||||
(void __user *) data);
|
||||
|
||||
case PTRACE_SETVSRREGS:
|
||||
return copy_regset_from_user(child, &user_ppc_native_view,
|
||||
REGSET_VSX,
|
||||
0, (32 * sizeof(vector128) +
|
||||
sizeof(u32)),
|
||||
(const void __user *) data);
|
||||
#endif
|
||||
#ifdef CONFIG_SPE
|
||||
case PTRACE_GETEVRREGS:
|
||||
/* Get the child spe register state. */
|
||||
|
@ -378,6 +378,21 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
|
||||
memcpy(&buf[i], ¤t->thread.fpscr, sizeof(double));
|
||||
if (__copy_to_user(&frame->mc_fregs, buf, ELF_NFPREG * sizeof(double)))
|
||||
return 1;
|
||||
/*
|
||||
* Copy VSR 0-31 upper half from thread_struct to local
|
||||
* buffer, then write that to userspace. Also set MSR_VSX in
|
||||
* the saved MSR value to indicate that frame->mc_vregs
|
||||
* contains valid data
|
||||
*/
|
||||
if (current->thread.used_vsr) {
|
||||
flush_vsx_to_thread(current);
|
||||
for (i = 0; i < 32 ; i++)
|
||||
buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET];
|
||||
if (__copy_to_user(&frame->mc_vsregs, buf,
|
||||
ELF_NVSRHALFREG * sizeof(double)))
|
||||
return 1;
|
||||
msr |= MSR_VSX;
|
||||
}
|
||||
#else
|
||||
/* save floating-point registers */
|
||||
if (__copy_to_user(&frame->mc_fregs, current->thread.fpr,
|
||||
@ -482,6 +497,24 @@ static long restore_user_regs(struct pt_regs *regs,
|
||||
for (i = 0; i < 32 ; i++)
|
||||
current->thread.TS_FPR(i) = buf[i];
|
||||
memcpy(¤t->thread.fpscr, &buf[i], sizeof(double));
|
||||
/*
|
||||
* Force the process to reload the VSX registers from
|
||||
* current->thread when it next does VSX instruction.
|
||||
*/
|
||||
regs->msr &= ~MSR_VSX;
|
||||
if (msr & MSR_VSX) {
|
||||
/*
|
||||
* Restore altivec registers from the stack to a local
|
||||
* buffer, then write this out to the thread_struct
|
||||
*/
|
||||
if (__copy_from_user(buf, &sr->mc_vsregs,
|
||||
sizeof(sr->mc_vsregs)))
|
||||
return 1;
|
||||
for (i = 0; i < 32 ; i++)
|
||||
current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i];
|
||||
} else if (current->thread.used_vsr)
|
||||
for (i = 0; i < 32 ; i++)
|
||||
current->thread.fpr[i][TS_VSRLOWOFFSET] = 0;
|
||||
#else
|
||||
if (__copy_from_user(current->thread.fpr, &sr->mc_fregs,
|
||||
sizeof(sr->mc_fregs)))
|
||||
|
@ -123,6 +123,22 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
|
||||
buf[i] = current->thread.TS_FPR(i);
|
||||
memcpy(&buf[i], ¤t->thread.fpscr, sizeof(double));
|
||||
err |= __copy_to_user(&sc->fp_regs, buf, FP_REGS_SIZE);
|
||||
/*
|
||||
* Copy VSX low doubleword to local buffer for formatting,
|
||||
* then out to userspace. Update v_regs to point after the
|
||||
* VMX data.
|
||||
*/
|
||||
if (current->thread.used_vsr) {
|
||||
flush_vsx_to_thread(current);
|
||||
v_regs += ELF_NVRREG;
|
||||
for (i = 0; i < 32 ; i++)
|
||||
buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET];
|
||||
err |= __copy_to_user(v_regs, buf, 32 * sizeof(double));
|
||||
/* set MSR_VSX in the MSR value in the frame to
|
||||
* indicate that sc->vs_reg) contains valid data.
|
||||
*/
|
||||
msr |= MSR_VSX;
|
||||
}
|
||||
#else /* CONFIG_VSX */
|
||||
/* copy fpr regs and fpscr */
|
||||
err |= __copy_to_user(&sc->fp_regs, ¤t->thread.fpr, FP_REGS_SIZE);
|
||||
@ -197,7 +213,7 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig,
|
||||
* This has to be done before copying stuff into current->thread.fpr/vr
|
||||
* for the reasons explained in the previous comment.
|
||||
*/
|
||||
regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1 | MSR_VEC);
|
||||
regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1 | MSR_VEC | MSR_VSX);
|
||||
|
||||
#ifdef CONFIG_ALTIVEC
|
||||
err |= __get_user(v_regs, &sc->v_regs);
|
||||
@ -226,6 +242,19 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig,
|
||||
current->thread.TS_FPR(i) = buf[i];
|
||||
memcpy(¤t->thread.fpscr, &buf[i], sizeof(double));
|
||||
|
||||
/*
|
||||
* Get additional VSX data. Update v_regs to point after the
|
||||
* VMX data. Copy VSX low doubleword from userspace to local
|
||||
* buffer for formatting, then into the taskstruct.
|
||||
*/
|
||||
v_regs += ELF_NVRREG;
|
||||
if ((msr & MSR_VSX) != 0)
|
||||
err |= __copy_from_user(buf, v_regs, 32 * sizeof(double));
|
||||
else
|
||||
memset(buf, 0, 32 * sizeof(double));
|
||||
|
||||
for (i = 0; i < 32 ; i++)
|
||||
current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i];
|
||||
#else
|
||||
err |= __copy_from_user(¤t->thread.fpr, &sc->fp_regs, FP_REGS_SIZE);
|
||||
#endif
|
||||
|
@ -967,6 +967,20 @@ void altivec_unavailable_exception(struct pt_regs *regs)
|
||||
die("Unrecoverable VMX/Altivec Unavailable Exception", regs, SIGABRT);
|
||||
}
|
||||
|
||||
void vsx_unavailable_exception(struct pt_regs *regs)
|
||||
{
|
||||
if (user_mode(regs)) {
|
||||
/* A user program has executed an vsx instruction,
|
||||
but this kernel doesn't support vsx. */
|
||||
_exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
|
||||
return;
|
||||
}
|
||||
|
||||
printk(KERN_EMERG "Unrecoverable VSX Unavailable Exception "
|
||||
"%lx at %lx\n", regs->trap, regs->nip);
|
||||
die("Unrecoverable VSX Unavailable Exception", regs, SIGABRT);
|
||||
}
|
||||
|
||||
void performance_monitor_exception(struct pt_regs *regs)
|
||||
{
|
||||
perf_irq(regs);
|
||||
@ -1099,6 +1113,21 @@ void altivec_assist_exception(struct pt_regs *regs)
|
||||
}
|
||||
#endif /* CONFIG_ALTIVEC */
|
||||
|
||||
#ifdef CONFIG_VSX
|
||||
void vsx_assist_exception(struct pt_regs *regs)
|
||||
{
|
||||
if (!user_mode(regs)) {
|
||||
printk(KERN_EMERG "VSX assist exception in kernel mode"
|
||||
" at %lx\n", regs->nip);
|
||||
die("Kernel VSX assist exception", regs, SIGILL);
|
||||
}
|
||||
|
||||
flush_vsx_to_thread(current);
|
||||
printk(KERN_INFO "VSX assist not supported at %lx\n", regs->nip);
|
||||
_exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
|
||||
}
|
||||
#endif /* CONFIG_VSX */
|
||||
|
||||
#ifdef CONFIG_FSL_BOOKE
|
||||
void CacheLockingException(struct pt_regs *regs, unsigned long address,
|
||||
unsigned long error_code)
|
||||
|
@ -109,6 +109,7 @@ typedef elf_gregset_t32 compat_elf_gregset_t;
|
||||
#ifdef __powerpc64__
|
||||
# define ELF_NVRREG32 33 /* includes vscr & vrsave stuffed together */
|
||||
# define ELF_NVRREG 34 /* includes vscr & vrsave in split vectors */
|
||||
# define ELF_NVSRHALFREG 32 /* Half the vsx registers */
|
||||
# define ELF_GREG_TYPE elf_greg_t64
|
||||
#else
|
||||
# define ELF_NEVRREG 34 /* includes acc (as 2) */
|
||||
@ -158,6 +159,7 @@ typedef __vector128 elf_vrreg_t;
|
||||
typedef elf_vrreg_t elf_vrregset_t[ELF_NVRREG];
|
||||
#ifdef __powerpc64__
|
||||
typedef elf_vrreg_t elf_vrregset_t32[ELF_NVRREG32];
|
||||
typedef elf_fpreg_t elf_vsrreghalf_t32[ELF_NVSRHALFREG];
|
||||
#endif
|
||||
|
||||
#ifdef __KERNEL__
|
||||
@ -219,8 +221,8 @@ extern int dump_task_fpu(struct task_struct *, elf_fpregset_t *);
|
||||
typedef elf_vrregset_t elf_fpxregset_t;
|
||||
|
||||
#ifdef CONFIG_ALTIVEC
|
||||
extern int dump_task_altivec(struct task_struct *, elf_vrregset_t *vrregs);
|
||||
#define ELF_CORE_COPY_XFPREGS(tsk, regs) dump_task_altivec(tsk, regs)
|
||||
extern int dump_task_vector(struct task_struct *, elf_vrregset_t *vrregs);
|
||||
#define ELF_CORE_COPY_XFPREGS(tsk, regs) dump_task_vector(tsk, regs)
|
||||
#define ELF_CORE_XFPREG_TYPE NT_PPC_VMX
|
||||
#endif
|
||||
|
||||
|
@ -224,6 +224,14 @@ extern void user_disable_single_step(struct task_struct *);
|
||||
#define PT_VRSAVE_32 (PT_VR0 + 33*4)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Only store first 32 VSRs here. The second 32 VSRs in VR0-31
|
||||
*/
|
||||
#define PT_VSR0 150 /* each VSR reg occupies 2 slots in 64-bit */
|
||||
#define PT_VSR31 (PT_VSR0 + 2*31)
|
||||
#ifdef __KERNEL__
|
||||
#define PT_VSR0_32 300 /* each VSR reg occupies 4 slots in 32-bit */
|
||||
#endif
|
||||
#endif /* __powerpc64__ */
|
||||
|
||||
/*
|
||||
@ -246,6 +254,10 @@ extern void user_disable_single_step(struct task_struct *);
|
||||
#define PTRACE_GETEVRREGS 20
|
||||
#define PTRACE_SETEVRREGS 21
|
||||
|
||||
/* Get the first 32 128bit VSX registers */
|
||||
#define PTRACE_GETVSRREGS 27
|
||||
#define PTRACE_SETVSRREGS 28
|
||||
|
||||
/*
|
||||
* Get or set a debug register. The first 16 are DABR registers and the
|
||||
* second 16 are IABR registers.
|
||||
|
@ -30,6 +30,7 @@
|
||||
#define MSR_ISF_LG 61 /* Interrupt 64b mode valid on 630 */
|
||||
#define MSR_HV_LG 60 /* Hypervisor state */
|
||||
#define MSR_VEC_LG 25 /* Enable AltiVec */
|
||||
#define MSR_VSX_LG 23 /* Enable VSX */
|
||||
#define MSR_POW_LG 18 /* Enable Power Management */
|
||||
#define MSR_WE_LG 18 /* Wait State Enable */
|
||||
#define MSR_TGPR_LG 17 /* TLB Update registers in use */
|
||||
@ -71,6 +72,7 @@
|
||||
#endif
|
||||
|
||||
#define MSR_VEC __MASK(MSR_VEC_LG) /* Enable AltiVec */
|
||||
#define MSR_VSX __MASK(MSR_VSX_LG) /* Enable VSX */
|
||||
#define MSR_POW __MASK(MSR_POW_LG) /* Enable Power Management */
|
||||
#define MSR_WE __MASK(MSR_WE_LG) /* Wait State Enable */
|
||||
#define MSR_TGPR __MASK(MSR_TGPR_LG) /* TLB Update registers in use */
|
||||
|
@ -43,9 +43,44 @@ struct sigcontext {
|
||||
* it must be copied via a vector register to/from storage) or as a word.
|
||||
* The entry with index 33 contains the vrsave as the first word (offset 0)
|
||||
* within the quadword.
|
||||
*
|
||||
* Part of the VSX data is stored here also by extending vmx_restore
|
||||
* by an additional 32 double words. Architecturally the layout of
|
||||
* the VSR registers and how they overlap on top of the legacy FPR and
|
||||
* VR registers is shown below:
|
||||
*
|
||||
* VSR doubleword 0 VSR doubleword 1
|
||||
* ----------------------------------------------------------------
|
||||
* VSR[0] | FPR[0] | |
|
||||
* ----------------------------------------------------------------
|
||||
* VSR[1] | FPR[1] | |
|
||||
* ----------------------------------------------------------------
|
||||
* | ... | |
|
||||
* | ... | |
|
||||
* ----------------------------------------------------------------
|
||||
* VSR[30] | FPR[30] | |
|
||||
* ----------------------------------------------------------------
|
||||
* VSR[31] | FPR[31] | |
|
||||
* ----------------------------------------------------------------
|
||||
* VSR[32] | VR[0] |
|
||||
* ----------------------------------------------------------------
|
||||
* VSR[33] | VR[1] |
|
||||
* ----------------------------------------------------------------
|
||||
* | ... |
|
||||
* | ... |
|
||||
* ----------------------------------------------------------------
|
||||
* VSR[62] | VR[30] |
|
||||
* ----------------------------------------------------------------
|
||||
* VSR[63] | VR[31] |
|
||||
* ----------------------------------------------------------------
|
||||
*
|
||||
* FPR/VSR 0-31 doubleword 0 is stored in fp_regs, and VMX/VSR 32-63
|
||||
* is stored at the start of vmx_reserve. vmx_reserve is extended for
|
||||
* backwards compatility to store VSR 0-31 doubleword 1 after the VMX
|
||||
* registers and vscr/vrsave.
|
||||
*/
|
||||
elf_vrreg_t __user *v_regs;
|
||||
long vmx_reserve[ELF_NVRREG+ELF_NVRREG+1];
|
||||
long vmx_reserve[ELF_NVRREG+ELF_NVRREG+32+1];
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -139,6 +139,7 @@ extern void enable_kernel_altivec(void);
|
||||
extern void giveup_altivec(struct task_struct *);
|
||||
extern void load_up_altivec(struct task_struct *);
|
||||
extern int emulate_altivec(struct pt_regs *);
|
||||
extern void giveup_vsx(struct task_struct *);
|
||||
extern void enable_kernel_spe(void);
|
||||
extern void giveup_spe(struct task_struct *);
|
||||
extern void load_up_spe(struct task_struct *);
|
||||
@ -162,6 +163,14 @@ static inline void flush_altivec_to_thread(struct task_struct *t)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_VSX
|
||||
extern void flush_vsx_to_thread(struct task_struct *);
|
||||
#else
|
||||
static inline void flush_vsx_to_thread(struct task_struct *t)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SPE
|
||||
extern void flush_spe_to_thread(struct task_struct *);
|
||||
#else
|
||||
|
Loading…
Reference in New Issue
Block a user