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