]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - arch/s390/mm/maccess.c
Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
[karo-tx-linux.git] / arch / s390 / mm / maccess.c
index e1335dc2b1b76fffea016ef83e7d1b229086bf20..921fa541dc0431050dfc6b429b4836b94972d6a0 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/gfp.h>
+#include <linux/cpu.h>
 #include <asm/ctl_reg.h>
 
 /*
@@ -100,19 +101,27 @@ int memcpy_real(void *dest, void *src, size_t count)
 }
 
 /*
- * Copy memory to absolute zero
+ * Copy memory in absolute mode (kernel to kernel)
  */
-void copy_to_absolute_zero(void *dest, void *src, size_t count)
+void memcpy_absolute(void *dest, void *src, size_t count)
 {
-       unsigned long cr0;
+       unsigned long cr0, flags, prefix;
 
-       BUG_ON((unsigned long) dest + count >= sizeof(struct _lowcore));
-       preempt_disable();
+       flags = arch_local_irq_save();
        __ctl_store(cr0, 0, 0);
        __ctl_clear_bit(0, 28); /* disable lowcore protection */
-       memcpy_real(dest + store_prefix(), src, count);
+       prefix = store_prefix();
+       if (prefix) {
+               local_mcck_disable();
+               set_prefix(0);
+               memcpy(dest, src, count);
+               set_prefix(prefix);
+               local_mcck_enable();
+       } else {
+               memcpy(dest, src, count);
+       }
        __ctl_load(cr0, 0, 0);
-       preempt_enable();
+       arch_local_irq_restore(flags);
 }
 
 /*
@@ -166,3 +175,55 @@ out:
        free_page((unsigned long) buf);
        return rc;
 }
+
+/*
+ * Check if physical address is within prefix or zero page
+ */
+static int is_swapped(unsigned long addr)
+{
+       unsigned long lc;
+       int cpu;
+
+       if (addr < sizeof(struct _lowcore))
+               return 1;
+       for_each_online_cpu(cpu) {
+               lc = (unsigned long) lowcore_ptr[cpu];
+               if (addr > lc + sizeof(struct _lowcore) - 1 || addr < lc)
+                       continue;
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * Convert a physical pointer for /dev/mem access
+ *
+ * For swapped prefix pages a new buffer is returned that contains a copy of
+ * the absolute memory. The buffer size is maximum one page large.
+ */
+void *xlate_dev_mem_ptr(unsigned long addr)
+{
+       void *bounce = (void *) addr;
+       unsigned long size;
+
+       get_online_cpus();
+       preempt_disable();
+       if (is_swapped(addr)) {
+               size = PAGE_SIZE - (addr & ~PAGE_MASK);
+               bounce = (void *) __get_free_page(GFP_ATOMIC);
+               if (bounce)
+                       memcpy_absolute(bounce, (void *) addr, size);
+       }
+       preempt_enable();
+       put_online_cpus();
+       return bounce;
+}
+
+/*
+ * Free converted buffer for /dev/mem access (if necessary)
+ */
+void unxlate_dev_mem_ptr(unsigned long addr, void *buf)
+{
+       if ((void *) addr != buf)
+               free_page((unsigned long) buf);
+}