diff options
-rw-r--r-- | arch/s390/include/asm/io.h | 7 | ||||
-rw-r--r-- | arch/s390/mm/maccess.c | 67 |
2 files changed, 69 insertions, 5 deletions
diff --git a/arch/s390/include/asm/io.h b/arch/s390/include/asm/io.h index b7ff6afc3caa..27216d317991 100644 --- a/arch/s390/include/asm/io.h +++ b/arch/s390/include/asm/io.h | |||
@@ -38,11 +38,8 @@ static inline void * phys_to_virt(unsigned long address) | |||
38 | return (void *) address; | 38 | return (void *) address; |
39 | } | 39 | } |
40 | 40 | ||
41 | /* | 41 | void *xlate_dev_mem_ptr(unsigned long phys); |
42 | * Convert a physical pointer to a virtual kernel pointer for /dev/mem | 42 | void unxlate_dev_mem_ptr(unsigned long phys, void *addr); |
43 | * access | ||
44 | */ | ||
45 | #define xlate_dev_mem_ptr(p) __va(p) | ||
46 | 43 | ||
47 | /* | 44 | /* |
48 | * Convert a virtual cached pointer to an uncached pointer | 45 | * Convert a virtual cached pointer to an uncached pointer |
diff --git a/arch/s390/mm/maccess.c b/arch/s390/mm/maccess.c index e1335dc2b1b7..795a0a9bb2eb 100644 --- a/arch/s390/mm/maccess.c +++ b/arch/s390/mm/maccess.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/types.h> | 12 | #include <linux/types.h> |
13 | #include <linux/errno.h> | 13 | #include <linux/errno.h> |
14 | #include <linux/gfp.h> | 14 | #include <linux/gfp.h> |
15 | #include <linux/cpu.h> | ||
15 | #include <asm/ctl_reg.h> | 16 | #include <asm/ctl_reg.h> |
16 | 17 | ||
17 | /* | 18 | /* |
@@ -166,3 +167,69 @@ out: | |||
166 | free_page((unsigned long) buf); | 167 | free_page((unsigned long) buf); |
167 | return rc; | 168 | return rc; |
168 | } | 169 | } |
170 | |||
171 | /* | ||
172 | * Check if physical address is within prefix or zero page | ||
173 | */ | ||
174 | static int is_swapped(unsigned long addr) | ||
175 | { | ||
176 | unsigned long lc; | ||
177 | int cpu; | ||
178 | |||
179 | if (addr < sizeof(struct _lowcore)) | ||
180 | return 1; | ||
181 | for_each_online_cpu(cpu) { | ||
182 | lc = (unsigned long) lowcore_ptr[cpu]; | ||
183 | if (addr > lc + sizeof(struct _lowcore) - 1 || addr < lc) | ||
184 | continue; | ||
185 | return 1; | ||
186 | } | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * Return swapped prefix or zero page address | ||
192 | */ | ||
193 | static unsigned long get_swapped(unsigned long addr) | ||
194 | { | ||
195 | unsigned long prefix = store_prefix(); | ||
196 | |||
197 | if (addr < sizeof(struct _lowcore)) | ||
198 | return addr + prefix; | ||
199 | if (addr >= prefix && addr < prefix + sizeof(struct _lowcore)) | ||
200 | return addr - prefix; | ||
201 | return addr; | ||
202 | } | ||
203 | |||
204 | /* | ||
205 | * Convert a physical pointer for /dev/mem access | ||
206 | * | ||
207 | * For swapped prefix pages a new buffer is returned that contains a copy of | ||
208 | * the absolute memory. The buffer size is maximum one page large. | ||
209 | */ | ||
210 | void *xlate_dev_mem_ptr(unsigned long addr) | ||
211 | { | ||
212 | void *bounce = (void *) addr; | ||
213 | unsigned long size; | ||
214 | |||
215 | get_online_cpus(); | ||
216 | preempt_disable(); | ||
217 | if (is_swapped(addr)) { | ||
218 | size = PAGE_SIZE - (addr & ~PAGE_MASK); | ||
219 | bounce = (void *) __get_free_page(GFP_ATOMIC); | ||
220 | if (bounce) | ||
221 | memcpy_real(bounce, (void *) get_swapped(addr), size); | ||
222 | } | ||
223 | preempt_enable(); | ||
224 | put_online_cpus(); | ||
225 | return bounce; | ||
226 | } | ||
227 | |||
228 | /* | ||
229 | * Free converted buffer for /dev/mem access (if necessary) | ||
230 | */ | ||
231 | void unxlate_dev_mem_ptr(unsigned long addr, void *buf) | ||
232 | { | ||
233 | if ((void *) addr != buf) | ||
234 | free_page((unsigned long) buf); | ||
235 | } | ||