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
*/
2008-01-30 15:34:08 +03:00
/**
* clflush_cache_range - flush a cache range with clflush
* @ addr : virtual start address
* @ size : number of bytes to flush
*
* clflush is an unordered instruction which needs fencing with mfence
* to avoid ordering issues .
*/
2008-01-30 15:34:09 +03:00
void clflush_cache_range ( void * vaddr , unsigned int size )
2008-01-30 15:34:07 +03:00
{
2008-01-30 15:34:09 +03:00
void * vend = vaddr + size - 1 ;
2008-01-30 15:34:07 +03:00
2008-01-30 15:34:08 +03:00
mb ( ) ;
2008-01-30 15:34:09 +03:00
for ( ; vaddr < vend ; vaddr + = boot_cpu_data . x86_clflush_size )
clflush ( vaddr ) ;
/*
* Flush any possible final partial cacheline :
*/
clflush ( vend ) ;
2008-01-30 15:34:08 +03:00
mb ( ) ;
2008-01-30 15:34:07 +03:00
}
2008-01-30 15:34:08 +03:00
static void __cpa_flush_all ( void * arg )
2008-01-30 15:34:07 +03:00
{
/*
* Flush all to work around Errata in early athlons regarding
* large page flushing .
*/
__flush_tlb_all ( ) ;
if ( boot_cpu_data . x86_model > = 4 )
wbinvd ( ) ;
}
2008-01-30 15:34:08 +03:00
static void cpa_flush_all ( void )
2008-01-30 15:34:07 +03:00
{
BUG_ON ( irqs_disabled ( ) ) ;
2008-01-30 15:34:08 +03:00
on_each_cpu ( __cpa_flush_all , NULL , 1 , 1 ) ;
2008-01-30 15:34:07 +03:00
}
2008-01-30 15:34:08 +03:00
static void __cpa_flush_range ( void * arg )
{
/*
* We could optimize that further and do individual per page
* tlb invalidates for a low number of pages . Caveat : we must
* flush the high aliases on 64 bit as well .
*/
__flush_tlb_all ( ) ;
}
2008-01-30 15:34:09 +03:00
static void cpa_flush_range ( unsigned long start , int numpages )
2008-01-30 15:34:08 +03:00
{
2008-01-30 15:34:09 +03:00
unsigned int i , level ;
unsigned long addr ;
2008-01-30 15:34:08 +03:00
BUG_ON ( irqs_disabled ( ) ) ;
2008-01-30 15:34:09 +03:00
WARN_ON ( PAGE_ALIGN ( start ) ! = start ) ;
2008-01-30 15:34:08 +03:00
2008-01-30 15:34:08 +03:00
on_each_cpu ( __cpa_flush_range , NULL , 1 , 1 ) ;
2008-01-30 15:34:08 +03:00
2008-01-30 15:34:08 +03:00
/*
* We only need to flush on one CPU ,
* clflush is a MESI - coherent instruction that
* will cause all other CPUs to flush the same
* cachelines :
*/
2008-01-30 15:34:09 +03:00
for ( i = 0 , addr = start ; i < numpages ; i + + , addr + = PAGE_SIZE ) {
pte_t * pte = lookup_address ( addr , & level ) ;
/*
* Only flush present addresses :
*/
if ( pte & & pte_present ( * pte ) )
clflush_cache_range ( ( void * ) addr , PAGE_SIZE ) ;
}
2008-01-30 15:34:08 +03:00
}
2008-02-04 18:48:05 +03:00
# define HIGH_MAP_START __START_KERNEL_map
# define HIGH_MAP_END (__START_KERNEL_map + KERNEL_TEXT_SIZE)
/*
* Converts a virtual address to a X86 - 64 highmap address
*/
static unsigned long virt_to_highmap ( void * address )
{
# ifdef CONFIG_X86_64
return __pa ( ( unsigned long ) address ) + HIGH_MAP_START - phys_base ;
# else
return ( unsigned long ) address ;
# endif
}
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 ;
2008-02-04 18:48:05 +03:00
/*
* Do the same for the x86 - 64 high kernel mapping
*/
if ( within ( address , virt_to_highmap ( _text ) , virt_to_highmap ( _etext ) ) )
pgprot_val ( forbidden ) | = _PAGE_NX ;
2008-01-30 15:34:04 +03:00
# 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 ;
2008-02-04 18:48:05 +03:00
/*
* Do the same for the x86 - 64 high kernel mapping
*/
if ( within ( address , virt_to_highmap ( __start_rodata ) ,
virt_to_highmap ( __end_rodata ) ) )
pgprot_val ( forbidden ) | = _PAGE_RW ;
2008-01-30 15:34:04 +03:00
# 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 ;
2008-01-30 15:34:11 +03:00
list_for_each_entry ( page , & pgd_list , lru ) {
2008-01-30 15:34:03 +03:00
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:34:09 +03:00
unsigned int i , level ;
2008-01-30 15:33:56 +03:00
2008-01-30 15:33:58 +03:00
# ifdef CONFIG_DEBUG_PAGEALLOC
2008-01-30 15:34:09 +03:00
gfp_flags = __GFP_HIGH | __GFP_NOFAIL | __GFP_NOWARN ;
gfp_flags = GFP_ATOMIC | __GFP_NOWARN ;
2008-01-30 15:33:58 +03:00
# 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-02-04 18:48:05 +03:00
__change_page_attr ( unsigned long address , pgprot_t mask_set , pgprot_t mask_clr )
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: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
if ( level = = PG_LEVEL_4K ) {
2008-01-30 15:34:09 +03:00
pte_t new_pte , old_pte = * kpte ;
2008-02-04 18:48:05 +03:00
pgprot_t new_prot = pte_pgprot ( old_pte ) ;
if ( ! pte_val ( old_pte ) ) {
WARN_ON_ONCE ( 1 ) ;
return - EINVAL ;
}
2008-01-30 15:34:09 +03:00
pgprot_val ( new_prot ) & = ~ pgprot_val ( mask_clr ) ;
pgprot_val ( new_prot ) | = pgprot_val ( mask_set ) ;
new_prot = static_protections ( new_prot , address ) ;
2008-02-04 18:48:05 +03:00
/*
* We need to keep the pfn from the existing PTE ,
* after all we ' re only going to change it ' s attributes
* not the memory it points to
*/
new_pte = pfn_pte ( pte_pfn ( old_pte ) , canon_pgprot ( new_prot ) ) ;
2008-01-30 15:34:09 +03:00
set_pte_atomic ( kpte , new_pte ) ;
} else {
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:09 +03:00
2008-01-30 15:34:09 +03:00
static int
change_page_attr_addr ( unsigned long address , pgprot_t mask_set ,
2008-01-30 15:34:09 +03:00
pgprot_t mask_clr )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:34:09 +03:00
int err ;
2008-01-30 15:34:03 +03:00
# ifdef CONFIG_X86_64
2008-02-04 18:48:05 +03:00
unsigned long phys_addr = __pa ( address ) ;
2008-01-30 15:34:09 +03:00
/*
* If we are inside the high mapped kernel range , then we
* fixup the low mapping first . __va ( ) returns the virtual
* address in the linear mapping :
*/
if ( within ( address , HIGH_MAP_START , HIGH_MAP_END ) )
address = ( unsigned long ) __va ( phys_addr ) ;
2008-01-30 15:34:03 +03:00
# endif
2008-02-04 18:48:05 +03:00
err = __change_page_attr ( address , mask_set , mask_clr ) ;
2008-01-30 15:34:09 +03:00
if ( err )
return err ;
2008-01-30 15:34:03 +03:00
# ifdef CONFIG_X86_64
2008-01-30 15:34:07 +03:00
/*
2008-01-30 15:34:09 +03:00
* If the physical address is inside the kernel map , we need
* to touch the high mapped kernel as well :
2008-01-30 15:34:07 +03:00
*/
2008-01-30 15:34:09 +03:00
if ( within ( phys_addr , 0 , KERNEL_TEXT_SIZE ) ) {
/*
* Calc the high mapping address . See __phys_addr ( )
* for the non obvious details .
2008-02-04 18:48:05 +03:00
*
* Note that NX and other required permissions are
* checked in static_protections ( ) .
2008-01-30 15:34:09 +03:00
*/
address = phys_addr + HIGH_MAP_START - phys_base ;
2008-01-30 15:34:09 +03:00
/*
2008-01-30 15:34:09 +03:00
* Our high aliases are imprecise , because we check
* everything between 0 and KERNEL_TEXT_SIZE , so do
* not propagate lookup failures back to users :
2008-01-30 15:34:09 +03:00
*/
2008-02-04 18:48:05 +03:00
__change_page_attr ( address , mask_set , mask_clr ) ;
2008-01-30 15:33:41 +03:00
}
2008-01-30 15:34:07 +03:00
# endif
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 )
{
2008-01-30 15:34:09 +03:00
unsigned int i ;
int ret ;
2008-01-30 15:34:08 +03:00
2008-01-30 15:34:09 +03:00
for ( i = 0 ; i < numpages ; i + + , addr + = PAGE_SIZE ) {
ret = change_page_attr_addr ( addr , mask_set , mask_clr ) ;
2008-01-30 15:34:08 +03:00
if ( ret )
return ret ;
}
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 ) ;
2008-01-30 15:34:08 +03:00
/*
* On success we use clflush , when the CPU supports it to
* avoid the wbindv . If the CPU does not support it and in the
2008-01-30 15:34:08 +03:00
* error case we fall back to cpa_flush_all ( which uses
2008-01-30 15:34:08 +03:00
* wbindv ) :
*/
if ( ! ret & & cpu_has_clflush )
cpa_flush_range ( addr , numpages ) ;
else
2008-01-30 15:34:08 +03:00
cpa_flush_all ( ) ;
2008-01-30 15:34:08 +03:00
return ret ;
}
2008-01-30 15:34:08 +03:00
static inline int change_page_attr_set ( unsigned long addr , int numpages ,
pgprot_t mask )
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
return change_page_attr_set_clr ( addr , numpages , mask , __pgprot ( 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 inline int change_page_attr_clear ( unsigned long addr , int numpages ,
pgprot_t mask )
2008-01-30 15:34:08 +03:00
{
2008-02-01 00:05:43 +03:00
return change_page_attr_set_clr ( addr , numpages , __pgprot ( 0 ) , mask ) ;
2008-01-30 15:34:08 +03:00
}
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
2008-01-30 15:34:08 +03:00
# if defined(CONFIG_DEBUG_PAGEALLOC) || defined(CONFIG_CPA_DEBUG)
static inline int __change_page_attr_set ( unsigned long addr , int numpages ,
pgprot_t mask )
{
return __change_page_attr_set_clr ( addr , numpages , mask , __pgprot ( 0 ) ) ;
}
static inline int __change_page_attr_clear ( unsigned long addr , int numpages ,
pgprot_t mask )
{
return __change_page_attr_set_clr ( addr , numpages , __pgprot ( 0 ) , mask ) ;
}
# endif
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