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