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(¤t->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(¤t->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