Merge tag 'for-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux...
[deliverable/linux.git] / arch / s390 / pci / pci_mmio.c
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
14 static 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);
29 out:
30 up_read(&current->mm->mmap_sem);
31 return ret;
32 }
33
34 SYSCALL_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);
68 out:
69 if (buf != local_buf)
70 kfree(buf);
71 return ret;
72 }
73
74 SYSCALL_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
110 out:
111 if (buf != local_buf)
112 kfree(buf);
113 return ret;
114 }
This page took 0.048002 seconds and 5 git commands to generate.