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); |
