linux/arch/powerpc/platforms/pseries/reconfig.c
<<
>>
Prefs
   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
  23static 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
  56out_err:
  57        if (np) {
  58                of_node_put(np->parent);
  59                kfree(np->full_name);
  60                kfree(np);
  61        }
  62        return err;
  63}
  64
  65static 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
  90static 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 */
 114static 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
 168static 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
 187cleanup:
 188        kfree(new->name);
 189        kfree(new->value);
 190        kfree(new);
 191        return NULL;
 192}
 193
 194static 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
 234out:
 235        if (rv)
 236                release_prop_list(prop);
 237        return rv;
 238}
 239
 240static 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
 252static 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
 272static 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
 297static 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
 316static 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 */
 358static 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;
 389out:
 390        kfree(kbuf);
 391        return rv ? rv : count;
 392}
 393
 394static const struct proc_ops ofdt_proc_ops = {
 395        .proc_write     = ofdt_write,
 396        .proc_lseek     = noop_llseek,
 397};
 398
 399/* create /proc/powerpc/ofdt write-only by root */
 400static int proc_ppc64_create_ofdt(void)
 401{
 402        struct proc_dir_entry *ent;
 403
 404        ent = proc_create("powerpc/ofdt", 0200, NULL, &ofdt_proc_ops);
 405        if (ent)
 406                proc_set_size(ent, 0);
 407
 408        return 0;
 409}
 410machine_device_initcall(pseries, proc_ppc64_create_ofdt);
 411