2009-10-26 19:24:31 +00:00
/*
* PPC64 Huge TLB Page Support for hash based MMUs ( POWER4 and later )
*
* Copyright ( C ) 2003 David Gibson , IBM Corporation .
*
* Based on the IA - 32 version :
* Copyright ( C ) 2002 , Rohit Seth < rohit . seth @ intel . com >
*/
# include <linux/mm.h>
# include <linux/hugetlb.h>
# include <asm/pgtable.h>
# include <asm/pgalloc.h>
# include <asm/cacheflush.h>
# include <asm/machdep.h>
2013-04-15 16:53:19 +00:00
extern long hpte_insert_repeating ( unsigned long hash , unsigned long vpn ,
unsigned long pa , unsigned long rlags ,
unsigned long vflags , int psize , int ssize ) ;
2009-10-26 19:24:31 +00:00
int __hash_page_huge ( unsigned long ea , unsigned long access , unsigned long vsid ,
2014-12-04 11:00:14 +05:30
pte_t * ptep , unsigned long trap , unsigned long flags ,
int ssize , unsigned int shift , unsigned int mmu_psize )
2009-10-26 19:24:31 +00:00
{
2012-09-10 02:52:50 +00:00
unsigned long vpn ;
2009-10-26 19:24:31 +00:00
unsigned long old_pte , new_pte ;
2012-09-10 02:52:50 +00:00
unsigned long rflags , pa , sz ;
2009-10-26 19:24:31 +00:00
long slot ;
BUG_ON ( shift ! = mmu_psize_defs [ mmu_psize ] . shift ) ;
/* Search the Linux page table for a match with va */
2012-09-10 02:52:50 +00:00
vpn = hpt_vpn ( ea , vsid , ssize ) ;
2009-10-26 19:24:31 +00:00
2010-07-23 09:02:27 +10:00
/* At this point, we have a pte (old_pte) which can be used to build
2009-10-26 19:24:31 +00:00
* or update an HPTE . There are 2 cases :
*
* 1. There is a valid ( present ) pte with no associated HPTE ( this is
* the most common case )
* 2. There is a valid ( present ) pte with an associated HPTE . The
* current values of the pp bits in the HPTE prevent access
* because we are doing software DIRTY bit management and the
* page is currently not DIRTY .
*/
do {
old_pte = pte_val ( * ptep ) ;
2010-07-23 09:02:27 +10:00
/* If PTE busy, retry the access */
2016-04-29 23:25:45 +10:00
if ( unlikely ( old_pte & H_PAGE_BUSY ) )
2010-07-23 09:02:27 +10:00
return 0 ;
/* If PTE permissions don't match, take page fault */
2016-04-29 23:25:34 +10:00
if ( unlikely ( ! check_pte_access ( access , old_pte ) ) )
2010-07-23 09:02:27 +10:00
return 1 ;
2016-04-29 23:25:34 +10:00
2010-07-23 09:02:27 +10:00
/* Try to lock the PTE, add ACCESSED and DIRTY if it was
* a write access */
2016-04-29 23:25:45 +10:00
new_pte = old_pte | H_PAGE_BUSY | _PAGE_ACCESSED ;
2016-04-29 23:25:30 +10:00
if ( access & _PAGE_WRITE )
2010-07-23 09:02:27 +10:00
new_pte | = _PAGE_DIRTY ;
2016-04-29 23:25:27 +10:00
} while ( ! pte_xchg ( ptep , __pte ( old_pte ) , __pte ( new_pte ) ) ) ;
2015-12-01 09:06:50 +05:30
rflags = htab_convert_pte_flags ( new_pte ) ;
2009-10-26 19:24:31 +00:00
sz = ( ( 1UL ) < < shift ) ;
if ( ! cpu_has_feature ( CPU_FTR_COHERENT_ICACHE ) )
/* No CPU has hugepages but lacks no execute, so we
* don ' t need to worry about that case */
2009-10-26 19:24:31 +00:00
rflags = hash_page_do_lazy_icache ( rflags , __pte ( old_pte ) , trap ) ;
2009-10-26 19:24:31 +00:00
/* Check if pte already has an hpte (case 2) */
2016-04-29 23:25:45 +10:00
if ( unlikely ( old_pte & H_PAGE_HASHPTE ) ) {
2009-10-26 19:24:31 +00:00
/* There MIGHT be an HPTE for this pte */
unsigned long hash , slot ;
2012-09-10 02:52:50 +00:00
hash = hpt_hash ( vpn , shift , ssize ) ;
2016-04-29 23:25:45 +10:00
if ( old_pte & H_PAGE_F_SECOND )
2009-10-26 19:24:31 +00:00
hash = ~ hash ;
slot = ( hash & htab_hash_mask ) * HPTES_PER_GROUP ;
2016-04-29 23:25:45 +10:00
slot + = ( old_pte & H_PAGE_F_GIX ) > > H_PAGE_F_GIX_SHIFT ;
2009-10-26 19:24:31 +00:00
2016-07-05 15:03:58 +10:00
if ( mmu_hash_ops . hpte_updatepp ( slot , rflags , vpn , mmu_psize ,
mmu_psize , ssize , flags ) = = - 1 )
2009-10-26 19:24:31 +00:00
old_pte & = ~ _PAGE_HPTEFLAGS ;
}
2016-04-29 23:25:45 +10:00
if ( likely ( ! ( old_pte & H_PAGE_HASHPTE ) ) ) {
2012-09-10 02:52:50 +00:00
unsigned long hash = hpt_hash ( vpn , shift , ssize ) ;
2009-10-26 19:24:31 +00:00
pa = pte_pfn ( __pte ( old_pte ) ) < < PAGE_SHIFT ;
/* clear HPTE slot informations in new PTE */
2016-04-29 23:25:45 +10:00
new_pte = ( new_pte & ~ _PAGE_HPTEFLAGS ) | H_PAGE_HASHPTE ;
2015-12-01 09:06:45 +05:30
2013-04-15 16:53:19 +00:00
slot = hpte_insert_repeating ( hash , vpn , pa , rflags , 0 ,
mmu_psize , ssize ) ;
2009-10-26 19:24:31 +00:00
2010-07-14 19:31:48 +00:00
/*
* Hypervisor failure . Restore old pte and return - 1
* similar to __hash_page_ *
*/
if ( unlikely ( slot = = - 2 ) ) {
* ptep = __pte ( old_pte ) ;
2010-07-23 10:31:13 +10:00
hash_failure_debug ( ea , access , vsid , trap , ssize ,
2013-04-28 09:37:37 +00:00
mmu_psize , mmu_psize , old_pte ) ;
2010-07-23 09:02:27 +10:00
return - 1 ;
2010-07-14 19:31:48 +00:00
}
2009-10-26 19:24:31 +00:00
2016-04-29 23:25:45 +10:00
new_pte | = ( slot < < H_PAGE_F_GIX_SHIFT ) &
( H_PAGE_F_SECOND | H_PAGE_F_GIX ) ;
2009-10-26 19:24:31 +00:00
}
/*
* No need to use ldarx / stdcx here
*/
2016-04-29 23:25:45 +10:00
* ptep = __pte ( new_pte & ~ H_PAGE_BUSY ) ;
2010-07-23 09:02:27 +10:00
return 0 ;
2009-10-26 19:24:31 +00:00
}
2015-12-01 09:06:52 +05:30
# if defined(CONFIG_PPC_64K_PAGES) && defined(CONFIG_DEBUG_VM)
/*
* This enables us to catch the wrong page directory format
* Moved here so that we can use WARN ( ) in the call .
*/
int hugepd_ok ( hugepd_t hpd )
{
bool is_hugepd ;
/*
* We should not find this format in page directory , warn otherwise .
*/
is_hugepd = ( ( ( hpd . pd & 0x3 ) = = 0x0 ) & & ( ( hpd . pd & HUGEPD_SHIFT_MASK ) ! = 0 ) ) ;
WARN ( is_hugepd , " Found wrong page directory format \n " ) ;
return 0 ;
}
# endif