2005-04-16 15:20:36 -07:00
/*
* pSeries_lpar . c
* Copyright ( C ) 2001 Todd Inglett , IBM Corporation
*
* pSeries LPAR support .
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
2005-11-07 11:06:55 +11:00
# undef DEBUG_LOW
2005-04-16 15:20:36 -07:00
# include <linux/kernel.h>
# include <linux/dma-mapping.h>
2005-11-23 17:56:06 +11:00
# include <linux/console.h>
2005-04-16 15:20:36 -07:00
# include <asm/processor.h>
# include <asm/mmu.h>
# include <asm/page.h>
# include <asm/pgtable.h>
# include <asm/machdep.h>
# include <asm/abs_addr.h>
# include <asm/mmu_context.h>
# include <asm/iommu.h>
# include <asm/tlbflush.h>
# include <asm/tlb.h>
# include <asm/prom.h>
# include <asm/cputable.h>
2005-11-07 09:49:43 +11:00
# include <asm/udbg.h>
2005-11-07 13:18:13 +11:00
# include <asm/smp.h>
2005-11-03 15:33:31 +11:00
# include "plpar_wrappers.h"
2005-04-16 15:20:36 -07:00
2005-11-07 11:06:55 +11:00
# ifdef DEBUG_LOW
# define DBG_LOW(fmt...) do { udbg_printf(fmt); } while(0)
2005-04-16 15:20:36 -07:00
# else
2005-11-07 11:06:55 +11:00
# define DBG_LOW(fmt...) do { } while(0)
2005-04-16 15:20:36 -07:00
# endif
2006-07-19 08:01:28 +10:00
/* in hvCall.S */
2005-04-16 15:20:36 -07:00
EXPORT_SYMBOL ( plpar_hcall ) ;
2006-07-19 08:01:28 +10:00
EXPORT_SYMBOL ( plpar_hcall9 ) ;
2005-04-16 15:20:36 -07:00
EXPORT_SYMBOL ( plpar_hcall_norets ) ;
2006-07-19 08:01:28 +10:00
2005-04-16 15:20:36 -07:00
extern void pSeries_find_serial_port ( void ) ;
int vtermno ; /* virtual terminal# for udbg */
# define __ALIGNED__ __attribute__((__aligned__(sizeof(long))))
2005-11-23 17:57:25 +11:00
static void udbg_hvsi_putc ( char c )
2005-04-16 15:20:36 -07:00
{
/* packet's seqno isn't used anyways */
uint8_t packet [ ] __ALIGNED__ = { 0xff , 5 , 0 , 0 , c } ;
int rc ;
if ( c = = ' \n ' )
udbg_hvsi_putc ( ' \r ' ) ;
do {
rc = plpar_put_term_char ( vtermno , sizeof ( packet ) , packet ) ;
2006-03-30 14:49:40 +02:00
} while ( rc = = H_BUSY ) ;
2005-04-16 15:20:36 -07:00
}
static long hvsi_udbg_buf_len ;
static uint8_t hvsi_udbg_buf [ 256 ] ;
static int udbg_hvsi_getc_poll ( void )
{
unsigned char ch ;
int rc , i ;
if ( hvsi_udbg_buf_len = = 0 ) {
rc = plpar_get_term_char ( vtermno , & hvsi_udbg_buf_len , hvsi_udbg_buf ) ;
2006-03-30 14:49:40 +02:00
if ( rc ! = H_SUCCESS | | hvsi_udbg_buf [ 0 ] ! = 0xff ) {
2005-04-16 15:20:36 -07:00
/* bad read or non-data packet */
hvsi_udbg_buf_len = 0 ;
} else {
/* remove the packet header */
for ( i = 4 ; i < hvsi_udbg_buf_len ; i + + )
hvsi_udbg_buf [ i - 4 ] = hvsi_udbg_buf [ i ] ;
hvsi_udbg_buf_len - = 4 ;
}
}
if ( hvsi_udbg_buf_len < = 0 | | hvsi_udbg_buf_len > 256 ) {
/* no data ready */
hvsi_udbg_buf_len = 0 ;
return - 1 ;
}
ch = hvsi_udbg_buf [ 0 ] ;
/* shift remaining data down */
for ( i = 1 ; i < hvsi_udbg_buf_len ; i + + ) {
hvsi_udbg_buf [ i - 1 ] = hvsi_udbg_buf [ i ] ;
}
hvsi_udbg_buf_len - - ;
return ch ;
}
2005-11-30 16:54:12 +11:00
static int udbg_hvsi_getc ( void )
2005-04-16 15:20:36 -07:00
{
int ch ;
for ( ; ; ) {
ch = udbg_hvsi_getc_poll ( ) ;
if ( ch = = - 1 ) {
/* This shouldn't be needed...but... */
volatile unsigned long delay ;
for ( delay = 0 ; delay < 2000000 ; delay + + )
;
} else {
return ch ;
}
}
}
2005-11-23 17:57:25 +11:00
static void udbg_putcLP ( char c )
2005-04-16 15:20:36 -07:00
{
char buf [ 16 ] ;
unsigned long rc ;
if ( c = = ' \n ' )
udbg_putcLP ( ' \r ' ) ;
buf [ 0 ] = c ;
do {
rc = plpar_put_term_char ( vtermno , 1 , buf ) ;
2006-03-30 14:49:40 +02:00
} while ( rc = = H_BUSY ) ;
2005-04-16 15:20:36 -07:00
}
/* Buffered chars getc */
static long inbuflen ;
static long inbuf [ 2 ] ; /* must be 2 longs */
static int udbg_getc_pollLP ( void )
{
/* The interface is tricky because it may return up to 16 chars.
* We save them statically for future calls to udbg_getc ( ) .
*/
char ch , * buf = ( char * ) inbuf ;
int i ;
long rc ;
if ( inbuflen = = 0 ) {
/* get some more chars. */
inbuflen = 0 ;
rc = plpar_get_term_char ( vtermno , & inbuflen , buf ) ;
2006-03-30 14:49:40 +02:00
if ( rc ! = H_SUCCESS )
2005-04-16 15:20:36 -07:00
inbuflen = 0 ; /* otherwise inbuflen is garbage */
}
if ( inbuflen < = 0 | | inbuflen > 16 ) {
/* Catch error case as well as other oddities (corruption) */
inbuflen = 0 ;
return - 1 ;
}
ch = buf [ 0 ] ;
for ( i = 1 ; i < inbuflen ; i + + ) /* shuffle them down. */
buf [ i - 1 ] = buf [ i ] ;
inbuflen - - ;
return ch ;
}
2005-11-30 16:54:12 +11:00
static int udbg_getcLP ( void )
2005-04-16 15:20:36 -07:00
{
int ch ;
for ( ; ; ) {
ch = udbg_getc_pollLP ( ) ;
if ( ch = = - 1 ) {
/* This shouldn't be needed...but... */
volatile unsigned long delay ;
for ( delay = 0 ; delay < 2000000 ; delay + + )
;
} else {
return ch ;
}
}
}
/* call this from early_init() for a working debug console on
* vterm capable LPAR machines
*/
2005-11-23 17:56:06 +11:00
void __init udbg_init_debug_lpar ( void )
2005-04-16 15:20:36 -07:00
{
vtermno = 0 ;
2005-09-06 11:56:42 +10:00
udbg_putc = udbg_putcLP ;
udbg_getc = udbg_getcLP ;
udbg_getc_poll = udbg_getc_pollLP ;
2005-04-16 15:20:36 -07:00
}
/* returns 0 if couldn't find or use /chosen/stdout as console */
2005-11-23 17:56:06 +11:00
void __init find_udbg_vterm ( void )
2005-04-16 15:20:36 -07:00
{
struct device_node * stdout_node ;
2006-07-12 15:39:43 +10:00
const u32 * termno ;
const char * name ;
2005-11-23 17:56:06 +11:00
int add_console ;
2005-04-16 15:20:36 -07:00
/* find the boot console from /chosen/stdout */
if ( ! of_chosen )
2005-11-23 17:56:06 +11:00
return ;
2007-04-03 22:26:41 +10:00
name = of_get_property ( of_chosen , " linux,stdout-path " , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( name = = NULL )
2005-11-23 17:56:06 +11:00
return ;
2005-04-16 15:20:36 -07:00
stdout_node = of_find_node_by_path ( name ) ;
if ( ! stdout_node )
2005-11-23 17:56:06 +11:00
return ;
2007-04-03 22:26:41 +10:00
name = of_get_property ( stdout_node , " name " , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( ! name ) {
printk ( KERN_WARNING " stdout node missing 'name' property! \n " ) ;
goto out ;
}
2005-11-23 17:56:06 +11:00
/* The user has requested a console so this is already set up. */
add_console = ! strstr ( cmd_line , " console= " ) ;
2005-04-16 15:20:36 -07:00
2005-11-23 17:56:06 +11:00
/* Check if it's a virtual terminal */
if ( strncmp ( name , " vty " , 3 ) ! = 0 )
goto out ;
2007-04-03 22:26:41 +10:00
termno = of_get_property ( stdout_node , " reg " , NULL ) ;
2005-11-23 17:56:06 +11:00
if ( termno = = NULL )
goto out ;
vtermno = termno [ 0 ] ;
2007-05-03 17:26:52 +10:00
if ( of_device_is_compatible ( stdout_node , " hvterm1 " ) ) {
2005-11-23 17:56:06 +11:00
udbg_putc = udbg_putcLP ;
udbg_getc = udbg_getcLP ;
udbg_getc_poll = udbg_getc_pollLP ;
if ( add_console )
add_preferred_console ( " hvc " , termno [ 0 ] & 0xff , NULL ) ;
2007-05-03 17:26:52 +10:00
} else if ( of_device_is_compatible ( stdout_node , " hvterm-protocol " ) ) {
2005-11-23 17:56:06 +11:00
vtermno = termno [ 0 ] ;
udbg_putc = udbg_hvsi_putc ;
udbg_getc = udbg_hvsi_getc ;
udbg_getc_poll = udbg_hvsi_getc_poll ;
if ( add_console )
add_preferred_console ( " hvsi " , termno [ 0 ] & 0xff , NULL ) ;
2005-04-16 15:20:36 -07:00
}
out :
of_node_put ( stdout_node ) ;
}
void vpa_init ( int cpu )
{
int hwcpu = get_hard_smp_processor_id ( cpu ) ;
2006-08-07 16:19:19 +10:00
unsigned long addr ;
2005-04-16 15:20:36 -07:00
long ret ;
2005-09-03 15:55:59 -07:00
if ( cpu_has_feature ( CPU_FTR_ALTIVEC ) )
2006-01-13 10:26:42 +11:00
lppaca [ cpu ] . vmxregs_in_use = 1 ;
2005-09-03 15:55:59 -07:00
2006-08-07 16:19:19 +10:00
addr = __pa ( & lppaca [ cpu ] ) ;
ret = register_vpa ( hwcpu , addr ) ;
2005-04-16 15:20:36 -07:00
2006-08-07 16:19:19 +10:00
if ( ret ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_ERR " WARNING: vpa_init: VPA registration for "
" cpu %d (hw %d) of area %lx returns %ld \n " ,
2006-08-07 16:19:19 +10:00
cpu , hwcpu , addr , ret ) ;
return ;
}
/*
* PAPR says this feature is SLB - Buffer but firmware never
* reports that . All SPLPAR support SLB shadow buffer .
*/
addr = __pa ( & slb_shadow [ cpu ] ) ;
if ( firmware_has_feature ( FW_FEATURE_SPLPAR ) ) {
ret = register_slb_shadow ( hwcpu , addr ) ;
if ( ret )
printk ( KERN_ERR
" WARNING: vpa_init: SLB shadow buffer "
" registration for cpu %d (hw %d) of area %lx "
" returns %ld \n " , cpu , hwcpu , addr , ret ) ;
}
2005-04-16 15:20:36 -07:00
}
2006-10-05 11:35:10 -07:00
static long pSeries_lpar_hpte_insert ( unsigned long hpte_group ,
2005-11-07 11:06:55 +11:00
unsigned long va , unsigned long pa ,
unsigned long rflags , unsigned long vflags ,
2007-10-11 20:37:10 +10:00
int psize , int ssize )
2005-04-16 15:20:36 -07:00
{
unsigned long lpar_rc ;
unsigned long flags ;
unsigned long slot ;
2005-07-13 01:11:42 -07:00
unsigned long hpte_v , hpte_r ;
2005-04-16 15:20:36 -07:00
2005-11-07 11:06:55 +11:00
if ( ! ( vflags & HPTE_V_BOLTED ) )
DBG_LOW ( " hpte_insert(group=%lx, va=%016lx, pa=%016lx, "
" rflags=%lx, vflags=%lx, psize=%d) \n " ,
hpte_group , va , pa , rflags , vflags , psize ) ;
2007-10-11 20:37:10 +10:00
hpte_v = hpte_encode_v ( va , psize , ssize ) | vflags | HPTE_V_VALID ;
2005-11-07 11:06:55 +11:00
hpte_r = hpte_encode_r ( pa , psize ) | rflags ;
if ( ! ( vflags & HPTE_V_BOLTED ) )
DBG_LOW ( " hpte_v=%016lx, hpte_r=%016lx \n " , hpte_v , hpte_r ) ;
2005-04-16 15:20:36 -07:00
/* Now fill in the actual HPTE */
/* Set CEC cookie to 0 */
/* Zero page = 0 */
/* I-cache Invalidate = 0 */
/* I-cache synchronize = 0 */
/* Exact = 0 */
flags = 0 ;
2005-11-07 11:06:55 +11:00
/* Make pHyp happy */
2005-07-13 01:11:42 -07:00
if ( rflags & ( _PAGE_GUARDED | _PAGE_NO_CACHE ) )
hpte_r & = ~ _PAGE_COHERENT ;
2005-04-16 15:20:36 -07:00
2006-07-19 08:01:28 +10:00
lpar_rc = plpar_pte_enter ( flags , hpte_group , hpte_v , hpte_r , & slot ) ;
2006-03-30 14:49:40 +02:00
if ( unlikely ( lpar_rc = = H_PTEG_FULL ) ) {
2005-11-07 11:06:55 +11:00
if ( ! ( vflags & HPTE_V_BOLTED ) )
DBG_LOW ( " full \n " ) ;
2005-04-16 15:20:36 -07:00
return - 1 ;
2005-11-07 11:06:55 +11:00
}
2005-04-16 15:20:36 -07:00
/*
* Since we try and ioremap PHBs we don ' t own , the pte insert
* will fail . However we must catch the failure in hash_page
* or we will loop forever , so return - 2 in this case .
*/
2006-03-30 14:49:40 +02:00
if ( unlikely ( lpar_rc ! = H_SUCCESS ) ) {
2005-11-07 11:06:55 +11:00
if ( ! ( vflags & HPTE_V_BOLTED ) )
DBG_LOW ( " lpar err %d \n " , lpar_rc ) ;
2005-04-16 15:20:36 -07:00
return - 2 ;
2005-11-07 11:06:55 +11:00
}
if ( ! ( vflags & HPTE_V_BOLTED ) )
DBG_LOW ( " -> slot: %d \n " , slot & 7 ) ;
2005-04-16 15:20:36 -07:00
/* Because of iSeries, we have to pass down the secondary
* bucket bit here as well
*/
2005-07-13 01:11:42 -07:00
return ( slot & 7 ) | ( ! ! ( vflags & HPTE_V_SECONDARY ) < < 3 ) ;
2005-04-16 15:20:36 -07:00
}
static DEFINE_SPINLOCK ( pSeries_lpar_tlbie_lock ) ;
static long pSeries_lpar_hpte_remove ( unsigned long hpte_group )
{
unsigned long slot_offset ;
unsigned long lpar_rc ;
int i ;
unsigned long dummy1 , dummy2 ;
/* pick a random slot to start at */
slot_offset = mftb ( ) & 0x7 ;
for ( i = 0 ; i < HPTES_PER_GROUP ; i + + ) {
/* don't remove a bolted entry */
lpar_rc = plpar_pte_remove ( H_ANDCOND , hpte_group + slot_offset ,
( 0x1UL < < 4 ) , & dummy1 , & dummy2 ) ;
2006-03-30 14:49:40 +02:00
if ( lpar_rc = = H_SUCCESS )
2005-04-16 15:20:36 -07:00
return i ;
2006-03-30 14:49:40 +02:00
BUG_ON ( lpar_rc ! = H_NOT_FOUND ) ;
2005-04-16 15:20:36 -07:00
slot_offset + + ;
slot_offset & = 0x7 ;
}
return - 1 ;
}
static void pSeries_lpar_hptab_clear ( void )
{
unsigned long size_bytes = 1UL < < ppc64_pft_size ;
unsigned long hpte_count = size_bytes > > 4 ;
2007-06-14 15:31:34 +10:00
unsigned long dummy1 , dummy2 , dword0 ;
long lpar_rc ;
2005-04-16 15:20:36 -07:00
int i ;
/* TODO: Use bulk call */
2007-06-14 15:31:34 +10:00
for ( i = 0 ; i < hpte_count ; i + + ) {
/* dont remove HPTEs with VRMA mappings */
lpar_rc = plpar_pte_remove_raw ( H_ANDCOND , i , HPTE_V_1TB_SEG ,
& dummy1 , & dummy2 ) ;
if ( lpar_rc = = H_NOT_FOUND ) {
lpar_rc = plpar_pte_read_raw ( 0 , i , & dword0 , & dummy1 ) ;
if ( ! lpar_rc & & ( ( dword0 & HPTE_V_VRMA_MASK )
! = HPTE_V_VRMA_MASK ) )
/* Can be hpte for 1TB Seg. So remove it */
plpar_pte_remove_raw ( 0 , i , 0 , & dummy1 , & dummy2 ) ;
}
}
2005-04-16 15:20:36 -07:00
}
2007-10-11 20:37:10 +10:00
/*
* This computes the AVPN and B fields of the first dword of a HPTE ,
* for use when we want to match an existing PTE . The bottom 7 bits
* of the returned value are zero .
*/
static inline unsigned long hpte_encode_avpn ( unsigned long va , int psize ,
int ssize )
{
unsigned long v ;
v = ( va > > 23 ) & ~ ( mmu_psize_defs [ psize ] . avpnm ) ;
v < < = HPTE_V_AVPN_SHIFT ;
v | = ( ( unsigned long ) ssize ) < < HPTE_V_SSIZE_SHIFT ;
return v ;
}
2005-04-16 15:20:36 -07:00
/*
* NOTE : for updatepp ops we are fortunate that the linux " newpp " bits and
* the low 3 bits of flags happen to line up . So no transform is needed .
* We can probably optimize here and assume the high bits of newpp are
* already zero . For now I am paranoid .
*/
2005-11-07 11:06:55 +11:00
static long pSeries_lpar_hpte_updatepp ( unsigned long slot ,
unsigned long newpp ,
unsigned long va ,
2007-10-11 20:37:10 +10:00
int psize , int ssize , int local )
2005-04-16 15:20:36 -07:00
{
unsigned long lpar_rc ;
unsigned long flags = ( newpp & 7 ) | H_AVPN ;
2005-11-07 11:06:55 +11:00
unsigned long want_v ;
2005-04-16 15:20:36 -07:00
2007-10-11 20:37:10 +10:00
want_v = hpte_encode_avpn ( va , psize , ssize ) ;
2005-04-16 15:20:36 -07:00
2005-11-07 11:06:55 +11:00
DBG_LOW ( " update: avpnv=%016lx, hash=%016lx, f=%x, psize: %d ... " ,
2007-10-11 20:37:10 +10:00
want_v , slot , flags , psize ) ;
2005-04-16 15:20:36 -07:00
2007-10-11 20:37:10 +10:00
lpar_rc = plpar_pte_protect ( flags , slot , want_v ) ;
2005-11-07 11:06:55 +11:00
2006-03-30 14:49:40 +02:00
if ( lpar_rc = = H_NOT_FOUND ) {
2005-11-07 11:06:55 +11:00
DBG_LOW ( " not found ! \n " ) ;
2005-04-16 15:20:36 -07:00
return - 1 ;
2005-11-07 11:06:55 +11:00
}
DBG_LOW ( " ok \n " ) ;
2005-04-16 15:20:36 -07:00
2006-03-30 14:49:40 +02:00
BUG_ON ( lpar_rc ! = H_SUCCESS ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static unsigned long pSeries_lpar_hpte_getword0 ( unsigned long slot )
{
unsigned long dword0 ;
unsigned long lpar_rc ;
unsigned long dummy_word1 ;
unsigned long flags ;
/* Read 1 pte at a time */
/* Do not need RPN to logical page translation */
/* No cross CEC PFT access */
flags = 0 ;
lpar_rc = plpar_pte_read ( flags , slot , & dword0 , & dummy_word1 ) ;
2006-03-30 14:49:40 +02:00
BUG_ON ( lpar_rc ! = H_SUCCESS ) ;
2005-04-16 15:20:36 -07:00
return dword0 ;
}
2007-10-11 20:37:10 +10:00
static long pSeries_lpar_hpte_find ( unsigned long va , int psize , int ssize )
2005-04-16 15:20:36 -07:00
{
unsigned long hash ;
2007-10-11 20:37:10 +10:00
unsigned long i ;
2005-04-16 15:20:36 -07:00
long slot ;
2005-11-07 11:06:55 +11:00
unsigned long want_v , hpte_v ;
2005-04-16 15:20:36 -07:00
2007-10-11 20:37:10 +10:00
hash = hpt_hash ( va , mmu_psize_defs [ psize ] . shift , ssize ) ;
want_v = hpte_encode_avpn ( va , psize , ssize ) ;
/* Bolted entries are always in the primary group */
slot = ( hash & htab_hash_mask ) * HPTES_PER_GROUP ;
for ( i = 0 ; i < HPTES_PER_GROUP ; i + + ) {
hpte_v = pSeries_lpar_hpte_getword0 ( slot ) ;
if ( HPTE_V_COMPARE ( hpte_v , want_v ) & & ( hpte_v & HPTE_V_VALID ) )
/* HPTE matches */
return slot ;
+ + slot ;
2005-04-16 15:20:36 -07:00
}
return - 1 ;
}
static void pSeries_lpar_hpte_updateboltedpp ( unsigned long newpp ,
2005-11-07 11:06:55 +11:00
unsigned long ea ,
2007-10-11 20:37:10 +10:00
int psize , int ssize )
2005-04-16 15:20:36 -07:00
{
2005-11-07 11:06:55 +11:00
unsigned long lpar_rc , slot , vsid , va , flags ;
2005-04-16 15:20:36 -07:00
2007-10-11 20:37:10 +10:00
vsid = get_kernel_vsid ( ea , ssize ) ;
va = hpt_va ( ea , vsid , ssize ) ;
2005-04-16 15:20:36 -07:00
2007-10-11 20:37:10 +10:00
slot = pSeries_lpar_hpte_find ( va , psize , ssize ) ;
2005-04-16 15:20:36 -07:00
BUG_ON ( slot = = - 1 ) ;
flags = newpp & 7 ;
lpar_rc = plpar_pte_protect ( flags , slot , 0 ) ;
2006-03-30 14:49:40 +02:00
BUG_ON ( lpar_rc ! = H_SUCCESS ) ;
2005-04-16 15:20:36 -07:00
}
static void pSeries_lpar_hpte_invalidate ( unsigned long slot , unsigned long va ,
2007-10-11 20:37:10 +10:00
int psize , int ssize , int local )
2005-04-16 15:20:36 -07:00
{
2005-11-07 11:06:55 +11:00
unsigned long want_v ;
2005-04-16 15:20:36 -07:00
unsigned long lpar_rc ;
unsigned long dummy1 , dummy2 ;
2005-11-07 11:06:55 +11:00
DBG_LOW ( " inval : slot=%lx, va=%016lx, psize: %d, local: %d " ,
slot , va , psize , local ) ;
2005-04-16 15:20:36 -07:00
2007-10-11 20:37:10 +10:00
want_v = hpte_encode_avpn ( va , psize , ssize ) ;
lpar_rc = plpar_pte_remove ( H_AVPN , slot , want_v , & dummy1 , & dummy2 ) ;
2006-03-30 14:49:40 +02:00
if ( lpar_rc = = H_NOT_FOUND )
2005-04-16 15:20:36 -07:00
return ;
2006-03-30 14:49:40 +02:00
BUG_ON ( lpar_rc ! = H_SUCCESS ) ;
2005-04-16 15:20:36 -07:00
}
2007-02-06 21:10:31 +11:00
/* Flag bits for H_BULK_REMOVE */
# define HBR_REQUEST 0x4000000000000000UL
# define HBR_RESPONSE 0x8000000000000000UL
# define HBR_END 0xc000000000000000UL
# define HBR_AVPN 0x0200000000000000UL
# define HBR_ANDCOND 0x0100000000000000UL
2005-04-16 15:20:36 -07:00
/*
* Take a spinlock around flushes to avoid bouncing the hypervisor tlbie
* lock .
*/
2006-10-05 11:35:10 -07:00
static void pSeries_lpar_flush_hash_range ( unsigned long number , int local )
2005-04-16 15:20:36 -07:00
{
2007-02-06 21:10:31 +11:00
unsigned long i , pix , rc ;
2007-02-08 15:02:35 +11:00
unsigned long flags = 0 ;
2005-04-16 15:20:36 -07:00
struct ppc64_tlb_batch * batch = & __get_cpu_var ( ppc64_tlb_batch ) ;
int lock_tlbie = ! cpu_has_feature ( CPU_FTR_LOCKLESS_TLBIE ) ;
2007-02-06 21:10:31 +11:00
unsigned long param [ 9 ] ;
unsigned long va ;
unsigned long hash , index , shift , hidx , slot ;
real_pte_t pte ;
2007-10-11 20:37:10 +10:00
int psize , ssize ;
2005-04-16 15:20:36 -07:00
if ( lock_tlbie )
spin_lock_irqsave ( & pSeries_lpar_tlbie_lock , flags ) ;
2007-02-06 21:10:31 +11:00
psize = batch - > psize ;
2007-10-11 20:37:10 +10:00
ssize = batch - > ssize ;
2007-02-06 21:10:31 +11:00
pix = 0 ;
for ( i = 0 ; i < number ; i + + ) {
va = batch - > vaddr [ i ] ;
pte = batch - > pte [ i ] ;
pte_iterate_hashed_subpages ( pte , psize , va , index , shift ) {
2007-10-11 20:37:10 +10:00
hash = hpt_hash ( va , shift , ssize ) ;
2007-02-06 21:10:31 +11:00
hidx = __rpte_to_hidx ( pte , index ) ;
if ( hidx & _PTEIDX_SECONDARY )
hash = ~ hash ;
slot = ( hash & htab_hash_mask ) * HPTES_PER_GROUP ;
slot + = hidx & _PTEIDX_GROUP_IX ;
2007-02-08 15:02:35 +11:00
if ( ! firmware_has_feature ( FW_FEATURE_BULK_REMOVE ) ) {
pSeries_lpar_hpte_invalidate ( slot , va , psize ,
2007-10-11 20:37:10 +10:00
ssize , local ) ;
2007-02-08 15:02:35 +11:00
} else {
param [ pix ] = HBR_REQUEST | HBR_AVPN | slot ;
2007-10-11 20:37:10 +10:00
param [ pix + 1 ] = hpte_encode_avpn ( va , psize ,
ssize ) ;
2007-02-08 15:02:35 +11:00
pix + = 2 ;
if ( pix = = 8 ) {
rc = plpar_hcall9 ( H_BULK_REMOVE , param ,
2007-02-06 21:10:31 +11:00
param [ 0 ] , param [ 1 ] , param [ 2 ] ,
param [ 3 ] , param [ 4 ] , param [ 5 ] ,
param [ 6 ] , param [ 7 ] ) ;
2007-02-08 15:02:35 +11:00
BUG_ON ( rc ! = H_SUCCESS ) ;
pix = 0 ;
}
2007-02-06 21:10:31 +11:00
}
} pte_iterate_hashed_end ( ) ;
}
if ( pix ) {
param [ pix ] = HBR_END ;
rc = plpar_hcall9 ( H_BULK_REMOVE , param , param [ 0 ] , param [ 1 ] ,
param [ 2 ] , param [ 3 ] , param [ 4 ] , param [ 5 ] ,
param [ 6 ] , param [ 7 ] ) ;
BUG_ON ( rc ! = H_SUCCESS ) ;
}
2005-04-16 15:20:36 -07:00
if ( lock_tlbie )
spin_unlock_irqrestore ( & pSeries_lpar_tlbie_lock , flags ) ;
}
2006-06-23 18:16:38 +10:00
void __init hpte_init_lpar ( void )
2005-04-16 15:20:36 -07:00
{
ppc_md . hpte_invalidate = pSeries_lpar_hpte_invalidate ;
ppc_md . hpte_updatepp = pSeries_lpar_hpte_updatepp ;
ppc_md . hpte_updateboltedpp = pSeries_lpar_hpte_updateboltedpp ;
ppc_md . hpte_insert = pSeries_lpar_hpte_insert ;
ppc_md . hpte_remove = pSeries_lpar_hpte_remove ;
ppc_md . flush_hash_range = pSeries_lpar_flush_hash_range ;
ppc_md . hpte_clear_all = pSeries_lpar_hptab_clear ;
}