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/abs_addr.h>
# include <linux/spinlock.h>
2005-10-14 17:09:16 +10:00
# include "call_hpt.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 ] ) ;
}
2005-11-07 11:06:55 +11:00
long iSeries_hpte_insert ( unsigned long hpte_group , unsigned long va ,
unsigned long pa , unsigned long rflags ,
unsigned long vflags , int psize )
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
2005-11-07 11:06:55 +11:00
BUG_ON ( psize ! = MMU_PAGE_4K ) ;
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 ) ;
2005-11-07 11:06:55 +11:00
slot = HvCallHpt_findValid ( & lhpte , va > > HW_PAGE_SHIFT ) ;
if ( unlikely ( lhpte . v & HPTE_V_VALID ) ) {
if ( vflags & HPTE_V_BOLTED ) {
HvCallHpt_setSwBits ( slot , 0x10 , 0 ) ;
HvCallHpt_setPp ( slot , PP_RWXX ) ;
iSeries_hunlock ( hpte_group ) ;
if ( slot < 0 )
return 0x8 | ( slot & 7 ) ;
else
return slot & 7 ;
}
BUG ( ) ;
}
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-10-21 13:41:19 +10:00
vflags | = HPTE_V_SECONDARY ;
2005-04-16 15:20:36 -07:00
secondary = 1 ;
slot & = 0x7fffffffffffffff ;
}
2005-08-03 20:21:24 +10:00
2005-11-07 11:06:55 +11:00
lhpte . v = hpte_encode_v ( va , MMU_PAGE_4K ) | vflags | HPTE_V_VALID ;
lhpte . r = hpte_encode_r ( phys_to_abs ( pa ) , MMU_PAGE_4K ) | 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 ) ;
}
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 ,
2005-11-07 11:06:55 +11:00
unsigned long va , int psize , int local )
2005-04-16 15:20:36 -07:00
{
2005-07-13 01:11:42 -07:00
hpte_t hpte ;
2005-11-07 11:06:55 +11:00
unsigned long want_v ;
2005-04-16 15:20:36 -07:00
iSeries_hlock ( slot ) ;
HvCallHpt_get ( & hpte , slot ) ;
2005-11-07 11:06:55 +11:00
want_v = hpte_encode_v ( va , MMU_PAGE_4K ) ;
if ( HPTE_V_COMPARE ( hpte . v , want_v ) & & ( 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 .
*/
2005-11-07 11:06:55 +11:00
static void iSeries_hpte_updateboltedpp ( unsigned long newpp , unsigned long ea ,
int psize )
2005-04-16 15:20:36 -07:00
{
unsigned long vsid , va , vpn ;
long slot ;
2005-11-07 11:06:55 +11:00
BUG_ON ( psize ! = MMU_PAGE_4K ) ;
2005-04-16 15:20:36 -07:00
vsid = get_kernel_vsid ( ea ) ;
va = ( vsid < < 28 ) | ( ea & 0x0fffffff ) ;
2005-11-07 11:06:55 +11:00
vpn = va > > HW_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 ,
2005-11-07 11:06:55 +11:00
int psize , int local )
2005-04-16 15:20:36 -07:00
{
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 ( ) ;
}