Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 1 | /* |
| 2 | * QEMU Module Infrastructure |
| 3 | * |
| 4 | * Copyright IBM, Corp. 2009 |
| 5 | * |
| 6 | * Authors: |
| 7 | * Anthony Liguori <aliguori@us.ibm.com> |
| 8 | * |
| 9 | * This work is licensed under the terms of the GNU GPL, version 2. See |
| 10 | * the COPYING file in the top-level directory. |
| 11 | * |
Paolo Bonzini | 6b620ca | 2012-01-13 17:44:23 +0100 | [diff] [blame] | 12 | * Contributions after 2012-01-13 are licensed under the terms of the |
| 13 | * GNU GPL, version 2 or (at your option) any later version. |
Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 14 | */ |
| 15 | |
Peter Maydell | aafd758 | 2016-01-29 17:49:55 +0000 | [diff] [blame] | 16 | #include "qemu/osdep.h" |
Fam Zheng | d844a7b | 2014-02-27 06:31:33 +0800 | [diff] [blame] | 17 | #include "qemu-common.h" |
Paolo Bonzini | aa0d1f4 | 2014-02-25 17:36:55 +0100 | [diff] [blame] | 18 | #ifdef CONFIG_MODULES |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 19 | #include <gmodule.h> |
Paolo Bonzini | aa0d1f4 | 2014-02-25 17:36:55 +0100 | [diff] [blame] | 20 | #endif |
Paolo Bonzini | 1de7afc | 2012-12-17 18:20:00 +0100 | [diff] [blame] | 21 | #include "qemu/queue.h" |
| 22 | #include "qemu/module.h" |
Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 23 | |
| 24 | typedef struct ModuleEntry |
| 25 | { |
Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 26 | void (*init)(void); |
Blue Swirl | 72cf2d4 | 2009-09-12 07:36:22 +0000 | [diff] [blame] | 27 | QTAILQ_ENTRY(ModuleEntry) node; |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 28 | module_init_type type; |
Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 29 | } ModuleEntry; |
| 30 | |
Blue Swirl | 72cf2d4 | 2009-09-12 07:36:22 +0000 | [diff] [blame] | 31 | typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList; |
Anthony Liguori | f789743 | 2009-05-14 17:57:31 -0500 | [diff] [blame] | 32 | |
| 33 | static ModuleTypeList init_type_list[MODULE_INIT_MAX]; |
| 34 | |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 35 | static ModuleTypeList dso_init_list; |
| 36 | |
| 37 | static void init_lists(void) |
Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 38 | { |
Anthony Liguori | f789743 | 2009-05-14 17:57:31 -0500 | [diff] [blame] | 39 | static int inited; |
| 40 | int i; |
Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 41 | |
Anthony Liguori | f789743 | 2009-05-14 17:57:31 -0500 | [diff] [blame] | 42 | if (inited) { |
| 43 | return; |
Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 44 | } |
| 45 | |
Anthony Liguori | f789743 | 2009-05-14 17:57:31 -0500 | [diff] [blame] | 46 | for (i = 0; i < MODULE_INIT_MAX; i++) { |
Blue Swirl | 72cf2d4 | 2009-09-12 07:36:22 +0000 | [diff] [blame] | 47 | QTAILQ_INIT(&init_type_list[i]); |
Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 48 | } |
| 49 | |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 50 | QTAILQ_INIT(&dso_init_list); |
| 51 | |
Anthony Liguori | f789743 | 2009-05-14 17:57:31 -0500 | [diff] [blame] | 52 | inited = 1; |
| 53 | } |
| 54 | |
| 55 | |
| 56 | static ModuleTypeList *find_type(module_init_type type) |
| 57 | { |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 58 | init_lists(); |
Anthony Liguori | f789743 | 2009-05-14 17:57:31 -0500 | [diff] [blame] | 59 | |
Eduardo Habkost | 9be3859 | 2016-06-13 18:57:58 -0300 | [diff] [blame] | 60 | return &init_type_list[type]; |
Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 61 | } |
| 62 | |
| 63 | void register_module_init(void (*fn)(void), module_init_type type) |
| 64 | { |
| 65 | ModuleEntry *e; |
| 66 | ModuleTypeList *l; |
| 67 | |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 68 | e = g_malloc0(sizeof(*e)); |
Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 69 | e->init = fn; |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 70 | e->type = type; |
Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 71 | |
Anthony Liguori | f789743 | 2009-05-14 17:57:31 -0500 | [diff] [blame] | 72 | l = find_type(type); |
Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 73 | |
Blue Swirl | 72cf2d4 | 2009-09-12 07:36:22 +0000 | [diff] [blame] | 74 | QTAILQ_INSERT_TAIL(l, e, node); |
Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 75 | } |
| 76 | |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 77 | void register_dso_module_init(void (*fn)(void), module_init_type type) |
| 78 | { |
| 79 | ModuleEntry *e; |
| 80 | |
| 81 | init_lists(); |
| 82 | |
| 83 | e = g_malloc0(sizeof(*e)); |
| 84 | e->init = fn; |
| 85 | e->type = type; |
| 86 | |
| 87 | QTAILQ_INSERT_TAIL(&dso_init_list, e, node); |
| 88 | } |
| 89 | |
Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 90 | void module_call_init(module_init_type type) |
| 91 | { |
| 92 | ModuleTypeList *l; |
| 93 | ModuleEntry *e; |
| 94 | |
Anthony Liguori | f789743 | 2009-05-14 17:57:31 -0500 | [diff] [blame] | 95 | l = find_type(type); |
Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 96 | |
Blue Swirl | 72cf2d4 | 2009-09-12 07:36:22 +0000 | [diff] [blame] | 97 | QTAILQ_FOREACH(e, l, node) { |
Anthony Liguori | 0bfe3ca | 2009-05-14 19:29:53 +0100 | [diff] [blame] | 98 | e->init(); |
| 99 | } |
| 100 | } |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 101 | |
| 102 | #ifdef CONFIG_MODULES |
| 103 | static int module_load_file(const char *fname) |
| 104 | { |
| 105 | GModule *g_module; |
| 106 | void (*sym)(void); |
| 107 | const char *dsosuf = HOST_DSOSUF; |
| 108 | int len = strlen(fname); |
| 109 | int suf_len = strlen(dsosuf); |
| 110 | ModuleEntry *e, *next; |
| 111 | int ret; |
| 112 | |
| 113 | if (len <= suf_len || strcmp(&fname[len - suf_len], dsosuf)) { |
| 114 | /* wrong suffix */ |
| 115 | ret = -EINVAL; |
| 116 | goto out; |
| 117 | } |
| 118 | if (access(fname, F_OK)) { |
| 119 | ret = -ENOENT; |
| 120 | goto out; |
| 121 | } |
| 122 | |
| 123 | assert(QTAILQ_EMPTY(&dso_init_list)); |
| 124 | |
| 125 | g_module = g_module_open(fname, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); |
| 126 | if (!g_module) { |
| 127 | fprintf(stderr, "Failed to open module: %s\n", |
| 128 | g_module_error()); |
| 129 | ret = -EINVAL; |
| 130 | goto out; |
| 131 | } |
| 132 | if (!g_module_symbol(g_module, DSO_STAMP_FUN_STR, (gpointer *)&sym)) { |
| 133 | fprintf(stderr, "Failed to initialize module: %s\n", |
| 134 | fname); |
| 135 | /* Print some info if this is a QEMU module (but from different build), |
| 136 | * this will make debugging user problems easier. */ |
| 137 | if (g_module_symbol(g_module, "qemu_module_dummy", (gpointer *)&sym)) { |
| 138 | fprintf(stderr, |
| 139 | "Note: only modules from the same build can be loaded.\n"); |
| 140 | } |
| 141 | g_module_close(g_module); |
| 142 | ret = -EINVAL; |
| 143 | } else { |
| 144 | QTAILQ_FOREACH(e, &dso_init_list, node) { |
Marc Mari | 88d8879 | 2016-08-12 09:27:03 -0400 | [diff] [blame] | 145 | e->init(); |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 146 | register_module_init(e->init, e->type); |
| 147 | } |
| 148 | ret = 0; |
| 149 | } |
| 150 | |
| 151 | QTAILQ_FOREACH_SAFE(e, &dso_init_list, node, next) { |
| 152 | QTAILQ_REMOVE(&dso_init_list, e, node); |
| 153 | g_free(e); |
| 154 | } |
| 155 | out: |
| 156 | return ret; |
| 157 | } |
| 158 | #endif |
| 159 | |
Marc Mari | 88d8879 | 2016-08-12 09:27:03 -0400 | [diff] [blame] | 160 | void module_load_one(const char *prefix, const char *lib_name) |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 161 | { |
| 162 | #ifdef CONFIG_MODULES |
| 163 | char *fname = NULL; |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 164 | char *exec_dir; |
ryang | 900610e | 2018-07-04 14:10:10 -0400 | [diff] [blame] | 165 | const char *search_dir; |
| 166 | char *dirs[4]; |
Fam Zheng | dffa41b | 2016-09-05 10:50:44 +0800 | [diff] [blame] | 167 | char *module_name; |
ryang | 900610e | 2018-07-04 14:10:10 -0400 | [diff] [blame] | 168 | int i = 0, n_dirs = 0; |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 169 | int ret; |
Fam Zheng | dffa41b | 2016-09-05 10:50:44 +0800 | [diff] [blame] | 170 | static GHashTable *loaded_modules; |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 171 | |
| 172 | if (!g_module_supported()) { |
| 173 | fprintf(stderr, "Module is not supported by system.\n"); |
| 174 | return; |
| 175 | } |
| 176 | |
Fam Zheng | dffa41b | 2016-09-05 10:50:44 +0800 | [diff] [blame] | 177 | if (!loaded_modules) { |
| 178 | loaded_modules = g_hash_table_new(g_str_hash, g_str_equal); |
| 179 | } |
| 180 | |
| 181 | module_name = g_strdup_printf("%s%s", prefix, lib_name); |
| 182 | |
| 183 | if (g_hash_table_lookup(loaded_modules, module_name)) { |
| 184 | g_free(module_name); |
| 185 | return; |
| 186 | } |
| 187 | g_hash_table_insert(loaded_modules, module_name, module_name); |
| 188 | |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 189 | exec_dir = qemu_get_exec_dir(); |
ryang | 900610e | 2018-07-04 14:10:10 -0400 | [diff] [blame] | 190 | search_dir = getenv("QEMU_MODULE_DIR"); |
| 191 | if (search_dir != NULL) { |
| 192 | dirs[n_dirs++] = g_strdup_printf("%s", search_dir); |
| 193 | } |
| 194 | dirs[n_dirs++] = g_strdup_printf("%s", CONFIG_QEMU_MODDIR); |
| 195 | dirs[n_dirs++] = g_strdup_printf("%s/..", exec_dir ? : ""); |
| 196 | dirs[n_dirs++] = g_strdup_printf("%s", exec_dir ? : ""); |
| 197 | assert(n_dirs <= ARRAY_SIZE(dirs)); |
| 198 | |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 199 | g_free(exec_dir); |
| 200 | exec_dir = NULL; |
| 201 | |
ryang | 900610e | 2018-07-04 14:10:10 -0400 | [diff] [blame] | 202 | for (i = 0; i < n_dirs; i++) { |
Fam Zheng | dffa41b | 2016-09-05 10:50:44 +0800 | [diff] [blame] | 203 | fname = g_strdup_printf("%s/%s%s", |
| 204 | dirs[i], module_name, HOST_DSOSUF); |
Marc Mari | 88d8879 | 2016-08-12 09:27:03 -0400 | [diff] [blame] | 205 | ret = module_load_file(fname); |
| 206 | g_free(fname); |
| 207 | fname = NULL; |
| 208 | /* Try loading until loaded a module file */ |
| 209 | if (!ret) { |
| 210 | break; |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 211 | } |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 212 | } |
| 213 | |
ryang | 900610e | 2018-07-04 14:10:10 -0400 | [diff] [blame] | 214 | for (i = 0; i < n_dirs; i++) { |
Fam Zheng | e26110c | 2014-02-10 14:48:57 +0800 | [diff] [blame] | 215 | g_free(dirs[i]); |
| 216 | } |
| 217 | |
| 218 | #endif |
| 219 | } |