Merge tag 'arc-5.2-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/vgupta/arc
[linux-2.6-microblaze.git] / arch / powerpc / platforms / pseries / reconfig.c
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);