linux/arch/powerpc/platforms/pseries/reconfig.c
<<
>>
Prefs
   1/*
   2 * pSeries_reconfig.c - support for dynamic reconfiguration (including PCI
   3 * Hotplug and Dynamic Logical Partitioning on RPA platforms).
   4 *
   5 * Copyright (C) 2005 Nathan Lynch
   6 * Copyright (C) 2005 IBM Corporation
   7 *
   8 *
   9 *      This program is free software; you can redistribute it and/or
  10 *      modify it under the terms of the GNU General Public License version
  11 *      2 as published by the Free Software Foundation.
  12 */
  13
  14#include <linux/kernel.h>
  15#include <linux/kref.h>
  16#include <linux/notifier.h>
  17#include <linux/proc_fs.h>
  18#include <linux/slab.h>
  19#include <linux/of.h>
  20
  21#include <asm/prom.h>
  22#include <asm/machdep.h>
  23#include <asm/uaccess.h>
  24#include <asm/mmu.h>
  25
  26/**
  27 *      derive_parent - basically like dirname(1)
  28 *      @path:  the full_name of a node to be added to the tree
  29 *
  30 *      Returns the node which should be the parent of the node
  31 *      described by path.  E.g., for path = "/foo/bar", returns
  32 *      the node with full_name = "/foo".
  33 */
  34static struct device_node *derive_parent(const char *path)
  35{
  36        struct device_node *parent = NULL;
  37        char *parent_path = "/";
  38        size_t parent_path_len = strrchr(path, '/') - path + 1;
  39
  40        /* reject if path is "/" */
  41        if (!strcmp(path, "/"))
  42                return ERR_PTR(-EINVAL);
  43
  44        if (strrchr(path, '/') != path) {
  45                parent_path = kmalloc(parent_path_len, GFP_KERNEL);
  46                if (!parent_path)
  47                        return ERR_PTR(-ENOMEM);
  48                strlcpy(parent_path, path, parent_path_len);
  49        }
  50        parent = of_find_node_by_path(parent_path);
  51        if (!parent)
  52                return ERR_PTR(-EINVAL);
  53        if (strcmp(parent_path, "/"))
  54                kfree(parent_path);
  55        return parent;
  56}
  57
  58static int pSeries_reconfig_add_node(const char *path, struct property *proplist)
  59{
  60        struct device_node *np;
  61        int err = -ENOMEM;
  62
  63        np = kzalloc(sizeof(*np), GFP_KERNEL);
  64        if (!np)
  65                goto out_err;
  66
  67        np->full_name = kstrdup(path, GFP_KERNEL);
  68        if (!np->full_name)
  69                goto out_err;
  70
  71        np->properties = proplist;
  72        of_node_set_flag(np, OF_DYNAMIC);
  73        kref_init(&np->kref);
  74
  75        np->parent = derive_parent(path);
  76        if (IS_ERR(np->parent)) {
  77                err = PTR_ERR(np->parent);
  78                goto out_err;
  79        }
  80
  81        err = of_attach_node(np);
  82        if (err) {
  83                printk(KERN_ERR "Failed to add device node %s\n", path);
  84                goto out_err;
  85        }
  86
  87        of_node_put(np->parent);
  88
  89        return 0;
  90
  91out_err:
  92        if (np) {
  93                of_node_put(np->parent);
  94                kfree(np->full_name);
  95                kfree(np);
  96        }
  97        return err;
  98}
  99
 100static int pSeries_reconfig_remove_node(struct device_node *np)
 101{
 102        struct device_node *parent, *child;
 103
 104        parent = of_get_parent(np);
 105        if (!parent)
 106                return -EINVAL;
 107
 108        if ((child = of_get_next_child(np, NULL))) {
 109                of_node_put(child);
 110                of_node_put(parent);
 111                return -EBUSY;
 112        }
 113
 114        of_detach_node(np);
 115        of_node_put(parent);
 116        of_node_put(np); /* Must decrement the refcount */
 117        return 0;
 118}
 119
 120/*
 121 * /proc/powerpc/ofdt - yucky binary interface for adding and removing
 122 * OF device nodes.  Should be deprecated as soon as we get an
 123 * in-kernel wrapper for the RTAS ibm,configure-connector call.
 124 */
 125
 126static void release_prop_list(const struct property *prop)
 127{
 128        struct property *next;
 129        for (; prop; prop = next) {
 130                next = prop->next;
 131                kfree(prop->name);
 132                kfree(prop->value);
 133                kfree(prop);
 134        }
 135
 136}
 137
 138/**
 139 * parse_next_property - process the next property from raw input buffer
 140 * @buf: input buffer, must be nul-terminated
 141 * @end: end of the input buffer + 1, for validation
 142 * @name: return value; set to property name in buf
 143 * @length: return value; set to length of value
 144 * @value: return value; set to the property value in buf
 145 *
 146 * Note that the caller must make copies of the name and value returned,
 147 * this function does no allocation or copying of the data.  Return value
 148 * is set to the next name in buf, or NULL on error.
 149 */
 150static char * parse_next_property(char *buf, char *end, char **name, int *length,
 151                                  unsigned char **value)
 152{
 153        char *tmp;
 154
 155        *name = buf;
 156
 157        tmp = strchr(buf, ' ');
 158        if (!tmp) {
 159                printk(KERN_ERR "property parse failed in %s at line %d\n",
 160                       __func__, __LINE__);
 161                return NULL;
 162        }
 163        *tmp = '\0';
 164
 165        if (++tmp >= end) {
 166                printk(KERN_ERR "property parse failed in %s at line %d\n",
 167                       __func__, __LINE__);
 168                return NULL;
 169        }
 170
 171        /* now we're on the length */
 172        *length = -1;
 173        *length = simple_strtoul(tmp, &tmp, 10);
 174        if (*length == -1) {
 175                printk(KERN_ERR "property parse failed in %s at line %d\n",
 176                       __func__, __LINE__);
 177                return NULL;
 178        }
 179        if (*tmp != ' ' || ++tmp >= end) {
 180                printk(KERN_ERR "property parse failed in %s at line %d\n",
 181                       __func__, __LINE__);
 182                return NULL;
 183        }
 184
 185        /* now we're on the value */
 186        *value = tmp;
 187        tmp += *length;
 188        if (tmp > end) {
 189                printk(KERN_ERR "property parse failed in %s at line %d\n",
 190                       __func__, __LINE__);
 191                return NULL;
 192        }
 193        else if (tmp < end && *tmp != ' ' && *tmp != '\0') {
 194                printk(KERN_ERR "property parse failed in %s at line %d\n",
 195                       __func__, __LINE__);
 196                return NULL;
 197        }
 198        tmp++;
 199
 200        /* and now we should be on the next name, or the end */
 201        return tmp;
 202}
 203
 204static struct property *new_property(const char *name, const int length,
 205                                     const unsigned char *value, struct property *last)
 206{
 207        struct property *new = kzalloc(sizeof(*new), GFP_KERNEL);
 208
 209        if (!new)
 210                return NULL;
 211
 212        if (!(new->name = kstrdup(name, GFP_KERNEL)))
 213                goto cleanup;
 214        if (!(new->value = kmalloc(length + 1, GFP_KERNEL)))
 215                goto cleanup;
 216
 217        memcpy(new->value, value, length);
 218        *(((char *)new->value) + length) = 0;
 219        new->length = length;
 220        new->next = last;
 221        return new;
 222
 223cleanup:
 224        kfree(new->name);
 225        kfree(new->value);
 226        kfree(new);
 227        return NULL;
 228}
 229
 230static int do_add_node(char *buf, size_t bufsize)
 231{
 232        char *path, *end, *name;
 233        struct device_node *np;
 234        struct property *prop = NULL;
 235        unsigned char* value;
 236        int length, rv = 0;
 237
 238        end = buf + bufsize;
 239        path = buf;
 240        buf = strchr(buf, ' ');
 241        if (!buf)
 242                return -EINVAL;
 243        *buf = '\0';
 244        buf++;
 245
 246        if ((np = of_find_node_by_path(path))) {
 247                of_node_put(np);
 248                return -EINVAL;
 249        }
 250
 251        /* rv = build_prop_list(tmp, bufsize - (tmp - buf), &proplist); */
 252        while (buf < end &&
 253               (buf = parse_next_property(buf, end, &name, &length, &value))) {
 254                struct property *last = prop;
 255
 256                prop = new_property(name, length, value, last);
 257                if (!prop) {
 258                        rv = -ENOMEM;
 259                        prop = last;
 260                        goto out;
 261                }
 262        }
 263        if (!buf) {
 264                rv = -EINVAL;
 265                goto out;
 266        }
 267
 268        rv = pSeries_reconfig_add_node(path, prop);
 269
 270out:
 271        if (rv)
 272                release_prop_list(prop);
 273        return rv;
 274}
 275
 276static int do_remove_node(char *buf)
 277{
 278        struct device_node *node;
 279        int rv = -ENODEV;
 280
 281        if ((node = of_find_node_by_path(buf)))
 282                rv = pSeries_reconfig_remove_node(node);
 283
 284        of_node_put(node);
 285        return rv;
 286}
 287
 288static char *parse_node(char *buf, size_t bufsize, struct device_node **npp)
 289{
 290        char *handle_str;
 291        phandle handle;
 292        *npp = NULL;
 293
 294        handle_str = buf;
 295
 296        buf = strchr(buf, ' ');
 297        if (!buf)
 298                return NULL;
 299        *buf = '\0';
 300        buf++;
 301
 302        handle = simple_strtoul(handle_str, NULL, 0);
 303
 304        *npp = of_find_node_by_phandle(handle);
 305        return buf;
 306}
 307
 308static int do_add_property(char *buf, size_t bufsize)
 309{
 310        struct property *prop = NULL;
 311        struct device_node *np;
 312        unsigned char *value;
 313        char *name, *end;
 314        int length;
 315        end = buf + bufsize;
 316        buf = parse_node(buf, bufsize, &np);
 317
 318        if (!np)
 319                return -ENODEV;
 320
 321        if (parse_next_property(buf, end, &name, &length, &value) == NULL)
 322                return -EINVAL;
 323
 324        prop = new_property(name, length, value, NULL);
 325        if (!prop)
 326                return -ENOMEM;
 327
 328        of_add_property(np, prop);
 329
 330        return 0;
 331}
 332
 333static int do_remove_property(char *buf, size_t bufsize)
 334{
 335        struct device_node *np;
 336        char *tmp;
 337        struct property *prop;
 338        buf = parse_node(buf, bufsize, &np);
 339
 340        if (!np)
 341                return -ENODEV;
 342
 343        tmp = strchr(buf,' ');
 344        if (tmp)
 345                *tmp = '\0';
 346
 347        if (strlen(buf) == 0)
 348                return -EINVAL;
 349
 350        prop = of_find_property(np, buf, NULL);
 351
 352        return of_remove_property(np, prop);
 353}
 354
 355static int do_update_property(char *buf, size_t bufsize)
 356{
 357        struct device_node *np;
 358        unsigned char *value;
 359        char *name, *end, *next_prop;
 360        int length;
 361        struct property *newprop;
 362        buf = parse_node(buf, bufsize, &np);
 363        end = buf + bufsize;
 364
 365        if (!np)
 366                return -ENODEV;
 367
 368        next_prop = parse_next_property(buf, end, &name, &length, &value);
 369        if (!next_prop)
 370                return -EINVAL;
 371
 372        if (!strlen(name))
 373                return -ENODEV;
 374
 375        newprop = new_property(name, length, value, NULL);
 376        if (!newprop)
 377                return -ENOMEM;
 378
 379        if (!strcmp(name, "slb-size") || !strcmp(name, "ibm,slb-size"))
 380                slb_set_size(*(int *)value);
 381
 382        return of_update_property(np, newprop);
 383}
 384
 385/**
 386 * ofdt_write - perform operations on the Open Firmware device tree
 387 *
 388 * @file: not used
 389 * @buf: command and arguments
 390 * @count: size of the command buffer
 391 * @off: not used
 392 *
 393 * Operations supported at this time are addition and removal of
 394 * whole nodes along with their properties.  Operations on individual
 395 * properties are not implemented (yet).
 396 */
 397static ssize_t ofdt_write(struct file *file, const char __user *buf, size_t count,
 398                          loff_t *off)
 399{
 400        int rv = 0;
 401        char *kbuf;
 402        char *tmp;
 403
 404        if (!(kbuf = kmalloc(count + 1, GFP_KERNEL))) {
 405                rv = -ENOMEM;
 406                goto out;
 407        }
 408        if (copy_from_user(kbuf, buf, count)) {
 409                rv = -EFAULT;
 410                goto out;
 411        }
 412
 413        kbuf[count] = '\0';
 414
 415        tmp = strchr(kbuf, ' ');
 416        if (!tmp) {
 417                rv = -EINVAL;
 418                goto out;
 419        }
 420        *tmp = '\0';
 421        tmp++;
 422
 423        if (!strcmp(kbuf, "add_node"))
 424                rv = do_add_node(tmp, count - (tmp - kbuf));
 425        else if (!strcmp(kbuf, "remove_node"))
 426                rv = do_remove_node(tmp);
 427        else if (!strcmp(kbuf, "add_property"))
 428                rv = do_add_property(tmp, count - (tmp - kbuf));
 429        else if (!strcmp(kbuf, "remove_property"))
 430                rv = do_remove_property(tmp, count - (tmp - kbuf));
 431        else if (!strcmp(kbuf, "update_property"))
 432                rv = do_update_property(tmp, count - (tmp - kbuf));
 433        else
 434                rv = -EINVAL;
 435out:
 436        kfree(kbuf);
 437        return rv ? rv : count;
 438}
 439
 440static const struct file_operations ofdt_fops = {
 441        .write = ofdt_write,
 442        .llseek = noop_llseek,
 443};
 444
 445/* create /proc/powerpc/ofdt write-only by root */
 446static int proc_ppc64_create_ofdt(void)
 447{
 448        struct proc_dir_entry *ent;
 449
 450        if (!machine_is(pseries))
 451                return 0;
 452
 453        ent = proc_create("powerpc/ofdt", S_IWUSR, NULL, &ofdt_fops);
 454        if (ent)
 455                proc_set_size(ent, 0);
 456
 457        return 0;
 458}
 459__initcall(proc_ppc64_create_ofdt);
 460