xtensa: implement clear_user_highpage and copy_user_highpage
Existing clear_user_page and copy_user_page cannot be used with highmem because they calculate physical page address from its virtual address and do it incorrectly in case of high memory page mapped with kmap_atomic. Also kmap is not needed, as most likely userspace mapping color would be different from the kmapped color. Provide clear_user_highpage and copy_user_highpage functions that determine if temporary mapping is needed for the pages. Move most of the logic of the former clear_user_page and copy_user_page to xtensa/mm/cache.c only leaving temporary mapping setup, invalidation and clearing/copying in the xtensa/mm/misc.S. Rename these functions to clear_page_alias and copy_page_alias. Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
This commit is contained in:
parent
7128039fe2
commit
a91902db29
@ -37,6 +37,7 @@
|
|||||||
* specials for cache aliasing:
|
* specials for cache aliasing:
|
||||||
*
|
*
|
||||||
* __flush_invalidate_dcache_page_alias(vaddr,paddr)
|
* __flush_invalidate_dcache_page_alias(vaddr,paddr)
|
||||||
|
* __invalidate_dcache_page_alias(vaddr,paddr)
|
||||||
* __invalidate_icache_page_alias(vaddr,paddr)
|
* __invalidate_icache_page_alias(vaddr,paddr)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -62,6 +63,7 @@ extern void __flush_invalidate_dcache_range(unsigned long, unsigned long);
|
|||||||
|
|
||||||
#if defined(CONFIG_MMU) && (DCACHE_WAY_SIZE > PAGE_SIZE)
|
#if defined(CONFIG_MMU) && (DCACHE_WAY_SIZE > PAGE_SIZE)
|
||||||
extern void __flush_invalidate_dcache_page_alias(unsigned long, unsigned long);
|
extern void __flush_invalidate_dcache_page_alias(unsigned long, unsigned long);
|
||||||
|
extern void __invalidate_dcache_page_alias(unsigned long, unsigned long);
|
||||||
#else
|
#else
|
||||||
static inline void __flush_invalidate_dcache_page_alias(unsigned long virt,
|
static inline void __flush_invalidate_dcache_page_alias(unsigned long virt,
|
||||||
unsigned long phys) { }
|
unsigned long phys) { }
|
||||||
|
@ -134,6 +134,7 @@ static inline __attribute_const__ int get_order(unsigned long size)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct page;
|
struct page;
|
||||||
|
struct vm_area_struct;
|
||||||
extern void clear_page(void *page);
|
extern void clear_page(void *page);
|
||||||
extern void copy_page(void *to, void *from);
|
extern void copy_page(void *to, void *from);
|
||||||
|
|
||||||
@ -143,8 +144,15 @@ extern void copy_page(void *to, void *from);
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#if DCACHE_WAY_SIZE > PAGE_SIZE
|
#if DCACHE_WAY_SIZE > PAGE_SIZE
|
||||||
extern void clear_user_page(void*, unsigned long, struct page*);
|
extern void clear_page_alias(void *vaddr, unsigned long paddr);
|
||||||
extern void copy_user_page(void*, void*, unsigned long, struct page*);
|
extern void copy_page_alias(void *to, void *from,
|
||||||
|
unsigned long to_paddr, unsigned long from_paddr);
|
||||||
|
|
||||||
|
#define clear_user_highpage clear_user_highpage
|
||||||
|
void clear_user_highpage(struct page *page, unsigned long vaddr);
|
||||||
|
#define __HAVE_ARCH_COPY_USER_HIGHPAGE
|
||||||
|
void copy_user_highpage(struct page *to, struct page *from,
|
||||||
|
unsigned long vaddr, struct vm_area_struct *vma);
|
||||||
#else
|
#else
|
||||||
# define clear_user_page(page, vaddr, pg) clear_page(page)
|
# define clear_user_page(page, vaddr, pg) clear_page(page)
|
||||||
# define copy_user_page(to, from, vaddr, pg) copy_page(to, from)
|
# define copy_user_page(to, from, vaddr, pg) copy_page(to, from)
|
||||||
|
@ -63,6 +63,69 @@
|
|||||||
#error "HIGHMEM is not supported on cores with aliasing cache."
|
#error "HIGHMEM is not supported on cores with aliasing cache."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if (DCACHE_WAY_SIZE > PAGE_SIZE)
|
||||||
|
static inline void kmap_invalidate_coherent(struct page *page,
|
||||||
|
unsigned long vaddr)
|
||||||
|
{
|
||||||
|
if (!DCACHE_ALIAS_EQ(page_to_phys(page), vaddr)) {
|
||||||
|
unsigned long kvaddr;
|
||||||
|
|
||||||
|
if (!PageHighMem(page)) {
|
||||||
|
kvaddr = (unsigned long)page_to_virt(page);
|
||||||
|
|
||||||
|
__invalidate_dcache_page(kvaddr);
|
||||||
|
} else {
|
||||||
|
kvaddr = TLBTEMP_BASE_1 +
|
||||||
|
(page_to_phys(page) & DCACHE_ALIAS_MASK);
|
||||||
|
|
||||||
|
__invalidate_dcache_page_alias(kvaddr,
|
||||||
|
page_to_phys(page));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *coherent_kvaddr(struct page *page, unsigned long base,
|
||||||
|
unsigned long vaddr, unsigned long *paddr)
|
||||||
|
{
|
||||||
|
if (PageHighMem(page) || !DCACHE_ALIAS_EQ(page_to_phys(page), vaddr)) {
|
||||||
|
*paddr = page_to_phys(page);
|
||||||
|
return (void *)(base + (vaddr & DCACHE_ALIAS_MASK));
|
||||||
|
} else {
|
||||||
|
*paddr = 0;
|
||||||
|
return page_to_virt(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_user_highpage(struct page *page, unsigned long vaddr)
|
||||||
|
{
|
||||||
|
unsigned long paddr;
|
||||||
|
void *kvaddr = coherent_kvaddr(page, TLBTEMP_BASE_1, vaddr, &paddr);
|
||||||
|
|
||||||
|
pagefault_disable();
|
||||||
|
kmap_invalidate_coherent(page, vaddr);
|
||||||
|
set_bit(PG_arch_1, &page->flags);
|
||||||
|
clear_page_alias(kvaddr, paddr);
|
||||||
|
pagefault_enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy_user_highpage(struct page *dst, struct page *src,
|
||||||
|
unsigned long vaddr, struct vm_area_struct *vma)
|
||||||
|
{
|
||||||
|
unsigned long dst_paddr, src_paddr;
|
||||||
|
void *dst_vaddr = coherent_kvaddr(dst, TLBTEMP_BASE_1, vaddr,
|
||||||
|
&dst_paddr);
|
||||||
|
void *src_vaddr = coherent_kvaddr(src, TLBTEMP_BASE_2, vaddr,
|
||||||
|
&src_paddr);
|
||||||
|
|
||||||
|
pagefault_disable();
|
||||||
|
kmap_invalidate_coherent(dst, vaddr);
|
||||||
|
set_bit(PG_arch_1, &dst->flags);
|
||||||
|
copy_page_alias(dst_vaddr, src_vaddr, dst_paddr, src_paddr);
|
||||||
|
pagefault_enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* DCACHE_WAY_SIZE > PAGE_SIZE */
|
||||||
|
|
||||||
#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK
|
#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -110,41 +110,24 @@ ENTRY(__tlbtemp_mapping_start)
|
|||||||
#if (DCACHE_WAY_SIZE > PAGE_SIZE)
|
#if (DCACHE_WAY_SIZE > PAGE_SIZE)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* clear_user_page (void *addr, unsigned long vaddr, struct page *page)
|
* clear_page_alias(void *addr, unsigned long paddr)
|
||||||
* a2 a3 a4
|
* a2 a3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ENTRY(clear_user_page)
|
ENTRY(clear_page_alias)
|
||||||
|
|
||||||
entry a1, 32
|
entry a1, 32
|
||||||
|
|
||||||
/* Mark page dirty and determine alias. */
|
/* Skip setting up a temporary DTLB if not aliased low page. */
|
||||||
|
|
||||||
movi a7, (1 << PG_ARCH_1)
|
movi a5, PAGE_OFFSET
|
||||||
l32i a5, a4, PAGE_FLAGS
|
movi a6, 0
|
||||||
xor a6, a2, a3
|
beqz a3, 1f
|
||||||
extui a3, a3, PAGE_SHIFT, DCACHE_ALIAS_ORDER
|
|
||||||
extui a6, a6, PAGE_SHIFT, DCACHE_ALIAS_ORDER
|
|
||||||
or a5, a5, a7
|
|
||||||
slli a3, a3, PAGE_SHIFT
|
|
||||||
s32i a5, a4, PAGE_FLAGS
|
|
||||||
|
|
||||||
/* Skip setting up a temporary DTLB if not aliased. */
|
/* Setup a temporary DTLB for the addr. */
|
||||||
|
|
||||||
beqz a6, 1f
|
|
||||||
|
|
||||||
/* Invalidate kernel page. */
|
|
||||||
|
|
||||||
mov a10, a2
|
|
||||||
call8 __invalidate_dcache_page
|
|
||||||
|
|
||||||
/* Setup a temporary DTLB with the color of the VPN */
|
|
||||||
|
|
||||||
movi a4, ((PAGE_KERNEL | _PAGE_HW_WRITE) - PAGE_OFFSET) & 0xffffffff
|
|
||||||
movi a5, TLBTEMP_BASE_1 # virt
|
|
||||||
add a6, a2, a4 # ppn
|
|
||||||
add a2, a5, a3 # add 'color'
|
|
||||||
|
|
||||||
|
addi a6, a3, (PAGE_KERNEL | _PAGE_HW_WRITE)
|
||||||
|
mov a4, a2
|
||||||
wdtlb a6, a2
|
wdtlb a6, a2
|
||||||
dsync
|
dsync
|
||||||
|
|
||||||
@ -165,62 +148,43 @@ ENTRY(clear_user_page)
|
|||||||
|
|
||||||
/* We need to invalidate the temporary idtlb entry, if any. */
|
/* We need to invalidate the temporary idtlb entry, if any. */
|
||||||
|
|
||||||
1: addi a2, a2, -PAGE_SIZE
|
1: idtlb a4
|
||||||
idtlb a2
|
|
||||||
dsync
|
dsync
|
||||||
|
|
||||||
retw
|
retw
|
||||||
|
|
||||||
ENDPROC(clear_user_page)
|
ENDPROC(clear_page_alias)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* copy_page_user (void *to, void *from, unsigned long vaddr, struct page *page)
|
* copy_page_alias(void *to, void *from,
|
||||||
* a2 a3 a4 a5
|
* a2 a3
|
||||||
|
* unsigned long to_paddr, unsigned long from_paddr)
|
||||||
|
* a4 a5
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ENTRY(copy_user_page)
|
ENTRY(copy_page_alias)
|
||||||
|
|
||||||
entry a1, 32
|
entry a1, 32
|
||||||
|
|
||||||
/* Mark page dirty and determine alias for destination. */
|
/* Skip setting up a temporary DTLB for destination if not aliased. */
|
||||||
|
|
||||||
movi a8, (1 << PG_ARCH_1)
|
movi a6, 0
|
||||||
l32i a9, a5, PAGE_FLAGS
|
movi a7, 0
|
||||||
xor a6, a2, a4
|
beqz a4, 1f
|
||||||
xor a7, a3, a4
|
|
||||||
extui a4, a4, PAGE_SHIFT, DCACHE_ALIAS_ORDER
|
|
||||||
extui a6, a6, PAGE_SHIFT, DCACHE_ALIAS_ORDER
|
|
||||||
extui a7, a7, PAGE_SHIFT, DCACHE_ALIAS_ORDER
|
|
||||||
or a9, a9, a8
|
|
||||||
slli a4, a4, PAGE_SHIFT
|
|
||||||
s32i a9, a5, PAGE_FLAGS
|
|
||||||
movi a5, ((PAGE_KERNEL | _PAGE_HW_WRITE) - PAGE_OFFSET) & 0xffffffff
|
|
||||||
|
|
||||||
beqz a6, 1f
|
/* Setup a temporary DTLB for destination. */
|
||||||
|
|
||||||
/* Invalidate dcache */
|
|
||||||
|
|
||||||
mov a10, a2
|
|
||||||
call8 __invalidate_dcache_page
|
|
||||||
|
|
||||||
/* Setup a temporary DTLB with a matching color. */
|
|
||||||
|
|
||||||
movi a8, TLBTEMP_BASE_1 # base
|
|
||||||
add a6, a2, a5 # ppn
|
|
||||||
add a2, a8, a4 # add 'color'
|
|
||||||
|
|
||||||
|
addi a6, a4, (PAGE_KERNEL | _PAGE_HW_WRITE)
|
||||||
wdtlb a6, a2
|
wdtlb a6, a2
|
||||||
dsync
|
dsync
|
||||||
|
|
||||||
/* Skip setting up a temporary DTLB for destination if not aliased. */
|
/* Skip setting up a temporary DTLB for source if not aliased. */
|
||||||
|
|
||||||
1: beqz a7, 1f
|
1: beqz a5, 1f
|
||||||
|
|
||||||
/* Setup a temporary DTLB with a matching color. */
|
/* Setup a temporary DTLB for source. */
|
||||||
|
|
||||||
movi a8, TLBTEMP_BASE_2 # base
|
addi a7, a5, PAGE_KERNEL
|
||||||
add a7, a3, a5 # ppn
|
|
||||||
add a3, a8, a4
|
|
||||||
addi a8, a3, 1 # way1
|
addi a8, a3, 1 # way1
|
||||||
|
|
||||||
wdtlb a7, a8
|
wdtlb a7, a8
|
||||||
@ -271,7 +235,7 @@ ENTRY(copy_user_page)
|
|||||||
|
|
||||||
retw
|
retw
|
||||||
|
|
||||||
ENDPROC(copy_user_page)
|
ENDPROC(copy_page_alias)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -300,6 +264,30 @@ ENTRY(__flush_invalidate_dcache_page_alias)
|
|||||||
retw
|
retw
|
||||||
|
|
||||||
ENDPROC(__flush_invalidate_dcache_page_alias)
|
ENDPROC(__flush_invalidate_dcache_page_alias)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* void __invalidate_dcache_page_alias (addr, phys)
|
||||||
|
* a2 a3
|
||||||
|
*/
|
||||||
|
|
||||||
|
ENTRY(__invalidate_dcache_page_alias)
|
||||||
|
|
||||||
|
entry sp, 16
|
||||||
|
|
||||||
|
movi a7, 0 # required for exception handler
|
||||||
|
addi a6, a3, (PAGE_KERNEL | _PAGE_HW_WRITE)
|
||||||
|
mov a4, a2
|
||||||
|
wdtlb a6, a2
|
||||||
|
dsync
|
||||||
|
|
||||||
|
___invalidate_dcache_page a2 a3
|
||||||
|
|
||||||
|
idtlb a4
|
||||||
|
dsync
|
||||||
|
|
||||||
|
retw
|
||||||
|
|
||||||
|
ENDPROC(__invalidate_dcache_page_alias)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ENTRY(__tlbtemp_mapping_itlb)
|
ENTRY(__tlbtemp_mapping_itlb)
|
||||||
|
Loading…
Reference in New Issue
Block a user