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