diff options
author | Magnus Damm <magnus.damm@gmail.com> | 2008-02-07 06:18:21 -0500 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2008-02-14 00:22:09 -0500 |
commit | e7cc9a7340b8ec018caa9eb1d035fdaef1f2fc51 (patch) | |
tree | a797888f8d3f95734288978351c33af3c965494c | |
parent | 2ade1a9b425c24037327197ea97db054395b536b (diff) |
sh: trapped io support V2
The idea is that we want to get rid of the in/out/readb/writeb callbacks from
the machvec and replace that with simple inline read and write operations to
memory. Fast and simple for most hardware devices (think pci).
Some devices require special treatment though - like 16-bit only CF devices -
so we need to have some method to hook in callbacks.
This patch makes it possible to add a per-device trap generating filter. This
way we can get maximum performance of sane hardware - which doesn't need this
filter - and crappy hardware works but gets punished by a performance hit.
V2 changes things around a bit and replaces io access callbacks with a
simple minimum_bus_width value. In the future we can add stride as well.
Signed-off-by: Magnus Damm <damm@igel.co.jp>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
-rw-r--r-- | arch/sh/Kconfig | 3 | ||||
-rw-r--r-- | arch/sh/kernel/Makefile_32 | 1 | ||||
-rw-r--r-- | arch/sh/kernel/Makefile_64 | 1 | ||||
-rw-r--r-- | arch/sh/kernel/io.c | 8 | ||||
-rw-r--r-- | arch/sh/kernel/io_generic.c | 24 | ||||
-rw-r--r-- | arch/sh/kernel/io_trapped.c | 269 | ||||
-rw-r--r-- | arch/sh/kernel/traps_32.c | 59 | ||||
-rw-r--r-- | arch/sh/mm/fault_32.c | 3 | ||||
-rw-r--r-- | include/asm-sh/io.h | 10 | ||||
-rw-r--r-- | include/asm-sh/io_trapped.h | 58 | ||||
-rw-r--r-- | include/asm-sh/system.h | 5 | ||||
-rw-r--r-- | include/asm-sh/system_32.h | 3 |
12 files changed, 406 insertions, 38 deletions
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 8398cf105a00..f61bf17db39f 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig | |||
@@ -93,6 +93,9 @@ config ARCH_NO_VIRT_TO_BUS | |||
93 | config ARCH_SUPPORTS_AOUT | 93 | config ARCH_SUPPORTS_AOUT |
94 | def_bool y | 94 | def_bool y |
95 | 95 | ||
96 | config IO_TRAPPED | ||
97 | bool | ||
98 | |||
96 | source "init/Kconfig" | 99 | source "init/Kconfig" |
97 | 100 | ||
98 | menu "System type" | 101 | menu "System type" |
diff --git a/arch/sh/kernel/Makefile_32 b/arch/sh/kernel/Makefile_32 index c89289831053..62bf373266f7 100644 --- a/arch/sh/kernel/Makefile_32 +++ b/arch/sh/kernel/Makefile_32 | |||
@@ -22,5 +22,6 @@ obj-$(CONFIG_CRASH_DUMP) += crash_dump.o | |||
22 | obj-$(CONFIG_PM) += pm.o | 22 | obj-$(CONFIG_PM) += pm.o |
23 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | 23 | obj-$(CONFIG_STACKTRACE) += stacktrace.o |
24 | obj-$(CONFIG_BINFMT_ELF) += dump_task.o | 24 | obj-$(CONFIG_BINFMT_ELF) += dump_task.o |
25 | obj-$(CONFIG_IO_TRAPPED) += io_trapped.o | ||
25 | 26 | ||
26 | EXTRA_CFLAGS += -Werror | 27 | EXTRA_CFLAGS += -Werror |
diff --git a/arch/sh/kernel/Makefile_64 b/arch/sh/kernel/Makefile_64 index 1ef21cc087f3..e01283d49cbf 100644 --- a/arch/sh/kernel/Makefile_64 +++ b/arch/sh/kernel/Makefile_64 | |||
@@ -18,5 +18,6 @@ obj-$(CONFIG_CRASH_DUMP) += crash_dump.o | |||
18 | obj-$(CONFIG_PM) += pm.o | 18 | obj-$(CONFIG_PM) += pm.o |
19 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | 19 | obj-$(CONFIG_STACKTRACE) += stacktrace.o |
20 | obj-$(CONFIG_BINFMT_ELF) += dump_task.o | 20 | obj-$(CONFIG_BINFMT_ELF) += dump_task.o |
21 | obj-$(CONFIG_IO_TRAPPED) += io_trapped.o | ||
21 | 22 | ||
22 | EXTRA_CFLAGS += -Werror | 23 | EXTRA_CFLAGS += -Werror |
diff --git a/arch/sh/kernel/io.c b/arch/sh/kernel/io.c index 71c9fde2fd90..2b8991229900 100644 --- a/arch/sh/kernel/io.c +++ b/arch/sh/kernel/io.c | |||
@@ -63,7 +63,13 @@ EXPORT_SYMBOL(memset_io); | |||
63 | 63 | ||
64 | void __iomem *ioport_map(unsigned long port, unsigned int nr) | 64 | void __iomem *ioport_map(unsigned long port, unsigned int nr) |
65 | { | 65 | { |
66 | return sh_mv.mv_ioport_map(port, nr); | 66 | void __iomem *ret; |
67 | |||
68 | ret = __ioport_map_trapped(port, nr); | ||
69 | if (ret) | ||
70 | return ret; | ||
71 | |||
72 | return __ioport_map(port, nr); | ||
67 | } | 73 | } |
68 | EXPORT_SYMBOL(ioport_map); | 74 | EXPORT_SYMBOL(ioport_map); |
69 | 75 | ||
diff --git a/arch/sh/kernel/io_generic.c b/arch/sh/kernel/io_generic.c index 771ea4230441..db769449f5a7 100644 --- a/arch/sh/kernel/io_generic.c +++ b/arch/sh/kernel/io_generic.c | |||
@@ -33,17 +33,17 @@ static inline void delay(void) | |||
33 | 33 | ||
34 | u8 generic_inb(unsigned long port) | 34 | u8 generic_inb(unsigned long port) |
35 | { | 35 | { |
36 | return ctrl_inb((unsigned long __force)ioport_map(port, 1)); | 36 | return ctrl_inb((unsigned long __force)__ioport_map(port, 1)); |
37 | } | 37 | } |
38 | 38 | ||
39 | u16 generic_inw(unsigned long port) | 39 | u16 generic_inw(unsigned long port) |
40 | { | 40 | { |
41 | return ctrl_inw((unsigned long __force)ioport_map(port, 2)); | 41 | return ctrl_inw((unsigned long __force)__ioport_map(port, 2)); |
42 | } | 42 | } |
43 | 43 | ||
44 | u32 generic_inl(unsigned long port) | 44 | u32 generic_inl(unsigned long port) |
45 | { | 45 | { |
46 | return ctrl_inl((unsigned long __force)ioport_map(port, 4)); | 46 | return ctrl_inl((unsigned long __force)__ioport_map(port, 4)); |
47 | } | 47 | } |
48 | 48 | ||
49 | u8 generic_inb_p(unsigned long port) | 49 | u8 generic_inb_p(unsigned long port) |
@@ -81,7 +81,7 @@ void generic_insb(unsigned long port, void *dst, unsigned long count) | |||
81 | volatile u8 *port_addr; | 81 | volatile u8 *port_addr; |
82 | u8 *buf = dst; | 82 | u8 *buf = dst; |
83 | 83 | ||
84 | port_addr = (volatile u8 *)ioport_map(port, 1); | 84 | port_addr = (volatile u8 *)__ioport_map(port, 1); |
85 | while (count--) | 85 | while (count--) |
86 | *buf++ = *port_addr; | 86 | *buf++ = *port_addr; |
87 | } | 87 | } |
@@ -91,7 +91,7 @@ void generic_insw(unsigned long port, void *dst, unsigned long count) | |||
91 | volatile u16 *port_addr; | 91 | volatile u16 *port_addr; |
92 | u16 *buf = dst; | 92 | u16 *buf = dst; |
93 | 93 | ||
94 | port_addr = (volatile u16 *)ioport_map(port, 2); | 94 | port_addr = (volatile u16 *)__ioport_map(port, 2); |
95 | while (count--) | 95 | while (count--) |
96 | *buf++ = *port_addr; | 96 | *buf++ = *port_addr; |
97 | 97 | ||
@@ -103,7 +103,7 @@ void generic_insl(unsigned long port, void *dst, unsigned long count) | |||
103 | volatile u32 *port_addr; | 103 | volatile u32 *port_addr; |
104 | u32 *buf = dst; | 104 | u32 *buf = dst; |
105 | 105 | ||
106 | port_addr = (volatile u32 *)ioport_map(port, 4); | 106 | port_addr = (volatile u32 *)__ioport_map(port, 4); |
107 | while (count--) | 107 | while (count--) |
108 | *buf++ = *port_addr; | 108 | *buf++ = *port_addr; |
109 | 109 | ||
@@ -112,17 +112,17 @@ void generic_insl(unsigned long port, void *dst, unsigned long count) | |||
112 | 112 | ||
113 | void generic_outb(u8 b, unsigned long port) | 113 | void generic_outb(u8 b, unsigned long port) |
114 | { | 114 | { |
115 | ctrl_outb(b, (unsigned long __force)ioport_map(port, 1)); | 115 | ctrl_outb(b, (unsigned long __force)__ioport_map(port, 1)); |
116 | } | 116 | } |
117 | 117 | ||
118 | void generic_outw(u16 b, unsigned long port) | 118 | void generic_outw(u16 b, unsigned long port) |
119 | { | 119 | { |
120 | ctrl_outw(b, (unsigned long __force)ioport_map(port, 2)); | 120 | ctrl_outw(b, (unsigned long __force)__ioport_map(port, 2)); |
121 | } | 121 | } |
122 | 122 | ||
123 | void generic_outl(u32 b, unsigned long port) | 123 | void generic_outl(u32 b, unsigned long port) |
124 | { | 124 | { |
125 | ctrl_outl(b, (unsigned long __force)ioport_map(port, 4)); | 125 | ctrl_outl(b, (unsigned long __force)__ioport_map(port, 4)); |
126 | } | 126 | } |
127 | 127 | ||
128 | void generic_outb_p(u8 b, unsigned long port) | 128 | void generic_outb_p(u8 b, unsigned long port) |
@@ -153,7 +153,7 @@ void generic_outsb(unsigned long port, const void *src, unsigned long count) | |||
153 | volatile u8 *port_addr; | 153 | volatile u8 *port_addr; |
154 | const u8 *buf = src; | 154 | const u8 *buf = src; |
155 | 155 | ||
156 | port_addr = (volatile u8 __force *)ioport_map(port, 1); | 156 | port_addr = (volatile u8 __force *)__ioport_map(port, 1); |
157 | 157 | ||
158 | while (count--) | 158 | while (count--) |
159 | *port_addr = *buf++; | 159 | *port_addr = *buf++; |
@@ -164,7 +164,7 @@ void generic_outsw(unsigned long port, const void *src, unsigned long count) | |||
164 | volatile u16 *port_addr; | 164 | volatile u16 *port_addr; |
165 | const u16 *buf = src; | 165 | const u16 *buf = src; |
166 | 166 | ||
167 | port_addr = (volatile u16 __force *)ioport_map(port, 2); | 167 | port_addr = (volatile u16 __force *)__ioport_map(port, 2); |
168 | 168 | ||
169 | while (count--) | 169 | while (count--) |
170 | *port_addr = *buf++; | 170 | *port_addr = *buf++; |
@@ -177,7 +177,7 @@ void generic_outsl(unsigned long port, const void *src, unsigned long count) | |||
177 | volatile u32 *port_addr; | 177 | volatile u32 *port_addr; |
178 | const u32 *buf = src; | 178 | const u32 *buf = src; |
179 | 179 | ||
180 | port_addr = (volatile u32 __force *)ioport_map(port, 4); | 180 | port_addr = (volatile u32 __force *)__ioport_map(port, 4); |
181 | while (count--) | 181 | while (count--) |
182 | *port_addr = *buf++; | 182 | *port_addr = *buf++; |
183 | 183 | ||
diff --git a/arch/sh/kernel/io_trapped.c b/arch/sh/kernel/io_trapped.c new file mode 100644 index 000000000000..0bfdc9a34e1a --- /dev/null +++ b/arch/sh/kernel/io_trapped.c | |||
@@ -0,0 +1,269 @@ | |||
1 | /* | ||
2 | * Trapped io support | ||
3 | * | ||
4 | * Copyright (C) 2008 Magnus Damm | ||
5 | * | ||
6 | * Intercept io operations by trapping. | ||
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/kernel.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <linux/bitops.h> | ||
15 | #include <linux/vmalloc.h> | ||
16 | #include <asm/system.h> | ||
17 | #include <asm/mmu_context.h> | ||
18 | #include <asm/uaccess.h> | ||
19 | #include <asm/io.h> | ||
20 | #include <asm/io_trapped.h> | ||
21 | |||
22 | #define TRAPPED_PAGES_MAX 16 | ||
23 | #define MAX(a, b) (((a) >= (b)) ? (a) : (b)) | ||
24 | |||
25 | #ifdef CONFIG_HAS_IOPORT | ||
26 | LIST_HEAD(trapped_io); | ||
27 | #endif | ||
28 | #ifdef CONFIG_HAS_IOMEM | ||
29 | LIST_HEAD(trapped_mem); | ||
30 | #endif | ||
31 | static DEFINE_SPINLOCK(trapped_lock); | ||
32 | |||
33 | int __init register_trapped_io(struct trapped_io *tiop) | ||
34 | { | ||
35 | struct resource *res; | ||
36 | unsigned long len = 0, flags = 0; | ||
37 | struct page *pages[TRAPPED_PAGES_MAX]; | ||
38 | int k, n; | ||
39 | |||
40 | /* structure must be page aligned */ | ||
41 | if ((unsigned long)tiop & (PAGE_SIZE - 1)) | ||
42 | goto bad; | ||
43 | |||
44 | for (k = 0; k < tiop->num_resources; k++) { | ||
45 | res = tiop->resource + k; | ||
46 | len += roundup((res->end - res->start) + 1, PAGE_SIZE); | ||
47 | flags |= res->flags; | ||
48 | } | ||
49 | |||
50 | /* support IORESOURCE_IO _or_ MEM, not both */ | ||
51 | if (hweight_long(flags) != 1) | ||
52 | goto bad; | ||
53 | |||
54 | n = len >> PAGE_SHIFT; | ||
55 | |||
56 | if (n >= TRAPPED_PAGES_MAX) | ||
57 | goto bad; | ||
58 | |||
59 | for (k = 0; k < n; k++) | ||
60 | pages[k] = virt_to_page(tiop); | ||
61 | |||
62 | tiop->virt_base = vmap(pages, n, VM_MAP, PAGE_NONE); | ||
63 | if (!tiop->virt_base) | ||
64 | goto bad; | ||
65 | |||
66 | len = 0; | ||
67 | for (k = 0; k < tiop->num_resources; k++) { | ||
68 | res = tiop->resource + k; | ||
69 | pr_info("trapped io 0x%08lx overrides %s 0x%08lx\n", | ||
70 | (unsigned long)(tiop->virt_base + len), | ||
71 | res->flags & IORESOURCE_IO ? "io" : "mmio", | ||
72 | (unsigned long)res->start); | ||
73 | len += roundup((res->end - res->start) + 1, PAGE_SIZE); | ||
74 | } | ||
75 | |||
76 | tiop->magic = IO_TRAPPED_MAGIC; | ||
77 | INIT_LIST_HEAD(&tiop->list); | ||
78 | spin_lock_irq(&trapped_lock); | ||
79 | if (flags & IORESOURCE_IO) | ||
80 | list_add(&tiop->list, &trapped_io); | ||
81 | if (flags & IORESOURCE_MEM) | ||
82 | list_add(&tiop->list, &trapped_mem); | ||
83 | spin_unlock_irq(&trapped_lock); | ||
84 | |||
85 | return 0; | ||
86 | bad: | ||
87 | pr_warning("unable to install trapped io filter\n"); | ||
88 | return -1; | ||
89 | } | ||
90 | |||
91 | void __iomem *match_trapped_io_handler(struct list_head *list, | ||
92 | unsigned long offset, | ||
93 | unsigned long size) | ||
94 | { | ||
95 | unsigned long voffs; | ||
96 | struct trapped_io *tiop; | ||
97 | struct resource *res; | ||
98 | int k, len; | ||
99 | |||
100 | spin_lock_irq(&trapped_lock); | ||
101 | list_for_each_entry(tiop, list, list) { | ||
102 | voffs = 0; | ||
103 | for (k = 0; k < tiop->num_resources; k++) { | ||
104 | res = tiop->resource + k; | ||
105 | if (res->start == offset) { | ||
106 | spin_unlock_irq(&trapped_lock); | ||
107 | return tiop->virt_base + voffs; | ||
108 | } | ||
109 | |||
110 | len = (res->end - res->start) + 1; | ||
111 | voffs += roundup(len, PAGE_SIZE); | ||
112 | } | ||
113 | } | ||
114 | spin_unlock_irq(&trapped_lock); | ||
115 | return NULL; | ||
116 | } | ||
117 | |||
118 | static struct trapped_io *lookup_tiop(unsigned long address) | ||
119 | { | ||
120 | pgd_t *pgd_k; | ||
121 | pud_t *pud_k; | ||
122 | pmd_t *pmd_k; | ||
123 | pte_t *pte_k; | ||
124 | pte_t entry; | ||
125 | |||
126 | pgd_k = swapper_pg_dir + pgd_index(address); | ||
127 | if (!pgd_present(*pgd_k)) | ||
128 | return NULL; | ||
129 | |||
130 | pud_k = pud_offset(pgd_k, address); | ||
131 | if (!pud_present(*pud_k)) | ||
132 | return NULL; | ||
133 | |||
134 | pmd_k = pmd_offset(pud_k, address); | ||
135 | if (!pmd_present(*pmd_k)) | ||
136 | return NULL; | ||
137 | |||
138 | pte_k = pte_offset_kernel(pmd_k, address); | ||
139 | entry = *pte_k; | ||
140 | |||
141 | return pfn_to_kaddr(pte_pfn(entry)); | ||
142 | } | ||
143 | |||
144 | static unsigned long lookup_address(struct trapped_io *tiop, | ||
145 | unsigned long address) | ||
146 | { | ||
147 | struct resource *res; | ||
148 | unsigned long vaddr = (unsigned long)tiop->virt_base; | ||
149 | unsigned long len; | ||
150 | int k; | ||
151 | |||
152 | for (k = 0; k < tiop->num_resources; k++) { | ||
153 | res = tiop->resource + k; | ||
154 | len = roundup((res->end - res->start) + 1, PAGE_SIZE); | ||
155 | if (address < (vaddr + len)) | ||
156 | return res->start + (address - vaddr); | ||
157 | vaddr += len; | ||
158 | } | ||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static unsigned long long copy_word(unsigned long src_addr, int src_len, | ||
163 | unsigned long dst_addr, int dst_len) | ||
164 | { | ||
165 | unsigned long long tmp = 0; | ||
166 | |||
167 | switch (src_len) { | ||
168 | case 1: | ||
169 | tmp = ctrl_inb(src_addr); | ||
170 | break; | ||
171 | case 2: | ||
172 | tmp = ctrl_inw(src_addr); | ||
173 | break; | ||
174 | case 4: | ||
175 | tmp = ctrl_inl(src_addr); | ||
176 | break; | ||
177 | case 8: | ||
178 | tmp = ctrl_inq(src_addr); | ||
179 | break; | ||
180 | } | ||
181 | |||
182 | switch (dst_len) { | ||
183 | case 1: | ||
184 | ctrl_outb(tmp, dst_addr); | ||
185 | break; | ||
186 | case 2: | ||
187 | ctrl_outw(tmp, dst_addr); | ||
188 | break; | ||
189 | case 4: | ||
190 | ctrl_outl(tmp, dst_addr); | ||
191 | break; | ||
192 | case 8: | ||
193 | ctrl_outq(tmp, dst_addr); | ||
194 | break; | ||
195 | } | ||
196 | |||
197 | return tmp; | ||
198 | } | ||
199 | |||
200 | static unsigned long from_device(void *dst, const void *src, unsigned long cnt) | ||
201 | { | ||
202 | struct trapped_io *tiop; | ||
203 | unsigned long src_addr = (unsigned long)src; | ||
204 | unsigned long long tmp; | ||
205 | |||
206 | pr_debug("trapped io read 0x%08lx (%ld)\n", src_addr, cnt); | ||
207 | tiop = lookup_tiop(src_addr); | ||
208 | WARN_ON(!tiop || (tiop->magic != IO_TRAPPED_MAGIC)); | ||
209 | |||
210 | src_addr = lookup_address(tiop, src_addr); | ||
211 | if (!src_addr) | ||
212 | return cnt; | ||
213 | |||
214 | tmp = copy_word(src_addr, MAX(cnt, (tiop->minimum_bus_width / 8)), | ||
215 | (unsigned long)dst, cnt); | ||
216 | |||
217 | pr_debug("trapped io read 0x%08lx -> 0x%08llx\n", src_addr, tmp); | ||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | static unsigned long to_device(void *dst, const void *src, unsigned long cnt) | ||
222 | { | ||
223 | struct trapped_io *tiop; | ||
224 | unsigned long dst_addr = (unsigned long)dst; | ||
225 | unsigned long long tmp; | ||
226 | |||
227 | pr_debug("trapped io write 0x%08lx (%ld)\n", dst_addr, cnt); | ||
228 | tiop = lookup_tiop(dst_addr); | ||
229 | WARN_ON(!tiop || (tiop->magic != IO_TRAPPED_MAGIC)); | ||
230 | |||
231 | dst_addr = lookup_address(tiop, dst_addr); | ||
232 | if (!dst_addr) | ||
233 | return cnt; | ||
234 | |||
235 | tmp = copy_word((unsigned long)src, cnt, | ||
236 | dst_addr, MAX(cnt, (tiop->minimum_bus_width / 8))); | ||
237 | |||
238 | pr_debug("trapped io write 0x%08lx -> 0x%08llx\n", dst_addr, tmp); | ||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | static struct mem_access trapped_io_access = { | ||
243 | from_device, | ||
244 | to_device, | ||
245 | }; | ||
246 | |||
247 | int handle_trapped_io(struct pt_regs *regs, unsigned long address) | ||
248 | { | ||
249 | mm_segment_t oldfs; | ||
250 | opcode_t instruction; | ||
251 | int tmp; | ||
252 | |||
253 | if (!lookup_tiop(address)) | ||
254 | return 0; | ||
255 | |||
256 | WARN_ON(user_mode(regs)); | ||
257 | |||
258 | oldfs = get_fs(); | ||
259 | set_fs(KERNEL_DS); | ||
260 | if (copy_from_user(&instruction, (void *)(regs->pc), | ||
261 | sizeof(instruction))) { | ||
262 | set_fs(oldfs); | ||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | tmp = handle_unaligned_access(instruction, regs, &trapped_io_access); | ||
267 | set_fs(oldfs); | ||
268 | return tmp == 0; | ||
269 | } | ||
diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index 25b1b8672cf0..baa4fa368dce 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c | |||
@@ -172,6 +172,11 @@ static inline void sign_extend(unsigned int count, unsigned char *dst) | |||
172 | #endif | 172 | #endif |
173 | } | 173 | } |
174 | 174 | ||
175 | static struct mem_access user_mem_access = { | ||
176 | copy_from_user, | ||
177 | copy_to_user, | ||
178 | }; | ||
179 | |||
175 | /* | 180 | /* |
176 | * handle an instruction that does an unaligned memory access by emulating the | 181 | * handle an instruction that does an unaligned memory access by emulating the |
177 | * desired behaviour | 182 | * desired behaviour |
@@ -179,7 +184,8 @@ static inline void sign_extend(unsigned int count, unsigned char *dst) | |||
179 | * (if that instruction is in a branch delay slot) | 184 | * (if that instruction is in a branch delay slot) |
180 | * - return 0 if emulation okay, -EFAULT on existential error | 185 | * - return 0 if emulation okay, -EFAULT on existential error |
181 | */ | 186 | */ |
182 | static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs) | 187 | static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs, |
188 | struct mem_access *ma) | ||
183 | { | 189 | { |
184 | int ret, index, count; | 190 | int ret, index, count; |
185 | unsigned long *rm, *rn; | 191 | unsigned long *rm, *rn; |
@@ -206,7 +212,7 @@ static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs) | |||
206 | #if !defined(__LITTLE_ENDIAN__) | 212 | #if !defined(__LITTLE_ENDIAN__) |
207 | dst += 4-count; | 213 | dst += 4-count; |
208 | #endif | 214 | #endif |
209 | if (copy_from_user(dst, src, count)) | 215 | if (ma->from(dst, src, count)) |
210 | goto fetch_fault; | 216 | goto fetch_fault; |
211 | 217 | ||
212 | sign_extend(count, dst); | 218 | sign_extend(count, dst); |
@@ -219,7 +225,7 @@ static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs) | |||
219 | dst = (unsigned char*) *rn; | 225 | dst = (unsigned char*) *rn; |
220 | dst += regs->regs[0]; | 226 | dst += regs->regs[0]; |
221 | 227 | ||
222 | if (copy_to_user(dst, src, count)) | 228 | if (ma->to(dst, src, count)) |
223 | goto fetch_fault; | 229 | goto fetch_fault; |
224 | } | 230 | } |
225 | ret = 0; | 231 | ret = 0; |
@@ -230,7 +236,7 @@ static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs) | |||
230 | dst = (unsigned char*) *rn; | 236 | dst = (unsigned char*) *rn; |
231 | dst += (instruction&0x000F)<<2; | 237 | dst += (instruction&0x000F)<<2; |
232 | 238 | ||
233 | if (copy_to_user(dst,src,4)) | 239 | if (ma->to(dst, src, 4)) |
234 | goto fetch_fault; | 240 | goto fetch_fault; |
235 | ret = 0; | 241 | ret = 0; |
236 | break; | 242 | break; |
@@ -243,7 +249,7 @@ static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs) | |||
243 | #if !defined(__LITTLE_ENDIAN__) | 249 | #if !defined(__LITTLE_ENDIAN__) |
244 | src += 4-count; | 250 | src += 4-count; |
245 | #endif | 251 | #endif |
246 | if (copy_to_user(dst, src, count)) | 252 | if (ma->to(dst, src, count)) |
247 | goto fetch_fault; | 253 | goto fetch_fault; |
248 | ret = 0; | 254 | ret = 0; |
249 | break; | 255 | break; |
@@ -254,7 +260,7 @@ static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs) | |||
254 | dst = (unsigned char*) rn; | 260 | dst = (unsigned char*) rn; |
255 | *(unsigned long*)dst = 0; | 261 | *(unsigned long*)dst = 0; |
256 | 262 | ||
257 | if (copy_from_user(dst,src,4)) | 263 | if (ma->from(dst, src, 4)) |
258 | goto fetch_fault; | 264 | goto fetch_fault; |
259 | ret = 0; | 265 | ret = 0; |
260 | break; | 266 | break; |
@@ -269,7 +275,7 @@ static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs) | |||
269 | #if !defined(__LITTLE_ENDIAN__) | 275 | #if !defined(__LITTLE_ENDIAN__) |
270 | dst += 4-count; | 276 | dst += 4-count; |
271 | #endif | 277 | #endif |
272 | if (copy_from_user(dst, src, count)) | 278 | if (ma->from(dst, src, count)) |
273 | goto fetch_fault; | 279 | goto fetch_fault; |
274 | sign_extend(count, dst); | 280 | sign_extend(count, dst); |
275 | ret = 0; | 281 | ret = 0; |
@@ -285,7 +291,7 @@ static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs) | |||
285 | dst = (unsigned char*) *rm; /* called Rn in the spec */ | 291 | dst = (unsigned char*) *rm; /* called Rn in the spec */ |
286 | dst += (instruction&0x000F)<<1; | 292 | dst += (instruction&0x000F)<<1; |
287 | 293 | ||
288 | if (copy_to_user(dst, src, 2)) | 294 | if (ma->to(dst, src, 2)) |
289 | goto fetch_fault; | 295 | goto fetch_fault; |
290 | ret = 0; | 296 | ret = 0; |
291 | break; | 297 | break; |
@@ -299,7 +305,7 @@ static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs) | |||
299 | #if !defined(__LITTLE_ENDIAN__) | 305 | #if !defined(__LITTLE_ENDIAN__) |
300 | dst += 2; | 306 | dst += 2; |
301 | #endif | 307 | #endif |
302 | if (copy_from_user(dst, src, 2)) | 308 | if (ma->from(dst, src, 2)) |
303 | goto fetch_fault; | 309 | goto fetch_fault; |
304 | sign_extend(2, dst); | 310 | sign_extend(2, dst); |
305 | ret = 0; | 311 | ret = 0; |
@@ -320,8 +326,9 @@ static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs) | |||
320 | * emulate the instruction in the delay slot | 326 | * emulate the instruction in the delay slot |
321 | * - fetches the instruction from PC+2 | 327 | * - fetches the instruction from PC+2 |
322 | */ | 328 | */ |
323 | static inline int handle_unaligned_delayslot(struct pt_regs *regs, | 329 | static inline int handle_delayslot(struct pt_regs *regs, |
324 | opcode_t old_instruction) | 330 | opcode_t old_instruction, |
331 | struct mem_access *ma) | ||
325 | { | 332 | { |
326 | opcode_t instruction; | 333 | opcode_t instruction; |
327 | void *addr = (void *)(regs->pc + instruction_size(old_instruction)); | 334 | void *addr = (void *)(regs->pc + instruction_size(old_instruction)); |
@@ -336,7 +343,7 @@ static inline int handle_unaligned_delayslot(struct pt_regs *regs, | |||
336 | regs, 0); | 343 | regs, 0); |
337 | } | 344 | } |
338 | 345 | ||
339 | return handle_unaligned_ins(instruction, regs); | 346 | return handle_unaligned_ins(instruction, regs, ma); |
340 | } | 347 | } |
341 | 348 | ||
342 | /* | 349 | /* |
@@ -362,7 +369,8 @@ static inline int handle_unaligned_delayslot(struct pt_regs *regs, | |||
362 | 369 | ||
363 | static int handle_unaligned_notify_count = 10; | 370 | static int handle_unaligned_notify_count = 10; |
364 | 371 | ||
365 | static int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs) | 372 | int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs, |
373 | struct mem_access *ma) | ||
366 | { | 374 | { |
367 | u_int rm; | 375 | u_int rm; |
368 | int ret, index; | 376 | int ret, index; |
@@ -385,19 +393,19 @@ static int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs) | |||
385 | case 0x0000: | 393 | case 0x0000: |
386 | if (instruction==0x000B) { | 394 | if (instruction==0x000B) { |
387 | /* rts */ | 395 | /* rts */ |
388 | ret = handle_unaligned_delayslot(regs, instruction); | 396 | ret = handle_delayslot(regs, instruction, ma); |
389 | if (ret==0) | 397 | if (ret==0) |
390 | regs->pc = regs->pr; | 398 | regs->pc = regs->pr; |
391 | } | 399 | } |
392 | else if ((instruction&0x00FF)==0x0023) { | 400 | else if ((instruction&0x00FF)==0x0023) { |
393 | /* braf @Rm */ | 401 | /* braf @Rm */ |
394 | ret = handle_unaligned_delayslot(regs, instruction); | 402 | ret = handle_delayslot(regs, instruction, ma); |
395 | if (ret==0) | 403 | if (ret==0) |
396 | regs->pc += rm + 4; | 404 | regs->pc += rm + 4; |
397 | } | 405 | } |
398 | else if ((instruction&0x00FF)==0x0003) { | 406 | else if ((instruction&0x00FF)==0x0003) { |
399 | /* bsrf @Rm */ | 407 | /* bsrf @Rm */ |
400 | ret = handle_unaligned_delayslot(regs, instruction); | 408 | ret = handle_delayslot(regs, instruction, ma); |
401 | if (ret==0) { | 409 | if (ret==0) { |
402 | regs->pr = regs->pc + 4; | 410 | regs->pr = regs->pc + 4; |
403 | regs->pc += rm + 4; | 411 | regs->pc += rm + 4; |
@@ -418,13 +426,13 @@ static int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs) | |||
418 | case 0x4000: | 426 | case 0x4000: |
419 | if ((instruction&0x00FF)==0x002B) { | 427 | if ((instruction&0x00FF)==0x002B) { |
420 | /* jmp @Rm */ | 428 | /* jmp @Rm */ |
421 | ret = handle_unaligned_delayslot(regs, instruction); | 429 | ret = handle_delayslot(regs, instruction, ma); |
422 | if (ret==0) | 430 | if (ret==0) |
423 | regs->pc = rm; | 431 | regs->pc = rm; |
424 | } | 432 | } |
425 | else if ((instruction&0x00FF)==0x000B) { | 433 | else if ((instruction&0x00FF)==0x000B) { |
426 | /* jsr @Rm */ | 434 | /* jsr @Rm */ |
427 | ret = handle_unaligned_delayslot(regs, instruction); | 435 | ret = handle_delayslot(regs, instruction, ma); |
428 | if (ret==0) { | 436 | if (ret==0) { |
429 | regs->pr = regs->pc + 4; | 437 | regs->pr = regs->pc + 4; |
430 | regs->pc = rm; | 438 | regs->pc = rm; |
@@ -451,7 +459,7 @@ static int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs) | |||
451 | case 0x0B00: /* bf lab - no delayslot*/ | 459 | case 0x0B00: /* bf lab - no delayslot*/ |
452 | break; | 460 | break; |
453 | case 0x0F00: /* bf/s lab */ | 461 | case 0x0F00: /* bf/s lab */ |
454 | ret = handle_unaligned_delayslot(regs, instruction); | 462 | ret = handle_delayslot(regs, instruction, ma); |
455 | if (ret==0) { | 463 | if (ret==0) { |
456 | #if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB) | 464 | #if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB) |
457 | if ((regs->sr & 0x00000001) != 0) | 465 | if ((regs->sr & 0x00000001) != 0) |
@@ -464,7 +472,7 @@ static int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs) | |||
464 | case 0x0900: /* bt lab - no delayslot */ | 472 | case 0x0900: /* bt lab - no delayslot */ |
465 | break; | 473 | break; |
466 | case 0x0D00: /* bt/s lab */ | 474 | case 0x0D00: /* bt/s lab */ |
467 | ret = handle_unaligned_delayslot(regs, instruction); | 475 | ret = handle_delayslot(regs, instruction, ma); |
468 | if (ret==0) { | 476 | if (ret==0) { |
469 | #if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB) | 477 | #if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB) |
470 | if ((regs->sr & 0x00000001) == 0) | 478 | if ((regs->sr & 0x00000001) == 0) |
@@ -478,13 +486,13 @@ static int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs) | |||
478 | break; | 486 | break; |
479 | 487 | ||
480 | case 0xA000: /* bra label */ | 488 | case 0xA000: /* bra label */ |
481 | ret = handle_unaligned_delayslot(regs, instruction); | 489 | ret = handle_delayslot(regs, instruction, ma); |
482 | if (ret==0) | 490 | if (ret==0) |
483 | regs->pc += SH_PC_12BIT_OFFSET(instruction); | 491 | regs->pc += SH_PC_12BIT_OFFSET(instruction); |
484 | break; | 492 | break; |
485 | 493 | ||
486 | case 0xB000: /* bsr label */ | 494 | case 0xB000: /* bsr label */ |
487 | ret = handle_unaligned_delayslot(regs, instruction); | 495 | ret = handle_delayslot(regs, instruction, ma); |
488 | if (ret==0) { | 496 | if (ret==0) { |
489 | regs->pr = regs->pc + 4; | 497 | regs->pr = regs->pc + 4; |
490 | regs->pc += SH_PC_12BIT_OFFSET(instruction); | 498 | regs->pc += SH_PC_12BIT_OFFSET(instruction); |
@@ -495,7 +503,7 @@ static int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs) | |||
495 | 503 | ||
496 | /* handle non-delay-slot instruction */ | 504 | /* handle non-delay-slot instruction */ |
497 | simple: | 505 | simple: |
498 | ret = handle_unaligned_ins(instruction, regs); | 506 | ret = handle_unaligned_ins(instruction, regs, ma); |
499 | if (ret==0) | 507 | if (ret==0) |
500 | regs->pc += instruction_size(instruction); | 508 | regs->pc += instruction_size(instruction); |
501 | return ret; | 509 | return ret; |
@@ -558,7 +566,8 @@ asmlinkage void do_address_error(struct pt_regs *regs, | |||
558 | goto uspace_segv; | 566 | goto uspace_segv; |
559 | } | 567 | } |
560 | 568 | ||
561 | tmp = handle_unaligned_access(instruction, regs); | 569 | tmp = handle_unaligned_access(instruction, regs, |
570 | &user_mem_access); | ||
562 | set_fs(oldfs); | 571 | set_fs(oldfs); |
563 | 572 | ||
564 | if (tmp==0) | 573 | if (tmp==0) |
@@ -587,7 +596,7 @@ uspace_segv: | |||
587 | die("insn faulting in do_address_error", regs, 0); | 596 | die("insn faulting in do_address_error", regs, 0); |
588 | } | 597 | } |
589 | 598 | ||
590 | handle_unaligned_access(instruction, regs); | 599 | handle_unaligned_access(instruction, regs, &user_mem_access); |
591 | set_fs(oldfs); | 600 | set_fs(oldfs); |
592 | } | 601 | } |
593 | } | 602 | } |
diff --git a/arch/sh/mm/fault_32.c b/arch/sh/mm/fault_32.c index 33b43d20e9f6..4ef0a1f1a9ab 100644 --- a/arch/sh/mm/fault_32.c +++ b/arch/sh/mm/fault_32.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/mm.h> | 15 | #include <linux/mm.h> |
16 | #include <linux/hardirq.h> | 16 | #include <linux/hardirq.h> |
17 | #include <linux/kprobes.h> | 17 | #include <linux/kprobes.h> |
18 | #include <asm/io_trapped.h> | ||
18 | #include <asm/system.h> | 19 | #include <asm/system.h> |
19 | #include <asm/mmu_context.h> | 20 | #include <asm/mmu_context.h> |
20 | #include <asm/tlbflush.h> | 21 | #include <asm/tlbflush.h> |
@@ -163,6 +164,8 @@ no_context: | |||
163 | if (fixup_exception(regs)) | 164 | if (fixup_exception(regs)) |
164 | return; | 165 | return; |
165 | 166 | ||
167 | if (handle_trapped_io(regs, address)) | ||
168 | return; | ||
166 | /* | 169 | /* |
167 | * Oops. The kernel tried to access some bad page. We'll have to | 170 | * Oops. The kernel tried to access some bad page. We'll have to |
168 | * terminate things with extreme prejudice. | 171 | * terminate things with extreme prejudice. |
diff --git a/include/asm-sh/io.h b/include/asm-sh/io.h index 94900c089519..3d2b114f9d57 100644 --- a/include/asm-sh/io.h +++ b/include/asm-sh/io.h | |||
@@ -38,6 +38,7 @@ | |||
38 | */ | 38 | */ |
39 | #define __IO_PREFIX generic | 39 | #define __IO_PREFIX generic |
40 | #include <asm/io_generic.h> | 40 | #include <asm/io_generic.h> |
41 | #include <asm/io_trapped.h> | ||
41 | 42 | ||
42 | #define maybebadio(port) \ | 43 | #define maybebadio(port) \ |
43 | printk(KERN_ERR "bad PC-like io %s:%u for port 0x%lx at 0x%08x\n", \ | 44 | printk(KERN_ERR "bad PC-like io %s:%u for port 0x%lx at 0x%08x\n", \ |
@@ -207,6 +208,8 @@ static inline void __set_io_port_base(unsigned long pbase) | |||
207 | generic_io_base = pbase; | 208 | generic_io_base = pbase; |
208 | } | 209 | } |
209 | 210 | ||
211 | #define __ioport_map(p, n) sh_mv.mv_ioport_map((p), (n)) | ||
212 | |||
210 | /* We really want to try and get these to memcpy etc */ | 213 | /* We really want to try and get these to memcpy etc */ |
211 | extern void memcpy_fromio(void *, volatile void __iomem *, unsigned long); | 214 | extern void memcpy_fromio(void *, volatile void __iomem *, unsigned long); |
212 | extern void memcpy_toio(volatile void __iomem *, const void *, unsigned long); | 215 | extern void memcpy_toio(volatile void __iomem *, const void *, unsigned long); |
@@ -309,7 +312,14 @@ __ioremap_mode(unsigned long offset, unsigned long size, unsigned long flags) | |||
309 | { | 312 | { |
310 | #ifdef CONFIG_SUPERH32 | 313 | #ifdef CONFIG_SUPERH32 |
311 | unsigned long last_addr = offset + size - 1; | 314 | unsigned long last_addr = offset + size - 1; |
315 | #endif | ||
316 | void __iomem *ret; | ||
312 | 317 | ||
318 | ret = __ioremap_trapped(offset, size); | ||
319 | if (ret) | ||
320 | return ret; | ||
321 | |||
322 | #ifdef CONFIG_SUPERH32 | ||
313 | /* | 323 | /* |
314 | * For P1 and P2 space this is trivial, as everything is already | 324 | * For P1 and P2 space this is trivial, as everything is already |
315 | * mapped. Uncached access for P1 addresses are done through P2. | 325 | * mapped. Uncached access for P1 addresses are done through P2. |
diff --git a/include/asm-sh/io_trapped.h b/include/asm-sh/io_trapped.h new file mode 100644 index 000000000000..f1251d4f0ba9 --- /dev/null +++ b/include/asm-sh/io_trapped.h | |||
@@ -0,0 +1,58 @@ | |||
1 | #ifndef __ASM_SH_IO_TRAPPED_H | ||
2 | #define __ASM_SH_IO_TRAPPED_H | ||
3 | |||
4 | #include <linux/list.h> | ||
5 | #include <linux/ioport.h> | ||
6 | #include <asm/page.h> | ||
7 | |||
8 | #define IO_TRAPPED_MAGIC 0xfeedbeef | ||
9 | |||
10 | struct trapped_io { | ||
11 | unsigned int magic; | ||
12 | struct resource *resource; | ||
13 | unsigned int num_resources; | ||
14 | unsigned int minimum_bus_width; | ||
15 | struct list_head list; | ||
16 | void __iomem *virt_base; | ||
17 | } __aligned(PAGE_SIZE); | ||
18 | |||
19 | #ifdef CONFIG_IO_TRAPPED | ||
20 | int register_trapped_io(struct trapped_io *tiop); | ||
21 | int handle_trapped_io(struct pt_regs *regs, unsigned long address); | ||
22 | |||
23 | void __iomem *match_trapped_io_handler(struct list_head *list, | ||
24 | unsigned long offset, | ||
25 | unsigned long size); | ||
26 | |||
27 | #ifdef CONFIG_HAS_IOMEM | ||
28 | extern struct list_head trapped_mem; | ||
29 | |||
30 | static inline void __iomem * | ||
31 | __ioremap_trapped(unsigned long offset, unsigned long size) | ||
32 | { | ||
33 | return match_trapped_io_handler(&trapped_mem, offset, size); | ||
34 | } | ||
35 | #else | ||
36 | #define __ioremap_trapped(offset, size) NULL | ||
37 | #endif | ||
38 | |||
39 | #ifdef CONFIG_HAS_IOPORT | ||
40 | extern struct list_head trapped_io; | ||
41 | |||
42 | static inline void __iomem * | ||
43 | __ioport_map_trapped(unsigned long offset, unsigned long size) | ||
44 | { | ||
45 | return match_trapped_io_handler(&trapped_io, offset, size); | ||
46 | } | ||
47 | #else | ||
48 | #define __ioport_map_trapped(offset, size) NULL | ||
49 | #endif | ||
50 | |||
51 | #else | ||
52 | #define register_trapped_io(tiop) (-1) | ||
53 | #define handle_trapped_io(tiop, address) 0 | ||
54 | #define __ioremap_trapped(offset, size) NULL | ||
55 | #define __ioport_map_trapped(offset, size) NULL | ||
56 | #endif | ||
57 | |||
58 | #endif /* __ASM_SH_IO_TRAPPED_H */ | ||
diff --git a/include/asm-sh/system.h b/include/asm-sh/system.h index 772cd1a0a674..5145aa2a0ce9 100644 --- a/include/asm-sh/system.h +++ b/include/asm-sh/system.h | |||
@@ -182,6 +182,11 @@ BUILD_TRAP_HANDLER(fpu_state_restore); | |||
182 | 182 | ||
183 | #define arch_align_stack(x) (x) | 183 | #define arch_align_stack(x) (x) |
184 | 184 | ||
185 | struct mem_access { | ||
186 | unsigned long (*from)(void *dst, const void *src, unsigned long cnt); | ||
187 | unsigned long (*to)(void *dst, const void *src, unsigned long cnt); | ||
188 | }; | ||
189 | |||
185 | #ifdef CONFIG_SUPERH32 | 190 | #ifdef CONFIG_SUPERH32 |
186 | # include "system_32.h" | 191 | # include "system_32.h" |
187 | #else | 192 | #else |
diff --git a/include/asm-sh/system_32.h b/include/asm-sh/system_32.h index 7ff08d956ba8..f11bcf0855ed 100644 --- a/include/asm-sh/system_32.h +++ b/include/asm-sh/system_32.h | |||
@@ -96,4 +96,7 @@ do { \ | |||
96 | : "=&r" (__dummy)); \ | 96 | : "=&r" (__dummy)); \ |
97 | } while (0) | 97 | } while (0) |
98 | 98 | ||
99 | int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs, | ||
100 | struct mem_access *ma); | ||
101 | |||
99 | #endif /* __ASM_SH_SYSTEM_32_H */ | 102 | #endif /* __ASM_SH_SYSTEM_32_H */ |