linux/arch/s390/mm/maccess.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Access kernel memory without faulting -- s390 specific implementation.
   4 *
   5 * Copyright IBM Corp. 2009, 2015
   6 *
   7 *   Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,
   8 *
   9 */
  10
  11#include <linux/uaccess.h>
  12#include <linux/kernel.h>
  13#include <linux/types.h>
  14#include <linux/errno.h>
  15#include <linux/gfp.h>
  16#include <linux/cpu.h>
  17#include <asm/ctl_reg.h>
  18#include <asm/io.h>
  19#include <asm/stacktrace.h>
  20
  21static notrace long s390_kernel_write_odd(void *dst, const void *src, size_t size)
  22{
  23        unsigned long aligned, offset, count;
  24        char tmp[8];
  25
  26        aligned = (unsigned long) dst & ~7UL;
  27        offset = (unsigned long) dst & 7UL;
  28        size = min(8UL - offset, size);
  29        count = size - 1;
  30        asm volatile(
  31                "       bras    1,0f\n"
  32                "       mvc     0(1,%4),0(%5)\n"
  33                "0:     mvc     0(8,%3),0(%0)\n"
  34                "       ex      %1,0(1)\n"
  35                "       lg      %1,0(%3)\n"
  36                "       lra     %0,0(%0)\n"
  37                "       sturg   %1,%0\n"
  38                : "+&a" (aligned), "+&a" (count), "=m" (tmp)
  39                : "a" (&tmp), "a" (&tmp[offset]), "a" (src)
  40                : "cc", "memory", "1");
  41        return size;
  42}
  43
  44/*
  45 * s390_kernel_write - write to kernel memory bypassing DAT
  46 * @dst: destination address
  47 * @src: source address
  48 * @size: number of bytes to copy
  49 *
  50 * This function writes to kernel memory bypassing DAT and possible page table
  51 * write protection. It writes to the destination using the sturg instruction.
  52 * Therefore we have a read-modify-write sequence: the function reads eight
  53 * bytes from destination at an eight byte boundary, modifies the bytes
  54 * requested and writes the result back in a loop.
  55 */
  56static DEFINE_SPINLOCK(s390_kernel_write_lock);
  57
  58notrace void *s390_kernel_write(void *dst, const void *src, size_t size)
  59{
  60        void *tmp = dst;
  61        unsigned long flags;
  62        long copied;
  63
  64        spin_lock_irqsave(&s390_kernel_write_lock, flags);
  65        if (!(flags & PSW_MASK_DAT)) {
  66                memcpy(dst, src, size);
  67        } else {
  68                while (size) {
  69                        copied = s390_kernel_write_odd(tmp, src, size);
  70                        tmp += copied;
  71                        src += copied;
  72                        size -= copied;
  73                }
  74        }
  75        spin_unlock_irqrestore(&s390_kernel_write_lock, flags);
  76
  77        return dst;
  78}
  79
  80static int __no_sanitize_address __memcpy_real(void *dest, void *src, size_t count)
  81{
  82        union register_pair _dst, _src;
  83        int rc = -EFAULT;
  84
  85        _dst.even = (unsigned long) dest;
  86        _dst.odd  = (unsigned long) count;
  87        _src.even = (unsigned long) src;
  88        _src.odd  = (unsigned long) count;
  89        asm volatile (
  90                "0:     mvcle   %[dst],%[src],0\n"
  91                "1:     jo      0b\n"
  92                "       lhi     %[rc],0\n"
  93                "2:\n"
  94                EX_TABLE(1b,2b)
  95                : [rc] "+&d" (rc), [dst] "+&d" (_dst.pair), [src] "+&d" (_src.pair)
  96                : : "cc", "memory");
  97        return rc;
  98}
  99
 100static unsigned long __no_sanitize_address _memcpy_real(unsigned long dest,
 101                                                        unsigned long src,
 102                                                        unsigned long count)
 103{
 104        int irqs_disabled, rc;
 105        unsigned long flags;
 106
 107        if (!count)
 108                return 0;
 109        flags = arch_local_irq_save();
 110        irqs_disabled = arch_irqs_disabled_flags(flags);
 111        if (!irqs_disabled)
 112                trace_hardirqs_off();
 113        __arch_local_irq_stnsm(0xf8); // disable DAT
 114        rc = __memcpy_real((void *) dest, (void *) src, (size_t) count);
 115        if (flags & PSW_MASK_DAT)
 116                __arch_local_irq_stosm(0x04); // enable DAT
 117        if (!irqs_disabled)
 118                trace_hardirqs_on();
 119        __arch_local_irq_ssm(flags);
 120        return rc;
 121}
 122
 123/*
 124 * Copy memory in real mode (kernel to kernel)
 125 */
 126int memcpy_real(void *dest, void *src, size_t count)
 127{
 128        unsigned long _dest  = (unsigned long)dest;
 129        unsigned long _src   = (unsigned long)src;
 130        unsigned long _count = (unsigned long)count;
 131        int rc;
 132
 133        if (S390_lowcore.nodat_stack != 0) {
 134                preempt_disable();
 135                rc = call_on_stack(3, S390_lowcore.nodat_stack,
 136                                   unsigned long, _memcpy_real,
 137                                   unsigned long, _dest,
 138                                   unsigned long, _src,
 139                                   unsigned long, _count);
 140                preempt_enable();
 141                return rc;
 142        }
 143        /*
 144         * This is a really early memcpy_real call, the stacks are
 145         * not set up yet. Just call _memcpy_real on the early boot
 146         * stack
 147         */
 148        return _memcpy_real(_dest, _src, _count);
 149}
 150
 151/*
 152 * Copy memory in absolute mode (kernel to kernel)
 153 */
 154void memcpy_absolute(void *dest, void *src, size_t count)
 155{
 156        unsigned long cr0, flags, prefix;
 157
 158        flags = arch_local_irq_save();
 159        __ctl_store(cr0, 0, 0);
 160        __ctl_clear_bit(0, 28); /* disable lowcore protection */
 161        prefix = store_prefix();
 162        if (prefix) {
 163                local_mcck_disable();
 164                set_prefix(0);
 165                memcpy(dest, src, count);
 166                set_prefix(prefix);
 167                local_mcck_enable();
 168        } else {
 169                memcpy(dest, src, count);
 170        }
 171        __ctl_load(cr0, 0, 0);
 172        arch_local_irq_restore(flags);
 173}
 174
 175/*
 176 * Copy memory from kernel (real) to user (virtual)
 177 */
 178int copy_to_user_real(void __user *dest, void *src, unsigned long count)
 179{
 180        int offs = 0, size, rc;
 181        char *buf;
 182
 183        buf = (char *) __get_free_page(GFP_KERNEL);
 184        if (!buf)
 185                return -ENOMEM;
 186        rc = -EFAULT;
 187        while (offs < count) {
 188                size = min(PAGE_SIZE, count - offs);
 189                if (memcpy_real(buf, src + offs, size))
 190                        goto out;
 191                if (copy_to_user(dest + offs, buf, size))
 192                        goto out;
 193                offs += size;
 194        }
 195        rc = 0;
 196out:
 197        free_page((unsigned long) buf);
 198        return rc;
 199}
 200
 201/*
 202 * Check if physical address is within prefix or zero page
 203 */
 204static int is_swapped(unsigned long addr)
 205{
 206        unsigned long lc;
 207        int cpu;
 208
 209        if (addr < sizeof(struct lowcore))
 210                return 1;
 211        for_each_online_cpu(cpu) {
 212                lc = (unsigned long) lowcore_ptr[cpu];
 213                if (addr > lc + sizeof(struct lowcore) - 1 || addr < lc)
 214                        continue;
 215                return 1;
 216        }
 217        return 0;
 218}
 219
 220/*
 221 * Convert a physical pointer for /dev/mem access
 222 *
 223 * For swapped prefix pages a new buffer is returned that contains a copy of
 224 * the absolute memory. The buffer size is maximum one page large.
 225 */
 226void *xlate_dev_mem_ptr(phys_addr_t addr)
 227{
 228        void *bounce = (void *) addr;
 229        unsigned long size;
 230
 231        get_online_cpus();
 232        preempt_disable();
 233        if (is_swapped(addr)) {
 234                size = PAGE_SIZE - (addr & ~PAGE_MASK);
 235                bounce = (void *) __get_free_page(GFP_ATOMIC);
 236                if (bounce)
 237                        memcpy_absolute(bounce, (void *) addr, size);
 238        }
 239        preempt_enable();
 240        put_online_cpus();
 241        return bounce;
 242}
 243
 244/*
 245 * Free converted buffer for /dev/mem access (if necessary)
 246 */
 247void unxlate_dev_mem_ptr(phys_addr_t addr, void *buf)
 248{
 249        if ((void *) addr != buf)
 250                free_page((unsigned long) buf);
 251}
 252