Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * pSeries_reconfig.c - support for dynamic reconfiguration (including PCI
0004  * Hotplug and Dynamic Logical Partitioning on RPA platforms).
0005  *
0006  * Copyright (C) 2005 Nathan Lynch
0007  * Copyright (C) 2005 IBM Corporation
0008  */
0009 
0010 #include <linux/kernel.h>
0011 #include <linux/notifier.h>
0012 #include <linux/proc_fs.h>
0013 #include <linux/slab.h>
0014 #include <linux/of.h>
0015 
0016 #include <asm/machdep.h>
0017 #include <linux/uaccess.h>
0018 #include <asm/mmu.h>
0019 
0020 #include "of_helpers.h"
0021 
0022 static int pSeries_reconfig_add_node(const char *path, struct property *proplist)
0023 {
0024     struct device_node *np;
0025     int err = -ENOMEM;
0026 
0027     np = kzalloc(sizeof(*np), GFP_KERNEL);
0028     if (!np)
0029         goto out_err;
0030 
0031     np->full_name = kstrdup(kbasename(path), GFP_KERNEL);
0032     if (!np->full_name)
0033         goto out_err;
0034 
0035     np->properties = proplist;
0036     of_node_set_flag(np, OF_DYNAMIC);
0037     of_node_init(np);
0038 
0039     np->parent = pseries_of_derive_parent(path);
0040     if (IS_ERR(np->parent)) {
0041         err = PTR_ERR(np->parent);
0042         goto out_err;
0043     }
0044 
0045     err = of_attach_node(np);
0046     if (err) {
0047         printk(KERN_ERR "Failed to add device node %s\n", path);
0048         goto out_err;
0049     }
0050 
0051     of_node_put(np->parent);
0052 
0053     return 0;
0054 
0055 out_err:
0056     if (np) {
0057         of_node_put(np->parent);
0058         kfree(np->full_name);
0059         kfree(np);
0060     }
0061     return err;
0062 }
0063 
0064 static int pSeries_reconfig_remove_node(struct device_node *np)
0065 {
0066     struct device_node *parent, *child;
0067 
0068     parent = of_get_parent(np);
0069     if (!parent)
0070         return -EINVAL;
0071 
0072     if ((child = of_get_next_child(np, NULL))) {
0073         of_node_put(child);
0074         of_node_put(parent);
0075         return -EBUSY;
0076     }
0077 
0078     of_detach_node(np);
0079     of_node_put(parent);
0080     return 0;
0081 }
0082 
0083 /*
0084  * /proc/powerpc/ofdt - yucky binary interface for adding and removing
0085  * OF device nodes.  Should be deprecated as soon as we get an
0086  * in-kernel wrapper for the RTAS ibm,configure-connector call.
0087  */
0088 
0089 static void release_prop_list(const struct property *prop)
0090 {
0091     struct property *next;
0092     for (; prop; prop = next) {
0093         next = prop->next;
0094         kfree(prop->name);
0095         kfree(prop->value);
0096         kfree(prop);
0097     }
0098 
0099 }
0100 
0101 /**
0102  * parse_next_property - process the next property from raw input buffer
0103  * @buf: input buffer, must be nul-terminated
0104  * @end: end of the input buffer + 1, for validation
0105  * @name: return value; set to property name in buf
0106  * @length: return value; set to length of value
0107  * @value: return value; set to the property value in buf
0108  *
0109  * Note that the caller must make copies of the name and value returned,
0110  * this function does no allocation or copying of the data.  Return value
0111  * is set to the next name in buf, or NULL on error.
0112  */
0113 static char * parse_next_property(char *buf, char *end, char **name, int *length,
0114                   unsigned char **value)
0115 {
0116     char *tmp;
0117 
0118     *name = buf;
0119 
0120     tmp = strchr(buf, ' ');
0121     if (!tmp) {
0122         printk(KERN_ERR "property parse failed in %s at line %d\n",
0123                __func__, __LINE__);
0124         return NULL;
0125     }
0126     *tmp = '\0';
0127 
0128     if (++tmp >= end) {
0129         printk(KERN_ERR "property parse failed in %s at line %d\n",
0130                __func__, __LINE__);
0131         return NULL;
0132     }
0133 
0134     /* now we're on the length */
0135     *length = -1;
0136     *length = simple_strtoul(tmp, &tmp, 10);
0137     if (*length == -1) {
0138         printk(KERN_ERR "property parse failed in %s at line %d\n",
0139                __func__, __LINE__);
0140         return NULL;
0141     }
0142     if (*tmp != ' ' || ++tmp >= end) {
0143         printk(KERN_ERR "property parse failed in %s at line %d\n",
0144                __func__, __LINE__);
0145         return NULL;
0146     }
0147 
0148     /* now we're on the value */
0149     *value = tmp;
0150     tmp += *length;
0151     if (tmp > end) {
0152         printk(KERN_ERR "property parse failed in %s at line %d\n",
0153                __func__, __LINE__);
0154         return NULL;
0155     }
0156     else if (tmp < end && *tmp != ' ' && *tmp != '\0') {
0157         printk(KERN_ERR "property parse failed in %s at line %d\n",
0158                __func__, __LINE__);
0159         return NULL;
0160     }
0161     tmp++;
0162 
0163     /* and now we should be on the next name, or the end */
0164     return tmp;
0165 }
0166 
0167 static struct property *new_property(const char *name, const int length,
0168                      const unsigned char *value, struct property *last)
0169 {
0170     struct property *new = kzalloc(sizeof(*new), GFP_KERNEL);
0171 
0172     if (!new)
0173         return NULL;
0174 
0175     if (!(new->name = kstrdup(name, GFP_KERNEL)))
0176         goto cleanup;
0177     if (!(new->value = kmalloc(length + 1, GFP_KERNEL)))
0178         goto cleanup;
0179 
0180     memcpy(new->value, value, length);
0181     *(((char *)new->value) + length) = 0;
0182     new->length = length;
0183     new->next = last;
0184     return new;
0185 
0186 cleanup:
0187     kfree(new->name);
0188     kfree(new->value);
0189     kfree(new);
0190     return NULL;
0191 }
0192 
0193 static int do_add_node(char *buf, size_t bufsize)
0194 {
0195     char *path, *end, *name;
0196     struct device_node *np;
0197     struct property *prop = NULL;
0198     unsigned char* value;
0199     int length, rv = 0;
0200 
0201     end = buf + bufsize;
0202     path = buf;
0203     buf = strchr(buf, ' ');
0204     if (!buf)
0205         return -EINVAL;
0206     *buf = '\0';
0207     buf++;
0208 
0209     if ((np = of_find_node_by_path(path))) {
0210         of_node_put(np);
0211         return -EINVAL;
0212     }
0213 
0214     /* rv = build_prop_list(tmp, bufsize - (tmp - buf), &proplist); */
0215     while (buf < end &&
0216            (buf = parse_next_property(buf, end, &name, &length, &value))) {
0217         struct property *last = prop;
0218 
0219         prop = new_property(name, length, value, last);
0220         if (!prop) {
0221             rv = -ENOMEM;
0222             prop = last;
0223             goto out;
0224         }
0225     }
0226     if (!buf) {
0227         rv = -EINVAL;
0228         goto out;
0229     }
0230 
0231     rv = pSeries_reconfig_add_node(path, prop);
0232 
0233 out:
0234     if (rv)
0235         release_prop_list(prop);
0236     return rv;
0237 }
0238 
0239 static int do_remove_node(char *buf)
0240 {
0241     struct device_node *node;
0242     int rv = -ENODEV;
0243 
0244     if ((node = of_find_node_by_path(buf)))
0245         rv = pSeries_reconfig_remove_node(node);
0246 
0247     of_node_put(node);
0248     return rv;
0249 }
0250 
0251 static char *parse_node(char *buf, size_t bufsize, struct device_node **npp)
0252 {
0253     char *handle_str;
0254     phandle handle;
0255     *npp = NULL;
0256 
0257     handle_str = buf;
0258 
0259     buf = strchr(buf, ' ');
0260     if (!buf)
0261         return NULL;
0262     *buf = '\0';
0263     buf++;
0264 
0265     handle = simple_strtoul(handle_str, NULL, 0);
0266 
0267     *npp = of_find_node_by_phandle(handle);
0268     return buf;
0269 }
0270 
0271 static int do_add_property(char *buf, size_t bufsize)
0272 {
0273     struct property *prop = NULL;
0274     struct device_node *np;
0275     unsigned char *value;
0276     char *name, *end;
0277     int length;
0278     end = buf + bufsize;
0279     buf = parse_node(buf, bufsize, &np);
0280 
0281     if (!np)
0282         return -ENODEV;
0283 
0284     if (parse_next_property(buf, end, &name, &length, &value) == NULL)
0285         return -EINVAL;
0286 
0287     prop = new_property(name, length, value, NULL);
0288     if (!prop)
0289         return -ENOMEM;
0290 
0291     of_add_property(np, prop);
0292 
0293     return 0;
0294 }
0295 
0296 static int do_remove_property(char *buf, size_t bufsize)
0297 {
0298     struct device_node *np;
0299     char *tmp;
0300     buf = parse_node(buf, bufsize, &np);
0301 
0302     if (!np)
0303         return -ENODEV;
0304 
0305     tmp = strchr(buf,' ');
0306     if (tmp)
0307         *tmp = '\0';
0308 
0309     if (strlen(buf) == 0)
0310         return -EINVAL;
0311 
0312     return of_remove_property(np, of_find_property(np, buf, NULL));
0313 }
0314 
0315 static int do_update_property(char *buf, size_t bufsize)
0316 {
0317     struct device_node *np;
0318     unsigned char *value;
0319     char *name, *end, *next_prop;
0320     int length;
0321     struct property *newprop;
0322     buf = parse_node(buf, bufsize, &np);
0323     end = buf + bufsize;
0324 
0325     if (!np)
0326         return -ENODEV;
0327 
0328     next_prop = parse_next_property(buf, end, &name, &length, &value);
0329     if (!next_prop)
0330         return -EINVAL;
0331 
0332     if (!strlen(name))
0333         return -ENODEV;
0334 
0335     newprop = new_property(name, length, value, NULL);
0336     if (!newprop)
0337         return -ENOMEM;
0338 
0339     if (!strcmp(name, "slb-size") || !strcmp(name, "ibm,slb-size"))
0340         slb_set_size(*(int *)value);
0341 
0342     return of_update_property(np, newprop);
0343 }
0344 
0345 /**
0346  * ofdt_write - perform operations on the Open Firmware device tree
0347  *
0348  * @file: not used
0349  * @buf: command and arguments
0350  * @count: size of the command buffer
0351  * @off: not used
0352  *
0353  * Operations supported at this time are addition and removal of
0354  * whole nodes along with their properties.  Operations on individual
0355  * properties are not implemented (yet).
0356  */
0357 static ssize_t ofdt_write(struct file *file, const char __user *buf, size_t count,
0358               loff_t *off)
0359 {
0360     int rv;
0361     char *kbuf;
0362     char *tmp;
0363 
0364     kbuf = memdup_user_nul(buf, count);
0365     if (IS_ERR(kbuf))
0366         return PTR_ERR(kbuf);
0367 
0368     tmp = strchr(kbuf, ' ');
0369     if (!tmp) {
0370         rv = -EINVAL;
0371         goto out;
0372     }
0373     *tmp = '\0';
0374     tmp++;
0375 
0376     if (!strcmp(kbuf, "add_node"))
0377         rv = do_add_node(tmp, count - (tmp - kbuf));
0378     else if (!strcmp(kbuf, "remove_node"))
0379         rv = do_remove_node(tmp);
0380     else if (!strcmp(kbuf, "add_property"))
0381         rv = do_add_property(tmp, count - (tmp - kbuf));
0382     else if (!strcmp(kbuf, "remove_property"))
0383         rv = do_remove_property(tmp, count - (tmp - kbuf));
0384     else if (!strcmp(kbuf, "update_property"))
0385         rv = do_update_property(tmp, count - (tmp - kbuf));
0386     else
0387         rv = -EINVAL;
0388 out:
0389     kfree(kbuf);
0390     return rv ? rv : count;
0391 }
0392 
0393 static const struct proc_ops ofdt_proc_ops = {
0394     .proc_write = ofdt_write,
0395     .proc_lseek = noop_llseek,
0396 };
0397 
0398 /* create /proc/powerpc/ofdt write-only by root */
0399 static int proc_ppc64_create_ofdt(void)
0400 {
0401     struct proc_dir_entry *ent;
0402 
0403     ent = proc_create("powerpc/ofdt", 0200, NULL, &ofdt_proc_ops);
0404     if (ent)
0405         proc_set_size(ent, 0);
0406 
0407     return 0;
0408 }
0409 machine_device_initcall(pseries, proc_ppc64_create_ofdt);