1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * linux/arch/arm/mm/copypage-v6.c |
4 | * |
5 | * Copyright (C) 2002 Deep Blue Solutions Ltd, All Rights Reserved. |
6 | */ |
7 | #include <linux/init.h> |
8 | #include <linux/spinlock.h> |
9 | #include <linux/mm.h> |
10 | #include <linux/highmem.h> |
11 | #include <linux/pagemap.h> |
12 | |
13 | #include <asm/shmparam.h> |
14 | #include <asm/tlbflush.h> |
15 | #include <asm/cacheflush.h> |
16 | #include <asm/cachetype.h> |
17 | |
18 | #include "mm.h" |
19 | |
20 | #if SHMLBA > 16384 |
21 | #error FIX ME |
22 | #endif |
23 | |
24 | static DEFINE_RAW_SPINLOCK(v6_lock); |
25 | |
26 | /* |
27 | * Copy the user page. No aliasing to deal with so we can just |
28 | * attack the kernel's existing mapping of these pages. |
29 | */ |
30 | static void v6_copy_user_highpage_nonaliasing(struct page *to, |
31 | struct page *from, unsigned long vaddr, struct vm_area_struct *vma) |
32 | { |
33 | void *kto, *kfrom; |
34 | |
35 | kfrom = kmap_atomic(page: from); |
36 | kto = kmap_atomic(page: to); |
37 | copy_page(to: kto, from: kfrom); |
38 | kunmap_atomic(kto); |
39 | kunmap_atomic(kfrom); |
40 | } |
41 | |
42 | /* |
43 | * Clear the user page. No aliasing to deal with so we can just |
44 | * attack the kernel's existing mapping of this page. |
45 | */ |
46 | static void v6_clear_user_highpage_nonaliasing(struct page *page, unsigned long vaddr) |
47 | { |
48 | void *kaddr = kmap_atomic(page); |
49 | clear_page(page: kaddr); |
50 | kunmap_atomic(kaddr); |
51 | } |
52 | |
53 | /* |
54 | * Discard data in the kernel mapping for the new page. |
55 | * FIXME: needs this MCRR to be supported. |
56 | */ |
57 | static void discard_old_kernel_data(void *kto) |
58 | { |
59 | __asm__("mcrr p15, 0, %1, %0, c6 @ 0xec401f06" |
60 | : |
61 | : "r" (kto), |
62 | "r" ((unsigned long)kto + PAGE_SIZE - 1) |
63 | : "cc" ); |
64 | } |
65 | |
66 | /* |
67 | * Copy the page, taking account of the cache colour. |
68 | */ |
69 | static void v6_copy_user_highpage_aliasing(struct page *to, |
70 | struct page *from, unsigned long vaddr, struct vm_area_struct *vma) |
71 | { |
72 | struct folio *src = page_folio(from); |
73 | unsigned int offset = CACHE_COLOUR(vaddr); |
74 | unsigned long kfrom, kto; |
75 | |
76 | if (!test_and_set_bit(nr: PG_dcache_clean, addr: &src->flags)) |
77 | __flush_dcache_folio(mapping: folio_flush_mapping(folio: src), folio: src); |
78 | |
79 | /* FIXME: not highmem safe */ |
80 | discard_old_kernel_data(page_address(to)); |
81 | |
82 | /* |
83 | * Now copy the page using the same cache colour as the |
84 | * pages ultimate destination. |
85 | */ |
86 | raw_spin_lock(&v6_lock); |
87 | |
88 | kfrom = COPYPAGE_V6_FROM + (offset << PAGE_SHIFT); |
89 | kto = COPYPAGE_V6_TO + (offset << PAGE_SHIFT); |
90 | |
91 | set_top_pte(va: kfrom, mk_pte(from, PAGE_KERNEL)); |
92 | set_top_pte(va: kto, mk_pte(to, PAGE_KERNEL)); |
93 | |
94 | copy_page(to: (void *)kto, from: (void *)kfrom); |
95 | |
96 | raw_spin_unlock(&v6_lock); |
97 | } |
98 | |
99 | /* |
100 | * Clear the user page. We need to deal with the aliasing issues, |
101 | * so remap the kernel page into the same cache colour as the user |
102 | * page. |
103 | */ |
104 | static void v6_clear_user_highpage_aliasing(struct page *page, unsigned long vaddr) |
105 | { |
106 | unsigned long to = COPYPAGE_V6_TO + (CACHE_COLOUR(vaddr) << PAGE_SHIFT); |
107 | |
108 | /* FIXME: not highmem safe */ |
109 | discard_old_kernel_data(page_address(page)); |
110 | |
111 | /* |
112 | * Now clear the page using the same cache colour as |
113 | * the pages ultimate destination. |
114 | */ |
115 | raw_spin_lock(&v6_lock); |
116 | |
117 | set_top_pte(va: to, mk_pte(page, PAGE_KERNEL)); |
118 | clear_page(page: (void *)to); |
119 | |
120 | raw_spin_unlock(&v6_lock); |
121 | } |
122 | |
123 | struct cpu_user_fns v6_user_fns __initdata = { |
124 | .cpu_clear_user_highpage = v6_clear_user_highpage_nonaliasing, |
125 | .cpu_copy_user_highpage = v6_copy_user_highpage_nonaliasing, |
126 | }; |
127 | |
128 | static int __init v6_userpage_init(void) |
129 | { |
130 | if (cache_is_vipt_aliasing()) { |
131 | cpu_user.cpu_clear_user_highpage = v6_clear_user_highpage_aliasing; |
132 | cpu_user.cpu_copy_user_highpage = v6_copy_user_highpage_aliasing; |
133 | } |
134 | |
135 | return 0; |
136 | } |
137 | |
138 | core_initcall(v6_userpage_init); |
139 | |