2005-05-10 17:23:01 +04:00
/*
* linux / arch / arm / lib / copypage - armv4mc . S
*
* Copyright ( C ) 1995 - 2005 Russell King
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This handles the mini data cache , as found on SA11x0 and XScale
* processors . When we copy a user page page , we map it in such a way
* that accesses to this page will not touch the main data cache , but
* will be cached in the mini data cache . This prevents us thrashing
* the main data cache on page faults .
*/
# include <linux/init.h>
# include <linux/mm.h>
2008-10-31 18:08:35 +03:00
# include <linux/highmem.h>
2005-05-10 17:23:01 +04:00
# include <asm/pgtable.h>
# include <asm/tlbflush.h>
2006-12-30 18:08:50 +03:00
# include <asm/cacheflush.h>
2005-05-10 17:23:01 +04:00
2006-08-21 20:06:38 +04:00
# include "mm.h"
2005-05-10 17:23:01 +04:00
/*
* 0xffff8000 to 0xffffffff is reserved for any ARM architecture
* specific hacks for copying pages efficiently .
*/
# define minicache_pgprot __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | \
2008-09-06 23:04:59 +04:00
L_PTE_MT_MINICACHE )
2005-05-10 17:23:01 +04:00
static DEFINE_SPINLOCK ( minicache_lock ) ;
/*
2008-10-31 18:08:35 +03:00
* ARMv4 mini - dcache optimised copy_user_highpage
2005-05-10 17:23:01 +04:00
*
* We flush the destination cache lines just before we write the data into the
* corresponding address . Since the Dcache is read - allocate , this removes the
* Dcache aliasing issue . The writes will be forwarded to the write buffer ,
* and merged as appropriate .
*
* Note : We rely on all ARMv4 processors implementing the " invalidate D line "
* instruction . If your processor does not supply this , you have to write your
2008-10-31 18:08:35 +03:00
* own copy_user_highpage that does the right thing .
2005-05-10 17:23:01 +04:00
*/
2009-03-12 20:03:16 +03:00
static void __naked
2005-05-10 17:23:01 +04:00
mc_copy_user_page ( void * from , void * to )
{
asm volatile (
" stmfd sp!, {r4, lr} @ 2 \n \
mov r4 , % 2 @ 1 \ n \
ldmia % 0 ! , { r2 , r3 , ip , lr } @ 4 \ n \
1 : mcr p15 , 0 , % 1 , c7 , c6 , 1 @ 1 invalidate D line \ n \
stmia % 1 ! , { r2 , r3 , ip , lr } @ 4 \ n \
ldmia % 0 ! , { r2 , r3 , ip , lr } @ 4 + 1 \ n \
stmia % 1 ! , { r2 , r3 , ip , lr } @ 4 \ n \
ldmia % 0 ! , { r2 , r3 , ip , lr } @ 4 \ n \
mcr p15 , 0 , % 1 , c7 , c6 , 1 @ 1 invalidate D line \ n \
stmia % 1 ! , { r2 , r3 , ip , lr } @ 4 \ n \
ldmia % 0 ! , { r2 , r3 , ip , lr } @ 4 \ n \
subs r4 , r4 , # 1 @ 1 \ n \
stmia % 1 ! , { r2 , r3 , ip , lr } @ 4 \ n \
ldmneia % 0 ! , { r2 , r3 , ip , lr } @ 4 \ n \
bne 1 b @ 1 \ n \
ldmfd sp ! , { r4 , pc } @ 3 "
:
: " r " ( from ) , " r " ( to ) , " I " ( PAGE_SIZE / 64 ) ) ;
}
2009-01-18 19:24:19 +03:00
void v4_mc_copy_user_highpage ( struct page * to , struct page * from ,
2009-10-05 18:17:45 +04:00
unsigned long vaddr , struct vm_area_struct * vma )
2005-05-10 17:23:01 +04:00
{
2008-10-31 18:08:35 +03:00
void * kto = kmap_atomic ( to , KM_USER1 ) ;
2006-12-30 18:08:50 +03:00
2010-09-13 18:57:36 +04:00
if ( ! test_and_set_bit ( PG_dcache_clean , & from - > flags ) )
2008-10-31 18:08:35 +03:00
__flush_dcache_page ( page_mapping ( from ) , from ) ;
2006-12-30 18:08:50 +03:00
2005-05-10 17:23:01 +04:00
spin_lock ( & minicache_lock ) ;
2008-10-31 18:08:35 +03:00
set_pte_ext ( TOP_PTE ( 0xffff8000 ) , pfn_pte ( page_to_pfn ( from ) , minicache_pgprot ) , 0 ) ;
2005-05-17 02:36:22 +04:00
flush_tlb_kernel_page ( 0xffff8000 ) ;
2005-05-10 17:23:01 +04:00
2005-05-17 02:36:22 +04:00
mc_copy_user_page ( ( void * ) 0xffff8000 , kto ) ;
2005-05-10 17:23:01 +04:00
spin_unlock ( & minicache_lock ) ;
2008-10-31 18:08:35 +03:00
kunmap_atomic ( kto , KM_USER1 ) ;
2005-05-10 17:23:01 +04:00
}
/*
* ARMv4 optimised clear_user_page
*/
2008-10-31 19:32:19 +03:00
void v4_mc_clear_user_highpage ( struct page * page , unsigned long vaddr )
2005-05-10 17:23:01 +04:00
{
2008-11-04 10:42:27 +03:00
void * ptr , * kaddr = kmap_atomic ( page , KM_USER0 ) ;
2008-10-31 19:32:19 +03:00
asm volatile ( " \
2008-11-04 10:42:27 +03:00
mov r1 , % 2 @ 1 \ n \
2005-05-10 17:23:01 +04:00
mov r2 , # 0 @ 1 \ n \
mov r3 , # 0 @ 1 \ n \
mov ip , # 0 @ 1 \ n \
mov lr , # 0 @ 1 \ n \
2008-10-31 19:32:19 +03:00
1 : mcr p15 , 0 , % 0 , c7 , c6 , 1 @ 1 invalidate D line \ n \
stmia % 0 ! , { r2 , r3 , ip , lr } @ 4 \ n \
stmia % 0 ! , { r2 , r3 , ip , lr } @ 4 \ n \
mcr p15 , 0 , % 0 , c7 , c6 , 1 @ 1 invalidate D line \ n \
stmia % 0 ! , { r2 , r3 , ip , lr } @ 4 \ n \
stmia % 0 ! , { r2 , r3 , ip , lr } @ 4 \ n \
2005-05-10 17:23:01 +04:00
subs r1 , r1 , # 1 @ 1 \ n \
2008-10-31 19:32:19 +03:00
bne 1 b @ 1 "
2008-11-04 10:42:27 +03:00
: " =r " ( ptr )
: " 0 " ( kaddr ) , " I " ( PAGE_SIZE / 64 )
2008-10-31 19:32:19 +03:00
: " r1 " , " r2 " , " r3 " , " ip " , " lr " ) ;
kunmap_atomic ( kaddr , KM_USER0 ) ;
2005-05-10 17:23:01 +04:00
}
struct cpu_user_fns v4_mc_user_fns __initdata = {
2008-10-31 19:32:19 +03:00
. cpu_clear_user_highpage = v4_mc_clear_user_highpage ,
2008-10-31 18:08:35 +03:00
. cpu_copy_user_highpage = v4_mc_copy_user_highpage ,
2005-05-10 17:23:01 +04:00
} ;