sclp_ctl.c 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * IOCTL interface for SCLP
  4. *
  5. * Copyright IBM Corp. 2012
  6. *
  7. * Author: Michael Holzheu <holzheu@linux.vnet.ibm.com>
  8. */
  9. #include <linux/compat.h>
  10. #include <linux/uaccess.h>
  11. #include <linux/miscdevice.h>
  12. #include <linux/gfp.h>
  13. #include <linux/init.h>
  14. #include <linux/ioctl.h>
  15. #include <linux/fs.h>
  16. #include <asm/compat.h>
  17. #include <asm/sclp_ctl.h>
  18. #include <asm/sclp.h>
  19. #include "sclp.h"
  20. /*
  21. * Supported command words
  22. */
  23. static unsigned int sclp_ctl_sccb_wlist[] = {
  24. 0x00400002,
  25. 0x00410002,
  26. };
  27. /*
  28. * Check if command word is supported
  29. */
  30. static int sclp_ctl_cmdw_supported(unsigned int cmdw)
  31. {
  32. int i;
  33. for (i = 0; i < ARRAY_SIZE(sclp_ctl_sccb_wlist); i++) {
  34. if (cmdw == sclp_ctl_sccb_wlist[i])
  35. return 1;
  36. }
  37. return 0;
  38. }
  39. static void __user *u64_to_uptr(u64 value)
  40. {
  41. if (is_compat_task())
  42. return compat_ptr(value);
  43. else
  44. return (void __user *)(unsigned long)value;
  45. }
  46. /*
  47. * Start SCLP request
  48. */
  49. static int sclp_ctl_ioctl_sccb(void __user *user_area)
  50. {
  51. struct sclp_ctl_sccb ctl_sccb;
  52. struct sccb_header *sccb;
  53. unsigned long copied;
  54. int rc;
  55. if (copy_from_user(&ctl_sccb, user_area, sizeof(ctl_sccb)))
  56. return -EFAULT;
  57. if (!sclp_ctl_cmdw_supported(ctl_sccb.cmdw))
  58. return -EOPNOTSUPP;
  59. sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
  60. if (!sccb)
  61. return -ENOMEM;
  62. copied = PAGE_SIZE -
  63. copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), PAGE_SIZE);
  64. if (offsetof(struct sccb_header, length) +
  65. sizeof(sccb->length) > copied || sccb->length > copied) {
  66. rc = -EFAULT;
  67. goto out_free;
  68. }
  69. if (sccb->length < 8) {
  70. rc = -EINVAL;
  71. goto out_free;
  72. }
  73. rc = sclp_sync_request(ctl_sccb.cmdw, sccb);
  74. if (rc)
  75. goto out_free;
  76. if (copy_to_user(u64_to_uptr(ctl_sccb.sccb), sccb, sccb->length))
  77. rc = -EFAULT;
  78. out_free:
  79. free_page((unsigned long) sccb);
  80. return rc;
  81. }
  82. /*
  83. * SCLP SCCB ioctl function
  84. */
  85. static long sclp_ctl_ioctl(struct file *filp, unsigned int cmd,
  86. unsigned long arg)
  87. {
  88. void __user *argp;
  89. if (is_compat_task())
  90. argp = compat_ptr(arg);
  91. else
  92. argp = (void __user *) arg;
  93. switch (cmd) {
  94. case SCLP_CTL_SCCB:
  95. return sclp_ctl_ioctl_sccb(argp);
  96. default: /* unknown ioctl number */
  97. return -ENOTTY;
  98. }
  99. }
  100. /*
  101. * File operations
  102. */
  103. static const struct file_operations sclp_ctl_fops = {
  104. .owner = THIS_MODULE,
  105. .open = nonseekable_open,
  106. .unlocked_ioctl = sclp_ctl_ioctl,
  107. .compat_ioctl = sclp_ctl_ioctl,
  108. .llseek = no_llseek,
  109. };
  110. /*
  111. * Misc device definition
  112. */
  113. static struct miscdevice sclp_ctl_device = {
  114. .minor = MISC_DYNAMIC_MINOR,
  115. .name = "sclp",
  116. .fops = &sclp_ctl_fops,
  117. };
  118. builtin_misc_device(sclp_ctl_device);