123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- /*
- * IOCTL interface for SCLP
- *
- * Copyright IBM Corp. 2012
- *
- * Author: Michael Holzheu <holzheu@linux.vnet.ibm.com>
- */
- #include <linux/compat.h>
- #include <linux/uaccess.h>
- #include <linux/miscdevice.h>
- #include <linux/gfp.h>
- #include <linux/module.h>
- #include <linux/ioctl.h>
- #include <linux/fs.h>
- #include <asm/compat.h>
- #include <asm/sclp_ctl.h>
- #include <asm/sclp.h>
- #include "sclp.h"
- /*
- * Supported command words
- */
- static unsigned int sclp_ctl_sccb_wlist[] = {
- 0x00400002,
- 0x00410002,
- };
- /*
- * Check if command word is supported
- */
- static int sclp_ctl_cmdw_supported(unsigned int cmdw)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(sclp_ctl_sccb_wlist); i++) {
- if (cmdw == sclp_ctl_sccb_wlist[i])
- return 1;
- }
- return 0;
- }
- static void __user *u64_to_uptr(u64 value)
- {
- if (is_compat_task())
- return compat_ptr(value);
- else
- return (void __user *)(unsigned long)value;
- }
- /*
- * Start SCLP request
- */
- static int sclp_ctl_ioctl_sccb(void __user *user_area)
- {
- struct sclp_ctl_sccb ctl_sccb;
- struct sccb_header *sccb;
- unsigned long copied;
- int rc;
- if (copy_from_user(&ctl_sccb, user_area, sizeof(ctl_sccb)))
- return -EFAULT;
- if (!sclp_ctl_cmdw_supported(ctl_sccb.cmdw))
- return -EOPNOTSUPP;
- sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!sccb)
- return -ENOMEM;
- copied = PAGE_SIZE -
- copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), PAGE_SIZE);
- if (offsetof(struct sccb_header, length) +
- sizeof(sccb->length) > copied || sccb->length > copied) {
- rc = -EFAULT;
- goto out_free;
- }
- if (sccb->length < 8) {
- rc = -EINVAL;
- goto out_free;
- }
- rc = sclp_sync_request(ctl_sccb.cmdw, sccb);
- if (rc)
- goto out_free;
- if (copy_to_user(u64_to_uptr(ctl_sccb.sccb), sccb, sccb->length))
- rc = -EFAULT;
- out_free:
- free_page((unsigned long) sccb);
- return rc;
- }
- /*
- * SCLP SCCB ioctl function
- */
- static long sclp_ctl_ioctl(struct file *filp, unsigned int cmd,
- unsigned long arg)
- {
- void __user *argp;
- if (is_compat_task())
- argp = compat_ptr(arg);
- else
- argp = (void __user *) arg;
- switch (cmd) {
- case SCLP_CTL_SCCB:
- return sclp_ctl_ioctl_sccb(argp);
- default: /* unknown ioctl number */
- return -ENOTTY;
- }
- }
- /*
- * File operations
- */
- static const struct file_operations sclp_ctl_fops = {
- .owner = THIS_MODULE,
- .open = nonseekable_open,
- .unlocked_ioctl = sclp_ctl_ioctl,
- .compat_ioctl = sclp_ctl_ioctl,
- .llseek = no_llseek,
- };
- /*
- * Misc device definition
- */
- static struct miscdevice sclp_ctl_device = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "sclp",
- .fops = &sclp_ctl_fops,
- };
- /*
- * Register sclp_ctl misc device
- */
- static int __init sclp_ctl_init(void)
- {
- return misc_register(&sclp_ctl_device);
- }
- module_init(sclp_ctl_init);
- /*
- * Deregister sclp_ctl misc device
- */
- static void __exit sclp_ctl_exit(void)
- {
- misc_deregister(&sclp_ctl_device);
- }
- module_exit(sclp_ctl_exit);
|