1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * linux/arch/arm/lib/copypage-armv4mc.S |
4 | * |
5 | * Copyright (C) 1995-2005 Russell King |
6 | * |
7 | * This handles the mini data cache, as found on SA11x0 and XScale |
8 | * processors. When we copy a user page page, we map it in such a way |
9 | * that accesses to this page will not touch the main data cache, but |
10 | * will be cached in the mini data cache. This prevents us thrashing |
11 | * the main data cache on page faults. |
12 | */ |
13 | #include <linux/init.h> |
14 | #include <linux/mm.h> |
15 | #include <linux/highmem.h> |
16 | #include <linux/pagemap.h> |
17 | |
18 | #include <asm/tlbflush.h> |
19 | #include <asm/cacheflush.h> |
20 | |
21 | #include "mm.h" |
22 | |
23 | #define minicache_pgprot __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | \ |
24 | L_PTE_MT_MINICACHE) |
25 | |
26 | static DEFINE_RAW_SPINLOCK(minicache_lock); |
27 | |
28 | /* |
29 | * ARMv4 mini-dcache optimised copy_user_highpage |
30 | * |
31 | * We flush the destination cache lines just before we write the data into the |
32 | * corresponding address. Since the Dcache is read-allocate, this removes the |
33 | * Dcache aliasing issue. The writes will be forwarded to the write buffer, |
34 | * and merged as appropriate. |
35 | * |
36 | * Note: We rely on all ARMv4 processors implementing the "invalidate D line" |
37 | * instruction. If your processor does not supply this, you have to write your |
38 | * own copy_user_highpage that does the right thing. |
39 | */ |
40 | static void mc_copy_user_page(void *from, void *to) |
41 | { |
42 | int tmp; |
43 | |
44 | asm volatile ("\ |
45 | .syntax unified\n\ |
46 | ldmia %0!, {r2, r3, ip, lr} @ 4\n\ |
47 | 1: mcr p15, 0, %1, c7, c6, 1 @ 1 invalidate D line\n\ |
48 | stmia %1!, {r2, r3, ip, lr} @ 4\n\ |
49 | ldmia %0!, {r2, r3, ip, lr} @ 4+1\n\ |
50 | stmia %1!, {r2, r3, ip, lr} @ 4\n\ |
51 | ldmia %0!, {r2, r3, ip, lr} @ 4\n\ |
52 | mcr p15, 0, %1, c7, c6, 1 @ 1 invalidate D line\n\ |
53 | stmia %1!, {r2, r3, ip, lr} @ 4\n\ |
54 | ldmia %0!, {r2, r3, ip, lr} @ 4\n\ |
55 | subs %2, %2, #1 @ 1\n\ |
56 | stmia %1!, {r2, r3, ip, lr} @ 4\n\ |
57 | ldmiane %0!, {r2, r3, ip, lr} @ 4\n\ |
58 | bne 1b @ " |
59 | : "+&r" (from), "+&r" (to), "=&r" (tmp) |
60 | : "2" (PAGE_SIZE / 64) |
61 | : "r2" , "r3" , "ip" , "lr" ); |
62 | } |
63 | |
64 | void v4_mc_copy_user_highpage(struct page *to, struct page *from, |
65 | unsigned long vaddr, struct vm_area_struct *vma) |
66 | { |
67 | struct folio *src = page_folio(from); |
68 | void *kto = kmap_atomic(page: to); |
69 | |
70 | if (!test_and_set_bit(nr: PG_dcache_clean, addr: &src->flags)) |
71 | __flush_dcache_folio(mapping: folio_flush_mapping(folio: src), folio: src); |
72 | |
73 | raw_spin_lock(&minicache_lock); |
74 | |
75 | set_top_pte(COPYPAGE_MINICACHE, mk_pte(from, minicache_pgprot)); |
76 | |
77 | mc_copy_user_page(from: (void *)COPYPAGE_MINICACHE, to: kto); |
78 | |
79 | raw_spin_unlock(&minicache_lock); |
80 | |
81 | kunmap_atomic(kto); |
82 | } |
83 | |
84 | /* |
85 | * ARMv4 optimised clear_user_page |
86 | */ |
87 | void v4_mc_clear_user_highpage(struct page *page, unsigned long vaddr) |
88 | { |
89 | void *ptr, *kaddr = kmap_atomic(page); |
90 | asm volatile("\ |
91 | mov r1, %2 @ 1\n\ |
92 | mov r2, #0 @ 1\n\ |
93 | mov r3, #0 @ 1\n\ |
94 | mov ip, #0 @ 1\n\ |
95 | mov lr, #0 @ 1\n\ |
96 | 1: mcr p15, 0, %0, c7, c6, 1 @ 1 invalidate D line\n\ |
97 | stmia %0!, {r2, r3, ip, lr} @ 4\n\ |
98 | stmia %0!, {r2, r3, ip, lr} @ 4\n\ |
99 | mcr p15, 0, %0, c7, c6, 1 @ 1 invalidate D line\n\ |
100 | stmia %0!, {r2, r3, ip, lr} @ 4\n\ |
101 | stmia %0!, {r2, r3, ip, lr} @ 4\n\ |
102 | subs r1, r1, #1 @ 1\n\ |
103 | bne 1b @ 1" |
104 | : "=r" (ptr) |
105 | : "0" (kaddr), "I" (PAGE_SIZE / 64) |
106 | : "r1" , "r2" , "r3" , "ip" , "lr" ); |
107 | kunmap_atomic(kaddr); |
108 | } |
109 | |
110 | struct cpu_user_fns v4_mc_user_fns __initdata = { |
111 | .cpu_clear_user_highpage = v4_mc_clear_user_highpage, |
112 | .cpu_copy_user_highpage = v4_mc_copy_user_highpage, |
113 | }; |
114 | |