xref: /openbmc/linux/arch/s390/pci/pci_mmio.c (revision ba61bb17)
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 }
116