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
pSeries_reconfig_add_node(const char * path,struct property * proplist)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
pSeries_reconfig_remove_node(struct device_node * np)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
release_prop_list(const struct property * prop)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 */
parse_next_property(char * buf,char * end,char ** name,int * length,unsigned char ** value)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
new_property(const char * name,const int length,const unsigned char * value,struct property * last)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
do_add_node(char * buf,size_t bufsize)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
do_remove_node(char * buf)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
parse_node(char * buf,size_t bufsize,struct device_node ** npp)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
do_add_property(char * buf,size_t bufsize)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
do_remove_property(char * buf,size_t bufsize)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
do_update_property(char * buf,size_t bufsize)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 */
ofdt_write(struct file * file,const char __user * buf,size_t count,loff_t * off)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 */
proc_ppc64_create_ofdt(void)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);
410