+ sub r0,r23,r20 ; Subtract one from the other
+ sub r21,r20,r23 ; Subtract the other from the one
+ or r21,r21,r0 ; Combine them
+ srawi r21,r21,31 ; Get a 0 if equal or -1 of not
+ and r23,r23,r21 ; Make 0 if same, unchanged if not
+ stwcx. r23,r24,r22 ; Try to invalidate it
+ bne-- vmxinvothr ; Try again if there was a collision...
+
+ isync
+
+;
+; Now if there is a savearea associated with the popped context, release it.
+; Either way, pop the level to the top stacked context.
+;
+
+ lwz r22,VMXsave(r20) ; Get pointer to the first savearea
+ li r21,0 ; Assume we popped all the way out
+ mr. r22,r22 ; Is there anything there?
+ beq++ vmxsetlvl ; No, see if we need to enable...
+
+ lwz r21,SAVlevel(r22) ; Get the level of that savearea
+ cmplw r21,r27 ; Is this the saved copy of the live stuff?
+ bne vmxsetlvl ; No, leave as is...
+
+ lwz r24,SAVprev+4(r22) ; Pick up the previous area
+ li r21,0 ; Assume we popped all the way out
+ mr. r24,r24 ; Any more context?
+ beq-- vmxonlyone ; Nope...
+ lwz r21,SAVlevel(r24) ; Get the level associated with save
+
+vmxonlyone: stw r24,VMXsave(r20) ; Dequeue this savearea
+
+ rlwinm r3,r22,0,0,19 ; Find main savearea header
+
+ lwz r8,quickfret(r31) ; Get the first in quickfret list (top)
+ lwz r9,quickfret+4(r31) ; Get the first in quickfret list (bottom)
+ lwz r2,SACvrswap(r3) ; Get the virtual to real conversion (top)
+ lwz r3,SACvrswap+4(r3) ; Get the virtual to real conversion (bottom)
+ stw r8,SAVprev(r22) ; Link the old in (top)
+ stw r9,SAVprev+4(r22) ; Link the old in (bottom)
+ xor r3,r22,r3 ; Convert to physical
+ stw r2,quickfret(r31) ; Set the first in quickfret list (top)
+ stw r3,quickfret+4(r31) ; Set the first in quickfret list (bottom)
+
+#if FPVECDBG
+ lis r0,HIGH_ADDR(CutTrace) ; (TEST/DEBUG)
+ li r2,0x3401 ; (TEST/DEBUG)
+ oris r0,r0,LOW_ADDR(CutTrace) ; (TEST/DEBUG)
+ sc ; (TEST/DEBUG)
+#endif
+
+vmxsetlvl: stw r21,VMXlevel(r20) ; Save the level
+
+;
+; Here we check if we are at the right level
+;
+
+vmxchkena: lwz r21,VMXowner(r31) ; Get the ID of the live context
+ lwz r23,VMXlevel(r26) ; Get the level ID
+ cmplw r26,r21 ; Do we have the live context?
+ lwz r24,VMXcpu(r26) ; Get the CPU that the context was last dispatched on
+ bne-- setena ; No, can not possibly enable...
+ cmplw r30,r23 ; Are we about to launch the live level?
+ cmplw cr1,r19,r24 ; Was facility used on this processor last?
+ bne-- setena ; No, not live...
+ bne-- cr1,setena ; No, wrong cpu, have to enable later....
+
+ lwz r24,VMXsave(r26) ; Get the first savearea
+ mr. r24,r24 ; Any savearea?
+ beq++ vmxena ; Nope...
+ lwz r25,SAVlevel(r24) ; Get the level of savearea
+ lwz r0,SAVprev+4(r24) ; Get the previous
+ cmplw r30,r25 ; Is savearea for the level we are launching?
+ bne++ vmxena ; No, just go enable...
+
+ stw r0,VMXsave(r26) ; Pop the chain
+
+ rlwinm r3,r24,0,0,19 ; Find main savearea header
+
+ lwz r8,quickfret(r31) ; Get the first in quickfret list (top)
+ lwz r9,quickfret+4(r31) ; Get the first in quickfret list (bottom)
+ lwz r2,SACvrswap(r3) ; Get the virtual to real conversion (top)
+ lwz r3,SACvrswap+4(r3) ; Get the virtual to real conversion (bottom)
+ stw r8,SAVprev(r24) ; Link the old in (top)
+ stw r9,SAVprev+4(r24) ; Link the old in (bottom)
+ xor r3,r24,r3 ; Convert to physical
+ stw r2,quickfret(r31) ; Set the first in quickfret list (top)
+ stw r3,quickfret+4(r31) ; Set the first in quickfret list (bottom)
+
+#if FPVECDBG
+ lis r0,HIGH_ADDR(CutTrace) ; (TEST/DEBUG)
+ li r2,0x3402 ; (TEST/DEBUG)
+ oris r0,r0,LOW_ADDR(CutTrace) ; (TEST/DEBUG)
+ sc ; (TEST/DEBUG)
+#endif
+
+vmxena: oris r29,r29,hi16(MASK(MSR_VEC)) ; Enable facility
+
+setena: lwz r18,umwSpace(r28) ; Get the space ID in case we are launching user
+ rlwinm. r0,r29,0,MSR_PR_BIT,MSR_PR_BIT ; Are we about to launch user state?
+ li r0,0 ; Get set to release quickfret holdoff
+ crmove cr7_eq,cr0_eq ; Remember if we are going to user state
+ rlwimi. r20,r29,(((31-floatCngbit)+(MSR_FP_BIT+1))&31),floatCngbit,floatCngbit ; Set flag if we enabled floats
+ lwz r19,deferctx(r28) ; Get any deferred facility context switch
+ rlwinm r20,r29,(((31-vectorCngbit)+(MSR_VEC_BIT+1))&31),vectorCngbit,vectorCngbit ; Set flag if we enabled vector
+ stw r29,savesrr1+4(r27) ; Turn facility on or off
+ stw r0,holdQFret(r31) ; Release quickfret
+ oris r18,r18,hi16(umwSwitchAway) ; Set the switch-away bit in case we go to user
+
+ beq setenaa ; Neither float nor vector turned on....
+
+ lwz r5,ACT_MACT_SPF(r28) ; Get activation copy
+ lwz r6,spcFlags(r31) ; Get per_proc copy
+ or r5,r5,r20 ; Set vector/float changed bits in activation
+ or r6,r6,r20 ; Set vector/float changed bits in per_proc
+ stw r5,ACT_MACT_SPF(r28) ; Set activation copy
+ stw r6,spcFlags(r31) ; Set per_proc copy
+
+setenaa: mfdec r24 ; Get decrementer
+ bf+ cr2_eq,nodefer ; No deferred to switch to...
+
+ li r20,0 ; Clear this
+ stw r26,curctx(r28) ; Make the facility context current
+ stw r20,deferctx(r28) ; Clear deferred context
+
+nodefer: lwz r22,qactTimer(r28) ; Get high order quick activation timer
+ mr. r24,r24 ; See if it has popped already...
+ lwz r23,qactTimer+4(r28) ; Get low order qact timer
+ ble- chkifuser ; We have popped or are just about to...
+
+segtb: mftbu r20 ; Get the upper time base
+ mftb r21 ; Get the low
+ mftbu r19 ; Get upper again
+ or. r0,r22,r23 ; Any time set?
+ cmplw cr1,r20,r19 ; Did they change?
+ beq++ chkifuser ; No time set....
+ bne-- cr1,segtb ; Timebase ticked, get them again...
+
+ subfc r6,r21,r23 ; Subtract current from qact time
+ li r0,0 ; Make a 0
+ subfe r5,r20,r22 ; Finish subtract
+ subfze r0,r0 ; Get a 0 if qact was bigger than current, -1 otherwise
+ andc. r12,r5,r0 ; Set 0 if qact has passed
+ andc r13,r6,r0 ; Set 0 if qact has passed
+ bne chkifuser ; If high order is non-zero, this is too big for a decrementer
+ cmplw r13,r24 ; Is this earlier than the decrementer? (logical compare takes care of high bit on)
+ bge++ chkifuser ; No, do not reset decrementer...
+
+ mtdec r13 ; Set our value
+
+chkifuser: addi r4,r28,SYSTEM_TIMER
+ mftb r3
+ beq-- cr7,chkifuser1 ; Skip this if we are going to kernel...
+ stw r18,umwSpace(r28) ; Half-invalidate to force MapUserAddressWindow to reload SRs
+ addi r4,r28,USER_TIMER
+
+chkifuser1: bl EXT(timer_event)
+
+chkenax: