Skip to content

Commit

Permalink
powerpc: Emulate FP/vector/VSX loads/stores correctly when regs not live
Browse files Browse the repository at this point in the history
At present, the analyse_instr/emulate_step code checks for the
relevant MSR_FP/VEC/VSX bit being set when a FP/VMX/VSX load
or store is decoded, but doesn't recheck the bit before reading or
writing the relevant FP/VMX/VSX register in emulate_step().

Since we don't have preemption disabled, it is possible that we get
preempted between checking the MSR bit and doing the register access.
If that happened, then the registers would have been saved to the
thread_struct for the current process.  Accesses to the CPU registers
would then potentially read stale values, or write values that would
never be seen by the user process.

Another way that the registers can become non-live is if a page
fault occurs when accessing user memory, and the page fault code
calls a copy routine that wants to use the VMX or VSX registers.

To fix this, the code for all the FP/VMX/VSX loads gets restructured
so that it forms an image in a local variable of the desired register
contents, then disables preemption, checks the MSR bit and either
sets the CPU register or writes the value to the thread struct.
Similarly, the code for stores checks the MSR bit, copies either the
CPU register or the thread struct to a local variable, then reenables
preemption and then copies the register image to memory.

If the instruction being emulated is in the kernel, then we must not
use the register values in the thread_struct.  In this case, if the
relevant MSR enable bit is not set, then emulate_step refuses to
emulate the instruction.

Signed-off-by: Paul Mackerras <[email protected]>
Signed-off-by: Michael Ellerman <[email protected]>
  • Loading branch information
paulusmack authored and mpe committed Sep 1, 2017
1 parent e0a0986 commit c22435a
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 267 deletions.
1 change: 1 addition & 0 deletions arch/powerpc/include/asm/sstep.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ union vsx_reg {
unsigned long d[2];
float fp[4];
double dp[2];
__vector128 v;
};

/*
Expand Down
241 changes: 43 additions & 198 deletions arch/powerpc/lib/ldstfp.S
Original file line number Diff line number Diff line change
Expand Up @@ -21,256 +21,102 @@

#define STKFRM (PPC_MIN_STKFRM + 16)

.macro inst32 op
reg = 0
.rept 32
20: \op reg,0,r4
b 3f
EX_TABLE(20b,99f)
reg = reg + 1
.endr
.endm

/* Get the contents of frN into fr0; N is in r3. */
/* Get the contents of frN into *p; N is in r3 and p is in r4. */
_GLOBAL(get_fpr)
mflr r0
mfmsr r6
ori r7, r6, MSR_FP
MTMSRD(r7)
isync
rlwinm r3,r3,3,0xf8
bcl 20,31,1f
blr /* fr0 is already in fr0 */
nop
reg = 1
.rept 31
fmr fr0,reg
blr
reg = 0
.rept 32
stfd reg, 0(r4)
b 2f
reg = reg + 1
.endr
1: mflr r5
add r5,r3,r5
mtctr r5
mtlr r0
bctr
2: MTMSRD(r6)
isync
blr

/* Put the contents of fr0 into frN; N is in r3. */
/* Put the contents of *p into frN; N is in r3 and p is in r4. */
_GLOBAL(put_fpr)
mflr r0
mfmsr r6
ori r7, r6, MSR_FP
MTMSRD(r7)
isync
rlwinm r3,r3,3,0xf8
bcl 20,31,1f
blr /* fr0 is already in fr0 */
nop
reg = 1
.rept 31
fmr reg,fr0
blr
reg = 0
.rept 32
lfd reg, 0(r4)
b 2f
reg = reg + 1
.endr
1: mflr r5
add r5,r3,r5
mtctr r5
mtlr r0
bctr

/* Load FP reg N from float at *p. N is in r3, p in r4. */
_GLOBAL(do_lfs)
PPC_STLU r1,-STKFRM(r1)
mflr r0
PPC_STL r0,STKFRM+PPC_LR_STKOFF(r1)
mfmsr r6
ori r7,r6,MSR_FP
cmpwi cr7,r3,0
MTMSRD(r7)
isync
beq cr7,1f
stfd fr0,STKFRM-16(r1)
1: li r9,-EFAULT
2: lfs fr0,0(r4)
li r9,0
3: bl put_fpr
beq cr7,4f
lfd fr0,STKFRM-16(r1)
4: PPC_LL r0,STKFRM+PPC_LR_STKOFF(r1)
mtlr r0
MTMSRD(r6)
isync
mr r3,r9
addi r1,r1,STKFRM
blr
EX_TABLE(2b,3b)

/* Load FP reg N from double at *p. N is in r3, p in r4. */
_GLOBAL(do_lfd)
PPC_STLU r1,-STKFRM(r1)
mflr r0
PPC_STL r0,STKFRM+PPC_LR_STKOFF(r1)
mfmsr r6
ori r7,r6,MSR_FP
cmpwi cr7,r3,0
MTMSRD(r7)
isync
beq cr7,1f
stfd fr0,STKFRM-16(r1)
1: li r9,-EFAULT
2: lfd fr0,0(r4)
li r9,0
3: beq cr7,4f
bl put_fpr
lfd fr0,STKFRM-16(r1)
4: PPC_LL r0,STKFRM+PPC_LR_STKOFF(r1)
mtlr r0
MTMSRD(r6)
isync
mr r3,r9
addi r1,r1,STKFRM
blr
EX_TABLE(2b,3b)

