/*
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#include <assym.s>
#include <debug.h>
#include <cpus.h>
#include <db_machine_commands.h>
#include <mach_rt.h>
#include <mach_debug.h>
#include <ppc/asm.h>
#include <ppc/proc_reg.h>
#include <ppc/exception.h>
#include <ppc/Performance.h>
#include <ppc/exception.h>
#include <ppc/pmap_internals.h>
#include <mach/ppc/vm_param.h>
.text
/*
* This routine will add a savearea block to the free list.
* Note really well: we can take NO exceptions of any kind,
* including a PTE miss once the savearea lock is held. That's
* a guaranteed deadlock. That means we must disable for interrutions
* and turn all translation off.
*
* Note that the savearea list should NEVER be empty
*/
ENTRY(save_queue,TAG_NO_FRAME_USED)
mfsprg r9,2 mtcrf 0x04,r9 lis r10,HIGH_ADDR(EXT(saveanchor)) ori r10,r10,LOW_ADDR(EXT(saveanchor)) bt pfNoMSRirb,sqNoMSR mtmsr r3 b sqNoMSRx
sqNoMSR: li r0,loadMSR sqNoMSRx:
#if 0
rlwinm. r3,r11,0,0,19 /* (TEST/DEBUG) */
bne+ notraceit /* (TEST/DEBUG) */
BREAKPOINT_TRAP /* (TEST/DEBUG) */
notraceit: /* (TEST/DEBUG) */
#else
rlwinm r3,r11,0,0,19 /* Make sure it's clean and tidy */
#endif
sqlck: lwarx r9,0,r10 /* Grab the lock value */
li r8,1 /* Use part of the delay time */
mr. r9,r9 /* Is it locked? */
bne- sqlcks /* Yeah, wait for it to clear... */
stwcx. r8,0,r10 /* Try to seize that there durn lock */
beq+ sqlckd /* Got it... */
b sqlck /* Collision, try again... */
sqlcks: lwz r9,SVlock(r10) /* Get that lock in here */
mr. r9,r9 /* Is it free yet? */
beq+ sqlck /* Yeah, try for it again... */
b sqlcks /* Sniff away... */
sqlckd: isync /* Make sure translation is off */
lwz r7,SVfree(r10) /* Get the free save area list anchor */
lwz r6,SVcount(r10) /* Get the total count of saveareas */
stw r3,SVfree(r10) /* Queue in the new one */
addi r6,r6,sac_cnt /* Count the ones we are linking in */
stw r7,SACnext(r3) /* Queue the old first one off of us */
li r8,0 /* Get a free lock value */
stw r6,SVcount(r10) /* Save the new count */
sync /* Make sure everything is done */
stw r8,SVlock(r10) /* Unlock the savearea chain */
mtmsr r12 /* Restore interrupts and translation */
isync /* Dump any speculations */
#if 0
lis r0,HIGH_ADDR(CutTrace) /* (TEST/DEBUG) */
li r2,0x2201 sc /* (TEST/DEBUG) */
#endif
blr /* Leave... */
/*
* This routine will find and remove an empty savearea block from the free list.
* Note really well: we can take NO exceptions of any kind,
* including a PTE miss once the savearea lock is held. That's
* a guaranteed deadlock. That means we must disable for interrutions
* and turn all translation off.
*
* We pass back the virtual address of the one we just released
* or a zero if none to free.
*
* Note that the savearea list should NEVER be empty
*/
ENTRY(save_dequeue,TAG_NO_FRAME_USED)
mfsprg r9,2 mtcrf 0x04,r9 andi. r3,r12,0x7FCF /* Turn off all translation and 'rupts */
ori r10,r10,LOW_ADDR(EXT(saveanchor)) /* Bottom half of the anchor */
bt pfNoMSRirb,sdNoMSR mtmsr r3 b sdNoMSRx
sdNoMSR: li r0,loadMSR sdNoMSRx:
sdqlck: lwarx r9,0,r10 /* Grab the lock value */
li r8,1 /* Use part of the delay time */
mr. r9,r9 /* Is it locked? */
bne- sdqlcks /* Yeah, wait for it to clear... */
stwcx. r8,0,r10 /* Try to seize that there durn lock */
beq+ sdqlckd /* Got it... */
b sdqlck /* Collision, try again... */
sdqlcks: lwz r9,SVlock(r10) /* Get that lock in here */
mr. r9,r9 /* Is it free yet? */
beq+ sdqlck /* Yeah, try for it again... */
b sdqlcks /* Sniff away... */
sdqlckd: isync la r5,SVfree(r10) /* Remember that the we're just starting out */
lwz r6,SVcount(r10) /* Get the total count of saveareas for later */
lis r8,sac_empty>>16 /* Get the empty block indication */
sdqchk: lwz r4,SACalloc(r3) /* Get the allocation flags */
lwz r9,SACflags(r3) /* Get the flags */
lwz r7,SACnext(r3) /* Point on to the next one */
andis. r9,r9,hi16(sac_perm) /* Is this permanently allocated? */
cmplw cr1,r4,r8 /* Does this look empty? */
bne- sdqperm /* It's permanent, can't release... */
beq- cr1,sdqfnd /* Yeah, empty... */
sdqperm: la r5,SACnext(r3) /* Remember the last guy */
mr. r3,r7 /* Any more left? */
bne+ sdqchk /* Yeah... */
b sdqunlk /* Nope, just go unlock and leave... */
sdqfnd: subi r6,r6,sac_cnt /* Back off the number of saveareas in here */
stw r7,0(r5) /* Dequeue our guy */
lwz r9,SACvrswap(r3) /* Get addressing conversion */
stw r6,SVcount(r10) /* Back off the count for this block */
xor r3,r3,r9 /* Flip to virtual addressing */
sdqunlk: li r8,0 /* Get a free lock value */
sync /* Make sure everything is done */
stw r8,SVlock(r10) /* Unlock the savearea chain */
mtmsr r12 /* Restore interrupts and translation */
isync /* Dump any speculations */
#if 0
lis r0,HIGH_ADDR(CutTrace) /* (TEST/DEBUG) */
li r2,0x2202 sc /* (TEST/DEBUG) */
#endif
blr /* Leave... */
/*
* This routine will obtain a savearea from the free list.
* Note really well: we can take NO exceptions of any kind,
* including a PTE miss once the savearea lock is held. That's
* a guaranteed deadlock. That means we must disable for interrutions
* and turn all translation off.
*
* We pass back the virtual address of the one we just obtained
* or a zero if none to allocate.
*
* Note that the savearea list should NEVER be empty
* NOTE!!! NEVER USE R0, R2, or R12 IN HERE THAT WAY WE DON'T NEED A
* STACK FRAME IN FPU_SAVE, FPU_SWITCH, VEC_SAVE, OR VEC_SWITCH.
*/
ENTRY(save_get_phys,TAG_NO_FRAME_USED)
cmplw cr1,r1,r1
ENTRY(save_get,TAG_NO_FRAME_USED)
cmplwi cr1,r1,0 csaveget: mfsprg r9,2 mtcrf 0x04,r9 andi. r3,r11,0x7FCF /* Turn off all translation and 'rupts */
ori r10,r10,LOW_ADDR(EXT(saveanchor)) /* Bottom half of the anchor */
bt pfNoMSRirb,sgNoMSR mtmsr r3 b sgNoMSRx
sgNoMSR: mr r9,r0 sc
sgNoMSRx:
sglck: lwarx r9,0,r10 /* Grab the lock value */
li r7,1 /* Use part of the delay time */
mr. r9,r9 /* Is it locked? */
bne- sglcks /* Yeah, wait for it to clear... */
stwcx. r7,0,r10 /* Try to seize that there durn lock */
beq+ sglckd /* Got it... */
b sglck /* Collision, try again... */
sglcks: lwz r9,SVlock(r10) /* Get that lock in here */
mr. r9,r9 /* Is it free yet? */
beq+ sglck /* Yeah, try for it again... */
b sglcks /* Sniff away... */
sglckd: isync /* Make sure translation is off */
lwz r8,SVfree(r10) /* Get the head of the save area list */
lwz r9,SVinuse(r10) /* Get the inuse field */
lwz r7,SACalloc(r8) /* Pick up the allocation bits */
lwz r5,SACvrswap(r8) /* Get real to virtual translation */
mr. r7,r7 /* Can we use the first one? */
blt use1st /* Yeah... */
andis. r7,r7,0x8000 /* Show we used the second and remember if it was the last */
addi r3,r8,0x0800 /* Point to the first one */
b gotsave /* We have the area now... */
use1st: andis. r7,r7,0x4000 /* Mark first gone and remember if empty */
mr r3,r8 /* Set the save area */
gotsave: stw r7,SACalloc(r8) /* Put back the allocation bits */
bne nodqsave /* There's still an empty slot, don't dequeue... */
lwz r4,SACnext(r8) /* Get the next in line */
stw r4,SVfree(r10) /* Dequeue our now empty save area block */
nodqsave: lis r6,HIGH_ADDR(SAVattach) /* Show that it is attached for now */
li r4,0 /* Clear this for the lock */
stw r6,SAVflags(r3) /* Set the flags to attached */
addi r9,r9,1 /* Bump up the inuse count */
stw r4,SAVprev(r3) /* Make sure that backchain is clear */
stw r9,SVinuse(r10) /* Set the inuse field */
sync /* Make sure all stores are done */
stw r4,SVlock(r10) /* Unlock both save and trace areas */
mtmsr r11 /* Restore translation and exceptions */
isync /* Make sure about it */
#if 0
mr r11,r0 /* (TEST/DEBUG) */
mr r7,r2 /* (TEST/DEBUG) */
lis r0,HIGH_ADDR(CutTrace) /* (TEST/DEBUG) */
li r2,0x2203 sc /* (TEST/DEBUG) */
mr r0,r11 /* (TEST/DEBUG) */
mr r2,r7 /* (TEST/DEBUG) */
#endif
li r7,0 xor r3,r3,r5 /* Get the virtual address */
blr /* Leave... */
/*
* This routine will return a savearea to the free list.
* Note really well: we can take NO exceptions of any kind,
* including a PTE miss once the savearea lock is held. That's
* a guaranteed deadlock. That means we must disable for interrutions
* and turn all translation off.
*
* We take a virtual address.
*
*/
ENTRY(save_ret,TAG_NO_FRAME_USED)
#if 0
cmplwi r3,0x1000 BREAKPOINT_TRAP /* (TEST/DEBUG) */
notpage0: rlwinm r6,r3,0,0,19 /* (TEST/DEBUG) */
rlwinm r7,r3,21,31,31 /* (TEST/DEBUG) */
lis r8,0x8000 /* (TEST/DEBUG) */
lwz r6,SACalloc(r6) /* (TEST/DEBUG) */
srw r8,r8,r7 /* (TEST/DEBUG) */
and. r8,r8,r6 /* (TEST/DEBUG) */
beq+ nodoublefret /* (TEST/DEBUG) */
BREAKPOINT_TRAP /* (TEST/DEBUG) */
nodoublefret: /* (TEST/DEBUG) */
#endif
mfsprg r9,2 rlwinm r6,r3,0,0,19 /* Round back down to the savearea page block */
andis. r7,r7,HIGH_ADDR(SAVinuse) /* Still in use? */
mfmsr r12 /* Get the MSR */
bnelr- /* Still in use, just leave... */
lwz r5,SACvrswap(r6) /* Get the conversion to real */
mr r8,r3 lis r10,HIGH_ADDR(EXT(saveanchor)) /* Get the high part of the anchor */
andi. r3,r12,0x7FCF /* Turn off all translation and 'rupts */
ori r10,r10,LOW_ADDR(EXT(saveanchor)) /* Bottom half of the anchor */
bt pfNoMSRirb,srNoMSR mtmsr r3 b srNoMSRx
srNoMSR: li r0,loadMSR srNoMSRx:
mfsprg r11,1 /* Get the active save area */
xor r3,r8,r5 /* Get the real address of the savearea */
cmplw r11,r3 /* Are we trying to toss the active one? */
xor r6,r6,r5 /* Make the savearea block real also */
beq- srbigtimepanic /* This is a no-no... */
rlwinm r7,r3,21,31,31 /* Get position of savearea in block */
lis r8,0x8000 /* Build a bit mask and assume first savearea */
srw r8,r8,r7 /* Get bit position of do deallocate */
srlck: lwarx r11,0,r10 /* Grab the lock value */
li r7,1 /* Use part of the delay time */
mr. r11,r11 /* Is it locked? */
bne- srlcks /* Yeah, wait for it to clear... */
stwcx. r7,0,r10 /* Try to seize that there durn lock */
beq+ srlckd /* Got it... */
b srlck /* Collision, try again... */
srlcks: lwz r11,SVlock(r10) /* Get that lock in here */
mr. r11,r11 /* Is it free yet? */
beq+ srlck /* Yeah, try for it again... */
b srlcks /* Sniff away... */
srlckd: isync /* Toss preexecutions */
lwz r11,SACalloc(r6) /* Get the allocation for this block */
lwz r7,SVinuse(r10) /* Get the in use count */
or r11,r11,r8 /* Turn on our bit */
subi r7,r7,1 /* We released one, adjust count */
cmplw r11,r8 /* Is our's the only one free? */
stw r7,SVinuse(r10) /* Save out count */
stw r11,SACalloc(r6) /* Save it out */
bne+ srtrest /* Nope, then the block is already on the free list */
lwz r11,SVfree(r10) /* Get the old head of the free list */
stw r6,SVfree(r10) /* Point the head at us now */
stw r11,SACnext(r6) /* Point us at the old last */
srtrest: li r8,0 /* Get set to clear the savearea lock */
sync /* Make sure it's all out there */
stw r8,SVlock(r10) /* Unlock it */
mtmsr r12 /* Restore interruptions and translation */
isync
#if 0
lis r0,HIGH_ADDR(CutTrace) /* (TEST/DEBUG) */
li r2,0x2204 sc /* (TEST/DEBUG) */
#endif
blr /* Go away... */
srbigtimepanic:
lis r6,HIGH_ADDR(EXT(panic)) /* First half of panic call */
lis r3,HIGH_ADDR(EXT(srfreeactive)) /* First half of panic string */
ori r6,r6,LOW_ADDR(EXT(panic)) /* Second half of panic call */
ori r3,r3,LOW_ADDR(EXT(srfreeactive)) /* Second half of panic string */
mtlr r6 /* Get the address of the panic routine */
mtmsr r12 /* Restore interruptions and translation */
isync
blrl /* Panic... */
.data
EXT(srfreeactive):
STRINGD "save_ret: Attempting to release the active savearea!!!!\000"
.text
/*
* struct savearea *save_cpv(struct savearea *)
.align 5
.globl EXT(save_cpv)
LEXT(save_cpv)
mfmsr r10 andi. r9,r10,0x7FEF isync
lwz r4,SACvrswap(r4) isync
xor r3,r3,r4
/*
* This routine will return the virtual address of the first free savearea
* block and disable for interruptions.
* Note really well: this is only for debugging, don't expect it to always work!
*
* We take a virtual address in R3 to save the original MSR, and
* return the virtual address.
*
*/
ENTRY(save_deb,TAG_NO_FRAME_USED)
mfsprg r9,2 lis r10,HIGH_ADDR(EXT(saveanchor)) /* Get the high part of the anchor */
mtcrf 0x04,r9 andi. r3,r12,0x7FCF /* Turn off all translation and 'rupts */
ori r10,r10,LOW_ADDR(EXT(saveanchor)) /* Bottom half of the anchor */
bt pfNoMSRirb,sdbNoMSR mtmsr r3 b sdbNoMSRx
sdbNoMSR: li r0,loadMSR sdbNoMSRx:
lwz r3,SVfree(r10) /* Get the physical first in list */
andi. r11,r12,0x7FFF /* Clear only interruption */
lwz r5,SACvrswap(r3) /* Get the conversion to virtual */
mtmsr r11 /* Restore DAT but not INT */
xor r3,r3,r5 /* Make it virtual */
isync
blr