2014-11-29 03:02:11 +01:00
/*
* Copyright ( c ) 2014 , The Linux Foundation . All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation .
*
* 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 .
*/
# include <linux/mm.h>
# include <linux/module.h>
# include <asm/pgtable.h>
# include <asm/tlbflush.h>
struct page_change_data {
pgprot_t set_mask ;
pgprot_t clear_mask ;
} ;
static int change_page_range ( pte_t * ptep , pgtable_t token , unsigned long addr ,
void * data )
{
struct page_change_data * cdata = data ;
pte_t pte = * ptep ;
pte = clear_pte_bit ( pte , cdata - > clear_mask ) ;
pte = set_pte_bit ( pte , cdata - > set_mask ) ;
set_pte_ext ( ptep , pte , 0 ) ;
return 0 ;
}
2016-11-21 16:02:08 +00:00
static bool in_range ( unsigned long start , unsigned long size ,
unsigned long range_start , unsigned long range_end )
{
return start > = range_start & & start < range_end & &
size < = range_end - start ;
}
2014-11-29 03:02:11 +01:00
static int change_memory_common ( unsigned long addr , int numpages ,
pgprot_t set_mask , pgprot_t clear_mask )
{
2016-11-21 16:02:08 +00:00
unsigned long start = addr & PAGE_MASK ;
unsigned long end = PAGE_ALIGN ( addr ) + numpages * PAGE_SIZE ;
unsigned long size = end - start ;
2014-11-29 03:02:11 +01:00
int ret ;
struct page_change_data data ;
2016-11-21 16:02:08 +00:00
WARN_ON_ONCE ( start ! = addr ) ;
2014-11-29 03:02:11 +01:00
2016-11-21 16:02:08 +00:00
if ( ! size )
2016-02-22 17:56:52 +01:00
return 0 ;
2016-11-21 16:07:05 +00:00
if ( ! in_range ( start , size , MODULES_VADDR , MODULES_END ) & &
! in_range ( start , size , VMALLOC_START , VMALLOC_END ) )
2014-11-29 03:02:11 +01:00
return - EINVAL ;
data . set_mask = set_mask ;
data . clear_mask = clear_mask ;
ret = apply_to_page_range ( & init_mm , start , size , change_page_range ,
& data ) ;
flush_tlb_kernel_range ( start , end ) ;
return ret ;
}
int set_memory_ro ( unsigned long addr , int numpages )
{
return change_memory_common ( addr , numpages ,
__pgprot ( L_PTE_RDONLY ) ,
__pgprot ( 0 ) ) ;
}
int set_memory_rw ( unsigned long addr , int numpages )
{
return change_memory_common ( addr , numpages ,
__pgprot ( 0 ) ,
__pgprot ( L_PTE_RDONLY ) ) ;
}
int set_memory_nx ( unsigned long addr , int numpages )
{
return change_memory_common ( addr , numpages ,
__pgprot ( L_PTE_XN ) ,
__pgprot ( 0 ) ) ;
}
int set_memory_x ( unsigned long addr , int numpages )
{
return change_memory_common ( addr , numpages ,
__pgprot ( 0 ) ,
__pgprot ( L_PTE_XN ) ) ;
}