1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * (C) Copyright 2012 4 * Joe Hershberger, National Instruments, joe.hershberger@ni.com 5 */ 6 7 #include <common.h> 8 #include <environment.h> 9 10 #if defined(CONFIG_NEEDS_MANUAL_RELOC) 11 DECLARE_GLOBAL_DATA_PTR; 12 #endif 13 14 /* 15 * Look up a callback function pointer by name 16 */ 17 static struct env_clbk_tbl *find_env_callback(const char *name) 18 { 19 struct env_clbk_tbl *clbkp; 20 int i; 21 int num_callbacks = ll_entry_count(struct env_clbk_tbl, env_clbk); 22 23 if (name == NULL) 24 return NULL; 25 26 /* look up the callback in the linker-list */ 27 for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk); 28 i < num_callbacks; 29 i++, clbkp++) { 30 if (strcmp(name, clbkp->name) == 0) 31 return clbkp; 32 } 33 34 return NULL; 35 } 36 37 static int first_call = 1; 38 static const char *callback_list; 39 40 /* 41 * Look for a possible callback for a newly added variable 42 * This is called specifically when the variable did not exist in the hash 43 * previously, so the blanket update did not find this variable. 44 */ 45 void env_callback_init(ENTRY *var_entry) 46 { 47 const char *var_name = var_entry->key; 48 char callback_name[256] = ""; 49 struct env_clbk_tbl *clbkp; 50 int ret = 1; 51 52 if (first_call) { 53 callback_list = env_get(ENV_CALLBACK_VAR); 54 first_call = 0; 55 } 56 57 /* look in the ".callbacks" var for a reference to this variable */ 58 if (callback_list != NULL) 59 ret = env_attr_lookup(callback_list, var_name, callback_name); 60 61 /* only if not found there, look in the static list */ 62 if (ret) 63 ret = env_attr_lookup(ENV_CALLBACK_LIST_STATIC, var_name, 64 callback_name); 65 66 /* if an association was found, set the callback pointer */ 67 if (!ret && strlen(callback_name)) { 68 clbkp = find_env_callback(callback_name); 69 if (clbkp != NULL) 70 #if defined(CONFIG_NEEDS_MANUAL_RELOC) 71 var_entry->callback = clbkp->callback + gd->reloc_off; 72 #else 73 var_entry->callback = clbkp->callback; 74 #endif 75 } 76 } 77 78 /* 79 * Called on each existing env var prior to the blanket update since removing 80 * a callback association should remove its callback. 81 */ 82 static int clear_callback(ENTRY *entry) 83 { 84 entry->callback = NULL; 85 86 return 0; 87 } 88 89 /* 90 * Call for each element in the list that associates variables to callbacks 91 */ 92 static int set_callback(const char *name, const char *value, void *priv) 93 { 94 ENTRY e, *ep; 95 struct env_clbk_tbl *clbkp; 96 97 e.key = name; 98 e.data = NULL; 99 e.callback = NULL; 100 hsearch_r(e, FIND, &ep, &env_htab, 0); 101 102 /* does the env variable actually exist? */ 103 if (ep != NULL) { 104 /* the assocaition delares no callback, so remove the pointer */ 105 if (value == NULL || strlen(value) == 0) 106 ep->callback = NULL; 107 else { 108 /* assign the requested callback */ 109 clbkp = find_env_callback(value); 110 if (clbkp != NULL) 111 #if defined(CONFIG_NEEDS_MANUAL_RELOC) 112 ep->callback = clbkp->callback + gd->reloc_off; 113 #else 114 ep->callback = clbkp->callback; 115 #endif 116 } 117 } 118 119 return 0; 120 } 121 122 static int on_callbacks(const char *name, const char *value, enum env_op op, 123 int flags) 124 { 125 /* remove all callbacks */ 126 hwalk_r(&env_htab, clear_callback); 127 128 /* configure any static callback bindings */ 129 env_attr_walk(ENV_CALLBACK_LIST_STATIC, set_callback, NULL); 130 /* configure any dynamic callback bindings */ 131 env_attr_walk(value, set_callback, NULL); 132 133 return 0; 134 } 135 U_BOOT_ENV_CALLBACK(callbacks, on_callbacks); 136