FlattenedDeviceTree.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /*
  2. * Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/ByteBuffer.h>
  7. #include <AK/Error.h>
  8. #include <AK/IterationDecision.h>
  9. #include <AK/MemoryStream.h>
  10. #include <AK/StringView.h>
  11. #include <LibDeviceTree/DeviceTree.h>
  12. #include <LibDeviceTree/FlattenedDeviceTree.h>
  13. #ifdef KERNEL
  14. # include <Kernel/Library/StdLib.h>
  15. #else
  16. # include <string.h>
  17. #endif
  18. namespace DeviceTree {
  19. static ErrorOr<StringView> read_string_view(ReadonlyBytes bytes, StringView error_string)
  20. {
  21. auto len = strnlen(reinterpret_cast<char const*>(bytes.data()), bytes.size());
  22. if (len == bytes.size()) {
  23. return Error::from_string_view_or_print_error_and_return_errno(error_string, EINVAL);
  24. }
  25. return StringView { bytes.slice(0, len) };
  26. }
  27. ErrorOr<void> walk_device_tree(FlattenedDeviceTreeHeader const& header, ReadonlyBytes raw_device_tree, DeviceTreeCallbacks callbacks)
  28. {
  29. ReadonlyBytes struct_bytes { raw_device_tree.data() + header.off_dt_struct, header.size_dt_struct };
  30. FixedMemoryStream stream(struct_bytes);
  31. char const* begin_strings_block = reinterpret_cast<char const*>(raw_device_tree.data() + header.off_dt_strings);
  32. FlattenedDeviceTreeTokenType prev_token = EndNode;
  33. StringView current_node_name;
  34. while (!stream.is_eof()) {
  35. auto current_token = TRY(stream.read_value<BigEndian<u32>>());
  36. switch (current_token) {
  37. case BeginNode: {
  38. current_node_name = TRY(read_string_view(struct_bytes.slice(stream.offset()), "Non-null terminated name for FDT_BEGIN_NODE token!"sv));
  39. size_t const consume_len = round_up_to_power_of_two(current_node_name.length() + 1, 4);
  40. TRY(stream.discard(consume_len));
  41. if (callbacks.on_node_begin) {
  42. if (IterationDecision::Break == TRY(callbacks.on_node_begin(current_node_name)))
  43. return {};
  44. }
  45. break;
  46. }
  47. case EndNode:
  48. if (callbacks.on_node_end) {
  49. if (IterationDecision::Break == TRY(callbacks.on_node_end(current_node_name)))
  50. return {};
  51. }
  52. break;
  53. case Property: {
  54. if (prev_token == EndNode) {
  55. return Error::from_string_view_or_print_error_and_return_errno("Invalid node sequence, FDT_PROP after FDT_END_NODE"sv, EINVAL);
  56. }
  57. auto len = TRY(stream.read_value<BigEndian<u32>>());
  58. auto nameoff = TRY(stream.read_value<BigEndian<u32>>());
  59. if (nameoff >= header.size_dt_strings) {
  60. return Error::from_string_view_or_print_error_and_return_errno("Invalid name offset in FDT_PROP"sv, EINVAL);
  61. }
  62. size_t const prop_name_max_len = header.size_dt_strings - nameoff;
  63. size_t const prop_name_len = strnlen(begin_strings_block + nameoff, prop_name_max_len);
  64. if (prop_name_len == prop_name_max_len) {
  65. return Error::from_string_view_or_print_error_and_return_errno("Non-null terminated name for FDT_PROP token!"sv, EINVAL);
  66. }
  67. StringView prop_name(begin_strings_block + nameoff, prop_name_len);
  68. if (len >= stream.remaining()) {
  69. return Error::from_string_view_or_print_error_and_return_errno("Property value length too large"sv, EINVAL);
  70. }
  71. ReadonlyBytes prop_value;
  72. if (len != 0) {
  73. prop_value = { struct_bytes.slice(stream.offset()).data(), len };
  74. size_t const consume_len = round_up_to_power_of_two(static_cast<u32>(len), 4);
  75. TRY(stream.discard(consume_len));
  76. }
  77. if (callbacks.on_property) {
  78. if (IterationDecision::Break == TRY(callbacks.on_property(prop_name, prop_value)))
  79. return {};
  80. }
  81. break;
  82. }
  83. case NoOp:
  84. if (callbacks.on_noop) {
  85. if (IterationDecision::Break == TRY(callbacks.on_noop()))
  86. return {};
  87. }
  88. break;
  89. case End: {
  90. if (prev_token == BeginNode || prev_token == Property) {
  91. return Error::from_string_view_or_print_error_and_return_errno("Invalid node sequence, FDT_END after BEGIN_NODE or PROP"sv, EINVAL);
  92. }
  93. if (!stream.is_eof()) {
  94. return Error::from_string_view_or_print_error_and_return_errno("Expected EOF at FTD_END but more data remains"sv, EINVAL);
  95. }
  96. if (callbacks.on_end) {
  97. return callbacks.on_end();
  98. }
  99. return {};
  100. }
  101. default:
  102. return Error::from_string_view_or_print_error_and_return_errno("Invalid token"sv, EINVAL);
  103. }
  104. prev_token = static_cast<FlattenedDeviceTreeTokenType>(static_cast<u32>(current_token));
  105. }
  106. return Error::from_string_view_or_print_error_and_return_errno("Unexpected end of stream"sv, EINVAL);
  107. }
  108. static ErrorOr<ReadonlyBytes> slow_get_property_raw(StringView name, FlattenedDeviceTreeHeader const& header, ReadonlyBytes raw_device_tree)
  109. {
  110. // Name is a path like /path/to/node/property
  111. Vector<StringView, 16> path;
  112. TRY(name.for_each_split_view('/', SplitBehavior::Nothing, [&path](StringView view) -> ErrorOr<void> {
  113. if (path.size() == path.capacity()) {
  114. return Error::from_errno(ENAMETOOLONG);
  115. }
  116. // This can never fail as all entries go into the inline buffer, enforced by the check above.
  117. path.unchecked_append(view);
  118. return {};
  119. }));
  120. bool check_property_name = path.size() == 1; // Properties on root node should be checked immediately
  121. ssize_t current_path_idx = -1; // Start "before" the root FDT_BEGIN_NODE tag
  122. ReadonlyBytes found_property_value;
  123. DeviceTreeCallbacks callbacks = {
  124. .on_node_begin = [&](StringView token_name) -> ErrorOr<IterationDecision> {
  125. if (current_path_idx < 0) {
  126. ++current_path_idx; // Root node
  127. return IterationDecision::Continue;
  128. }
  129. // FIXME: This might need to ignore address details in the path
  130. if (token_name == path[current_path_idx]) {
  131. ++current_path_idx;
  132. if (current_path_idx == static_cast<ssize_t>(path.size() - 1)) {
  133. check_property_name = true;
  134. }
  135. }
  136. return IterationDecision::Continue;
  137. },
  138. .on_node_end = [&](StringView) -> ErrorOr<IterationDecision> {
  139. if (check_property_name) {
  140. // Not found, but we were looking for the property
  141. return Error::from_errno(EINVAL);
  142. }
  143. return IterationDecision::Continue;
  144. },
  145. .on_property = [&](StringView property_name, ReadonlyBytes property_value) -> ErrorOr<IterationDecision> {
  146. if (check_property_name && property_name == path[current_path_idx]) {
  147. found_property_value = property_value;
  148. return IterationDecision::Break;
  149. }
  150. return IterationDecision::Continue;
  151. },
  152. .on_noop = nullptr,
  153. .on_end = [&]() -> ErrorOr<void> {
  154. return Error::from_string_view_or_print_error_and_return_errno("Property not found"sv, EINVAL);
  155. }
  156. };
  157. TRY(walk_device_tree(header, raw_device_tree, move(callbacks)));
  158. return found_property_value;
  159. }
  160. ErrorOr<DeviceTreeProperty> slow_get_property(StringView name, FlattenedDeviceTreeHeader const& header, ReadonlyBytes raw_device_tree)
  161. {
  162. return DeviceTreeProperty { TRY(slow_get_property_raw(name, header, raw_device_tree)) };
  163. }
  164. } // namespace DeviceTree