1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/of.h> |
3 | #include <linux/slab.h> |
4 | |
5 | #include "of_private.h" |
6 | |
7 | /* true when node is initialized */ |
8 | static int of_node_is_initialized(const struct device_node *node) |
9 | { |
10 | return node && node->kobj.state_initialized; |
11 | } |
12 | |
13 | /* true when node is attached (i.e. present on sysfs) */ |
14 | int of_node_is_attached(const struct device_node *node) |
15 | { |
16 | return node && node->kobj.state_in_sysfs; |
17 | } |
18 | |
19 | |
20 | #ifndef CONFIG_OF_DYNAMIC |
21 | static void of_node_release(struct kobject *kobj) |
22 | { |
23 | /* Without CONFIG_OF_DYNAMIC, no nodes gets freed */ |
24 | } |
25 | #endif /* CONFIG_OF_DYNAMIC */ |
26 | |
27 | const struct kobj_type of_node_ktype = { |
28 | .release = of_node_release, |
29 | }; |
30 | |
31 | static ssize_t of_node_property_read(struct file *filp, struct kobject *kobj, |
32 | struct bin_attribute *bin_attr, char *buf, |
33 | loff_t offset, size_t count) |
34 | { |
35 | struct property *pp = container_of(bin_attr, struct property, attr); |
36 | return memory_read_from_buffer(to: buf, count, ppos: &offset, from: pp->value, available: pp->length); |
37 | } |
38 | |
39 | /* always return newly allocated name, caller must free after use */ |
40 | static const char *safe_name(struct kobject *kobj, const char *orig_name) |
41 | { |
42 | const char *name = orig_name; |
43 | struct kernfs_node *kn; |
44 | int i = 0; |
45 | |
46 | /* don't be a hero. After 16 tries give up */ |
47 | while (i < 16 && (kn = sysfs_get_dirent(parent: kobj->sd, name))) { |
48 | sysfs_put(kn); |
49 | if (name != orig_name) |
50 | kfree(objp: name); |
51 | name = kasprintf(GFP_KERNEL, fmt: "%s#%i" , orig_name, ++i); |
52 | } |
53 | |
54 | if (name == orig_name) { |
55 | name = kstrdup(s: orig_name, GFP_KERNEL); |
56 | } else { |
57 | pr_warn("Duplicate name in %s, renamed to \"%s\"\n" , |
58 | kobject_name(kobj), name); |
59 | } |
60 | return name; |
61 | } |
62 | |
63 | int __of_add_property_sysfs(struct device_node *np, struct property *pp) |
64 | { |
65 | int rc; |
66 | |
67 | /* Important: Don't leak passwords */ |
68 | bool secure = strncmp(pp->name, "security-" , 9) == 0; |
69 | |
70 | if (!IS_ENABLED(CONFIG_SYSFS)) |
71 | return 0; |
72 | |
73 | if (!of_kset || !of_node_is_attached(node: np)) |
74 | return 0; |
75 | |
76 | sysfs_bin_attr_init(&pp->attr); |
77 | pp->attr.attr.name = safe_name(kobj: &np->kobj, orig_name: pp->name); |
78 | pp->attr.attr.mode = secure ? 0400 : 0444; |
79 | pp->attr.size = secure ? 0 : pp->length; |
80 | pp->attr.read = of_node_property_read; |
81 | |
82 | rc = sysfs_create_bin_file(kobj: &np->kobj, attr: &pp->attr); |
83 | WARN(rc, "error adding attribute %s to node %pOF\n" , pp->name, np); |
84 | return rc; |
85 | } |
86 | |
87 | void __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop) |
88 | { |
89 | if (!IS_ENABLED(CONFIG_SYSFS)) |
90 | return; |
91 | |
92 | sysfs_remove_bin_file(kobj: &np->kobj, attr: &prop->attr); |
93 | kfree(objp: prop->attr.attr.name); |
94 | } |
95 | |
96 | void __of_remove_property_sysfs(struct device_node *np, struct property *prop) |
97 | { |
98 | /* at early boot, bail here and defer setup to of_init() */ |
99 | if (of_kset && of_node_is_attached(node: np)) |
100 | __of_sysfs_remove_bin_file(np, prop); |
101 | } |
102 | |
103 | void __of_update_property_sysfs(struct device_node *np, struct property *newprop, |
104 | struct property *oldprop) |
105 | { |
106 | /* At early boot, bail out and defer setup to of_init() */ |
107 | if (!of_kset) |
108 | return; |
109 | |
110 | if (oldprop) |
111 | __of_sysfs_remove_bin_file(np, prop: oldprop); |
112 | __of_add_property_sysfs(np, pp: newprop); |
113 | } |
114 | |
115 | int __of_attach_node_sysfs(struct device_node *np) |
116 | { |
117 | const char *name; |
118 | struct kobject *parent; |
119 | struct property *pp; |
120 | int rc; |
121 | |
122 | if (!IS_ENABLED(CONFIG_SYSFS) || !of_kset) |
123 | return 0; |
124 | |
125 | np->kobj.kset = of_kset; |
126 | if (!np->parent) { |
127 | /* Nodes without parents are new top level trees */ |
128 | name = safe_name(kobj: &of_kset->kobj, orig_name: "base" ); |
129 | parent = NULL; |
130 | } else { |
131 | name = safe_name(kobj: &np->parent->kobj, orig_name: kbasename(path: np->full_name)); |
132 | parent = &np->parent->kobj; |
133 | } |
134 | if (!name) |
135 | return -ENOMEM; |
136 | |
137 | rc = kobject_add(kobj: &np->kobj, parent, fmt: "%s" , name); |
138 | kfree(objp: name); |
139 | if (rc) |
140 | return rc; |
141 | |
142 | for_each_property_of_node(np, pp) |
143 | __of_add_property_sysfs(np, pp); |
144 | |
145 | of_node_get(node: np); |
146 | return 0; |
147 | } |
148 | |
149 | void __of_detach_node_sysfs(struct device_node *np) |
150 | { |
151 | struct property *pp; |
152 | |
153 | BUG_ON(!of_node_is_initialized(np)); |
154 | if (!of_kset) |
155 | return; |
156 | |
157 | /* only remove properties if on sysfs */ |
158 | if (of_node_is_attached(node: np)) { |
159 | for_each_property_of_node(np, pp) |
160 | __of_sysfs_remove_bin_file(np, prop: pp); |
161 | kobject_del(kobj: &np->kobj); |
162 | } |
163 | |
164 | of_node_put(node: np); |
165 | } |
166 | |