Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags   |
// 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/slab.h>
#include <linux/of.h>
#include <asm/prom.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;
<------>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);