sclp_ctl.c 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /*
  2. * IOCTL interface for SCLP
  3. *
  4. * Copyright IBM Corp. 2012
  5. *
  6. * Author: Michael Holzheu <holzheu@linux.vnet.ibm.com>
  7. */
  8. #include <linux/compat.h>
  9. #include <linux/uaccess.h>
  10. #include <linux/miscdevice.h>
  11. #include <linux/gfp.h>
  12. #include <linux/module.h>
  13. #include <linux/ioctl.h>
  14. #include <linux/fs.h>
  15. #include <linux/compat.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. int rc;
  54. if (copy_from_user(&ctl_sccb, user_area, sizeof(ctl_sccb)))
  55. return -EFAULT;
  56. if (!sclp_ctl_cmdw_supported(ctl_sccb.cmdw))
  57. return -EOPNOTSUPP;
  58. sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
  59. if (!sccb)
  60. return -ENOMEM;
  61. if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sizeof(*sccb))) {
  62. rc = -EFAULT;
  63. goto out_free;
  64. }
  65. if (sccb->length > PAGE_SIZE || sccb->length < 8)
  66. return -EINVAL;
  67. if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sccb->length)) {
  68. rc = -EFAULT;
  69. goto out_free;
  70. }
  71. rc = sclp_sync_request(ctl_sccb.cmdw, sccb);
  72. if (rc)
  73. goto out_free;
  74. if (copy_to_user(u64_to_uptr(ctl_sccb.sccb), sccb, sccb->length))
  75. rc = -EFAULT;
  76. out_free:
  77. free_page((unsigned long) sccb);
  78. return rc;
  79. }
  80. /*
  81. * SCLP SCCB ioctl function
  82. */
  83. static long sclp_ctl_ioctl(struct file *filp, unsigned int cmd,
  84. unsigned long arg)
  85. {
  86. void __user *argp;
  87. if (is_compat_task())
  88. argp = compat_ptr(arg);
  89. else
  90. argp = (void __user *) arg;
  91. switch (cmd) {
  92. case SCLP_CTL_SCCB:
  93. return sclp_ctl_ioctl_sccb(argp);
  94. default: /* unknown ioctl number */
  95. return -ENOTTY;
  96. }
  97. }
  98. /*
  99. * File operations
  100. */
  101. static const struct file_operations sclp_ctl_fops = {
  102. .owner = THIS_MODULE,
  103. .open = nonseekable_open,
  104. .unlocked_ioctl = sclp_ctl_ioctl,
  105. .compat_ioctl = sclp_ctl_ioctl,
  106. .llseek = no_llseek,
  107. };
  108. /*
  109. * Misc device definition
  110. */
  111. static struct miscdevice sclp_ctl_device = {
  112. .minor = MISC_DYNAMIC_MINOR,
  113. .name = "sclp",
  114. .fops = &sclp_ctl_fops,
  115. };
  116. /*
  117. * Register sclp_ctl misc device
  118. */
  119. static int __init sclp_ctl_init(void)
  120. {
  121. return misc_register(&sclp_ctl_device);
  122. }
  123. module_init(sclp_ctl_init);
  124. /*
  125. * Deregister sclp_ctl misc device
  126. */
  127. static void __exit sclp_ctl_exit(void)
  128. {
  129. misc_deregister(&sclp_ctl_device);
  130. }
  131. module_exit(sclp_ctl_exit);