1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * PowerNV system parameter code
4  *
5  * Copyright (C) 2013 IBM
6  */
7 
8 #include <linux/kobject.h>
9 #include <linux/mutex.h>
10 #include <linux/slab.h>
11 #include <linux/of.h>
12 #include <linux/gfp.h>
13 #include <linux/stat.h>
14 #include <asm/opal.h>
15 
16 #define MAX_PARAM_DATA_LEN	64
17 
18 static DEFINE_MUTEX(opal_sysparam_mutex);
19 static struct kobject *sysparam_kobj;
20 static void *param_data_buf;
21 
22 struct param_attr {
23 	struct list_head list;
24 	u32 param_id;
25 	u32 param_size;
26 	struct kobj_attribute kobj_attr;
27 };
28 
opal_get_sys_param(u32 param_id,u32 length,void * buffer)29 static ssize_t opal_get_sys_param(u32 param_id, u32 length, void *buffer)
30 {
31 	struct opal_msg msg;
32 	ssize_t ret;
33 	int token;
34 
35 	token = opal_async_get_token_interruptible();
36 	if (token < 0) {
37 		if (token != -ERESTARTSYS)
38 			pr_err("%s: Couldn't get the token, returning\n",
39 					__func__);
40 		ret = token;
41 		goto out;
42 	}
43 
44 	ret = opal_get_param(token, param_id, (u64)buffer, length);
45 	if (ret != OPAL_ASYNC_COMPLETION) {
46 		ret = opal_error_code(ret);
47 		goto out_token;
48 	}
49 
50 	ret = opal_async_wait_response(token, &msg);
51 	if (ret) {
52 		pr_err("%s: Failed to wait for the async response, %zd\n",
53 				__func__, ret);
54 		goto out_token;
55 	}
56 
57 	ret = opal_error_code(opal_get_async_rc(msg));
58 
59 out_token:
60 	opal_async_release_token(token);
61 out:
62 	return ret;
63 }
64 
opal_set_sys_param(u32 param_id,u32 length,void * buffer)65 static int opal_set_sys_param(u32 param_id, u32 length, void *buffer)
66 {
67 	struct opal_msg msg;
68 	int ret, token;
69 
70 	token = opal_async_get_token_interruptible();
71 	if (token < 0) {
72 		if (token != -ERESTARTSYS)
73 			pr_err("%s: Couldn't get the token, returning\n",
74 					__func__);
75 		ret = token;
76 		goto out;
77 	}
78 
79 	ret = opal_set_param(token, param_id, (u64)buffer, length);
80 
81 	if (ret != OPAL_ASYNC_COMPLETION) {
82 		ret = opal_error_code(ret);
83 		goto out_token;
84 	}
85 
86 	ret = opal_async_wait_response(token, &msg);
87 	if (ret) {
88 		pr_err("%s: Failed to wait for the async response, %d\n",
89 				__func__, ret);
90 		goto out_token;
91 	}
92 
93 	ret = opal_error_code(opal_get_async_rc(msg));
94 
95 out_token:
96 	opal_async_release_token(token);
97 out:
98 	return ret;
99 }
100 
sys_param_show(struct kobject * kobj,struct kobj_attribute * kobj_attr,char * buf)101 static ssize_t sys_param_show(struct kobject *kobj,
102 		struct kobj_attribute *kobj_attr, char *buf)
103 {
104 	struct param_attr *attr = container_of(kobj_attr, struct param_attr,
105 			kobj_attr);
106 	ssize_t ret;
107 
108 	mutex_lock(&opal_sysparam_mutex);
109 	ret = opal_get_sys_param(attr->param_id, attr->param_size,
110 			param_data_buf);
111 	if (ret)
112 		goto out;
113 
114 	memcpy(buf, param_data_buf, attr->param_size);
115 
116 	ret = attr->param_size;
117 out:
118 	mutex_unlock(&opal_sysparam_mutex);
119 	return ret;
120 }
121 
sys_param_store(struct kobject * kobj,struct kobj_attribute * kobj_attr,const char * buf,size_t count)122 static ssize_t sys_param_store(struct kobject *kobj,
123 		struct kobj_attribute *kobj_attr, const char *buf, size_t count)
124 {
125 	struct param_attr *attr = container_of(kobj_attr, struct param_attr,
126 			kobj_attr);
127 	ssize_t ret;
128 
129         /* MAX_PARAM_DATA_LEN is sizeof(param_data_buf) */
130         if (count > MAX_PARAM_DATA_LEN)
131                 count = MAX_PARAM_DATA_LEN;
132 
133 	mutex_lock(&opal_sysparam_mutex);
134 	memcpy(param_data_buf, buf, count);
135 	ret = opal_set_sys_param(attr->param_id, attr->param_size,
136 			param_data_buf);
137 	mutex_unlock(&opal_sysparam_mutex);
138 	if (!ret)
139 		ret = count;
140 	return ret;
141 }
142 
opal_sys_param_init(void)143 void __init opal_sys_param_init(void)
144 {
145 	struct device_node *sysparam;
146 	struct param_attr *attr;
147 	u32 *id, *size;
148 	int count, i;
149 	u8 *perm;
150 
151 	if (!opal_kobj) {
152 		pr_warn("SYSPARAM: opal kobject is not available\n");
153 		goto out;
154 	}
155 
156 	/* Some systems do not use sysparams; this is not an error */
157 	sysparam = of_find_node_by_path("/ibm,opal/sysparams");
158 	if (!sysparam)
159 		goto out;
160 
161 	if (!of_device_is_compatible(sysparam, "ibm,opal-sysparams")) {
162 		pr_err("SYSPARAM: Opal sysparam node not compatible\n");
163 		goto out_node_put;
164 	}
165 
166 	sysparam_kobj = kobject_create_and_add("sysparams", opal_kobj);
167 	if (!sysparam_kobj) {
168 		pr_err("SYSPARAM: Failed to create sysparam kobject\n");
169 		goto out_node_put;
170 	}
171 
172 	/* Allocate big enough buffer for any get/set transactions */
173 	param_data_buf = kzalloc(MAX_PARAM_DATA_LEN, GFP_KERNEL);
174 	if (!param_data_buf) {
175 		pr_err("SYSPARAM: Failed to allocate memory for param data "
176 				"buf\n");
177 		goto out_kobj_put;
178 	}
179 
180 	/* Number of parameters exposed through DT */
181 	count = of_property_count_strings(sysparam, "param-name");
182 	if (count < 0) {
183 		pr_err("SYSPARAM: No string found of property param-name in "
184 				"the node %pOFn\n", sysparam);
185 		goto out_param_buf;
186 	}
187 
188 	id = kcalloc(count, sizeof(*id), GFP_KERNEL);
189 	if (!id) {
190 		pr_err("SYSPARAM: Failed to allocate memory to read parameter "
191 				"id\n");
192 		goto out_param_buf;
193 	}
194 
195 	size = kcalloc(count, sizeof(*size), GFP_KERNEL);
196 	if (!size) {
197 		pr_err("SYSPARAM: Failed to allocate memory to read parameter "
198 				"size\n");
199 		goto out_free_id;
200 	}
201 
202 	perm = kcalloc(count, sizeof(*perm), GFP_KERNEL);
203 	if (!perm) {
204 		pr_err("SYSPARAM: Failed to allocate memory to read supported "
205 				"action on the parameter");
206 		goto out_free_size;
207 	}
208 
209 	if (of_property_read_u32_array(sysparam, "param-id", id, count)) {
210 		pr_err("SYSPARAM: Missing property param-id in the DT\n");
211 		goto out_free_perm;
212 	}
213 
214 	if (of_property_read_u32_array(sysparam, "param-len", size, count)) {
215 		pr_err("SYSPARAM: Missing property param-len in the DT\n");
216 		goto out_free_perm;
217 	}
218 
219 
220 	if (of_property_read_u8_array(sysparam, "param-perm", perm, count)) {
221 		pr_err("SYSPARAM: Missing property param-perm in the DT\n");
222 		goto out_free_perm;
223 	}
224 
225 	attr = kcalloc(count, sizeof(*attr), GFP_KERNEL);
226 	if (!attr) {
227 		pr_err("SYSPARAM: Failed to allocate memory for parameter "
228 				"attributes\n");
229 		goto out_free_perm;
230 	}
231 
232 	/* For each of the parameters, populate the parameter attributes */
233 	for (i = 0; i < count; i++) {
234 		if (size[i] > MAX_PARAM_DATA_LEN) {
235 			pr_warn("SYSPARAM: Not creating parameter %d as size "
236 				"exceeds buffer length\n", i);
237 			continue;
238 		}
239 
240 		sysfs_attr_init(&attr[i].kobj_attr.attr);
241 		attr[i].param_id = id[i];
242 		attr[i].param_size = size[i];
243 		if (of_property_read_string_index(sysparam, "param-name", i,
244 				&attr[i].kobj_attr.attr.name))
245 			continue;
246 
247 		/* If the parameter is read-only or read-write */
248 		switch (perm[i] & 3) {
249 		case OPAL_SYSPARAM_READ:
250 			attr[i].kobj_attr.attr.mode = 0444;
251 			break;
252 		case OPAL_SYSPARAM_WRITE:
253 			attr[i].kobj_attr.attr.mode = 0200;
254 			break;
255 		case OPAL_SYSPARAM_RW:
256 			attr[i].kobj_attr.attr.mode = 0644;
257 			break;
258 		default:
259 			break;
260 		}
261 
262 		attr[i].kobj_attr.show = sys_param_show;
263 		attr[i].kobj_attr.store = sys_param_store;
264 
265 		if (sysfs_create_file(sysparam_kobj, &attr[i].kobj_attr.attr)) {
266 			pr_err("SYSPARAM: Failed to create sysfs file %s\n",
267 					attr[i].kobj_attr.attr.name);
268 			goto out_free_attr;
269 		}
270 	}
271 
272 	kfree(perm);
273 	kfree(size);
274 	kfree(id);
275 	of_node_put(sysparam);
276 	return;
277 
278 out_free_attr:
279 	kfree(attr);
280 out_free_perm:
281 	kfree(perm);
282 out_free_size:
283 	kfree(size);
284 out_free_id:
285 	kfree(id);
286 out_param_buf:
287 	kfree(param_data_buf);
288 out_kobj_put:
289 	kobject_put(sysparam_kobj);
290 out_node_put:
291 	of_node_put(sysparam);
292 out:
293 	return;
294 }
295