Path: blob/master/arch/powerpc/platforms/pseries/reconfig.c
26481 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* pSeries_reconfig.c - support for dynamic reconfiguration (including PCI3* Hotplug and Dynamic Logical Partitioning on RPA platforms).4*5* Copyright (C) 2005 Nathan Lynch6* Copyright (C) 2005 IBM Corporation7*/89#include <linux/kernel.h>10#include <linux/notifier.h>11#include <linux/proc_fs.h>12#include <linux/security.h>13#include <linux/slab.h>14#include <linux/of.h>1516#include <asm/machdep.h>17#include <linux/uaccess.h>18#include <asm/mmu.h>1920#include "of_helpers.h"2122static int pSeries_reconfig_add_node(const char *path, struct property *proplist)23{24struct device_node *np;25int err = -ENOMEM;2627np = kzalloc(sizeof(*np), GFP_KERNEL);28if (!np)29goto out_err;3031np->full_name = kstrdup(kbasename(path), GFP_KERNEL);32if (!np->full_name)33goto out_err;3435np->properties = proplist;36of_node_set_flag(np, OF_DYNAMIC);37of_node_init(np);3839np->parent = pseries_of_derive_parent(path);40if (IS_ERR(np->parent)) {41err = PTR_ERR(np->parent);42goto out_err;43}4445err = of_attach_node(np);46if (err) {47printk(KERN_ERR "Failed to add device node %s\n", path);48goto out_err;49}5051of_node_put(np->parent);5253return 0;5455out_err:56if (np) {57of_node_put(np->parent);58kfree(np->full_name);59kfree(np);60}61return err;62}6364static int pSeries_reconfig_remove_node(struct device_node *np)65{66struct device_node *parent, *child;6768parent = of_get_parent(np);69if (!parent)70return -EINVAL;7172if ((child = of_get_next_child(np, NULL))) {73of_node_put(child);74of_node_put(parent);75return -EBUSY;76}7778of_detach_node(np);79of_node_put(parent);80return 0;81}8283/*84* /proc/powerpc/ofdt - yucky binary interface for adding and removing85* OF device nodes. Should be deprecated as soon as we get an86* in-kernel wrapper for the RTAS ibm,configure-connector call.87*/8889static void release_prop_list(const struct property *prop)90{91struct property *next;92for (; prop; prop = next) {93next = prop->next;94kfree(prop->name);95kfree(prop->value);96kfree(prop);97}9899}100101/**102* parse_next_property - process the next property from raw input buffer103* @buf: input buffer, must be nul-terminated104* @end: end of the input buffer + 1, for validation105* @name: return value; set to property name in buf106* @length: return value; set to length of value107* @value: return value; set to the property value in buf108*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 value111* is set to the next name in buf, or NULL on error.112*/113static char * parse_next_property(char *buf, char *end, char **name, int *length,114unsigned char **value)115{116char *tmp;117118*name = buf;119120tmp = strchr(buf, ' ');121if (!tmp) {122printk(KERN_ERR "property parse failed in %s at line %d\n",123__func__, __LINE__);124return NULL;125}126*tmp = '\0';127128if (++tmp >= end) {129printk(KERN_ERR "property parse failed in %s at line %d\n",130__func__, __LINE__);131return NULL;132}133134/* now we're on the length */135*length = -1;136*length = simple_strtoul(tmp, &tmp, 10);137if (*length == -1) {138printk(KERN_ERR "property parse failed in %s at line %d\n",139__func__, __LINE__);140return NULL;141}142if (*tmp != ' ' || ++tmp >= end) {143printk(KERN_ERR "property parse failed in %s at line %d\n",144__func__, __LINE__);145return NULL;146}147148/* now we're on the value */149*value = tmp;150tmp += *length;151if (tmp > end) {152printk(KERN_ERR "property parse failed in %s at line %d\n",153__func__, __LINE__);154return NULL;155}156else if (tmp < end && *tmp != ' ' && *tmp != '\0') {157printk(KERN_ERR "property parse failed in %s at line %d\n",158__func__, __LINE__);159return NULL;160}161tmp++;162163/* and now we should be on the next name, or the end */164return tmp;165}166167static struct property *new_property(const char *name, const int length,168const unsigned char *value, struct property *last)169{170struct property *new = kzalloc(sizeof(*new), GFP_KERNEL);171172if (!new)173return NULL;174175if (!(new->name = kstrdup(name, GFP_KERNEL)))176goto cleanup;177if (!(new->value = kmalloc(length + 1, GFP_KERNEL)))178goto cleanup;179180memcpy(new->value, value, length);181*(((char *)new->value) + length) = 0;182new->length = length;183new->next = last;184return new;185186cleanup:187kfree(new->name);188kfree(new->value);189kfree(new);190return NULL;191}192193static int do_add_node(char *buf, size_t bufsize)194{195char *path, *end, *name;196struct device_node *np;197struct property *prop = NULL;198unsigned char* value;199int length, rv = 0;200201end = buf + bufsize;202path = buf;203buf = strchr(buf, ' ');204if (!buf)205return -EINVAL;206*buf = '\0';207buf++;208209if ((np = of_find_node_by_path(path))) {210of_node_put(np);211return -EINVAL;212}213214/* rv = build_prop_list(tmp, bufsize - (tmp - buf), &proplist); */215while (buf < end &&216(buf = parse_next_property(buf, end, &name, &length, &value))) {217struct property *last = prop;218219prop = new_property(name, length, value, last);220if (!prop) {221rv = -ENOMEM;222prop = last;223goto out;224}225}226if (!buf) {227rv = -EINVAL;228goto out;229}230231rv = pSeries_reconfig_add_node(path, prop);232233out:234if (rv)235release_prop_list(prop);236return rv;237}238239static int do_remove_node(char *buf)240{241struct device_node *node;242int rv = -ENODEV;243244if ((node = of_find_node_by_path(buf)))245rv = pSeries_reconfig_remove_node(node);246247of_node_put(node);248return rv;249}250251static char *parse_node(char *buf, size_t bufsize, struct device_node **npp)252{253char *handle_str;254phandle handle;255*npp = NULL;256257handle_str = buf;258259buf = strchr(buf, ' ');260if (!buf)261return NULL;262*buf = '\0';263buf++;264265handle = simple_strtoul(handle_str, NULL, 0);266267*npp = of_find_node_by_phandle(handle);268return buf;269}270271static int do_add_property(char *buf, size_t bufsize)272{273struct property *prop = NULL;274struct device_node *np;275unsigned char *value;276char *name, *end;277int length;278end = buf + bufsize;279buf = parse_node(buf, bufsize, &np);280281if (!np)282return -ENODEV;283284if (parse_next_property(buf, end, &name, &length, &value) == NULL)285return -EINVAL;286287prop = new_property(name, length, value, NULL);288if (!prop)289return -ENOMEM;290291of_add_property(np, prop);292293return 0;294}295296static int do_remove_property(char *buf, size_t bufsize)297{298struct device_node *np;299char *tmp;300buf = parse_node(buf, bufsize, &np);301302if (!np)303return -ENODEV;304305tmp = strchr(buf,' ');306if (tmp)307*tmp = '\0';308309if (strlen(buf) == 0)310return -EINVAL;311312return of_remove_property(np, of_find_property(np, buf, NULL));313}314315static int do_update_property(char *buf, size_t bufsize)316{317struct device_node *np;318unsigned char *value;319char *name, *end, *next_prop;320int length;321struct property *newprop;322buf = parse_node(buf, bufsize, &np);323end = buf + bufsize;324325if (!np)326return -ENODEV;327328next_prop = parse_next_property(buf, end, &name, &length, &value);329if (!next_prop)330return -EINVAL;331332if (!strlen(name))333return -ENODEV;334335newprop = new_property(name, length, value, NULL);336if (!newprop)337return -ENOMEM;338339if (!strcmp(name, "slb-size") || !strcmp(name, "ibm,slb-size"))340slb_set_size(*(int *)value);341342return of_update_property(np, newprop);343}344345/**346* ofdt_write - perform operations on the Open Firmware device tree347*348* @file: not used349* @buf: command and arguments350* @count: size of the command buffer351* @off: not used352*353* Operations supported at this time are addition and removal of354* whole nodes along with their properties. Operations on individual355* properties are not implemented (yet).356*/357static ssize_t ofdt_write(struct file *file, const char __user *buf, size_t count,358loff_t *off)359{360int rv;361char *kbuf;362char *tmp;363364rv = security_locked_down(LOCKDOWN_DEVICE_TREE);365if (rv)366return rv;367368kbuf = memdup_user_nul(buf, count);369if (IS_ERR(kbuf))370return PTR_ERR(kbuf);371372tmp = strchr(kbuf, ' ');373if (!tmp) {374rv = -EINVAL;375goto out;376}377*tmp = '\0';378tmp++;379380if (!strcmp(kbuf, "add_node"))381rv = do_add_node(tmp, count - (tmp - kbuf));382else if (!strcmp(kbuf, "remove_node"))383rv = do_remove_node(tmp);384else if (!strcmp(kbuf, "add_property"))385rv = do_add_property(tmp, count - (tmp - kbuf));386else if (!strcmp(kbuf, "remove_property"))387rv = do_remove_property(tmp, count - (tmp - kbuf));388else if (!strcmp(kbuf, "update_property"))389rv = do_update_property(tmp, count - (tmp - kbuf));390else391rv = -EINVAL;392out:393kfree(kbuf);394return rv ? rv : count;395}396397static const struct proc_ops ofdt_proc_ops = {398.proc_write = ofdt_write,399.proc_lseek = noop_llseek,400};401402/* create /proc/powerpc/ofdt write-only by root */403static int proc_ppc64_create_ofdt(void)404{405struct proc_dir_entry *ent;406407ent = proc_create("powerpc/ofdt", 0200, NULL, &ofdt_proc_ops);408if (ent)409proc_set_size(ent, 0);410411return 0;412}413machine_device_initcall(pseries, proc_ppc64_create_ofdt);414415416