diff options
author | Frank Rowand <frank.rowand@sony.com> | 2017-07-19 12:25:22 -0400 |
---|---|---|
committer | Rob Herring <robh@kernel.org> | 2017-07-20 10:40:05 -0400 |
commit | d1651b03c2df75db8eda3fbcd3a07adb337ee8b0 (patch) | |
tree | 56a8aab2aaac0b3c63383ec984167442fd4ecdfb | |
parent | c1cd1e01fece0c139a7946c14b788f887d8b658a (diff) |
of: overlay: add overlay symbols to live device tree
Add overlay __symbols__ properties to live tree when an overlay
is added to the live tree so that the symbols are available to
subsequent overlays.
Expected test result is new __symbols__ entries for labels from
the overlay after this commit.
Before this commit:
Console error message near end of unittest:
### dt-test ### FAIL of_unittest_overlay_high_level():2296 Adding overlay 'overlay_bad_symbol' failed
### dt-test ### end of unittest - 190 passed, 1 failed
The new unittest "fails" because the expected result of loading the
new overlay is an error instead of success.
$ # node hvac-medium-2 exists because the overlay loaded
$ # since the duplicate symbol was not detected
$ cd /proc/device-tree/testcase-data-2/substation@100/
$ ls
compatible hvac-medium-2 motor-8 reg
hvac-large-1 linux,phandle name status
hvac-medium-1 motor-1 phandle
$ cd /proc/device-tree/__symbols__/
$ ls
electric_1 lights_1 name rides_1 spin_ctrl_2
hvac_1 lights_2 retail_1 spin_ctrl_1
After this commit:
Previous console error message no longer occurs, but expected error
occurs:
OF: overlay: Failed to apply prop @/__symbols__/hvac_1
OF: overlay: apply failed '/__symbols__'
### dt-test ### end of unittest - 191 passed, 0 failed
$ # node hvac-medium-2 does not exist because the overlay
$ # properly failed to load due to the duplicate symbol
$ cd /proc/device-tree/testcase-data-2/substation@100/
$ ls
compatible hvac-medium-1 motor-1 name reg
hvac-large-1 linux,phandle motor-8 phandle status
$ cd /proc/device-tree/__symbols__/
$ ls
electric_1 lights_1 retail_1 ride_200_right spin_ctrl_2
hvac_1 lights_2 ride_200 rides_1
hvac_2 name ride_200_left spin_ctrl_1
$ cat ride_200; echo
/testcase-data-2/fairway-1/ride@200
$ cat ride_200_left ; echo
/testcase-data-2/fairway-1/ride@200/track@10
$ cat ride_200_right ; echo
/testcase-data-2/fairway-1/ride@200/track@20
Signed-off-by: Frank Rowand <frank.rowand@sony.com>
Signed-off-by: Rob Herring <robh@kernel.org>
-rw-r--r-- | drivers/of/overlay.c | 116 |
1 files changed, 107 insertions, 9 deletions
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index fbe1980accb6..8ecfee31ab6d 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c | |||
@@ -35,6 +35,7 @@ | |||
35 | struct of_overlay_info { | 35 | struct of_overlay_info { |
36 | struct device_node *target; | 36 | struct device_node *target; |
37 | struct device_node *overlay; | 37 | struct device_node *overlay; |
38 | bool is_symbols_node; | ||
38 | }; | 39 | }; |
39 | 40 | ||
40 | /** | 41 | /** |
@@ -55,7 +56,8 @@ struct of_overlay { | |||
55 | }; | 56 | }; |
56 | 57 | ||
57 | static int of_overlay_apply_one(struct of_overlay *ov, | 58 | static int of_overlay_apply_one(struct of_overlay *ov, |
58 | struct device_node *target, const struct device_node *overlay); | 59 | struct device_node *target, const struct device_node *overlay, |
60 | bool is_symbols_node); | ||
59 | 61 | ||
60 | static BLOCKING_NOTIFIER_HEAD(of_overlay_chain); | 62 | static BLOCKING_NOTIFIER_HEAD(of_overlay_chain); |
61 | 63 | ||
@@ -92,10 +94,74 @@ static int of_overlay_notify(struct of_overlay *ov, | |||
92 | return 0; | 94 | return 0; |
93 | } | 95 | } |
94 | 96 | ||
97 | static struct property *dup_and_fixup_symbol_prop(struct of_overlay *ov, | ||
98 | const struct property *prop) | ||
99 | { | ||
100 | struct of_overlay_info *ovinfo; | ||
101 | struct property *new; | ||
102 | const char *overlay_name; | ||
103 | char *label_path; | ||
104 | char *symbol_path; | ||
105 | const char *target_path; | ||
106 | int k; | ||
107 | int label_path_len; | ||
108 | int overlay_name_len; | ||
109 | int target_path_len; | ||
110 | |||
111 | if (!prop->value) | ||
112 | return NULL; | ||
113 | symbol_path = prop->value; | ||
114 | |||
115 | new = kzalloc(sizeof(*new), GFP_KERNEL); | ||
116 | if (!new) | ||
117 | return NULL; | ||
118 | |||
119 | for (k = 0; k < ov->count; k++) { | ||
120 | ovinfo = &ov->ovinfo_tab[k]; | ||
121 | overlay_name = ovinfo->overlay->full_name; | ||
122 | overlay_name_len = strlen(overlay_name); | ||
123 | if (!strncasecmp(symbol_path, overlay_name, overlay_name_len)) | ||
124 | break; | ||
125 | } | ||
126 | |||
127 | if (k >= ov->count) | ||
128 | goto err_free; | ||
129 | |||
130 | target_path = ovinfo->target->full_name; | ||
131 | target_path_len = strlen(target_path); | ||
132 | |||
133 | label_path = symbol_path + overlay_name_len; | ||
134 | label_path_len = strlen(label_path); | ||
135 | |||
136 | new->name = kstrdup(prop->name, GFP_KERNEL); | ||
137 | new->length = target_path_len + label_path_len + 1; | ||
138 | new->value = kzalloc(new->length, GFP_KERNEL); | ||
139 | |||
140 | if (!new->name || !new->value) | ||
141 | goto err_free; | ||
142 | |||
143 | strcpy(new->value, target_path); | ||
144 | strcpy(new->value + target_path_len, label_path); | ||
145 | |||
146 | /* mark the property as dynamic */ | ||
147 | of_property_set_flag(new, OF_DYNAMIC); | ||
148 | |||
149 | return new; | ||
150 | |||
151 | err_free: | ||
152 | kfree(new->name); | ||
153 | kfree(new->value); | ||
154 | kfree(new); | ||
155 | return NULL; | ||
156 | |||
157 | |||
158 | } | ||
159 | |||
95 | static int of_overlay_apply_single_property(struct of_overlay *ov, | 160 | static int of_overlay_apply_single_property(struct of_overlay *ov, |
96 | struct device_node *target, struct property *prop) | 161 | struct device_node *target, struct property *prop, |
162 | bool is_symbols_node) | ||
97 | { | 163 | { |
98 | struct property *propn, *tprop; | 164 | struct property *propn = NULL, *tprop; |
99 | 165 | ||
100 | /* NOTE: Multiple changes of single properties not supported */ | 166 | /* NOTE: Multiple changes of single properties not supported */ |
101 | tprop = of_find_property(target, prop->name, NULL); | 167 | tprop = of_find_property(target, prop->name, NULL); |
@@ -106,7 +172,15 @@ static int of_overlay_apply_single_property(struct of_overlay *ov, | |||
106 | of_prop_cmp(prop->name, "linux,phandle") == 0) | 172 | of_prop_cmp(prop->name, "linux,phandle") == 0) |
107 | return 0; | 173 | return 0; |
108 | 174 | ||
109 | propn = __of_prop_dup(prop, GFP_KERNEL); | 175 | if (is_symbols_node) { |
176 | /* changing a property in __symbols__ node not allowed */ | ||
177 | if (tprop) | ||
178 | return -EINVAL; | ||
179 | propn = dup_and_fixup_symbol_prop(ov, prop); | ||
180 | } else { | ||
181 | propn = __of_prop_dup(prop, GFP_KERNEL); | ||
182 | } | ||
183 | |||
110 | if (propn == NULL) | 184 | if (propn == NULL) |
111 | return -ENOMEM; | 185 | return -ENOMEM; |
112 | 186 | ||
@@ -140,7 +214,7 @@ static int of_overlay_apply_single_device_node(struct of_overlay *ov, | |||
140 | return -EINVAL; | 214 | return -EINVAL; |
141 | 215 | ||
142 | /* apply overlay recursively */ | 216 | /* apply overlay recursively */ |
143 | ret = of_overlay_apply_one(ov, tchild, child); | 217 | ret = of_overlay_apply_one(ov, tchild, child, 0); |
144 | of_node_put(tchild); | 218 | of_node_put(tchild); |
145 | } else { | 219 | } else { |
146 | /* create empty tree as a target */ | 220 | /* create empty tree as a target */ |
@@ -155,7 +229,7 @@ static int of_overlay_apply_single_device_node(struct of_overlay *ov, | |||
155 | if (ret) | 229 | if (ret) |
156 | return ret; | 230 | return ret; |
157 | 231 | ||
158 | ret = of_overlay_apply_one(ov, tchild, child); | 232 | ret = of_overlay_apply_one(ov, tchild, child, 0); |
159 | if (ret) | 233 | if (ret) |
160 | return ret; | 234 | return ret; |
161 | } | 235 | } |
@@ -171,14 +245,16 @@ static int of_overlay_apply_single_device_node(struct of_overlay *ov, | |||
171 | * by using the changeset. | 245 | * by using the changeset. |
172 | */ | 246 | */ |
173 | static int of_overlay_apply_one(struct of_overlay *ov, | 247 | static int of_overlay_apply_one(struct of_overlay *ov, |
174 | struct device_node *target, const struct device_node *overlay) | 248 | struct device_node *target, const struct device_node *overlay, |
249 | bool is_symbols_node) | ||
175 | { | 250 | { |
176 | struct device_node *child; | 251 | struct device_node *child; |
177 | struct property *prop; | 252 | struct property *prop; |
178 | int ret; | 253 | int ret; |
179 | 254 | ||
180 | for_each_property_of_node(overlay, prop) { | 255 | for_each_property_of_node(overlay, prop) { |
181 | ret = of_overlay_apply_single_property(ov, target, prop); | 256 | ret = of_overlay_apply_single_property(ov, target, prop, |
257 | is_symbols_node); | ||
182 | if (ret) { | 258 | if (ret) { |
183 | pr_err("Failed to apply prop @%pOF/%s\n", | 259 | pr_err("Failed to apply prop @%pOF/%s\n", |
184 | target, prop->name); | 260 | target, prop->name); |
@@ -186,6 +262,10 @@ static int of_overlay_apply_one(struct of_overlay *ov, | |||
186 | } | 262 | } |
187 | } | 263 | } |
188 | 264 | ||
265 | /* do not allow symbols node to have any children */ | ||
266 | if (is_symbols_node) | ||
267 | return 0; | ||
268 | |||
189 | for_each_child_of_node(overlay, child) { | 269 | for_each_child_of_node(overlay, child) { |
190 | ret = of_overlay_apply_single_device_node(ov, target, child); | 270 | ret = of_overlay_apply_single_device_node(ov, target, child); |
191 | if (ret != 0) { | 271 | if (ret != 0) { |
@@ -216,7 +296,8 @@ static int of_overlay_apply(struct of_overlay *ov) | |||
216 | for (i = 0; i < ov->count; i++) { | 296 | for (i = 0; i < ov->count; i++) { |
217 | struct of_overlay_info *ovinfo = &ov->ovinfo_tab[i]; | 297 | struct of_overlay_info *ovinfo = &ov->ovinfo_tab[i]; |
218 | 298 | ||
219 | err = of_overlay_apply_one(ov, ovinfo->target, ovinfo->overlay); | 299 | err = of_overlay_apply_one(ov, ovinfo->target, ovinfo->overlay, |
300 | ovinfo->is_symbols_node); | ||
220 | if (err != 0) { | 301 | if (err != 0) { |
221 | pr_err("apply failed '%pOF'\n", ovinfo->target); | 302 | pr_err("apply failed '%pOF'\n", ovinfo->target); |
222 | return err; | 303 | return err; |
@@ -314,6 +395,9 @@ static int of_build_overlay_info(struct of_overlay *ov, | |||
314 | for_each_child_of_node(tree, node) | 395 | for_each_child_of_node(tree, node) |
315 | cnt++; | 396 | cnt++; |
316 | 397 | ||
398 | if (of_get_child_by_name(tree, "__symbols__")) | ||
399 | cnt++; | ||
400 | |||
317 | ovinfo = kcalloc(cnt, sizeof(*ovinfo), GFP_KERNEL); | 401 | ovinfo = kcalloc(cnt, sizeof(*ovinfo), GFP_KERNEL); |
318 | if (ovinfo == NULL) | 402 | if (ovinfo == NULL) |
319 | return -ENOMEM; | 403 | return -ENOMEM; |
@@ -325,6 +409,20 @@ static int of_build_overlay_info(struct of_overlay *ov, | |||
325 | cnt++; | 409 | cnt++; |
326 | } | 410 | } |
327 | 411 | ||
412 | node = of_get_child_by_name(tree, "__symbols__"); | ||
413 | if (node) { | ||
414 | ovinfo[cnt].overlay = node; | ||
415 | ovinfo[cnt].target = of_find_node_by_path("/__symbols__"); | ||
416 | ovinfo[cnt].is_symbols_node = 1; | ||
417 | |||
418 | if (!ovinfo[cnt].target) { | ||
419 | pr_err("no symbols in root of device tree.\n"); | ||
420 | return -EINVAL; | ||
421 | } | ||
422 | |||
423 | cnt++; | ||
424 | } | ||
425 | |||
328 | /* if nothing filled, return error */ | 426 | /* if nothing filled, return error */ |
329 | if (cnt == 0) { | 427 | if (cnt == 0) { |
330 | kfree(ovinfo); | 428 | kfree(ovinfo); |