145 lines
		
	
	
	
		
			2.8 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			145 lines
		
	
	
	
		
			2.8 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * 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; | ||
|  | 	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; | ||
|  | 	if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sizeof(*sccb))) { | ||
|  | 		rc = -EFAULT; | ||
|  | 		goto out_free; | ||
|  | 	} | ||
|  | 	if (sccb->length > PAGE_SIZE || sccb->length < 8) | ||
|  | 		return -EINVAL; | ||
|  | 	if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sccb->length)) { | ||
|  | 		rc = -EFAULT; | ||
|  | 		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); |