~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/powerpc/platforms/pseries/reconfig.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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/security.h>
 14 #include <linux/slab.h>
 15 #include <linux/of.h>
 16 
 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         rv = security_locked_down(LOCKDOWN_DEVICE_TREE);
366         if (rv)
367                 return rv;
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 proc_ops ofdt_proc_ops = {
399         .proc_write     = ofdt_write,
400         .proc_lseek     = 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_proc_ops);
409         if (ent)
410                 proc_set_size(ent, 0);
411 
412         return 0;
413 }
414 machine_device_initcall(pseries, proc_ppc64_create_ofdt);
415 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php