Centos-kernel-stream-9/drivers/of/kobj.c

166 lines
3.9 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
#include <linux/of.h>
#include <linux/slab.h>
#include "of_private.h"
/* true when node is initialized */
static int of_node_is_initialized(const struct device_node *node)
{
return node && node->kobj.state_initialized;
}
/* true when node is attached (i.e. present on sysfs) */
int of_node_is_attached(const struct device_node *node)
{
return node && node->kobj.state_in_sysfs;
}
#ifndef CONFIG_OF_DYNAMIC
static void of_node_release(struct kobject *kobj)
{
/* Without CONFIG_OF_DYNAMIC, no nodes gets freed */
}
#endif /* CONFIG_OF_DYNAMIC */
struct kobj_type of_node_ktype = {
.release = of_node_release,
};
static ssize_t of_node_property_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf,
loff_t offset, size_t count)
{
struct property *pp = container_of(bin_attr, struct property, attr);
return memory_read_from_buffer(buf, count, &offset, pp->value, pp->length);
}
/* always return newly allocated name, caller must free after use */
static const char *safe_name(struct kobject *kobj, const char *orig_name)
{
const char *name = orig_name;
struct kernfs_node *kn;
int i = 0;
/* don't be a hero. After 16 tries give up */
while (i < 16 && (kn = sysfs_get_dirent(kobj->sd, name))) {
sysfs_put(kn);
if (name != orig_name)
kfree(name);
name = kasprintf(GFP_KERNEL, "%s#%i", orig_name, ++i);
}
if (name == orig_name) {
name = kstrdup(orig_name, GFP_KERNEL);
} else {
pr_warn("Duplicate name in %s, renamed to \"%s\"\n",
kobject_name(kobj), name);
}
return name;
}
int __of_add_property_sysfs(struct device_node *np, struct property *pp)
{
int rc;
/* Important: Don't leak passwords */
bool secure = strncmp(pp->name, "security-", 9) == 0;
if (!IS_ENABLED(CONFIG_SYSFS))
return 0;
if (!of_kset || !of_node_is_attached(np))
return 0;
sysfs_bin_attr_init(&pp->attr);
pp->attr.attr.name = safe_name(&np->kobj, pp->name);
pp->attr.attr.mode = secure ? 0400 : 0444;
pp->attr.size = secure ? 0 : pp->length;
pp->attr.read = of_node_property_read;
rc = sysfs_create_bin_file(&np->kobj, &pp->attr);
WARN(rc, "error adding attribute %s to node %pOF\n", pp->name, np);
return rc;
}
void __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop)
{
if (!IS_ENABLED(CONFIG_SYSFS))
return;
sysfs_remove_bin_file(&np->kobj, &prop->attr);
kfree(prop->attr.attr.name);
}
void __of_remove_property_sysfs(struct device_node *np, struct property *prop)
{
/* at early boot, bail here and defer setup to of_init() */
if (of_kset && of_node_is_attached(np))
__of_sysfs_remove_bin_file(np, prop);
}
void __of_update_property_sysfs(struct device_node *np, struct property *newprop,
struct property *oldprop)
{
/* At early boot, bail out and defer setup to of_init() */
if (!of_kset)
return;
if (oldprop)
__of_sysfs_remove_bin_file(np, oldprop);
__of_add_property_sysfs(np, newprop);
}
int __of_attach_node_sysfs(struct device_node *np)
{
const char *name;
struct kobject *parent;
struct property *pp;
int rc;
of: Don't allow __of_attached_node_sysfs() without CONFIG_SYSFS Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2071840 Tested: This is one of a series of patch sets to enable Arm SystemReady IR support in the kernel for NXP i.MX8 platforms. At this stage, this has been tested by ensuring we can survive the CI/CD loop -- i.e., that we have not broken anything else, and a simple boot test. When sufficient drivers have been brought in for i.MX8M, we will be able to run further tests. commit 6211e9cb2f8faf7faae0b6caf844bfe9527cc607 Author: Marc Zyngier <maz@kernel.org> Date: Fri Aug 20 15:47:22 2021 +0100 of: Don't allow __of_attached_node_sysfs() without CONFIG_SYSFS Trying to boot without SYSFS, but with OF_DYNAMIC quickly results in a crash: [ 0.088460] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000070 [...] [ 0.103927] CPU: 1 PID: 1 Comm: swapper/0 Not tainted 5.14.0-rc3 #4179 [ 0.105810] Hardware name: linux,dummy-virt (DT) [ 0.107147] pstate: 80000005 (Nzcv daif -PAN -UAO -TCO BTYPE=--) [ 0.108876] pc : kernfs_find_and_get_ns+0x3c/0x7c [ 0.110244] lr : kernfs_find_and_get_ns+0x3c/0x7c [...] [ 0.134087] Call trace: [ 0.134800] kernfs_find_and_get_ns+0x3c/0x7c [ 0.136054] safe_name+0x4c/0xd0 [ 0.136994] __of_attach_node_sysfs+0xf8/0x124 [ 0.138287] of_core_init+0x90/0xfc [ 0.139296] driver_init+0x30/0x4c [ 0.140283] kernel_init_freeable+0x160/0x1b8 [ 0.141543] kernel_init+0x30/0x140 [ 0.142561] ret_from_fork+0x10/0x18 While not having sysfs isn't a very common option these days, it is still expected that such configuration would work. Paper over it by bailing out from __of_attach_node_sysfs() if CONFIG_SYSFS isn't enabled. Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20210820144722.169226-1-maz@kernel.org Signed-off-by: Rob Herring <robh@kernel.org> (cherry picked from commit 6211e9cb2f8faf7faae0b6caf844bfe9527cc607) Signed-off-by: Al Stone <ahs3@redhat.com>
2022-06-30 22:34:38 +00:00
if (!IS_ENABLED(CONFIG_SYSFS) || !of_kset)
return 0;
np->kobj.kset = of_kset;
if (!np->parent) {
/* Nodes without parents are new top level trees */
name = safe_name(&of_kset->kobj, "base");
parent = NULL;
} else {
name = safe_name(&np->parent->kobj, kbasename(np->full_name));
parent = &np->parent->kobj;
}
if (!name)
return -ENOMEM;
rc = kobject_add(&np->kobj, parent, "%s", name);
kfree(name);
if (rc)
return rc;
for_each_property_of_node(np, pp)
__of_add_property_sysfs(np, pp);
of_node_get(np);
return 0;
}
void __of_detach_node_sysfs(struct device_node *np)
{
struct property *pp;
BUG_ON(!of_node_is_initialized(np));
if (!of_kset)
return;
/* only remove properties if on sysfs */
if (of_node_is_attached(np)) {
for_each_property_of_node(np, pp)
__of_sysfs_remove_bin_file(np, pp);
kobject_del(&np->kobj);
}
of_node_put(np);
}