pci_mmio.c 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  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. 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. down_read(&current->mm->mmap_sem);
  20. ret = -EINVAL;
  21. vma = find_vma(current->mm, user_addr);
  22. if (!vma)
  23. goto out;
  24. ret = -EACCES;
  25. if (!(vma->vm_flags & access))
  26. goto out;
  27. ret = follow_pfn(vma, user_addr, pfn);
  28. out:
  29. up_read(&current->mm->mmap_sem);
  30. return ret;
  31. }
  32. SYSCALL_DEFINE3(s390_pci_mmio_write, unsigned long, mmio_addr,
  33. const void __user *, user_buffer, size_t, length)
  34. {
  35. u8 local_buf[64];
  36. void __iomem *io_addr;
  37. void *buf;
  38. unsigned long pfn;
  39. long ret;
  40. if (!zpci_is_enabled())
  41. return -ENODEV;
  42. if (length <= 0 || PAGE_SIZE - (mmio_addr & ~PAGE_MASK) < length)
  43. return -EINVAL;
  44. if (length > 64) {
  45. buf = kmalloc(length, GFP_KERNEL);
  46. if (!buf)
  47. return -ENOMEM;
  48. } else
  49. buf = local_buf;
  50. ret = get_pfn(mmio_addr, VM_WRITE, &pfn);
  51. if (ret)
  52. goto out;
  53. io_addr = (void __iomem *)((pfn << PAGE_SHIFT) | (mmio_addr & ~PAGE_MASK));
  54. ret = -EFAULT;
  55. if ((unsigned long) io_addr < ZPCI_IOMAP_ADDR_BASE)
  56. goto out;
  57. if (copy_from_user(buf, user_buffer, length))
  58. goto out;
  59. ret = zpci_memcpy_toio(io_addr, buf, length);
  60. out:
  61. if (buf != local_buf)
  62. kfree(buf);
  63. return ret;
  64. }
  65. SYSCALL_DEFINE3(s390_pci_mmio_read, unsigned long, mmio_addr,
  66. void __user *, user_buffer, size_t, length)
  67. {
  68. u8 local_buf[64];
  69. void __iomem *io_addr;
  70. void *buf;
  71. unsigned long pfn;
  72. long ret;
  73. if (!zpci_is_enabled())
  74. return -ENODEV;
  75. if (length <= 0 || PAGE_SIZE - (mmio_addr & ~PAGE_MASK) < length)
  76. return -EINVAL;
  77. if (length > 64) {
  78. buf = kmalloc(length, GFP_KERNEL);
  79. if (!buf)
  80. return -ENOMEM;
  81. } else
  82. buf = local_buf;
  83. ret = get_pfn(mmio_addr, VM_READ, &pfn);
  84. if (ret)
  85. goto out;
  86. io_addr = (void __iomem *)((pfn << PAGE_SHIFT) | (mmio_addr & ~PAGE_MASK));
  87. if ((unsigned long) io_addr < ZPCI_IOMAP_ADDR_BASE) {
  88. ret = -EFAULT;
  89. goto out;
  90. }
  91. ret = zpci_memcpy_fromio(buf, io_addr, length);
  92. if (ret)
  93. goto out;
  94. if (copy_to_user(user_buffer, buf, length))
  95. ret = -EFAULT;
  96. out:
  97. if (buf != local_buf)
  98. kfree(buf);
  99. return ret;
  100. }