xref: /linux/arch/powerpc/platforms/pseries/reconfig.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * pSeries_reconfig.c - support for dynamic reconfiguration (including PCI
4  * Hotplug and Dynamic Logical Partitioning on RPA platforms).
5  *
6  * Copyright (C) 2005 Nathan Lynch
7  * Copyright (C) 2005 IBM Corporation
8  */
9 
10 #include <linux/kernel.h>
11 #include <linux/notifier.h>
12 #include <linux/proc_fs.h>
13 #include <linux/security.h>
14 #include <linux/slab.h>
15 #include <linux/of.h>
16 
17 #include <asm/machdep.h>
18 #include <linux/uaccess.h>
19 #include <asm/mmu.h>
20 
21 #include "of_helpers.h"
22 
pSeries_reconfig_add_node(const char * path,struct property * proplist)23 static int pSeries_reconfig_add_node(const char *path, struct property *proplist)
24 {
25 	struct device_node *np;
26 	int err = -ENOMEM;
27 
28 	np = kzalloc(sizeof(*np), GFP_KERNEL);
29 	if (!np)
30 		goto out_err;
31 
32 	np->full_name = kstrdup(kbasename(path), GFP_KERNEL);
33 	if (!np->full_name)
34 		goto out_err;
35 
36 	np->properties = proplist;
37 	of_node_set_flag(np, OF_DYNAMIC);
38 	of_node_init(np);
39 
40 	np->parent = pseries_of_derive_parent(path);
41 	if (IS_ERR(np->parent)) {
42 		err = PTR_ERR(np->parent);
43 		goto out_err;
44 	}
45 
46 	err = of_attach_node(np);
47 	if (err) {
48 		printk(KERN_ERR "Failed to add device node %s\n", path);
49 		goto out_err;
50 	}
51 
52 	of_node_put(np->parent);
53 
54 	return 0;
55 
56 out_err:
57 	if (np) {
58 		of_node_put(np->parent);
59 		kfree(np->full_name);
60 		kfree(np);
61 	}
62 	return err;
63 }
64 
pSeries_reconfig_remove_node(struct device_node * np)65 static int pSeries_reconfig_remove_node(struct device_node *np)
66 {
67 	struct device_node *parent, *child;
68 
69 	parent = of_get_parent(np);
70 	if (!parent)
71 		return -EINVAL;
72 
73 	if ((child = of_get_next_child(np, NULL))) {
74 		of_node_put(child);
75 		of_node_put(parent);
76 		return -EBUSY;
77 	}
78 
79 	of_detach_node(np);
80 	of_node_put(parent);
81 	return 0;
82 }
83 
84 /*
85  * /proc/powerpc/ofdt - yucky binary interface for adding and removing
86  * OF device nodes.  Should be deprecated as soon as we get an
87  * in-kernel wrapper for the RTAS ibm,configure-connector call.
88  */
89 
release_prop_list(const struct property * prop)90 static void release_prop_list(const struct property *prop)
91 {
92 	struct property *next;
93 	for (; prop; prop = next) {
94 		next = prop->next;
95 		kfree(prop->name);
96 		kfree(prop->value);
97 		kfree(prop);
98 	}
99 
100 }
101 
102 /**
103  * parse_next_property - process the next property from raw input buffer
104  * @buf: input buffer, must be nul-terminated
105  * @end: end of the input buffer + 1, for validation
106  * @name: return value; set to property name in buf
107  * @length: return value; set to length of value
108  * @value: return value; set to the property value in buf
109  *
110  * Note that the caller must make copies of the name and value returned,
111  * this function does no allocation or copying of the data.  Return value
112  * is set to the next name in buf, or NULL on error.
113  */
parse_next_property(char * buf,char * end,char ** name,int * length,unsigned char ** value)114 static char * parse_next_property(char *buf, char *end, char **name, int *length,
115 				  unsigned char **value)
116 {
117 	char *tmp;
118 
119 	*name = buf;
120 
121 	tmp = strchr(buf, ' ');
122 	if (!tmp) {
123 		printk(KERN_ERR "property parse failed in %s at line %d\n",
124 		       __func__, __LINE__);
125 		return NULL;
126 	}
127 	*tmp = '\0';
128 
129 	if (++tmp >= end) {
130 		printk(KERN_ERR "property parse failed in %s at line %d\n",
131 		       __func__, __LINE__);
132 		return NULL;
133 	}
134 
135 	/* now we're on the length */
136 	*length = -1;
137 	*length = simple_strtoul(tmp, &tmp, 10);
138 	if (*length == -1) {
139 		printk(KERN_ERR "property parse failed in %s at line %d\n",
140 		       __func__, __LINE__);
141 		return NULL;
142 	}
143 	if (*tmp != ' ' || ++tmp >= end) {
144 		printk(KERN_ERR "property parse failed in %s at line %d\n",
145 		       __func__, __LINE__);
146 		return NULL;
147 	}
148 
149 	/* now we're on the value */
150 	*value = tmp;
151 	tmp += *length;
152 	if (tmp > end) {
153 		printk(KERN_ERR "property parse failed in %s at line %d\n",
154 		       __func__, __LINE__);
155 		return NULL;
156 	}
157 	else if (tmp < end && *tmp != ' ' && *tmp != '\0') {
158 		printk(KERN_ERR "property parse failed in %s at line %d\n",
159 		       __func__, __LINE__);
160 		return NULL;
161 	}
162 	tmp++;
163 
164 	/* and now we should be on the next name, or the end */
165 	return tmp;
166 }
167 
new_property(const char * name,const int length,const unsigned char * value,struct property * last)168 static struct property *new_property(const char *name, const int length,
169 				     const unsigned char *value, struct property *last)
170 {
171 	struct property *new = kzalloc(sizeof(*new), GFP_KERNEL);
172 
173 	if (!new)
174 		return NULL;
175 
176 	if (!(new->name = kstrdup(name, GFP_KERNEL)))
177 		goto cleanup;
178 	if (!(new->value = kmalloc(length + 1, GFP_KERNEL)))
179 		goto cleanup;
180 
181 	memcpy(new->value, value, length);
182 	*(((char *)new->value) + length) = 0;
183 	new->length = length;
184 	new->next = last;
185 	return new;
186 
187 cleanup:
188 	kfree(new->name);
189 	kfree(new->value);
190 	kfree(new);
191 	return NULL;
192 }
193 
do_add_node(char * buf,size_t bufsize)194 static int do_add_node(char *buf, size_t bufsize)
195 {
196 	char *path, *end, *name;
197 	struct device_node *np;
198 	struct property *prop = NULL;
199 	unsigned char* value;
200 	int length, rv = 0;
201 
202 	end = buf + bufsize;
203 	path = buf;
204 	buf = strchr(buf, ' ');
205 	if (!buf)
206 		return -EINVAL;
207 	*buf = '\0';
208 	buf++;
209 
210 	if ((np = of_find_node_by_path(path))) {
211 		of_node_put(np);
212 		return -EINVAL;
213 	}
214 
215 	/* rv = build_prop_list(tmp, bufsize - (tmp - buf), &proplist); */
216 	while (buf < end &&
217 	       (buf = parse_next_property(buf, end, &name, &length, &value))) {
218 		struct property *last = prop;
219 
220 		prop = new_property(name, length, value, last);
221 		if (!prop) {
222 			rv = -ENOMEM;
223 			prop = last;
224 			goto out;
225 		}
226 	}
227 	if (!buf) {
228 		rv = -EINVAL;
229 		goto out;
230 	}
231 
232 	rv = pSeries_reconfig_add_node(path, prop);
233 
234 out:
235 	if (rv)
236 		release_prop_list(prop);
237 	return rv;
238 }
239 
do_remove_node(char * buf)240 static int do_remove_node(char *buf)
241 {
242 	struct device_node *node;
243 	int rv = -ENODEV;
244 
245 	if ((node = of_find_node_by_path(buf)))
246 		rv = pSeries_reconfig_remove_node(node);
247 
248 	of_node_put(node);
249 	return rv;
250 }
251 
parse_node(char * buf,size_t bufsize,struct device_node ** npp)252 static char *parse_node(char *buf, size_t bufsize, struct device_node **npp)
253 {
254 	char *handle_str;
255 	phandle handle;
256 	*npp = NULL;
257 
258 	handle_str = buf;
259 
260 	buf = strchr(buf, ' ');
261 	if (!buf)
262 		return NULL;
263 	*buf = '\0';
264 	buf++;
265 
266 	handle = simple_strtoul(handle_str, NULL, 0);
267 
268 	*npp = of_find_node_by_phandle(handle);
269 	return buf;
270 }
271 
do_add_property(char * buf,size_t bufsize)272 static int do_add_property(char *buf, size_t bufsize)
273 {
274 	struct property *prop = NULL;
275 	struct device_node *np;
276 	unsigned char *value;
277 	char *name, *end;
278 	int length;
279 	end = buf + bufsize;
280 	buf = parse_node(buf, bufsize, &np);
281 
282 	if (!np)
283 		return -ENODEV;
284 
285 	if (parse_next_property(buf, end, &name, &length, &value) == NULL)
286 		return -EINVAL;
287 
288 	prop = new_property(name, length, value, NULL);
289 	if (!prop)
290 		return -ENOMEM;
291 
292 	of_add_property(np, prop);
293 
294 	return 0;
295 }
296 
do_remove_property(char * buf,size_t bufsize)297 static int do_remove_property(char *buf, size_t bufsize)
298 {
299 	struct device_node *np;
300 	char *tmp;
301 	buf = parse_node(buf, bufsize, &np);
302 
303 	if (!np)
304 		return -ENODEV;
305 
306 	tmp = strchr(buf,' ');
307 	if (tmp)
308 		*tmp = '\0';
309 
310 	if (strlen(buf) == 0)
311 		return -EINVAL;
312 
313 	return of_remove_property(np, of_find_property(np, buf, NULL));
314 }
315 
do_update_property(char * buf,size_t bufsize)316 static int do_update_property(char *buf, size_t bufsize)
317 {
318 	struct device_node *np;
319 	unsigned char *value;
320 	char *name, *end, *next_prop;
321 	int length;
322 	struct property *newprop;
323 	buf = parse_node(buf, bufsize, &np);
324 	end = buf + bufsize;
325 
326 	if (!np)
327 		return -ENODEV;
328 
329 	next_prop = parse_next_property(buf, end, &name, &length, &value);
330 	if (!next_prop)
331 		return -EINVAL;
332 
333 	if (!strlen(name))
334 		return -ENODEV;
335 
336 	newprop = new_property(name, length, value, NULL);
337 	if (!newprop)
338 		return -ENOMEM;
339 
340 	if (!strcmp(name, "slb-size") || !strcmp(name, "ibm,slb-size"))
341 		slb_set_size(*(int *)value);
342 
343 	return of_update_property(np, newprop);
344 }
345 
346 /**
347  * ofdt_write - perform operations on the Open Firmware device tree
348  *
349  * @file: not used
350  * @buf: command and arguments
351  * @count: size of the command buffer
352  * @off: not used
353  *
354  * Operations supported at this time are addition and removal of
355  * whole nodes along with their properties.  Operations on individual
356  * properties are not implemented (yet).
357  */
ofdt_write(struct file * file,const char __user * buf,size_t count,loff_t * off)358 static ssize_t ofdt_write(struct file *file, const char __user *buf, size_t count,
359 			  loff_t *off)
360 {
361 	int rv;
362 	char *kbuf;
363 	char *tmp;
364 
365 	rv = security_locked_down(LOCKDOWN_DEVICE_TREE);
366 	if (rv)
367 		return rv;
368 
369 	kbuf = memdup_user_nul(buf, count);
370 	if (IS_ERR(kbuf))
371 		return PTR_ERR(kbuf);
372 
373 	tmp = strchr(kbuf, ' ');
374 	if (!tmp) {
375 		rv = -EINVAL;
376 		goto out;
377 	}
378 	*tmp = '\0';
379 	tmp++;
380 
381 	if (!strcmp(kbuf, "add_node"))
382 		rv = do_add_node(tmp, count - (tmp - kbuf));
383 	else if (!strcmp(kbuf, "remove_node"))
384 		rv = do_remove_node(tmp);
385 	else if (!strcmp(kbuf, "add_property"))
386 		rv = do_add_property(tmp, count - (tmp - kbuf));
387 	else if (!strcmp(kbuf, "remove_property"))
388 		rv = do_remove_property(tmp, count - (tmp - kbuf));
389 	else if (!strcmp(kbuf, "update_property"))
390 		rv = do_update_property(tmp, count - (tmp - kbuf));
391 	else
392 		rv = -EINVAL;
393 out:
394 	kfree(kbuf);
395 	return rv ? rv : count;
396 }
397 
398 static const struct proc_ops ofdt_proc_ops = {
399 	.proc_write	= ofdt_write,
400 	.proc_lseek	= noop_llseek,
401 };
402 
403 /* create /proc/powerpc/ofdt write-only by root */
proc_ppc64_create_ofdt(void)404 static int proc_ppc64_create_ofdt(void)
405 {
406 	struct proc_dir_entry *ent;
407 
408 	ent = proc_create("powerpc/ofdt", 0200, NULL, &ofdt_proc_ops);
409 	if (ent)
410 		proc_set_size(ent, 0);
411 
412 	return 0;
413 }
414 machine_device_initcall(pseries, proc_ppc64_create_ofdt);
415