2008-01-30 15:33:41 +03:00
/*
* Copyright 2002 Andi Kleen , SuSE Labs .
2005-04-17 02:20:36 +04:00
* Thanks to Ben LaHaise for precious feedback .
2008-01-30 15:33:41 +03:00
*/
2005-04-17 02:20:36 +04:00
# include <linux/highmem.h>
2008-01-30 15:34:04 +03:00
# include <linux/bootmem.h>
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
2008-01-30 15:33:41 +03:00
# include <linux/sched.h>
2005-04-17 02:20:36 +04:00
# include <linux/slab.h>
2008-01-30 15:33:41 +03:00
# include <linux/mm.h>
2008-01-30 15:34:06 +03:00
# include <asm/e820.h>
2005-04-17 02:20:36 +04:00
# include <asm/processor.h>
# include <asm/tlbflush.h>
2006-01-06 11:12:10 +03:00
# include <asm/sections.h>
2008-01-30 15:33:41 +03:00
# include <asm/uaccess.h>
# include <asm/pgalloc.h>
2005-04-17 02:20:36 +04:00
2008-01-30 15:34:04 +03:00
static inline int
within ( unsigned long addr , unsigned long start , unsigned long end )
2008-01-30 15:34:04 +03:00
{
2008-01-30 15:34:04 +03:00
return addr > = start & & addr < end ;
}
2008-01-30 15:34:07 +03:00
/*
* Flushing functions
*/
void clflush_cache_range ( void * addr , int size )
{
int i ;
for ( i = 0 ; i < size ; i + = boot_cpu_data . x86_clflush_size )
clflush ( addr + i ) ;
}
static void flush_kernel_map ( void * arg )
{
/*
* Flush all to work around Errata in early athlons regarding
* large page flushing .
*/
__flush_tlb_all ( ) ;
if ( boot_cpu_data . x86_model > = 4 )
wbinvd ( ) ;
}
static void global_flush_tlb ( void )
{
BUG_ON ( irqs_disabled ( ) ) ;
on_each_cpu ( flush_kernel_map , NULL , 1 , 1 ) ;
}
2008-01-30 15:34:04 +03:00
/*
* Certain areas of memory on x86 require very specific protection flags ,
* for example the BIOS area or kernel text . Callers don ' t always get this
* right ( again , ioremap ( ) on BIOS memory is not uncommon ) so this function
* checks and fixes these known static required protection bits .
*/
static inline pgprot_t static_protections ( pgprot_t prot , unsigned long address )
{
pgprot_t forbidden = __pgprot ( 0 ) ;
2008-01-30 15:34:04 +03:00
/*
2008-01-30 15:34:04 +03:00
* The BIOS area between 640 k and 1 Mb needs to be executable for
* PCI BIOS based config access ( CONFIG_PCI_GOBIOS ) support .
2008-01-30 15:34:04 +03:00
*/
2008-01-30 15:34:04 +03:00
if ( within ( __pa ( address ) , BIOS_BEGIN , BIOS_END ) )
pgprot_val ( forbidden ) | = _PAGE_NX ;
/*
* The kernel text needs to be executable for obvious reasons
* Does not cover __inittext since that is gone later on
*/
if ( within ( address , ( unsigned long ) _text , ( unsigned long ) _etext ) )
pgprot_val ( forbidden ) | = _PAGE_NX ;
# ifdef CONFIG_DEBUG_RODATA
/* The .rodata section needs to be read-only */
if ( within ( address , ( unsigned long ) __start_rodata ,
( unsigned long ) __end_rodata ) )
pgprot_val ( forbidden ) | = _PAGE_RW ;
# endif
prot = __pgprot ( pgprot_val ( prot ) & ~ pgprot_val ( forbidden ) ) ;
2008-01-30 15:34:04 +03:00
return prot ;
}
2008-01-30 15:33:43 +03:00
pte_t * lookup_address ( unsigned long address , int * level )
2008-01-30 15:33:41 +03:00
{
2005-04-17 02:20:36 +04:00
pgd_t * pgd = pgd_offset_k ( address ) ;
pud_t * pud ;
pmd_t * pmd ;
2008-01-30 15:33:41 +03:00
2008-01-30 15:34:04 +03:00
* level = PG_LEVEL_NONE ;
2005-04-17 02:20:36 +04:00
if ( pgd_none ( * pgd ) )
return NULL ;
pud = pud_offset ( pgd , address ) ;
if ( pud_none ( * pud ) )
return NULL ;
pmd = pmd_offset ( pud , address ) ;
if ( pmd_none ( * pmd ) )
return NULL ;
2008-01-30 15:34:04 +03:00
* level = PG_LEVEL_2M ;
2005-04-17 02:20:36 +04:00
if ( pmd_large ( * pmd ) )
return ( pte_t * ) pmd ;
2008-01-30 15:34:04 +03:00
* level = PG_LEVEL_4K ;
2008-01-30 15:33:41 +03:00
return pte_offset_kernel ( pmd , address ) ;
}
2008-01-30 15:33:57 +03:00
static void __set_pmd_pte ( pte_t * kpte , unsigned long address , pte_t pte )
2008-01-30 15:33:41 +03:00
{
/* change init_mm */
set_pte_atomic ( kpte , pte ) ;
2008-01-30 15:34:03 +03:00
# ifdef CONFIG_X86_32
2008-01-30 15:34:04 +03:00
if ( ! SHARED_KERNEL_PMD ) {
2008-01-30 15:34:03 +03:00
struct page * page ;
for ( page = pgd_list ; page ; page = ( struct page * ) page - > index ) {
pgd_t * pgd ;
pud_t * pud ;
pmd_t * pmd ;
pgd = ( pgd_t * ) page_address ( page ) + pgd_index ( address ) ;
pud = pud_offset ( pgd , address ) ;
pmd = pmd_offset ( pud , address ) ;
set_pte_atomic ( ( pte_t * ) pmd , pte ) ;
}
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:34:03 +03:00
# endif
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:33:57 +03:00
static int split_large_page ( pte_t * kpte , unsigned long address )
2008-01-30 15:33:56 +03:00
{
2008-01-30 15:33:57 +03:00
pgprot_t ref_prot = pte_pgprot ( pte_clrhuge ( * kpte ) ) ;
2008-01-30 15:33:58 +03:00
gfp_t gfp_flags = GFP_KERNEL ;
2008-01-30 15:33:57 +03:00
unsigned long flags ;
2008-01-30 15:33:56 +03:00
unsigned long addr ;
pte_t * pbase , * tmp ;
struct page * base ;
2008-01-30 15:33:57 +03:00
int i , level ;
2008-01-30 15:33:56 +03:00
2008-01-30 15:33:58 +03:00
# ifdef CONFIG_DEBUG_PAGEALLOC
gfp_flags = GFP_ATOMIC ;
# endif
base = alloc_pages ( gfp_flags , 0 ) ;
2008-01-30 15:33:56 +03:00
if ( ! base )
return - ENOMEM ;
2008-01-30 15:33:57 +03:00
spin_lock_irqsave ( & pgd_lock , flags ) ;
2008-01-30 15:33:56 +03:00
/*
* Check for races , another CPU might have split this page
* up for us already :
*/
tmp = lookup_address ( address , & level ) ;
2008-01-30 15:33:56 +03:00
if ( tmp ! = kpte ) {
WARN_ON_ONCE ( 1 ) ;
2008-01-30 15:33:56 +03:00
goto out_unlock ;
2008-01-30 15:33:56 +03:00
}
2008-01-30 15:33:56 +03:00
address = __pa ( address ) ;
addr = address & LARGE_PAGE_MASK ;
pbase = ( pte_t * ) page_address ( base ) ;
2008-01-30 15:34:03 +03:00
# ifdef CONFIG_X86_32
2008-01-30 15:33:56 +03:00
paravirt_alloc_pt ( & init_mm , page_to_pfn ( base ) ) ;
2008-01-30 15:34:03 +03:00
# endif
2008-01-30 15:33:56 +03:00
for ( i = 0 ; i < PTRS_PER_PTE ; i + + , addr + = PAGE_SIZE )
set_pte ( & pbase [ i ] , pfn_pte ( addr > > PAGE_SHIFT , ref_prot ) ) ;
/*
2008-01-30 15:34:04 +03:00
* Install the new , split up pagetable . Important detail here :
*
* On Intel the NX bit of all levels must be cleared to make a
* page executable . See section 4.13 .2 of Intel 64 and IA - 32
* Architectures Software Developer ' s Manual ) .
2008-01-30 15:33:56 +03:00
*/
2008-01-30 15:34:04 +03:00
ref_prot = pte_pgprot ( pte_mkexec ( pte_clrhuge ( * kpte ) ) ) ;
2008-01-30 15:33:57 +03:00
__set_pmd_pte ( kpte , address , mk_pte ( base , ref_prot ) ) ;
2008-01-30 15:33:56 +03:00
base = NULL ;
out_unlock :
2008-01-30 15:33:57 +03:00
spin_unlock_irqrestore ( & pgd_lock , flags ) ;
2008-01-30 15:33:56 +03:00
if ( base )
__free_pages ( base , 0 ) ;
return 0 ;
}
2008-01-30 15:34:03 +03:00
static int
2008-01-30 15:34:04 +03:00
__change_page_attr ( unsigned long address , unsigned long pfn , pgprot_t prot )
2008-01-30 15:33:41 +03:00
{
2005-04-17 02:20:36 +04:00
struct page * kpte_page ;
2008-01-30 15:33:56 +03:00
int level , err = 0 ;
2008-01-30 15:33:41 +03:00
pte_t * kpte ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:34:04 +03:00
# ifdef CONFIG_X86_32
BUG_ON ( pfn > max_low_pfn ) ;
# endif
2005-04-17 02:20:36 +04:00
2008-01-30 15:33:55 +03:00
repeat :
2008-01-30 15:33:43 +03:00
kpte = lookup_address ( address , & level ) ;
2005-04-17 02:20:36 +04:00
if ( ! kpte )
return - EINVAL ;
2008-01-30 15:33:41 +03:00
2005-04-17 02:20:36 +04:00
kpte_page = virt_to_page ( kpte ) ;
2007-07-21 19:09:51 +04:00
BUG_ON ( PageLRU ( kpte_page ) ) ;
BUG_ON ( PageCompound ( kpte_page ) ) ;
2008-01-30 15:34:04 +03:00
prot = static_protections ( prot , address ) ;
2007-07-21 19:09:51 +04:00
2008-01-30 15:34:04 +03:00
if ( level = = PG_LEVEL_4K ) {
2008-01-30 15:34:07 +03:00
WARN_ON_ONCE ( pgprot_val ( prot ) & _PAGE_PSE ) ;
2008-01-30 15:34:04 +03:00
set_pte_atomic ( kpte , pfn_pte ( pfn , canon_pgprot ( prot ) ) ) ;
2008-01-30 15:33:55 +03:00
} else {
2008-01-30 15:34:07 +03:00
/* Clear the PSE bit for the 4k level pages ! */
pgprot_val ( prot ) = pgprot_val ( prot ) & ~ _PAGE_PSE ;
2008-01-30 15:33:57 +03:00
err = split_large_page ( kpte , address ) ;
2008-01-30 15:33:56 +03:00
if ( ! err )
goto repeat ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:33:56 +03:00
return err ;
2008-01-30 15:33:41 +03:00
}
2005-04-17 02:20:36 +04:00
2008-01-30 15:34:03 +03:00
/**
* change_page_attr_addr - Change page table attributes in linear mapping
* @ address : Virtual address in linear mapping .
* @ prot : New page table attribute ( PAGE_ * )
2005-04-17 02:20:36 +04:00
*
2008-01-30 15:34:03 +03:00
* Change page attributes of a page in the direct mapping . This is a variant
* of change_page_attr ( ) that also works on memory holes that do not have
* mem_map entry ( pfn_valid ( ) is false ) .
2008-01-30 15:33:41 +03:00
*
2008-01-30 15:34:03 +03:00
* See change_page_attr ( ) documentation for more details .
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
*
* Modules and drivers should use the set_memory_ * APIs instead .
2005-04-17 02:20:36 +04:00
*/
2008-01-30 15:34:03 +03:00
2008-01-30 15:34:07 +03:00
static int change_page_attr_addr ( unsigned long address , pgprot_t prot )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:34:07 +03:00
int err = 0 , kernel_map = 0 ;
unsigned long pfn = __pa ( address ) > > PAGE_SHIFT ;
2008-01-30 15:34:03 +03:00
# ifdef CONFIG_X86_64
if ( address > = __START_KERNEL_map & &
address < __START_KERNEL_map + KERNEL_TEXT_SIZE ) {
2005-04-17 02:20:36 +04:00
2008-01-30 15:34:03 +03:00
address = ( unsigned long ) __va ( __pa ( address ) ) ;
kernel_map = 1 ;
}
# endif
2008-01-30 15:34:07 +03:00
if ( ! kernel_map | | pte_present ( pfn_pte ( 0 , prot ) ) ) {
err = __change_page_attr ( address , pfn , prot ) ;
if ( err )
return err ;
}
2008-01-30 15:34:03 +03:00
# ifdef CONFIG_X86_64
2008-01-30 15:34:07 +03:00
/*
* Handle kernel mapping too which aliases part of
* lowmem :
*/
if ( __pa ( address ) < KERNEL_TEXT_SIZE ) {
unsigned long addr2 ;
pgprot_t prot2 ;
addr2 = __START_KERNEL_map + __pa ( address ) ;
/* Make sure the kernel mappings stay executable */
prot2 = pte_pgprot ( pte_mkexec ( pfn_pte ( 0 , prot ) ) ) ;
err = __change_page_attr ( addr2 , pfn , prot2 ) ;
2008-01-30 15:33:41 +03:00
}
2008-01-30 15:34:07 +03:00
# endif
2008-01-30 15:33:41 +03:00
2005-04-17 02:20:36 +04:00
return err ;
}
2008-01-30 15:34:08 +03:00
static int __change_page_attr_set_clr ( unsigned long addr , int numpages ,
pgprot_t mask_set , pgprot_t mask_clr )
{
pgprot_t new_prot ;
int level ;
pte_t * pte ;
int i , ret ;
for ( i = 0 ; i < numpages ; i + + ) {
pte = lookup_address ( addr , & level ) ;
if ( ! pte )
return - EINVAL ;
new_prot = pte_pgprot ( * pte ) ;
pgprot_val ( new_prot ) & = ~ pgprot_val ( mask_clr ) ;
pgprot_val ( new_prot ) | = pgprot_val ( mask_set ) ;
ret = change_page_attr_addr ( addr , new_prot ) ;
if ( ret )
return ret ;
addr + = PAGE_SIZE ;
}
return 0 ;
}
static int change_page_attr_set_clr ( unsigned long addr , int numpages ,
pgprot_t mask_set , pgprot_t mask_clr )
{
int ret = __change_page_attr_set_clr ( addr , numpages , mask_set ,
mask_clr ) ;
global_flush_tlb ( ) ;
return ret ;
}
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
/**
* change_page_attr_set - Change page table attributes in the linear mapping .
* @ addr : Virtual address in linear mapping .
* @ numpages : Number of pages to change
* @ prot : Protection / caching type bits to set ( PAGE_ * )
*
* Returns 0 on success , otherwise a negated errno .
*
* This should be used when a page is mapped with a different caching policy
* than write - back somewhere - some CPUs do not like it when mappings with
* different caching policies exist . This changes the page attributes of the
* in kernel linear mapping too .
*
* The caller needs to ensure that there are no conflicting mappings elsewhere
* ( e . g . in user space ) * This function only deals with the kernel linear map .
*
* This function is different from change_page_attr ( ) in that only selected bits
* are impacted , all other bits remain as is .
*/
2008-01-30 15:34:08 +03:00
static int __change_page_attr_set ( unsigned long addr , int numpages ,
pgprot_t prot )
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
{
2008-01-30 15:34:07 +03:00
pgprot_t current_prot , new_prot ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
int level ;
pte_t * pte ;
2008-01-30 15:34:07 +03:00
int i , ret ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
2008-01-30 15:34:07 +03:00
for ( i = 0 ; i < numpages ; i + + ) {
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
2008-01-30 15:34:07 +03:00
pte = lookup_address ( addr , & level ) ;
2008-01-30 15:34:08 +03:00
if ( ! pte )
return - EINVAL ;
current_prot = pte_pgprot ( * pte ) ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
2008-01-30 15:34:07 +03:00
pgprot_val ( new_prot ) =
pgprot_val ( current_prot ) | pgprot_val ( prot ) ;
2008-01-30 15:34:07 +03:00
2008-01-30 15:34:07 +03:00
ret = change_page_attr_addr ( addr , new_prot ) ;
2008-01-30 15:34:07 +03:00
if ( ret )
return ret ;
addr + = PAGE_SIZE ;
}
2008-01-30 15:34:08 +03:00
2008-01-30 15:34:07 +03:00
return 0 ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
}
2008-01-30 15:34:08 +03:00
static int change_page_attr_set ( unsigned long addr , int numpages , pgprot_t prot )
{
int ret = __change_page_attr_set ( addr , numpages , prot ) ;
global_flush_tlb ( ) ;
return ret ;
}
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
/**
* change_page_attr_clear - Change page table attributes in the linear mapping .
* @ addr : Virtual address in linear mapping .
* @ numpages : Number of pages to change
* @ prot : Protection / caching type bits to clear ( PAGE_ * )
*
* Returns 0 on success , otherwise a negated errno .
*
* This should be used when a page is mapped with a different caching policy
* than write - back somewhere - some CPUs do not like it when mappings with
* different caching policies exist . This changes the page attributes of the
* in kernel linear mapping too .
*
* The caller needs to ensure that there are no conflicting mappings elsewhere
* ( e . g . in user space ) * This function only deals with the kernel linear map .
*
* This function is different from change_page_attr ( ) in that only selected bits
* are impacted , all other bits remain as is .
*/
2008-01-30 15:34:08 +03:00
static int __change_page_attr_clear ( unsigned long addr , int numpages ,
pgprot_t prot )
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
{
2008-01-30 15:34:07 +03:00
pgprot_t current_prot , new_prot ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
int level ;
pte_t * pte ;
2008-01-30 15:34:07 +03:00
int i , ret ;
for ( i = 0 ; i < numpages ; i + + ) {
2008-01-30 15:34:08 +03:00
2008-01-30 15:34:07 +03:00
pte = lookup_address ( addr , & level ) ;
2008-01-30 15:34:08 +03:00
if ( ! pte )
return - EINVAL ;
current_prot = pte_pgprot ( * pte ) ;
2008-01-30 15:34:07 +03:00
2008-01-30 15:34:07 +03:00
pgprot_val ( new_prot ) =
2008-01-30 15:34:07 +03:00
pgprot_val ( current_prot ) & ~ pgprot_val ( prot ) ;
2008-01-30 15:34:07 +03:00
ret = change_page_attr_addr ( addr , new_prot ) ;
2008-01-30 15:34:07 +03:00
if ( ret )
return ret ;
addr + = PAGE_SIZE ;
}
2008-01-30 15:34:08 +03:00
2008-01-30 15:34:07 +03:00
return 0 ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
}
2008-01-30 15:34:08 +03:00
static int change_page_attr_clear ( unsigned long addr , int numpages ,
pgprot_t prot )
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
{
2008-01-30 15:34:08 +03:00
int ret = __change_page_attr_clear ( addr , numpages , prot ) ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
2008-01-30 15:34:07 +03:00
global_flush_tlb ( ) ;
2008-01-30 15:34:08 +03:00
return ret ;
}
int set_memory_uc ( unsigned long addr , int numpages )
{
return change_page_attr_set ( addr , numpages ,
__pgprot ( _PAGE_PCD | _PAGE_PWT ) ) ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
}
EXPORT_SYMBOL ( set_memory_uc ) ;
int set_memory_wb ( unsigned long addr , int numpages )
{
2008-01-30 15:34:08 +03:00
return change_page_attr_clear ( addr , numpages ,
__pgprot ( _PAGE_PCD | _PAGE_PWT ) ) ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
}
EXPORT_SYMBOL ( set_memory_wb ) ;
int set_memory_x ( unsigned long addr , int numpages )
{
2008-01-30 15:34:08 +03:00
return change_page_attr_clear ( addr , numpages , __pgprot ( _PAGE_NX ) ) ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
}
EXPORT_SYMBOL ( set_memory_x ) ;
int set_memory_nx ( unsigned long addr , int numpages )
{
2008-01-30 15:34:08 +03:00
return change_page_attr_set ( addr , numpages , __pgprot ( _PAGE_NX ) ) ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
}
EXPORT_SYMBOL ( set_memory_nx ) ;
int set_memory_ro ( unsigned long addr , int numpages )
{
2008-01-30 15:34:08 +03:00
return change_page_attr_clear ( addr , numpages , __pgprot ( _PAGE_RW ) ) ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
}
int set_memory_rw ( unsigned long addr , int numpages )
{
2008-01-30 15:34:08 +03:00
return change_page_attr_set ( addr , numpages , __pgprot ( _PAGE_RW ) ) ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
}
2008-01-30 15:34:07 +03:00
int set_memory_np ( unsigned long addr , int numpages )
{
2008-01-30 15:34:08 +03:00
return change_page_attr_clear ( addr , numpages , __pgprot ( _PAGE_PRESENT ) ) ;
2008-01-30 15:34:07 +03:00
}
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
int set_pages_uc ( struct page * page , int numpages )
{
unsigned long addr = ( unsigned long ) page_address ( page ) ;
2008-01-30 15:34:07 +03:00
return set_memory_uc ( addr , numpages ) ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
}
EXPORT_SYMBOL ( set_pages_uc ) ;
int set_pages_wb ( struct page * page , int numpages )
{
unsigned long addr = ( unsigned long ) page_address ( page ) ;
2008-01-30 15:34:07 +03:00
return set_memory_wb ( addr , numpages ) ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
}
EXPORT_SYMBOL ( set_pages_wb ) ;
int set_pages_x ( struct page * page , int numpages )
{
unsigned long addr = ( unsigned long ) page_address ( page ) ;
2008-01-30 15:34:07 +03:00
return set_memory_x ( addr , numpages ) ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
}
EXPORT_SYMBOL ( set_pages_x ) ;
int set_pages_nx ( struct page * page , int numpages )
{
unsigned long addr = ( unsigned long ) page_address ( page ) ;
2008-01-30 15:34:07 +03:00
return set_memory_nx ( addr , numpages ) ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
}
EXPORT_SYMBOL ( set_pages_nx ) ;
int set_pages_ro ( struct page * page , int numpages )
{
unsigned long addr = ( unsigned long ) page_address ( page ) ;
2008-01-30 15:34:07 +03:00
return set_memory_ro ( addr , numpages ) ;
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-01-30 15:34:06 +03:00
}
int set_pages_rw ( struct page * page , int numpages )
{
unsigned long addr = ( unsigned long ) page_address ( page ) ;
2008-01-30 15:34:06 +03:00
2008-01-30 15:34:07 +03:00
return set_memory_rw ( addr , numpages ) ;
2008-01-30 15:33:55 +03:00
}
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_DEBUG_PAGEALLOC
2008-01-30 15:34:07 +03:00
static int __set_pages_p ( struct page * page , int numpages )
{
unsigned long addr = ( unsigned long ) page_address ( page ) ;
2008-01-30 15:34:08 +03:00
return __change_page_attr_set ( addr , numpages ,
__pgprot ( _PAGE_PRESENT | _PAGE_RW ) ) ;
2008-01-30 15:34:07 +03:00
}
static int __set_pages_np ( struct page * page , int numpages )
{
unsigned long addr = ( unsigned long ) page_address ( page ) ;
2008-01-30 15:34:08 +03:00
return __change_page_attr_clear ( addr , numpages ,
__pgprot ( _PAGE_PRESENT ) ) ;
2008-01-30 15:34:07 +03:00
}
2005-04-17 02:20:36 +04:00
void kernel_map_pages ( struct page * page , int numpages , int enable )
{
if ( PageHighMem ( page ) )
return ;
2008-01-30 15:33:41 +03:00
if ( ! enable ) {
2006-06-27 13:54:49 +04:00
debug_check_no_locks_freed ( page_address ( page ) ,
numpages * PAGE_SIZE ) ;
2008-01-30 15:33:41 +03:00
}
2006-01-10 02:59:21 +03:00
2008-01-30 15:33:58 +03:00
/*
* If page allocator is not up yet then do not call c_p_a ( ) :
*/
if ( ! debug_pagealloc_enabled )
return ;
2008-01-30 15:33:41 +03:00
/*
2008-01-30 15:34:04 +03:00
* The return value is ignored - the calls cannot fail ,
* large pages are disabled at boot time :
2005-04-17 02:20:36 +04:00
*/
2008-01-30 15:34:07 +03:00
if ( enable )
__set_pages_p ( page , numpages ) ;
else
__set_pages_np ( page , numpages ) ;
2008-01-30 15:33:41 +03:00
/*
2008-01-30 15:34:04 +03:00
* We should perform an IPI and flush all tlbs ,
* but that can deadlock - > flush only current cpu :
2005-04-17 02:20:36 +04:00
*/
__flush_tlb_all ( ) ;
}
# endif
2008-01-30 15:34:07 +03:00
/*
* The testcases use internal knowledge of the implementation that shouldn ' t
* be exposed to the rest of the kernel . Include these directly here .
*/
# ifdef CONFIG_CPA_DEBUG
# include "pageattr-test.c"
# endif