From db2c1be6f1b6b8605e3949473a8c18dc79dfc22c Mon Sep 17 00:00:00 2001 From: Swarnendu Roy Chowdhury Date: Tue, 30 May 2023 01:59:07 -0500 Subject: [PATCH] Adding helper functions for switching of devtree backend in the same process Context: It is now required that the devtree backend in BMC systems should be able to switch in the same process on runtime e.g.; from KERNEL to SBEFIFO or vice-versa. This is to support running extract SBE RC hardware procedure. This commit introduces the necessary changes in order to achieve that. Solution: It basically introduces few functions which will clear the pdbg_dt_root and its associated children subsequently if the pdbg_targets_init function is called more than once now (earlier it was not allowed to call pdbg_targets_init function twice in the same process) Test-1: The switching of the backend in the same process was tested through a standalone application which runs in a loop for multiple times and each time it sets the intial backend to SBEFIFO first and then switched it to KERNEL. Trace log is attached along with the PR Test-2: While booting BMC up injected a clock error dueing IPL in between istep0 to istep2 and call the SBR RC extract procedure to get the error code after switching the backend from SBEFIFO to KERNEL. Once the extraction is done switched back to SBEFIFO for further processing. Journal log is attached with the PR. Signed-off-by: swarnendu.roy.chowdhury@ibm.com --- libpdbg/device.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++ libpdbg/dtb.c | 12 +++++++-- libpdbg/libpdbg.h | 12 +++++++++ libpdbg/target.c | 13 +++++++++ libpdbg/target.h | 11 ++++++++ 5 files changed, 113 insertions(+), 2 deletions(-) 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;