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