GNU Linux-libre 5.19-rc6-gnu
[releases.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/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);