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/prom.h>
17 #include <asm/machdep.h>
18 #include <linux/uaccess.h>
19 #include <asm/mmu.h>
20 
21 #include "of_helpers.h"
22 
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 
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 
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  */
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 
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 
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 
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 
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 
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 
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 
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  */
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 	kbuf = memdup_user_nul(buf, count);
366 	if (IS_ERR(kbuf))
367 		return PTR_ERR(kbuf);
368 
369 	tmp = strchr(kbuf, ' ');
370 	if (!tmp) {
371 		rv = -EINVAL;
372 		goto out;
373 	}
374 	*tmp = '\0';
375 	tmp++;
376 
377 	if (!strcmp(kbuf, "add_node"))
378 		rv = do_add_node(tmp, count - (tmp - kbuf));
379 	else if (!strcmp(kbuf, "remove_node"))
380 		rv = do_remove_node(tmp);
381 	else if (!strcmp(kbuf, "add_property"))
382 		rv = do_add_property(tmp, count - (tmp - kbuf));
383 	else if (!strcmp(kbuf, "remove_property"))
384 		rv = do_remove_property(tmp, count - (tmp - kbuf));
385 	else if (!strcmp(kbuf, "update_property"))
386 		rv = do_update_property(tmp, count - (tmp - kbuf));
387 	else
388 		rv = -EINVAL;
389 out:
390 	kfree(kbuf);
391 	return rv ? rv : count;
392 }
393 
394 static const struct file_operations ofdt_fops = {
395 	.write = ofdt_write,
396 	.llseek = noop_llseek,
397 };
398 
399 /* create /proc/powerpc/ofdt write-only by root */
400 static int proc_ppc64_create_ofdt(void)
401 {
402 	struct proc_dir_entry *ent;
403 
404 	ent = proc_create("powerpc/ofdt", 0200, NULL, &ofdt_fops);
405 	if (ent)
406 		proc_set_size(ent, 0);
407 
408 	return 0;
409 }
410 machine_device_initcall(pseries, proc_ppc64_create_ofdt);
411