linux/arch/s390/pci/pci_mmio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Access to PCI I/O memory from user space programs.
   4 *
   5 * Copyright IBM Corp. 2014
   6 * Author(s): Alexey Ishchuk <aishchuk@linux.vnet.ibm.com>
   7 */
   8#include <linux/kernel.h>
   9#include <linux/syscalls.h>
  10#include <linux/init.h>
  11#include <linux/mm.h>
  12#include <linux/errno.h>
  13#include <linux/pci.h>
  14
  15static long get_pfn(unsigned long user_addr, unsigned long access,
  16                    unsigned long *pfn)
  17{
  18        struct vm_area_struct *vma;
  19        long ret;
  20
  21        down_read(&current->mm->mmap_sem);
  22        ret = -EINVAL;
  23        vma = find_vma(current->mm, user_addr);
  24        if (!vma)
  25                goto out;
  26        ret = -EACCES;
  27        if (!(vma->vm_flags & access))
  28                goto out;
  29        ret = follow_pfn(vma, user_addr, pfn);
  30out:
  31        up_read(&current->mm->mmap_sem);
  32        return ret;
  33}
  34
  35SYSCALL_DEFINE3(s390_pci_mmio_write, unsigned long, mmio_addr,
  36                const void __user *, user_buffer, size_t, length)
  37{
  38        u8 local_buf[64];
  39        void __iomem *io_addr;
  40        void *buf;
  41        unsigned long pfn;
  42        long ret;
  43
  44        if (!zpci_is_enabled())
  45                return -ENODEV;
  46
  47        if (length <= 0 || PAGE_SIZE - (mmio_addr & ~PAGE_MASK) < length)
  48                return -EINVAL;
  49        if (length > 64) {
  50                buf = kmalloc(length, GFP_KERNEL);
  51                if (!buf)
  52                        return -ENOMEM;
  53        } else
  54                buf = local_buf;
  55
  56        ret = get_pfn(mmio_addr, VM_WRITE, &pfn);
  57        if (ret)
  58                goto out;
  59        io_addr = (void __iomem *)((pfn << PAGE_SHIFT) | (mmio_addr & ~PAGE_MASK));
  60
  61        ret = -EFAULT;
  62        if ((unsigned long) io_addr < ZPCI_IOMAP_ADDR_BASE)
  63                goto out;
  64
  65        if (copy_from_user(buf, user_buffer, length))
  66                goto out;
  67
  68        ret = zpci_memcpy_toio(io_addr, buf, length);
  69out:
  70        if (buf != local_buf)
  71                kfree(buf);
  72        return ret;
  73}
  74
  75SYSCALL_DEFINE3(s390_pci_mmio_read, unsigned long, mmio_addr,
  76                void __user *, user_buffer, size_t, length)
  77{
  78        u8 local_buf[64];
  79        void __iomem *io_addr;
  80        void *buf;
  81        unsigned long pfn;
  82        long ret;
  83
  84        if (!zpci_is_enabled())
  85                return -ENODEV;
  86
  87        if (length <= 0 || PAGE_SIZE - (mmio_addr & ~PAGE_MASK) < length)
  88                return -EINVAL;
  89        if (length > 64) {
  90                buf = kmalloc(length, GFP_KERNEL);
  91                if (!buf)
  92                        return -ENOMEM;
  93        } else
  94                buf = local_buf;
  95
  96        ret = get_pfn(mmio_addr, VM_READ, &pfn);
  97        if (ret)
  98                goto out;
  99        io_addr = (void __iomem *)((pfn << PAGE_SHIFT) | (mmio_addr & ~PAGE_MASK));
 100
 101        if ((unsigned long) io_addr < ZPCI_IOMAP_ADDR_BASE) {
 102                ret = -EFAULT;
 103                goto out;
 104        }
 105        ret = zpci_memcpy_fromio(buf, io_addr, length);
 106        if (ret)
 107                goto out;
 108        if (copy_to_user(user_buffer, buf, length))
 109                ret = -EFAULT;
 110
 111out:
 112        if (buf != local_buf)
 113                kfree(buf);
 114        return ret;
 115}
 116