Add the new __NR_s390_pci_mmio_write and __NR_s390_pci_mmio_read system calls to allow user space applications to access device PCI I/O memory pages on s390x platform. [ Martin Schwidefsky: some code beautification ]
Unified · Split
| @@ -287,7 +287,9 @@ · 286 unmodified lines | ||
| 287 | 287 | #define __NR_getrandom 349 |
| 288 | 288 | #define __NR_memfd_create 350 |
| 289 | 289 | #define __NR_bpf 351 |
| 290 | −#define NR_syscalls 352 | |
| 290 | +#define __NR_s390_pci_mmio_write 352 | |
| 291 | +#define __NR_s390_pci_mmio_read 353 | |
| 292 | +#define NR_syscalls 354 | |
| 291 | 293 | |
| 292 | 294 | /* |
| 293 | 295 | * There are some system calls that are not present on 64 bit, some |
| @@ -218,3 +218,5 @@ COMPAT_SYSCALL_WRAP3(seccomp, unsigned int, op, unsigned int, flags, const char · 217 unmodified lines | ||
| 218 | 218 | COMPAT_SYSCALL_WRAP3(getrandom, char __user *, buf, size_t, count, unsigned int, flags) |
| 219 | 219 | COMPAT_SYSCALL_WRAP2(memfd_create, const char __user *, uname, unsigned int, flags) |
| 220 | 220 | COMPAT_SYSCALL_WRAP3(bpf, int, cmd, union bpf_attr *, attr, unsigned int, size); |
| 221 | +COMPAT_SYSCALL_WRAP3(s390_pci_mmio_write, const unsigned long, mmio_addr, const void __user *, user_buffer, const size_t, length); | |
| 222 | +COMPAT_SYSCALL_WRAP3(s390_pci_mmio_read, const unsigned long, mmio_addr, void __user *, user_buffer, const size_t, length); | |
| @@ -74,4 +74,6 @@ struct old_sigaction; · 73 unmodified lines | ||
| 74 | 74 | long sys_s390_personality(unsigned int personality); |
| 75 | 75 | long sys_s390_runtime_instr(int command, int signum); |
| 76 | 76 | |
| 77 | +long sys_s390_pci_mmio_write(unsigned long, const void __user *, size_t); | |
| 78 | +long sys_s390_pci_mmio_read(unsigned long, void __user *, size_t); | |
| 77 | 79 | #endif /* _ENTRY_H */ |
| @@ -360,3 +360,5 @@ SYSCALL(sys_seccomp,sys_seccomp,compat_sys_seccomp) · 359 unmodified lines | ||
| 360 | 360 | SYSCALL(sys_getrandom,sys_getrandom,compat_sys_getrandom) |
| 361 | 361 | SYSCALL(sys_memfd_create,sys_memfd_create,compat_sys_memfd_create) /* 350 */ |
| 362 | 362 | SYSCALL(sys_bpf,sys_bpf,compat_sys_bpf) |
| 363 | +SYSCALL(sys_ni_syscall,sys_s390_pci_mmio_write,compat_sys_s390_pci_mmio_write) | |
| 364 | +SYSCALL(sys_ni_syscall,sys_s390_pci_mmio_read,compat_sys_s390_pci_mmio_read) | |
| @@ -3,4 +3,4 @@ · 2 unmodified lines | ||
| 3 | 3 | # |
| 4 | 4 | |
| 5 | 5 | obj-$(CONFIG_PCI) += pci.o pci_dma.o pci_clp.o pci_sysfs.o \ |
| 6 | − pci_event.o pci_debug.o pci_insn.o | |
| 6 | + pci_event.o pci_debug.o pci_insn.o pci_mmio.o | |
| @@ -0,0 +1,115 @@ | ||
| 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(¤t->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(¤t->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 *)((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 | + memcpy_toio(io_addr, buf, length); | |
| 68 | + ret = 0; | |
| 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 *)((pfn << PAGE_SHIFT) | (mmio_addr & ~PAGE_MASK)); | |
| 100 | + | |
| 101 | + ret = -EFAULT; | |
| 102 | + if ((unsigned long) io_addr < ZPCI_IOMAP_ADDR_BASE) | |
| 103 | + goto out; | |
| 104 | + | |
| 105 | + memcpy_fromio(buf, io_addr, length); | |
| 106 | + | |
| 107 | + if (copy_to_user(user_buffer, buf, length)) | |
| 108 | + goto out; | |
| 109 | + | |
| 110 | + ret = 0; | |
| 111 | +out: | |
| 112 | + if (buf != local_buf) | |
| 113 | + kfree(buf); | |
| 114 | + return ret; | |
| 115 | +} | |
| @@ -169,6 +169,8 @@ cond_syscall(ppc_rtas); · 168 unmodified lines | ||
| 169 | 169 | cond_syscall(sys_spu_run); |
| 170 | 170 | cond_syscall(sys_spu_create); |
| 171 | 171 | cond_syscall(sys_subpage_prot); |
| 172 | +cond_syscall(sys_s390_pci_mmio_read); | |
| 173 | +cond_syscall(sys_s390_pci_mmio_write); | |
| 172 | 174 | |
| 173 | 175 | /* mmu depending weak syscall entries */ |
| 174 | 176 | cond_syscall(sys_mprotect); |