2006-11-23 00:46:54 +01:00
/*
* PS3 pagetable management routines .
*
* Copyright ( C ) 2006 Sony Computer Entertainment Inc .
2007-02-12 00:55:26 -08:00
* Copyright 2006 , 2007 Sony Corporation
2006-11-23 00:46:54 +01: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 ; version 2 of the License .
*
* 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
*/
# include <linux/kernel.h>
2010-07-12 14:36:09 +10:00
# include <linux/memblock.h>
2006-11-23 00:46:54 +01:00
# include <asm/machdep.h>
2008-02-13 16:56:49 -08:00
# include <asm/prom.h>
2006-11-23 00:46:54 +01:00
# include <asm/udbg.h>
# include <asm/lv1call.h>
2007-02-12 00:55:26 -08:00
# include <asm/ps3fb.h>
2006-11-23 00:46:54 +01:00
# include "platform.h"
2008-08-21 06:18:51 +10:00
/**
* enum lpar_vas_id - id of LPAR virtual address space .
* @ lpar_vas_id_current : Current selected virtual address space
*
* Identify the target LPAR address space .
*/
enum ps3_lpar_vas_id {
PS3_LPAR_VAS_ID_CURRENT = 0 ,
} ;
static DEFINE_SPINLOCK ( ps3_htab_lock ) ;
2006-11-23 00:46:54 +01:00
static long ps3_hpte_insert ( unsigned long hpte_group , unsigned long va ,
2007-10-11 20:37:10 +10:00
unsigned long pa , unsigned long rflags , unsigned long vflags ,
int psize , int ssize )
2006-11-23 00:46:54 +01:00
{
2008-08-21 06:18:51 +10:00
int result ;
u64 hpte_v , hpte_r ;
u64 inserted_index ;
u64 evicted_v , evicted_r ;
u64 hpte_v_array [ 4 ] , hpte_rs ;
2006-11-23 00:46:54 +01:00
unsigned long flags ;
2008-08-21 06:18:51 +10:00
long ret = - 1 ;
2006-11-23 00:46:54 +01:00
2008-08-21 06:18:51 +10:00
/*
* lv1_insert_htab_entry ( ) will search for victim
* entry in both primary and secondary pte group
*/
vflags & = ~ HPTE_V_SECONDARY ;
2006-11-23 00:46:54 +01:00
2008-08-21 06:18:51 +10:00
hpte_v = hpte_encode_v ( va , psize , ssize ) | vflags | HPTE_V_VALID ;
hpte_r = hpte_encode_r ( ps3_mm_phys_to_lpar ( pa ) , psize ) | rflags ;
2006-11-23 00:46:54 +01:00
2008-08-21 06:18:51 +10:00
spin_lock_irqsave ( & ps3_htab_lock , flags ) ;
2006-11-23 00:46:54 +01:00
2008-08-21 06:18:51 +10:00
/* talk hvc to replace entries BOLTED == 0 */
result = lv1_insert_htab_entry ( PS3_LPAR_VAS_ID_CURRENT , hpte_group ,
hpte_v , hpte_r ,
HPTE_V_BOLTED , 0 ,
& inserted_index ,
& evicted_v , & evicted_r ) ;
2006-11-23 00:46:54 +01:00
if ( result ) {
2008-08-21 06:18:51 +10:00
/* all entries bolted !*/
2009-01-13 20:02:39 +00:00
pr_info ( " %s:result=%d va=%lx pa=%lx ix=%lx v=%llx r=%llx \n " ,
2008-08-21 06:18:51 +10:00
__func__ , result , va , pa , hpte_group , hpte_v , hpte_r ) ;
2006-11-23 00:46:54 +01:00
BUG ( ) ;
}
/*
2008-08-21 06:18:51 +10:00
* see if the entry is inserted into secondary pteg
2006-11-23 00:46:54 +01:00
*/
2008-08-21 06:18:51 +10:00
result = lv1_read_htab_entries ( PS3_LPAR_VAS_ID_CURRENT ,
inserted_index & ~ 0x3UL ,
& hpte_v_array [ 0 ] , & hpte_v_array [ 1 ] ,
& hpte_v_array [ 2 ] , & hpte_v_array [ 3 ] ,
& hpte_rs ) ;
BUG_ON ( result ) ;
2006-11-23 00:46:54 +01:00
2008-08-21 06:18:51 +10:00
if ( hpte_v_array [ inserted_index % 4 ] & HPTE_V_SECONDARY )
ret = ( inserted_index & 7 ) | ( 1 < < 3 ) ;
else
ret = inserted_index & 7 ;
2006-11-23 00:46:54 +01:00
2008-08-21 06:18:51 +10:00
spin_unlock_irqrestore ( & ps3_htab_lock , flags ) ;
2006-11-23 00:46:54 +01:00
2008-08-21 06:18:51 +10:00
return ret ;
2006-11-23 00:46:54 +01:00
}
static long ps3_hpte_remove ( unsigned long hpte_group )
{
panic ( " ps3_hpte_remove() not implemented " ) ;
return 0 ;
}
static long ps3_hpte_updatepp ( unsigned long slot , unsigned long newpp ,
2007-10-11 20:37:10 +10:00
unsigned long va , int psize , int ssize , int local )
2006-11-23 00:46:54 +01:00
{
2008-08-21 06:18:51 +10:00
int result ;
u64 hpte_v , want_v , hpte_rs ;
u64 hpte_v_array [ 4 ] ;
2006-11-23 00:46:54 +01:00
unsigned long flags ;
2008-08-21 06:18:51 +10:00
long ret ;
2006-11-23 00:46:54 +01:00
2008-08-21 06:18:51 +10:00
want_v = hpte_encode_v ( va , psize , ssize ) ;
2006-11-23 00:46:54 +01:00
2008-08-21 06:18:51 +10:00
spin_lock_irqsave ( & ps3_htab_lock , flags ) ;
2006-11-23 00:46:54 +01:00
2008-08-21 06:18:51 +10:00
result = lv1_read_htab_entries ( PS3_LPAR_VAS_ID_CURRENT , slot & ~ 0x3UL ,
& hpte_v_array [ 0 ] , & hpte_v_array [ 1 ] ,
& hpte_v_array [ 2 ] , & hpte_v_array [ 3 ] ,
& hpte_rs ) ;
2006-11-23 00:46:54 +01:00
if ( result ) {
2008-08-21 06:18:51 +10:00
pr_info ( " %s: res=%d read va=%lx slot=%lx psize=%d \n " ,
__func__ , result , va , slot , psize ) ;
2006-11-23 00:46:54 +01:00
BUG ( ) ;
}
2008-08-21 06:18:51 +10:00
hpte_v = hpte_v_array [ slot % 4 ] ;
2006-11-23 00:46:54 +01:00
2008-08-21 06:18:51 +10:00
/*
* As lv1_read_htab_entries ( ) does not give us the RPN , we can
* not synthesize the new hpte_r value here , and therefore can
* not update the hpte with lv1_insert_htab_entry ( ) , so we
2010-06-11 12:16:56 +02:00
* instead invalidate it and ask the caller to update it via
2008-08-21 06:18:51 +10:00
* ps3_hpte_insert ( ) by returning a - 1 value .
*/
if ( ! HPTE_V_COMPARE ( hpte_v , want_v ) | | ! ( hpte_v & HPTE_V_VALID ) ) {
/* not found */
ret = - 1 ;
} else {
/* entry found, just invalidate it */
result = lv1_write_htab_entry ( PS3_LPAR_VAS_ID_CURRENT ,
slot , 0 , 0 ) ;
ret = - 1 ;
}
2006-11-23 00:46:54 +01:00
2008-08-21 06:18:51 +10:00
spin_unlock_irqrestore ( & ps3_htab_lock , flags ) ;
return ret ;
2006-11-23 00:46:54 +01:00
}
static void ps3_hpte_updateboltedpp ( unsigned long newpp , unsigned long ea ,
2007-10-11 20:37:10 +10:00
int psize , int ssize )
2006-11-23 00:46:54 +01:00
{
panic ( " ps3_hpte_updateboltedpp() not implemented " ) ;
}
static void ps3_hpte_invalidate ( unsigned long slot , unsigned long va ,
2007-10-11 20:37:10 +10:00
int psize , int ssize , int local )
2006-11-23 00:46:54 +01:00
{
unsigned long flags ;
2008-08-21 06:18:51 +10:00
int result ;
spin_lock_irqsave ( & ps3_htab_lock , flags ) ;
2006-11-23 00:46:54 +01:00
2008-08-21 06:18:51 +10:00
result = lv1_write_htab_entry ( PS3_LPAR_VAS_ID_CURRENT , slot , 0 , 0 ) ;
2006-11-23 00:46:54 +01:00
if ( result ) {
2008-08-21 06:18:51 +10:00
pr_info ( " %s: res=%d va=%lx slot=%lx psize=%d \n " ,
__func__ , result , va , slot , psize ) ;
2006-11-23 00:46:54 +01:00
BUG ( ) ;
}
2008-08-21 06:18:51 +10:00
spin_unlock_irqrestore ( & ps3_htab_lock , flags ) ;
2006-11-23 00:46:54 +01:00
}
static void ps3_hpte_clear ( void )
{
2008-08-21 06:18:51 +10:00
unsigned long hpte_count = ( 1UL < < ppc64_pft_size ) > > 4 ;
u64 i ;
2007-06-16 07:19:32 +10:00
2008-08-21 06:18:51 +10:00
for ( i = 0 ; i < hpte_count ; i + + )
lv1_write_htab_entry ( PS3_LPAR_VAS_ID_CURRENT , i , 0 , 0 ) ;
2007-06-16 07:19:32 +10:00
ps3_mm_shutdown ( ) ;
ps3_mm_vas_destroy ( ) ;
2006-11-23 00:46:54 +01:00
}
void __init ps3_hpte_init ( unsigned long htab_size )
{
ppc_md . hpte_invalidate = ps3_hpte_invalidate ;
ppc_md . hpte_updatepp = ps3_hpte_updatepp ;
ppc_md . hpte_updateboltedpp = ps3_hpte_updateboltedpp ;
ppc_md . hpte_insert = ps3_hpte_insert ;
ppc_md . hpte_remove = ps3_hpte_remove ;
ppc_md . hpte_clear_all = ps3_hpte_clear ;
ppc64_pft_size = __ilog2 ( htab_size ) ;
}