1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2013 David Chisnall 5 * All rights reserved. 6 * 7 * This software was developed by SRI International and the University of 8 * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 9 * ("CTSRD"), as part of the DARPA CRASH research programme. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $FreeBSD$ 33 */ 34 35 #define __STDC_LIMIT_MACROS 1 36 37 #include "fdt.hh" 38 #include "dtb.hh" 39 40 #include <algorithm> 41 #include <sstream> 42 43 #include <ctype.h> 44 #include <fcntl.h> 45 #include <inttypes.h> 46 #include <libgen.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <unistd.h> 50 #include <sys/types.h> 51 #include <sys/stat.h> 52 #include <errno.h> 53 54 using std::string; 55 56 namespace dtc 57 { 58 59 namespace fdt 60 { 61 62 uint32_t 63 property_value::get_as_uint32() 64 { 65 if (byte_data.size() != 4) 66 { 67 return 0; 68 } 69 uint32_t v = 0; 70 v &= byte_data[0] << 24; 71 v &= byte_data[1] << 16; 72 v &= byte_data[2] << 8; 73 v &= byte_data[3] << 0; 74 return v; 75 } 76 77 void 78 property_value::push_to_buffer(byte_buffer &buffer) 79 { 80 if (!byte_data.empty()) 81 { 82 buffer.insert(buffer.end(), byte_data.begin(), byte_data.end()); 83 } 84 else 85 { 86 push_string(buffer, string_data, true); 87 // Trailing nul 88 buffer.push_back(0); 89 } 90 } 91 92 void 93 property_value::write_dts(FILE *file) 94 { 95 resolve_type(); 96 switch (type) 97 { 98 default: 99 assert(0 && "Invalid type"); 100 case STRING: 101 case STRING_LIST: 102 case CROSS_REFERENCE: 103 write_as_string(file); 104 break; 105 case PHANDLE: 106 write_as_cells(file); 107 break; 108 case BINARY: 109 if (byte_data.size() % 4 == 0) 110 { 111 write_as_cells(file); 112 break; 113 } 114 write_as_bytes(file); 115 break; 116 } 117 } 118 119 void 120 property_value::resolve_type() 121 { 122 if (type != UNKNOWN) 123 { 124 return; 125 } 126 if (byte_data.empty()) 127 { 128 type = STRING; 129 return; 130 } 131 if (byte_data.back() == 0) 132 { 133 bool is_all_printable = true; 134 int nuls = 0; 135 int bytes = 0; 136 bool lastWasNull = false; 137 for (auto i : byte_data) 138 { 139 bytes++; 140 is_all_printable &= (i == '\0') || isprint(i); 141 if (i == '\0') 142 { 143 // If there are two nulls in a row, then we're probably binary. 144 if (lastWasNull) 145 { 146 type = BINARY; 147 return; 148 } 149 nuls++; 150 lastWasNull = true; 151 } 152 else 153 { 154 lastWasNull = false; 155 } 156 if (!is_all_printable) 157 { 158 break; 159 } 160 } 161 if ((is_all_printable && (bytes > nuls)) || bytes == 0) 162 { 163 type = STRING; 164 if (nuls > 1) 165 { 166 type = STRING_LIST; 167 } 168 return; 169 } 170 } 171 type = BINARY; 172 } 173 174 size_t 175 property_value::size() 176 { 177 if (!byte_data.empty()) 178 { 179 return byte_data.size(); 180 } 181 return string_data.size() + 1; 182 } 183 184 void 185 property_value::write_as_string(FILE *file) 186 { 187 putc('"', file); 188 if (byte_data.empty()) 189 { 190 fputs(string_data.c_str(), file); 191 } 192 else 193 { 194 bool hasNull = (byte_data.back() == '\0'); 195 // Remove trailing null bytes from the string before printing as dts. 196 if (hasNull) 197 { 198 byte_data.pop_back(); 199 } 200 for (auto i : byte_data) 201 { 202 // FIXME Escape tabs, newlines, and so on. 203 if (i == '\0') 204 { 205 fputs("\", \"", file); 206 continue; 207 } 208 putc(i, file); 209 } 210 if (hasNull) 211 { 212 byte_data.push_back('\0'); 213 } 214 } 215 putc('"', file); 216 } 217 218 void 219 property_value::write_as_cells(FILE *file) 220 { 221 putc('<', file); 222 assert((byte_data.size() % 4) == 0); 223 for (auto i=byte_data.begin(), e=byte_data.end(); i!=e ; ++i) 224 { 225 uint32_t v = 0; 226 v = (v << 8) | *i; 227 ++i; 228 v = (v << 8) | *i; 229 ++i; 230 v = (v << 8) | *i; 231 ++i; 232 v = (v << 8) | *i; 233 fprintf(file, "0x%" PRIx32, v); 234 if (i+1 != e) 235 { 236 putc(' ', file); 237 } 238 } 239 putc('>', file); 240 } 241 242 void 243 property_value::write_as_bytes(FILE *file) 244 { 245 putc('[', file); 246 for (auto i=byte_data.begin(), e=byte_data.end(); i!=e ; i++) 247 { 248 fprintf(file, "%02hhx", *i); 249 if (i+1 != e) 250 { 251 putc(' ', file); 252 } 253 } 254 putc(']', file); 255 } 256 257 void 258 property::parse_string(text_input_buffer &input) 259 { 260 property_value v; 261 assert(*input == '"'); 262 ++input; 263 std::vector<char> bytes; 264 bool isEscaped = false; 265 while (char c = *input) 266 { 267 if (c == '"' && !isEscaped) 268 { 269 input.consume('"'); 270 break; 271 } 272 isEscaped = (c == '\\'); 273 bytes.push_back(c); 274 ++input; 275 } 276 v.string_data = string(bytes.begin(), bytes.end()); 277 values.push_back(v); 278 } 279 280 void 281 property::parse_cells(text_input_buffer &input, int cell_size) 282 { 283 assert(*input == '<'); 284 ++input; 285 property_value v; 286 input.next_token(); 287 while (!input.consume('>')) 288 { 289 input.next_token(); 290 // If this is a phandle then we need to get the name of the 291 // referenced node 292 if (input.consume('&')) 293 { 294 if (cell_size != 32) 295 { 296 input.parse_error("reference only permitted in 32-bit arrays"); 297 valid = false; 298 return; 299 } 300 input.next_token(); 301 string referenced; 302 if (!input.consume('{')) 303 { 304 referenced = input.parse_node_name(); 305 } 306 else 307 { 308 referenced = input.parse_to('}'); 309 input.consume('}'); 310 } 311 if (referenced.empty()) 312 { 313 input.parse_error("Expected node name"); 314 valid = false; 315 return; 316 } 317 input.next_token(); 318 // If we already have some bytes, make the phandle a 319 // separate component. 320 if (!v.byte_data.empty()) 321 { 322 values.push_back(v); 323 v = property_value(); 324 } 325 v.string_data = referenced; 326 v.type = property_value::PHANDLE; 327 values.push_back(v); 328 v = property_value(); 329 } 330 else 331 { 332 //FIXME: We should support labels in the middle 333 //of these, but we don't. 334 unsigned long long val; 335 if (!input.consume_integer_expression(val)) 336 { 337 input.parse_error("Expected numbers in array of cells"); 338 valid = false; 339 return; 340 } 341 switch (cell_size) 342 { 343 case 8: 344 v.byte_data.push_back(val); 345 break; 346 case 16: 347 push_big_endian(v.byte_data, (uint16_t)val); 348 break; 349 case 32: 350 push_big_endian(v.byte_data, (uint32_t)val); 351 break; 352 case 64: 353 push_big_endian(v.byte_data, (uint64_t)val); 354 break; 355 default: 356 assert(0 && "Invalid cell size!"); 357 } 358 input.next_token(); 359 } 360 } 361 // Don't store an empty string value here. 362 if (v.byte_data.size() > 0) 363 { 364 values.push_back(v); 365 } 366 } 367 368 void 369 property::parse_bytes(text_input_buffer &input) 370 { 371 assert(*input == '['); 372 ++input; 373 property_value v; 374 input.next_token(); 375 while (!input.consume(']')) 376 { 377 { 378 //FIXME: We should support 379 //labels in the middle of 380 //these, but we don't. 381 uint8_t val; 382 if (!input.consume_hex_byte(val)) 383 { 384 input.parse_error("Expected hex bytes in array of bytes"); 385 valid = false; 386 return; 387 } 388 v.byte_data.push_back(val); 389 input.next_token(); 390 } 391 } 392 values.push_back(v); 393 } 394 395 void 396 property::parse_reference(text_input_buffer &input) 397 { 398 assert(*input == '&'); 399 ++input; 400 input.next_token(); 401 property_value v; 402 v.string_data = input.parse_node_name(); 403 if (v.string_data.empty()) 404 { 405 input.parse_error("Expected node name"); 406 valid = false; 407 return; 408 } 409 v.type = property_value::CROSS_REFERENCE; 410 values.push_back(v); 411 } 412 413 property::property(input_buffer &structs, input_buffer &strings) 414 { 415 uint32_t name_offset; 416 uint32_t length; 417 valid = structs.consume_binary(length) && 418 structs.consume_binary(name_offset); 419 if (!valid) 420 { 421 fprintf(stderr, "Failed to read property\n"); 422 return; 423 } 424 // Find the name 425 input_buffer name_buffer = strings.buffer_from_offset(name_offset); 426 if (name_buffer.finished()) 427 { 428 fprintf(stderr, "Property name offset %" PRIu32 429 " is past the end of the strings table\n", 430 name_offset); 431 valid = false; 432 return; 433 } 434 key = name_buffer.parse_to(0); 435 436 // If we're empty, do not push anything as value. 437 if (!length) 438 return; 439 440 // Read the value 441 uint8_t byte; 442 property_value v; 443 for (uint32_t i=0 ; i<length ; i++) 444 { 445 if (!(valid = structs.consume_binary(byte))) 446 { 447 fprintf(stderr, "Failed to read property value\n"); 448 return; 449 } 450 v.byte_data.push_back(byte); 451 } 452 values.push_back(v); 453 } 454 455 void property::parse_define(text_input_buffer &input, define_map *defines) 456 { 457 input.consume('$'); 458 if (!defines) 459 { 460 input.parse_error("No predefined properties to match name\n"); 461 valid = false; 462 return; 463 } 464 string name = input.parse_property_name(); 465 define_map::iterator found; 466 if ((name == string()) || 467 ((found = defines->find(name)) == defines->end())) 468 { 469 input.parse_error("Undefined property name\n"); 470 valid = false; 471 return; 472 } 473 values.push_back((*found).second->values[0]); 474 } 475 476 property::property(text_input_buffer &input, 477 string &&k, 478 string_set &&l, 479 bool semicolonTerminated, 480 define_map *defines) : key(k), labels(l), valid(true) 481 { 482 do { 483 input.next_token(); 484 switch (*input) 485 { 486 case '$': 487 { 488 parse_define(input, defines); 489 if (valid) 490 { 491 break; 492 } 493 } 494 default: 495 input.parse_error("Invalid property value."); 496 valid = false; 497 return; 498 case '/': 499 { 500 unsigned long long bits = 0; 501 valid = input.consume("/bits/"); 502 input.next_token(); 503 valid &= input.consume_integer(bits); 504 if ((bits != 8) && 505 (bits != 16) && 506 (bits != 32) && 507 (bits != 64)) { 508 input.parse_error("Invalid size for elements"); 509 valid = false; 510 } 511 if (!valid) return; 512 input.next_token(); 513 if (*input != '<') 514 { 515 input.parse_error("/bits/ directive is only valid on arrays"); 516 valid = false; 517 return; 518 } 519 parse_cells(input, bits); 520 break; 521 } 522 case '"': 523 parse_string(input); 524 break; 525 case '<': 526 parse_cells(input, 32); 527 break; 528 case '[': 529 parse_bytes(input); 530 break; 531 case '&': 532 parse_reference(input); 533 break; 534 case ';': 535 { 536 break; 537 } 538 } 539 input.next_token(); 540 } while (input.consume(',')); 541 if (semicolonTerminated && !input.consume(';')) 542 { 543 input.parse_error("Expected ; at end of property"); 544 valid = false; 545 } 546 } 547 548 property_ptr 549 property::parse_dtb(input_buffer &structs, input_buffer &strings) 550 { 551 property_ptr p(new property(structs, strings)); 552 if (!p->valid) 553 { 554 p = nullptr; 555 } 556 return p; 557 } 558 559 property_ptr 560 property::parse(text_input_buffer &input, string &&key, string_set &&label, 561 bool semicolonTerminated, define_map *defines) 562 { 563 property_ptr p(new property(input, 564 std::move(key), 565 std::move(label), 566 semicolonTerminated, 567 defines)); 568 if (!p->valid) 569 { 570 p = nullptr; 571 } 572 return p; 573 } 574 575 void 576 property::write(dtb::output_writer &writer, dtb::string_table &strings) 577 { 578 writer.write_token(dtb::FDT_PROP); 579 byte_buffer value_buffer; 580 for (value_iterator i=begin(), e=end() ; i!=e ; ++i) 581 { 582 i->push_to_buffer(value_buffer); 583 } 584 writer.write_data((uint32_t)value_buffer.size()); 585 writer.write_comment(key); 586 writer.write_data(strings.add_string(key)); 587 writer.write_data(value_buffer); 588 } 589 590 bool 591 property_value::try_to_merge(property_value &other) 592 { 593 resolve_type(); 594 switch (type) 595 { 596 case UNKNOWN: 597 __builtin_unreachable(); 598 assert(0); 599 return false; 600 case EMPTY: 601 *this = other; 602 case STRING: 603 case STRING_LIST: 604 case CROSS_REFERENCE: 605 return false; 606 case PHANDLE: 607 case BINARY: 608 if (other.type == PHANDLE || other.type == BINARY) 609 { 610 type = BINARY; 611 byte_data.insert(byte_data.end(), other.byte_data.begin(), 612 other.byte_data.end()); 613 return true; 614 } 615 } 616 return false; 617 } 618 619 void 620 property::write_dts(FILE *file, int indent) 621 { 622 for (int i=0 ; i<indent ; i++) 623 { 624 putc('\t', file); 625 } 626 #ifdef PRINT_LABELS 627 for (auto &l : labels) 628 { 629 fputs(l.c_str(), file); 630 fputs(": ", file); 631 } 632 #endif 633 if (key != string()) 634 { 635 fputs(key.c_str(), file); 636 } 637 if (!values.empty()) 638 { 639 std::vector<property_value> *vals = &values; 640 std::vector<property_value> v; 641 // If we've got multiple values then try to merge them all together. 642 if (values.size() > 1) 643 { 644 vals = &v; 645 v.push_back(values.front()); 646 for (auto i=(++begin()), e=end() ; i!=e ; ++i) 647 { 648 if (!v.back().try_to_merge(*i)) 649 { 650 v.push_back(*i); 651 } 652 } 653 } 654 fputs(" = ", file); 655 for (auto i=vals->begin(), e=vals->end() ; i!=e ; ++i) 656 { 657 i->write_dts(file); 658 if (i+1 != e) 659 { 660 putc(',', file); 661 putc(' ', file); 662 } 663 } 664 } 665 fputs(";\n", file); 666 } 667 668 size_t 669 property::offset_of_value(property_value &val) 670 { 671 size_t off = 0; 672 for (auto &v : values) 673 { 674 if (&v == &val) 675 { 676 return off; 677 } 678 off += v.size(); 679 } 680 return -1; 681 } 682 683 string 684 node::parse_name(text_input_buffer &input, bool &is_property, const char *error) 685 { 686 if (!valid) 687 { 688 return string(); 689 } 690 input.next_token(); 691 if (is_property) 692 { 693 return input.parse_property_name(); 694 } 695 string n = input.parse_node_or_property_name(is_property); 696 if (n.empty()) 697 { 698 if (n.empty()) 699 { 700 input.parse_error(error); 701 valid = false; 702 } 703 } 704 return n; 705 } 706 707 void 708 node::visit(std::function<void(node&)> fn) 709 { 710 fn(*this); 711 for (auto &&c : children) 712 { 713 c->visit(fn); 714 } 715 } 716 717 node::node(input_buffer &structs, input_buffer &strings) : valid(true) 718 { 719 std::vector<char> bytes; 720 while (structs[0] != '\0' && structs[0] != '@') 721 { 722 bytes.push_back(structs[0]); 723 ++structs; 724 } 725 name = string(bytes.begin(), bytes.end()); 726 bytes.clear(); 727 if (structs[0] == '@') 728 { 729 ++structs; 730 while (structs[0] != '\0') 731 { 732 bytes.push_back(structs[0]); 733 ++structs; 734 } 735 unit_address = string(bytes.begin(), bytes.end()); 736 } 737 ++structs; 738 uint32_t token; 739 while (structs.consume_binary(token)) 740 { 741 switch (token) 742 { 743 default: 744 fprintf(stderr, "Unexpected token 0x%" PRIx32 745 " while parsing node.\n", token); 746 valid = false; 747 return; 748 // Child node, parse it. 749 case dtb::FDT_BEGIN_NODE: 750 { 751 node_ptr child = node::parse_dtb(structs, strings); 752 if (child == 0) 753 { 754 valid = false; 755 return; 756 } 757 children.push_back(std::move(child)); 758 break; 759 } 760 // End of this node, no errors. 761 case dtb::FDT_END_NODE: 762 return; 763 // Property, parse it. 764 case dtb::FDT_PROP: 765 { 766 property_ptr prop = property::parse_dtb(structs, strings); 767 if (prop == 0) 768 { 769 valid = false; 770 return; 771 } 772 props.push_back(prop); 773 break; 774 } 775 break; 776 // End of structs table. Should appear after 777 // the end of the last node. 778 case dtb::FDT_END: 779 fprintf(stderr, "Unexpected FDT_END token while parsing node.\n"); 780 valid = false; 781 return; 782 // NOPs are padding. Ignore them. 783 case dtb::FDT_NOP: 784 break; 785 } 786 } 787 fprintf(stderr, "Failed to read token from structs table while parsing node.\n"); 788 valid = false; 789 return; 790 } 791 792 793 node::node(const string &n, 794 const std::vector<property_ptr> &p) 795 : name(n) 796 { 797 props.insert(props.begin(), p.begin(), p.end()); 798 } 799 800 node_ptr node::create_special_node(const string &name, 801 const std::vector<property_ptr> &props) 802 { 803 node_ptr n(new node(name, props)); 804 return n; 805 } 806 807 node::node(text_input_buffer &input, 808 string &&n, 809 std::unordered_set<string> &&l, 810 string &&a, 811 define_map *defines) 812 : labels(l), name(n), unit_address(a), valid(true) 813 { 814 if (!input.consume('{')) 815 { 816 input.parse_error("Expected { to start new device tree node.\n"); 817 } 818 input.next_token(); 819 while (valid && !input.consume('}')) 820 { 821 // flag set if we find any characters that are only in 822 // the property name character set, not the node 823 bool is_property = false; 824 string child_name, child_address; 825 std::unordered_set<string> child_labels; 826 auto parse_delete = [&](const char *expected, bool at) 827 { 828 if (child_name == string()) 829 { 830 input.parse_error(expected); 831 valid = false; 832 return; 833 } 834 input.next_token(); 835 if (at && input.consume('@')) 836 { 837 child_name += '@'; 838 child_name += parse_name(input, is_property, "Expected unit address"); 839 } 840 if (!input.consume(';')) 841 { 842 input.parse_error("Expected semicolon"); 843 valid = false; 844 return; 845 } 846 input.next_token(); 847 }; 848 if (input.consume("/delete-node/")) 849 { 850 input.next_token(); 851 child_name = input.parse_node_name(); 852 parse_delete("Expected node name", true); 853 if (valid) 854 { 855 deleted_children.insert(child_name); 856 } 857 continue; 858 } 859 if (input.consume("/delete-property/")) 860 { 861 input.next_token(); 862 child_name = input.parse_property_name(); 863 parse_delete("Expected property name", false); 864 if (valid) 865 { 866 deleted_props.insert(child_name); 867 } 868 continue; 869 } 870 child_name = parse_name(input, is_property, 871 "Expected property or node name"); 872 while (input.consume(':')) 873 { 874 // Node labels can contain any characters? The 875 // spec doesn't say, so we guess so... 876 is_property = false; 877 child_labels.insert(std::move(child_name)); 878 child_name = parse_name(input, is_property, "Expected property or node name"); 879 } 880 if (input.consume('@')) 881 { 882 child_address = parse_name(input, is_property, "Expected unit address"); 883 } 884 if (!valid) 885 { 886 return; 887 } 888 input.next_token(); 889 // If we're parsing a property, then we must actually do that. 890 if (input.consume('=')) 891 { 892 property_ptr p = property::parse(input, std::move(child_name), 893 std::move(child_labels), true, defines); 894 if (p == 0) 895 { 896 valid = false; 897 } 898 else 899 { 900 props.push_back(p); 901 } 902 } 903 else if (!is_property && *input == ('{')) 904 { 905 node_ptr child = node::parse(input, std::move(child_name), 906 std::move(child_labels), std::move(child_address), defines); 907 if (child) 908 { 909 children.push_back(std::move(child)); 910 } 911 else 912 { 913 valid = false; 914 } 915 } 916 else if (input.consume(';')) 917 { 918 props.push_back(property_ptr(new property(std::move(child_name), std::move(child_labels)))); 919 } 920 else 921 { 922 input.parse_error("Error parsing property. Expected property value"); 923 valid = false; 924 } 925 input.next_token(); 926 } 927 input.next_token(); 928 input.consume(';'); 929 } 930 931 bool 932 node::cmp_properties(property_ptr &p1, property_ptr &p2) 933 { 934 return p1->get_key() < p2->get_key(); 935 } 936 937 bool 938 node::cmp_children(node_ptr &c1, node_ptr &c2) 939 { 940 if (c1->name == c2->name) 941 { 942 return c1->unit_address < c2->unit_address; 943 } 944 return c1->name < c2->name; 945 } 946 947 void 948 node::sort() 949 { 950 std::sort(property_begin(), property_end(), cmp_properties); 951 std::sort(child_begin(), child_end(), cmp_children); 952 for (auto &c : child_nodes()) 953 { 954 c->sort(); 955 } 956 } 957 958 node_ptr 959 node::parse(text_input_buffer &input, 960 string &&name, 961 string_set &&label, 962 string &&address, 963 define_map *defines) 964 { 965 node_ptr n(new node(input, 966 std::move(name), 967 std::move(label), 968 std::move(address), 969 defines)); 970 if (!n->valid) 971 { 972 n = 0; 973 } 974 return n; 975 } 976 977 node_ptr 978 node::parse_dtb(input_buffer &structs, input_buffer &strings) 979 { 980 node_ptr n(new node(structs, strings)); 981 if (!n->valid) 982 { 983 n = 0; 984 } 985 return n; 986 } 987 988 property_ptr 989 node::get_property(const string &key) 990 { 991 for (auto &i : props) 992 { 993 if (i->get_key() == key) 994 { 995 return i; 996 } 997 } 998 return 0; 999 } 1000 1001 void 1002 node::merge_node(node_ptr other) 1003 { 1004 for (auto &l : other->labels) 1005 { 1006 labels.insert(l); 1007 } 1008 // Note: this is an O(n*m) operation. It might be sensible to 1009 // optimise this if we find that there are nodes with very 1010 // large numbers of properties, but for typical usage the 1011 // entire vector will fit (easily) into cache, so iterating 1012 // over it repeatedly isn't that expensive. 1013 for (auto &p : other->properties()) 1014 { 1015 bool found = false; 1016 for (auto &mp : properties()) 1017 { 1018 if (mp->get_key() == p->get_key()) 1019 { 1020 mp = p; 1021 found = true; 1022 break; 1023 } 1024 } 1025 if (!found) 1026 { 1027 add_property(p); 1028 } 1029 } 1030 for (auto &c : other->children) 1031 { 1032 bool found = false; 1033 for (auto &i : children) 1034 { 1035 if (i->name == c->name && i->unit_address == c->unit_address) 1036 { 1037 i->merge_node(std::move(c)); 1038 found = true; 1039 break; 1040 } 1041 } 1042 if (!found) 1043 { 1044 children.push_back(std::move(c)); 1045 } 1046 } 1047 children.erase(std::remove_if(children.begin(), children.end(), 1048 [&](const node_ptr &p) { 1049 string full_name = p->name; 1050 if (p->unit_address != string()) 1051 { 1052 full_name += '@'; 1053 full_name += p->unit_address; 1054 } 1055 if (other->deleted_children.count(full_name) > 0) 1056 { 1057 other->deleted_children.erase(full_name); 1058 return true; 1059 } 1060 return false; 1061 }), children.end()); 1062 props.erase(std::remove_if(props.begin(), props.end(), 1063 [&](const property_ptr &p) { 1064 if (other->deleted_props.count(p->get_key()) > 0) 1065 { 1066 other->deleted_props.erase(p->get_key()); 1067 return true; 1068 } 1069 return false; 1070 }), props.end()); 1071 } 1072 1073 void 1074 node::write(dtb::output_writer &writer, dtb::string_table &strings) 1075 { 1076 writer.write_token(dtb::FDT_BEGIN_NODE); 1077 byte_buffer name_buffer; 1078 push_string(name_buffer, name); 1079 if (unit_address != string()) 1080 { 1081 name_buffer.push_back('@'); 1082 push_string(name_buffer, unit_address); 1083 } 1084 writer.write_comment(name); 1085 writer.write_data(name_buffer); 1086 writer.write_data((uint8_t)0); 1087 for (auto p : properties()) 1088 { 1089 p->write(writer, strings); 1090 } 1091 for (auto &c : child_nodes()) 1092 { 1093 c->write(writer, strings); 1094 } 1095 writer.write_token(dtb::FDT_END_NODE); 1096 } 1097 1098 void 1099 node::write_dts(FILE *file, int indent) 1100 { 1101 for (int i=0 ; i<indent ; i++) 1102 { 1103 putc('\t', file); 1104 } 1105 #ifdef PRINT_LABELS 1106 for (auto &label : labels) 1107 { 1108 fprintf(file, "%s: ", label.c_str()); 1109 } 1110 #endif 1111 if (name != string()) 1112 { 1113 fputs(name.c_str(), file); 1114 } 1115 if (unit_address != string()) 1116 { 1117 putc('@', file); 1118 fputs(unit_address.c_str(), file); 1119 } 1120 fputs(" {\n\n", file); 1121 for (auto p : properties()) 1122 { 1123 p->write_dts(file, indent+1); 1124 } 1125 for (auto &c : child_nodes()) 1126 { 1127 c->write_dts(file, indent+1); 1128 } 1129 for (int i=0 ; i<indent ; i++) 1130 { 1131 putc('\t', file); 1132 } 1133 fputs("};\n", file); 1134 } 1135 1136 void 1137 device_tree::collect_names_recursive(node_ptr &n, node_path &path) 1138 { 1139 path.push_back(std::make_pair(n->name, n->unit_address)); 1140 for (const string &name : n->labels) 1141 { 1142 if (name != string()) 1143 { 1144 auto iter = node_names.find(name); 1145 if (iter == node_names.end()) 1146 { 1147 node_names.insert(std::make_pair(name, n.get())); 1148 node_paths.insert(std::make_pair(name, path)); 1149 } 1150 else 1151 { 1152 node_names.erase(iter); 1153 auto i = node_paths.find(name); 1154 if (i != node_paths.end()) 1155 { 1156 node_paths.erase(name); 1157 } 1158 fprintf(stderr, "Label not unique: %s. References to this label will not be resolved.\n", name.c_str()); 1159 } 1160 } 1161 } 1162 for (auto &c : n->child_nodes()) 1163 { 1164 collect_names_recursive(c, path); 1165 } 1166 // Now we collect the phandles and properties that reference 1167 // other nodes. 1168 for (auto &p : n->properties()) 1169 { 1170 for (auto &v : *p) 1171 { 1172 if (v.is_phandle()) 1173 { 1174 fixups.push_back({path, p, v}); 1175 } 1176 if (v.is_cross_reference()) 1177 { 1178 cross_references.push_back(&v); 1179 } 1180 } 1181 if ((p->get_key() == "phandle") || 1182 (p->get_key() == "linux,phandle")) 1183 { 1184 if (p->begin()->byte_data.size() != 4) 1185 { 1186 fprintf(stderr, "Invalid phandle value for node %s. Should be a 4-byte value.\n", n->name.c_str()); 1187 valid = false; 1188 } 1189 else 1190 { 1191 uint32_t phandle = p->begin()->get_as_uint32(); 1192 used_phandles.insert(std::make_pair(phandle, n.get())); 1193 } 1194 } 1195 } 1196 path.pop_back(); 1197 } 1198 1199 void 1200 device_tree::collect_names() 1201 { 1202 node_path p; 1203 node_names.clear(); 1204 node_paths.clear(); 1205 cross_references.clear(); 1206 fixups.clear(); 1207 collect_names_recursive(root, p); 1208 } 1209 1210 void 1211 device_tree::resolve_cross_references() 1212 { 1213 for (auto *pv : cross_references) 1214 { 1215 node_path path = node_paths[pv->string_data]; 1216 auto p = path.begin(); 1217 auto pe = path.end(); 1218 if (p != pe) 1219 { 1220 // Skip the first name in the path. It's always "", and implicitly / 1221 for (++p ; p!=pe ; ++p) 1222 { 1223 pv->byte_data.push_back('/'); 1224 push_string(pv->byte_data, p->first); 1225 if (!(p->second.empty())) 1226 { 1227 pv->byte_data.push_back('@'); 1228 push_string(pv->byte_data, p->second); 1229 } 1230 } 1231 pv->byte_data.push_back(0); 1232 } 1233 } 1234 std::unordered_map<property_value*, fixup&> phandle_set; 1235 for (auto &i : fixups) 1236 { 1237 phandle_set.insert({&i.val, i}); 1238 } 1239 std::vector<std::reference_wrapper<fixup>> sorted_phandles; 1240 root->visit([&](node &n) { 1241 for (auto &p : n.properties()) 1242 { 1243 for (auto &v : *p) 1244 { 1245 auto i = phandle_set.find(&v); 1246 if (i != phandle_set.end()) 1247 { 1248 sorted_phandles.push_back(i->second); 1249 } 1250 } 1251 } 1252 }); 1253 assert(sorted_phandles.size() == fixups.size()); 1254 1255 uint32_t phandle = 1; 1256 for (auto &i : sorted_phandles) 1257 { 1258 string target_name = i.get().val.string_data; 1259 node *target = nullptr; 1260 string possible; 1261 // If the node name is a path, then look it up by following the path, 1262 // otherwise jump directly to the named node. 1263 if (target_name[0] == '/') 1264 { 1265 string path; 1266 target = root.get(); 1267 std::istringstream ss(target_name); 1268 string path_element; 1269 // Read the leading / 1270 std::getline(ss, path_element, '/'); 1271 // Iterate over path elements 1272 while (!ss.eof()) 1273 { 1274 path += '/'; 1275 std::getline(ss, path_element, '/'); 1276 std::istringstream nss(path_element); 1277 string node_name, node_address; 1278 std::getline(nss, node_name, '@'); 1279 std::getline(nss, node_address, '@'); 1280 node *next = nullptr; 1281 for (auto &c : target->child_nodes()) 1282 { 1283 if (c->name == node_name) 1284 { 1285 if (c->unit_address == node_address) 1286 { 1287 next = c.get(); 1288 break; 1289 } 1290 else 1291 { 1292 possible = path + c->name; 1293 if (c->unit_address != string()) 1294 { 1295 possible += '@'; 1296 possible += c->unit_address; 1297 } 1298 } 1299 } 1300 } 1301 path += node_name; 1302 if (node_address != string()) 1303 { 1304 path += '@'; 1305 path += node_address; 1306 } 1307 target = next; 1308 if (target == nullptr) 1309 { 1310 break; 1311 } 1312 } 1313 } 1314 else 1315 { 1316 target = node_names[target_name]; 1317 } 1318 if (target == nullptr) 1319 { 1320 if (is_plugin) 1321 { 1322 unresolved_fixups.push_back(i); 1323 continue; 1324 } 1325 else 1326 { 1327 fprintf(stderr, "Failed to find node with label: %s\n", target_name.c_str()); 1328 if (possible != string()) 1329 { 1330 fprintf(stderr, "Possible intended match: %s\n", possible.c_str()); 1331 } 1332 valid = 0; 1333 return; 1334 } 1335 } 1336 // If there is an existing phandle, use it 1337 property_ptr p = target->get_property("phandle"); 1338 if (p == 0) 1339 { 1340 p = target->get_property("linux,phandle"); 1341 } 1342 if (p == 0) 1343 { 1344 // Otherwise insert a new phandle node 1345 property_value v; 1346 while (used_phandles.find(phandle) != used_phandles.end()) 1347 { 1348 // Note that we only don't need to 1349 // store this phandle in the set, 1350 // because we are monotonically 1351 // increasing the value of phandle and 1352 // so will only ever revisit this value 1353 // if we have used 2^32 phandles, at 1354 // which point our blob won't fit in 1355 // any 32-bit system and we've done 1356 // something badly wrong elsewhere 1357 // already. 1358 phandle++; 1359 } 1360 push_big_endian(v.byte_data, phandle++); 1361 if (phandle_node_name == BOTH || phandle_node_name == LINUX) 1362 { 1363 p.reset(new property("linux,phandle")); 1364 p->add_value(v); 1365 target->add_property(p); 1366 } 1367 if (phandle_node_name == BOTH || phandle_node_name == EPAPR) 1368 { 1369 p.reset(new property("phandle")); 1370 p->add_value(v); 1371 target->add_property(p); 1372 } 1373 } 1374 p->begin()->push_to_buffer(i.get().val.byte_data); 1375 assert(i.get().val.byte_data.size() == 4); 1376 } 1377 } 1378 1379 1380 void 1381 device_tree::parse_file(text_input_buffer &input, 1382 std::vector<node_ptr> &roots, 1383 bool &read_header) 1384 { 1385 input.next_token(); 1386 // Read the header 1387 if (input.consume("/dts-v1/;")) 1388 { 1389 read_header = true; 1390 } 1391 input.next_token(); 1392 if (input.consume("/plugin/;")) 1393 { 1394 is_plugin = true; 1395 } 1396 input.next_token(); 1397 if (!read_header) 1398 { 1399 input.parse_error("Expected /dts-v1/; version string"); 1400 } 1401 // Read any memory reservations 1402 while (input.consume("/memreserve/")) 1403 { 1404 unsigned long long start, len; 1405 input.next_token(); 1406 // Read the start and length. 1407 if (!(input.consume_integer_expression(start) && 1408 (input.next_token(), 1409 input.consume_integer_expression(len)))) 1410 { 1411 input.parse_error("Expected size on /memreserve/ node."); 1412 } 1413 input.next_token(); 1414 input.consume(';'); 1415 reservations.push_back(reservation(start, len)); 1416 input.next_token(); 1417 } 1418 while (valid && !input.finished()) 1419 { 1420 node_ptr n; 1421 if (input.consume('/')) 1422 { 1423 input.next_token(); 1424 n = node::parse(input, string(), string_set(), string(), &defines); 1425 } 1426 else if (input.consume('&')) 1427 { 1428 input.next_token(); 1429 string name = input.parse_node_name(); 1430 input.next_token(); 1431 n = node::parse(input, std::move(name), string_set(), string(), &defines); 1432 } 1433 else 1434 { 1435 input.parse_error("Failed to find root node /."); 1436 } 1437 if (n) 1438 { 1439 roots.push_back(std::move(n)); 1440 } 1441 else 1442 { 1443 valid = false; 1444 } 1445 input.next_token(); 1446 } 1447 } 1448 1449 template<class writer> void 1450 device_tree::write(int fd) 1451 { 1452 dtb::string_table st; 1453 dtb::header head; 1454 writer head_writer; 1455 writer reservation_writer; 1456 writer struct_writer; 1457 writer strings_writer; 1458 1459 // Build the reservation table 1460 reservation_writer.write_comment(string("Memory reservations")); 1461 reservation_writer.write_label(string("dt_reserve_map")); 1462 for (auto &i : reservations) 1463 { 1464 reservation_writer.write_comment(string("Reservation start")); 1465 reservation_writer.write_data(i.first); 1466 reservation_writer.write_comment(string("Reservation length")); 1467 reservation_writer.write_data(i.first); 1468 } 1469 // Write n spare reserve map entries, plus the trailing 0. 1470 for (uint32_t i=0 ; i<=spare_reserve_map_entries ; i++) 1471 { 1472 reservation_writer.write_data((uint64_t)0); 1473 reservation_writer.write_data((uint64_t)0); 1474 } 1475 1476 1477 struct_writer.write_comment(string("Device tree")); 1478 struct_writer.write_label(string("dt_struct_start")); 1479 root->write(struct_writer, st); 1480 struct_writer.write_token(dtb::FDT_END); 1481 struct_writer.write_label(string("dt_struct_end")); 1482 1483 st.write(strings_writer); 1484 // Find the strings size before we stick padding on the end. 1485 // Note: We should possibly use a new writer for the padding. 1486 head.size_dt_strings = strings_writer.size(); 1487 1488 // Stick the padding in the strings writer, but after the 1489 // marker indicating that it's the end. 1490 // Note: We probably should add a padding call to the writer so 1491 // that the asm back end can write padding directives instead 1492 // of a load of 0 bytes. 1493 for (uint32_t i=0 ; i<blob_padding ; i++) 1494 { 1495 strings_writer.write_data((uint8_t)0); 1496 } 1497 head.totalsize = sizeof(head) + strings_writer.size() + 1498 struct_writer.size() + reservation_writer.size(); 1499 while (head.totalsize < minimum_blob_size) 1500 { 1501 head.totalsize++; 1502 strings_writer.write_data((uint8_t)0); 1503 } 1504 head.off_dt_struct = sizeof(head) + reservation_writer.size();; 1505 head.off_dt_strings = head.off_dt_struct + struct_writer.size(); 1506 head.off_mem_rsvmap = sizeof(head); 1507 head.boot_cpuid_phys = boot_cpu; 1508 head.size_dt_struct = struct_writer.size(); 1509 head.write(head_writer); 1510 1511 head_writer.write_to_file(fd); 1512 reservation_writer.write_to_file(fd); 1513 struct_writer.write_to_file(fd); 1514 strings_writer.write_label(string("dt_blob_end")); 1515 strings_writer.write_to_file(fd); 1516 } 1517 1518 node* 1519 device_tree::referenced_node(property_value &v) 1520 { 1521 if (v.is_phandle()) 1522 { 1523 return node_names[v.string_data]; 1524 } 1525 if (v.is_binary()) 1526 { 1527 return used_phandles[v.get_as_uint32()]; 1528 } 1529 return 0; 1530 } 1531 1532 void 1533 device_tree::write_binary(int fd) 1534 { 1535 write<dtb::binary_writer>(fd); 1536 } 1537 1538 void 1539 device_tree::write_asm(int fd) 1540 { 1541 write<dtb::asm_writer>(fd); 1542 } 1543 1544 void 1545 device_tree::write_dts(int fd) 1546 { 1547 FILE *file = fdopen(fd, "w"); 1548 fputs("/dts-v1/;\n\n", file); 1549 1550 if (!reservations.empty()) 1551 { 1552 const char msg[] = "/memreserve/"; 1553 fwrite(msg, sizeof(msg), 1, file); 1554 for (auto &i : reservations) 1555 { 1556 fprintf(file, " %" PRIx64 " %" PRIx64, i.first, i.second); 1557 } 1558 fputs(";\n\n", file); 1559 } 1560 putc('/', file); 1561 putc(' ', file); 1562 root->write_dts(file, 0); 1563 fclose(file); 1564 } 1565 1566 void 1567 device_tree::parse_dtb(const string &fn, FILE *) 1568 { 1569 auto in = input_buffer::buffer_for_file(fn); 1570 if (in == 0) 1571 { 1572 valid = false; 1573 return; 1574 } 1575 input_buffer &input = *in; 1576 dtb::header h; 1577 valid = h.read_dtb(input); 1578 boot_cpu = h.boot_cpuid_phys; 1579 if (h.last_comp_version > 17) 1580 { 1581 fprintf(stderr, "Don't know how to read this version of the device tree blob"); 1582 valid = false; 1583 } 1584 if (!valid) 1585 { 1586 return; 1587 } 1588 input_buffer reservation_map = 1589 input.buffer_from_offset(h.off_mem_rsvmap, 0); 1590 uint64_t start, length; 1591 do 1592 { 1593 if (!(reservation_map.consume_binary(start) && 1594 reservation_map.consume_binary(length))) 1595 { 1596 fprintf(stderr, "Failed to read memory reservation table\n"); 1597 valid = false; 1598 return; 1599 } 1600 } while (!((start == 0) && (length == 0))); 1601 input_buffer struct_table = 1602 input.buffer_from_offset(h.off_dt_struct, h.size_dt_struct); 1603 input_buffer strings_table = 1604 input.buffer_from_offset(h.off_dt_strings, h.size_dt_strings); 1605 uint32_t token; 1606 if (!(struct_table.consume_binary(token) && 1607 (token == dtb::FDT_BEGIN_NODE))) 1608 { 1609 fprintf(stderr, "Expected FDT_BEGIN_NODE token.\n"); 1610 valid = false; 1611 return; 1612 } 1613 root = node::parse_dtb(struct_table, strings_table); 1614 if (!(struct_table.consume_binary(token) && (token == dtb::FDT_END))) 1615 { 1616 fprintf(stderr, "Expected FDT_END token after parsing root node.\n"); 1617 valid = false; 1618 return; 1619 } 1620 valid = (root != 0); 1621 } 1622 1623 string 1624 device_tree::node_path::to_string() const 1625 { 1626 string path; 1627 auto p = begin(); 1628 auto pe = end(); 1629 if ((p == pe) || (p+1 == pe)) 1630 { 1631 return string("/"); 1632 } 1633 // Skip the first name in the path. It's always "", and implicitly / 1634 for (++p ; p!=pe ; ++p) 1635 { 1636 path += '/'; 1637 path += p->first; 1638 if (!(p->second.empty())) 1639 { 1640 path += '@'; 1641 path += p->second; 1642 } 1643 } 1644 return path; 1645 } 1646 1647 void 1648 device_tree::parse_dts(const string &fn, FILE *depfile) 1649 { 1650 auto in = input_buffer::buffer_for_file(fn); 1651 if (!in) 1652 { 1653 valid = false; 1654 return; 1655 } 1656 std::vector<node_ptr> roots; 1657 std::unordered_set<string> defnames; 1658 for (auto &i : defines) 1659 { 1660 defnames.insert(i.first); 1661 } 1662 text_input_buffer input(std::move(in), 1663 std::move(defnames), 1664 std::vector<string>(include_paths), 1665 dirname(fn), 1666 depfile); 1667 bool read_header = false; 1668 parse_file(input, roots, read_header); 1669 switch (roots.size()) 1670 { 1671 case 0: 1672 valid = false; 1673 input.parse_error("Failed to find root node /."); 1674 return; 1675 case 1: 1676 root = std::move(roots[0]); 1677 break; 1678 default: 1679 { 1680 root = std::move(roots[0]); 1681 for (auto i=++(roots.begin()), e=roots.end() ; i!=e ; ++i) 1682 { 1683 auto &node = *i; 1684 string name = node->name; 1685 if (name == string()) 1686 { 1687 root->merge_node(std::move(node)); 1688 } 1689 else 1690 { 1691 auto existing = node_names.find(name); 1692 if (existing == node_names.end()) 1693 { 1694 collect_names(); 1695 existing = node_names.find(name); 1696 } 1697 if (existing == node_names.end()) 1698 { 1699 fprintf(stderr, "Unable to merge node: %s\n", name.c_str()); 1700 } 1701 else 1702 { 1703 existing->second->merge_node(std::move(node)); 1704 } 1705 } 1706 } 1707 } 1708 } 1709 collect_names(); 1710 resolve_cross_references(); 1711 if (write_symbols) 1712 { 1713 std::vector<property_ptr> symbols; 1714 // Create a symbol table. Each label in this device tree may be 1715 // referenced by other plugins, so we create a __symbols__ node inside 1716 // the root that contains mappings (properties) from label names to 1717 // paths. 1718 for (auto &s : node_paths) 1719 { 1720 property_value v; 1721 v.string_data = s.second.to_string(); 1722 v.type = property_value::STRING; 1723 string name = s.first; 1724 auto prop = std::make_shared<property>(std::move(name)); 1725 prop->add_value(v); 1726 symbols.push_back(prop); 1727 } 1728 root->add_child(node::create_special_node("__symbols__", symbols)); 1729 // If this is a plugin, then we also need to create two extra nodes. 1730 // Internal phandles will need to be renumbered to avoid conflicts with 1731 // already-loaded nodes and external references will need to be 1732 // resolved. 1733 if (is_plugin) 1734 { 1735 // Create the fixups entry. This is of the form: 1736 // {target} = {path}:{property name}:{offset} 1737 auto create_fixup_entry = [&](fixup &i, string target) 1738 { 1739 string value = i.path.to_string(); 1740 value += ':'; 1741 value += i.prop->get_key(); 1742 value += ':'; 1743 value += std::to_string(i.prop->offset_of_value(i.val)); 1744 property_value v; 1745 v.string_data = value; 1746 v.type = property_value::STRING; 1747 auto prop = std::make_shared<property>(std::move(target)); 1748 prop->add_value(v); 1749 return prop; 1750 }; 1751 // If we have any unresolved phandle references in this plugin, 1752 // then we must update them to 0xdeadbeef and leave a property in 1753 // the /__fixups__ node whose key is the label and whose value is 1754 // as described above. 1755 if (!unresolved_fixups.empty()) 1756 { 1757 symbols.clear(); 1758 for (auto &i : unresolved_fixups) 1759 { 1760 auto &val = i.get().val; 1761 symbols.push_back(create_fixup_entry(i, val.string_data)); 1762 val.byte_data.push_back(0xde); 1763 val.byte_data.push_back(0xad); 1764 val.byte_data.push_back(0xbe); 1765 val.byte_data.push_back(0xef); 1766 val.type = property_value::BINARY; 1767 } 1768 root->add_child(node::create_special_node("__fixups__", symbols)); 1769 } 1770 symbols.clear(); 1771 // If we have any resolved phandle references in this plugin, then 1772 // we must leave a property in the /__local_fixups__ node whose key 1773 // is 'fixup' and whose value is as described above. 1774 for (auto &i : fixups) 1775 { 1776 if (!i.val.is_phandle()) 1777 { 1778 continue; 1779 } 1780 symbols.push_back(create_fixup_entry(i, "fixup")); 1781 } 1782 // We've iterated over all fixups, but only emit the 1783 // __local_fixups__ if we found some that were resolved internally. 1784 if (!symbols.empty()) 1785 { 1786 root->add_child(node::create_special_node("__local_fixups__", symbols)); 1787 } 1788 } 1789 } 1790 } 1791 1792 bool device_tree::parse_define(const char *def) 1793 { 1794 const char *val = strchr(def, '='); 1795 if (!val) 1796 { 1797 if (strlen(def) != 0) 1798 { 1799 string name(def); 1800 defines[name]; 1801 return true; 1802 } 1803 return false; 1804 } 1805 string name(def, val-def); 1806 string name_copy = name; 1807 val++; 1808 std::unique_ptr<input_buffer> raw(new input_buffer(val, strlen(val))); 1809 text_input_buffer in(std::move(raw), 1810 std::unordered_set<string>(), 1811 std::vector<string>(), 1812 string(), 1813 nullptr); 1814 property_ptr p = property::parse(in, std::move(name_copy), string_set(), false); 1815 if (p) 1816 defines[name] = p; 1817 return (bool)p; 1818 } 1819 1820 } // namespace fdt 1821 1822 } // namespace dtc 1823 1824