.. | .. |
---|
49 | 49 | * @overlay: pointer to the __overlay__ node |
---|
50 | 50 | */ |
---|
51 | 51 | struct fragment { |
---|
52 | | - struct device_node *target; |
---|
53 | 52 | struct device_node *overlay; |
---|
| 53 | + struct device_node *target; |
---|
54 | 54 | }; |
---|
55 | 55 | |
---|
56 | 56 | /** |
---|
.. | .. |
---|
170 | 170 | |
---|
171 | 171 | ret = blocking_notifier_call_chain(&overlay_notify_chain, |
---|
172 | 172 | action, &nd); |
---|
173 | | - if (ret == NOTIFY_OK || ret == NOTIFY_STOP) |
---|
174 | | - return 0; |
---|
175 | | - if (ret) { |
---|
| 173 | + if (notifier_to_errno(ret)) { |
---|
176 | 174 | ret = notifier_to_errno(ret); |
---|
177 | 175 | pr_err("overlay changeset %s notifier error %d, target: %pOF\n", |
---|
178 | 176 | of_overlay_action_name[action], ret, nd.target); |
---|
.. | .. |
---|
402 | 400 | * a live devicetree created from Open Firmware. |
---|
403 | 401 | * |
---|
404 | 402 | * NOTE_2: Multiple mods of created nodes not supported. |
---|
405 | | - * If more than one fragment contains a node that does not already exist |
---|
406 | | - * in the live tree, then for each fragment of_changeset_attach_node() |
---|
407 | | - * will add a changeset entry to add the node. When the changeset is |
---|
408 | | - * applied, __of_attach_node() will attach the node twice (once for |
---|
409 | | - * each fragment). At this point the device tree will be corrupted. |
---|
410 | | - * |
---|
411 | | - * TODO: add integrity check to ensure that multiple fragments do not |
---|
412 | | - * create the same node. |
---|
413 | 403 | * |
---|
414 | 404 | * Returns 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if |
---|
415 | 405 | * invalid @overlay. |
---|
.. | .. |
---|
436 | 426 | |
---|
437 | 427 | tchild->parent = target->np; |
---|
438 | 428 | tchild->name = __of_get_property(node, "name", NULL); |
---|
439 | | - tchild->type = __of_get_property(node, "device_type", NULL); |
---|
440 | 429 | |
---|
441 | 430 | if (!tchild->name) |
---|
442 | 431 | tchild->name = "<NULL>"; |
---|
443 | | - if (!tchild->type) |
---|
444 | | - tchild->type = "<NULL>"; |
---|
445 | 432 | |
---|
446 | 433 | /* ignore obsolete "linux,phandle" */ |
---|
447 | 434 | phandle = __of_get_property(node, "phandle", &size); |
---|
.. | .. |
---|
531 | 518 | for_each_property_of_node(overlay_symbols_node, prop) { |
---|
532 | 519 | ret = add_changeset_property(ovcs, target, prop, 1); |
---|
533 | 520 | if (ret) { |
---|
534 | | - pr_debug("Failed to apply prop @%pOF/%s, err=%d\n", |
---|
| 521 | + pr_debug("Failed to apply symbols prop @%pOF/%s, err=%d\n", |
---|
535 | 522 | target->np, prop->name, ret); |
---|
536 | 523 | return ret; |
---|
537 | 524 | } |
---|
538 | 525 | } |
---|
539 | 526 | |
---|
540 | 527 | return 0; |
---|
| 528 | +} |
---|
| 529 | + |
---|
| 530 | +static int find_dup_cset_node_entry(struct overlay_changeset *ovcs, |
---|
| 531 | + struct of_changeset_entry *ce_1) |
---|
| 532 | +{ |
---|
| 533 | + struct of_changeset_entry *ce_2; |
---|
| 534 | + char *fn_1, *fn_2; |
---|
| 535 | + int node_path_match; |
---|
| 536 | + |
---|
| 537 | + if (ce_1->action != OF_RECONFIG_ATTACH_NODE && |
---|
| 538 | + ce_1->action != OF_RECONFIG_DETACH_NODE) |
---|
| 539 | + return 0; |
---|
| 540 | + |
---|
| 541 | + ce_2 = ce_1; |
---|
| 542 | + list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) { |
---|
| 543 | + if ((ce_2->action != OF_RECONFIG_ATTACH_NODE && |
---|
| 544 | + ce_2->action != OF_RECONFIG_DETACH_NODE) || |
---|
| 545 | + of_node_cmp(ce_1->np->full_name, ce_2->np->full_name)) |
---|
| 546 | + continue; |
---|
| 547 | + |
---|
| 548 | + fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np); |
---|
| 549 | + fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np); |
---|
| 550 | + node_path_match = !strcmp(fn_1, fn_2); |
---|
| 551 | + kfree(fn_1); |
---|
| 552 | + kfree(fn_2); |
---|
| 553 | + if (node_path_match) { |
---|
| 554 | + pr_err("ERROR: multiple fragments add and/or delete node %pOF\n", |
---|
| 555 | + ce_1->np); |
---|
| 556 | + return -EINVAL; |
---|
| 557 | + } |
---|
| 558 | + } |
---|
| 559 | + |
---|
| 560 | + return 0; |
---|
| 561 | +} |
---|
| 562 | + |
---|
| 563 | +static int find_dup_cset_prop(struct overlay_changeset *ovcs, |
---|
| 564 | + struct of_changeset_entry *ce_1) |
---|
| 565 | +{ |
---|
| 566 | + struct of_changeset_entry *ce_2; |
---|
| 567 | + char *fn_1, *fn_2; |
---|
| 568 | + int node_path_match; |
---|
| 569 | + |
---|
| 570 | + if (ce_1->action != OF_RECONFIG_ADD_PROPERTY && |
---|
| 571 | + ce_1->action != OF_RECONFIG_REMOVE_PROPERTY && |
---|
| 572 | + ce_1->action != OF_RECONFIG_UPDATE_PROPERTY) |
---|
| 573 | + return 0; |
---|
| 574 | + |
---|
| 575 | + ce_2 = ce_1; |
---|
| 576 | + list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) { |
---|
| 577 | + if ((ce_2->action != OF_RECONFIG_ADD_PROPERTY && |
---|
| 578 | + ce_2->action != OF_RECONFIG_REMOVE_PROPERTY && |
---|
| 579 | + ce_2->action != OF_RECONFIG_UPDATE_PROPERTY) || |
---|
| 580 | + of_node_cmp(ce_1->np->full_name, ce_2->np->full_name)) |
---|
| 581 | + continue; |
---|
| 582 | + |
---|
| 583 | + fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np); |
---|
| 584 | + fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np); |
---|
| 585 | + node_path_match = !strcmp(fn_1, fn_2); |
---|
| 586 | + kfree(fn_1); |
---|
| 587 | + kfree(fn_2); |
---|
| 588 | + if (node_path_match && |
---|
| 589 | + !of_prop_cmp(ce_1->prop->name, ce_2->prop->name)) { |
---|
| 590 | + pr_err("ERROR: multiple fragments add, update, and/or delete property %pOF/%s\n", |
---|
| 591 | + ce_1->np, ce_1->prop->name); |
---|
| 592 | + return -EINVAL; |
---|
| 593 | + } |
---|
| 594 | + } |
---|
| 595 | + |
---|
| 596 | + return 0; |
---|
| 597 | +} |
---|
| 598 | + |
---|
| 599 | +/** |
---|
| 600 | + * changeset_dup_entry_check() - check for duplicate entries |
---|
| 601 | + * @ovcs: Overlay changeset |
---|
| 602 | + * |
---|
| 603 | + * Check changeset @ovcs->cset for multiple {add or delete} node entries for |
---|
| 604 | + * the same node or duplicate {add, delete, or update} properties entries |
---|
| 605 | + * for the same property. |
---|
| 606 | + * |
---|
| 607 | + * Returns 0 on success, or -EINVAL if duplicate changeset entry found. |
---|
| 608 | + */ |
---|
| 609 | +static int changeset_dup_entry_check(struct overlay_changeset *ovcs) |
---|
| 610 | +{ |
---|
| 611 | + struct of_changeset_entry *ce_1; |
---|
| 612 | + int dup_entry = 0; |
---|
| 613 | + |
---|
| 614 | + list_for_each_entry(ce_1, &ovcs->cset.entries, node) { |
---|
| 615 | + dup_entry |= find_dup_cset_node_entry(ovcs, ce_1); |
---|
| 616 | + dup_entry |= find_dup_cset_prop(ovcs, ce_1); |
---|
| 617 | + } |
---|
| 618 | + |
---|
| 619 | + return dup_entry ? -EINVAL : 0; |
---|
541 | 620 | } |
---|
542 | 621 | |
---|
543 | 622 | /** |
---|
.. | .. |
---|
575 | 654 | ret = build_changeset_next_level(ovcs, &target, |
---|
576 | 655 | fragment->overlay); |
---|
577 | 656 | if (ret) { |
---|
578 | | - pr_debug("apply failed '%pOF'\n", fragment->target); |
---|
| 657 | + pr_debug("fragment apply failed '%pOF'\n", |
---|
| 658 | + fragment->target); |
---|
579 | 659 | return ret; |
---|
580 | 660 | } |
---|
581 | 661 | } |
---|
.. | .. |
---|
588 | 668 | ret = build_changeset_symbols_node(ovcs, &target, |
---|
589 | 669 | fragment->overlay); |
---|
590 | 670 | if (ret) { |
---|
591 | | - pr_debug("apply failed '%pOF'\n", fragment->target); |
---|
| 671 | + pr_debug("symbols fragment apply failed '%pOF'\n", |
---|
| 672 | + fragment->target); |
---|
592 | 673 | return ret; |
---|
593 | 674 | } |
---|
594 | 675 | } |
---|
595 | 676 | |
---|
596 | | - return 0; |
---|
| 677 | + return changeset_dup_entry_check(ovcs); |
---|
597 | 678 | } |
---|
598 | 679 | |
---|
599 | 680 | /* |
---|
.. | .. |
---|
713 | 794 | if (!fragment->target) { |
---|
714 | 795 | of_node_put(fragment->overlay); |
---|
715 | 796 | ret = -EINVAL; |
---|
| 797 | + of_node_put(node); |
---|
716 | 798 | goto err_free_fragments; |
---|
717 | 799 | } |
---|
718 | 800 | |
---|
.. | .. |
---|
893 | 975 | goto err_free_overlay_changeset; |
---|
894 | 976 | } |
---|
895 | 977 | |
---|
896 | | - of_populate_phandle_cache(); |
---|
897 | | - |
---|
898 | 978 | ret = __of_changeset_apply_notify(&ovcs->cset); |
---|
899 | 979 | if (ret) |
---|
900 | | - pr_err("overlay changeset entry notify error %d\n", ret); |
---|
| 980 | + pr_err("overlay apply changeset entry notify error %d\n", ret); |
---|
901 | 981 | /* notify failure is not fatal, continue */ |
---|
902 | 982 | |
---|
903 | 983 | list_add_tail(&ovcs->ovcs_list, &ovcs_list); |
---|
.. | .. |
---|
1137 | 1217 | |
---|
1138 | 1218 | list_del(&ovcs->ovcs_list); |
---|
1139 | 1219 | |
---|
1140 | | - /* |
---|
1141 | | - * Disable phandle cache. Avoids race condition that would arise |
---|
1142 | | - * from removing cache entry when the associated node is deleted. |
---|
1143 | | - */ |
---|
1144 | | - of_free_phandle_cache(); |
---|
1145 | | - |
---|
1146 | 1220 | ret_apply = 0; |
---|
1147 | 1221 | ret = __of_changeset_revert_entries(&ovcs->cset, &ret_apply); |
---|
1148 | | - |
---|
1149 | | - of_populate_phandle_cache(); |
---|
1150 | | - |
---|
1151 | 1222 | if (ret) { |
---|
1152 | 1223 | if (ret_apply) |
---|
1153 | 1224 | devicetree_state_flags |= DTSF_REVERT_FAIL; |
---|
.. | .. |
---|
1156 | 1227 | |
---|
1157 | 1228 | ret = __of_changeset_revert_notify(&ovcs->cset); |
---|
1158 | 1229 | if (ret) |
---|
1159 | | - pr_err("overlay changeset entry notify error %d\n", ret); |
---|
| 1230 | + pr_err("overlay remove changeset entry notify error %d\n", ret); |
---|
1160 | 1231 | /* notify failure is not fatal, continue */ |
---|
1161 | 1232 | |
---|
1162 | 1233 | *ovcs_id = 0; |
---|