/* Store FP reg N to float at *p. N is in r3, p in r4. */
_GLOBAL(do_stfs)
PPC_STLU r1,-STKFRM(r1)
mflr r0
PPC_STL r0,STKFRM+PPC_LR_STKOFF(r1)
mfmsr r6
ori r7,r6,MSR_FP
cmpwi cr7,r3,0
MTMSRD(r7)
isync
beq cr7,1f
stfd fr0,STKFRM-16(r1)
bl get_fpr
1: li r9,-EFAULT
2: stfs fr0,0(r4)
li r9,0
3: beq cr7,4f
lfd fr0,STKFRM-16(r1)
4: PPC_LL r0,STKFRM+PPC_LR_STKOFF(r1)
mtlr r0
MTMSRD(r6)
2: MTMSRD(r6)
isync
mr r3,r9
addi r1,r1,STKFRM
blr
EX_TABLE(2b,3b)

/* Store FP reg N to double at *p. N is in r3, p in r4. */
_GLOBAL(do_stfd)
PPC_STLU r1,-STKFRM(r1)
#ifdef CONFIG_ALTIVEC
/* Get the contents of vrN into *p; N is in r3 and p is in r4. */
_GLOBAL(get_vr)
mflr r0
PPC_STL r0,STKFRM+PPC_LR_STKOFF(r1)
mfmsr r6
ori r7,r6,MSR_FP
cmpwi cr7,r3,0
oris r7, r6, MSR_VEC@h
MTMSRD(r7)
isync
beq cr7,1f
stfd fr0,STKFRM-16(r1)
bl get_fpr
1: li r9,-EFAULT
2: stfd fr0,0(r4)
li r9,0
3: beq cr7,4f
lfd fr0,STKFRM-16(r1)
4: PPC_LL r0,STKFRM+PPC_LR_STKOFF(r1)
mtlr r0
MTMSRD(r6)
isync
mr r3,r9
addi r1,r1,STKFRM
blr
EX_TABLE(2b,3b)

#ifdef CONFIG_ALTIVEC
/* Get the contents of vrN into v0; N is in r3. Doesn't touch r3 or r4. */
_GLOBAL(get_vr)
mflr r0
rlwinm r6,r3,3,0xf8
bcl 20,31,1f
blr /* v0 is already in v0 */
nop
reg = 1
.rept 31
vor v0,reg,reg /* assembler doesn't know vmr? */
blr
reg = 0
.rept 32
stvx reg, 0, r4
b 2f
reg = reg + 1
.endr
1: mflr r5
add r5,r6,r5
mtctr r5
mtlr r0
bctr
2: MTMSRD(r6)
isync
blr

/* Put the contents of v0 into vrN; N is in r3. Doesn't touch r3 or r4. */
/* Put the contents of *p into vrN; N is in r3 and p is in r4. */
_GLOBAL(put_vr)
mflr r0
mfmsr r6
oris r7, r6, MSR_VEC@h
MTMSRD(r7)
isync
rlwinm r6,r3,3,0xf8
bcl 20,31,1f
blr /* v0 is already in v0 */
nop
reg = 1
.rept 31
vor reg,v0,v0
blr
reg = 0
.rept 32
lvx reg, 0, r4
b 2f
reg = reg + 1
.endr
1: mflr r5
add r5,r6,r5
mtctr r5
mtlr r0
bctr

/* Load vector reg N from *p. N is in r3, p in r4. */
_GLOBAL(do_lvx)
PPC_STLU r1,-STKFRM(r1)
mflr r0
PPC_STL r0,STKFRM+PPC_LR_STKOFF(r1)
mfmsr r6
oris r7,r6,MSR_VEC@h
cmpwi cr7,r3,0
li r8,STKFRM-16
MTMSRD(r7)
isync
beq cr7,1f
stvx v0,r1,r8
1: li r9,-EFAULT
2: lvx v0,0,r4
li r9,0
3: beq cr7,4f
bl put_vr
lvx v0,r1,r8
4: PPC_LL r0,STKFRM+PPC_LR_STKOFF(r1)
mtlr r0
MTMSRD(r6)
isync
mr r3,r9
addi r1,r1,STKFRM
blr
EX_TABLE(2b,3b)

/* Store vector reg N to *p. N is in r3, p in r4. */
_GLOBAL(do_stvx)
PPC_STLU r1,-STKFRM(r1)
mflr r0
PPC_STL r0,STKFRM+PPC_LR_STKOFF(r1)
mfmsr r6
oris r7,r6,MSR_VEC@h
cmpwi cr7,r3,0
li r8,STKFRM-16
MTMSRD(r7)
isync
beq cr7,1f
stvx v0,r1,r8
bl get_vr
1: li r9,-EFAULT
2: stvx v0,0,r4
li r9,0
3: beq cr7,4f
lvx v0,r1,r8
4: PPC_LL r0,STKFRM+PPC_LR_STKOFF(r1)
mtlr r0
MTMSRD(r6)
2: MTMSRD(r6)
isync
mr r3,r9
addi r1,r1,STKFRM
blr
EX_TABLE(2b,3b)
#endif /* CONFIG_ALTIVEC */

#ifdef CONFIG_VSX
Expand Down Expand Up @@ -363,7 +209,6 @@ _GLOBAL(store_vsrn)
mr r3,r9
addi r1,r1,STKFRM
blr
EX_TABLE(2b,3b)
#endif /* CONFIG_VSX */

/* Convert single-precision to double, without disturbing FPRs. */
Expand Down
Loading

0 comments on commit c22435a

Please sign in to comment.