142 lines
		
	
	
	
		
			3.1 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			142 lines
		
	
	
	
		
			3.1 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  *    Hypervisor filesystem for Linux on s390. | ||
|  |  *    Set Partition-Resource Parameter interface. | ||
|  |  * | ||
|  |  *    Copyright IBM Corp. 2013 | ||
|  |  *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> | ||
|  |  */ | ||
|  | 
 | ||
|  | #include <linux/compat.h>
 | ||
|  | #include <linux/errno.h>
 | ||
|  | #include <linux/gfp.h>
 | ||
|  | #include <linux/string.h>
 | ||
|  | #include <linux/types.h>
 | ||
|  | #include <linux/uaccess.h>
 | ||
|  | #include <asm/compat.h>
 | ||
|  | #include <asm/sclp.h>
 | ||
|  | #include "hypfs.h"
 | ||
|  | 
 | ||
|  | #define DIAG304_SET_WEIGHTS	0
 | ||
|  | #define DIAG304_QUERY_PRP	1
 | ||
|  | #define DIAG304_SET_CAPPING	2
 | ||
|  | 
 | ||
|  | #define DIAG304_CMD_MAX		2
 | ||
|  | 
 | ||
|  | static unsigned long hypfs_sprp_diag304(void *data, unsigned long cmd) | ||
|  | { | ||
|  | 	register unsigned long _data asm("2") = (unsigned long) data; | ||
|  | 	register unsigned long _rc asm("3"); | ||
|  | 	register unsigned long _cmd asm("4") = cmd; | ||
|  | 
 | ||
|  | 	asm volatile("diag %1,%2,0x304\n" | ||
|  | 		     : "=d" (_rc) : "d" (_data), "d" (_cmd) : "memory"); | ||
|  | 
 | ||
|  | 	return _rc; | ||
|  | } | ||
|  | 
 | ||
|  | static void hypfs_sprp_free(const void *data) | ||
|  | { | ||
|  | 	free_page((unsigned long) data); | ||
|  | } | ||
|  | 
 | ||
|  | static int hypfs_sprp_create(void **data_ptr, void **free_ptr, size_t *size) | ||
|  | { | ||
|  | 	unsigned long rc; | ||
|  | 	void *data; | ||
|  | 
 | ||
|  | 	data = (void *) get_zeroed_page(GFP_KERNEL); | ||
|  | 	if (!data) | ||
|  | 		return -ENOMEM; | ||
|  | 	rc = hypfs_sprp_diag304(data, DIAG304_QUERY_PRP); | ||
|  | 	if (rc != 1) { | ||
|  | 		*data_ptr = *free_ptr = NULL; | ||
|  | 		*size = 0; | ||
|  | 		free_page((unsigned long) data); | ||
|  | 		return -EIO; | ||
|  | 	} | ||
|  | 	*data_ptr = *free_ptr = data; | ||
|  | 	*size = PAGE_SIZE; | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 | ||
|  | static int __hypfs_sprp_ioctl(void __user *user_area) | ||
|  | { | ||
|  | 	struct hypfs_diag304 diag304; | ||
|  | 	unsigned long cmd; | ||
|  | 	void __user *udata; | ||
|  | 	void *data; | ||
|  | 	int rc; | ||
|  | 
 | ||
|  | 	if (copy_from_user(&diag304, user_area, sizeof(diag304))) | ||
|  | 		return -EFAULT; | ||
|  | 	if ((diag304.args[0] >> 8) != 0 || diag304.args[1] > DIAG304_CMD_MAX) | ||
|  | 		return -EINVAL; | ||
|  | 
 | ||
|  | 	data = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
|  | 	if (!data) | ||
|  | 		return -ENOMEM; | ||
|  | 
 | ||
|  | 	udata = (void __user *)(unsigned long) diag304.data; | ||
|  | 	if (diag304.args[1] == DIAG304_SET_WEIGHTS || | ||
|  | 	    diag304.args[1] == DIAG304_SET_CAPPING) | ||
|  | 		if (copy_from_user(data, udata, PAGE_SIZE)) { | ||
|  | 			rc = -EFAULT; | ||
|  | 			goto out; | ||
|  | 		} | ||
|  | 
 | ||
|  | 	cmd = *(unsigned long *) &diag304.args[0]; | ||
|  | 	diag304.rc = hypfs_sprp_diag304(data, cmd); | ||
|  | 
 | ||
|  | 	if (diag304.args[1] == DIAG304_QUERY_PRP) | ||
|  | 		if (copy_to_user(udata, data, PAGE_SIZE)) { | ||
|  | 			rc = -EFAULT; | ||
|  | 			goto out; | ||
|  | 		} | ||
|  | 
 | ||
|  | 	rc = copy_to_user(user_area, &diag304, sizeof(diag304)) ? -EFAULT : 0; | ||
|  | out: | ||
|  | 	free_page((unsigned long) data); | ||
|  | 	return rc; | ||
|  | } | ||
|  | 
 | ||
|  | static long hypfs_sprp_ioctl(struct file *file, unsigned int cmd, | ||
|  | 			       unsigned long arg) | ||
|  | { | ||
|  | 	void __user *argp; | ||
|  | 
 | ||
|  | 	if (!capable(CAP_SYS_ADMIN)) | ||
|  | 		return -EACCES; | ||
|  | 	if (is_compat_task()) | ||
|  | 		argp = compat_ptr(arg); | ||
|  | 	else | ||
|  | 		argp = (void __user *) arg; | ||
|  | 	switch (cmd) { | ||
|  | 	case HYPFS_DIAG304: | ||
|  | 		return __hypfs_sprp_ioctl(argp); | ||
|  | 	default: /* unknown ioctl number */ | ||
|  | 		return -ENOTTY; | ||
|  | 	} | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 | ||
|  | static struct hypfs_dbfs_file hypfs_sprp_file = { | ||
|  | 	.name		= "diag_304", | ||
|  | 	.data_create	= hypfs_sprp_create, | ||
|  | 	.data_free	= hypfs_sprp_free, | ||
|  | 	.unlocked_ioctl = hypfs_sprp_ioctl, | ||
|  | }; | ||
|  | 
 | ||
|  | int hypfs_sprp_init(void) | ||
|  | { | ||
|  | 	if (!sclp_has_sprp()) | ||
|  | 		return 0; | ||
|  | 	return hypfs_dbfs_create_file(&hypfs_sprp_file); | ||
|  | } | ||
|  | 
 | ||
|  | void hypfs_sprp_exit(void) | ||
|  | { | ||
|  | 	if (!sclp_has_sprp()) | ||
|  | 		return; | ||
|  | 	hypfs_dbfs_remove_file(&hypfs_sprp_file); | ||
|  | } |