]> err.no Git - linux-2.6/blob - arch/powerpc/platforms/pseries/reconfig.c
Pull sbs into release branch
[linux-2.6] / arch / powerpc / platforms / pseries / reconfig.c
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/kref.h>
16 #include <linux/notifier.h>
17 #include <linux/proc_fs.h>
18
19 #include <asm/prom.h>
20 #include <asm/machdep.h>
21 #include <asm/uaccess.h>
22 #include <asm/pSeries_reconfig.h>
23
24
25
26 /*
27  * Routines for "runtime" addition and removal of device tree nodes.
28  */
29 #ifdef CONFIG_PROC_DEVICETREE
30 /*
31  * Add a node to /proc/device-tree.
32  */
33 static void add_node_proc_entries(struct device_node *np)
34 {
35         struct proc_dir_entry *ent;
36
37         ent = proc_mkdir(strrchr(np->full_name, '/') + 1, np->parent->pde);
38         if (ent)
39                 proc_device_tree_add_node(np, ent);
40 }
41
42 static void remove_node_proc_entries(struct device_node *np)
43 {
44         struct property *pp = np->properties;
45         struct device_node *parent = np->parent;
46
47         while (pp) {
48                 remove_proc_entry(pp->name, np->pde);
49                 pp = pp->next;
50         }
51         if (np->pde)
52                 remove_proc_entry(np->pde->name, parent->pde);
53 }
54 #else /* !CONFIG_PROC_DEVICETREE */
55 static void add_node_proc_entries(struct device_node *np)
56 {
57         return;
58 }
59
60 static void remove_node_proc_entries(struct device_node *np)
61 {
62         return;
63 }
64 #endif /* CONFIG_PROC_DEVICETREE */
65
66 /**
67  *      derive_parent - basically like dirname(1)
68  *      @path:  the full_name of a node to be added to the tree
69  *
70  *      Returns the node which should be the parent of the node
71  *      described by path.  E.g., for path = "/foo/bar", returns
72  *      the node with full_name = "/foo".
73  */
74 static struct device_node *derive_parent(const char *path)
75 {
76         struct device_node *parent = NULL;
77         char *parent_path = "/";
78         size_t parent_path_len = strrchr(path, '/') - path + 1;
79
80         /* reject if path is "/" */
81         if (!strcmp(path, "/"))
82                 return ERR_PTR(-EINVAL);
83
84         if (strrchr(path, '/') != path) {
85                 parent_path = kmalloc(parent_path_len, GFP_KERNEL);
86                 if (!parent_path)
87                         return ERR_PTR(-ENOMEM);
88                 strlcpy(parent_path, path, parent_path_len);
89         }
90         parent = of_find_node_by_path(parent_path);
91         if (!parent)
92                 return ERR_PTR(-EINVAL);
93         if (strcmp(parent_path, "/"))
94                 kfree(parent_path);
95         return parent;
96 }
97
98 static BLOCKING_NOTIFIER_HEAD(pSeries_reconfig_chain);
99
100 int pSeries_reconfig_notifier_register(struct notifier_block *nb)
101 {
102         return blocking_notifier_chain_register(&pSeries_reconfig_chain, nb);
103 }
104
105 void pSeries_reconfig_notifier_unregister(struct notifier_block *nb)
106 {
107         blocking_notifier_chain_unregister(&pSeries_reconfig_chain, nb);
108 }
109
110 static int pSeries_reconfig_add_node(const char *path, struct property *proplist)
111 {
112         struct device_node *np;
113         int err = -ENOMEM;
114
115         np = kzalloc(sizeof(*np), GFP_KERNEL);
116         if (!np)
117                 goto out_err;
118
119         np->full_name = kmalloc(strlen(path) + 1, GFP_KERNEL);
120         if (!np->full_name)
121                 goto out_err;
122
123         strcpy(np->full_name, path);
124
125         np->properties = proplist;
126         of_node_set_flag(np, OF_DYNAMIC);
127         kref_init(&np->kref);
128
129         np->parent = derive_parent(path);
130         if (IS_ERR(np->parent)) {
131                 err = PTR_ERR(np->parent);
132                 goto out_err;
133         }
134
135         err = blocking_notifier_call_chain(&pSeries_reconfig_chain,
136                                   PSERIES_RECONFIG_ADD, np);
137         if (err == NOTIFY_BAD) {
138                 printk(KERN_ERR "Failed to add device node %s\n", path);
139                 err = -ENOMEM; /* For now, safe to assume kmalloc failure */
140                 goto out_err;
141         }
142
143         of_attach_node(np);
144
145         add_node_proc_entries(np);
146
147         of_node_put(np->parent);
148
149         return 0;
150
151 out_err:
152         if (np) {
153                 of_node_put(np->parent);
154                 kfree(np->full_name);
155                 kfree(np);
156         }
157         return err;
158 }
159
160 static int pSeries_reconfig_remove_node(struct device_node *np)
161 {
162         struct device_node *parent, *child;
163
164         parent = of_get_parent(np);
165         if (!parent)
166                 return -EINVAL;
167
168         if ((child = of_get_next_child(np, NULL))) {
169                 of_node_put(child);
170                 return -EBUSY;
171         }
172
173         remove_node_proc_entries(np);
174
175         blocking_notifier_call_chain(&pSeries_reconfig_chain,
176                             PSERIES_RECONFIG_REMOVE, np);
177         of_detach_node(np);
178
179         of_node_put(parent);
180         of_node_put(np); /* Must decrement the refcount */
181         return 0;
182 }
183
184 /*
185  * /proc/ppc64/ofdt - yucky binary interface for adding and removing
186  * OF device nodes.  Should be deprecated as soon as we get an
187  * in-kernel wrapper for the RTAS ibm,configure-connector call.
188  */
189
190 static void release_prop_list(const struct property *prop)
191 {
192         struct property *next;
193         for (; prop; prop = next) {
194                 next = prop->next;
195                 kfree(prop->name);
196                 kfree(prop->value);
197                 kfree(prop);
198         }
199
200 }
201
202 /**
203  * parse_next_property - process the next property from raw input buffer
204  * @buf: input buffer, must be nul-terminated
205  * @end: end of the input buffer + 1, for validation
206  * @name: return value; set to property name in buf
207  * @length: return value; set to length of value
208  * @value: return value; set to the property value in buf
209  *
210  * Note that the caller must make copies of the name and value returned,
211  * this function does no allocation or copying of the data.  Return value
212  * is set to the next name in buf, or NULL on error.
213  */
214 static char * parse_next_property(char *buf, char *end, char **name, int *length,
215                                   unsigned char **value)
216 {
217         char *tmp;
218
219         *name = buf;
220
221         tmp = strchr(buf, ' ');
222         if (!tmp) {
223                 printk(KERN_ERR "property parse failed in %s at line %d\n",
224                        __FUNCTION__, __LINE__);
225                 return NULL;
226         }
227         *tmp = '\0';
228
229         if (++tmp >= end) {
230                 printk(KERN_ERR "property parse failed in %s at line %d\n",
231                        __FUNCTION__, __LINE__);
232                 return NULL;
233         }
234
235         /* now we're on the length */
236         *length = -1;
237         *length = simple_strtoul(tmp, &tmp, 10);
238         if (*length == -1) {
239                 printk(KERN_ERR "property parse failed in %s at line %d\n",
240                        __FUNCTION__, __LINE__);
241                 return NULL;
242         }
243         if (*tmp != ' ' || ++tmp >= end) {
244                 printk(KERN_ERR "property parse failed in %s at line %d\n",
245                        __FUNCTION__, __LINE__);
246                 return NULL;
247         }
248
249         /* now we're on the value */
250         *value = tmp;
251         tmp += *length;
252         if (tmp > end) {
253                 printk(KERN_ERR "property parse failed in %s at line %d\n",
254                        __FUNCTION__, __LINE__);
255                 return NULL;
256         }
257         else if (tmp < end && *tmp != ' ' && *tmp != '\0') {
258                 printk(KERN_ERR "property parse failed in %s at line %d\n",
259                        __FUNCTION__, __LINE__);
260                 return NULL;
261         }
262         tmp++;
263
264         /* and now we should be on the next name, or the end */
265         return tmp;
266 }
267
268 static struct property *new_property(const char *name, const int length,
269                                      const unsigned char *value, struct property *last)
270 {
271         struct property *new = kzalloc(sizeof(*new), GFP_KERNEL);
272
273         if (!new)
274                 return NULL;
275
276         if (!(new->name = kmalloc(strlen(name) + 1, GFP_KERNEL)))
277                 goto cleanup;
278         if (!(new->value = kmalloc(length + 1, GFP_KERNEL)))
279                 goto cleanup;
280
281         strcpy(new->name, name);
282         memcpy(new->value, value, length);
283         *(((char *)new->value) + length) = 0;
284         new->length = length;
285         new->next = last;
286         return new;
287
288 cleanup:
289         kfree(new->name);
290         kfree(new->value);
291         kfree(new);
292         return NULL;
293 }
294
295 static int do_add_node(char *buf, size_t bufsize)
296 {
297         char *path, *end, *name;
298         struct device_node *np;
299         struct property *prop = NULL;
300         unsigned char* value;
301         int length, rv = 0;
302
303         end = buf + bufsize;
304         path = buf;
305         buf = strchr(buf, ' ');
306         if (!buf)
307                 return -EINVAL;
308         *buf = '\0';
309         buf++;
310
311         if ((np = of_find_node_by_path(path))) {
312                 of_node_put(np);
313                 return -EINVAL;
314         }
315
316         /* rv = build_prop_list(tmp, bufsize - (tmp - buf), &proplist); */
317         while (buf < end &&
318                (buf = parse_next_property(buf, end, &name, &length, &value))) {
319                 struct property *last = prop;
320
321                 prop = new_property(name, length, value, last);
322                 if (!prop) {
323                         rv = -ENOMEM;
324                         prop = last;
325                         goto out;
326                 }
327         }
328         if (!buf) {
329                 rv = -EINVAL;
330                 goto out;
331         }
332
333         rv = pSeries_reconfig_add_node(path, prop);
334
335 out:
336         if (rv)
337                 release_prop_list(prop);
338         return rv;
339 }
340
341 static int do_remove_node(char *buf)
342 {
343         struct device_node *node;
344         int rv = -ENODEV;
345
346         if ((node = of_find_node_by_path(buf)))
347                 rv = pSeries_reconfig_remove_node(node);
348
349         of_node_put(node);
350         return rv;
351 }
352
353 static char *parse_node(char *buf, size_t bufsize, struct device_node **npp)
354 {
355         char *handle_str;
356         phandle handle;
357         *npp = NULL;
358
359         handle_str = buf;
360
361         buf = strchr(buf, ' ');
362         if (!buf)
363                 return NULL;
364         *buf = '\0';
365         buf++;
366
367         handle = simple_strtoul(handle_str, NULL, 10);
368
369         *npp = of_find_node_by_phandle(handle);
370         return buf;
371 }
372
373 static int do_add_property(char *buf, size_t bufsize)
374 {
375         struct property *prop = NULL;
376         struct device_node *np;
377         unsigned char *value;
378         char *name, *end;
379         int length;
380         end = buf + bufsize;
381         buf = parse_node(buf, bufsize, &np);
382
383         if (!np)
384                 return -ENODEV;
385
386         if (parse_next_property(buf, end, &name, &length, &value) == NULL)
387                 return -EINVAL;
388
389         prop = new_property(name, length, value, NULL);
390         if (!prop)
391                 return -ENOMEM;
392
393         prom_add_property(np, prop);
394
395         return 0;
396 }
397
398 static int do_remove_property(char *buf, size_t bufsize)
399 {
400         struct device_node *np;
401         char *tmp;
402         struct property *prop;
403         buf = parse_node(buf, bufsize, &np);
404
405         if (!np)
406                 return -ENODEV;
407
408         tmp = strchr(buf,' ');
409         if (tmp)
410                 *tmp = '\0';
411
412         if (strlen(buf) == 0)
413                 return -EINVAL;
414
415         prop = of_find_property(np, buf, NULL);
416
417         return prom_remove_property(np, prop);
418 }
419
420 static int do_update_property(char *buf, size_t bufsize)
421 {
422         struct device_node *np;
423         unsigned char *value;
424         char *name, *end;
425         int length;
426         struct property *newprop, *oldprop;
427         buf = parse_node(buf, bufsize, &np);
428         end = buf + bufsize;
429
430         if (!np)
431                 return -ENODEV;
432
433         if (parse_next_property(buf, end, &name, &length, &value) == NULL)
434                 return -EINVAL;
435
436         newprop = new_property(name, length, value, NULL);
437         if (!newprop)
438                 return -ENOMEM;
439
440         oldprop = of_find_property(np, name,NULL);
441         if (!oldprop)
442                 return -ENODEV;
443
444         return prom_update_property(np, newprop, oldprop);
445 }
446
447 /**
448  * ofdt_write - perform operations on the Open Firmware device tree
449  *
450  * @file: not used
451  * @buf: command and arguments
452  * @count: size of the command buffer
453  * @off: not used
454  *
455  * Operations supported at this time are addition and removal of
456  * whole nodes along with their properties.  Operations on individual
457  * properties are not implemented (yet).
458  */
459 static ssize_t ofdt_write(struct file *file, const char __user *buf, size_t count,
460                           loff_t *off)
461 {
462         int rv = 0;
463         char *kbuf;
464         char *tmp;
465
466         if (!(kbuf = kmalloc(count + 1, GFP_KERNEL))) {
467                 rv = -ENOMEM;
468                 goto out;
469         }
470         if (copy_from_user(kbuf, buf, count)) {
471                 rv = -EFAULT;
472                 goto out;
473         }
474
475         kbuf[count] = '\0';
476
477         tmp = strchr(kbuf, ' ');
478         if (!tmp) {
479                 rv = -EINVAL;
480                 goto out;
481         }
482         *tmp = '\0';
483         tmp++;
484
485         if (!strcmp(kbuf, "add_node"))
486                 rv = do_add_node(tmp, count - (tmp - kbuf));
487         else if (!strcmp(kbuf, "remove_node"))
488                 rv = do_remove_node(tmp);
489         else if (!strcmp(kbuf, "add_property"))
490                 rv = do_add_property(tmp, count - (tmp - kbuf));
491         else if (!strcmp(kbuf, "remove_property"))
492                 rv = do_remove_property(tmp, count - (tmp - kbuf));
493         else if (!strcmp(kbuf, "update_property"))
494                 rv = do_update_property(tmp, count - (tmp - kbuf));
495         else
496                 rv = -EINVAL;
497 out:
498         kfree(kbuf);
499         return rv ? rv : count;
500 }
501
502 static const struct file_operations ofdt_fops = {
503         .write = ofdt_write
504 };
505
506 /* create /proc/ppc64/ofdt write-only by root */
507 static int proc_ppc64_create_ofdt(void)
508 {
509         struct proc_dir_entry *ent;
510
511         if (!machine_is(pseries))
512                 return 0;
513
514         ent = create_proc_entry("ppc64/ofdt", S_IWUSR, NULL);
515         if (ent) {
516                 ent->data = NULL;
517                 ent->size = 0;
518                 ent->proc_fops = &ofdt_fops;
519         }
520
521         return 0;
522 }
523 __initcall(proc_ppc64_create_ofdt);