GNU Linux-libre 4.19.286-gnu1
[releases.git] / arch / s390 / pci / pci_mmio.c
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
15 static 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);
30 out:
31         up_read(&current->mm->mmap_sem);
32         return ret;
33 }
34
35 SYSCALL_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);
69 out:
70         if (buf != local_buf)
71                 kfree(buf);
72         return ret;
73 }
74
75 SYSCALL_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
111 out:
112         if (buf != local_buf)
113                 kfree(buf);
114         return ret;
115 }