diff --git a/libpdbg/device.c b/libpdbg/device.c index 4a0aa58c5..b8c12cf9a 100644 --- a/libpdbg/device.c +++ b/libpdbg/device.c @@ -701,6 +701,73 @@ static void pdbg_targets_init_virtual(struct pdbg_target *node, struct pdbg_targ } } +/** + * @brief It releases/deletes a target from its target_class list. + * Each pdbg_target object (except the virtuals one) has an + * associated target class linked via it's class_link. This + * function deletes/removes a target form the list of associated + * pdbg_target_class object. For internal use only. + * + * @param target The pdbg_target object which will be removed from it's associated + * pdbg_target_class list + * + * @see pdbg_release_target for more details + */ +static void pdbg_del_target_from_target_class_list(struct pdbg_target* target) +{ + if (!target) + return; + + struct pdbg_target_class* target_class = get_target_class(target); + if (target_class) + list_del_from(&target_class->targets, &target->class_link); +} + +/** + * @brief A function to delete/release an existing pdbg_target object along + * with its children from the node list/dev tree. Recursive in nature. + * For internal use only. + * + * @param target The target node for which the children and then the node itself + * will be released/freed + * + * @see pdbg_release_dt_root for more details + */ +static void pdbg_release_target(struct pdbg_target* target) +{ + if (!target) + return; + + struct pdbg_target *childTarget, *next = NULL; + list_for_each_safe(&target->children, childTarget, next, list) + pdbg_release_target(childTarget); + + if (target->class) + pdbg_del_target_from_target_class_list(target); + + struct pdbg_target* parentTarget = target->parent; + if (parentTarget) + list_del_from(&parentTarget->children, &target->list); + + if (target) + free(target); + target = NULL; +} + +void pdbg_release_dt_root() +{ + if (pdbg_dt_root) + { + pdbg_release_target(pdbg_dt_root); + if (pdbg_dt_root) + pdbg_dt_root = NULL; + //Reset the phandle count to zero + last_phandle = 0; + //Clear the existing target classes + clear_target_classes(); + } +} + bool pdbg_targets_init(void *fdt) { struct pdbg_dtb *dtb; diff --git a/libpdbg/dtb.c b/libpdbg/dtb.c index 2b084f74a..20de9a10e 100644 --- a/libpdbg/dtb.c +++ b/libpdbg/dtb.c @@ -412,8 +412,16 @@ static void mmap_dtb(const char *file, bool readonly, struct pdbg_mfile *mfile) bool pdbg_set_backend(enum pdbg_backend backend, const char *backend_option) { - if (pdbg_target_root()) { - pdbg_log(PDBG_ERROR, "pdbg_set_backend() must be called before pdbg_targets_init()\n"); + if (pdbg_target_root()) + { + pdbg_log(PDBG_ERROR, "pdbg_set_backend() must be called before pdbg_targets_init() or + after calling pdbg_release_dt_root() if a dev tree is already set\n"); + return false; + } + + if (pdbg_backend == backend) + { + pdbg_log(PDBG_ERROR, "New backend is same as the current backend. Not proceeding further\n"); return false; } diff --git a/libpdbg/libpdbg.h b/libpdbg/libpdbg.h index 252b1b162..59d39ed58 100644 --- a/libpdbg/libpdbg.h +++ b/libpdbg/libpdbg.h @@ -1452,6 +1452,18 @@ void pdbg_log(int loglevel, const char *fmt, ...); */ bool pdbg_context_short(void); +/** + * @brief Clears/Releases the existing device tree and it's root node + * + * This function needs to be called if for some very good reason we are + * switching the backend in the same process. It clears/releases the + * existing dev tree (if any) children by children along with the root node. + * + * Call this function before ##pdbg_set_backend() if there is a + * dev tree already is in place and we are switching the backend + */ +void pdbg_release_dt_root(); + #ifdef __cplusplus } #endif diff --git a/libpdbg/target.c b/libpdbg/target.c index a6935b1d5..1599392c7 100644 --- a/libpdbg/target.c +++ b/libpdbg/target.c @@ -638,3 +638,16 @@ struct pdbg_target *target_to_virtual(struct pdbg_target *target, bool strict) return target; } + +void clear_target_classes() +{ + struct pdbg_target_class *child = NULL; + struct pdbg_target_class *next = NULL; + list_for_each_safe(&target_classes, child, next, class_head_link) + { + list_del_from(&target_classes, &child->class_head_link); + if (child) + free(child); + child = NULL; + } +} diff --git a/libpdbg/target.h b/libpdbg/target.h index 5cc694c2a..ff274e3a3 100644 --- a/libpdbg/target.h +++ b/libpdbg/target.h @@ -83,6 +83,17 @@ struct pdbg_target_class *require_target_class(const char *name); struct pdbg_target_class *get_target_class(struct pdbg_target *target); bool pdbg_target_is_class(struct pdbg_target *target, const char *class); +/** + * @brief Clears the list of target classes + * It clears the list of target classes + * from the global static list target_classes + * once the device tree is cleared and associated + * all pdbg_target objects are destroyed + * + * @see pdbg_release_dt_root() for more details + */ +void clear_target_classes(); + extern struct list_head empty_list; extern struct list_head target_classes;