xref: /linux/arch/s390/hypfs/hypfs_sprp.c (revision 7fc2cd2e4b398c57c9cf961cfea05eadbf34c05c)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *    Hypervisor filesystem for Linux on s390.
4  *    Set Partition-Resource Parameter interface.
5  *
6  *    Copyright IBM Corp. 2013
7  *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
8  */
9 
10 #include <linux/errno.h>
11 #include <linux/gfp.h>
12 #include <linux/string.h>
13 #include <linux/types.h>
14 #include <linux/uaccess.h>
15 #include <asm/diag.h>
16 #include <asm/sclp.h>
17 #include "hypfs.h"
18 
19 #define DIAG304_SET_WEIGHTS	0
20 #define DIAG304_QUERY_PRP	1
21 #define DIAG304_SET_CAPPING	2
22 
23 #define DIAG304_CMD_MAX		2
24 
25 static inline unsigned long __hypfs_sprp_diag304(void *data, unsigned long cmd)
26 {
27 	union register_pair r1 = { .even = virt_to_phys(data), };
28 
29 	asm volatile("diag %[r1],%[r3],0x304"
30 		     : [r1] "+&d" (r1.pair)
31 		     : [r3] "d" (cmd)
32 		     : "memory");
33 	return r1.odd;
34 }
35 
36 static unsigned long hypfs_sprp_diag304(void *data, unsigned long cmd)
37 {
38 	diag_stat_inc(DIAG_STAT_X304);
39 	return __hypfs_sprp_diag304(data, cmd);
40 }
41 
42 static void hypfs_sprp_free(const void *data)
43 {
44 	free_page((unsigned long) data);
45 }
46 
47 static int hypfs_sprp_create(void **data_ptr, void **free_ptr, size_t *size)
48 {
49 	unsigned long rc;
50 	void *data;
51 
52 	data = (void *) get_zeroed_page(GFP_KERNEL);
53 	if (!data)
54 		return -ENOMEM;
55 	rc = hypfs_sprp_diag304(data, DIAG304_QUERY_PRP);
56 	if (rc != 1) {
57 		*data_ptr = *free_ptr = NULL;
58 		*size = 0;
59 		free_page((unsigned long) data);
60 		return -EIO;
61 	}
62 	*data_ptr = *free_ptr = data;
63 	*size = PAGE_SIZE;
64 	return 0;
65 }
66 
67 static int __hypfs_sprp_ioctl(void __user *user_area)
68 {
69 	struct hypfs_diag304 *diag304;
70 	unsigned long cmd;
71 	void __user *udata;
72 	void *data;
73 	int rc;
74 
75 	rc = -ENOMEM;
76 	data = (void *)get_zeroed_page(GFP_KERNEL);
77 	diag304 = kzalloc(sizeof(*diag304), GFP_KERNEL);
78 	if (!data || !diag304)
79 		goto out;
80 
81 	rc = -EFAULT;
82 	if (copy_from_user(diag304, user_area, sizeof(*diag304)))
83 		goto out;
84 	rc = -EINVAL;
85 	if ((diag304->args[0] >> 8) != 0 || diag304->args[1] > DIAG304_CMD_MAX)
86 		goto out;
87 
88 	rc = -EFAULT;
89 	udata = (void __user *)(unsigned long) diag304->data;
90 	if (diag304->args[1] == DIAG304_SET_WEIGHTS ||
91 	    diag304->args[1] == DIAG304_SET_CAPPING)
92 		if (copy_from_user(data, udata, PAGE_SIZE))
93 			goto out;
94 
95 	cmd = *(unsigned long *) &diag304->args[0];
96 	diag304->rc = hypfs_sprp_diag304(data, cmd);
97 
98 	if (diag304->args[1] == DIAG304_QUERY_PRP)
99 		if (copy_to_user(udata, data, PAGE_SIZE)) {
100 			rc = -EFAULT;
101 			goto out;
102 		}
103 
104 	rc = copy_to_user(user_area, diag304, sizeof(*diag304)) ? -EFAULT : 0;
105 out:
106 	kfree(diag304);
107 	free_page((unsigned long) data);
108 	return rc;
109 }
110 
111 static long hypfs_sprp_ioctl(struct file *file, unsigned int cmd,
112 			       unsigned long arg)
113 {
114 	void __user *argp;
115 
116 	if (!capable(CAP_SYS_ADMIN))
117 		return -EACCES;
118 	argp = (void __user *)arg;
119 	switch (cmd) {
120 	case HYPFS_DIAG304:
121 		return __hypfs_sprp_ioctl(argp);
122 	default: /* unknown ioctl number */
123 		return -ENOTTY;
124 	}
125 	return 0;
126 }
127 
128 static struct hypfs_dbfs_file hypfs_sprp_file = {
129 	.name		= "diag_304",
130 	.data_create	= hypfs_sprp_create,
131 	.data_free	= hypfs_sprp_free,
132 	.unlocked_ioctl = hypfs_sprp_ioctl,
133 };
134 
135 void hypfs_sprp_init(void)
136 {
137 	if (!sclp.has_sprp)
138 		return;
139 	hypfs_dbfs_create_file(&hypfs_sprp_file);
140 }
141 
142 void hypfs_sprp_exit(void)
143 {
144 	if (!sclp.has_sprp)
145 		return;
146 	hypfs_dbfs_remove_file(&hypfs_sprp_file);
147 }
148