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