2008-04-30 13:38:46 +02:00
/*
* IBM System z Huge TLB Page Support for Kernel .
*
2012-07-20 11:15:04 +02:00
* Copyright IBM Corp . 2007
2008-04-30 13:38:46 +02:00
* Author ( s ) : Gerald Schaefer < gerald . schaefer @ de . ibm . com >
*/
# include <linux/mm.h>
# include <linux/hugetlb.h>
2013-07-23 20:57:57 +02:00
static inline pmd_t __pte_to_pmd ( pte_t pte )
{
pmd_t pmd ;
/*
2014-07-24 11:03:41 +02:00
* Convert encoding pte bits pmd bits
2015-04-22 13:55:59 +02:00
* lIR . uswrdy . p dy . . R . . . I . . . wr
* empty 010.000000 .0 - > 00. .0 . . .1 . . .00
* prot - none , clean , old 111.000000 .1 - > 00. .1 . . .1 . . .00
* prot - none , clean , young 111.000001 .1 - > 01. .1 . . .1 . . .00
* prot - none , dirty , old 111.000010 .1 - > 10. .1 . . .1 . . .00
* prot - none , dirty , young 111.000011 .1 - > 11. .1 . . .1 . . .00
* read - only , clean , old 111.000100 .1 - > 00. .1 . . .1 . . .01
* read - only , clean , young 101.000101 .1 - > 01. .1 . . .0 . . .01
* read - only , dirty , old 111.000110 .1 - > 10. .1 . . .1 . . .01
* read - only , dirty , young 101.000111 .1 - > 11. .1 . . .0 . . .01
* read - write , clean , old 111.001100 .1 - > 00. .1 . . .1 . . .11
* read - write , clean , young 101.001101 .1 - > 01. .1 . . .0 . . .11
* read - write , dirty , old 110.001110 .1 - > 10. .0 . . .1 . . .11
* read - write , dirty , young 100.001111 .1 - > 11. .0 . . .0 . . .11
* HW - bits : R read - only , I invalid
* SW - bits : p present , y young , d dirty , r read , w write , s special ,
* u unused , l large
2013-07-23 20:57:57 +02:00
*/
if ( pte_present ( pte ) ) {
pmd_val ( pmd ) = pte_val ( pte ) & PAGE_MASK ;
2014-07-24 11:03:41 +02:00
pmd_val ( pmd ) | = ( pte_val ( pte ) & _PAGE_READ ) > > 4 ;
pmd_val ( pmd ) | = ( pte_val ( pte ) & _PAGE_WRITE ) > > 4 ;
pmd_val ( pmd ) | = ( pte_val ( pte ) & _PAGE_INVALID ) > > 5 ;
pmd_val ( pmd ) | = ( pte_val ( pte ) & _PAGE_PROTECT ) ;
pmd_val ( pmd ) | = ( pte_val ( pte ) & _PAGE_DIRTY ) < < 10 ;
pmd_val ( pmd ) | = ( pte_val ( pte ) & _PAGE_YOUNG ) < < 10 ;
2013-07-23 20:57:57 +02:00
} else
pmd_val ( pmd ) = _SEGMENT_ENTRY_INVALID ;
return pmd ;
}
static inline pte_t __pmd_to_pte ( pmd_t pmd )
{
pte_t pte ;
/*
2014-07-24 11:03:41 +02:00
* Convert encoding pmd bits pte bits
2015-04-22 13:55:59 +02:00
* dy . . R . . . I . . . wr lIR . uswrdy . p
* empty 00. .0 . . .1 . . .00 - > 010.000000 .0
* prot - none , clean , old 00. .1 . . .1 . . .00 - > 111.000000 .1
* prot - none , clean , young 01. .1 . . .1 . . .00 - > 111.000001 .1
* prot - none , dirty , old 10. .1 . . .1 . . .00 - > 111.000010 .1
* prot - none , dirty , young 11. .1 . . .1 . . .00 - > 111.000011 .1
* read - only , clean , old 00. .1 . . .1 . . .01 - > 111.000100 .1
* read - only , clean , young 01. .1 . . .0 . . .01 - > 101.000101 .1
* read - only , dirty , old 10. .1 . . .1 . . .01 - > 111.000110 .1
* read - only , dirty , young 11. .1 . . .0 . . .01 - > 101.000111 .1
* read - write , clean , old 00. .1 . . .1 . . .11 - > 111.001100 .1
* read - write , clean , young 01. .1 . . .0 . . .11 - > 101.001101 .1
* read - write , dirty , old 10. .0 . . .1 . . .11 - > 110.001110 .1
* read - write , dirty , young 11. .0 . . .0 . . .11 - > 100.001111 .1
* HW - bits : R read - only , I invalid
* SW - bits : p present , y young , d dirty , r read , w write , s special ,
* u unused , l large
2013-07-23 20:57:57 +02:00
*/
if ( pmd_present ( pmd ) ) {
2014-07-24 11:03:41 +02:00
pte_val ( pte ) = pmd_val ( pmd ) & _SEGMENT_ENTRY_ORIGIN_LARGE ;
pte_val ( pte ) | = _PAGE_LARGE | _PAGE_PRESENT ;
pte_val ( pte ) | = ( pmd_val ( pmd ) & _SEGMENT_ENTRY_READ ) < < 4 ;
pte_val ( pte ) | = ( pmd_val ( pmd ) & _SEGMENT_ENTRY_WRITE ) < < 4 ;
pte_val ( pte ) | = ( pmd_val ( pmd ) & _SEGMENT_ENTRY_INVALID ) < < 5 ;
pte_val ( pte ) | = ( pmd_val ( pmd ) & _SEGMENT_ENTRY_PROTECT ) ;
2015-04-22 10:26:20 +02:00
pte_val ( pte ) | = ( pmd_val ( pmd ) & _SEGMENT_ENTRY_DIRTY ) > > 10 ;
pte_val ( pte ) | = ( pmd_val ( pmd ) & _SEGMENT_ENTRY_YOUNG ) > > 10 ;
2013-07-23 20:57:57 +02:00
} else
pte_val ( pte ) = _PAGE_INVALID ;
return pte ;
}
2008-04-30 13:38:46 +02:00
void set_huge_pte_at ( struct mm_struct * mm , unsigned long addr ,
2013-07-23 20:57:57 +02:00
pte_t * ptep , pte_t pte )
2008-04-30 13:38:46 +02:00
{
2015-06-25 14:59:47 -07:00
pmd_t pmd = __pte_to_pmd ( pte ) ;
2008-04-30 13:38:46 +02:00
2015-06-25 14:59:47 -07:00
pmd_val ( pmd ) | = _SEGMENT_ENTRY_LARGE ;
2013-07-23 20:57:57 +02:00
* ( pmd_t * ) ptep = pmd ;
}
pte_t huge_ptep_get ( pte_t * ptep )
{
2015-06-25 14:59:47 -07:00
pmd_t pmd = * ( pmd_t * ) ptep ;
2013-07-23 20:57:57 +02:00
return __pmd_to_pte ( pmd ) ;
}
pte_t huge_ptep_get_and_clear ( struct mm_struct * mm ,
unsigned long addr , pte_t * ptep )
{
pmd_t * pmdp = ( pmd_t * ) ptep ;
pte_t pte = huge_ptep_get ( ptep ) ;
2008-04-30 13:38:46 +02:00
2014-04-03 13:55:01 +02:00
pmdp_flush_direct ( mm , addr , pmdp ) ;
2013-07-23 20:57:57 +02:00
pmd_val ( * pmdp ) = _SEGMENT_ENTRY_EMPTY ;
return pte ;
2008-04-30 13:38:46 +02:00
}
2008-07-23 21:27:41 -07:00
pte_t * huge_pte_alloc ( struct mm_struct * mm ,
unsigned long addr , unsigned long sz )
2008-04-30 13:38:46 +02:00
{
pgd_t * pgdp ;
pud_t * pudp ;
pmd_t * pmdp = NULL ;
pgdp = pgd_offset ( mm , addr ) ;
pudp = pud_alloc ( mm , pgdp , addr ) ;
if ( pudp )
pmdp = pmd_alloc ( mm , pudp , addr ) ;
return ( pte_t * ) pmdp ;
}
pte_t * huge_pte_offset ( struct mm_struct * mm , unsigned long addr )
{
pgd_t * pgdp ;
pud_t * pudp ;
pmd_t * pmdp = NULL ;
pgdp = pgd_offset ( mm , addr ) ;
if ( pgd_present ( * pgdp ) ) {
pudp = pud_offset ( pgdp , addr ) ;
if ( pud_present ( * pudp ) )
pmdp = pmd_offset ( pudp , addr ) ;
}
return ( pte_t * ) pmdp ;
}
int pmd_huge ( pmd_t pmd )
{
2015-06-25 14:59:49 -07:00
return pmd_large ( pmd ) ;
2008-04-30 13:38:46 +02:00
}
2008-07-23 21:27:50 -07:00
int pud_huge ( pud_t pud )
{
return 0 ;
}