When running openbios-ppc32 on a ppc64 VM, we need to unset SF on every interrupt to ensure that things still work.
So far we've been using mtmsr for this operation, but according to the spec mtmsr doesn't set any bit above 32bit, so we were merely exploiting a bug in qemu before.
This patch adds a runtime check on PVR to see if we're running on a 64-bit capable CPU. If so, we use mtmsrd, which can set the high 32bits of MSR.
CC: Andreas Färber andreas.faerber@web.de Signed-off-by: Alexander Graf agraf@suse.de --- arch/ppc/qemu/start.S | 50 +++++++++++++++++++++++++++++++++++++++++------- 1 files changed, 42 insertions(+), 8 deletions(-)
diff --git a/arch/ppc/qemu/start.S b/arch/ppc/qemu/start.S index 0d64568..70a4f92 100644 --- a/arch/ppc/qemu/start.S +++ b/arch/ppc/qemu/start.S @@ -37,9 +37,21 @@
#define EXCEPTION_PREAMBLE_TEMPLATE \ mtsprg1 r1 ; /* scratch */ \ + mfcr r1 ; \ + mtsprg2 r1 ; /* scratch */ \ + mtsprg3 r2 ; /* scratch */ \ + mfpvr r1 ; \ + lis r2, 0x330000@h ; /* lowest 64-bit PVR */ \ + cmplw r1, r2 ; \ + blt 1f; \ + oris r2, r2, 0x70000000@h ; /* highest 64-bit PVR */ \ + cmplw r1, r2 ; \ + bge 1f; \ + \ mfmsr r1 ; /* unset MSR_SF */ \ clrlwi r1,r1,0 ; \ - mtmsr r1 ; \ + mtmsrd r1 ; \ +1: \ mfsprg0 r1 ; /* exception stack in sprg0 */ \ .ifc ULONG_SIZE, 8 ; \ addi r1,r1,-(40 * ULONG_SIZE) ; /* push exception frame */ \ @@ -50,6 +62,7 @@ stl r0,(0 * ULONG_SIZE)(r1) ; /* save r0 */ \ mfsprg1 r0 ; \ stl r0,(1 * ULONG_SIZE)(r1) ; /* save r1 */ \ + mfsprg3 r2 ; \ stl r2,(2 * ULONG_SIZE)(r1) ; /* save r2 */ \ stl r3,(3 * ULONG_SIZE)(r1) ; /* save r3 */ \ stl r4,(4 * ULONG_SIZE)(r1) ; \ @@ -85,7 +98,7 @@ \ mflr r0 ; \ stl r0,(13 * ULONG_SIZE)(r1) ; \ - mfcr r0 ; \ + mfsprg2 r0 ; \ stl r0,(14 * ULONG_SIZE)(r1) ; \ mfctr r0 ; \ stl r0,(15 * ULONG_SIZE)(r1) ; \ @@ -301,14 +314,12 @@ VECTOR( 0x100, "SRE" ): ILLEGAL_VECTOR( 0x200 )
VECTOR( 0x300, "DSI" ): - EXCEPTION_PREAMBLE - b call_dsi_exception + b real_dsi
ILLEGAL_VECTOR( 0x380 )
VECTOR( 0x400, "ISI" ): - EXCEPTION_PREAMBLE - b call_isi_exception + b real_isi
ILLEGAL_VECTOR( 0x480 )
@@ -359,6 +370,14 @@ VECTOR( 0x2200, "ISI_64" ):
#endif
+real_dsi: + EXCEPTION_PREAMBLE + b call_dsi_exception + +real_isi: + EXCEPTION_PREAMBLE + b call_isi_exception + GLOBL(__vectors_end):
/************************************************************************/ @@ -368,10 +387,25 @@ GLOBL(__vectors_end): GLOBL(_entry):
#ifdef CONFIG_PPC_64BITSUPPORT - /* clear MSR, disable MMU */ - li r0,0 + + mfpvr r3 + lis r4, 0x330000@h /* lowest 64-bit PVR */ + cmplw r3, r4 + blt no_64bit + oris r4, r4, 0x70000000@h /* highest 64-bit PVR */ + cmplw r3, r4 + bge no_64bit + + /* clear MSR, disable MMU, SF */ + mtmsrd r0 + b real_entry + +no_64bit: + /* clear MSR, disable MMU */ mtmsr r0 + +real_entry: #endif
/* copy exception vectors */
This patch adds a runtime check on PVR to see if we're running on a 64-bit capable CPU. If so, we use mtmsrd, which can set the high 32bits of MSR.
That PVR check isn't quite correct.
mfmsr r1 ; /* unset MSR_SF */ \ clrlwi r1,r1,0 ; \
- mtmsr r1 ; \
- mtmsrd r1 ; \
clrlwi 1,1,0 is rlwinm 1,1,0,0,31 which clears all the top 32 bits, not only MSR[SF]. Importantly it clears MSR[HV], which you do not want. Use rldicl instead?
Instead of the PVR thing, you could check if MSR[SF] is set, and only then clear it. Is this same code used on 32-bit systems?
lis x,0x8000 ; add. x,x,x ; beq ohai_we_are_32bit
Segher
On 16.06.2011, at 21:43, Segher Boessenkool wrote:
This patch adds a runtime check on PVR to see if we're running on a 64-bit capable CPU. If so, we use mtmsrd, which can set the high 32bits of MSR.
That PVR check isn't quite correct.
mfmsr r1 ; /* unset MSR_SF */ \ clrlwi r1,r1,0 ; \
- mtmsr r1 ; \
- mtmsrd r1 ; \
clrlwi 1,1,0 is rlwinm 1,1,0,0,31 which clears all the top 32 bits, not only MSR[SF]. Importantly it clears MSR[HV], which you do not want. Use rldicl instead?
Now that we actually detect if we're on a 64-bit CPU, we can indeed be a bit more clever about it. However, I don't think any of the upper 32 bits are important to us, no?
Instead of the PVR thing, you could check if MSR[SF] is set, and only then clear it. Is this same code used on 32-bit systems?
lis x,0x8000 ; add. x,x,x ; beq ohai_we_are_32bit
lis x,0x8000 would result in 0xffffffff80000000 or 0x80000000 in x respectively. add. compares the full register with 0, so we also get to analyze the shifted out 8. Very nice trick indeed and exactly what I was searching for! Thanks a lot!
Alex
mfmsr r1 ; /* unset MSR_SF */ \ clrlwi r1,r1,0 ; \
- mtmsr r1 ; \
- mtmsrd r1 ; \
clrlwi 1,1,0 is rlwinm 1,1,0,0,31 which clears all the top 32 bits, not only MSR[SF]. Importantly it clears MSR[HV], which you do not want. Use rldicl instead?
Now that we actually detect if we're on a 64-bit CPU, we can indeed be a bit more clever about it. However, I don't think any of the upper 32 bits are important to us, no?
You set MSR[HV]=0. That will cause trouble: many mtspr's are then treated as a noop, for example. And if you have an RMO, things break. Etc. Trouble :-)
lis x,0x8000 ; add. x,x,x ; beq ohai_we_are_32bit
lis x,0x8000 would result in 0xffffffff80000000 or 0x80000000 in x respectively. add. compares the full register with 0, so we also get to analyze the shifted out 8. Very nice trick indeed and exactly what I was searching for! Thanks a lot!
add. sets CR0 based on the low 32 bits of the result, in 32-bit mode. 64-bit CPUs always load 0xffffffff80000000 for lis x,0x8000, no matter what mode the CPU is in.
Segher
On 17.06.2011, at 00:56, Segher Boessenkool wrote:
mfmsr r1 ; /* unset MSR_SF */ \ clrlwi r1,r1,0 ; \
- mtmsr r1 ; \
- mtmsrd r1 ; \
clrlwi 1,1,0 is rlwinm 1,1,0,0,31 which clears all the top 32 bits, not only MSR[SF]. Importantly it clears MSR[HV], which you do not want. Use rldicl instead?
Now that we actually detect if we're on a 64-bit CPU, we can indeed be a bit more clever about it. However, I don't think any of the upper 32 bits are important to us, no?
You set MSR[HV]=0. That will cause trouble: many mtspr's are then treated as a noop, for example. And if you have an RMO, things break. Etc. Trouble :-)
But we don't run on any of those, do we? You're probably right though - let's make this clean.
lis x,0x8000 ; add. x,x,x ; beq ohai_we_are_32bit
lis x,0x8000 would result in 0xffffffff80000000 or 0x80000000 in x respectively. add. compares the full register with 0, so we also get to analyze the shifted out 8. Very nice trick indeed and exactly what I was searching for! Thanks a lot!
add. sets CR0 based on the low 32 bits of the result, in 32-bit mode. 64-bit CPUs always load 0xffffffff80000000 for lis x,0x8000, no matter what mode the CPU is in.
Oh, sure, I meant that on 32-bit CPUs it won't set the f's since they don't exist :)
Alex
mfmsr r1 ; /* unset MSR_SF */ \ clrlwi r1,r1,0 ; \
- mtmsr r1 ; \
- mtmsrd r1 ; \
clrlwi 1,1,0 is rlwinm 1,1,0,0,31 which clears all the top 32 bits, not only MSR[SF]. Importantly it clears MSR[HV], which you do not want. Use rldicl instead?
Now that we actually detect if we're on a 64-bit CPU, we can indeed be a bit more clever about it. However, I don't think any of the upper 32 bits are important to us, no?
You set MSR[HV]=0. That will cause trouble: many mtspr's are then treated as a noop, for example. And if you have an RMO, things break. Etc. Trouble :-)
But we don't run on any of those, do we? You're probably right though
- let's make this clean.
You probably don't set real mode offset in the firmware, no -- but the SPR thing will kill you. In non-hypervisor mode you cannot write most of the fun SPRs.
lis x,0x8000 ; add. x,x,x ; beq ohai_we_are_32bit
lis x,0x8000 would result in 0xffffffff80000000 or 0x80000000 in x respectively. add. compares the full register with 0, so we also get to analyze the shifted out 8. Very nice trick indeed and exactly what I was searching for! Thanks a lot!
add. sets CR0 based on the low 32 bits of the result, in 32-bit mode. 64-bit CPUs always load 0xffffffff80000000 for lis x,0x8000, no matter what mode the CPU is in.
Oh, sure, I meant that on 32-bit CPUs it won't set the f's since they don't exist :)
Yeah. And the result in 32-bit mode on a 64-bit CPU is exactly the same, for all 32-bit Book I instructions :-)
I don't think there is any nice way to detect the CPU you're running on is 64-bit capable, when running in 32-bit mode. But you don't need that here.
Segher