diff --git a/docs/source/pages/api/libs/dt.rst b/docs/source/pages/api/libs/dt.rst index 3ce7ed91..48352188 100644 --- a/docs/source/pages/api/libs/dt.rst +++ b/docs/source/pages/api/libs/dt.rst @@ -2,4 +2,16 @@ Device Tree API =============== +---------- +Public API +---------- + .. doxygengroup:: dt + :members: + +--------- +Internals +--------- + +.. doxygengroup:: dt_internal + :members: diff --git a/include/dt/dt.h b/include/dt/dt.h index 81dc6007..402e5da0 100644 --- a/include/dt/dt.h +++ b/include/dt/dt.h @@ -4,28 +4,27 @@ * @brief Device tree access interface. */ -#ifndef _DT_ -#define _DT_ - -// TODO: Change return values of functions that return e.g. no next properties and errors as the same +#ifndef DT_DT +#define DT_DT #include +#include #include +/// @addtogroup dt +/// @{ + /** - * @ingroup dt * @brief A node is an offset from the start of fdt with FDT_BEGIN_NODE. */ typedef u32 dt_node_t; /** - * @ingroup dt * @brief A property is an offset from the start of fdt with FDT_PROP. */ typedef u32 dt_prop_t; /** - * @ingroup dt * @brief Flattened device tree structure. */ typedef struct { @@ -39,129 +38,182 @@ typedef struct { } fdt_t; /** - * @ingroup dt * @brief Read the header at fdt and set fields of obj. * @param fdt Pointer to the flattened device tree data. * @param obj Pointer to the fdt_t structure to initialize. - * @return 0 if success, <0 if error. If an error occurs, fdt_buffer of obj is set to an invalid buffer. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args + * @retval ERR_NOT_VALID if the FDT is invalid or unsupported + * @note If an error occurs, fdt_buffer of obj is set to an invalid buffer. */ -int dt_init(const void* fdt, fdt_t* obj); +error_t dt_init(const void* fdt, fdt_t* obj); /** - * @ingroup dt * @brief Reset properties of obj. * @param obj Pointer to the fdt_t structure to reset. */ void dt_reset(fdt_t* obj); /** - * @ingroup dt * @brief Get a node in a subtree of fdt, leave as 0 for global search. * @param fdt Pointer to the fdt object. * @param node The node of which subtree to search, leave as 0 for global search. * @param node_path Path to the node. - * @return >0 if success, 0 if error. + * @param[out] nodeOUT Found node. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args + * @retval ERR_NOT_VALID if the FDT is invalid + * @retval ERR_NOT_FOUND if no node found + * @retval ERR_OUT_OF_BOUNDS if search went out of bounds */ -dt_node_t dt_get_node_in_subtree_by_path(const fdt_t* fdt, dt_node_t node, const char* node_path); +[[gnu::nonnull(4)]] +error_t dt_get_node_in_subtree_by_path(const fdt_t* fdt, dt_node_t node, const char* node_path, dt_node_t* nodeOUT); /** - * @ingroup dt * @brief Get a node globally in fdt. * @param fdt Pointer to the fdt object. * @param node_path Path to the node. - * @return >0 if success, 0 if error. + * @param[out] nodeOUT Found node. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args + * @retval ERR_NOT_VALID if the FDT is invalid + * @retval ERR_NOT_FOUND if no node found + * @retval ERR_OUT_OF_BOUNDS if search went out of bounds */ -dt_node_t dt_get_node_by_path(const fdt_t* fdt, const char* node_path); +[[gnu::nonnull(3)]] +error_t dt_get_node_by_path(const fdt_t* fdt, const char* node_path, dt_node_t* nodeOUT); /** - * @ingroup dt * @brief Get a node's first child in fdt. * @param fdt Pointer to the fdt object. * @param node The parent node. - * @return 0 if no children exist or error, >0 if success. + * @param[out] nodeOUT Child node. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args + * @retval ERR_NOT_VALID if the FDT is invalid + * @retval ERR_NOT_FOUND if no node found + * @retval ERR_OUT_OF_BOUNDS if search went out of bounds */ -dt_node_t dt_get_node_child(const fdt_t* fdt, dt_node_t node); +[[gnu::nonnull(3)]] +error_t dt_get_node_child(const fdt_t* fdt, dt_node_t node, dt_node_t* nodeOUT); /** - * @ingroup dt * @brief Get a node's next sibling in fdt. * @param fdt Pointer to the fdt object. * @param node The node to get the sibling of. - * @return 0 if no siblings exist or error, >0 if success. + * @param[out] nodeOUT Sibling node. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args + * @retval ERR_NOT_VALID if the FDT is invalid + * @retval ERR_NOT_FOUND if no node found + * @retval ERR_OUT_OF_BOUNDS if search went out of bounds */ -dt_node_t dt_get_node_sibling(const fdt_t* fdt, dt_node_t node); +[[gnu::nonnull(3)]] +error_t dt_get_node_sibling(const fdt_t* fdt, dt_node_t node, dt_node_t* nodeOUT); /** - * @ingroup dt * @brief Get a node's name as a buffer in fdt. * @param fdt Pointer to the fdt object. * @param node The node to get the name of. - * @return Valid buffer if success, invalid buffer if error. + * @param[out] bufOUT Buffer with the name. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args or misaligned @p node + * @retval ERR_NOT_VALID if the FDT is invalid */ -buffer_t dt_get_node_name(const fdt_t* fdt, dt_node_t node); +[[gnu::nonnull(3)]] +error_t dt_get_node_name(const fdt_t* fdt, dt_node_t node, buffer_t* bufOUT); /** - * @ingroup dt * @brief Get a node's name as a pointer in fdt. * @param fdt Pointer to the fdt object. * @param node The node to get the name of. - * @return Pointer to the node name. + * @param[out] ptrOUT Pointer to the name. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args or misaligned @p node + * @retval ERR_NOT_VALID if the FDT is invalid */ -const char* dt_get_node_name_ptr(const fdt_t* fdt, dt_node_t node); +[[gnu::nonnull(3)]] +error_t dt_get_node_name_ptr(const fdt_t* fdt, dt_node_t node, const char** ptrOUT); /** - * @ingroup dt * @brief Get a node's prop_name property in fdt. * @param fdt Pointer to the fdt object. * @param node The node to search in. * @param prop_name Name of the property. - * @return >0 if success, 0 if error. + * @param[out] propOUT Property. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args or misaligned @p node + * @retval ERR_NOT_VALID if the FDT is invalid + * @retval ERR_NOT_FOUND if no prop found + * @retval ERR_OUT_OF_BOUNDS if search went out of bounds */ -dt_prop_t dt_get_prop_by_name(const fdt_t* fdt, dt_node_t node, const char* prop_name); +[[gnu::nonnull(4)]] +error_t dt_get_prop_by_name(const fdt_t* fdt, dt_node_t node, const char* prop_name, dt_prop_t* propOUT); /** - * @ingroup dt * @brief Get a node's first property in fdt. * @param fdt Pointer to the fdt object. * @param node The node to get the first property of. - * @return 0 if no properties exist or error, >0 if success. + * @param[out] propOUT First property. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args or misaligned @p node + * @retval ERR_NOT_VALID if the FDT is invalid + * @retval ERR_NOT_FOUND if no prop found + * @retval ERR_OUT_OF_BOUNDS if search went out of bounds */ -dt_prop_t dt_get_first_prop(const fdt_t* fdt, dt_node_t node); +[[gnu::nonnull(3)]] +error_t dt_get_first_prop(const fdt_t* fdt, dt_node_t node, dt_prop_t* propOUT); /** - * @ingroup dt * @brief Get a node's next property after prop in fdt. * @param fdt Pointer to the fdt object. * @param prop The current property. - * @return 0 if no next properties exist or error, >0 if success. + * @param[out] propOUT Next property. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args or misaligned @p prop + * @retval ERR_NOT_VALID if the FDT is invalid + * @retval ERR_NOT_FOUND if no prop found + * @retval ERR_OUT_OF_BOUNDS if search went out of bounds */ -dt_prop_t dt_get_next_prop(const fdt_t* fdt, dt_prop_t prop); +[[gnu::nonnull(3)]] +error_t dt_get_next_prop(const fdt_t* fdt, dt_prop_t prop, dt_prop_t* propOUT); /** - * @ingroup dt * @brief Get a prop's name as a buffer in fdt. * @param fdt Pointer to the fdt object. * @param prop The property to get the name of. - * @return Valid buffer if success, invalid buffer if error. + * @param[out] bufOUT Buffer with the name. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args or misaligned @p prop + * @retval ERR_NOT_VALID if the FDT is invalid */ -buffer_t dt_get_prop_name(const fdt_t* fdt, dt_prop_t prop); +[[gnu::nonnull(3)]] +error_t dt_get_prop_name(const fdt_t* fdt, dt_prop_t prop, buffer_t* bufOUT); /** - * @ingroup dt * @brief Get a prop's name as a pointer in fdt. * @param fdt Pointer to the fdt object. * @param prop The property to get the name of. - * @return Pointer to the property name. + * @param[out] ptrOUT Pointer to the name. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args or misaligned @p prop + * @retval ERR_NOT_VALID if the FDT is invalid */ -const char* dt_get_prop_name_ptr(const fdt_t* fdt, dt_prop_t prop); +[[gnu::nonnull(3)]] +error_t dt_get_prop_name_ptr(const fdt_t* fdt, dt_prop_t prop, const char** ptrOUT); /** - * @ingroup dt * @brief Get a buffer for prop's data. * @param fdt Pointer to the fdt object. * @param prop The property to get the data of. - * @return Buffer containing the property data. + * @param[out] bufOUT Buffer with the data. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args + * @retval ERR_NOT_VALID if the FDT is invalid */ -buffer_t dt_get_prop_buffer(const fdt_t* fdt, dt_prop_t prop); +[[gnu::nonnull(3)]] +error_t dt_get_prop_buffer(const fdt_t* fdt, dt_prop_t prop, buffer_t* bufOUT); + +/// @} -#endif // !_DT_ +#endif // !DT_DT diff --git a/src/example_dtree/entry.c b/src/example_dtree/entry.c index 55b55004..8b8d55ad 100644 --- a/src/example_dtree/entry.c +++ b/src/example_dtree/entry.c @@ -6,24 +6,37 @@ void main([[maybe_unused]] u32 hartid, const void* fdt) { fdt_t fdt_obj; - int a = dt_init(fdt, &fdt_obj); - if (a < 0) { - DEBUG_PRINTF("DT_INIT FAILED %d", a); + // Ignoring further errors for brevity + error_t error = dt_init(fdt, &fdt_obj); + if (error != ERR_NONE) { + DEBUG_PRINTF("DT_INIT FAILED %d", error); return; } - u32 main_node = dt_get_node_by_path(&fdt_obj, "/cpus"); - u32 child = dt_get_node_child(&fdt_obj, main_node); - u32 sibling = dt_get_node_sibling(&fdt_obj, main_node); + u32 main_node; + error = dt_get_node_by_path(&fdt_obj, "/cpus", &main_node); + u32 child; + error = dt_get_node_child(&fdt_obj, main_node, &child); + u32 sibling; + error = dt_get_node_sibling(&fdt_obj, main_node, &sibling); DEBUG_PRINTF("Main node: %u\n", main_node); - DEBUG_PRINTF("Child: %s\n", dt_get_node_name_ptr(&fdt_obj, child)); - DEBUG_PRINTF("Sibling: %s\n", dt_get_node_name_ptr(&fdt_obj, sibling)); - - u32 main_prop = dt_get_prop_by_name(&fdt_obj, main_node, "#address-cells"); - buffer_t buf = dt_get_prop_buffer(&fdt_obj, main_prop); - - const char* prop_name = dt_get_prop_name_ptr(&fdt_obj, main_prop); + buffer_t buff; + const char* name; + error = dt_get_node_name(&fdt_obj, child, &buff); + name = buff.data; + DEBUG_PRINTF("Child: %s\n", name); + error = dt_get_node_name_ptr(&fdt_obj, sibling, &name); + DEBUG_PRINTF("Sibling: %s\n", name); + + u32 main_prop; + error = dt_get_prop_by_name(&fdt_obj, main_node, "#address-cells", &main_prop); + buffer_t buf; + error = dt_get_prop_buffer(&fdt_obj, main_prop, &buf); + + const char* prop_name; + error = dt_get_prop_name(&fdt_obj, main_prop, &buff); + prop_name = buff.data; u32 prop_val; if (!buffer_read_u32_be(buf, 0, &prop_val)) { DEBUG_PRINTF("Bad read from %s\n", prop_name); @@ -32,9 +45,13 @@ void main([[maybe_unused]] u32 hartid, const void* fdt) { DEBUG_PRINTF("Main node prop: %s: %u\n", prop_name, prop_val); - u32 first_prop = dt_get_first_prop(&fdt_obj, main_node); - u32 next_prop = dt_get_next_prop(&fdt_obj, first_prop); + u32 first_prop; + error = dt_get_first_prop(&fdt_obj, main_node, &first_prop); + u32 next_prop; + error = dt_get_next_prop(&fdt_obj, first_prop, &next_prop); - DEBUG_PRINTF("First prop: %s\n", dt_get_prop_name_ptr(&fdt_obj, first_prop)); - DEBUG_PRINTF("Next prop: %s\n", dt_get_prop_name_ptr(&fdt_obj, next_prop)); + error = dt_get_prop_name_ptr(&fdt_obj, first_prop, &prop_name); + DEBUG_PRINTF("First prop: %s\n", prop_name); + error = dt_get_prop_name_ptr(&fdt_obj, next_prop, &prop_name); + DEBUG_PRINTF("Next prop: %s\n", prop_name); } diff --git a/src/lib/dt/dt_access.c b/src/lib/dt/dt_access.c index a69dd402..f4299206 100644 --- a/src/lib/dt/dt_access.c +++ b/src/lib/dt/dt_access.c @@ -27,80 +27,104 @@ static buffer_t buffer_strtok(const char** path, const char* sep) { return make_buffer(p, sz); } -dt_node_t dt_get_node_in_subtree_by_path(const fdt_t* fdt, dt_node_t node, const char* node_path) { - if (!node && !fdt->root_node) - return 0; - if (fdt->root_node) +error_t dt_get_node_in_subtree_by_path(const fdt_t* fdt, dt_node_t node, const char* node_path, dt_node_t* nodeOUT) { + if (fdt == nullptr) + return ERR_BAD_ARG; + + if (!node && fdt->root_node) node = fdt->root_node; while (true) { buffer_t segment_name = buffer_strtok(&node_path, "/"); if (!buffer_is_valid(segment_name)) { - return node; + *nodeOUT = node; + return ERR_NONE; } - node = dt_get_node_child(fdt, node); + error_t error = dt_get_node_child(fdt, node, &node); + + if (error != ERR_NONE) + return error; + bool found = false; - while (node) { - buffer_t child_name = dt_get_node_name(fdt, node); + while (true) { + buffer_t child_name; + + error = dt_get_node_name(fdt, node, &child_name); + + if (error != ERR_NONE) + return error; + if (buffer_equal(child_name, segment_name)) { found = true; break; } - node = dt_get_node_sibling(fdt, node); + error = dt_get_node_sibling(fdt, node, &node); + + if (error != ERR_NONE) + return error; } if (!found) - return 0; + return ERR_NOT_FOUND; } } -dt_node_t dt_get_node_by_path(const fdt_t* fdt, const char* node_path) { - return dt_get_node_in_subtree_by_path(fdt, (dt_node_t)0, node_path); +error_t dt_get_node_by_path(const fdt_t* fdt, const char* node_path, dt_node_t* nodeOUT) { + return dt_get_node_in_subtree_by_path(fdt, (dt_node_t)0, node_path, nodeOUT); } -dt_node_t dt_get_node_child(const fdt_t* fdt, dt_node_t node) { +error_t dt_get_node_child(const fdt_t* fdt, dt_node_t node, dt_node_t* nodeOUT) { + if (fdt == nullptr) + return ERR_BAD_ARG; + buffer_t fdt_buf = fdt->fdt_buffer; - u32 curr_offset = dt_skip_node_header(fdt, node); + u32 curr_offset; + error_t error = dt_skip_node_header(fdt, node, &curr_offset); - if (curr_offset == 0) - return 0; + if (error != ERR_NONE) + return error; u32 max_offset = fdt->struct_off + fdt->struct_size; while (curr_offset < max_offset) { u32 tag; if (!buffer_read_u32_be(fdt_buf, curr_offset, &tag)) - return 0; + return ERR_NOT_VALID; u32 node_offset = curr_offset; curr_offset += sizeof(fdt_token_t); switch ((fdt_token_t)tag) { - case FDT_BEGIN_NODE: return node_offset; + case FDT_BEGIN_NODE: *nodeOUT = node_offset; return ERR_NONE; case FDT_NOP: continue; case FDT_END_NODE: - case FDT_END: return 0; // No such node found + case FDT_END: return ERR_NOT_FOUND; // No such node found - default: // Unknown element type (not defined in v17) + default: // Unknown element type (not defined in v17) DEBUG_PRINTF("Invalid FDT structure\n"); - return 0; + return ERR_NOT_VALID; } } - return 0; + + return ERR_OUT_OF_BOUNDS; } -dt_node_t dt_get_node_sibling(const fdt_t* fdt, dt_node_t node) { +error_t dt_get_node_sibling(const fdt_t* fdt, dt_node_t node, dt_node_t* nodeOUT) { + if (fdt == nullptr) + return ERR_BAD_ARG; + buffer_t fdt_buf = fdt->fdt_buffer; - u32 curr_offset = dt_skip_node_header(fdt, node); + u32 curr_offset; + error_t error = dt_skip_node_header(fdt, node, &curr_offset); - if (curr_offset == 0) - return 0; + if (error != ERR_NONE) + return error; bool not_nested = false; @@ -109,36 +133,42 @@ dt_node_t dt_get_node_sibling(const fdt_t* fdt, dt_node_t node) { while (curr_offset < max_offset) { u32 tag; if (!buffer_read_u32_be(fdt_buf, curr_offset, &tag)) - return 0; + return ERR_NOT_VALID; u32 node_offset = curr_offset; curr_offset += sizeof(fdt_token_t); switch ((fdt_token_t)tag) { case FDT_BEGIN_NODE: - if (not_nested) - return node_offset; + if (not_nested) { + *nodeOUT = node_offset; + return ERR_NONE; + } + + error = dt_skip_nested_nodes(fdt, node_offset, &curr_offset); + if (error != ERR_NONE) + return error; - curr_offset = dt_skip_nested_nodes(fdt, node_offset); - if (curr_offset == 0) - return 0; break; case FDT_NOP: continue; case FDT_END_NODE: not_nested = true; break; - case FDT_END: return 0; // No such node found + case FDT_END: return ERR_NOT_FOUND; // No such node found - default: // Unknown element type (not defined in v17) + default: // Unknown element type (not defined in v17) DEBUG_PRINTF("Invalid FDT structure\n"); - return 0; + return ERR_NOT_VALID; } } - return 0; + return ERR_OUT_OF_BOUNDS; } -buffer_t dt_get_node_name(const fdt_t* fdt, dt_node_t node) { +error_t dt_get_node_name(const fdt_t* fdt, dt_node_t node, buffer_t* bufOUT) { + if (fdt == nullptr) + return ERR_BAD_ARG; + buffer_t fdt_buf = fdt->fdt_buffer; u32 curr_offset = node; @@ -146,48 +176,76 @@ buffer_t dt_get_node_name(const fdt_t* fdt, dt_node_t node) { u32 tag; // Check validity of node - if (!buffer_read_u32_be(fdt_buf, curr_offset, &tag) || (fdt_token_t)tag != FDT_BEGIN_NODE) - return make_buffer(nullptr, 0); + if (!buffer_read_u32_be(fdt_buf, curr_offset, &tag)) + return ERR_NOT_VALID; + + if ((fdt_token_t)tag != FDT_BEGIN_NODE) + return ERR_BAD_ARG; const char* name; u64 name_len; if (!buffer_read_cstring_len(fdt_buf, curr_offset + sizeof(fdt_token_t), &name, &name_len)) - return make_buffer(nullptr, 0); + return ERR_NOT_VALID; + + *bufOUT = buffer_sub_buffer(fdt_buf, curr_offset + sizeof(fdt_token_t), name_len); - return buffer_sub_buffer(fdt_buf, curr_offset + sizeof(fdt_token_t), name_len); + return ERR_NONE; } -const char* dt_get_node_name_ptr(const fdt_t* fdt, dt_node_t node) { - buffer_t b = dt_get_node_name(fdt, node); - return b.data; +error_t dt_get_node_name_ptr(const fdt_t* fdt, dt_node_t node, const char** ptrOUT) { + buffer_t b; + error_t error = dt_get_node_name(fdt, node, &b); + + if (error != ERR_NONE) + return error; + + *ptrOUT = b.data; + + return ERR_NONE; } -dt_prop_t dt_get_prop_by_name(const fdt_t* fdt, dt_node_t node, const char* prop_name) { - if (!node && !fdt->root_node) - return 0; - if (fdt->root_node) - node = fdt->root_node; +error_t dt_get_prop_by_name(const fdt_t* fdt, dt_node_t node, const char* prop_name, dt_prop_t* propOUT) { + if (fdt == nullptr || !prop_name) + return ERR_BAD_ARG; buffer_t name = make_buffer(prop_name, strlen(prop_name)); - dt_prop_t prop = dt_get_first_prop(fdt, node); - while (prop) { - buffer_t child_name = dt_get_prop_name(fdt, prop); + dt_prop_t prop; + error_t error = dt_get_first_prop(fdt, node, &prop); + + if (error != ERR_NONE) + return error; + + while (true) { + buffer_t child_name; + error_t error = dt_get_prop_name(fdt, prop, &child_name); + + if (error != ERR_NONE) + return error; + if (buffer_equal(child_name, name)) { - return prop; + *propOUT = prop; + return ERR_NONE; } - prop = dt_get_next_prop(fdt, prop); + error = dt_get_next_prop(fdt, prop, &prop); + + if (error != ERR_NONE) { + return error; + } } - return 0; } -dt_prop_t dt_get_first_prop(const fdt_t* fdt, dt_node_t node) { +error_t dt_get_first_prop(const fdt_t* fdt, dt_node_t node, dt_prop_t* propOUT) { + if (fdt == nullptr) + return ERR_BAD_ARG; + buffer_t fdt_buf = fdt->fdt_buffer; - u32 curr_offset = dt_skip_node_name(fdt, node); + u32 curr_offset; + error_t error = dt_skip_node_name(fdt, node, &curr_offset); - if (curr_offset == 0) - return 0; + if (error != ERR_NONE) + return error; u32 tag; @@ -195,37 +253,44 @@ dt_prop_t dt_get_first_prop(const fdt_t* fdt, dt_node_t node) { while (curr_offset < max_offset) { if (!buffer_read_u32_be(fdt_buf, curr_offset, &tag)) - return 0; + return ERR_NOT_VALID; switch ((fdt_token_t)tag) { - case FDT_PROP: return curr_offset; + case FDT_PROP: *propOUT = curr_offset; return ERR_NONE; case FDT_NOP: break; + case FDT_END_NODE: + case FDT_BEGIN_NODE: return ERR_NOT_FOUND; + // Something else before the first prop - default: return 0; + default: return ERR_NOT_VALID; } } - return 0; + return ERR_NOT_FOUND; } -dt_prop_t dt_get_next_prop(const fdt_t* fdt, dt_prop_t prop) { +error_t dt_get_next_prop(const fdt_t* fdt, dt_prop_t prop, dt_prop_t* propOUT) { + if (fdt == nullptr) + return ERR_BAD_ARG; + buffer_t fdt_buf = fdt->fdt_buffer; u32 curr_offset = prop; u32 tag; if (!buffer_read_u32_be(fdt_buf, curr_offset, &tag)) - return 0; + return ERR_NOT_VALID; // Check validity of prop if ((fdt_token_t)tag != FDT_PROP) - return 0; + return ERR_BAD_ARG; u32 p_len; - if (!buffer_read_u32_be(fdt_buf, curr_offset + sizeof(u32), &p_len)) - return 0; + if (!buffer_read_u32_be(fdt_buf, curr_offset + sizeof(u32), &p_len)) { + return ERR_NOT_VALID; + } curr_offset += 3 * sizeof(u32); // Skip tag, length, name_offset curr_offset = align_u32(curr_offset + p_len, sizeof(u32)); @@ -233,48 +298,68 @@ dt_prop_t dt_get_next_prop(const fdt_t* fdt, dt_prop_t prop) { u32 max_offset = fdt->struct_off + fdt->struct_size; while (curr_offset < max_offset) { - if (!buffer_read_u32_be(fdt_buf, curr_offset, &tag)) - return 0; + if (!buffer_read_u32_be(fdt_buf, curr_offset, &tag)) { + return ERR_NOT_VALID; + } switch ((fdt_token_t)tag) { - case FDT_PROP: return curr_offset; + case FDT_PROP: *propOUT = curr_offset; return ERR_NONE; case FDT_NOP: break; + case FDT_END_NODE: + case FDT_BEGIN_NODE: return ERR_NOT_FOUND; + // Something else before the next prop - default: return 0; + default: return ERR_NOT_VALID; } } - return 0; + return ERR_OUT_OF_BOUNDS; } -buffer_t dt_get_prop_name(const fdt_t* fdt, dt_prop_t prop) { +error_t dt_get_prop_name(const fdt_t* fdt, dt_prop_t prop, buffer_t* bufOUT) { + if (fdt == nullptr) + return ERR_BAD_ARG; + buffer_t fdt_buf = fdt->fdt_buffer; u32 name_offset; if (!buffer_read_u32_be(fdt_buf, prop + 2 * sizeof(fdt_token_t), &name_offset)) - return make_buffer(nullptr, 0); + return ERR_NOT_VALID; const char* name; u64 name_len; if (!buffer_read_cstring_len(fdt_buf, fdt->strings_off + name_offset, &name, &name_len)) - return make_buffer(nullptr, 0); + return ERR_NOT_VALID; + + *bufOUT = buffer_sub_buffer(fdt_buf, fdt->strings_off + name_offset, name_len); - return buffer_sub_buffer(fdt_buf, fdt->strings_off + name_offset, name_len); + return ERR_NONE; } -const char* dt_get_prop_name_ptr(const fdt_t* fdt, dt_prop_t prop) { - buffer_t buf = dt_get_prop_name(fdt, prop); - return buf.data; +error_t dt_get_prop_name_ptr(const fdt_t* fdt, dt_prop_t prop, const char** ptrOUT) { + buffer_t buf; + error_t error = dt_get_prop_name(fdt, prop, &buf); + + if (error != ERR_NONE) + return error; + + *ptrOUT = buf.data; + return ERR_NONE; } -buffer_t dt_get_prop_buffer(const fdt_t* fdt, dt_prop_t prop) { +error_t dt_get_prop_buffer(const fdt_t* fdt, dt_prop_t prop, buffer_t* bufOUT) { + if (fdt == nullptr) + return ERR_BAD_ARG; + buffer_t fdt_buf = fdt->fdt_buffer; u32 prop_len; if (!buffer_read_u32_be(fdt_buf, prop + sizeof(fdt_token_t), &prop_len)) - return make_buffer(nullptr, 0); + return ERR_NOT_VALID; + + *bufOUT = buffer_sub_buffer(fdt_buf, prop + 3 * sizeof(fdt_token_t), prop_len); - return buffer_sub_buffer(fdt_buf, prop + 3 * sizeof(fdt_token_t), prop_len); + return ERR_NONE; } diff --git a/src/lib/dt/dt_common.c b/src/lib/dt/dt_common.c index 44948256..2d42ec55 100644 --- a/src/lib/dt/dt_common.c +++ b/src/lib/dt/dt_common.c @@ -9,32 +9,43 @@ #include "dt_defines.h" -u32 dt_skip_node_name(const fdt_t* fdt, dt_node_t node) { +error_t dt_skip_node_name(const fdt_t* fdt, dt_node_t node, u32* offsetOUT) { + if (fdt == nullptr) + return ERR_BAD_ARG; + buffer_t fdt_buf = fdt->fdt_buffer; u32 curr_offset = node; u32 tag; + if (!buffer_read_u32_be(fdt_buf, curr_offset, &tag)) + return ERR_NOT_VALID; + // Check validity of node - if (!buffer_read_u32_be(fdt_buf, curr_offset, &tag) || (fdt_token_t)tag != FDT_BEGIN_NODE) - return 0; + if ((fdt_token_t)tag != FDT_BEGIN_NODE) + return ERR_BAD_ARG; curr_offset += sizeof(fdt_token_t); const char* name; if (!buffer_read_cstring(fdt_buf, curr_offset, &name)) - return 0; + return ERR_NOT_VALID; u32 name_len = strlen(name) + 1; // Skip name of node curr_offset = align_u32(curr_offset + name_len, sizeof(u32)); - return curr_offset; + *offsetOUT = curr_offset; + + return ERR_NONE; } -u32 dt_skip_node_properties(const fdt_t* fdt, u32 offset) { +error_t dt_skip_node_properties(const fdt_t* fdt, u32 offset, u32* offsetOUT) { + if (fdt == nullptr) + return ERR_BAD_ARG; + buffer_t fdt_buf = fdt->fdt_buffer; u32 curr_offset = offset; @@ -46,14 +57,14 @@ u32 dt_skip_node_properties(const fdt_t* fdt, u32 offset) { while (curr_offset < max_offset) { u32 tag; if (!buffer_read_u32_be(fdt_buf, curr_offset, &tag)) - return 0; + return ERR_NOT_VALID; if ((fdt_token_t)tag != FDT_PROP) break; u32 p_len; if (!buffer_read_u32_be(fdt_buf, curr_offset + sizeof(u32), &p_len)) - return 0; + return ERR_NOT_VALID; curr_offset += 3 * sizeof(u32); // Skip tag, length, name_offset curr_offset = align_u32(curr_offset + p_len, sizeof(u32)); @@ -62,24 +73,36 @@ u32 dt_skip_node_properties(const fdt_t* fdt, u32 offset) { curr_offset = align_u32(props_off + props_len, sizeof(u32)); - return curr_offset; + *offsetOUT = curr_offset; + + return ERR_NONE; } -u32 dt_skip_node_header(const fdt_t* fdt, dt_node_t node) { +error_t dt_skip_node_header(const fdt_t* fdt, dt_node_t node, u32* offsetOUT) { + if (fdt == nullptr) + return ERR_BAD_ARG; + u32 curr_offset = node; - u32 name_skip = dt_skip_node_name(fdt, curr_offset); + error_t error = dt_skip_node_name(fdt, curr_offset, &curr_offset); - if (name_skip == 0) - return 0; + if (error != ERR_NONE) + return error; - u32 prop_skip = dt_skip_node_properties(fdt, name_skip); + error = dt_skip_node_properties(fdt, curr_offset, &curr_offset); - // If skip dt_skip_node_properties breaks, prop_skip will return 0 - return prop_skip; + if (error != ERR_NONE) + return error; + + *offsetOUT = curr_offset; + + return ERR_NONE; } -u32 dt_skip_nested_nodes(const fdt_t* fdt, dt_node_t nested_node) { +error_t dt_skip_nested_nodes(const fdt_t* fdt, dt_node_t nested_node, dt_node_t* nodeOUT) { + if (fdt == nullptr) + return ERR_BAD_ARG; + buffer_t fdt_buf = fdt->fdt_buffer; u32 curr_offset = nested_node; @@ -96,16 +119,17 @@ u32 dt_skip_nested_nodes(const fdt_t* fdt, dt_node_t nested_node) { u32 tag; if (!buffer_read_u32_be(fdt_buf, curr_offset, &tag)) - return 0; + return ERR_NOT_VALID; switch (tag) { case FDT_BEGIN_NODE: depth += 1; - curr_offset = dt_skip_node_name(fdt, curr_offset); - if (curr_offset == 0) - return 0; + error_t error = dt_skip_node_name(fdt, curr_offset, &curr_offset); + + if (error != ERR_NONE) + return error; break; @@ -115,10 +139,10 @@ u32 dt_skip_nested_nodes(const fdt_t* fdt, dt_node_t nested_node) { break; case FDT_PROP: - curr_offset = dt_skip_node_properties(fdt, curr_offset); + error = dt_skip_node_properties(fdt, curr_offset, &curr_offset); - if (curr_offset == 0) - return 0; + if (error != ERR_NONE) + return error; break; @@ -126,9 +150,11 @@ u32 dt_skip_nested_nodes(const fdt_t* fdt, dt_node_t nested_node) { case FDT_END: default: // Unknown element type (not defined in v17) - return 0; + return ERR_NOT_VALID; } } - return curr_offset; + *nodeOUT = curr_offset; + + return ERR_NONE; } diff --git a/src/lib/dt/dt_common.h b/src/lib/dt/dt_common.h index 40fbefe3..8c42c251 100644 --- a/src/lib/dt/dt_common.h +++ b/src/lib/dt/dt_common.h @@ -1,4 +1,5 @@ /** + * @ingroup dt_internal * @file dt_common.h * @brief Common device tree parsing utilities. */ @@ -7,44 +8,63 @@ #define SRC_LIB_DT_COMMON #include
+#include #include // Offsets are of type u32 +/// @addtogroup dt_internal +/// @ingroup dt +/// @{ + /** - * @ingroup dt - * @brief Skip node's tag and name and return first aligned offset after them. + * @brief Skip node's tag and name and output first aligned offset after them. * @param fdt Pointer to the flattened device tree. * @param node The device tree node. - * @return >0 if success, 0 if error. + * @param[out] offsetOUT First aligned offset after the @p node. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args or misaligned @p node + * @retval ERR_NOT_VALID if the FDT is invalid */ -u32 dt_skip_node_name(const fdt_t* fdt, dt_node_t node); +[[gnu::nonnull(3)]] +error_t dt_skip_node_name(const fdt_t* fdt, dt_node_t node, u32* offsetOUT); /** - * @ingroup dt - * @brief Skip node's properties starting at offset (if they exist) and return first aligned offset after them. + * @brief Skip node's properties starting at offset (if they exist) and output first aligned offset after them. * @param fdt Pointer to the flattened device tree. * @param offset The offset to start skipping properties from. - * @return >0 if success, 0 if error. + * @param[out] offsetOUT First aligned offset after properties at @p offset. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args + * @retval ERR_NOT_VALID if the FDT is invalid */ -u32 dt_skip_node_properties(const fdt_t* fdt, u32 offset); +[[gnu::nonnull(3)]] +error_t dt_skip_node_properties(const fdt_t* fdt, u32 offset, u32* offsetOUT); /** - * @ingroup dt - * @brief Skip node's tag, name and properties and return first aligned offset after them. + * @brief Skip node's tag, name and properties and output first aligned offset after them. * @param fdt Pointer to the flattened device tree. * @param node The device tree node. - * @return >0 if success, 0 if error. + * @param[out] offsetOUT First aligned offset after the @p node. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args or misaligned @p node + * @retval ERR_NOT_VALID if the FDT is invalid */ -u32 dt_skip_node_header(const fdt_t* fdt, dt_node_t node); +[[gnu::nonnull(3)]] +error_t dt_skip_node_header(const fdt_t* fdt, dt_node_t node, u32* offsetOUT); /** - * @ingroup dt * @brief Skip an overarching node's nested nodes starting at the start of the first nested node. * @param fdt Pointer to the flattened device tree. * @param nested_node The first nested node to start skipping from. - * @return >0 if success, 0 if error. + * @param[out] nodeOUT First non-nested node after the @p nested_node. + * @retval ERR_NONE on success + * @retval ERR_BAD_ARG on nullptr args or misaligned @p nested_node + * @retval ERR_NOT_VALID if the FDT is invalid */ -u32 dt_skip_nested_nodes(const fdt_t* fdt, dt_node_t nested_node); +[[gnu::nonnull(3)]] +error_t dt_skip_nested_nodes(const fdt_t* fdt, dt_node_t nested_node, dt_node_t* nodeOUT); + +/// @} #endif // !SRC_LIB_DT_COMMON diff --git a/src/lib/dt/dt_defines.h b/src/lib/dt/dt_defines.h index f45b2727..9ca8c142 100644 --- a/src/lib/dt/dt_defines.h +++ b/src/lib/dt/dt_defines.h @@ -1,5 +1,5 @@ /** - * @ingroup dt + * @ingroup dt_internal * @file dt_defines.h * @brief Magic number, offsets for the device tree header and minimum compatible version. */ @@ -7,8 +7,11 @@ #ifndef SRC_LIB_DT_DEFINES #define SRC_LIB_DT_DEFINES +/// @addtogroup dt_internal +/// @ingroup dt +/// @{ + /** - * @ingroup dt * @name Defines * @{ */ @@ -27,7 +30,6 @@ /** @} */ /** - * @ingroup dt * @brief FDT token values. */ typedef enum : u32 { @@ -39,4 +41,6 @@ typedef enum : u32 { FDT_END = 0x9 /**< End of structure block token. */ } fdt_token_t; +/// @} + #endif // !SRC_LIB_DT_DEFINES diff --git a/src/lib/dt/dt_utils.c b/src/lib/dt/dt_utils.c index af931607..70766a4a 100644 --- a/src/lib/dt/dt_utils.c +++ b/src/lib/dt/dt_utils.c @@ -5,15 +5,15 @@ #include "dt_defines.h" -int dt_init(const void* fdt, fdt_t* obj) { - obj->fdt_buffer = make_buffer(nullptr, 0); +error_t dt_init(const void* fdt, fdt_t* obj) { + if (fdt == nullptr || obj == nullptr) + return ERR_BAD_ARG; - if (!fdt) - return -1; + obj->fdt_buffer = make_buffer(nullptr, 0); u32 magic = read_be32(fdt); if (magic != FDT_MAGIC) - return -1; + return ERR_NOT_VALID; u32 fdt_size = read_be32((u8*)fdt + FDT_OFF_TOTAL_SIZE); @@ -23,7 +23,7 @@ int dt_init(const void* fdt, fdt_t* obj) { if (fdt_buf.size < (FDT_OFF_OFF_DT_STRINGS + 4)) { obj->fdt_buffer = make_buffer(nullptr, 0); - return -1; + return ERR_NOT_VALID; } u32 last_comp_version; @@ -35,22 +35,22 @@ int dt_init(const void* fdt, fdt_t* obj) { !buffer_read_u32_be(fdt_buf, FDT_OFF_VERSION, &obj->fdt_version) || !buffer_read_u32_be(fdt_buf, FDT_OFF_VERSION, &last_comp_version)) { obj->fdt_buffer = make_buffer(nullptr, 0); - return -2; + return ERR_NOT_VALID; } if (FDT_COMPATIBLE_VERSION < last_comp_version || FDT_COMPATIBLE_VERSION > obj->fdt_version) { obj->fdt_buffer = make_buffer(nullptr, 0); - return -3; + return ERR_NOT_VALID; } if (obj->struct_off + obj->struct_size > obj->total_size) { obj->fdt_buffer = make_buffer(nullptr, 0); - return -4; + return ERR_NOT_VALID; } obj->root_node = obj->struct_off; - return 0; + return ERR_NONE; } // Reset properties