2011-03-15 19:08:22 +03:00
/*
* Copyright IBM Corp . 2011
* Author ( s ) : Jan Glauber < jang @ linux . vnet . ibm . com >
*/
2012-11-02 15:56:43 +04:00
# include <linux/hugetlb.h>
2011-03-15 19:08:22 +03:00
# include <linux/module.h>
# include <linux/mm.h>
2011-10-30 18:17:13 +04:00
# include <asm/cacheflush.h>
2011-03-15 19:08:22 +03:00
# include <asm/pgtable.h>
2012-11-02 15:56:43 +04:00
# include <asm/page.h>
2013-10-07 14:12:32 +04:00
# if PAGE_DEFAULT_KEY
2013-03-14 19:46:05 +04:00
static inline unsigned long sske_frame ( unsigned long addr , unsigned char skey )
{
asm volatile ( " .insn rrf,0xb22b0000,%[skey],%[addr],9,0 "
: [ addr ] " +a " ( addr ) : [ skey ] " d " ( skey ) ) ;
return addr ;
}
2013-10-07 14:12:32 +04:00
void __storage_key_init_range ( unsigned long start , unsigned long end )
2012-11-02 15:56:43 +04:00
{
2013-03-14 19:46:05 +04:00
unsigned long boundary , size ;
2012-11-02 15:56:43 +04:00
while ( start < end ) {
if ( MACHINE_HAS_EDAT1 ) {
/* set storage keys for a 1MB frame */
size = 1UL < < 20 ;
boundary = ( start + size ) & ~ ( size - 1 ) ;
if ( boundary < = end ) {
do {
2013-03-14 19:46:05 +04:00
start = sske_frame ( start , PAGE_DEFAULT_KEY ) ;
2012-11-02 15:56:43 +04:00
} while ( start < boundary ) ;
continue ;
}
}
page_set_storage_key ( start , PAGE_DEFAULT_KEY , 0 ) ;
start + = PAGE_SIZE ;
}
}
2013-10-07 14:12:32 +04:00
# endif
2011-03-15 19:08:22 +03:00
2012-10-01 18:18:46 +04:00
static pte_t * walk_page_table ( unsigned long addr )
{
pgd_t * pgdp ;
pud_t * pudp ;
pmd_t * pmdp ;
pte_t * ptep ;
pgdp = pgd_offset_k ( addr ) ;
if ( pgd_none ( * pgdp ) )
return NULL ;
pudp = pud_offset ( pgdp , addr ) ;
2012-10-08 11:18:26 +04:00
if ( pud_none ( * pudp ) | | pud_large ( * pudp ) )
2012-10-01 18:18:46 +04:00
return NULL ;
pmdp = pmd_offset ( pudp , addr ) ;
if ( pmd_none ( * pmdp ) | | pmd_large ( * pmdp ) )
return NULL ;
ptep = pte_offset_kernel ( pmdp , addr ) ;
if ( pte_none ( * ptep ) )
return NULL ;
return ptep ;
}
2011-03-15 19:08:22 +03:00
static void change_page_attr ( unsigned long addr , int numpages ,
pte_t ( * set ) ( pte_t ) )
{
pte_t * ptep , pte ;
int i ;
for ( i = 0 ; i < numpages ; i + + ) {
2012-10-01 18:18:46 +04:00
ptep = walk_page_table ( addr ) ;
if ( WARN_ON_ONCE ( ! ptep ) )
break ;
2011-03-15 19:08:22 +03:00
pte = * ptep ;
pte = set ( pte ) ;
2011-05-23 12:24:40 +04:00
__ptep_ipte ( addr , ptep ) ;
2011-03-15 19:08:22 +03:00
* ptep = pte ;
2011-04-20 12:15:32 +04:00
addr + = PAGE_SIZE ;
2011-03-15 19:08:22 +03:00
}
}
int set_memory_ro ( unsigned long addr , int numpages )
{
change_page_attr ( addr , numpages , pte_wrprotect ) ;
return 0 ;
}
int set_memory_rw ( unsigned long addr , int numpages )
{
change_page_attr ( addr , numpages , pte_mkwrite ) ;
return 0 ;
}
/* not possible */
int set_memory_nx ( unsigned long addr , int numpages )
{
return 0 ;
}
2011-05-20 02:55:26 +04:00
int set_memory_x ( unsigned long addr , int numpages )
{
return 0 ;
}
2012-11-02 16:28:48 +04:00
# ifdef CONFIG_DEBUG_PAGEALLOC
void kernel_map_pages ( struct page * page , int numpages , int enable )
{
unsigned long address ;
pgd_t * pgd ;
pud_t * pud ;
pmd_t * pmd ;
pte_t * pte ;
int i ;
for ( i = 0 ; i < numpages ; i + + ) {
address = page_to_phys ( page + i ) ;
pgd = pgd_offset_k ( address ) ;
pud = pud_offset ( pgd , address ) ;
pmd = pmd_offset ( pud , address ) ;
pte = pte_offset_kernel ( pmd , address ) ;
if ( ! enable ) {
__ptep_ipte ( address , pte ) ;
2013-07-23 22:57:57 +04:00
pte_val ( * pte ) = _PAGE_INVALID ;
2012-11-02 16:28:48 +04:00
continue ;
}
2012-11-07 16:17:37 +04:00
pte_val ( * pte ) = __pa ( address ) ;
2012-11-02 16:28:48 +04:00
}
}
# ifdef CONFIG_HIBERNATION
bool kernel_page_present ( struct page * page )
{
unsigned long addr ;
int cc ;
addr = page_to_phys ( page ) ;
asm volatile (
" lra %1,0(%1) \n "
" ipm %0 \n "
" srl %0,28 "
: " =d " ( cc ) , " +a " ( addr ) : : " cc " ) ;
return cc = = 0 ;
}
# endif /* CONFIG_HIBERNATION */
# endif /* CONFIG_DEBUG_PAGEALLOC */