diff options
Diffstat (limited to 'arch/sh/mm')
25 files changed, 2774 insertions, 863 deletions
diff --git a/arch/sh/mm/Kconfig b/arch/sh/mm/Kconfig index 1265f204f7d1..f549b8cd2501 100644 --- a/arch/sh/mm/Kconfig +++ b/arch/sh/mm/Kconfig | |||
@@ -1,193 +1,3 @@ | |||
1 | # | ||
2 | # Processor families | ||
3 | # | ||
4 | config CPU_SH2 | ||
5 | bool | ||
6 | |||
7 | config CPU_SH2A | ||
8 | bool | ||
9 | select CPU_SH2 | ||
10 | |||
11 | config CPU_SH3 | ||
12 | bool | ||
13 | select CPU_HAS_INTEVT | ||
14 | select CPU_HAS_SR_RB | ||
15 | |||
16 | config CPU_SH4 | ||
17 | bool | ||
18 | select CPU_HAS_INTEVT | ||
19 | select CPU_HAS_SR_RB | ||
20 | select CPU_HAS_PTEA if !CPU_SH4A || CPU_SHX2 | ||
21 | select CPU_HAS_FPU if !CPU_SH4AL_DSP | ||
22 | |||
23 | config CPU_SH4A | ||
24 | bool | ||
25 | select CPU_SH4 | ||
26 | |||
27 | config CPU_SH4AL_DSP | ||
28 | bool | ||
29 | select CPU_SH4A | ||
30 | select CPU_HAS_DSP | ||
31 | |||
32 | config CPU_SHX2 | ||
33 | bool | ||
34 | |||
35 | config CPU_SHX3 | ||
36 | bool | ||
37 | |||
38 | choice | ||
39 | prompt "Processor sub-type selection" | ||
40 | |||
41 | # | ||
42 | # Processor subtypes | ||
43 | # | ||
44 | |||
45 | # SH-2 Processor Support | ||
46 | |||
47 | config CPU_SUBTYPE_SH7619 | ||
48 | bool "Support SH7619 processor" | ||
49 | select CPU_SH2 | ||
50 | |||
51 | # SH-2A Processor Support | ||
52 | |||
53 | config CPU_SUBTYPE_SH7206 | ||
54 | bool "Support SH7206 processor" | ||
55 | select CPU_SH2A | ||
56 | |||
57 | # SH-3 Processor Support | ||
58 | |||
59 | config CPU_SUBTYPE_SH7705 | ||
60 | bool "Support SH7705 processor" | ||
61 | select CPU_SH3 | ||
62 | |||
63 | config CPU_SUBTYPE_SH7706 | ||
64 | bool "Support SH7706 processor" | ||
65 | select CPU_SH3 | ||
66 | help | ||
67 | Select SH7706 if you have a 133 Mhz SH-3 HD6417706 CPU. | ||
68 | |||
69 | config CPU_SUBTYPE_SH7707 | ||
70 | bool "Support SH7707 processor" | ||
71 | select CPU_SH3 | ||
72 | help | ||
73 | Select SH7707 if you have a 60 Mhz SH-3 HD6417707 CPU. | ||
74 | |||
75 | config CPU_SUBTYPE_SH7708 | ||
76 | bool "Support SH7708 processor" | ||
77 | select CPU_SH3 | ||
78 | help | ||
79 | Select SH7708 if you have a 60 Mhz SH-3 HD6417708S or | ||
80 | if you have a 100 Mhz SH-3 HD6417708R CPU. | ||
81 | |||
82 | config CPU_SUBTYPE_SH7709 | ||
83 | bool "Support SH7709 processor" | ||
84 | select CPU_SH3 | ||
85 | help | ||
86 | Select SH7709 if you have a 80 Mhz SH-3 HD6417709 CPU. | ||
87 | |||
88 | config CPU_SUBTYPE_SH7710 | ||
89 | bool "Support SH7710 processor" | ||
90 | select CPU_SH3 | ||
91 | select CPU_HAS_DSP | ||
92 | help | ||
93 | Select SH7710 if you have a SH3-DSP SH7710 CPU. | ||
94 | |||
95 | config CPU_SUBTYPE_SH7712 | ||
96 | bool "Support SH7712 processor" | ||
97 | select CPU_SH3 | ||
98 | select CPU_HAS_DSP | ||
99 | help | ||
100 | Select SH7712 if you have a SH3-DSP SH7712 CPU. | ||
101 | |||
102 | config CPU_SUBTYPE_SH7720 | ||
103 | bool "Support SH7720 processor" | ||
104 | select CPU_SH3 | ||
105 | select CPU_HAS_DSP | ||
106 | help | ||
107 | Select SH7720 if you have a SH3-DSP SH7720 CPU. | ||
108 | |||
109 | # SH-4 Processor Support | ||
110 | |||
111 | config CPU_SUBTYPE_SH7750 | ||
112 | bool "Support SH7750 processor" | ||
113 | select CPU_SH4 | ||
114 | help | ||
115 | Select SH7750 if you have a 200 Mhz SH-4 HD6417750 CPU. | ||
116 | |||
117 | config CPU_SUBTYPE_SH7091 | ||
118 | bool "Support SH7091 processor" | ||
119 | select CPU_SH4 | ||
120 | help | ||
121 | Select SH7091 if you have an SH-4 based Sega device (such as | ||
122 | the Dreamcast, Naomi, and Naomi 2). | ||
123 | |||
124 | config CPU_SUBTYPE_SH7750R | ||
125 | bool "Support SH7750R processor" | ||
126 | select CPU_SH4 | ||
127 | |||
128 | config CPU_SUBTYPE_SH7750S | ||
129 | bool "Support SH7750S processor" | ||
130 | select CPU_SH4 | ||
131 | |||
132 | config CPU_SUBTYPE_SH7751 | ||
133 | bool "Support SH7751 processor" | ||
134 | select CPU_SH4 | ||
135 | help | ||
136 | Select SH7751 if you have a 166 Mhz SH-4 HD6417751 CPU, | ||
137 | or if you have a HD6417751R CPU. | ||
138 | |||
139 | config CPU_SUBTYPE_SH7751R | ||
140 | bool "Support SH7751R processor" | ||
141 | select CPU_SH4 | ||
142 | |||
143 | config CPU_SUBTYPE_SH7760 | ||
144 | bool "Support SH7760 processor" | ||
145 | select CPU_SH4 | ||
146 | |||
147 | config CPU_SUBTYPE_SH4_202 | ||
148 | bool "Support SH4-202 processor" | ||
149 | select CPU_SH4 | ||
150 | |||
151 | # SH-4A Processor Support | ||
152 | |||
153 | config CPU_SUBTYPE_SH7770 | ||
154 | bool "Support SH7770 processor" | ||
155 | select CPU_SH4A | ||
156 | |||
157 | config CPU_SUBTYPE_SH7780 | ||
158 | bool "Support SH7780 processor" | ||
159 | select CPU_SH4A | ||
160 | |||
161 | config CPU_SUBTYPE_SH7785 | ||
162 | bool "Support SH7785 processor" | ||
163 | select CPU_SH4A | ||
164 | select CPU_SHX2 | ||
165 | select ARCH_SPARSEMEM_ENABLE | ||
166 | select SYS_SUPPORTS_NUMA | ||
167 | |||
168 | config CPU_SUBTYPE_SHX3 | ||
169 | bool "Support SH-X3 processor" | ||
170 | select CPU_SH4A | ||
171 | select CPU_SHX3 | ||
172 | select ARCH_SPARSEMEM_ENABLE | ||
173 | select SYS_SUPPORTS_NUMA | ||
174 | select SYS_SUPPORTS_SMP | ||
175 | |||
176 | # SH4AL-DSP Processor Support | ||
177 | |||
178 | config CPU_SUBTYPE_SH7343 | ||
179 | bool "Support SH7343 processor" | ||
180 | select CPU_SH4AL_DSP | ||
181 | |||
182 | config CPU_SUBTYPE_SH7722 | ||
183 | bool "Support SH7722 processor" | ||
184 | select CPU_SH4AL_DSP | ||
185 | select CPU_SHX2 | ||
186 | select ARCH_SPARSEMEM_ENABLE | ||
187 | select SYS_SUPPORTS_NUMA | ||
188 | |||
189 | endchoice | ||
190 | |||
191 | menu "Memory management options" | 1 | menu "Memory management options" |
192 | 2 | ||
193 | config QUICKLIST | 3 | config QUICKLIST |
@@ -207,7 +17,8 @@ config MMU | |||
207 | 17 | ||
208 | config PAGE_OFFSET | 18 | config PAGE_OFFSET |
209 | hex | 19 | hex |
210 | default "0x80000000" if MMU | 20 | default "0x80000000" if MMU && SUPERH32 |
21 | default "0x20000000" if MMU && SUPERH64 | ||
211 | default "0x00000000" | 22 | default "0x00000000" |
212 | 23 | ||
213 | config MEMORY_START | 24 | config MEMORY_START |
@@ -228,17 +39,28 @@ config MEMORY_START | |||
228 | 39 | ||
229 | config MEMORY_SIZE | 40 | config MEMORY_SIZE |
230 | hex "Physical memory size" | 41 | hex "Physical memory size" |
231 | default "0x00400000" | 42 | default "0x04000000" |
232 | help | 43 | help |
233 | This sets the default memory size assumed by your SH kernel. It can | 44 | This sets the default memory size assumed by your SH kernel. It can |
234 | be overridden as normal by the 'mem=' argument on the kernel command | 45 | be overridden as normal by the 'mem=' argument on the kernel command |
235 | line. If unsure, consult your board specifications or just leave it | 46 | line. If unsure, consult your board specifications or just leave it |
236 | as 0x00400000 which was the default value before this became | 47 | as 0x04000000 which was the default value before this became |
237 | configurable. | 48 | configurable. |
238 | 49 | ||
50 | # Physical addressing modes | ||
51 | |||
52 | config 29BIT | ||
53 | def_bool !32BIT | ||
54 | depends on SUPERH32 | ||
55 | |||
239 | config 32BIT | 56 | config 32BIT |
57 | bool | ||
58 | default y if CPU_SH5 | ||
59 | |||
60 | config PMB | ||
240 | bool "Support 32-bit physical addressing through PMB" | 61 | bool "Support 32-bit physical addressing through PMB" |
241 | depends on MMU && (CPU_SUBTYPE_SH7780 || CPU_SUBTYPE_SH7785) | 62 | depends on MMU && (CPU_SUBTYPE_SH7780 || CPU_SUBTYPE_SH7785) |
63 | select 32BIT | ||
242 | default y | 64 | default y |
243 | help | 65 | help |
244 | If you say Y here, physical addressing will be extended to | 66 | If you say Y here, physical addressing will be extended to |
@@ -256,7 +78,7 @@ config X2TLB | |||
256 | 78 | ||
257 | config VSYSCALL | 79 | config VSYSCALL |
258 | bool "Support vsyscall page" | 80 | bool "Support vsyscall page" |
259 | depends on MMU | 81 | depends on MMU && (CPU_SH3 || CPU_SH4) |
260 | default y | 82 | default y |
261 | help | 83 | help |
262 | This will enable support for the kernel mapping a vDSO page | 84 | This will enable support for the kernel mapping a vDSO page |
@@ -335,7 +157,7 @@ config PAGE_SIZE_8KB | |||
335 | 157 | ||
336 | config PAGE_SIZE_64KB | 158 | config PAGE_SIZE_64KB |
337 | bool "64kB" | 159 | bool "64kB" |
338 | depends on CPU_SH4 | 160 | depends on CPU_SH4 || CPU_SH5 |
339 | help | 161 | help |
340 | This enables support for 64kB pages, possible on all SH-4 | 162 | This enables support for 64kB pages, possible on all SH-4 |
341 | CPUs and later. | 163 | CPUs and later. |
@@ -344,7 +166,7 @@ endchoice | |||
344 | 166 | ||
345 | choice | 167 | choice |
346 | prompt "HugeTLB page size" | 168 | prompt "HugeTLB page size" |
347 | depends on HUGETLB_PAGE && CPU_SH4 && MMU | 169 | depends on HUGETLB_PAGE && (CPU_SH4 || CPU_SH5) && MMU |
348 | default HUGETLB_PAGE_SIZE_64K | 170 | default HUGETLB_PAGE_SIZE_64K |
349 | 171 | ||
350 | config HUGETLB_PAGE_SIZE_64K | 172 | config HUGETLB_PAGE_SIZE_64K |
@@ -365,6 +187,10 @@ config HUGETLB_PAGE_SIZE_64MB | |||
365 | bool "64MB" | 187 | bool "64MB" |
366 | depends on X2TLB | 188 | depends on X2TLB |
367 | 189 | ||
190 | config HUGETLB_PAGE_SIZE_512MB | ||
191 | bool "512MB" | ||
192 | depends on CPU_SH5 | ||
193 | |||
368 | endchoice | 194 | endchoice |
369 | 195 | ||
370 | source "mm/Kconfig" | 196 | source "mm/Kconfig" |
@@ -392,12 +218,12 @@ config SH_DIRECT_MAPPED | |||
392 | 218 | ||
393 | choice | 219 | choice |
394 | prompt "Cache mode" | 220 | prompt "Cache mode" |
395 | default CACHE_WRITEBACK if CPU_SH2A || CPU_SH3 || CPU_SH4 | 221 | default CACHE_WRITEBACK if CPU_SH2A || CPU_SH3 || CPU_SH4 || CPU_SH5 |
396 | default CACHE_WRITETHROUGH if (CPU_SH2 && !CPU_SH2A) | 222 | default CACHE_WRITETHROUGH if (CPU_SH2 && !CPU_SH2A) |
397 | 223 | ||
398 | config CACHE_WRITEBACK | 224 | config CACHE_WRITEBACK |
399 | bool "Write-back" | 225 | bool "Write-back" |
400 | depends on CPU_SH2A || CPU_SH3 || CPU_SH4 | 226 | depends on CPU_SH2A || CPU_SH3 || CPU_SH4 || CPU_SH5 |
401 | 227 | ||
402 | config CACHE_WRITETHROUGH | 228 | config CACHE_WRITETHROUGH |
403 | bool "Write-through" | 229 | bool "Write-through" |
diff --git a/arch/sh/mm/Makefile b/arch/sh/mm/Makefile index aa44607f072d..9f4bc3d90b1e 100644 --- a/arch/sh/mm/Makefile +++ b/arch/sh/mm/Makefile | |||
@@ -1,37 +1,5 @@ | |||
1 | # | 1 | ifeq ($(CONFIG_SUPERH32),y) |
2 | # Makefile for the Linux SuperH-specific parts of the memory manager. | 2 | include ${srctree}/arch/sh/mm/Makefile_32 |
3 | # | 3 | else |
4 | 4 | include ${srctree}/arch/sh/mm/Makefile_64 | |
5 | obj-y := init.o extable.o consistent.o | ||
6 | |||
7 | ifndef CONFIG_CACHE_OFF | ||
8 | obj-$(CONFIG_CPU_SH2) += cache-sh2.o | ||
9 | obj-$(CONFIG_CPU_SH3) += cache-sh3.o | ||
10 | obj-$(CONFIG_CPU_SH4) += cache-sh4.o | ||
11 | obj-$(CONFIG_SH7705_CACHE_32KB) += cache-sh7705.o | ||
12 | endif | 5 | endif |
13 | |||
14 | mmu-y := tlb-nommu.o pg-nommu.o | ||
15 | mmu-$(CONFIG_MMU) := fault.o clear_page.o copy_page.o tlb-flush.o \ | ||
16 | ioremap.o | ||
17 | |||
18 | obj-y += $(mmu-y) | ||
19 | |||
20 | ifdef CONFIG_DEBUG_FS | ||
21 | obj-$(CONFIG_CPU_SH4) += cache-debugfs.o | ||
22 | endif | ||
23 | |||
24 | ifdef CONFIG_MMU | ||
25 | obj-$(CONFIG_CPU_SH3) += tlb-sh3.o | ||
26 | obj-$(CONFIG_CPU_SH4) += tlb-sh4.o | ||
27 | ifndef CONFIG_CACHE_OFF | ||
28 | obj-$(CONFIG_CPU_SH4) += pg-sh4.o | ||
29 | obj-$(CONFIG_SH7705_CACHE_32KB) += pg-sh7705.o | ||
30 | endif | ||
31 | endif | ||
32 | |||
33 | obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o | ||
34 | obj-$(CONFIG_32BIT) += pmb.o | ||
35 | obj-$(CONFIG_NUMA) += numa.o | ||
36 | |||
37 | EXTRA_CFLAGS += -Werror | ||
diff --git a/arch/sh/mm/Makefile_32 b/arch/sh/mm/Makefile_32 new file mode 100644 index 000000000000..e295db60b91b --- /dev/null +++ b/arch/sh/mm/Makefile_32 | |||
@@ -0,0 +1,36 @@ | |||
1 | # | ||
2 | # Makefile for the Linux SuperH-specific parts of the memory manager. | ||
3 | # | ||
4 | |||
5 | obj-y := init.o extable_32.o consistent.o | ||
6 | |||
7 | ifndef CONFIG_CACHE_OFF | ||
8 | obj-$(CONFIG_CPU_SH2) += cache-sh2.o | ||
9 | obj-$(CONFIG_CPU_SH3) += cache-sh3.o | ||
10 | obj-$(CONFIG_CPU_SH4) += cache-sh4.o | ||
11 | obj-$(CONFIG_SH7705_CACHE_32KB) += cache-sh7705.o | ||
12 | endif | ||
13 | |||
14 | mmu-y := tlb-nommu.o pg-nommu.o | ||
15 | mmu-$(CONFIG_MMU) := fault_32.o tlbflush_32.o ioremap_32.o | ||
16 | |||
17 | obj-y += $(mmu-y) | ||
18 | |||
19 | ifdef CONFIG_DEBUG_FS | ||
20 | obj-$(CONFIG_CPU_SH4) += cache-debugfs.o | ||
21 | endif | ||
22 | |||
23 | ifdef CONFIG_MMU | ||
24 | obj-$(CONFIG_CPU_SH3) += tlb-sh3.o | ||
25 | obj-$(CONFIG_CPU_SH4) += tlb-sh4.o | ||
26 | ifndef CONFIG_CACHE_OFF | ||
27 | obj-$(CONFIG_CPU_SH4) += pg-sh4.o | ||
28 | obj-$(CONFIG_SH7705_CACHE_32KB) += pg-sh7705.o | ||
29 | endif | ||
30 | endif | ||
31 | |||
32 | obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o | ||
33 | obj-$(CONFIG_PMB) += pmb.o | ||
34 | obj-$(CONFIG_NUMA) += numa.o | ||
35 | |||
36 | EXTRA_CFLAGS += -Werror | ||
diff --git a/arch/sh/mm/Makefile_64 b/arch/sh/mm/Makefile_64 new file mode 100644 index 000000000000..cbd6aa33c5ac --- /dev/null +++ b/arch/sh/mm/Makefile_64 | |||
@@ -0,0 +1,44 @@ | |||
1 | # | ||
2 | # Makefile for the Linux SuperH-specific parts of the memory manager. | ||
3 | # | ||
4 | |||
5 | obj-y := init.o extable_64.o consistent.o | ||
6 | |||
7 | mmu-y := tlb-nommu.o pg-nommu.o | ||
8 | mmu-$(CONFIG_MMU) := fault_64.o ioremap_64.o tlbflush_64.o tlb-sh5.o | ||
9 | |||
10 | ifndef CONFIG_CACHE_OFF | ||
11 | obj-y += cache-sh5.o | ||
12 | endif | ||
13 | |||
14 | obj-y += $(mmu-y) | ||
15 | |||
16 | obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o | ||
17 | obj-$(CONFIG_NUMA) += numa.o | ||
18 | |||
19 | EXTRA_CFLAGS += -Werror | ||
20 | |||
21 | # Special flags for fault_64.o. This puts restrictions on the number of | ||
22 | # caller-save registers that the compiler can target when building this file. | ||
23 | # This is required because the code is called from a context in entry.S where | ||
24 | # very few registers have been saved in the exception handler (for speed | ||
25 | # reasons). | ||
26 | # The caller save registers that have been saved and which can be used are | ||
27 | # r2,r3,r4,r5 : argument passing | ||
28 | # r15, r18 : SP and LINK | ||
29 | # tr0-4 : allow all caller-save TR's. The compiler seems to be able to make | ||
30 | # use of them, so it's probably beneficial to performance to save them | ||
31 | # and have them available for it. | ||
32 | # | ||
33 | # The resources not listed below are callee save, i.e. the compiler is free to | ||
34 | # use any of them and will spill them to the stack itself. | ||
35 | |||
36 | CFLAGS_fault_64.o += -ffixed-r7 \ | ||
37 | -ffixed-r8 -ffixed-r9 -ffixed-r10 -ffixed-r11 -ffixed-r12 \ | ||
38 | -ffixed-r13 -ffixed-r14 -ffixed-r16 -ffixed-r17 -ffixed-r19 \ | ||
39 | -ffixed-r20 -ffixed-r21 -ffixed-r22 -ffixed-r23 \ | ||
40 | -ffixed-r24 -ffixed-r25 -ffixed-r26 -ffixed-r27 \ | ||
41 | -ffixed-r36 -ffixed-r37 -ffixed-r38 -ffixed-r39 -ffixed-r40 \ | ||
42 | -ffixed-r41 -ffixed-r42 -ffixed-r43 \ | ||
43 | -ffixed-r60 -ffixed-r61 -ffixed-r62 \ | ||
44 | -fomit-frame-pointer | ||
diff --git a/arch/sh/mm/cache-debugfs.c b/arch/sh/mm/cache-debugfs.c index de6d2c9aa477..db6d950b6f5e 100644 --- a/arch/sh/mm/cache-debugfs.c +++ b/arch/sh/mm/cache-debugfs.c | |||
@@ -22,7 +22,8 @@ enum cache_type { | |||
22 | CACHE_TYPE_UNIFIED, | 22 | CACHE_TYPE_UNIFIED, |
23 | }; | 23 | }; |
24 | 24 | ||
25 | static int cache_seq_show(struct seq_file *file, void *iter) | 25 | static int __uses_jump_to_uncached cache_seq_show(struct seq_file *file, |
26 | void *iter) | ||
26 | { | 27 | { |
27 | unsigned int cache_type = (unsigned int)file->private; | 28 | unsigned int cache_type = (unsigned int)file->private; |
28 | struct cache_info *cache; | 29 | struct cache_info *cache; |
@@ -34,11 +35,11 @@ static int cache_seq_show(struct seq_file *file, void *iter) | |||
34 | * Go uncached immediately so we don't skew the results any | 35 | * Go uncached immediately so we don't skew the results any |
35 | * more than we already are.. | 36 | * more than we already are.. |
36 | */ | 37 | */ |
37 | jump_to_P2(); | 38 | jump_to_uncached(); |
38 | 39 | ||
39 | ccr = ctrl_inl(CCR); | 40 | ccr = ctrl_inl(CCR); |
40 | if ((ccr & CCR_CACHE_ENABLE) == 0) { | 41 | if ((ccr & CCR_CACHE_ENABLE) == 0) { |
41 | back_to_P1(); | 42 | back_to_cached(); |
42 | 43 | ||
43 | seq_printf(file, "disabled\n"); | 44 | seq_printf(file, "disabled\n"); |
44 | return 0; | 45 | return 0; |
@@ -104,7 +105,7 @@ static int cache_seq_show(struct seq_file *file, void *iter) | |||
104 | addrstart += cache->way_incr; | 105 | addrstart += cache->way_incr; |
105 | } | 106 | } |
106 | 107 | ||
107 | back_to_P1(); | 108 | back_to_cached(); |
108 | 109 | ||
109 | return 0; | 110 | return 0; |
110 | } | 111 | } |
diff --git a/arch/sh/mm/cache-sh4.c b/arch/sh/mm/cache-sh4.c index 226b190c5b9c..43d7ff6b6ec7 100644 --- a/arch/sh/mm/cache-sh4.c +++ b/arch/sh/mm/cache-sh4.c | |||
@@ -190,7 +190,7 @@ void flush_icache_range(unsigned long start, unsigned long end) | |||
190 | * .. which happens to be the same behavior as flush_icache_range(). | 190 | * .. which happens to be the same behavior as flush_icache_range(). |
191 | * So, we simply flush out a line. | 191 | * So, we simply flush out a line. |
192 | */ | 192 | */ |
193 | void flush_cache_sigtramp(unsigned long addr) | 193 | void __uses_jump_to_uncached flush_cache_sigtramp(unsigned long addr) |
194 | { | 194 | { |
195 | unsigned long v, index; | 195 | unsigned long v, index; |
196 | unsigned long flags; | 196 | unsigned long flags; |
@@ -205,13 +205,13 @@ void flush_cache_sigtramp(unsigned long addr) | |||
205 | (v & boot_cpu_data.icache.entry_mask); | 205 | (v & boot_cpu_data.icache.entry_mask); |
206 | 206 | ||
207 | local_irq_save(flags); | 207 | local_irq_save(flags); |
208 | jump_to_P2(); | 208 | jump_to_uncached(); |
209 | 209 | ||
210 | for (i = 0; i < boot_cpu_data.icache.ways; | 210 | for (i = 0; i < boot_cpu_data.icache.ways; |
211 | i++, index += boot_cpu_data.icache.way_incr) | 211 | i++, index += boot_cpu_data.icache.way_incr) |
212 | ctrl_outl(0, index); /* Clear out Valid-bit */ | 212 | ctrl_outl(0, index); /* Clear out Valid-bit */ |
213 | 213 | ||
214 | back_to_P1(); | 214 | back_to_cached(); |
215 | wmb(); | 215 | wmb(); |
216 | local_irq_restore(flags); | 216 | local_irq_restore(flags); |
217 | } | 217 | } |
@@ -256,12 +256,12 @@ void flush_dcache_page(struct page *page) | |||
256 | } | 256 | } |
257 | 257 | ||
258 | /* TODO: Selective icache invalidation through IC address array.. */ | 258 | /* TODO: Selective icache invalidation through IC address array.. */ |
259 | static inline void flush_icache_all(void) | 259 | static inline void __uses_jump_to_uncached flush_icache_all(void) |
260 | { | 260 | { |
261 | unsigned long flags, ccr; | 261 | unsigned long flags, ccr; |
262 | 262 | ||
263 | local_irq_save(flags); | 263 | local_irq_save(flags); |
264 | jump_to_P2(); | 264 | jump_to_uncached(); |
265 | 265 | ||
266 | /* Flush I-cache */ | 266 | /* Flush I-cache */ |
267 | ccr = ctrl_inl(CCR); | 267 | ccr = ctrl_inl(CCR); |
@@ -269,11 +269,11 @@ static inline void flush_icache_all(void) | |||
269 | ctrl_outl(ccr, CCR); | 269 | ctrl_outl(ccr, CCR); |
270 | 270 | ||
271 | /* | 271 | /* |
272 | * back_to_P1() will take care of the barrier for us, don't add | 272 | * back_to_cached() will take care of the barrier for us, don't add |
273 | * another one! | 273 | * another one! |
274 | */ | 274 | */ |
275 | 275 | ||
276 | back_to_P1(); | 276 | back_to_cached(); |
277 | local_irq_restore(flags); | 277 | local_irq_restore(flags); |
278 | } | 278 | } |
279 | 279 | ||
diff --git a/arch/sh/mm/cache-sh5.c b/arch/sh/mm/cache-sh5.c new file mode 100644 index 000000000000..4617e3aeee73 --- /dev/null +++ b/arch/sh/mm/cache-sh5.c | |||
@@ -0,0 +1,1029 @@ | |||
1 | /* | ||
2 | * arch/sh/mm/cache-sh5.c | ||
3 | * | ||
4 | * Original version Copyright (C) 2000, 2001 Paolo Alberelli | ||
5 | * Second version Copyright (C) benedict.gaster@superh.com 2002 | ||
6 | * Third version Copyright Richard.Curnow@superh.com 2003 | ||
7 | * Hacks to third version Copyright (C) 2003 Paul Mundt | ||
8 | * | ||
9 | * This file is subject to the terms and conditions of the GNU General Public | ||
10 | * License. See the file "COPYING" in the main directory of this archive | ||
11 | * for more details. | ||
12 | */ | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/mman.h> | ||
15 | #include <linux/mm.h> | ||
16 | #include <linux/threads.h> | ||
17 | #include <asm/page.h> | ||
18 | #include <asm/pgtable.h> | ||
19 | #include <asm/processor.h> | ||
20 | #include <asm/cache.h> | ||
21 | #include <asm/tlb.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/uaccess.h> | ||
24 | #include <asm/mmu_context.h> | ||
25 | #include <asm/pgalloc.h> /* for flush_itlb_range */ | ||
26 | |||
27 | #include <linux/proc_fs.h> | ||
28 | |||
29 | /* This function is in entry.S */ | ||
30 | extern unsigned long switch_and_save_asid(unsigned long new_asid); | ||
31 | |||
32 | /* Wired TLB entry for the D-cache */ | ||
33 | static unsigned long long dtlb_cache_slot; | ||
34 | |||
35 | /** | ||
36 | * sh64_cache_init() | ||
37 | * | ||
38 | * This is pretty much just a straightforward clone of the SH | ||
39 | * detect_cpu_and_cache_system(). | ||
40 | * | ||
41 | * This function is responsible for setting up all of the cache | ||
42 | * info dynamically as well as taking care of CPU probing and | ||
43 | * setting up the relevant subtype data. | ||
44 | * | ||
45 | * FIXME: For the time being, we only really support the SH5-101 | ||
46 | * out of the box, and don't support dynamic probing for things | ||
47 | * like the SH5-103 or even cut2 of the SH5-101. Implement this | ||
48 | * later! | ||
49 | */ | ||
50 | int __init sh64_cache_init(void) | ||
51 | { | ||
52 | /* | ||
53 | * First, setup some sane values for the I-cache. | ||
54 | */ | ||
55 | cpu_data->icache.ways = 4; | ||
56 | cpu_data->icache.sets = 256; | ||
57 | cpu_data->icache.linesz = L1_CACHE_BYTES; | ||
58 | |||
59 | /* | ||
60 | * FIXME: This can probably be cleaned up a bit as well.. for example, | ||
61 | * do we really need the way shift _and_ the way_step_shift ?? Judging | ||
62 | * by the existing code, I would guess no.. is there any valid reason | ||
63 | * why we need to be tracking this around? | ||
64 | */ | ||
65 | cpu_data->icache.way_shift = 13; | ||
66 | cpu_data->icache.entry_shift = 5; | ||
67 | cpu_data->icache.set_shift = 4; | ||
68 | cpu_data->icache.way_step_shift = 16; | ||
69 | cpu_data->icache.asid_shift = 2; | ||
70 | |||
71 | /* | ||
72 | * way offset = cache size / associativity, so just don't factor in | ||
73 | * associativity in the first place.. | ||
74 | */ | ||
75 | cpu_data->icache.way_ofs = cpu_data->icache.sets * | ||
76 | cpu_data->icache.linesz; | ||
77 | |||
78 | cpu_data->icache.asid_mask = 0x3fc; | ||
79 | cpu_data->icache.idx_mask = 0x1fe0; | ||
80 | cpu_data->icache.epn_mask = 0xffffe000; | ||
81 | cpu_data->icache.flags = 0; | ||
82 | |||
83 | /* | ||
84 | * Next, setup some sane values for the D-cache. | ||
85 | * | ||
86 | * On the SH5, these are pretty consistent with the I-cache settings, | ||
87 | * so we just copy over the existing definitions.. these can be fixed | ||
88 | * up later, especially if we add runtime CPU probing. | ||
89 | * | ||
90 | * Though in the meantime it saves us from having to duplicate all of | ||
91 | * the above definitions.. | ||
92 | */ | ||
93 | cpu_data->dcache = cpu_data->icache; | ||
94 | |||
95 | /* | ||
96 | * Setup any cache-related flags here | ||
97 | */ | ||
98 | #if defined(CONFIG_DCACHE_WRITE_THROUGH) | ||
99 | set_bit(SH_CACHE_MODE_WT, &(cpu_data->dcache.flags)); | ||
100 | #elif defined(CONFIG_DCACHE_WRITE_BACK) | ||
101 | set_bit(SH_CACHE_MODE_WB, &(cpu_data->dcache.flags)); | ||
102 | #endif | ||
103 | |||
104 | /* | ||
105 | * We also need to reserve a slot for the D-cache in the DTLB, so we | ||
106 | * do this now .. | ||
107 | */ | ||
108 | dtlb_cache_slot = sh64_get_wired_dtlb_entry(); | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | #ifdef CONFIG_DCACHE_DISABLED | ||
114 | #define sh64_dcache_purge_all() do { } while (0) | ||
115 | #define sh64_dcache_purge_coloured_phy_page(paddr, eaddr) do { } while (0) | ||
116 | #define sh64_dcache_purge_user_range(mm, start, end) do { } while (0) | ||
117 | #define sh64_dcache_purge_phy_page(paddr) do { } while (0) | ||
118 | #define sh64_dcache_purge_virt_page(mm, eaddr) do { } while (0) | ||
119 | #define sh64_dcache_purge_kernel_range(start, end) do { } while (0) | ||
120 | #define sh64_dcache_wback_current_user_range(start, end) do { } while (0) | ||
121 | #endif | ||
122 | |||
123 | /*##########################################################################*/ | ||
124 | |||
125 | /* From here onwards, a rewrite of the implementation, | ||
126 | by Richard.Curnow@superh.com. | ||
127 | |||
128 | The major changes in this compared to the old version are; | ||
129 | 1. use more selective purging through OCBP instead of using ALLOCO to purge | ||
130 | by natural replacement. This avoids purging out unrelated cache lines | ||
131 | that happen to be in the same set. | ||
132 | 2. exploit the APIs copy_user_page and clear_user_page better | ||
133 | 3. be more selective about I-cache purging, in particular use invalidate_all | ||
134 | more sparingly. | ||
135 | |||
136 | */ | ||
137 | |||
138 | /*########################################################################## | ||
139 | SUPPORT FUNCTIONS | ||
140 | ##########################################################################*/ | ||
141 | |||
142 | /****************************************************************************/ | ||
143 | /* The following group of functions deal with mapping and unmapping a temporary | ||
144 | page into the DTLB slot that have been set aside for our exclusive use. */ | ||
145 | /* In order to accomplish this, we use the generic interface for adding and | ||
146 | removing a wired slot entry as defined in arch/sh/mm/tlb-sh5.c */ | ||
147 | /****************************************************************************/ | ||
148 | |||
149 | static unsigned long slot_own_flags; | ||
150 | |||
151 | static inline void sh64_setup_dtlb_cache_slot(unsigned long eaddr, unsigned long asid, unsigned long paddr) | ||
152 | { | ||
153 | local_irq_save(slot_own_flags); | ||
154 | sh64_setup_tlb_slot(dtlb_cache_slot, eaddr, asid, paddr); | ||
155 | } | ||
156 | |||
157 | static inline void sh64_teardown_dtlb_cache_slot(void) | ||
158 | { | ||
159 | sh64_teardown_tlb_slot(dtlb_cache_slot); | ||
160 | local_irq_restore(slot_own_flags); | ||
161 | } | ||
162 | |||
163 | /****************************************************************************/ | ||
164 | |||
165 | #ifndef CONFIG_ICACHE_DISABLED | ||
166 | |||
167 | static void __inline__ sh64_icache_inv_all(void) | ||
168 | { | ||
169 | unsigned long long addr, flag, data; | ||
170 | unsigned int flags; | ||
171 | |||
172 | addr=ICCR0; | ||
173 | flag=ICCR0_ICI; | ||
174 | data=0; | ||
175 | |||
176 | /* Make this a critical section for safety (probably not strictly necessary.) */ | ||
177 | local_irq_save(flags); | ||
178 | |||
179 | /* Without %1 it gets unexplicably wrong */ | ||
180 | asm volatile("getcfg %3, 0, %0\n\t" | ||
181 | "or %0, %2, %0\n\t" | ||
182 | "putcfg %3, 0, %0\n\t" | ||
183 | "synci" | ||
184 | : "=&r" (data) | ||
185 | : "0" (data), "r" (flag), "r" (addr)); | ||
186 | |||
187 | local_irq_restore(flags); | ||
188 | } | ||
189 | |||
190 | static void sh64_icache_inv_kernel_range(unsigned long start, unsigned long end) | ||
191 | { | ||
192 | /* Invalidate range of addresses [start,end] from the I-cache, where | ||
193 | * the addresses lie in the kernel superpage. */ | ||
194 | |||
195 | unsigned long long ullend, addr, aligned_start; | ||
196 | #if (NEFF == 32) | ||
197 | aligned_start = (unsigned long long)(signed long long)(signed long) start; | ||
198 | #else | ||
199 | #error "NEFF != 32" | ||
200 | #endif | ||
201 | aligned_start &= L1_CACHE_ALIGN_MASK; | ||
202 | addr = aligned_start; | ||
203 | #if (NEFF == 32) | ||
204 | ullend = (unsigned long long) (signed long long) (signed long) end; | ||
205 | #else | ||
206 | #error "NEFF != 32" | ||
207 | #endif | ||
208 | while (addr <= ullend) { | ||
209 | asm __volatile__ ("icbi %0, 0" : : "r" (addr)); | ||
210 | addr += L1_CACHE_BYTES; | ||
211 | } | ||
212 | } | ||
213 | |||
214 | static void sh64_icache_inv_user_page(struct vm_area_struct *vma, unsigned long eaddr) | ||
215 | { | ||
216 | /* If we get called, we know that vma->vm_flags contains VM_EXEC. | ||
217 | Also, eaddr is page-aligned. */ | ||
218 | |||
219 | unsigned long long addr, end_addr; | ||
220 | unsigned long flags = 0; | ||
221 | unsigned long running_asid, vma_asid; | ||
222 | addr = eaddr; | ||
223 | end_addr = addr + PAGE_SIZE; | ||
224 | |||
225 | /* Check whether we can use the current ASID for the I-cache | ||
226 | invalidation. For example, if we're called via | ||
227 | access_process_vm->flush_cache_page->here, (e.g. when reading from | ||
228 | /proc), 'running_asid' will be that of the reader, not of the | ||
229 | victim. | ||
230 | |||
231 | Also, note the risk that we might get pre-empted between the ASID | ||
232 | compare and blocking IRQs, and before we regain control, the | ||
233 | pid->ASID mapping changes. However, the whole cache will get | ||
234 | invalidated when the mapping is renewed, so the worst that can | ||
235 | happen is that the loop below ends up invalidating somebody else's | ||
236 | cache entries. | ||
237 | */ | ||
238 | |||
239 | running_asid = get_asid(); | ||
240 | vma_asid = (vma->vm_mm->context & MMU_CONTEXT_ASID_MASK); | ||
241 | if (running_asid != vma_asid) { | ||
242 | local_irq_save(flags); | ||
243 | switch_and_save_asid(vma_asid); | ||
244 | } | ||
245 | while (addr < end_addr) { | ||
246 | /* Worth unrolling a little */ | ||
247 | asm __volatile__("icbi %0, 0" : : "r" (addr)); | ||
248 | asm __volatile__("icbi %0, 32" : : "r" (addr)); | ||
249 | asm __volatile__("icbi %0, 64" : : "r" (addr)); | ||
250 | asm __volatile__("icbi %0, 96" : : "r" (addr)); | ||
251 | addr += 128; | ||
252 | } | ||
253 | if (running_asid != vma_asid) { | ||
254 | switch_and_save_asid(running_asid); | ||
255 | local_irq_restore(flags); | ||
256 | } | ||
257 | } | ||
258 | |||
259 | /****************************************************************************/ | ||
260 | |||
261 | static void sh64_icache_inv_user_page_range(struct mm_struct *mm, | ||
262 | unsigned long start, unsigned long end) | ||
263 | { | ||
264 | /* Used for invalidating big chunks of I-cache, i.e. assume the range | ||
265 | is whole pages. If 'start' or 'end' is not page aligned, the code | ||
266 | is conservative and invalidates to the ends of the enclosing pages. | ||
267 | This is functionally OK, just a performance loss. */ | ||
268 | |||
269 | /* See the comments below in sh64_dcache_purge_user_range() regarding | ||
270 | the choice of algorithm. However, for the I-cache option (2) isn't | ||
271 | available because there are no physical tags so aliases can't be | ||
272 | resolved. The icbi instruction has to be used through the user | ||
273 | mapping. Because icbi is cheaper than ocbp on a cache hit, it | ||
274 | would be cheaper to use the selective code for a large range than is | ||
275 | possible with the D-cache. Just assume 64 for now as a working | ||
276 | figure. | ||
277 | */ | ||
278 | |||
279 | int n_pages; | ||
280 | |||
281 | if (!mm) return; | ||
282 | |||
283 | n_pages = ((end - start) >> PAGE_SHIFT); | ||
284 | if (n_pages >= 64) { | ||
285 | sh64_icache_inv_all(); | ||
286 | } else { | ||
287 | unsigned long aligned_start; | ||
288 | unsigned long eaddr; | ||
289 | unsigned long after_last_page_start; | ||
290 | unsigned long mm_asid, current_asid; | ||
291 | unsigned long long flags = 0ULL; | ||
292 | |||
293 | mm_asid = mm->context & MMU_CONTEXT_ASID_MASK; | ||
294 | current_asid = get_asid(); | ||
295 | |||
296 | if (mm_asid != current_asid) { | ||
297 | /* Switch ASID and run the invalidate loop under cli */ | ||
298 | local_irq_save(flags); | ||
299 | switch_and_save_asid(mm_asid); | ||
300 | } | ||
301 | |||
302 | aligned_start = start & PAGE_MASK; | ||
303 | after_last_page_start = PAGE_SIZE + ((end - 1) & PAGE_MASK); | ||
304 | |||
305 | while (aligned_start < after_last_page_start) { | ||
306 | struct vm_area_struct *vma; | ||
307 | unsigned long vma_end; | ||
308 | vma = find_vma(mm, aligned_start); | ||
309 | if (!vma || (aligned_start <= vma->vm_end)) { | ||
310 | /* Avoid getting stuck in an error condition */ | ||
311 | aligned_start += PAGE_SIZE; | ||
312 | continue; | ||
313 | } | ||
314 | vma_end = vma->vm_end; | ||
315 | if (vma->vm_flags & VM_EXEC) { | ||
316 | /* Executable */ | ||
317 | eaddr = aligned_start; | ||
318 | while (eaddr < vma_end) { | ||
319 | sh64_icache_inv_user_page(vma, eaddr); | ||
320 | eaddr += PAGE_SIZE; | ||
321 | } | ||
322 | } | ||
323 | aligned_start = vma->vm_end; /* Skip to start of next region */ | ||
324 | } | ||
325 | if (mm_asid != current_asid) { | ||
326 | switch_and_save_asid(current_asid); | ||
327 | local_irq_restore(flags); | ||
328 | } | ||
329 | } | ||
330 | } | ||
331 | |||
332 | static void sh64_icache_inv_user_small_range(struct mm_struct *mm, | ||
333 | unsigned long start, int len) | ||
334 | { | ||
335 | |||
336 | /* Invalidate a small range of user context I-cache, not necessarily | ||
337 | page (or even cache-line) aligned. */ | ||
338 | |||
339 | unsigned long long eaddr = start; | ||
340 | unsigned long long eaddr_end = start + len; | ||
341 | unsigned long current_asid, mm_asid; | ||
342 | unsigned long long flags; | ||
343 | unsigned long long epage_start; | ||
344 | |||
345 | /* Since this is used inside ptrace, the ASID in the mm context | ||
346 | typically won't match current_asid. We'll have to switch ASID to do | ||
347 | this. For safety, and given that the range will be small, do all | ||
348 | this under cli. | ||
349 | |||
350 | Note, there is a hazard that the ASID in mm->context is no longer | ||
351 | actually associated with mm, i.e. if the mm->context has started a | ||
352 | new cycle since mm was last active. However, this is just a | ||
353 | performance issue: all that happens is that we invalidate lines | ||
354 | belonging to another mm, so the owning process has to refill them | ||
355 | when that mm goes live again. mm itself can't have any cache | ||
356 | entries because there will have been a flush_cache_all when the new | ||
357 | mm->context cycle started. */ | ||
358 | |||
359 | /* Align to start of cache line. Otherwise, suppose len==8 and start | ||
360 | was at 32N+28 : the last 4 bytes wouldn't get invalidated. */ | ||
361 | eaddr = start & L1_CACHE_ALIGN_MASK; | ||
362 | eaddr_end = start + len; | ||
363 | |||
364 | local_irq_save(flags); | ||
365 | mm_asid = mm->context & MMU_CONTEXT_ASID_MASK; | ||
366 | current_asid = switch_and_save_asid(mm_asid); | ||
367 | |||
368 | epage_start = eaddr & PAGE_MASK; | ||
369 | |||
370 | while (eaddr < eaddr_end) | ||
371 | { | ||
372 | asm __volatile__("icbi %0, 0" : : "r" (eaddr)); | ||
373 | eaddr += L1_CACHE_BYTES; | ||
374 | } | ||
375 | switch_and_save_asid(current_asid); | ||
376 | local_irq_restore(flags); | ||
377 | } | ||
378 | |||
379 | static void sh64_icache_inv_current_user_range(unsigned long start, unsigned long end) | ||
380 | { | ||
381 | /* The icbi instruction never raises ITLBMISS. i.e. if there's not a | ||
382 | cache hit on the virtual tag the instruction ends there, without a | ||
383 | TLB lookup. */ | ||
384 | |||
385 | unsigned long long aligned_start; | ||
386 | unsigned long long ull_end; | ||
387 | unsigned long long addr; | ||
388 | |||
389 | ull_end = end; | ||
390 | |||
391 | /* Just invalidate over the range using the natural addresses. TLB | ||
392 | miss handling will be OK (TBC). Since it's for the current process, | ||
393 | either we're already in the right ASID context, or the ASIDs have | ||
394 | been recycled since we were last active in which case we might just | ||
395 | invalidate another processes I-cache entries : no worries, just a | ||
396 | performance drop for him. */ | ||
397 | aligned_start = start & L1_CACHE_ALIGN_MASK; | ||
398 | addr = aligned_start; | ||
399 | while (addr < ull_end) { | ||
400 | asm __volatile__ ("icbi %0, 0" : : "r" (addr)); | ||
401 | asm __volatile__ ("nop"); | ||
402 | asm __volatile__ ("nop"); | ||
403 | addr += L1_CACHE_BYTES; | ||
404 | } | ||
405 | } | ||
406 | |||
407 | #endif /* !CONFIG_ICACHE_DISABLED */ | ||
408 | |||
409 | /****************************************************************************/ | ||
410 | |||
411 | #ifndef CONFIG_DCACHE_DISABLED | ||
412 | |||
413 | /* Buffer used as the target of alloco instructions to purge data from cache | ||
414 | sets by natural eviction. -- RPC */ | ||
415 | #define DUMMY_ALLOCO_AREA_SIZE L1_CACHE_SIZE_BYTES + (1024 * 4) | ||
416 | static unsigned char dummy_alloco_area[DUMMY_ALLOCO_AREA_SIZE] __cacheline_aligned = { 0, }; | ||
417 | |||
418 | /****************************************************************************/ | ||
419 | |||
420 | static void __inline__ sh64_dcache_purge_sets(int sets_to_purge_base, int n_sets) | ||
421 | { | ||
422 | /* Purge all ways in a particular block of sets, specified by the base | ||
423 | set number and number of sets. Can handle wrap-around, if that's | ||
424 | needed. */ | ||
425 | |||
426 | int dummy_buffer_base_set; | ||
427 | unsigned long long eaddr, eaddr0, eaddr1; | ||
428 | int j; | ||
429 | int set_offset; | ||
430 | |||
431 | dummy_buffer_base_set = ((int)&dummy_alloco_area & cpu_data->dcache.idx_mask) >> cpu_data->dcache.entry_shift; | ||
432 | set_offset = sets_to_purge_base - dummy_buffer_base_set; | ||
433 | |||
434 | for (j=0; j<n_sets; j++, set_offset++) { | ||
435 | set_offset &= (cpu_data->dcache.sets - 1); | ||
436 | eaddr0 = (unsigned long long)dummy_alloco_area + (set_offset << cpu_data->dcache.entry_shift); | ||
437 | |||
438 | /* Do one alloco which hits the required set per cache way. For | ||
439 | write-back mode, this will purge the #ways resident lines. There's | ||
440 | little point unrolling this loop because the allocos stall more if | ||
441 | they're too close together. */ | ||
442 | eaddr1 = eaddr0 + cpu_data->dcache.way_ofs * cpu_data->dcache.ways; | ||
443 | for (eaddr=eaddr0; eaddr<eaddr1; eaddr+=cpu_data->dcache.way_ofs) { | ||
444 | asm __volatile__ ("alloco %0, 0" : : "r" (eaddr)); | ||
445 | asm __volatile__ ("synco"); /* TAKum03020 */ | ||
446 | } | ||
447 | |||
448 | eaddr1 = eaddr0 + cpu_data->dcache.way_ofs * cpu_data->dcache.ways; | ||
449 | for (eaddr=eaddr0; eaddr<eaddr1; eaddr+=cpu_data->dcache.way_ofs) { | ||
450 | /* Load from each address. Required because alloco is a NOP if | ||
451 | the cache is write-through. Write-through is a config option. */ | ||
452 | if (test_bit(SH_CACHE_MODE_WT, &(cpu_data->dcache.flags))) | ||
453 | *(volatile unsigned char *)(int)eaddr; | ||
454 | } | ||
455 | } | ||
456 | |||
457 | /* Don't use OCBI to invalidate the lines. That costs cycles directly. | ||
458 | If the dummy block is just left resident, it will naturally get | ||
459 | evicted as required. */ | ||
460 | |||
461 | return; | ||
462 | } | ||
463 | |||
464 | /****************************************************************************/ | ||
465 | |||
466 | static void sh64_dcache_purge_all(void) | ||
467 | { | ||
468 | /* Purge the entire contents of the dcache. The most efficient way to | ||
469 | achieve this is to use alloco instructions on a region of unused | ||
470 | memory equal in size to the cache, thereby causing the current | ||
471 | contents to be discarded by natural eviction. The alternative, | ||
472 | namely reading every tag, setting up a mapping for the corresponding | ||
473 | page and doing an OCBP for the line, would be much more expensive. | ||
474 | */ | ||
475 | |||
476 | sh64_dcache_purge_sets(0, cpu_data->dcache.sets); | ||
477 | |||
478 | return; | ||
479 | |||
480 | } | ||
481 | |||
482 | /****************************************************************************/ | ||
483 | |||
484 | static void sh64_dcache_purge_kernel_range(unsigned long start, unsigned long end) | ||
485 | { | ||
486 | /* Purge the range of addresses [start,end] from the D-cache. The | ||
487 | addresses lie in the superpage mapping. There's no harm if we | ||
488 | overpurge at either end - just a small performance loss. */ | ||
489 | unsigned long long ullend, addr, aligned_start; | ||
490 | #if (NEFF == 32) | ||
491 | aligned_start = (unsigned long long)(signed long long)(signed long) start; | ||
492 | #else | ||
493 | #error "NEFF != 32" | ||
494 | #endif | ||
495 | aligned_start &= L1_CACHE_ALIGN_MASK; | ||
496 | addr = aligned_start; | ||
497 | #if (NEFF == 32) | ||
498 | ullend = (unsigned long long) (signed long long) (signed long) end; | ||
499 | #else | ||
500 | #error "NEFF != 32" | ||
501 | #endif | ||
502 | while (addr <= ullend) { | ||
503 | asm __volatile__ ("ocbp %0, 0" : : "r" (addr)); | ||
504 | addr += L1_CACHE_BYTES; | ||
505 | } | ||
506 | return; | ||
507 | } | ||
508 | |||
509 | /* Assumes this address (+ (2**n_synbits) pages up from it) aren't used for | ||
510 | anything else in the kernel */ | ||
511 | #define MAGIC_PAGE0_START 0xffffffffec000000ULL | ||
512 | |||
513 | static void sh64_dcache_purge_coloured_phy_page(unsigned long paddr, unsigned long eaddr) | ||
514 | { | ||
515 | /* Purge the physical page 'paddr' from the cache. It's known that any | ||
516 | cache lines requiring attention have the same page colour as the the | ||
517 | address 'eaddr'. | ||
518 | |||
519 | This relies on the fact that the D-cache matches on physical tags | ||
520 | when no virtual tag matches. So we create an alias for the original | ||
521 | page and purge through that. (Alternatively, we could have done | ||
522 | this by switching ASID to match the original mapping and purged | ||
523 | through that, but that involves ASID switching cost + probably a | ||
524 | TLBMISS + refill anyway.) | ||
525 | */ | ||
526 | |||
527 | unsigned long long magic_page_start; | ||
528 | unsigned long long magic_eaddr, magic_eaddr_end; | ||
529 | |||
530 | magic_page_start = MAGIC_PAGE0_START + (eaddr & CACHE_OC_SYN_MASK); | ||
531 | |||
532 | /* As long as the kernel is not pre-emptible, this doesn't need to be | ||
533 | under cli/sti. */ | ||
534 | |||
535 | sh64_setup_dtlb_cache_slot(magic_page_start, get_asid(), paddr); | ||
536 | |||
537 | magic_eaddr = magic_page_start; | ||
538 | magic_eaddr_end = magic_eaddr + PAGE_SIZE; | ||
539 | while (magic_eaddr < magic_eaddr_end) { | ||
540 | /* Little point in unrolling this loop - the OCBPs are blocking | ||
541 | and won't go any quicker (i.e. the loop overhead is parallel | ||
542 | to part of the OCBP execution.) */ | ||
543 | asm __volatile__ ("ocbp %0, 0" : : "r" (magic_eaddr)); | ||
544 | magic_eaddr += L1_CACHE_BYTES; | ||
545 | } | ||
546 | |||
547 | sh64_teardown_dtlb_cache_slot(); | ||
548 | } | ||
549 | |||
550 | /****************************************************************************/ | ||
551 | |||
552 | static void sh64_dcache_purge_phy_page(unsigned long paddr) | ||
553 | { | ||
554 | /* Pure a page given its physical start address, by creating a | ||
555 | temporary 1 page mapping and purging across that. Even if we know | ||
556 | the virtual address (& vma or mm) of the page, the method here is | ||
557 | more elegant because it avoids issues of coping with page faults on | ||
558 | the purge instructions (i.e. no special-case code required in the | ||
559 | critical path in the TLB miss handling). */ | ||
560 | |||
561 | unsigned long long eaddr_start, eaddr, eaddr_end; | ||
562 | int i; | ||
563 | |||
564 | /* As long as the kernel is not pre-emptible, this doesn't need to be | ||
565 | under cli/sti. */ | ||
566 | |||
567 | eaddr_start = MAGIC_PAGE0_START; | ||
568 | for (i=0; i < (1 << CACHE_OC_N_SYNBITS); i++) { | ||
569 | sh64_setup_dtlb_cache_slot(eaddr_start, get_asid(), paddr); | ||
570 | |||
571 | eaddr = eaddr_start; | ||
572 | eaddr_end = eaddr + PAGE_SIZE; | ||
573 | while (eaddr < eaddr_end) { | ||
574 | asm __volatile__ ("ocbp %0, 0" : : "r" (eaddr)); | ||
575 | eaddr += L1_CACHE_BYTES; | ||
576 | } | ||
577 | |||
578 | sh64_teardown_dtlb_cache_slot(); | ||
579 | eaddr_start += PAGE_SIZE; | ||
580 | } | ||
581 | } | ||
582 | |||
583 | static void sh64_dcache_purge_user_pages(struct mm_struct *mm, | ||
584 | unsigned long addr, unsigned long end) | ||
585 | { | ||
586 | pgd_t *pgd; | ||
587 | pmd_t *pmd; | ||
588 | pte_t *pte; | ||
589 | pte_t entry; | ||
590 | spinlock_t *ptl; | ||
591 | unsigned long paddr; | ||
592 | |||
593 | if (!mm) | ||
594 | return; /* No way to find physical address of page */ | ||
595 | |||
596 | pgd = pgd_offset(mm, addr); | ||
597 | if (pgd_bad(*pgd)) | ||
598 | return; | ||
599 | |||
600 | pmd = pmd_offset(pgd, addr); | ||
601 | if (pmd_none(*pmd) || pmd_bad(*pmd)) | ||
602 | return; | ||
603 | |||
604 | pte = pte_offset_map_lock(mm, pmd, addr, &ptl); | ||
605 | do { | ||
606 | entry = *pte; | ||
607 | if (pte_none(entry) || !pte_present(entry)) | ||
608 | continue; | ||
609 | paddr = pte_val(entry) & PAGE_MASK; | ||
610 | sh64_dcache_purge_coloured_phy_page(paddr, addr); | ||
611 | } while (pte++, addr += PAGE_SIZE, addr != end); | ||
612 | pte_unmap_unlock(pte - 1, ptl); | ||
613 | } | ||
614 | /****************************************************************************/ | ||
615 | |||
616 | static void sh64_dcache_purge_user_range(struct mm_struct *mm, | ||
617 | unsigned long start, unsigned long end) | ||
618 | { | ||
619 | /* There are at least 5 choices for the implementation of this, with | ||
620 | pros (+), cons(-), comments(*): | ||
621 | |||
622 | 1. ocbp each line in the range through the original user's ASID | ||
623 | + no lines spuriously evicted | ||
624 | - tlbmiss handling (must either handle faults on demand => extra | ||
625 | special-case code in tlbmiss critical path), or map the page in | ||
626 | advance (=> flush_tlb_range in advance to avoid multiple hits) | ||
627 | - ASID switching | ||
628 | - expensive for large ranges | ||
629 | |||
630 | 2. temporarily map each page in the range to a special effective | ||
631 | address and ocbp through the temporary mapping; relies on the | ||
632 | fact that SH-5 OCB* always do TLB lookup and match on ptags (they | ||
633 | never look at the etags) | ||
634 | + no spurious evictions | ||
635 | - expensive for large ranges | ||
636 | * surely cheaper than (1) | ||
637 | |||
638 | 3. walk all the lines in the cache, check the tags, if a match | ||
639 | occurs create a page mapping to ocbp the line through | ||
640 | + no spurious evictions | ||
641 | - tag inspection overhead | ||
642 | - (especially for small ranges) | ||
643 | - potential cost of setting up/tearing down page mapping for | ||
644 | every line that matches the range | ||
645 | * cost partly independent of range size | ||
646 | |||
647 | 4. walk all the lines in the cache, check the tags, if a match | ||
648 | occurs use 4 * alloco to purge the line (+3 other probably | ||
649 | innocent victims) by natural eviction | ||
650 | + no tlb mapping overheads | ||
651 | - spurious evictions | ||
652 | - tag inspection overhead | ||
653 | |||
654 | 5. implement like flush_cache_all | ||
655 | + no tag inspection overhead | ||
656 | - spurious evictions | ||
657 | - bad for small ranges | ||
658 | |||
659 | (1) can be ruled out as more expensive than (2). (2) appears best | ||
660 | for small ranges. The choice between (3), (4) and (5) for large | ||
661 | ranges and the range size for the large/small boundary need | ||
662 | benchmarking to determine. | ||
663 | |||
664 | For now use approach (2) for small ranges and (5) for large ones. | ||
665 | |||
666 | */ | ||
667 | |||
668 | int n_pages; | ||
669 | |||
670 | n_pages = ((end - start) >> PAGE_SHIFT); | ||
671 | if (n_pages >= 64 || ((start ^ (end - 1)) & PMD_MASK)) { | ||
672 | #if 1 | ||
673 | sh64_dcache_purge_all(); | ||
674 | #else | ||
675 | unsigned long long set, way; | ||
676 | unsigned long mm_asid = mm->context & MMU_CONTEXT_ASID_MASK; | ||
677 | for (set = 0; set < cpu_data->dcache.sets; set++) { | ||
678 | unsigned long long set_base_config_addr = CACHE_OC_ADDRESS_ARRAY + (set << cpu_data->dcache.set_shift); | ||
679 | for (way = 0; way < cpu_data->dcache.ways; way++) { | ||
680 | unsigned long long config_addr = set_base_config_addr + (way << cpu_data->dcache.way_step_shift); | ||
681 | unsigned long long tag0; | ||
682 | unsigned long line_valid; | ||
683 | |||
684 | asm __volatile__("getcfg %1, 0, %0" : "=r" (tag0) : "r" (config_addr)); | ||
685 | line_valid = tag0 & SH_CACHE_VALID; | ||
686 | if (line_valid) { | ||
687 | unsigned long cache_asid; | ||
688 | unsigned long epn; | ||
689 | |||
690 | cache_asid = (tag0 & cpu_data->dcache.asid_mask) >> cpu_data->dcache.asid_shift; | ||
691 | /* The next line needs some | ||
692 | explanation. The virtual tags | ||
693 | encode bits [31:13] of the virtual | ||
694 | address, bit [12] of the 'tag' being | ||
695 | implied by the cache set index. */ | ||
696 | epn = (tag0 & cpu_data->dcache.epn_mask) | ((set & 0x80) << cpu_data->dcache.entry_shift); | ||
697 | |||
698 | if ((cache_asid == mm_asid) && (start <= epn) && (epn < end)) { | ||
699 | /* TODO : could optimise this | ||
700 | call by batching multiple | ||
701 | adjacent sets together. */ | ||
702 | sh64_dcache_purge_sets(set, 1); | ||
703 | break; /* Don't waste time inspecting other ways for this set */ | ||
704 | } | ||
705 | } | ||
706 | } | ||
707 | } | ||
708 | #endif | ||
709 | } else { | ||
710 | /* Small range, covered by a single page table page */ | ||
711 | start &= PAGE_MASK; /* should already be so */ | ||
712 | end = PAGE_ALIGN(end); /* should already be so */ | ||
713 | sh64_dcache_purge_user_pages(mm, start, end); | ||
714 | } | ||
715 | return; | ||
716 | } | ||
717 | |||
718 | static void sh64_dcache_wback_current_user_range(unsigned long start, unsigned long end) | ||
719 | { | ||
720 | unsigned long long aligned_start; | ||
721 | unsigned long long ull_end; | ||
722 | unsigned long long addr; | ||
723 | |||
724 | ull_end = end; | ||
725 | |||
726 | /* Just wback over the range using the natural addresses. TLB miss | ||
727 | handling will be OK (TBC) : the range has just been written to by | ||
728 | the signal frame setup code, so the PTEs must exist. | ||
729 | |||
730 | Note, if we have CONFIG_PREEMPT and get preempted inside this loop, | ||
731 | it doesn't matter, even if the pid->ASID mapping changes whilst | ||
732 | we're away. In that case the cache will have been flushed when the | ||
733 | mapping was renewed. So the writebacks below will be nugatory (and | ||
734 | we'll doubtless have to fault the TLB entry/ies in again with the | ||
735 | new ASID), but it's a rare case. | ||
736 | */ | ||
737 | aligned_start = start & L1_CACHE_ALIGN_MASK; | ||
738 | addr = aligned_start; | ||
739 | while (addr < ull_end) { | ||
740 | asm __volatile__ ("ocbwb %0, 0" : : "r" (addr)); | ||
741 | addr += L1_CACHE_BYTES; | ||
742 | } | ||
743 | } | ||
744 | |||
745 | /****************************************************************************/ | ||
746 | |||
747 | /* These *MUST* lie in an area of virtual address space that's otherwise unused. */ | ||
748 | #define UNIQUE_EADDR_START 0xe0000000UL | ||
749 | #define UNIQUE_EADDR_END 0xe8000000UL | ||
750 | |||
751 | static unsigned long sh64_make_unique_eaddr(unsigned long user_eaddr, unsigned long paddr) | ||
752 | { | ||
753 | /* Given a physical address paddr, and a user virtual address | ||
754 | user_eaddr which will eventually be mapped to it, create a one-off | ||
755 | kernel-private eaddr mapped to the same paddr. This is used for | ||
756 | creating special destination pages for copy_user_page and | ||
757 | clear_user_page */ | ||
758 | |||
759 | static unsigned long current_pointer = UNIQUE_EADDR_START; | ||
760 | unsigned long coloured_pointer; | ||
761 | |||
762 | if (current_pointer == UNIQUE_EADDR_END) { | ||
763 | sh64_dcache_purge_all(); | ||
764 | current_pointer = UNIQUE_EADDR_START; | ||
765 | } | ||
766 | |||
767 | coloured_pointer = (current_pointer & ~CACHE_OC_SYN_MASK) | (user_eaddr & CACHE_OC_SYN_MASK); | ||
768 | sh64_setup_dtlb_cache_slot(coloured_pointer, get_asid(), paddr); | ||
769 | |||
770 | current_pointer += (PAGE_SIZE << CACHE_OC_N_SYNBITS); | ||
771 | |||
772 | return coloured_pointer; | ||
773 | } | ||
774 | |||
775 | /****************************************************************************/ | ||
776 | |||
777 | static void sh64_copy_user_page_coloured(void *to, void *from, unsigned long address) | ||
778 | { | ||
779 | void *coloured_to; | ||
780 | |||
781 | /* Discard any existing cache entries of the wrong colour. These are | ||
782 | present quite often, if the kernel has recently used the page | ||
783 | internally, then given it up, then it's been allocated to the user. | ||
784 | */ | ||
785 | sh64_dcache_purge_coloured_phy_page(__pa(to), (unsigned long) to); | ||
786 | |||
787 | coloured_to = (void *) sh64_make_unique_eaddr(address, __pa(to)); | ||
788 | sh64_page_copy(from, coloured_to); | ||
789 | |||
790 | sh64_teardown_dtlb_cache_slot(); | ||
791 | } | ||
792 | |||
793 | static void sh64_clear_user_page_coloured(void *to, unsigned long address) | ||
794 | { | ||
795 | void *coloured_to; | ||
796 | |||
797 | /* Discard any existing kernel-originated lines of the wrong colour (as | ||
798 | above) */ | ||
799 | sh64_dcache_purge_coloured_phy_page(__pa(to), (unsigned long) to); | ||
800 | |||
801 | coloured_to = (void *) sh64_make_unique_eaddr(address, __pa(to)); | ||
802 | sh64_page_clear(coloured_to); | ||
803 | |||
804 | sh64_teardown_dtlb_cache_slot(); | ||
805 | } | ||
806 | |||
807 | #endif /* !CONFIG_DCACHE_DISABLED */ | ||
808 | |||
809 | /****************************************************************************/ | ||
810 | |||
811 | /*########################################################################## | ||
812 | EXTERNALLY CALLABLE API. | ||
813 | ##########################################################################*/ | ||
814 | |||
815 | /* These functions are described in Documentation/cachetlb.txt. | ||
816 | Each one of these functions varies in behaviour depending on whether the | ||
817 | I-cache and/or D-cache are configured out. | ||
818 | |||
819 | Note that the Linux term 'flush' corresponds to what is termed 'purge' in | ||
820 | the sh/sh64 jargon for the D-cache, i.e. write back dirty data then | ||
821 | invalidate the cache lines, and 'invalidate' for the I-cache. | ||
822 | */ | ||
823 | |||
824 | #undef FLUSH_TRACE | ||
825 | |||
826 | void flush_cache_all(void) | ||
827 | { | ||
828 | /* Invalidate the entire contents of both caches, after writing back to | ||
829 | memory any dirty data from the D-cache. */ | ||
830 | sh64_dcache_purge_all(); | ||
831 | sh64_icache_inv_all(); | ||
832 | } | ||
833 | |||
834 | /****************************************************************************/ | ||
835 | |||
836 | void flush_cache_mm(struct mm_struct *mm) | ||
837 | { | ||
838 | /* Invalidate an entire user-address space from both caches, after | ||
839 | writing back dirty data (e.g. for shared mmap etc). */ | ||
840 | |||
841 | /* This could be coded selectively by inspecting all the tags then | ||
842 | doing 4*alloco on any set containing a match (as for | ||
843 | flush_cache_range), but fork/exit/execve (where this is called from) | ||
844 | are expensive anyway. */ | ||
845 | |||
846 | /* Have to do a purge here, despite the comments re I-cache below. | ||
847 | There could be odd-coloured dirty data associated with the mm still | ||
848 | in the cache - if this gets written out through natural eviction | ||
849 | after the kernel has reused the page there will be chaos. | ||
850 | */ | ||
851 | |||
852 | sh64_dcache_purge_all(); | ||
853 | |||
854 | /* The mm being torn down won't ever be active again, so any Icache | ||
855 | lines tagged with its ASID won't be visible for the rest of the | ||
856 | lifetime of this ASID cycle. Before the ASID gets reused, there | ||
857 | will be a flush_cache_all. Hence we don't need to touch the | ||
858 | I-cache. This is similar to the lack of action needed in | ||
859 | flush_tlb_mm - see fault.c. */ | ||
860 | } | ||
861 | |||
862 | /****************************************************************************/ | ||
863 | |||
864 | void flush_cache_range(struct vm_area_struct *vma, unsigned long start, | ||
865 | unsigned long end) | ||
866 | { | ||
867 | struct mm_struct *mm = vma->vm_mm; | ||
868 | |||
869 | /* Invalidate (from both caches) the range [start,end) of virtual | ||
870 | addresses from the user address space specified by mm, after writing | ||
871 | back any dirty data. | ||
872 | |||
873 | Note, 'end' is 1 byte beyond the end of the range to flush. */ | ||
874 | |||
875 | sh64_dcache_purge_user_range(mm, start, end); | ||
876 | sh64_icache_inv_user_page_range(mm, start, end); | ||
877 | } | ||
878 | |||
879 | /****************************************************************************/ | ||
880 | |||
881 | void flush_cache_page(struct vm_area_struct *vma, unsigned long eaddr, unsigned long pfn) | ||
882 | { | ||
883 | /* Invalidate any entries in either cache for the vma within the user | ||
884 | address space vma->vm_mm for the page starting at virtual address | ||
885 | 'eaddr'. This seems to be used primarily in breaking COW. Note, | ||
886 | the I-cache must be searched too in case the page in question is | ||
887 | both writable and being executed from (e.g. stack trampolines.) | ||
888 | |||
889 | Note, this is called with pte lock held. | ||
890 | */ | ||
891 | |||
892 | sh64_dcache_purge_phy_page(pfn << PAGE_SHIFT); | ||
893 | |||
894 | if (vma->vm_flags & VM_EXEC) { | ||
895 | sh64_icache_inv_user_page(vma, eaddr); | ||
896 | } | ||
897 | } | ||
898 | |||
899 | /****************************************************************************/ | ||
900 | |||
901 | #ifndef CONFIG_DCACHE_DISABLED | ||
902 | |||
903 | void copy_user_page(void *to, void *from, unsigned long address, struct page *page) | ||
904 | { | ||
905 | /* 'from' and 'to' are kernel virtual addresses (within the superpage | ||
906 | mapping of the physical RAM). 'address' is the user virtual address | ||
907 | where the copy 'to' will be mapped after. This allows a custom | ||
908 | mapping to be used to ensure that the new copy is placed in the | ||
909 | right cache sets for the user to see it without having to bounce it | ||
910 | out via memory. Note however : the call to flush_page_to_ram in | ||
911 | (generic)/mm/memory.c:(break_cow) undoes all this good work in that one | ||
912 | very important case! | ||
913 | |||
914 | TBD : can we guarantee that on every call, any cache entries for | ||
915 | 'from' are in the same colour sets as 'address' also? i.e. is this | ||
916 | always used just to deal with COW? (I suspect not). */ | ||
917 | |||
918 | /* There are two possibilities here for when the page 'from' was last accessed: | ||
919 | * by the kernel : this is OK, no purge required. | ||
920 | * by the/a user (e.g. for break_COW) : need to purge. | ||
921 | |||
922 | If the potential user mapping at 'address' is the same colour as | ||
923 | 'from' there is no need to purge any cache lines from the 'from' | ||
924 | page mapped into cache sets of colour 'address'. (The copy will be | ||
925 | accessing the page through 'from'). | ||
926 | */ | ||
927 | |||
928 | if (((address ^ (unsigned long) from) & CACHE_OC_SYN_MASK) != 0) { | ||
929 | sh64_dcache_purge_coloured_phy_page(__pa(from), address); | ||
930 | } | ||
931 | |||
932 | if (((address ^ (unsigned long) to) & CACHE_OC_SYN_MASK) == 0) { | ||
933 | /* No synonym problem on destination */ | ||
934 | sh64_page_copy(from, to); | ||
935 | } else { | ||
936 | sh64_copy_user_page_coloured(to, from, address); | ||
937 | } | ||
938 | |||
939 | /* Note, don't need to flush 'from' page from the cache again - it's | ||
940 | done anyway by the generic code */ | ||
941 | } | ||
942 | |||
943 | void clear_user_page(void *to, unsigned long address, struct page *page) | ||
944 | { | ||
945 | /* 'to' is a kernel virtual address (within the superpage | ||
946 | mapping of the physical RAM). 'address' is the user virtual address | ||
947 | where the 'to' page will be mapped after. This allows a custom | ||
948 | mapping to be used to ensure that the new copy is placed in the | ||
949 | right cache sets for the user to see it without having to bounce it | ||
950 | out via memory. | ||
951 | */ | ||
952 | |||
953 | if (((address ^ (unsigned long) to) & CACHE_OC_SYN_MASK) == 0) { | ||
954 | /* No synonym problem on destination */ | ||
955 | sh64_page_clear(to); | ||
956 | } else { | ||
957 | sh64_clear_user_page_coloured(to, address); | ||
958 | } | ||
959 | } | ||
960 | |||
961 | #endif /* !CONFIG_DCACHE_DISABLED */ | ||
962 | |||
963 | /****************************************************************************/ | ||
964 | |||
965 | void flush_dcache_page(struct page *page) | ||
966 | { | ||
967 | sh64_dcache_purge_phy_page(page_to_phys(page)); | ||
968 | wmb(); | ||
969 | } | ||
970 | |||
971 | /****************************************************************************/ | ||
972 | |||
973 | void flush_icache_range(unsigned long start, unsigned long end) | ||
974 | { | ||
975 | /* Flush the range [start,end] of kernel virtual adddress space from | ||
976 | the I-cache. The corresponding range must be purged from the | ||
977 | D-cache also because the SH-5 doesn't have cache snooping between | ||
978 | the caches. The addresses will be visible through the superpage | ||
979 | mapping, therefore it's guaranteed that there no cache entries for | ||
980 | the range in cache sets of the wrong colour. | ||
981 | |||
982 | Primarily used for cohering the I-cache after a module has | ||
983 | been loaded. */ | ||
984 | |||
985 | /* We also make sure to purge the same range from the D-cache since | ||
986 | flush_page_to_ram() won't be doing this for us! */ | ||
987 | |||
988 | sh64_dcache_purge_kernel_range(start, end); | ||
989 | wmb(); | ||
990 | sh64_icache_inv_kernel_range(start, end); | ||
991 | } | ||
992 | |||
993 | /****************************************************************************/ | ||
994 | |||
995 | void flush_icache_user_range(struct vm_area_struct *vma, | ||
996 | struct page *page, unsigned long addr, int len) | ||
997 | { | ||
998 | /* Flush the range of user (defined by vma->vm_mm) address space | ||
999 | starting at 'addr' for 'len' bytes from the cache. The range does | ||
1000 | not straddle a page boundary, the unique physical page containing | ||
1001 | the range is 'page'. This seems to be used mainly for invalidating | ||
1002 | an address range following a poke into the program text through the | ||
1003 | ptrace() call from another process (e.g. for BRK instruction | ||
1004 | insertion). */ | ||
1005 | |||
1006 | sh64_dcache_purge_coloured_phy_page(page_to_phys(page), addr); | ||
1007 | mb(); | ||
1008 | |||
1009 | if (vma->vm_flags & VM_EXEC) { | ||
1010 | sh64_icache_inv_user_small_range(vma->vm_mm, addr, len); | ||
1011 | } | ||
1012 | } | ||
1013 | |||
1014 | /*########################################################################## | ||
1015 | ARCH/SH64 PRIVATE CALLABLE API. | ||
1016 | ##########################################################################*/ | ||
1017 | |||
1018 | void flush_cache_sigtramp(unsigned long start, unsigned long end) | ||
1019 | { | ||
1020 | /* For the address range [start,end), write back the data from the | ||
1021 | D-cache and invalidate the corresponding region of the I-cache for | ||
1022 | the current process. Used to flush signal trampolines on the stack | ||
1023 | to make them executable. */ | ||
1024 | |||
1025 | sh64_dcache_wback_current_user_range(start, end); | ||
1026 | wmb(); | ||
1027 | sh64_icache_inv_current_user_range(start, end); | ||
1028 | } | ||
1029 | |||
diff --git a/arch/sh/mm/cache-sh7705.c b/arch/sh/mm/cache-sh7705.c index 4896d7376926..22dacc778823 100644 --- a/arch/sh/mm/cache-sh7705.c +++ b/arch/sh/mm/cache-sh7705.c | |||
@@ -71,7 +71,7 @@ void flush_icache_range(unsigned long start, unsigned long end) | |||
71 | /* | 71 | /* |
72 | * Writeback&Invalidate the D-cache of the page | 72 | * Writeback&Invalidate the D-cache of the page |
73 | */ | 73 | */ |
74 | static void __flush_dcache_page(unsigned long phys) | 74 | static void __uses_jump_to_uncached __flush_dcache_page(unsigned long phys) |
75 | { | 75 | { |
76 | unsigned long ways, waysize, addrstart; | 76 | unsigned long ways, waysize, addrstart; |
77 | unsigned long flags; | 77 | unsigned long flags; |
@@ -92,7 +92,7 @@ static void __flush_dcache_page(unsigned long phys) | |||
92 | * possible. | 92 | * possible. |
93 | */ | 93 | */ |
94 | local_irq_save(flags); | 94 | local_irq_save(flags); |
95 | jump_to_P2(); | 95 | jump_to_uncached(); |
96 | 96 | ||
97 | ways = current_cpu_data.dcache.ways; | 97 | ways = current_cpu_data.dcache.ways; |
98 | waysize = current_cpu_data.dcache.sets; | 98 | waysize = current_cpu_data.dcache.sets; |
@@ -118,7 +118,7 @@ static void __flush_dcache_page(unsigned long phys) | |||
118 | addrstart += current_cpu_data.dcache.way_incr; | 118 | addrstart += current_cpu_data.dcache.way_incr; |
119 | } while (--ways); | 119 | } while (--ways); |
120 | 120 | ||
121 | back_to_P1(); | 121 | back_to_cached(); |
122 | local_irq_restore(flags); | 122 | local_irq_restore(flags); |
123 | } | 123 | } |
124 | 124 | ||
@@ -132,15 +132,15 @@ void flush_dcache_page(struct page *page) | |||
132 | __flush_dcache_page(PHYSADDR(page_address(page))); | 132 | __flush_dcache_page(PHYSADDR(page_address(page))); |
133 | } | 133 | } |
134 | 134 | ||
135 | void flush_cache_all(void) | 135 | void __uses_jump_to_uncached flush_cache_all(void) |
136 | { | 136 | { |
137 | unsigned long flags; | 137 | unsigned long flags; |
138 | 138 | ||
139 | local_irq_save(flags); | 139 | local_irq_save(flags); |
140 | jump_to_P2(); | 140 | jump_to_uncached(); |
141 | 141 | ||
142 | cache_wback_all(); | 142 | cache_wback_all(); |
143 | back_to_P1(); | 143 | back_to_cached(); |
144 | local_irq_restore(flags); | 144 | local_irq_restore(flags); |
145 | } | 145 | } |
146 | 146 | ||
diff --git a/arch/sh/mm/clear_page.S b/arch/sh/mm/clear_page.S deleted file mode 100644 index 7a7c81ee3f01..000000000000 --- a/arch/sh/mm/clear_page.S +++ /dev/null | |||
@@ -1,152 +0,0 @@ | |||
1 | /* | ||
2 | * __clear_user_page, __clear_user, clear_page implementation of SuperH | ||
3 | * | ||
4 | * Copyright (C) 2001 Kaz Kojima | ||
5 | * Copyright (C) 2001, 2002 Niibe Yutaka | ||
6 | * Copyright (C) 2006 Paul Mundt | ||
7 | */ | ||
8 | #include <linux/linkage.h> | ||
9 | #include <asm/page.h> | ||
10 | |||
11 | /* | ||
12 | * clear_page_slow | ||
13 | * @to: P1 address | ||
14 | * | ||
15 | * void clear_page_slow(void *to) | ||
16 | */ | ||
17 | |||
18 | /* | ||
19 | * r0 --- scratch | ||
20 | * r4 --- to | ||
21 | * r5 --- to + PAGE_SIZE | ||
22 | */ | ||
23 | ENTRY(clear_page_slow) | ||
24 | mov r4,r5 | ||
25 | mov.l .Llimit,r0 | ||
26 | add r0,r5 | ||
27 | mov #0,r0 | ||
28 | ! | ||
29 | 1: | ||
30 | #if defined(CONFIG_CPU_SH3) | ||
31 | mov.l r0,@r4 | ||
32 | #elif defined(CONFIG_CPU_SH4) | ||
33 | movca.l r0,@r4 | ||
34 | mov r4,r1 | ||
35 | #endif | ||
36 | add #32,r4 | ||
37 | mov.l r0,@-r4 | ||
38 | mov.l r0,@-r4 | ||
39 | mov.l r0,@-r4 | ||
40 | mov.l r0,@-r4 | ||
41 | mov.l r0,@-r4 | ||
42 | mov.l r0,@-r4 | ||
43 | mov.l r0,@-r4 | ||
44 | #if defined(CONFIG_CPU_SH4) | ||
45 | ocbwb @r1 | ||
46 | #endif | ||
47 | cmp/eq r5,r4 | ||
48 | bf/s 1b | ||
49 | add #28,r4 | ||
50 | ! | ||
51 | rts | ||
52 | nop | ||
53 | .Llimit: .long (PAGE_SIZE-28) | ||
54 | |||
55 | ENTRY(__clear_user) | ||
56 | ! | ||
57 | mov #0, r0 | ||
58 | mov #0xe0, r1 ! 0xffffffe0 | ||
59 | ! | ||
60 | ! r4..(r4+31)&~32 -------- not aligned [ Area 0 ] | ||
61 | ! (r4+31)&~32..(r4+r5)&~32 -------- aligned [ Area 1 ] | ||
62 | ! (r4+r5)&~32..r4+r5 -------- not aligned [ Area 2 ] | ||
63 | ! | ||
64 | ! Clear area 0 | ||
65 | mov r4, r2 | ||
66 | ! | ||
67 | tst r1, r5 ! length < 32 | ||
68 | bt .Larea2 ! skip to remainder | ||
69 | ! | ||
70 | add #31, r2 | ||
71 | and r1, r2 | ||
72 | cmp/eq r4, r2 | ||
73 | bt .Larea1 | ||
74 | mov r2, r3 | ||
75 | sub r4, r3 | ||
76 | mov r3, r7 | ||
77 | mov r4, r2 | ||
78 | ! | ||
79 | .L0: dt r3 | ||
80 | 0: mov.b r0, @r2 | ||
81 | bf/s .L0 | ||
82 | add #1, r2 | ||
83 | ! | ||
84 | sub r7, r5 | ||
85 | mov r2, r4 | ||
86 | .Larea1: | ||
87 | mov r4, r3 | ||
88 | add r5, r3 | ||
89 | and r1, r3 | ||
90 | cmp/hi r2, r3 | ||
91 | bf .Larea2 | ||
92 | ! | ||
93 | ! Clear area 1 | ||
94 | #if defined(CONFIG_CPU_SH4) | ||
95 | 1: movca.l r0, @r2 | ||
96 | #else | ||
97 | 1: mov.l r0, @r2 | ||
98 | #endif | ||
99 | add #4, r2 | ||
100 | 2: mov.l r0, @r2 | ||
101 | add #4, r2 | ||
102 | 3: mov.l r0, @r2 | ||
103 | add #4, r2 | ||
104 | 4: mov.l r0, @r2 | ||
105 | add #4, r2 | ||
106 | 5: mov.l r0, @r2 | ||
107 | add #4, r2 | ||
108 | 6: mov.l r0, @r2 | ||
109 | add #4, r2 | ||
110 | 7: mov.l r0, @r2 | ||
111 | add #4, r2 | ||
112 | 8: mov.l r0, @r2 | ||
113 | add #4, r2 | ||
114 | cmp/hi r2, r3 | ||
115 | bt/s 1b | ||
116 | nop | ||
117 | ! | ||
118 | ! Clear area 2 | ||
119 | .Larea2: | ||
120 | mov r4, r3 | ||
121 | add r5, r3 | ||
122 | cmp/hs r3, r2 | ||
123 | bt/s .Ldone | ||
124 | sub r2, r3 | ||
125 | .L2: dt r3 | ||
126 | 9: mov.b r0, @r2 | ||
127 | bf/s .L2 | ||
128 | add #1, r2 | ||
129 | ! | ||
130 | .Ldone: rts | ||
131 | mov #0, r0 ! return 0 as normal return | ||
132 | |||
133 | ! return the number of bytes remained | ||
134 | .Lbad_clear_user: | ||
135 | mov r4, r0 | ||
136 | add r5, r0 | ||
137 | rts | ||
138 | sub r2, r0 | ||
139 | |||
140 | .section __ex_table,"a" | ||
141 | .align 2 | ||
142 | .long 0b, .Lbad_clear_user | ||
143 | .long 1b, .Lbad_clear_user | ||
144 | .long 2b, .Lbad_clear_user | ||
145 | .long 3b, .Lbad_clear_user | ||
146 | .long 4b, .Lbad_clear_user | ||
147 | .long 5b, .Lbad_clear_user | ||
148 | .long 6b, .Lbad_clear_user | ||
149 | .long 7b, .Lbad_clear_user | ||
150 | .long 8b, .Lbad_clear_user | ||
151 | .long 9b, .Lbad_clear_user | ||
152 | .previous | ||
diff --git a/arch/sh/mm/consistent.c b/arch/sh/mm/consistent.c index e220c29a3c00..7b2131c9eeda 100644 --- a/arch/sh/mm/consistent.c +++ b/arch/sh/mm/consistent.c | |||
@@ -1,7 +1,9 @@ | |||
1 | /* | 1 | /* |
2 | * arch/sh/mm/consistent.c | 2 | * arch/sh/mm/consistent.c |
3 | * | 3 | * |
4 | * Copyright (C) 2004 Paul Mundt | 4 | * Copyright (C) 2004 - 2007 Paul Mundt |
5 | * | ||
6 | * Declared coherent memory functions based on arch/x86/kernel/pci-dma_32.c | ||
5 | * | 7 | * |
6 | * This file is subject to the terms and conditions of the GNU General Public | 8 | * This file is subject to the terms and conditions of the GNU General Public |
7 | * License. See the file "COPYING" in the main directory of this archive | 9 | * License. See the file "COPYING" in the main directory of this archive |
@@ -13,58 +15,152 @@ | |||
13 | #include <asm/addrspace.h> | 15 | #include <asm/addrspace.h> |
14 | #include <asm/io.h> | 16 | #include <asm/io.h> |
15 | 17 | ||
16 | void *consistent_alloc(gfp_t gfp, size_t size, dma_addr_t *handle) | 18 | struct dma_coherent_mem { |
19 | void *virt_base; | ||
20 | u32 device_base; | ||
21 | int size; | ||
22 | int flags; | ||
23 | unsigned long *bitmap; | ||
24 | }; | ||
25 | |||
26 | void *dma_alloc_coherent(struct device *dev, size_t size, | ||
27 | dma_addr_t *dma_handle, gfp_t gfp) | ||
17 | { | 28 | { |
18 | struct page *page, *end, *free; | ||
19 | void *ret; | 29 | void *ret; |
20 | int order; | 30 | struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; |
31 | int order = get_order(size); | ||
21 | 32 | ||
22 | size = PAGE_ALIGN(size); | 33 | if (mem) { |
23 | order = get_order(size); | 34 | int page = bitmap_find_free_region(mem->bitmap, mem->size, |
35 | order); | ||
36 | if (page >= 0) { | ||
37 | *dma_handle = mem->device_base + (page << PAGE_SHIFT); | ||
38 | ret = mem->virt_base + (page << PAGE_SHIFT); | ||
39 | memset(ret, 0, size); | ||
40 | return ret; | ||
41 | } | ||
42 | if (mem->flags & DMA_MEMORY_EXCLUSIVE) | ||
43 | return NULL; | ||
44 | } | ||
24 | 45 | ||
25 | page = alloc_pages(gfp, order); | 46 | ret = (void *)__get_free_pages(gfp, order); |
26 | if (!page) | ||
27 | return NULL; | ||
28 | split_page(page, order); | ||
29 | 47 | ||
30 | ret = page_address(page); | 48 | if (ret != NULL) { |
31 | memset(ret, 0, size); | 49 | memset(ret, 0, size); |
32 | *handle = virt_to_phys(ret); | 50 | /* |
51 | * Pages from the page allocator may have data present in | ||
52 | * cache. So flush the cache before using uncached memory. | ||
53 | */ | ||
54 | dma_cache_sync(NULL, ret, size, DMA_BIDIRECTIONAL); | ||
55 | *dma_handle = virt_to_phys(ret); | ||
56 | } | ||
57 | return ret; | ||
58 | } | ||
59 | EXPORT_SYMBOL(dma_alloc_coherent); | ||
33 | 60 | ||
34 | /* | 61 | void dma_free_coherent(struct device *dev, size_t size, |
35 | * We must flush the cache before we pass it on to the device | 62 | void *vaddr, dma_addr_t dma_handle) |
36 | */ | 63 | { |
37 | __flush_purge_region(ret, size); | 64 | struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; |
65 | int order = get_order(size); | ||
38 | 66 | ||
39 | page = virt_to_page(ret); | 67 | if (mem && vaddr >= mem->virt_base && vaddr < (mem->virt_base + (mem->size << PAGE_SHIFT))) { |
40 | free = page + (size >> PAGE_SHIFT); | 68 | int page = (vaddr - mem->virt_base) >> PAGE_SHIFT; |
41 | end = page + (1 << order); | ||
42 | 69 | ||
43 | while (++page < end) { | 70 | bitmap_release_region(mem->bitmap, page, order); |
44 | /* Free any unused pages */ | 71 | } else { |
45 | if (page >= free) { | 72 | WARN_ON(irqs_disabled()); /* for portability */ |
46 | __free_page(page); | 73 | BUG_ON(mem && mem->flags & DMA_MEMORY_EXCLUSIVE); |
47 | } | 74 | free_pages((unsigned long)vaddr, order); |
48 | } | 75 | } |
76 | } | ||
77 | EXPORT_SYMBOL(dma_free_coherent); | ||
78 | |||
79 | int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, | ||
80 | dma_addr_t device_addr, size_t size, int flags) | ||
81 | { | ||
82 | void __iomem *mem_base = NULL; | ||
83 | int pages = size >> PAGE_SHIFT; | ||
84 | int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long); | ||
85 | |||
86 | if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0) | ||
87 | goto out; | ||
88 | if (!size) | ||
89 | goto out; | ||
90 | if (dev->dma_mem) | ||
91 | goto out; | ||
92 | |||
93 | /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */ | ||
49 | 94 | ||
50 | return P2SEGADDR(ret); | 95 | mem_base = ioremap_nocache(bus_addr, size); |
96 | if (!mem_base) | ||
97 | goto out; | ||
98 | |||
99 | dev->dma_mem = kmalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL); | ||
100 | if (!dev->dma_mem) | ||
101 | goto out; | ||
102 | dev->dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL); | ||
103 | if (!dev->dma_mem->bitmap) | ||
104 | goto free1_out; | ||
105 | |||
106 | dev->dma_mem->virt_base = mem_base; | ||
107 | dev->dma_mem->device_base = device_addr; | ||
108 | dev->dma_mem->size = pages; | ||
109 | dev->dma_mem->flags = flags; | ||
110 | |||
111 | if (flags & DMA_MEMORY_MAP) | ||
112 | return DMA_MEMORY_MAP; | ||
113 | |||
114 | return DMA_MEMORY_IO; | ||
115 | |||
116 | free1_out: | ||
117 | kfree(dev->dma_mem); | ||
118 | out: | ||
119 | if (mem_base) | ||
120 | iounmap(mem_base); | ||
121 | return 0; | ||
51 | } | 122 | } |
123 | EXPORT_SYMBOL(dma_declare_coherent_memory); | ||
52 | 124 | ||
53 | void consistent_free(void *vaddr, size_t size) | 125 | void dma_release_declared_memory(struct device *dev) |
54 | { | 126 | { |
55 | unsigned long addr = P1SEGADDR((unsigned long)vaddr); | 127 | struct dma_coherent_mem *mem = dev->dma_mem; |
56 | struct page *page=virt_to_page(addr); | ||
57 | int num_pages=(size+PAGE_SIZE-1) >> PAGE_SHIFT; | ||
58 | int i; | ||
59 | 128 | ||
60 | for(i=0;i<num_pages;i++) { | 129 | if (!mem) |
61 | __free_page((page+i)); | 130 | return; |
62 | } | 131 | dev->dma_mem = NULL; |
132 | iounmap(mem->virt_base); | ||
133 | kfree(mem->bitmap); | ||
134 | kfree(mem); | ||
63 | } | 135 | } |
136 | EXPORT_SYMBOL(dma_release_declared_memory); | ||
64 | 137 | ||
65 | void consistent_sync(void *vaddr, size_t size, int direction) | 138 | void *dma_mark_declared_memory_occupied(struct device *dev, |
139 | dma_addr_t device_addr, size_t size) | ||
66 | { | 140 | { |
67 | void * p1addr = (void*) P1SEGADDR((unsigned long)vaddr); | 141 | struct dma_coherent_mem *mem = dev->dma_mem; |
142 | int pages = (size + (device_addr & ~PAGE_MASK) + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
143 | int pos, err; | ||
144 | |||
145 | if (!mem) | ||
146 | return ERR_PTR(-EINVAL); | ||
147 | |||
148 | pos = (device_addr - mem->device_base) >> PAGE_SHIFT; | ||
149 | err = bitmap_allocate_region(mem->bitmap, pos, get_order(pages)); | ||
150 | if (err != 0) | ||
151 | return ERR_PTR(err); | ||
152 | return mem->virt_base + (pos << PAGE_SHIFT); | ||
153 | } | ||
154 | EXPORT_SYMBOL(dma_mark_declared_memory_occupied); | ||
155 | |||
156 | void dma_cache_sync(struct device *dev, void *vaddr, size_t size, | ||
157 | enum dma_data_direction direction) | ||
158 | { | ||
159 | #ifdef CONFIG_CPU_SH5 | ||
160 | void *p1addr = vaddr; | ||
161 | #else | ||
162 | void *p1addr = (void*) P1SEGADDR((unsigned long)vaddr); | ||
163 | #endif | ||
68 | 164 | ||
69 | switch (direction) { | 165 | switch (direction) { |
70 | case DMA_FROM_DEVICE: /* invalidate only */ | 166 | case DMA_FROM_DEVICE: /* invalidate only */ |
@@ -80,8 +176,4 @@ void consistent_sync(void *vaddr, size_t size, int direction) | |||
80 | BUG(); | 176 | BUG(); |
81 | } | 177 | } |
82 | } | 178 | } |
83 | 179 | EXPORT_SYMBOL(dma_cache_sync); | |
84 | EXPORT_SYMBOL(consistent_alloc); | ||
85 | EXPORT_SYMBOL(consistent_free); | ||
86 | EXPORT_SYMBOL(consistent_sync); | ||
87 | |||
diff --git a/arch/sh/mm/copy_page.S b/arch/sh/mm/copy_page.S deleted file mode 100644 index 40685018b952..000000000000 --- a/arch/sh/mm/copy_page.S +++ /dev/null | |||
@@ -1,388 +0,0 @@ | |||
1 | /* | ||
2 | * copy_page, __copy_user_page, __copy_user implementation of SuperH | ||
3 | * | ||
4 | * Copyright (C) 2001 Niibe Yutaka & Kaz Kojima | ||
5 | * Copyright (C) 2002 Toshinobu Sugioka | ||
6 | * Copyright (C) 2006 Paul Mundt | ||
7 | */ | ||
8 | #include <linux/linkage.h> | ||
9 | #include <asm/page.h> | ||
10 | |||
11 | /* | ||
12 | * copy_page_slow | ||
13 | * @to: P1 address | ||
14 | * @from: P1 address | ||
15 | * | ||
16 | * void copy_page_slow(void *to, void *from) | ||
17 | */ | ||
18 | |||
19 | /* | ||
20 | * r0, r1, r2, r3, r4, r5, r6, r7 --- scratch | ||
21 | * r8 --- from + PAGE_SIZE | ||
22 | * r9 --- not used | ||
23 | * r10 --- to | ||
24 | * r11 --- from | ||
25 | */ | ||
26 | ENTRY(copy_page_slow) | ||
27 | mov.l r8,@-r15 | ||
28 | mov.l r10,@-r15 | ||
29 | mov.l r11,@-r15 | ||
30 | mov r4,r10 | ||
31 | mov r5,r11 | ||
32 | mov r5,r8 | ||
33 | mov.l .Lpsz,r0 | ||
34 | add r0,r8 | ||
35 | ! | ||
36 | 1: mov.l @r11+,r0 | ||
37 | mov.l @r11+,r1 | ||
38 | mov.l @r11+,r2 | ||
39 | mov.l @r11+,r3 | ||
40 | mov.l @r11+,r4 | ||
41 | mov.l @r11+,r5 | ||
42 | mov.l @r11+,r6 | ||
43 | mov.l @r11+,r7 | ||
44 | #if defined(CONFIG_CPU_SH3) | ||
45 | mov.l r0,@r10 | ||
46 | #elif defined(CONFIG_CPU_SH4) | ||
47 | movca.l r0,@r10 | ||
48 | mov r10,r0 | ||
49 | #endif | ||
50 | add #32,r10 | ||
51 | mov.l r7,@-r10 | ||
52 | mov.l r6,@-r10 | ||
53 | mov.l r5,@-r10 | ||
54 | mov.l r4,@-r10 | ||
55 | mov.l r3,@-r10 | ||
56 | mov.l r2,@-r10 | ||
57 | mov.l r1,@-r10 | ||
58 | #if defined(CONFIG_CPU_SH4) | ||
59 | ocbwb @r0 | ||
60 | #endif | ||
61 | cmp/eq r11,r8 | ||
62 | bf/s 1b | ||
63 | add #28,r10 | ||
64 | ! | ||
65 | mov.l @r15+,r11 | ||
66 | mov.l @r15+,r10 | ||
67 | mov.l @r15+,r8 | ||
68 | rts | ||
69 | nop | ||
70 | |||
71 | .align 2 | ||
72 | .Lpsz: .long PAGE_SIZE | ||
73 | /* | ||
74 | * __kernel_size_t __copy_user(void *to, const void *from, __kernel_size_t n); | ||
75 | * Return the number of bytes NOT copied | ||
76 | */ | ||
77 | #define EX(...) \ | ||
78 | 9999: __VA_ARGS__ ; \ | ||
79 | .section __ex_table, "a"; \ | ||
80 | .long 9999b, 6000f ; \ | ||
81 | .previous | ||
82 | ENTRY(__copy_user) | ||
83 | ! Check if small number of bytes | ||
84 | mov #11,r0 | ||
85 | mov r4,r3 | ||
86 | cmp/gt r0,r6 ! r6 (len) > r0 (11) | ||
87 | bf/s .L_cleanup_loop_no_pop | ||
88 | add r6,r3 ! last destination address | ||
89 | |||
90 | ! Calculate bytes needed to align to src | ||
91 | mov.l r11,@-r15 | ||
92 | neg r5,r0 | ||
93 | mov.l r10,@-r15 | ||
94 | add #4,r0 | ||
95 | mov.l r9,@-r15 | ||
96 | and #3,r0 | ||
97 | mov.l r8,@-r15 | ||
98 | tst r0,r0 | ||
99 | bt 2f | ||
100 | |||
101 | 1: | ||
102 | ! Copy bytes to long word align src | ||
103 | EX( mov.b @r5+,r1 ) | ||
104 | dt r0 | ||
105 | add #-1,r6 | ||
106 | EX( mov.b r1,@r4 ) | ||
107 | bf/s 1b | ||
108 | add #1,r4 | ||
109 | |||
110 | ! Jump to appropriate routine depending on dest | ||
111 | 2: mov #3,r1 | ||
112 | mov r6, r2 | ||
113 | and r4,r1 | ||
114 | shlr2 r2 | ||
115 | shll2 r1 | ||
116 | mova .L_jump_tbl,r0 | ||
117 | mov.l @(r0,r1),r1 | ||
118 | jmp @r1 | ||
119 | nop | ||
120 | |||
121 | .align 2 | ||
122 | .L_jump_tbl: | ||
123 | .long .L_dest00 | ||
124 | .long .L_dest01 | ||
125 | .long .L_dest10 | ||
126 | .long .L_dest11 | ||
127 | |||
128 | /* | ||
129 | * Come here if there are less than 12 bytes to copy | ||
130 | * | ||
131 | * Keep the branch target close, so the bf/s callee doesn't overflow | ||
132 | * and result in a more expensive branch being inserted. This is the | ||
133 | * fast-path for small copies, the jump via the jump table will hit the | ||
134 | * default slow-path cleanup. -PFM. | ||
135 | */ | ||
136 | .L_cleanup_loop_no_pop: | ||
137 | tst r6,r6 ! Check explicitly for zero | ||
138 | bt 1f | ||
139 | |||
140 | 2: | ||
141 | EX( mov.b @r5+,r0 ) | ||
142 | dt r6 | ||
143 | EX( mov.b r0,@r4 ) | ||
144 | bf/s 2b | ||
145 | add #1,r4 | ||
146 | |||
147 | 1: mov #0,r0 ! normal return | ||
148 | 5000: | ||
149 | |||
150 | # Exception handler: | ||
151 | .section .fixup, "ax" | ||
152 | 6000: | ||
153 | mov.l 8000f,r1 | ||
154 | mov r3,r0 | ||
155 | jmp @r1 | ||
156 | sub r4,r0 | ||
157 | .align 2 | ||
158 | 8000: .long 5000b | ||
159 | |||
160 | .previous | ||
161 | rts | ||
162 | nop | ||
163 | |||
164 | ! Destination = 00 | ||
165 | |||
166 | .L_dest00: | ||
167 | ! Skip the large copy for small transfers | ||
168 | mov #(32+32-4), r0 | ||
169 | cmp/gt r6, r0 ! r0 (60) > r6 (len) | ||
170 | bt 1f | ||
171 | |||
172 | ! Align dest to a 32 byte boundary | ||
173 | neg r4,r0 | ||
174 | add #0x20, r0 | ||
175 | and #0x1f, r0 | ||
176 | tst r0, r0 | ||
177 | bt 2f | ||
178 | |||
179 | sub r0, r6 | ||
180 | shlr2 r0 | ||
181 | 3: | ||
182 | EX( mov.l @r5+,r1 ) | ||
183 | dt r0 | ||
184 | EX( mov.l r1,@r4 ) | ||
185 | bf/s 3b | ||
186 | add #4,r4 | ||
187 | |||
188 | 2: | ||
189 | EX( mov.l @r5+,r0 ) | ||
190 | EX( mov.l @r5+,r1 ) | ||
191 | EX( mov.l @r5+,r2 ) | ||
192 | EX( mov.l @r5+,r7 ) | ||
193 | EX( mov.l @r5+,r8 ) | ||
194 | EX( mov.l @r5+,r9 ) | ||
195 | EX( mov.l @r5+,r10 ) | ||
196 | EX( mov.l @r5+,r11 ) | ||
197 | #ifdef CONFIG_CPU_SH4 | ||
198 | EX( movca.l r0,@r4 ) | ||
199 | #else | ||
200 | EX( mov.l r0,@r4 ) | ||
201 | #endif | ||
202 | add #-32, r6 | ||
203 | EX( mov.l r1,@(4,r4) ) | ||
204 | mov #32, r0 | ||
205 | EX( mov.l r2,@(8,r4) ) | ||
206 | cmp/gt r6, r0 ! r0 (32) > r6 (len) | ||
207 | EX( mov.l r7,@(12,r4) ) | ||
208 | EX( mov.l r8,@(16,r4) ) | ||
209 | EX( mov.l r9,@(20,r4) ) | ||
210 | EX( mov.l r10,@(24,r4) ) | ||
211 | EX( mov.l r11,@(28,r4) ) | ||
212 | bf/s 2b | ||
213 | add #32,r4 | ||
214 | |||
215 | 1: mov r6, r0 | ||
216 | shlr2 r0 | ||
217 | tst r0, r0 | ||
218 | bt .L_cleanup | ||
219 | 1: | ||
220 | EX( mov.l @r5+,r1 ) | ||
221 | dt r0 | ||
222 | EX( mov.l r1,@r4 ) | ||
223 | bf/s 1b | ||
224 | add #4,r4 | ||
225 | |||
226 | bra .L_cleanup | ||
227 | nop | ||
228 | |||
229 | ! Destination = 10 | ||
230 | |||
231 | .L_dest10: | ||
232 | mov r2,r7 | ||
233 | shlr2 r7 | ||
234 | shlr r7 | ||
235 | tst r7,r7 | ||
236 | mov #7,r0 | ||
237 | bt/s 1f | ||
238 | and r0,r2 | ||
239 | 2: | ||
240 | dt r7 | ||
241 | #ifdef CONFIG_CPU_LITTLE_ENDIAN | ||
242 | EX( mov.l @r5+,r0 ) | ||
243 | EX( mov.l @r5+,r1 ) | ||
244 | EX( mov.l @r5+,r8 ) | ||
245 | EX( mov.l @r5+,r9 ) | ||
246 | EX( mov.l @r5+,r10 ) | ||
247 | EX( mov.w r0,@r4 ) | ||
248 | add #2,r4 | ||
249 | xtrct r1,r0 | ||
250 | xtrct r8,r1 | ||
251 | xtrct r9,r8 | ||
252 | xtrct r10,r9 | ||
253 | |||
254 | EX( mov.l r0,@r4 ) | ||
255 | EX( mov.l r1,@(4,r4) ) | ||
256 | EX( mov.l r8,@(8,r4) ) | ||
257 | EX( mov.l r9,@(12,r4) ) | ||
258 | |||
259 | EX( mov.l @r5+,r1 ) | ||
260 | EX( mov.l @r5+,r8 ) | ||
261 | EX( mov.l @r5+,r0 ) | ||
262 | xtrct r1,r10 | ||
263 | xtrct r8,r1 | ||
264 | xtrct r0,r8 | ||
265 | shlr16 r0 | ||
266 | EX( mov.l r10,@(16,r4) ) | ||
267 | EX( mov.l r1,@(20,r4) ) | ||
268 | EX( mov.l r8,@(24,r4) ) | ||
269 | EX( mov.w r0,@(28,r4) ) | ||
270 | bf/s 2b | ||
271 | add #30,r4 | ||
272 | #else | ||
273 | EX( mov.l @(28,r5),r0 ) | ||
274 | EX( mov.l @(24,r5),r8 ) | ||
275 | EX( mov.l @(20,r5),r9 ) | ||
276 | EX( mov.l @(16,r5),r10 ) | ||
277 | EX( mov.w r0,@(30,r4) ) | ||
278 | add #-2,r4 | ||
279 | xtrct r8,r0 | ||
280 | xtrct r9,r8 | ||
281 | xtrct r10,r9 | ||
282 | EX( mov.l r0,@(28,r4) ) | ||
283 | EX( mov.l r8,@(24,r4) ) | ||
284 | EX( mov.l r9,@(20,r4) ) | ||
285 | |||
286 | EX( mov.l @(12,r5),r0 ) | ||
287 | EX( mov.l @(8,r5),r8 ) | ||
288 | xtrct r0,r10 | ||
289 | EX( mov.l @(4,r5),r9 ) | ||
290 | mov.l r10,@(16,r4) | ||
291 | EX( mov.l @r5,r10 ) | ||
292 | xtrct r8,r0 | ||
293 | xtrct r9,r8 | ||
294 | xtrct r10,r9 | ||
295 | EX( mov.l r0,@(12,r4) ) | ||
296 | EX( mov.l r8,@(8,r4) ) | ||
297 | swap.w r10,r0 | ||
298 | EX( mov.l r9,@(4,r4) ) | ||
299 | EX( mov.w r0,@(2,r4) ) | ||
300 | |||
301 | add #32,r5 | ||
302 | bf/s 2b | ||
303 | add #34,r4 | ||
304 | #endif | ||
305 | tst r2,r2 | ||
306 | bt .L_cleanup | ||
307 | |||
308 | 1: ! Read longword, write two words per iteration | ||
309 | EX( mov.l @r5+,r0 ) | ||
310 | dt r2 | ||
311 | #ifdef CONFIG_CPU_LITTLE_ENDIAN | ||
312 | EX( mov.w r0,@r4 ) | ||
313 | shlr16 r0 | ||
314 | EX( mov.w r0,@(2,r4) ) | ||
315 | #else | ||
316 | EX( mov.w r0,@(2,r4) ) | ||
317 | shlr16 r0 | ||
318 | EX( mov.w r0,@r4 ) | ||
319 | #endif | ||
320 | bf/s 1b | ||
321 | add #4,r4 | ||
322 | |||
323 | bra .L_cleanup | ||
324 | nop | ||
325 | |||
326 | ! Destination = 01 or 11 | ||
327 | |||
328 | .L_dest01: | ||
329 | .L_dest11: | ||
330 | ! Read longword, write byte, word, byte per iteration | ||
331 | EX( mov.l @r5+,r0 ) | ||
332 | dt r2 | ||
333 | #ifdef CONFIG_CPU_LITTLE_ENDIAN | ||
334 | EX( mov.b r0,@r4 ) | ||
335 | shlr8 r0 | ||
336 | add #1,r4 | ||
337 | EX( mov.w r0,@r4 ) | ||
338 | shlr16 r0 | ||
339 | EX( mov.b r0,@(2,r4) ) | ||
340 | bf/s .L_dest01 | ||
341 | add #3,r4 | ||
342 | #else | ||
343 | EX( mov.b r0,@(3,r4) ) | ||
344 | shlr8 r0 | ||
345 | swap.w r0,r7 | ||
346 | EX( mov.b r7,@r4 ) | ||
347 | add #1,r4 | ||
348 | EX( mov.w r0,@r4 ) | ||
349 | bf/s .L_dest01 | ||
350 | add #3,r4 | ||
351 | #endif | ||
352 | |||
353 | ! Cleanup last few bytes | ||
354 | .L_cleanup: | ||
355 | mov r6,r0 | ||
356 | and #3,r0 | ||
357 | tst r0,r0 | ||
358 | bt .L_exit | ||
359 | mov r0,r6 | ||
360 | |||
361 | .L_cleanup_loop: | ||
362 | EX( mov.b @r5+,r0 ) | ||
363 | dt r6 | ||
364 | EX( mov.b r0,@r4 ) | ||
365 | bf/s .L_cleanup_loop | ||
366 | add #1,r4 | ||
367 | |||
368 | .L_exit: | ||
369 | mov #0,r0 ! normal return | ||
370 | |||
371 | 5000: | ||
372 | |||
373 | # Exception handler: | ||
374 | .section .fixup, "ax" | ||
375 | 6000: | ||
376 | mov.l 8000f,r1 | ||
377 | mov r3,r0 | ||
378 | jmp @r1 | ||
379 | sub r4,r0 | ||
380 | .align 2 | ||
381 | 8000: .long 5000b | ||
382 | |||
383 | .previous | ||
384 | mov.l @r15+,r8 | ||
385 | mov.l @r15+,r9 | ||
386 | mov.l @r15+,r10 | ||
387 | rts | ||
388 | mov.l @r15+,r11 | ||
diff --git a/arch/sh/mm/extable.c b/arch/sh/mm/extable_32.c index c1cf4463d09d..c1cf4463d09d 100644 --- a/arch/sh/mm/extable.c +++ b/arch/sh/mm/extable_32.c | |||
diff --git a/arch/sh/mm/extable_64.c b/arch/sh/mm/extable_64.c new file mode 100644 index 000000000000..f05499688d88 --- /dev/null +++ b/arch/sh/mm/extable_64.c | |||
@@ -0,0 +1,82 @@ | |||
1 | /* | ||
2 | * arch/sh/mm/extable_64.c | ||
3 | * | ||
4 | * Copyright (C) 2003 Richard Curnow | ||
5 | * Copyright (C) 2003, 2004 Paul Mundt | ||
6 | * | ||
7 | * Cloned from the 2.5 SH version.. | ||
8 | * | ||
9 | * This file is subject to the terms and conditions of the GNU General Public | ||
10 | * License. See the file "COPYING" in the main directory of this archive | ||
11 | * for more details. | ||
12 | */ | ||
13 | #include <linux/rwsem.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <asm/uaccess.h> | ||
16 | |||
17 | extern unsigned long copy_user_memcpy, copy_user_memcpy_end; | ||
18 | extern void __copy_user_fixup(void); | ||
19 | |||
20 | static const struct exception_table_entry __copy_user_fixup_ex = { | ||
21 | .fixup = (unsigned long)&__copy_user_fixup, | ||
22 | }; | ||
23 | |||
24 | /* | ||
25 | * Some functions that may trap due to a bad user-mode address have too | ||
26 | * many loads and stores in them to make it at all practical to label | ||
27 | * each one and put them all in the main exception table. | ||
28 | * | ||
29 | * In particular, the fast memcpy routine is like this. It's fix-up is | ||
30 | * just to fall back to a slow byte-at-a-time copy, which is handled the | ||
31 | * conventional way. So it's functionally OK to just handle any trap | ||
32 | * occurring in the fast memcpy with that fixup. | ||
33 | */ | ||
34 | static const struct exception_table_entry *check_exception_ranges(unsigned long addr) | ||
35 | { | ||
36 | if ((addr >= (unsigned long)©_user_memcpy) && | ||
37 | (addr <= (unsigned long)©_user_memcpy_end)) | ||
38 | return &__copy_user_fixup_ex; | ||
39 | |||
40 | return NULL; | ||
41 | } | ||
42 | |||
43 | /* Simple binary search */ | ||
44 | const struct exception_table_entry * | ||
45 | search_extable(const struct exception_table_entry *first, | ||
46 | const struct exception_table_entry *last, | ||
47 | unsigned long value) | ||
48 | { | ||
49 | const struct exception_table_entry *mid; | ||
50 | |||
51 | mid = check_exception_ranges(value); | ||
52 | if (mid) | ||
53 | return mid; | ||
54 | |||
55 | while (first <= last) { | ||
56 | long diff; | ||
57 | |||
58 | mid = (last - first) / 2 + first; | ||
59 | diff = mid->insn - value; | ||
60 | if (diff == 0) | ||
61 | return mid; | ||
62 | else if (diff < 0) | ||
63 | first = mid+1; | ||
64 | else | ||
65 | last = mid-1; | ||
66 | } | ||
67 | |||
68 | return NULL; | ||
69 | } | ||
70 | |||
71 | int fixup_exception(struct pt_regs *regs) | ||
72 | { | ||
73 | const struct exception_table_entry *fixup; | ||
74 | |||
75 | fixup = search_exception_tables(regs->pc); | ||
76 | if (fixup) { | ||
77 | regs->pc = fixup->fixup; | ||
78 | return 1; | ||
79 | } | ||
80 | |||
81 | return 0; | ||
82 | } | ||
diff --git a/arch/sh/mm/fault.c b/arch/sh/mm/fault_32.c index 60d74f793a1d..33b43d20e9f6 100644 --- a/arch/sh/mm/fault.c +++ b/arch/sh/mm/fault_32.c | |||
@@ -172,7 +172,7 @@ no_context: | |||
172 | bust_spinlocks(1); | 172 | bust_spinlocks(1); |
173 | 173 | ||
174 | if (oops_may_print()) { | 174 | if (oops_may_print()) { |
175 | __typeof__(pte_val(__pte(0))) page; | 175 | unsigned long page; |
176 | 176 | ||
177 | if (address < PAGE_SIZE) | 177 | if (address < PAGE_SIZE) |
178 | printk(KERN_ALERT "Unable to handle kernel NULL " | 178 | printk(KERN_ALERT "Unable to handle kernel NULL " |
diff --git a/arch/sh/mm/fault_64.c b/arch/sh/mm/fault_64.c new file mode 100644 index 000000000000..399d53710d2f --- /dev/null +++ b/arch/sh/mm/fault_64.c | |||
@@ -0,0 +1,275 @@ | |||
1 | /* | ||
2 | * The SH64 TLB miss. | ||
3 | * | ||
4 | * Original code from fault.c | ||
5 | * Copyright (C) 2000, 2001 Paolo Alberelli | ||
6 | * | ||
7 | * Fast PTE->TLB refill path | ||
8 | * Copyright (C) 2003 Richard.Curnow@superh.com | ||
9 | * | ||
10 | * IMPORTANT NOTES : | ||
11 | * The do_fast_page_fault function is called from a context in entry.S | ||
12 | * where very few registers have been saved. In particular, the code in | ||
13 | * this file must be compiled not to use ANY caller-save registers that | ||
14 | * are not part of the restricted save set. Also, it means that code in | ||
15 | * this file must not make calls to functions elsewhere in the kernel, or | ||
16 | * else the excepting context will see corruption in its caller-save | ||
17 | * registers. Plus, the entry.S save area is non-reentrant, so this code | ||
18 | * has to run with SR.BL==1, i.e. no interrupts taken inside it and panic | ||
19 | * on any exception. | ||
20 | * | ||
21 | * This file is subject to the terms and conditions of the GNU General Public | ||
22 | * License. See the file "COPYING" in the main directory of this archive | ||
23 | * for more details. | ||
24 | */ | ||
25 | #include <linux/signal.h> | ||
26 | #include <linux/sched.h> | ||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/errno.h> | ||
29 | #include <linux/string.h> | ||
30 | #include <linux/types.h> | ||
31 | #include <linux/ptrace.h> | ||
32 | #include <linux/mman.h> | ||
33 | #include <linux/mm.h> | ||
34 | #include <linux/smp.h> | ||
35 | #include <linux/interrupt.h> | ||
36 | #include <asm/system.h> | ||
37 | #include <asm/tlb.h> | ||
38 | #include <asm/io.h> | ||
39 | #include <asm/uaccess.h> | ||
40 | #include <asm/pgalloc.h> | ||
41 | #include <asm/mmu_context.h> | ||
42 | #include <asm/cpu/registers.h> | ||
43 | |||
44 | /* Callable from fault.c, so not static */ | ||
45 | inline void __do_tlb_refill(unsigned long address, | ||
46 | unsigned long long is_text_not_data, pte_t *pte) | ||
47 | { | ||
48 | unsigned long long ptel; | ||
49 | unsigned long long pteh=0; | ||
50 | struct tlb_info *tlbp; | ||
51 | unsigned long long next; | ||
52 | |||
53 | /* Get PTEL first */ | ||
54 | ptel = pte_val(*pte); | ||
55 | |||
56 | /* | ||
57 | * Set PTEH register | ||
58 | */ | ||
59 | pteh = address & MMU_VPN_MASK; | ||
60 | |||
61 | /* Sign extend based on neff. */ | ||
62 | #if (NEFF == 32) | ||
63 | /* Faster sign extension */ | ||
64 | pteh = (unsigned long long)(signed long long)(signed long)pteh; | ||
65 | #else | ||
66 | /* General case */ | ||
67 | pteh = (pteh & NEFF_SIGN) ? (pteh | NEFF_MASK) : pteh; | ||
68 | #endif | ||
69 | |||
70 | /* Set the ASID. */ | ||
71 | pteh |= get_asid() << PTEH_ASID_SHIFT; | ||
72 | pteh |= PTEH_VALID; | ||
73 | |||
74 | /* Set PTEL register, set_pte has performed the sign extension */ | ||
75 | ptel &= _PAGE_FLAGS_HARDWARE_MASK; /* drop software flags */ | ||
76 | |||
77 | tlbp = is_text_not_data ? &(cpu_data->itlb) : &(cpu_data->dtlb); | ||
78 | next = tlbp->next; | ||
79 | __flush_tlb_slot(next); | ||
80 | asm volatile ("putcfg %0,1,%2\n\n\t" | ||
81 | "putcfg %0,0,%1\n" | ||
82 | : : "r" (next), "r" (pteh), "r" (ptel) ); | ||
83 | |||
84 | next += TLB_STEP; | ||
85 | if (next > tlbp->last) next = tlbp->first; | ||
86 | tlbp->next = next; | ||
87 | |||
88 | } | ||
89 | |||
90 | static int handle_vmalloc_fault(struct mm_struct *mm, | ||
91 | unsigned long protection_flags, | ||
92 | unsigned long long textaccess, | ||
93 | unsigned long address) | ||
94 | { | ||
95 | pgd_t *dir; | ||
96 | pud_t *pud; | ||
97 | pmd_t *pmd; | ||
98 | static pte_t *pte; | ||
99 | pte_t entry; | ||
100 | |||
101 | dir = pgd_offset_k(address); | ||
102 | |||
103 | pud = pud_offset(dir, address); | ||
104 | if (pud_none_or_clear_bad(pud)) | ||
105 | return 0; | ||
106 | |||
107 | pmd = pmd_offset(pud, address); | ||
108 | if (pmd_none_or_clear_bad(pmd)) | ||
109 | return 0; | ||
110 | |||
111 | pte = pte_offset_kernel(pmd, address); | ||
112 | entry = *pte; | ||
113 | |||
114 | if (pte_none(entry) || !pte_present(entry)) | ||
115 | return 0; | ||
116 | if ((pte_val(entry) & protection_flags) != protection_flags) | ||
117 | return 0; | ||
118 | |||
119 | __do_tlb_refill(address, textaccess, pte); | ||
120 | |||
121 | return 1; | ||
122 | } | ||
123 | |||
124 | static int handle_tlbmiss(struct mm_struct *mm, | ||
125 | unsigned long long protection_flags, | ||
126 | unsigned long long textaccess, | ||
127 | unsigned long address) | ||
128 | { | ||
129 | pgd_t *dir; | ||
130 | pud_t *pud; | ||
131 | pmd_t *pmd; | ||
132 | pte_t *pte; | ||
133 | pte_t entry; | ||
134 | |||
135 | /* NB. The PGD currently only contains a single entry - there is no | ||
136 | page table tree stored for the top half of the address space since | ||
137 | virtual pages in that region should never be mapped in user mode. | ||
138 | (In kernel mode, the only things in that region are the 512Mb super | ||
139 | page (locked in), and vmalloc (modules) + I/O device pages (handled | ||
140 | by handle_vmalloc_fault), so no PGD for the upper half is required | ||
141 | by kernel mode either). | ||
142 | |||
143 | See how mm->pgd is allocated and initialised in pgd_alloc to see why | ||
144 | the next test is necessary. - RPC */ | ||
145 | if (address >= (unsigned long) TASK_SIZE) | ||
146 | /* upper half - never has page table entries. */ | ||
147 | return 0; | ||
148 | |||
149 | dir = pgd_offset(mm, address); | ||
150 | if (pgd_none(*dir) || !pgd_present(*dir)) | ||
151 | return 0; | ||
152 | if (!pgd_present(*dir)) | ||
153 | return 0; | ||
154 | |||
155 | pud = pud_offset(dir, address); | ||
156 | if (pud_none(*pud) || !pud_present(*pud)) | ||
157 | return 0; | ||
158 | |||
159 | pmd = pmd_offset(pud, address); | ||
160 | if (pmd_none(*pmd) || !pmd_present(*pmd)) | ||
161 | return 0; | ||
162 | |||
163 | pte = pte_offset_kernel(pmd, address); | ||
164 | entry = *pte; | ||
165 | |||
166 | if (pte_none(entry) || !pte_present(entry)) | ||
167 | return 0; | ||
168 | |||
169 | /* | ||
170 | * If the page doesn't have sufficient protection bits set to | ||
171 | * service the kind of fault being handled, there's not much | ||
172 | * point doing the TLB refill. Punt the fault to the general | ||
173 | * handler. | ||
174 | */ | ||
175 | if ((pte_val(entry) & protection_flags) != protection_flags) | ||
176 | return 0; | ||
177 | |||
178 | __do_tlb_refill(address, textaccess, pte); | ||
179 | |||
180 | return 1; | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * Put all this information into one structure so that everything is just | ||
185 | * arithmetic relative to a single base address. This reduces the number | ||
186 | * of movi/shori pairs needed just to load addresses of static data. | ||
187 | */ | ||
188 | struct expevt_lookup { | ||
189 | unsigned short protection_flags[8]; | ||
190 | unsigned char is_text_access[8]; | ||
191 | unsigned char is_write_access[8]; | ||
192 | }; | ||
193 | |||
194 | #define PRU (1<<9) | ||
195 | #define PRW (1<<8) | ||
196 | #define PRX (1<<7) | ||
197 | #define PRR (1<<6) | ||
198 | |||
199 | #define DIRTY (_PAGE_DIRTY | _PAGE_ACCESSED) | ||
200 | #define YOUNG (_PAGE_ACCESSED) | ||
201 | |||
202 | /* Sized as 8 rather than 4 to allow checking the PTE's PRU bit against whether | ||
203 | the fault happened in user mode or privileged mode. */ | ||
204 | static struct expevt_lookup expevt_lookup_table = { | ||
205 | .protection_flags = {PRX, PRX, 0, 0, PRR, PRR, PRW, PRW}, | ||
206 | .is_text_access = {1, 1, 0, 0, 0, 0, 0, 0} | ||
207 | }; | ||
208 | |||
209 | /* | ||
210 | This routine handles page faults that can be serviced just by refilling a | ||
211 | TLB entry from an existing page table entry. (This case represents a very | ||
212 | large majority of page faults.) Return 1 if the fault was successfully | ||
213 | handled. Return 0 if the fault could not be handled. (This leads into the | ||
214 | general fault handling in fault.c which deals with mapping file-backed | ||
215 | pages, stack growth, segmentation faults, swapping etc etc) | ||
216 | */ | ||
217 | asmlinkage int do_fast_page_fault(unsigned long long ssr_md, | ||
218 | unsigned long long expevt, | ||
219 | unsigned long address) | ||
220 | { | ||
221 | struct task_struct *tsk; | ||
222 | struct mm_struct *mm; | ||
223 | unsigned long long textaccess; | ||
224 | unsigned long long protection_flags; | ||
225 | unsigned long long index; | ||
226 | unsigned long long expevt4; | ||
227 | |||
228 | /* The next few lines implement a way of hashing EXPEVT into a | ||
229 | * small array index which can be used to lookup parameters | ||
230 | * specific to the type of TLBMISS being handled. | ||
231 | * | ||
232 | * Note: | ||
233 | * ITLBMISS has EXPEVT==0xa40 | ||
234 | * RTLBMISS has EXPEVT==0x040 | ||
235 | * WTLBMISS has EXPEVT==0x060 | ||
236 | */ | ||
237 | expevt4 = (expevt >> 4); | ||
238 | /* TODO : xor ssr_md into this expression too. Then we can check | ||
239 | * that PRU is set when it needs to be. */ | ||
240 | index = expevt4 ^ (expevt4 >> 5); | ||
241 | index &= 7; | ||
242 | protection_flags = expevt_lookup_table.protection_flags[index]; | ||
243 | textaccess = expevt_lookup_table.is_text_access[index]; | ||
244 | |||
245 | /* SIM | ||
246 | * Note this is now called with interrupts still disabled | ||
247 | * This is to cope with being called for a missing IO port | ||
248 | * address with interrupts disabled. This should be fixed as | ||
249 | * soon as we have a better 'fast path' miss handler. | ||
250 | * | ||
251 | * Plus take care how you try and debug this stuff. | ||
252 | * For example, writing debug data to a port which you | ||
253 | * have just faulted on is not going to work. | ||
254 | */ | ||
255 | |||
256 | tsk = current; | ||
257 | mm = tsk->mm; | ||
258 | |||
259 | if ((address >= VMALLOC_START && address < VMALLOC_END) || | ||
260 | (address >= IOBASE_VADDR && address < IOBASE_END)) { | ||
261 | if (ssr_md) | ||
262 | /* | ||
263 | * Process-contexts can never have this address | ||
264 | * range mapped | ||
265 | */ | ||
266 | if (handle_vmalloc_fault(mm, protection_flags, | ||
267 | textaccess, address)) | ||
268 | return 1; | ||
269 | } else if (!in_interrupt() && mm) { | ||
270 | if (handle_tlbmiss(mm, protection_flags, textaccess, address)) | ||
271 | return 1; | ||
272 | } | ||
273 | |||
274 | return 0; | ||
275 | } | ||
diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index d5e160da64b2..2918c6b14659 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c | |||
@@ -23,9 +23,7 @@ | |||
23 | 23 | ||
24 | DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); | 24 | DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); |
25 | pgd_t swapper_pg_dir[PTRS_PER_PGD]; | 25 | pgd_t swapper_pg_dir[PTRS_PER_PGD]; |
26 | 26 | unsigned long cached_to_uncached = 0; | |
27 | void (*copy_page)(void *from, void *to); | ||
28 | void (*clear_page)(void *to); | ||
29 | 27 | ||
30 | void show_mem(void) | 28 | void show_mem(void) |
31 | { | 29 | { |
@@ -102,7 +100,8 @@ static void set_pte_phys(unsigned long addr, unsigned long phys, pgprot_t prot) | |||
102 | 100 | ||
103 | set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, prot)); | 101 | set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, prot)); |
104 | 102 | ||
105 | flush_tlb_one(get_asid(), addr); | 103 | if (cached_to_uncached) |
104 | flush_tlb_one(get_asid(), addr); | ||
106 | } | 105 | } |
107 | 106 | ||
108 | /* | 107 | /* |
@@ -131,6 +130,37 @@ void __set_fixmap(enum fixed_addresses idx, unsigned long phys, pgprot_t prot) | |||
131 | 130 | ||
132 | set_pte_phys(address, phys, prot); | 131 | set_pte_phys(address, phys, prot); |
133 | } | 132 | } |
133 | |||
134 | void __init page_table_range_init(unsigned long start, unsigned long end, | ||
135 | pgd_t *pgd_base) | ||
136 | { | ||
137 | pgd_t *pgd; | ||
138 | pud_t *pud; | ||
139 | pmd_t *pmd; | ||
140 | int pgd_idx; | ||
141 | unsigned long vaddr; | ||
142 | |||
143 | vaddr = start & PMD_MASK; | ||
144 | end = (end + PMD_SIZE - 1) & PMD_MASK; | ||
145 | pgd_idx = pgd_index(vaddr); | ||
146 | pgd = pgd_base + pgd_idx; | ||
147 | |||
148 | for ( ; (pgd_idx < PTRS_PER_PGD) && (vaddr != end); pgd++, pgd_idx++) { | ||
149 | BUG_ON(pgd_none(*pgd)); | ||
150 | pud = pud_offset(pgd, 0); | ||
151 | BUG_ON(pud_none(*pud)); | ||
152 | pmd = pmd_offset(pud, 0); | ||
153 | |||
154 | if (!pmd_present(*pmd)) { | ||
155 | pte_t *pte_table; | ||
156 | pte_table = (pte_t *)alloc_bootmem_low_pages(PAGE_SIZE); | ||
157 | memset(pte_table, 0, PAGE_SIZE); | ||
158 | pmd_populate_kernel(&init_mm, pmd, pte_table); | ||
159 | } | ||
160 | |||
161 | vaddr += PMD_SIZE; | ||
162 | } | ||
163 | } | ||
134 | #endif /* CONFIG_MMU */ | 164 | #endif /* CONFIG_MMU */ |
135 | 165 | ||
136 | /* | 166 | /* |
@@ -150,6 +180,11 @@ void __init paging_init(void) | |||
150 | * check for a null value. */ | 180 | * check for a null value. */ |
151 | set_TTB(swapper_pg_dir); | 181 | set_TTB(swapper_pg_dir); |
152 | 182 | ||
183 | /* Populate the relevant portions of swapper_pg_dir so that | ||
184 | * we can use the fixmap entries without calling kmalloc. | ||
185 | * pte's will be filled in by __set_fixmap(). */ | ||
186 | page_table_range_init(FIXADDR_START, FIXADDR_TOP, swapper_pg_dir); | ||
187 | |||
153 | memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); | 188 | memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); |
154 | 189 | ||
155 | for_each_online_node(nid) { | 190 | for_each_online_node(nid) { |
@@ -167,9 +202,22 @@ void __init paging_init(void) | |||
167 | } | 202 | } |
168 | 203 | ||
169 | free_area_init_nodes(max_zone_pfns); | 204 | free_area_init_nodes(max_zone_pfns); |
205 | |||
206 | /* Set up the uncached fixmap */ | ||
207 | set_fixmap_nocache(FIX_UNCACHED, __pa(&__uncached_start)); | ||
208 | |||
209 | #ifdef CONFIG_29BIT | ||
210 | /* | ||
211 | * Handle trivial transitions between cached and uncached | ||
212 | * segments, making use of the 1:1 mapping relationship in | ||
213 | * 512MB lowmem. | ||
214 | */ | ||
215 | cached_to_uncached = P2SEG - P1SEG; | ||
216 | #endif | ||
170 | } | 217 | } |
171 | 218 | ||
172 | static struct kcore_list kcore_mem, kcore_vmalloc; | 219 | static struct kcore_list kcore_mem, kcore_vmalloc; |
220 | int after_bootmem = 0; | ||
173 | 221 | ||
174 | void __init mem_init(void) | 222 | void __init mem_init(void) |
175 | { | 223 | { |
@@ -202,17 +250,7 @@ void __init mem_init(void) | |||
202 | memset(empty_zero_page, 0, PAGE_SIZE); | 250 | memset(empty_zero_page, 0, PAGE_SIZE); |
203 | __flush_wback_region(empty_zero_page, PAGE_SIZE); | 251 | __flush_wback_region(empty_zero_page, PAGE_SIZE); |
204 | 252 | ||
205 | /* | 253 | after_bootmem = 1; |
206 | * Setup wrappers for copy/clear_page(), these will get overridden | ||
207 | * later in the boot process if a better method is available. | ||
208 | */ | ||
209 | #ifdef CONFIG_MMU | ||
210 | copy_page = copy_page_slow; | ||
211 | clear_page = clear_page_slow; | ||
212 | #else | ||
213 | copy_page = copy_page_nommu; | ||
214 | clear_page = clear_page_nommu; | ||
215 | #endif | ||
216 | 254 | ||
217 | codesize = (unsigned long) &_etext - (unsigned long) &_text; | 255 | codesize = (unsigned long) &_etext - (unsigned long) &_text; |
218 | datasize = (unsigned long) &_edata - (unsigned long) &_etext; | 256 | datasize = (unsigned long) &_edata - (unsigned long) &_etext; |
diff --git a/arch/sh/mm/ioremap.c b/arch/sh/mm/ioremap_32.c index 0c7b7e33abdc..0c7b7e33abdc 100644 --- a/arch/sh/mm/ioremap.c +++ b/arch/sh/mm/ioremap_32.c | |||
diff --git a/arch/sh/mm/ioremap_64.c b/arch/sh/mm/ioremap_64.c new file mode 100644 index 000000000000..e27d16519235 --- /dev/null +++ b/arch/sh/mm/ioremap_64.c | |||
@@ -0,0 +1,404 @@ | |||
1 | /* | ||
2 | * arch/sh/mm/ioremap_64.c | ||
3 | * | ||
4 | * Copyright (C) 2000, 2001 Paolo Alberelli | ||
5 | * Copyright (C) 2003 - 2007 Paul Mundt | ||
6 | * | ||
7 | * Mostly derived from arch/sh/mm/ioremap.c which, in turn is mostly | ||
8 | * derived from arch/i386/mm/ioremap.c . | ||
9 | * | ||
10 | * (C) Copyright 1995 1996 Linus Torvalds | ||
11 | * | ||
12 | * This file is subject to the terms and conditions of the GNU General Public | ||
13 | * License. See the file "COPYING" in the main directory of this archive | ||
14 | * for more details. | ||
15 | */ | ||
16 | #include <linux/vmalloc.h> | ||
17 | #include <linux/ioport.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/mm.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/bootmem.h> | ||
22 | #include <linux/proc_fs.h> | ||
23 | #include <asm/page.h> | ||
24 | #include <asm/pgalloc.h> | ||
25 | #include <asm/addrspace.h> | ||
26 | #include <asm/cacheflush.h> | ||
27 | #include <asm/tlbflush.h> | ||
28 | #include <asm/mmu.h> | ||
29 | |||
30 | static void shmedia_mapioaddr(unsigned long, unsigned long); | ||
31 | static unsigned long shmedia_ioremap(struct resource *, u32, int); | ||
32 | |||
33 | /* | ||
34 | * Generic mapping function (not visible outside): | ||
35 | */ | ||
36 | |||
37 | /* | ||
38 | * Remap an arbitrary physical address space into the kernel virtual | ||
39 | * address space. Needed when the kernel wants to access high addresses | ||
40 | * directly. | ||
41 | * | ||
42 | * NOTE! We need to allow non-page-aligned mappings too: we will obviously | ||
43 | * have to convert them into an offset in a page-aligned mapping, but the | ||
44 | * caller shouldn't need to know that small detail. | ||
45 | */ | ||
46 | void *__ioremap(unsigned long phys_addr, unsigned long size, | ||
47 | unsigned long flags) | ||
48 | { | ||
49 | void * addr; | ||
50 | struct vm_struct * area; | ||
51 | unsigned long offset, last_addr; | ||
52 | pgprot_t pgprot; | ||
53 | |||
54 | /* Don't allow wraparound or zero size */ | ||
55 | last_addr = phys_addr + size - 1; | ||
56 | if (!size || last_addr < phys_addr) | ||
57 | return NULL; | ||
58 | |||
59 | pgprot = __pgprot(_PAGE_PRESENT | _PAGE_READ | | ||
60 | _PAGE_WRITE | _PAGE_DIRTY | | ||
61 | _PAGE_ACCESSED | _PAGE_SHARED | flags); | ||
62 | |||
63 | /* | ||
64 | * Mappings have to be page-aligned | ||
65 | */ | ||
66 | offset = phys_addr & ~PAGE_MASK; | ||
67 | phys_addr &= PAGE_MASK; | ||
68 | size = PAGE_ALIGN(last_addr + 1) - phys_addr; | ||
69 | |||
70 | /* | ||
71 | * Ok, go for it.. | ||
72 | */ | ||
73 | area = get_vm_area(size, VM_IOREMAP); | ||
74 | pr_debug("Get vm_area returns %p addr %p\n",area,area->addr); | ||
75 | if (!area) | ||
76 | return NULL; | ||
77 | area->phys_addr = phys_addr; | ||
78 | addr = area->addr; | ||
79 | if (ioremap_page_range((unsigned long)addr, (unsigned long)addr + size, | ||
80 | phys_addr, pgprot)) { | ||
81 | vunmap(addr); | ||
82 | return NULL; | ||
83 | } | ||
84 | return (void *) (offset + (char *)addr); | ||
85 | } | ||
86 | EXPORT_SYMBOL(__ioremap); | ||
87 | |||
88 | void __iounmap(void *addr) | ||
89 | { | ||
90 | struct vm_struct *area; | ||
91 | |||
92 | vfree((void *) (PAGE_MASK & (unsigned long) addr)); | ||
93 | area = remove_vm_area((void *) (PAGE_MASK & (unsigned long) addr)); | ||
94 | if (!area) { | ||
95 | printk(KERN_ERR "iounmap: bad address %p\n", addr); | ||
96 | return; | ||
97 | } | ||
98 | |||
99 | kfree(area); | ||
100 | } | ||
101 | EXPORT_SYMBOL(__iounmap); | ||
102 | |||
103 | static struct resource shmedia_iomap = { | ||
104 | .name = "shmedia_iomap", | ||
105 | .start = IOBASE_VADDR + PAGE_SIZE, | ||
106 | .end = IOBASE_END - 1, | ||
107 | }; | ||
108 | |||
109 | static void shmedia_mapioaddr(unsigned long pa, unsigned long va); | ||
110 | static void shmedia_unmapioaddr(unsigned long vaddr); | ||
111 | static unsigned long shmedia_ioremap(struct resource *res, u32 pa, int sz); | ||
112 | |||
113 | /* | ||
114 | * We have the same problem as the SPARC, so lets have the same comment: | ||
115 | * Our mini-allocator... | ||
116 | * Boy this is gross! We need it because we must map I/O for | ||
117 | * timers and interrupt controller before the kmalloc is available. | ||
118 | */ | ||
119 | |||
120 | #define XNMLN 15 | ||
121 | #define XNRES 10 | ||
122 | |||
123 | struct xresource { | ||
124 | struct resource xres; /* Must be first */ | ||
125 | int xflag; /* 1 == used */ | ||
126 | char xname[XNMLN+1]; | ||
127 | }; | ||
128 | |||
129 | static struct xresource xresv[XNRES]; | ||
130 | |||
131 | static struct xresource *xres_alloc(void) | ||
132 | { | ||
133 | struct xresource *xrp; | ||
134 | int n; | ||
135 | |||
136 | xrp = xresv; | ||
137 | for (n = 0; n < XNRES; n++) { | ||
138 | if (xrp->xflag == 0) { | ||
139 | xrp->xflag = 1; | ||
140 | return xrp; | ||
141 | } | ||
142 | xrp++; | ||
143 | } | ||
144 | return NULL; | ||
145 | } | ||
146 | |||
147 | static void xres_free(struct xresource *xrp) | ||
148 | { | ||
149 | xrp->xflag = 0; | ||
150 | } | ||
151 | |||
152 | static struct resource *shmedia_find_resource(struct resource *root, | ||
153 | unsigned long vaddr) | ||
154 | { | ||
155 | struct resource *res; | ||
156 | |||
157 | for (res = root->child; res; res = res->sibling) | ||
158 | if (res->start <= vaddr && res->end >= vaddr) | ||
159 | return res; | ||
160 | |||
161 | return NULL; | ||
162 | } | ||
163 | |||
164 | static unsigned long shmedia_alloc_io(unsigned long phys, unsigned long size, | ||
165 | const char *name) | ||
166 | { | ||
167 | static int printed_full = 0; | ||
168 | struct xresource *xres; | ||
169 | struct resource *res; | ||
170 | char *tack; | ||
171 | int tlen; | ||
172 | |||
173 | if (name == NULL) name = "???"; | ||
174 | |||
175 | if ((xres = xres_alloc()) != 0) { | ||
176 | tack = xres->xname; | ||
177 | res = &xres->xres; | ||
178 | } else { | ||
179 | if (!printed_full) { | ||
180 | printk("%s: done with statics, switching to kmalloc\n", | ||
181 | __FUNCTION__); | ||
182 | printed_full = 1; | ||
183 | } | ||
184 | tlen = strlen(name); | ||
185 | tack = kmalloc(sizeof (struct resource) + tlen + 1, GFP_KERNEL); | ||
186 | if (!tack) | ||
187 | return -ENOMEM; | ||
188 | memset(tack, 0, sizeof(struct resource)); | ||
189 | res = (struct resource *) tack; | ||
190 | tack += sizeof (struct resource); | ||
191 | } | ||
192 | |||
193 | strncpy(tack, name, XNMLN); | ||
194 | tack[XNMLN] = 0; | ||
195 | res->name = tack; | ||
196 | |||
197 | return shmedia_ioremap(res, phys, size); | ||
198 | } | ||
199 | |||
200 | static unsigned long shmedia_ioremap(struct resource *res, u32 pa, int sz) | ||
201 | { | ||
202 | unsigned long offset = ((unsigned long) pa) & (~PAGE_MASK); | ||
203 | unsigned long round_sz = (offset + sz + PAGE_SIZE-1) & PAGE_MASK; | ||
204 | unsigned long va; | ||
205 | unsigned int psz; | ||
206 | |||
207 | if (allocate_resource(&shmedia_iomap, res, round_sz, | ||
208 | shmedia_iomap.start, shmedia_iomap.end, | ||
209 | PAGE_SIZE, NULL, NULL) != 0) { | ||
210 | panic("alloc_io_res(%s): cannot occupy\n", | ||
211 | (res->name != NULL)? res->name: "???"); | ||
212 | } | ||
213 | |||
214 | va = res->start; | ||
215 | pa &= PAGE_MASK; | ||
216 | |||
217 | psz = (res->end - res->start + (PAGE_SIZE - 1)) / PAGE_SIZE; | ||
218 | |||
219 | /* log at boot time ... */ | ||
220 | printk("mapioaddr: %6s [%2d page%s] va 0x%08lx pa 0x%08x\n", | ||
221 | ((res->name != NULL) ? res->name : "???"), | ||
222 | psz, psz == 1 ? " " : "s", va, pa); | ||
223 | |||
224 | for (psz = res->end - res->start + 1; psz != 0; psz -= PAGE_SIZE) { | ||
225 | shmedia_mapioaddr(pa, va); | ||
226 | va += PAGE_SIZE; | ||
227 | pa += PAGE_SIZE; | ||
228 | } | ||
229 | |||
230 | res->start += offset; | ||
231 | res->end = res->start + sz - 1; /* not strictly necessary.. */ | ||
232 | |||
233 | return res->start; | ||
234 | } | ||
235 | |||
236 | static void shmedia_free_io(struct resource *res) | ||
237 | { | ||
238 | unsigned long len = res->end - res->start + 1; | ||
239 | |||
240 | BUG_ON((len & (PAGE_SIZE - 1)) != 0); | ||
241 | |||
242 | while (len) { | ||
243 | len -= PAGE_SIZE; | ||
244 | shmedia_unmapioaddr(res->start + len); | ||
245 | } | ||
246 | |||
247 | release_resource(res); | ||
248 | } | ||
249 | |||
250 | static __init_refok void *sh64_get_page(void) | ||
251 | { | ||
252 | extern int after_bootmem; | ||
253 | void *page; | ||
254 | |||
255 | if (after_bootmem) { | ||
256 | page = (void *)get_zeroed_page(GFP_ATOMIC); | ||
257 | } else { | ||
258 | page = alloc_bootmem_pages(PAGE_SIZE); | ||
259 | } | ||
260 | |||
261 | if (!page || ((unsigned long)page & ~PAGE_MASK)) | ||
262 | panic("sh64_get_page: Out of memory already?\n"); | ||
263 | |||
264 | return page; | ||
265 | } | ||
266 | |||
267 | static void shmedia_mapioaddr(unsigned long pa, unsigned long va) | ||
268 | { | ||
269 | pgd_t *pgdp; | ||
270 | pud_t *pudp; | ||
271 | pmd_t *pmdp; | ||
272 | pte_t *ptep, pte; | ||
273 | pgprot_t prot; | ||
274 | unsigned long flags = 1; /* 1 = CB0-1 device */ | ||
275 | |||
276 | pr_debug("shmedia_mapiopage pa %08lx va %08lx\n", pa, va); | ||
277 | |||
278 | pgdp = pgd_offset_k(va); | ||
279 | if (pgd_none(*pgdp) || !pgd_present(*pgdp)) { | ||
280 | pudp = (pud_t *)sh64_get_page(); | ||
281 | set_pgd(pgdp, __pgd((unsigned long)pudp | _KERNPG_TABLE)); | ||
282 | } | ||
283 | |||
284 | pudp = pud_offset(pgdp, va); | ||
285 | if (pud_none(*pudp) || !pud_present(*pudp)) { | ||
286 | pmdp = (pmd_t *)sh64_get_page(); | ||
287 | set_pud(pudp, __pud((unsigned long)pmdp | _KERNPG_TABLE)); | ||
288 | } | ||
289 | |||
290 | pmdp = pmd_offset(pudp, va); | ||
291 | if (pmd_none(*pmdp) || !pmd_present(*pmdp) ) { | ||
292 | ptep = (pte_t *)sh64_get_page(); | ||
293 | set_pmd(pmdp, __pmd((unsigned long)ptep + _PAGE_TABLE)); | ||
294 | } | ||
295 | |||
296 | prot = __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | | ||
297 | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_SHARED | flags); | ||
298 | |||
299 | pte = pfn_pte(pa >> PAGE_SHIFT, prot); | ||
300 | ptep = pte_offset_kernel(pmdp, va); | ||
301 | |||
302 | if (!pte_none(*ptep) && | ||
303 | pte_val(*ptep) != pte_val(pte)) | ||
304 | pte_ERROR(*ptep); | ||
305 | |||
306 | set_pte(ptep, pte); | ||
307 | |||
308 | flush_tlb_kernel_range(va, PAGE_SIZE); | ||
309 | } | ||
310 | |||
311 | static void shmedia_unmapioaddr(unsigned long vaddr) | ||
312 | { | ||
313 | pgd_t *pgdp; | ||
314 | pud_t *pudp; | ||
315 | pmd_t *pmdp; | ||
316 | pte_t *ptep; | ||
317 | |||
318 | pgdp = pgd_offset_k(vaddr); | ||
319 | if (pgd_none(*pgdp) || pgd_bad(*pgdp)) | ||
320 | return; | ||
321 | |||
322 | pudp = pud_offset(pgdp, vaddr); | ||
323 | if (pud_none(*pudp) || pud_bad(*pudp)) | ||
324 | return; | ||
325 | |||
326 | pmdp = pmd_offset(pudp, vaddr); | ||
327 | if (pmd_none(*pmdp) || pmd_bad(*pmdp)) | ||
328 | return; | ||
329 | |||
330 | ptep = pte_offset_kernel(pmdp, vaddr); | ||
331 | |||
332 | if (pte_none(*ptep) || !pte_present(*ptep)) | ||
333 | return; | ||
334 | |||
335 | clear_page((void *)ptep); | ||
336 | pte_clear(&init_mm, vaddr, ptep); | ||
337 | } | ||
338 | |||
339 | unsigned long onchip_remap(unsigned long phys, unsigned long size, const char *name) | ||
340 | { | ||
341 | if (size < PAGE_SIZE) | ||
342 | size = PAGE_SIZE; | ||
343 | |||
344 | return shmedia_alloc_io(phys, size, name); | ||
345 | } | ||
346 | |||
347 | void onchip_unmap(unsigned long vaddr) | ||
348 | { | ||
349 | struct resource *res; | ||
350 | unsigned int psz; | ||
351 | |||
352 | res = shmedia_find_resource(&shmedia_iomap, vaddr); | ||
353 | if (!res) { | ||
354 | printk(KERN_ERR "%s: Failed to free 0x%08lx\n", | ||
355 | __FUNCTION__, vaddr); | ||
356 | return; | ||
357 | } | ||
358 | |||
359 | psz = (res->end - res->start + (PAGE_SIZE - 1)) / PAGE_SIZE; | ||
360 | |||
361 | printk(KERN_DEBUG "unmapioaddr: %6s [%2d page%s] freed\n", | ||
362 | res->name, psz, psz == 1 ? " " : "s"); | ||
363 | |||
364 | shmedia_free_io(res); | ||
365 | |||
366 | if ((char *)res >= (char *)xresv && | ||
367 | (char *)res < (char *)&xresv[XNRES]) { | ||
368 | xres_free((struct xresource *)res); | ||
369 | } else { | ||
370 | kfree(res); | ||
371 | } | ||
372 | } | ||
373 | |||
374 | #ifdef CONFIG_PROC_FS | ||
375 | static int | ||
376 | ioremap_proc_info(char *buf, char **start, off_t fpos, int length, int *eof, | ||
377 | void *data) | ||
378 | { | ||
379 | char *p = buf, *e = buf + length; | ||
380 | struct resource *r; | ||
381 | const char *nm; | ||
382 | |||
383 | for (r = ((struct resource *)data)->child; r != NULL; r = r->sibling) { | ||
384 | if (p + 32 >= e) /* Better than nothing */ | ||
385 | break; | ||
386 | if ((nm = r->name) == 0) nm = "???"; | ||
387 | p += sprintf(p, "%08lx-%08lx: %s\n", | ||
388 | (unsigned long)r->start, | ||
389 | (unsigned long)r->end, nm); | ||
390 | } | ||
391 | |||
392 | return p-buf; | ||
393 | } | ||
394 | #endif /* CONFIG_PROC_FS */ | ||
395 | |||
396 | static int __init register_proc_onchip(void) | ||
397 | { | ||
398 | #ifdef CONFIG_PROC_FS | ||
399 | create_proc_read_entry("io_map",0,0, ioremap_proc_info, &shmedia_iomap); | ||
400 | #endif | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | __initcall(register_proc_onchip); | ||
diff --git a/arch/sh/mm/pg-nommu.c b/arch/sh/mm/pg-nommu.c index d15221beaa16..677dd57f0877 100644 --- a/arch/sh/mm/pg-nommu.c +++ b/arch/sh/mm/pg-nommu.c | |||
@@ -14,12 +14,12 @@ | |||
14 | #include <linux/string.h> | 14 | #include <linux/string.h> |
15 | #include <asm/page.h> | 15 | #include <asm/page.h> |
16 | 16 | ||
17 | void copy_page_nommu(void *to, void *from) | 17 | void copy_page(void *to, void *from) |
18 | { | 18 | { |
19 | memcpy(to, from, PAGE_SIZE); | 19 | memcpy(to, from, PAGE_SIZE); |
20 | } | 20 | } |
21 | 21 | ||
22 | void clear_page_nommu(void *to) | 22 | void clear_page(void *to) |
23 | { | 23 | { |
24 | memset(to, 0, PAGE_SIZE); | 24 | memset(to, 0, PAGE_SIZE); |
25 | } | 25 | } |
diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c index 1d45b82f0a63..ab81c602295f 100644 --- a/arch/sh/mm/pmb.c +++ b/arch/sh/mm/pmb.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <asm/pgtable.h> | 27 | #include <asm/pgtable.h> |
28 | #include <asm/mmu.h> | 28 | #include <asm/mmu.h> |
29 | #include <asm/io.h> | 29 | #include <asm/io.h> |
30 | #include <asm/mmu_context.h> | ||
30 | 31 | ||
31 | #define NR_PMB_ENTRIES 16 | 32 | #define NR_PMB_ENTRIES 16 |
32 | 33 | ||
@@ -162,18 +163,18 @@ repeat: | |||
162 | return 0; | 163 | return 0; |
163 | } | 164 | } |
164 | 165 | ||
165 | int set_pmb_entry(struct pmb_entry *pmbe) | 166 | int __uses_jump_to_uncached set_pmb_entry(struct pmb_entry *pmbe) |
166 | { | 167 | { |
167 | int ret; | 168 | int ret; |
168 | 169 | ||
169 | jump_to_P2(); | 170 | jump_to_uncached(); |
170 | ret = __set_pmb_entry(pmbe->vpn, pmbe->ppn, pmbe->flags, &pmbe->entry); | 171 | ret = __set_pmb_entry(pmbe->vpn, pmbe->ppn, pmbe->flags, &pmbe->entry); |
171 | back_to_P1(); | 172 | back_to_cached(); |
172 | 173 | ||
173 | return ret; | 174 | return ret; |
174 | } | 175 | } |
175 | 176 | ||
176 | void clear_pmb_entry(struct pmb_entry *pmbe) | 177 | void __uses_jump_to_uncached clear_pmb_entry(struct pmb_entry *pmbe) |
177 | { | 178 | { |
178 | unsigned int entry = pmbe->entry; | 179 | unsigned int entry = pmbe->entry; |
179 | unsigned long addr; | 180 | unsigned long addr; |
@@ -187,7 +188,7 @@ void clear_pmb_entry(struct pmb_entry *pmbe) | |||
187 | entry >= NR_PMB_ENTRIES)) | 188 | entry >= NR_PMB_ENTRIES)) |
188 | return; | 189 | return; |
189 | 190 | ||
190 | jump_to_P2(); | 191 | jump_to_uncached(); |
191 | 192 | ||
192 | /* Clear V-bit */ | 193 | /* Clear V-bit */ |
193 | addr = mk_pmb_addr(entry); | 194 | addr = mk_pmb_addr(entry); |
@@ -196,7 +197,7 @@ void clear_pmb_entry(struct pmb_entry *pmbe) | |||
196 | addr = mk_pmb_data(entry); | 197 | addr = mk_pmb_data(entry); |
197 | ctrl_outl(ctrl_inl(addr) & ~PMB_V, addr); | 198 | ctrl_outl(ctrl_inl(addr) & ~PMB_V, addr); |
198 | 199 | ||
199 | back_to_P1(); | 200 | back_to_cached(); |
200 | 201 | ||
201 | clear_bit(entry, &pmb_map); | 202 | clear_bit(entry, &pmb_map); |
202 | } | 203 | } |
@@ -301,17 +302,17 @@ static void pmb_cache_ctor(struct kmem_cache *cachep, void *pmb) | |||
301 | pmbe->entry = PMB_NO_ENTRY; | 302 | pmbe->entry = PMB_NO_ENTRY; |
302 | } | 303 | } |
303 | 304 | ||
304 | static int __init pmb_init(void) | 305 | static int __uses_jump_to_uncached pmb_init(void) |
305 | { | 306 | { |
306 | unsigned int nr_entries = ARRAY_SIZE(pmb_init_map); | 307 | unsigned int nr_entries = ARRAY_SIZE(pmb_init_map); |
307 | unsigned int entry; | 308 | unsigned int entry, i; |
308 | 309 | ||
309 | BUG_ON(unlikely(nr_entries >= NR_PMB_ENTRIES)); | 310 | BUG_ON(unlikely(nr_entries >= NR_PMB_ENTRIES)); |
310 | 311 | ||
311 | pmb_cache = kmem_cache_create("pmb", sizeof(struct pmb_entry), 0, | 312 | pmb_cache = kmem_cache_create("pmb", sizeof(struct pmb_entry), 0, |
312 | SLAB_PANIC, pmb_cache_ctor); | 313 | SLAB_PANIC, pmb_cache_ctor); |
313 | 314 | ||
314 | jump_to_P2(); | 315 | jump_to_uncached(); |
315 | 316 | ||
316 | /* | 317 | /* |
317 | * Ordering is important, P2 must be mapped in the PMB before we | 318 | * Ordering is important, P2 must be mapped in the PMB before we |
@@ -329,7 +330,12 @@ static int __init pmb_init(void) | |||
329 | /* PMB.SE and UB[7] */ | 330 | /* PMB.SE and UB[7] */ |
330 | ctrl_outl((1 << 31) | (1 << 7), PMB_PASCR); | 331 | ctrl_outl((1 << 31) | (1 << 7), PMB_PASCR); |
331 | 332 | ||
332 | back_to_P1(); | 333 | /* Flush out the TLB */ |
334 | i = ctrl_inl(MMUCR); | ||
335 | i |= MMUCR_TI; | ||
336 | ctrl_outl(i, MMUCR); | ||
337 | |||
338 | back_to_cached(); | ||
333 | 339 | ||
334 | return 0; | 340 | return 0; |
335 | } | 341 | } |
diff --git a/arch/sh/mm/tlb-nommu.c b/arch/sh/mm/tlb-nommu.c index 1ccca7c0532e..15111bc7ddd6 100644 --- a/arch/sh/mm/tlb-nommu.c +++ b/arch/sh/mm/tlb-nommu.c | |||
@@ -9,6 +9,7 @@ | |||
9 | */ | 9 | */ |
10 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
11 | #include <linux/mm.h> | 11 | #include <linux/mm.h> |
12 | #include <asm/pgtable.h> | ||
12 | 13 | ||
13 | /* | 14 | /* |
14 | * Nothing too terribly exciting here .. | 15 | * Nothing too terribly exciting here .. |
@@ -49,3 +50,12 @@ void update_mmu_cache(struct vm_area_struct * vma, | |||
49 | { | 50 | { |
50 | BUG(); | 51 | BUG(); |
51 | } | 52 | } |
53 | |||
54 | void __init page_table_range_init(unsigned long start, unsigned long end, | ||
55 | pgd_t *pgd_base) | ||
56 | { | ||
57 | } | ||
58 | |||
59 | void __set_fixmap(enum fixed_addresses idx, unsigned long phys, pgprot_t prot) | ||
60 | { | ||
61 | } | ||
diff --git a/arch/sh/mm/tlb-sh4.c b/arch/sh/mm/tlb-sh4.c index 2d1dd6044307..f0c7b7397fa6 100644 --- a/arch/sh/mm/tlb-sh4.c +++ b/arch/sh/mm/tlb-sh4.c | |||
@@ -79,7 +79,8 @@ void update_mmu_cache(struct vm_area_struct * vma, | |||
79 | local_irq_restore(flags); | 79 | local_irq_restore(flags); |
80 | } | 80 | } |
81 | 81 | ||
82 | void local_flush_tlb_one(unsigned long asid, unsigned long page) | 82 | void __uses_jump_to_uncached local_flush_tlb_one(unsigned long asid, |
83 | unsigned long page) | ||
83 | { | 84 | { |
84 | unsigned long addr, data; | 85 | unsigned long addr, data; |
85 | 86 | ||
@@ -91,7 +92,7 @@ void local_flush_tlb_one(unsigned long asid, unsigned long page) | |||
91 | */ | 92 | */ |
92 | addr = MMU_UTLB_ADDRESS_ARRAY | MMU_PAGE_ASSOC_BIT; | 93 | addr = MMU_UTLB_ADDRESS_ARRAY | MMU_PAGE_ASSOC_BIT; |
93 | data = page | asid; /* VALID bit is off */ | 94 | data = page | asid; /* VALID bit is off */ |
94 | jump_to_P2(); | 95 | jump_to_uncached(); |
95 | ctrl_outl(data, addr); | 96 | ctrl_outl(data, addr); |
96 | back_to_P1(); | 97 | back_to_cached(); |
97 | } | 98 | } |
diff --git a/arch/sh/mm/tlb-sh5.c b/arch/sh/mm/tlb-sh5.c new file mode 100644 index 000000000000..f34274a1ded3 --- /dev/null +++ b/arch/sh/mm/tlb-sh5.c | |||
@@ -0,0 +1,164 @@ | |||
1 | /* | ||
2 | * arch/sh/mm/tlb-sh5.c | ||
3 | * | ||
4 | * Copyright (C) 2003 Paul Mundt <lethal@linux-sh.org> | ||
5 | * Copyright (C) 2003 Richard Curnow <richard.curnow@superh.com> | ||
6 | * | ||
7 | * This file is subject to the terms and conditions of the GNU General Public | ||
8 | * License. See the file "COPYING" in the main directory of this archive | ||
9 | * for more details. | ||
10 | */ | ||
11 | #include <linux/mm.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <asm/page.h> | ||
14 | #include <asm/tlb.h> | ||
15 | #include <asm/mmu_context.h> | ||
16 | |||
17 | /** | ||
18 | * sh64_tlb_init | ||
19 | * | ||
20 | * Perform initial setup for the DTLB and ITLB. | ||
21 | */ | ||
22 | int __init sh64_tlb_init(void) | ||
23 | { | ||
24 | /* Assign some sane DTLB defaults */ | ||
25 | cpu_data->dtlb.entries = 64; | ||
26 | cpu_data->dtlb.step = 0x10; | ||
27 | |||
28 | cpu_data->dtlb.first = DTLB_FIXED | cpu_data->dtlb.step; | ||
29 | cpu_data->dtlb.next = cpu_data->dtlb.first; | ||
30 | |||
31 | cpu_data->dtlb.last = DTLB_FIXED | | ||
32 | ((cpu_data->dtlb.entries - 1) * | ||
33 | cpu_data->dtlb.step); | ||
34 | |||
35 | /* And again for the ITLB */ | ||
36 | cpu_data->itlb.entries = 64; | ||
37 | cpu_data->itlb.step = 0x10; | ||
38 | |||
39 | cpu_data->itlb.first = ITLB_FIXED | cpu_data->itlb.step; | ||
40 | cpu_data->itlb.next = cpu_data->itlb.first; | ||
41 | cpu_data->itlb.last = ITLB_FIXED | | ||
42 | ((cpu_data->itlb.entries - 1) * | ||
43 | cpu_data->itlb.step); | ||
44 | |||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * sh64_next_free_dtlb_entry | ||
50 | * | ||
51 | * Find the next available DTLB entry | ||
52 | */ | ||
53 | unsigned long long sh64_next_free_dtlb_entry(void) | ||
54 | { | ||
55 | return cpu_data->dtlb.next; | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * sh64_get_wired_dtlb_entry | ||
60 | * | ||
61 | * Allocate a wired (locked-in) entry in the DTLB | ||
62 | */ | ||
63 | unsigned long long sh64_get_wired_dtlb_entry(void) | ||
64 | { | ||
65 | unsigned long long entry = sh64_next_free_dtlb_entry(); | ||
66 | |||
67 | cpu_data->dtlb.first += cpu_data->dtlb.step; | ||
68 | cpu_data->dtlb.next += cpu_data->dtlb.step; | ||
69 | |||
70 | return entry; | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * sh64_put_wired_dtlb_entry | ||
75 | * | ||
76 | * @entry: Address of TLB slot. | ||
77 | * | ||
78 | * Free a wired (locked-in) entry in the DTLB. | ||
79 | * | ||
80 | * Works like a stack, last one to allocate must be first one to free. | ||
81 | */ | ||
82 | int sh64_put_wired_dtlb_entry(unsigned long long entry) | ||
83 | { | ||
84 | __flush_tlb_slot(entry); | ||
85 | |||
86 | /* | ||
87 | * We don't do any particularly useful tracking of wired entries, | ||
88 | * so this approach works like a stack .. last one to be allocated | ||
89 | * has to be the first one to be freed. | ||
90 | * | ||
91 | * We could potentially load wired entries into a list and work on | ||
92 | * rebalancing the list periodically (which also entails moving the | ||
93 | * contents of a TLB entry) .. though I have a feeling that this is | ||
94 | * more trouble than it's worth. | ||
95 | */ | ||
96 | |||
97 | /* | ||
98 | * Entry must be valid .. we don't want any ITLB addresses! | ||
99 | */ | ||
100 | if (entry <= DTLB_FIXED) | ||
101 | return -EINVAL; | ||
102 | |||
103 | /* | ||
104 | * Next, check if we're within range to be freed. (ie, must be the | ||
105 | * entry beneath the first 'free' entry! | ||
106 | */ | ||
107 | if (entry < (cpu_data->dtlb.first - cpu_data->dtlb.step)) | ||
108 | return -EINVAL; | ||
109 | |||
110 | /* If we are, then bring this entry back into the list */ | ||
111 | cpu_data->dtlb.first -= cpu_data->dtlb.step; | ||
112 | cpu_data->dtlb.next = entry; | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | /** | ||
118 | * sh64_setup_tlb_slot | ||
119 | * | ||
120 | * @config_addr: Address of TLB slot. | ||
121 | * @eaddr: Virtual address. | ||
122 | * @asid: Address Space Identifier. | ||
123 | * @paddr: Physical address. | ||
124 | * | ||
125 | * Load up a virtual<->physical translation for @eaddr<->@paddr in the | ||
126 | * pre-allocated TLB slot @config_addr (see sh64_get_wired_dtlb_entry). | ||
127 | */ | ||
128 | inline void sh64_setup_tlb_slot(unsigned long long config_addr, | ||
129 | unsigned long eaddr, | ||
130 | unsigned long asid, | ||
131 | unsigned long paddr) | ||
132 | { | ||
133 | unsigned long long pteh, ptel; | ||
134 | |||
135 | /* Sign extension */ | ||
136 | #if (NEFF == 32) | ||
137 | pteh = (unsigned long long)(signed long long)(signed long) eaddr; | ||
138 | #else | ||
139 | #error "Can't sign extend more than 32 bits yet" | ||
140 | #endif | ||
141 | pteh &= PAGE_MASK; | ||
142 | pteh |= (asid << PTEH_ASID_SHIFT) | PTEH_VALID; | ||
143 | #if (NEFF == 32) | ||
144 | ptel = (unsigned long long)(signed long long)(signed long) paddr; | ||
145 | #else | ||
146 | #error "Can't sign extend more than 32 bits yet" | ||
147 | #endif | ||
148 | ptel &= PAGE_MASK; | ||
149 | ptel |= (_PAGE_CACHABLE | _PAGE_READ | _PAGE_WRITE); | ||
150 | |||
151 | asm volatile("putcfg %0, 1, %1\n\t" | ||
152 | "putcfg %0, 0, %2\n" | ||
153 | : : "r" (config_addr), "r" (ptel), "r" (pteh)); | ||
154 | } | ||
155 | |||
156 | /** | ||
157 | * sh64_teardown_tlb_slot | ||
158 | * | ||
159 | * @config_addr: Address of TLB slot. | ||
160 | * | ||
161 | * Teardown any existing mapping in the TLB slot @config_addr. | ||
162 | */ | ||
163 | inline void sh64_teardown_tlb_slot(unsigned long long config_addr) | ||
164 | __attribute__ ((alias("__flush_tlb_slot"))); | ||
diff --git a/arch/sh/mm/tlb-flush.c b/arch/sh/mm/tlbflush_32.c index 6f45c1f8a7fe..6f45c1f8a7fe 100644 --- a/arch/sh/mm/tlb-flush.c +++ b/arch/sh/mm/tlbflush_32.c | |||
diff --git a/arch/sh/mm/tlbflush_64.c b/arch/sh/mm/tlbflush_64.c new file mode 100644 index 000000000000..2a98c9ec88ff --- /dev/null +++ b/arch/sh/mm/tlbflush_64.c | |||
@@ -0,0 +1,475 @@ | |||
1 | /* | ||
2 | * arch/sh/mm/tlb-flush_64.c | ||
3 | * | ||
4 | * Copyright (C) 2000, 2001 Paolo Alberelli | ||
5 | * Copyright (C) 2003 Richard Curnow (/proc/tlb, bug fixes) | ||
6 | * Copyright (C) 2003 Paul Mundt | ||
7 | * | ||
8 | * This file is subject to the terms and conditions of the GNU General Public | ||
9 | * License. See the file "COPYING" in the main directory of this archive | ||
10 | * for more details. | ||
11 | */ | ||
12 | #include <linux/signal.h> | ||
13 | #include <linux/rwsem.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/string.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/ptrace.h> | ||
20 | #include <linux/mman.h> | ||
21 | #include <linux/mm.h> | ||
22 | #include <linux/smp.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <asm/system.h> | ||
25 | #include <asm/io.h> | ||
26 | #include <asm/tlb.h> | ||
27 | #include <asm/uaccess.h> | ||
28 | #include <asm/pgalloc.h> | ||
29 | #include <asm/mmu_context.h> | ||
30 | |||
31 | extern void die(const char *,struct pt_regs *,long); | ||
32 | |||
33 | #define PFLAG(val,flag) (( (val) & (flag) ) ? #flag : "" ) | ||
34 | #define PPROT(flag) PFLAG(pgprot_val(prot),flag) | ||
35 | |||
36 | static inline void print_prots(pgprot_t prot) | ||
37 | { | ||
38 | printk("prot is 0x%08lx\n",pgprot_val(prot)); | ||
39 | |||
40 | printk("%s %s %s %s %s\n",PPROT(_PAGE_SHARED),PPROT(_PAGE_READ), | ||
41 | PPROT(_PAGE_EXECUTE),PPROT(_PAGE_WRITE),PPROT(_PAGE_USER)); | ||
42 | } | ||
43 | |||
44 | static inline void print_vma(struct vm_area_struct *vma) | ||
45 | { | ||
46 | printk("vma start 0x%08lx\n", vma->vm_start); | ||
47 | printk("vma end 0x%08lx\n", vma->vm_end); | ||
48 | |||
49 | print_prots(vma->vm_page_prot); | ||
50 | printk("vm_flags 0x%08lx\n", vma->vm_flags); | ||
51 | } | ||
52 | |||
53 | static inline void print_task(struct task_struct *tsk) | ||
54 | { | ||
55 | printk("Task pid %d\n", task_pid_nr(tsk)); | ||
56 | } | ||
57 | |||
58 | static pte_t *lookup_pte(struct mm_struct *mm, unsigned long address) | ||
59 | { | ||
60 | pgd_t *dir; | ||
61 | pud_t *pud; | ||
62 | pmd_t *pmd; | ||
63 | pte_t *pte; | ||
64 | pte_t entry; | ||
65 | |||
66 | dir = pgd_offset(mm, address); | ||
67 | if (pgd_none(*dir)) | ||
68 | return NULL; | ||
69 | |||
70 | pud = pud_offset(dir, address); | ||
71 | if (pud_none(*pud)) | ||
72 | return NULL; | ||
73 | |||
74 | pmd = pmd_offset(pud, address); | ||
75 | if (pmd_none(*pmd)) | ||
76 | return NULL; | ||
77 | |||
78 | pte = pte_offset_kernel(pmd, address); | ||
79 | entry = *pte; | ||
80 | if (pte_none(entry) || !pte_present(entry)) | ||
81 | return NULL; | ||
82 | |||
83 | return pte; | ||
84 | } | ||
85 | |||
86 | /* | ||
87 | * This routine handles page faults. It determines the address, | ||
88 | * and the problem, and then passes it off to one of the appropriate | ||
89 | * routines. | ||
90 | */ | ||
91 | asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long writeaccess, | ||
92 | unsigned long textaccess, unsigned long address) | ||
93 | { | ||
94 | struct task_struct *tsk; | ||
95 | struct mm_struct *mm; | ||
96 | struct vm_area_struct * vma; | ||
97 | const struct exception_table_entry *fixup; | ||
98 | pte_t *pte; | ||
99 | int fault; | ||
100 | |||
101 | /* SIM | ||
102 | * Note this is now called with interrupts still disabled | ||
103 | * This is to cope with being called for a missing IO port | ||
104 | * address with interrupts disabled. This should be fixed as | ||
105 | * soon as we have a better 'fast path' miss handler. | ||
106 | * | ||
107 | * Plus take care how you try and debug this stuff. | ||
108 | * For example, writing debug data to a port which you | ||
109 | * have just faulted on is not going to work. | ||
110 | */ | ||
111 | |||
112 | tsk = current; | ||
113 | mm = tsk->mm; | ||
114 | |||
115 | /* Not an IO address, so reenable interrupts */ | ||
116 | local_irq_enable(); | ||
117 | |||
118 | /* | ||
119 | * If we're in an interrupt or have no user | ||
120 | * context, we must not take the fault.. | ||
121 | */ | ||
122 | if (in_atomic() || !mm) | ||
123 | goto no_context; | ||
124 | |||
125 | /* TLB misses upon some cache flushes get done under cli() */ | ||
126 | down_read(&mm->mmap_sem); | ||
127 | |||
128 | vma = find_vma(mm, address); | ||
129 | |||
130 | if (!vma) { | ||
131 | #ifdef DEBUG_FAULT | ||
132 | print_task(tsk); | ||
133 | printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n", | ||
134 | __FUNCTION__,__LINE__, | ||
135 | address,regs->pc,textaccess,writeaccess); | ||
136 | show_regs(regs); | ||
137 | #endif | ||
138 | goto bad_area; | ||
139 | } | ||
140 | if (vma->vm_start <= address) { | ||
141 | goto good_area; | ||
142 | } | ||
143 | |||
144 | if (!(vma->vm_flags & VM_GROWSDOWN)) { | ||
145 | #ifdef DEBUG_FAULT | ||
146 | print_task(tsk); | ||
147 | printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n", | ||
148 | __FUNCTION__,__LINE__, | ||
149 | address,regs->pc,textaccess,writeaccess); | ||
150 | show_regs(regs); | ||
151 | |||
152 | print_vma(vma); | ||
153 | #endif | ||
154 | goto bad_area; | ||
155 | } | ||
156 | if (expand_stack(vma, address)) { | ||
157 | #ifdef DEBUG_FAULT | ||
158 | print_task(tsk); | ||
159 | printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n", | ||
160 | __FUNCTION__,__LINE__, | ||
161 | address,regs->pc,textaccess,writeaccess); | ||
162 | show_regs(regs); | ||
163 | #endif | ||
164 | goto bad_area; | ||
165 | } | ||
166 | /* | ||
167 | * Ok, we have a good vm_area for this memory access, so | ||
168 | * we can handle it.. | ||
169 | */ | ||
170 | good_area: | ||
171 | if (textaccess) { | ||
172 | if (!(vma->vm_flags & VM_EXEC)) | ||
173 | goto bad_area; | ||
174 | } else { | ||
175 | if (writeaccess) { | ||
176 | if (!(vma->vm_flags & VM_WRITE)) | ||
177 | goto bad_area; | ||
178 | } else { | ||
179 | if (!(vma->vm_flags & VM_READ)) | ||
180 | goto bad_area; | ||
181 | } | ||
182 | } | ||
183 | |||
184 | /* | ||
185 | * If for any reason at all we couldn't handle the fault, | ||
186 | * make sure we exit gracefully rather than endlessly redo | ||
187 | * the fault. | ||
188 | */ | ||
189 | survive: | ||
190 | fault = handle_mm_fault(mm, vma, address, writeaccess); | ||
191 | if (unlikely(fault & VM_FAULT_ERROR)) { | ||
192 | if (fault & VM_FAULT_OOM) | ||
193 | goto out_of_memory; | ||
194 | else if (fault & VM_FAULT_SIGBUS) | ||
195 | goto do_sigbus; | ||
196 | BUG(); | ||
197 | } | ||
198 | if (fault & VM_FAULT_MAJOR) | ||
199 | tsk->maj_flt++; | ||
200 | else | ||
201 | tsk->min_flt++; | ||
202 | |||
203 | /* If we get here, the page fault has been handled. Do the TLB refill | ||
204 | now from the newly-setup PTE, to avoid having to fault again right | ||
205 | away on the same instruction. */ | ||
206 | pte = lookup_pte (mm, address); | ||
207 | if (!pte) { | ||
208 | /* From empirical evidence, we can get here, due to | ||
209 | !pte_present(pte). (e.g. if a swap-in occurs, and the page | ||
210 | is swapped back out again before the process that wanted it | ||
211 | gets rescheduled?) */ | ||
212 | goto no_pte; | ||
213 | } | ||
214 | |||
215 | __do_tlb_refill(address, textaccess, pte); | ||
216 | |||
217 | no_pte: | ||
218 | |||
219 | up_read(&mm->mmap_sem); | ||
220 | return; | ||
221 | |||
222 | /* | ||
223 | * Something tried to access memory that isn't in our memory map.. | ||
224 | * Fix it, but check if it's kernel or user first.. | ||
225 | */ | ||
226 | bad_area: | ||
227 | #ifdef DEBUG_FAULT | ||
228 | printk("fault:bad area\n"); | ||
229 | #endif | ||
230 | up_read(&mm->mmap_sem); | ||
231 | |||
232 | if (user_mode(regs)) { | ||
233 | static int count=0; | ||
234 | siginfo_t info; | ||
235 | if (count < 4) { | ||
236 | /* This is really to help debug faults when starting | ||
237 | * usermode, so only need a few */ | ||
238 | count++; | ||
239 | printk("user mode bad_area address=%08lx pid=%d (%s) pc=%08lx\n", | ||
240 | address, task_pid_nr(current), current->comm, | ||
241 | (unsigned long) regs->pc); | ||
242 | #if 0 | ||
243 | show_regs(regs); | ||
244 | #endif | ||
245 | } | ||
246 | if (is_global_init(tsk)) { | ||
247 | panic("INIT had user mode bad_area\n"); | ||
248 | } | ||
249 | tsk->thread.address = address; | ||
250 | tsk->thread.error_code = writeaccess; | ||
251 | info.si_signo = SIGSEGV; | ||
252 | info.si_errno = 0; | ||
253 | info.si_addr = (void *) address; | ||
254 | force_sig_info(SIGSEGV, &info, tsk); | ||
255 | return; | ||
256 | } | ||
257 | |||
258 | no_context: | ||
259 | #ifdef DEBUG_FAULT | ||
260 | printk("fault:No context\n"); | ||
261 | #endif | ||
262 | /* Are we prepared to handle this kernel fault? */ | ||
263 | fixup = search_exception_tables(regs->pc); | ||
264 | if (fixup) { | ||
265 | regs->pc = fixup->fixup; | ||
266 | return; | ||
267 | } | ||
268 | |||
269 | /* | ||
270 | * Oops. The kernel tried to access some bad page. We'll have to | ||
271 | * terminate things with extreme prejudice. | ||
272 | * | ||
273 | */ | ||
274 | if (address < PAGE_SIZE) | ||
275 | printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); | ||
276 | else | ||
277 | printk(KERN_ALERT "Unable to handle kernel paging request"); | ||
278 | printk(" at virtual address %08lx\n", address); | ||
279 | printk(KERN_ALERT "pc = %08Lx%08Lx\n", regs->pc >> 32, regs->pc & 0xffffffff); | ||
280 | die("Oops", regs, writeaccess); | ||
281 | do_exit(SIGKILL); | ||
282 | |||
283 | /* | ||
284 | * We ran out of memory, or some other thing happened to us that made | ||
285 | * us unable to handle the page fault gracefully. | ||
286 | */ | ||
287 | out_of_memory: | ||
288 | if (is_global_init(current)) { | ||
289 | panic("INIT out of memory\n"); | ||
290 | yield(); | ||
291 | goto survive; | ||
292 | } | ||
293 | printk("fault:Out of memory\n"); | ||
294 | up_read(&mm->mmap_sem); | ||
295 | if (is_global_init(current)) { | ||
296 | yield(); | ||
297 | down_read(&mm->mmap_sem); | ||
298 | goto survive; | ||
299 | } | ||
300 | printk("VM: killing process %s\n", tsk->comm); | ||
301 | if (user_mode(regs)) | ||
302 | do_group_exit(SIGKILL); | ||
303 | goto no_context; | ||
304 | |||
305 | do_sigbus: | ||
306 | printk("fault:Do sigbus\n"); | ||
307 | up_read(&mm->mmap_sem); | ||
308 | |||
309 | /* | ||
310 | * Send a sigbus, regardless of whether we were in kernel | ||
311 | * or user mode. | ||
312 | */ | ||
313 | tsk->thread.address = address; | ||
314 | tsk->thread.error_code = writeaccess; | ||
315 | tsk->thread.trap_no = 14; | ||
316 | force_sig(SIGBUS, tsk); | ||
317 | |||
318 | /* Kernel mode? Handle exceptions or die */ | ||
319 | if (!user_mode(regs)) | ||
320 | goto no_context; | ||
321 | } | ||
322 | |||
323 | void update_mmu_cache(struct vm_area_struct * vma, | ||
324 | unsigned long address, pte_t pte) | ||
325 | { | ||
326 | /* | ||
327 | * This appears to get called once for every pte entry that gets | ||
328 | * established => I don't think it's efficient to try refilling the | ||
329 | * TLBs with the pages - some may not get accessed even. Also, for | ||
330 | * executable pages, it is impossible to determine reliably here which | ||
331 | * TLB they should be mapped into (or both even). | ||
332 | * | ||
333 | * So, just do nothing here and handle faults on demand. In the | ||
334 | * TLBMISS handling case, the refill is now done anyway after the pte | ||
335 | * has been fixed up, so that deals with most useful cases. | ||
336 | */ | ||
337 | } | ||
338 | |||
339 | void local_flush_tlb_one(unsigned long asid, unsigned long page) | ||
340 | { | ||
341 | unsigned long long match, pteh=0, lpage; | ||
342 | unsigned long tlb; | ||
343 | |||
344 | /* | ||
345 | * Sign-extend based on neff. | ||
346 | */ | ||
347 | lpage = (page & NEFF_SIGN) ? (page | NEFF_MASK) : page; | ||
348 | match = (asid << PTEH_ASID_SHIFT) | PTEH_VALID; | ||
349 | match |= lpage; | ||
350 | |||
351 | for_each_itlb_entry(tlb) { | ||
352 | asm volatile ("getcfg %1, 0, %0" | ||
353 | : "=r" (pteh) | ||
354 | : "r" (tlb) ); | ||
355 | |||
356 | if (pteh == match) { | ||
357 | __flush_tlb_slot(tlb); | ||
358 | break; | ||
359 | } | ||
360 | } | ||
361 | |||
362 | for_each_dtlb_entry(tlb) { | ||
363 | asm volatile ("getcfg %1, 0, %0" | ||
364 | : "=r" (pteh) | ||
365 | : "r" (tlb) ); | ||
366 | |||
367 | if (pteh == match) { | ||
368 | __flush_tlb_slot(tlb); | ||
369 | break; | ||
370 | } | ||
371 | |||
372 | } | ||
373 | } | ||
374 | |||
375 | void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) | ||
376 | { | ||
377 | unsigned long flags; | ||
378 | |||
379 | if (vma->vm_mm) { | ||
380 | page &= PAGE_MASK; | ||
381 | local_irq_save(flags); | ||
382 | local_flush_tlb_one(get_asid(), page); | ||
383 | local_irq_restore(flags); | ||
384 | } | ||
385 | } | ||
386 | |||
387 | void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | ||
388 | unsigned long end) | ||
389 | { | ||
390 | unsigned long flags; | ||
391 | unsigned long long match, pteh=0, pteh_epn, pteh_low; | ||
392 | unsigned long tlb; | ||
393 | unsigned int cpu = smp_processor_id(); | ||
394 | struct mm_struct *mm; | ||
395 | |||
396 | mm = vma->vm_mm; | ||
397 | if (cpu_context(cpu, mm) == NO_CONTEXT) | ||
398 | return; | ||
399 | |||
400 | local_irq_save(flags); | ||
401 | |||
402 | start &= PAGE_MASK; | ||
403 | end &= PAGE_MASK; | ||
404 | |||
405 | match = (cpu_asid(cpu, mm) << PTEH_ASID_SHIFT) | PTEH_VALID; | ||
406 | |||
407 | /* Flush ITLB */ | ||
408 | for_each_itlb_entry(tlb) { | ||
409 | asm volatile ("getcfg %1, 0, %0" | ||
410 | : "=r" (pteh) | ||
411 | : "r" (tlb) ); | ||
412 | |||
413 | pteh_epn = pteh & PAGE_MASK; | ||
414 | pteh_low = pteh & ~PAGE_MASK; | ||
415 | |||
416 | if (pteh_low == match && pteh_epn >= start && pteh_epn <= end) | ||
417 | __flush_tlb_slot(tlb); | ||
418 | } | ||
419 | |||
420 | /* Flush DTLB */ | ||
421 | for_each_dtlb_entry(tlb) { | ||
422 | asm volatile ("getcfg %1, 0, %0" | ||
423 | : "=r" (pteh) | ||
424 | : "r" (tlb) ); | ||
425 | |||
426 | pteh_epn = pteh & PAGE_MASK; | ||
427 | pteh_low = pteh & ~PAGE_MASK; | ||
428 | |||
429 | if (pteh_low == match && pteh_epn >= start && pteh_epn <= end) | ||
430 | __flush_tlb_slot(tlb); | ||
431 | } | ||
432 | |||
433 | local_irq_restore(flags); | ||
434 | } | ||
435 | |||
436 | void local_flush_tlb_mm(struct mm_struct *mm) | ||
437 | { | ||
438 | unsigned long flags; | ||
439 | unsigned int cpu = smp_processor_id(); | ||
440 | |||
441 | if (cpu_context(cpu, mm) == NO_CONTEXT) | ||
442 | return; | ||
443 | |||
444 | local_irq_save(flags); | ||
445 | |||
446 | cpu_context(cpu, mm) = NO_CONTEXT; | ||
447 | if (mm == current->mm) | ||
448 | activate_context(mm, cpu); | ||
449 | |||
450 | local_irq_restore(flags); | ||
451 | } | ||
452 | |||
453 | void local_flush_tlb_all(void) | ||
454 | { | ||
455 | /* Invalidate all, including shared pages, excluding fixed TLBs */ | ||
456 | unsigned long flags, tlb; | ||
457 | |||
458 | local_irq_save(flags); | ||
459 | |||
460 | /* Flush each ITLB entry */ | ||
461 | for_each_itlb_entry(tlb) | ||
462 | __flush_tlb_slot(tlb); | ||
463 | |||
464 | /* Flush each DTLB entry */ | ||
465 | for_each_dtlb_entry(tlb) | ||
466 | __flush_tlb_slot(tlb); | ||
467 | |||
468 | local_irq_restore(flags); | ||
469 | } | ||
470 | |||
471 | void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) | ||
472 | { | ||
473 | /* FIXME: Optimize this later.. */ | ||
474 | flush_tlb_all(); | ||
475 | } | ||