// SPDX-License-Identifier: GPL-2.0-only
/*
* pSeries_reconfig . c - support for dynamic reconfiguration ( including PCI
* Hotplug and Dynamic Logical Partitioning on RPA platforms ) .
*
* Copyright ( C ) 2005 Nathan Lynch
* Copyright ( C ) 2005 IBM Corporation
*/
#include <linux/kernel.h>
#include <linux/notifier.h>
#include <linux/proc_fs.h>
#include <linux/security.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <asm /machdep.h>
#include <linux/uaccess.h>
#include <asm /mmu.h>
#include "of_helpers.h"
static int pSeries_reconfig_add_node(const char *path, struct property *proplist)
{
struct device_node *np;
int err = -ENOMEM;
np = kzalloc(sizeof (*np), GFP_KERNEL);
if (!np)
goto out_err;
np->full_name = kstrdup(kbasename(path), GFP_KERNEL);
if (!np->full_name)
goto out_err;
np->properties = proplist;
of_node_set_flag(np, OF_DYNAMIC);
of_node_init(np);
np->parent = pseries_of_derive_parent(path);
if (IS_ERR(np->parent)) {
err = PTR_ERR(np->parent);
goto out_err;
}
err = of_attach_node(np);
if (err) {
printk(KERN_ERR "Failed to add device node %s\n" , path);
goto out_err;
}
of_node_put(np->parent);
return 0 ;
out_err:
if (np) {
of_node_put(np->parent);
kfree(np->full_name);
kfree(np);
}
return err;
}
static int pSeries_reconfig_remove_node(struct device_node *np)
{
struct device_node *parent, *child;
parent = of_get_parent(np);
if (!parent)
return -EINVAL;
if ((child = of_get_next_child(np, NULL))) {
of_node_put(child);
of_node_put(parent);
return -EBUSY;
}
of_detach_node(np);
of_node_put(parent);
return 0 ;
}
/*
* / proc / powerpc / ofdt - yucky binary interface for adding and removing
* OF device nodes . Should be deprecated as soon as we get an
* in - kernel wrapper for the RTAS ibm , configure - connector call .
*/
static void release_prop_list(const struct property *prop)
{
struct property *next;
for (; prop; prop = next) {
next = prop->next;
kfree(prop->name);
kfree(prop->value);
kfree(prop);
}
}
/**
* parse_next_property - process the next property from raw input buffer
* @ buf : input buffer , must be nul - terminated
* @ end : end of the input buffer + 1 , for validation
* @ name : return value ; set to property name in buf
* @ length : return value ; set to length of value
* @ value : return value ; set to the property value in buf
*
* Note that the caller must make copies of the name and value returned ,
* this function does no allocation or copying of the data . Return value
* is set to the next name in buf , or NULL on error .
*/
static char * parse_next_property(char *buf, char *end, char **name, int *length,
unsigned char **value)
{
char *tmp;
*name = buf;
tmp = strchr(buf, ' ' );
if (!tmp) {
printk(KERN_ERR "property parse failed in %s at line %d\n" ,
__func__, __LINE__);
return NULL;
}
*tmp = '\0' ;
if (++tmp >= end) {
printk(KERN_ERR "property parse failed in %s at line %d\n" ,
__func__, __LINE__);
return NULL;
}
/* now we're on the length */
*length = -1 ;
*length = simple_strtoul(tmp, &tmp, 10 );
if (*length == -1 ) {
printk(KERN_ERR "property parse failed in %s at line %d\n" ,
__func__, __LINE__);
return NULL;
}
if (*tmp != ' ' || ++tmp >= end) {
printk(KERN_ERR "property parse failed in %s at line %d\n" ,
__func__, __LINE__);
return NULL;
}
/* now we're on the value */
*value = tmp;
tmp += *length;
if (tmp > end) {
printk(KERN_ERR "property parse failed in %s at line %d\n" ,
__func__, __LINE__);
return NULL;
}
else if (tmp < end && *tmp != ' ' && *tmp != '\0' ) {
printk(KERN_ERR "property parse failed in %s at line %d\n" ,
__func__, __LINE__);
return NULL;
}
tmp++;
/* and now we should be on the next name, or the end */
return tmp;
}
static struct property *new_property(const char *name, const int length,
const unsigned char *value, struct property *last)
{
struct property *new = kzalloc(sizeof (*new ), GFP_KERNEL);
if (!new )
return NULL;
if (!(new ->name = kstrdup(name, GFP_KERNEL)))
goto cleanup;
if (!(new ->value = kmalloc(length + 1 , GFP_KERNEL)))
goto cleanup;
memcpy(new ->value, value, length);
*(((char *)new ->value) + length) = 0 ;
new ->length = length;
new ->next = last;
return new ;
cleanup:
kfree(new ->name);
kfree(new ->value);
kfree(new );
return NULL;
}
static int do_add_node(char *buf, size_t bufsize)
{
char *path, *end, *name;
struct device_node *np;
struct property *prop = NULL;
unsigned char * value;
int length, rv = 0 ;
end = buf + bufsize;
path = buf;
buf = strchr(buf, ' ' );
if (!buf)
return -EINVAL;
*buf = '\0' ;
buf++;
if ((np = of_find_node_by_path(path))) {
of_node_put(np);
return -EINVAL;
}
/* rv = build_prop_list(tmp, bufsize - (tmp - buf), &proplist); */
while (buf < end &&
(buf = parse_next_property(buf, end, &name, &length, &value))) {
struct property *last = prop;
prop = new_property(name, length, value, last);
if (!prop) {
rv = -ENOMEM;
prop = last;
goto out;
}
}
if (!buf) {
rv = -EINVAL;
goto out;
}
rv = pSeries_reconfig_add_node(path, prop);
out:
if (rv)
release_prop_list(prop);
return rv;
}
static int do_remove_node(char *buf)
{
struct device_node *node;
int rv = -ENODEV;
if ((node = of_find_node_by_path(buf)))
rv = pSeries_reconfig_remove_node(node);
of_node_put(node);
return rv;
}
static char *parse_node(char *buf, size_t bufsize, struct device_node **npp)
{
char *handle_str;
phandle handle;
*npp = NULL;
handle_str = buf;
buf = strchr(buf, ' ' );
if (!buf)
return NULL;
*buf = '\0' ;
buf++;
handle = simple_strtoul(handle_str, NULL, 0 );
*npp = of_find_node_by_phandle(handle);
return buf;
}
static int do_add_property(char *buf, size_t bufsize)
{
struct property *prop = NULL;
struct device_node *np;
unsigned char *value;
char *name, *end;
int length;
end = buf + bufsize;
buf = parse_node(buf, bufsize, &np);
if (!np)
return -ENODEV;
if (parse_next_property(buf, end, &name, &length, &value) == NULL)
return -EINVAL;
prop = new_property(name, length, value, NULL);
if (!prop)
return -ENOMEM;
of_add_property(np, prop);
return 0 ;
}
static int do_remove_property(char *buf, size_t bufsize)
{
struct device_node *np;
char *tmp;
buf = parse_node(buf, bufsize, &np);
if (!np)
return -ENODEV;
tmp = strchr(buf,' ' );
if (tmp)
*tmp = '\0' ;
if (strlen(buf) == 0 )
return -EINVAL;
return of_remove_property(np, of_find_property(np, buf, NULL));
}
static int do_update_property(char *buf, size_t bufsize)
{
struct device_node *np;
unsigned char *value;
char *name, *end, *next_prop;
int length;
struct property *newprop;
buf = parse_node(buf, bufsize, &np);
end = buf + bufsize;
if (!np)
return -ENODEV;
next_prop = parse_next_property(buf, end, &name, &length, &value);
if (!next_prop)
return -EINVAL;
if (!strlen(name))
return -ENODEV;
newprop = new_property(name, length, value, NULL);
if (!newprop)
return -ENOMEM;
if (!strcmp(name, "slb-size" ) || !strcmp(name, "ibm,slb-size" ))
slb_set_size(*(int *)value);
return of_update_property(np, newprop);
}
/**
* ofdt_write - perform operations on the Open Firmware device tree
*
* @ file : not used
* @ buf : command and arguments
* @ count : size of the command buffer
* @ off : not used
*
* Operations supported at this time are addition and removal of
* whole nodes along with their properties . Operations on individual
* properties are not implemented ( yet ) .
*/
static ssize_t ofdt_write(struct file *file, const char __user *buf, size_t count,
loff_t *off)
{
int rv;
char *kbuf;
char *tmp;
rv = security_locked_down(LOCKDOWN_DEVICE_TREE);
if (rv)
return rv;
kbuf = memdup_user_nul(buf, count);
if (IS_ERR(kbuf))
return PTR_ERR(kbuf);
tmp = strchr(kbuf, ' ' );
if (!tmp) {
rv = -EINVAL;
goto out;
}
*tmp = '\0' ;
tmp++;
if (!strcmp(kbuf, "add_node" ))
rv = do_add_node(tmp, count - (tmp - kbuf));
else if (!strcmp(kbuf, "remove_node" ))
rv = do_remove_node(tmp);
else if (!strcmp(kbuf, "add_property" ))
rv = do_add_property(tmp, count - (tmp - kbuf));
else if (!strcmp(kbuf, "remove_property" ))
rv = do_remove_property(tmp, count - (tmp - kbuf));
else if (!strcmp(kbuf, "update_property" ))
rv = do_update_property(tmp, count - (tmp - kbuf));
else
rv = -EINVAL;
out:
kfree(kbuf);
return rv ? rv : count;
}
static const struct proc_ops ofdt_proc_ops = {
.proc_write = ofdt_write,
.proc_lseek = noop_llseek,
};
/* create /proc/powerpc/ofdt write-only by root */
static int proc_ppc64_create_ofdt(void )
{
struct proc_dir_entry *ent;
ent = proc_create("powerpc/ofdt" , 0200 , NULL, &ofdt_proc_ops);
if (ent)
proc_set_size(ent, 0 );
return 0 ;
}
machine_device_initcall(pseries, proc_ppc64_create_ofdt);
Messung V0.5 in Prozent C=98 H=88 G=93
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-10-02)
¤
*© Formatika GbR, Deutschland