162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/of.h> 362306a36Sopenharmony_ci#include <linux/slab.h> 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include "of_private.h" 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci/* true when node is initialized */ 862306a36Sopenharmony_cistatic int of_node_is_initialized(const struct device_node *node) 962306a36Sopenharmony_ci{ 1062306a36Sopenharmony_ci return node && node->kobj.state_initialized; 1162306a36Sopenharmony_ci} 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* true when node is attached (i.e. present on sysfs) */ 1462306a36Sopenharmony_ciint of_node_is_attached(const struct device_node *node) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci return node && node->kobj.state_in_sysfs; 1762306a36Sopenharmony_ci} 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#ifndef CONFIG_OF_DYNAMIC 2162306a36Sopenharmony_cistatic void of_node_release(struct kobject *kobj) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci /* Without CONFIG_OF_DYNAMIC, no nodes gets freed */ 2462306a36Sopenharmony_ci} 2562306a36Sopenharmony_ci#endif /* CONFIG_OF_DYNAMIC */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciconst struct kobj_type of_node_ktype = { 2862306a36Sopenharmony_ci .release = of_node_release, 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic ssize_t of_node_property_read(struct file *filp, struct kobject *kobj, 3262306a36Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 3362306a36Sopenharmony_ci loff_t offset, size_t count) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct property *pp = container_of(bin_attr, struct property, attr); 3662306a36Sopenharmony_ci return memory_read_from_buffer(buf, count, &offset, pp->value, pp->length); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* always return newly allocated name, caller must free after use */ 4062306a36Sopenharmony_cistatic const char *safe_name(struct kobject *kobj, const char *orig_name) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci const char *name = orig_name; 4362306a36Sopenharmony_ci struct kernfs_node *kn; 4462306a36Sopenharmony_ci int i = 0; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* don't be a hero. After 16 tries give up */ 4762306a36Sopenharmony_ci while (i < 16 && (kn = sysfs_get_dirent(kobj->sd, name))) { 4862306a36Sopenharmony_ci sysfs_put(kn); 4962306a36Sopenharmony_ci if (name != orig_name) 5062306a36Sopenharmony_ci kfree(name); 5162306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%s#%i", orig_name, ++i); 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (name == orig_name) { 5562306a36Sopenharmony_ci name = kstrdup(orig_name, GFP_KERNEL); 5662306a36Sopenharmony_ci } else { 5762306a36Sopenharmony_ci pr_warn("Duplicate name in %s, renamed to \"%s\"\n", 5862306a36Sopenharmony_ci kobject_name(kobj), name); 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci return name; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ciint __of_add_property_sysfs(struct device_node *np, struct property *pp) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci int rc; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* Important: Don't leak passwords */ 6862306a36Sopenharmony_ci bool secure = strncmp(pp->name, "security-", 9) == 0; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_SYSFS)) 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (!of_kset || !of_node_is_attached(np)) 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci sysfs_bin_attr_init(&pp->attr); 7762306a36Sopenharmony_ci pp->attr.attr.name = safe_name(&np->kobj, pp->name); 7862306a36Sopenharmony_ci pp->attr.attr.mode = secure ? 0400 : 0444; 7962306a36Sopenharmony_ci pp->attr.size = secure ? 0 : pp->length; 8062306a36Sopenharmony_ci pp->attr.read = of_node_property_read; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci rc = sysfs_create_bin_file(&np->kobj, &pp->attr); 8362306a36Sopenharmony_ci WARN(rc, "error adding attribute %s to node %pOF\n", pp->name, np); 8462306a36Sopenharmony_ci return rc; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_civoid __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_SYSFS)) 9062306a36Sopenharmony_ci return; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci sysfs_remove_bin_file(&np->kobj, &prop->attr); 9362306a36Sopenharmony_ci kfree(prop->attr.attr.name); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_civoid __of_remove_property_sysfs(struct device_node *np, struct property *prop) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci /* at early boot, bail here and defer setup to of_init() */ 9962306a36Sopenharmony_ci if (of_kset && of_node_is_attached(np)) 10062306a36Sopenharmony_ci __of_sysfs_remove_bin_file(np, prop); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_civoid __of_update_property_sysfs(struct device_node *np, struct property *newprop, 10462306a36Sopenharmony_ci struct property *oldprop) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci /* At early boot, bail out and defer setup to of_init() */ 10762306a36Sopenharmony_ci if (!of_kset) 10862306a36Sopenharmony_ci return; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (oldprop) 11162306a36Sopenharmony_ci __of_sysfs_remove_bin_file(np, oldprop); 11262306a36Sopenharmony_ci __of_add_property_sysfs(np, newprop); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ciint __of_attach_node_sysfs(struct device_node *np) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci const char *name; 11862306a36Sopenharmony_ci struct kobject *parent; 11962306a36Sopenharmony_ci struct property *pp; 12062306a36Sopenharmony_ci int rc; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_SYSFS) || !of_kset) 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci np->kobj.kset = of_kset; 12662306a36Sopenharmony_ci if (!np->parent) { 12762306a36Sopenharmony_ci /* Nodes without parents are new top level trees */ 12862306a36Sopenharmony_ci name = safe_name(&of_kset->kobj, "base"); 12962306a36Sopenharmony_ci parent = NULL; 13062306a36Sopenharmony_ci } else { 13162306a36Sopenharmony_ci name = safe_name(&np->parent->kobj, kbasename(np->full_name)); 13262306a36Sopenharmony_ci parent = &np->parent->kobj; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci if (!name) 13562306a36Sopenharmony_ci return -ENOMEM; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci rc = kobject_add(&np->kobj, parent, "%s", name); 13862306a36Sopenharmony_ci kfree(name); 13962306a36Sopenharmony_ci if (rc) 14062306a36Sopenharmony_ci return rc; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci for_each_property_of_node(np, pp) 14362306a36Sopenharmony_ci __of_add_property_sysfs(np, pp); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci of_node_get(np); 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_civoid __of_detach_node_sysfs(struct device_node *np) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct property *pp; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci BUG_ON(!of_node_is_initialized(np)); 15462306a36Sopenharmony_ci if (!of_kset) 15562306a36Sopenharmony_ci return; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* only remove properties if on sysfs */ 15862306a36Sopenharmony_ci if (of_node_is_attached(np)) { 15962306a36Sopenharmony_ci for_each_property_of_node(np, pp) 16062306a36Sopenharmony_ci __of_sysfs_remove_bin_file(np, pp); 16162306a36Sopenharmony_ci kobject_del(&np->kobj); 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci of_node_put(np); 16562306a36Sopenharmony_ci} 166