18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/of.h>
38c2ecf20Sopenharmony_ci#include <linux/slab.h>
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include "of_private.h"
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci/* true when node is initialized */
88c2ecf20Sopenharmony_cistatic int of_node_is_initialized(struct device_node *node)
98c2ecf20Sopenharmony_ci{
108c2ecf20Sopenharmony_ci	return node && node->kobj.state_initialized;
118c2ecf20Sopenharmony_ci}
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/* true when node is attached (i.e. present on sysfs) */
148c2ecf20Sopenharmony_ciint of_node_is_attached(struct device_node *node)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	return node && node->kobj.state_in_sysfs;
178c2ecf20Sopenharmony_ci}
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#ifndef CONFIG_OF_DYNAMIC
218c2ecf20Sopenharmony_cistatic void of_node_release(struct kobject *kobj)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	/* Without CONFIG_OF_DYNAMIC, no nodes gets freed */
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci#endif /* CONFIG_OF_DYNAMIC */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct kobj_type of_node_ktype = {
288c2ecf20Sopenharmony_ci	.release = of_node_release,
298c2ecf20Sopenharmony_ci};
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic ssize_t of_node_property_read(struct file *filp, struct kobject *kobj,
328c2ecf20Sopenharmony_ci				struct bin_attribute *bin_attr, char *buf,
338c2ecf20Sopenharmony_ci				loff_t offset, size_t count)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct property *pp = container_of(bin_attr, struct property, attr);
368c2ecf20Sopenharmony_ci	return memory_read_from_buffer(buf, count, &offset, pp->value, pp->length);
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/* always return newly allocated name, caller must free after use */
408c2ecf20Sopenharmony_cistatic const char *safe_name(struct kobject *kobj, const char *orig_name)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	const char *name = orig_name;
438c2ecf20Sopenharmony_ci	struct kernfs_node *kn;
448c2ecf20Sopenharmony_ci	int i = 0;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	/* don't be a hero. After 16 tries give up */
478c2ecf20Sopenharmony_ci	while (i < 16 && (kn = sysfs_get_dirent(kobj->sd, name))) {
488c2ecf20Sopenharmony_ci		sysfs_put(kn);
498c2ecf20Sopenharmony_ci		if (name != orig_name)
508c2ecf20Sopenharmony_ci			kfree(name);
518c2ecf20Sopenharmony_ci		name = kasprintf(GFP_KERNEL, "%s#%i", orig_name, ++i);
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (name == orig_name) {
558c2ecf20Sopenharmony_ci		name = kstrdup(orig_name, GFP_KERNEL);
568c2ecf20Sopenharmony_ci	} else {
578c2ecf20Sopenharmony_ci		pr_warn("Duplicate name in %s, renamed to \"%s\"\n",
588c2ecf20Sopenharmony_ci			kobject_name(kobj), name);
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci	return name;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ciint __of_add_property_sysfs(struct device_node *np, struct property *pp)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	int rc;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	/* Important: Don't leak passwords */
688c2ecf20Sopenharmony_ci	bool secure = strncmp(pp->name, "security-", 9) == 0;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_SYSFS))
718c2ecf20Sopenharmony_ci		return 0;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (!of_kset || !of_node_is_attached(np))
748c2ecf20Sopenharmony_ci		return 0;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	sysfs_bin_attr_init(&pp->attr);
778c2ecf20Sopenharmony_ci	pp->attr.attr.name = safe_name(&np->kobj, pp->name);
788c2ecf20Sopenharmony_ci	pp->attr.attr.mode = secure ? 0400 : 0444;
798c2ecf20Sopenharmony_ci	pp->attr.size = secure ? 0 : pp->length;
808c2ecf20Sopenharmony_ci	pp->attr.read = of_node_property_read;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	rc = sysfs_create_bin_file(&np->kobj, &pp->attr);
838c2ecf20Sopenharmony_ci	WARN(rc, "error adding attribute %s to node %pOF\n", pp->name, np);
848c2ecf20Sopenharmony_ci	return rc;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_civoid __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_SYSFS))
908c2ecf20Sopenharmony_ci		return;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	sysfs_remove_bin_file(&np->kobj, &prop->attr);
938c2ecf20Sopenharmony_ci	kfree(prop->attr.attr.name);
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_civoid __of_remove_property_sysfs(struct device_node *np, struct property *prop)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	/* at early boot, bail here and defer setup to of_init() */
998c2ecf20Sopenharmony_ci	if (of_kset && of_node_is_attached(np))
1008c2ecf20Sopenharmony_ci		__of_sysfs_remove_bin_file(np, prop);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_civoid __of_update_property_sysfs(struct device_node *np, struct property *newprop,
1048c2ecf20Sopenharmony_ci		struct property *oldprop)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	/* At early boot, bail out and defer setup to of_init() */
1078c2ecf20Sopenharmony_ci	if (!of_kset)
1088c2ecf20Sopenharmony_ci		return;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (oldprop)
1118c2ecf20Sopenharmony_ci		__of_sysfs_remove_bin_file(np, oldprop);
1128c2ecf20Sopenharmony_ci	__of_add_property_sysfs(np, newprop);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ciint __of_attach_node_sysfs(struct device_node *np)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	const char *name;
1188c2ecf20Sopenharmony_ci	struct kobject *parent;
1198c2ecf20Sopenharmony_ci	struct property *pp;
1208c2ecf20Sopenharmony_ci	int rc;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_SYSFS) || !of_kset)
1238c2ecf20Sopenharmony_ci		return 0;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	np->kobj.kset = of_kset;
1268c2ecf20Sopenharmony_ci	if (!np->parent) {
1278c2ecf20Sopenharmony_ci		/* Nodes without parents are new top level trees */
1288c2ecf20Sopenharmony_ci		name = safe_name(&of_kset->kobj, "base");
1298c2ecf20Sopenharmony_ci		parent = NULL;
1308c2ecf20Sopenharmony_ci	} else {
1318c2ecf20Sopenharmony_ci		name = safe_name(&np->parent->kobj, kbasename(np->full_name));
1328c2ecf20Sopenharmony_ci		parent = &np->parent->kobj;
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci	if (!name)
1358c2ecf20Sopenharmony_ci		return -ENOMEM;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	rc = kobject_add(&np->kobj, parent, "%s", name);
1388c2ecf20Sopenharmony_ci	kfree(name);
1398c2ecf20Sopenharmony_ci	if (rc)
1408c2ecf20Sopenharmony_ci		return rc;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	for_each_property_of_node(np, pp)
1438c2ecf20Sopenharmony_ci		__of_add_property_sysfs(np, pp);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	of_node_get(np);
1468c2ecf20Sopenharmony_ci	return 0;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_civoid __of_detach_node_sysfs(struct device_node *np)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct property *pp;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	BUG_ON(!of_node_is_initialized(np));
1548c2ecf20Sopenharmony_ci	if (!of_kset)
1558c2ecf20Sopenharmony_ci		return;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/* only remove properties if on sysfs */
1588c2ecf20Sopenharmony_ci	if (of_node_is_attached(np)) {
1598c2ecf20Sopenharmony_ci		for_each_property_of_node(np, pp)
1608c2ecf20Sopenharmony_ci			__of_sysfs_remove_bin_file(np, pp);
1618c2ecf20Sopenharmony_ci		kobject_del(&np->kobj);
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	of_node_put(np);
1658c2ecf20Sopenharmony_ci}
166