2005-04-16 15:20:36 -07:00
/*
* iSeries hashtable management .
2005-09-28 02:28:45 +10:00
* Derived from pSeries_htab . c
2005-04-16 15:20:36 -07:00
*
* SMP scalability work :
* Copyright ( C ) 2001 Anton Blanchard < anton @ au . ibm . com > , IBM
2005-09-28 02:28:45 +10:00
*
2005-04-16 15:20:36 -07:00
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <asm/machdep.h>
# include <asm/pgtable.h>
# include <asm/mmu.h>
# include <asm/mmu_context.h>
# include <asm/iSeries/HvCallHpt.h>
# include <asm/abs_addr.h>
# include <linux/spinlock.h>
2005-09-28 02:28:45 +10:00
static spinlock_t iSeries_hlocks [ 64 ] __cacheline_aligned_in_smp =
{ [ 0 . . . 63 ] = SPIN_LOCK_UNLOCKED } ;
2005-04-16 15:20:36 -07:00
/*
* Very primitive algorithm for picking up a lock
*/
static inline void iSeries_hlock ( unsigned long slot )
{
if ( slot & 0x8 )
slot = ~ slot ;
spin_lock ( & iSeries_hlocks [ ( slot > > 4 ) & 0x3f ] ) ;
}
static inline void iSeries_hunlock ( unsigned long slot )
{
if ( slot & 0x8 )
slot = ~ slot ;
spin_unlock ( & iSeries_hlocks [ ( slot > > 4 ) & 0x3f ] ) ;
}
static long iSeries_hpte_insert ( unsigned long hpte_group , unsigned long va ,
2005-07-13 01:11:42 -07:00
unsigned long prpn , unsigned long vflags ,
unsigned long rflags )
2005-04-16 15:20:36 -07:00
{
2005-08-03 20:21:24 +10:00
unsigned long arpn ;
2005-04-16 15:20:36 -07:00
long slot ;
2005-07-13 01:11:42 -07:00
hpte_t lhpte ;
int secondary = 0 ;
2005-04-16 15:20:36 -07:00
/*
* The hypervisor tries both primary and secondary .
* If we are being called to insert in the secondary ,
* it means we have already tried both primary and secondary ,
* so we return failure immediately .
*/
2005-07-13 01:11:42 -07:00
if ( vflags & HPTE_V_SECONDARY )
2005-04-16 15:20:36 -07:00
return - 1 ;
iSeries_hlock ( hpte_group ) ;
slot = HvCallHpt_findValid ( & lhpte , va > > PAGE_SHIFT ) ;
2005-07-13 01:11:42 -07:00
BUG_ON ( lhpte . v & HPTE_V_VALID ) ;
2005-04-16 15:20:36 -07:00
if ( slot = = - 1 ) { /* No available entry found in either group */
iSeries_hunlock ( hpte_group ) ;
return - 1 ;
}
if ( slot < 0 ) { /* MSB set means secondary group */
2005-07-13 01:11:42 -07:00
vflags | = HPTE_V_VALID ;
2005-04-16 15:20:36 -07:00
secondary = 1 ;
slot & = 0x7fffffffffffffff ;
}
2005-08-03 20:21:24 +10:00
arpn = phys_to_abs ( prpn < < PAGE_SHIFT ) > > PAGE_SHIFT ;
2005-07-13 01:11:42 -07:00
lhpte . v = ( va > > 23 ) < < HPTE_V_AVPN_SHIFT | vflags | HPTE_V_VALID ;
2005-08-03 20:21:24 +10:00
lhpte . r = ( arpn < < HPTE_R_RPN_SHIFT ) | rflags ;
2005-04-16 15:20:36 -07:00
/* Now fill in the actual HPTE */
HvCallHpt_addValidate ( slot , secondary , & lhpte ) ;
iSeries_hunlock ( hpte_group ) ;
return ( secondary < < 3 ) | ( slot & 7 ) ;
}
2005-09-23 14:47:58 +10:00
long iSeries_hpte_bolt_or_insert ( unsigned long hpte_group ,
unsigned long va , unsigned long prpn , unsigned long vflags ,
unsigned long rflags )
{
long slot ;
hpte_t lhpte ;
slot = HvCallHpt_findValid ( & lhpte , va > > PAGE_SHIFT ) ;
if ( lhpte . v & HPTE_V_VALID ) {
/* Bolt the existing HPTE */
HvCallHpt_setSwBits ( slot , 0x10 , 0 ) ;
HvCallHpt_setPp ( slot , PP_RWXX ) ;
return 0 ;
}
return iSeries_hpte_insert ( hpte_group , va , prpn , vflags , rflags ) ;
}
2005-04-16 15:20:36 -07:00
static unsigned long iSeries_hpte_getword0 ( unsigned long slot )
{
2005-07-13 01:11:42 -07:00
hpte_t hpte ;
2005-04-16 15:20:36 -07:00
HvCallHpt_get ( & hpte , slot ) ;
2005-07-13 01:11:42 -07:00
return hpte . v ;
2005-04-16 15:20:36 -07:00
}
static long iSeries_hpte_remove ( unsigned long hpte_group )
{
unsigned long slot_offset ;
int i ;
2005-07-13 01:11:42 -07:00
unsigned long hpte_v ;
2005-04-16 15:20:36 -07:00
/* Pick a random slot to start at */
slot_offset = mftb ( ) & 0x7 ;
iSeries_hlock ( hpte_group ) ;
for ( i = 0 ; i < HPTES_PER_GROUP ; i + + ) {
2005-07-13 01:11:42 -07:00
hpte_v = iSeries_hpte_getword0 ( hpte_group + slot_offset ) ;
2005-04-16 15:20:36 -07:00
2005-07-13 01:11:42 -07:00
if ( ! ( hpte_v & HPTE_V_BOLTED ) ) {
2005-09-28 02:28:45 +10:00
HvCallHpt_invalidateSetSwBitsGet ( hpte_group +
2005-04-16 15:20:36 -07:00
slot_offset , 0 , 0 ) ;
iSeries_hunlock ( hpte_group ) ;
return i ;
}
slot_offset + + ;
slot_offset & = 0x7 ;
}
iSeries_hunlock ( hpte_group ) ;
return - 1 ;
}
/*
* The HyperVisor expects the " flags " argument in this form :
2005-09-28 02:28:45 +10:00
* bits 0. .59 : reserved
* bit 60 : N
* bits 61. .63 : PP2 , PP1 , PP0
2005-04-16 15:20:36 -07:00
*/
static long iSeries_hpte_updatepp ( unsigned long slot , unsigned long newpp ,
unsigned long va , int large , int local )
{
2005-07-13 01:11:42 -07:00
hpte_t hpte ;
2005-04-16 15:20:36 -07:00
unsigned long avpn = va > > 23 ;
iSeries_hlock ( slot ) ;
HvCallHpt_get ( & hpte , slot ) ;
2005-07-13 01:11:42 -07:00
if ( ( HPTE_V_AVPN_VAL ( hpte . v ) = = avpn ) & & ( hpte . v & HPTE_V_VALID ) ) {
2005-04-16 15:20:36 -07:00
/*
* Hypervisor expects bits as NPPP , which is
* different from how they are mapped in our PP .
*/
HvCallHpt_setPp ( slot , ( newpp & 0x3 ) | ( ( newpp & 0x4 ) < < 1 ) ) ;
iSeries_hunlock ( slot ) ;
return 0 ;
}
iSeries_hunlock ( slot ) ;
return - 1 ;
}
/*
2005-09-28 02:28:45 +10:00
* Functions used to find the PTE for a particular virtual address .
2005-04-16 15:20:36 -07:00
* Only used during boot when bolting pages .
*
* Input : vpn : virtual page number
* Output : PTE index within the page table of the entry
* - 1 on failure
*/
static long iSeries_hpte_find ( unsigned long vpn )
{
2005-07-13 01:11:42 -07:00
hpte_t hpte ;
2005-04-16 15:20:36 -07:00
long slot ;
/*
* The HvCallHpt_findValid interface is as follows :
* 0xffffffffffffffff : No entry found .
* 0x00000000 xxxxxxxx : Entry found in primary group , slot x
* 0x80000000 xxxxxxxx : Entry found in secondary group , slot x
*/
2005-09-28 02:28:45 +10:00
slot = HvCallHpt_findValid ( & hpte , vpn ) ;
2005-07-13 01:11:42 -07:00
if ( hpte . v & HPTE_V_VALID ) {
2005-04-16 15:20:36 -07:00
if ( slot < 0 ) {
slot & = 0x7fffffffffffffff ;
slot = - slot ;
}
} else
slot = - 1 ;
return slot ;
}
/*
* Update the page protection bits . Intended to be used to create
* guard pages for kernel data structures on pages which are bolted
* in the HPT . Assumes pages being operated on will not be stolen .
* Does not work on large pages .
*
* No need to lock here because we should be the only user .
*/
static void iSeries_hpte_updateboltedpp ( unsigned long newpp , unsigned long ea )
{
unsigned long vsid , va , vpn ;
long slot ;
vsid = get_kernel_vsid ( ea ) ;
va = ( vsid < < 28 ) | ( ea & 0x0fffffff ) ;
vpn = va > > PAGE_SHIFT ;
2005-09-28 02:28:45 +10:00
slot = iSeries_hpte_find ( vpn ) ;
2005-04-16 15:20:36 -07:00
if ( slot = = - 1 )
panic ( " updateboltedpp: Could not find page to bolt \n " ) ;
HvCallHpt_setPp ( slot , newpp ) ;
}
static void iSeries_hpte_invalidate ( unsigned long slot , unsigned long va ,
int large , int local )
{
2005-07-13 01:11:42 -07:00
unsigned long hpte_v ;
2005-04-16 15:20:36 -07:00
unsigned long avpn = va > > 23 ;
unsigned long flags ;
local_irq_save ( flags ) ;
iSeries_hlock ( slot ) ;
2005-07-13 01:11:42 -07:00
hpte_v = iSeries_hpte_getword0 ( slot ) ;
2005-09-28 02:28:45 +10:00
2005-07-13 01:11:42 -07:00
if ( ( HPTE_V_AVPN_VAL ( hpte_v ) = = avpn ) & & ( hpte_v & HPTE_V_VALID ) )
2005-04-16 15:20:36 -07:00
HvCallHpt_invalidateSetSwBitsGet ( slot , 0 , 0 ) ;
iSeries_hunlock ( slot ) ;
local_irq_restore ( flags ) ;
}
void hpte_init_iSeries ( void )
{
ppc_md . hpte_invalidate = iSeries_hpte_invalidate ;
ppc_md . hpte_updatepp = iSeries_hpte_updatepp ;
ppc_md . hpte_updateboltedpp = iSeries_hpte_updateboltedpp ;
ppc_md . hpte_insert = iSeries_hpte_insert ;
2005-09-28 02:28:45 +10:00
ppc_md . hpte_remove = iSeries_hpte_remove ;
2005-04-16 15:20:36 -07:00
htab_finish_init ( ) ;
}