xref: /openbmc/linux/kernel/static_call_inline.c (revision 16f6ccde74a6f8538c62f127f17207c75f4dba7a)
18fd4dddaSChristophe Leroy // SPDX-License-Identifier: GPL-2.0
28fd4dddaSChristophe Leroy #include <linux/init.h>
38fd4dddaSChristophe Leroy #include <linux/static_call.h>
48fd4dddaSChristophe Leroy #include <linux/bug.h>
58fd4dddaSChristophe Leroy #include <linux/smp.h>
68fd4dddaSChristophe Leroy #include <linux/sort.h>
78fd4dddaSChristophe Leroy #include <linux/slab.h>
88fd4dddaSChristophe Leroy #include <linux/module.h>
98fd4dddaSChristophe Leroy #include <linux/cpu.h>
108fd4dddaSChristophe Leroy #include <linux/processor.h>
118fd4dddaSChristophe Leroy #include <asm/sections.h>
128fd4dddaSChristophe Leroy 
138fd4dddaSChristophe Leroy extern struct static_call_site __start_static_call_sites[],
148fd4dddaSChristophe Leroy 			       __stop_static_call_sites[];
158fd4dddaSChristophe Leroy extern struct static_call_tramp_key __start_static_call_tramp_key[],
168fd4dddaSChristophe Leroy 				    __stop_static_call_tramp_key[];
178fd4dddaSChristophe Leroy 
18*cd951495SJuergen Gross int static_call_initialized;
197825451fSPeter Zijlstra 
207825451fSPeter Zijlstra /*
217825451fSPeter Zijlstra  * Must be called before early_initcall() to be effective.
227825451fSPeter Zijlstra  */
static_call_force_reinit(void)237825451fSPeter Zijlstra void static_call_force_reinit(void)
247825451fSPeter Zijlstra {
257825451fSPeter Zijlstra 	if (WARN_ON_ONCE(!static_call_initialized))
267825451fSPeter Zijlstra 		return;
277825451fSPeter Zijlstra 
287825451fSPeter Zijlstra 	static_call_initialized++;
297825451fSPeter Zijlstra }
308fd4dddaSChristophe Leroy 
318fd4dddaSChristophe Leroy /* mutex to protect key modules/sites */
328fd4dddaSChristophe Leroy static DEFINE_MUTEX(static_call_mutex);
338fd4dddaSChristophe Leroy 
static_call_lock(void)348fd4dddaSChristophe Leroy static void static_call_lock(void)
358fd4dddaSChristophe Leroy {
368fd4dddaSChristophe Leroy 	mutex_lock(&static_call_mutex);
378fd4dddaSChristophe Leroy }
388fd4dddaSChristophe Leroy 
static_call_unlock(void)398fd4dddaSChristophe Leroy static void static_call_unlock(void)
408fd4dddaSChristophe Leroy {
418fd4dddaSChristophe Leroy 	mutex_unlock(&static_call_mutex);
428fd4dddaSChristophe Leroy }
438fd4dddaSChristophe Leroy 
static_call_addr(struct static_call_site * site)448fd4dddaSChristophe Leroy static inline void *static_call_addr(struct static_call_site *site)
458fd4dddaSChristophe Leroy {
468fd4dddaSChristophe Leroy 	return (void *)((long)site->addr + (long)&site->addr);
478fd4dddaSChristophe Leroy }
488fd4dddaSChristophe Leroy 
__static_call_key(const struct static_call_site * site)498fd4dddaSChristophe Leroy static inline unsigned long __static_call_key(const struct static_call_site *site)
508fd4dddaSChristophe Leroy {
518fd4dddaSChristophe Leroy 	return (long)site->key + (long)&site->key;
528fd4dddaSChristophe Leroy }
538fd4dddaSChristophe Leroy 
static_call_key(const struct static_call_site * site)548fd4dddaSChristophe Leroy static inline struct static_call_key *static_call_key(const struct static_call_site *site)
558fd4dddaSChristophe Leroy {
568fd4dddaSChristophe Leroy 	return (void *)(__static_call_key(site) & ~STATIC_CALL_SITE_FLAGS);
578fd4dddaSChristophe Leroy }
588fd4dddaSChristophe Leroy 
598fd4dddaSChristophe Leroy /* These assume the key is word-aligned. */
static_call_is_init(struct static_call_site * site)608fd4dddaSChristophe Leroy static inline bool static_call_is_init(struct static_call_site *site)
618fd4dddaSChristophe Leroy {
628fd4dddaSChristophe Leroy 	return __static_call_key(site) & STATIC_CALL_SITE_INIT;
638fd4dddaSChristophe Leroy }
648fd4dddaSChristophe Leroy 
static_call_is_tail(struct static_call_site * site)658fd4dddaSChristophe Leroy static inline bool static_call_is_tail(struct static_call_site *site)
668fd4dddaSChristophe Leroy {
678fd4dddaSChristophe Leroy 	return __static_call_key(site) & STATIC_CALL_SITE_TAIL;
688fd4dddaSChristophe Leroy }
698fd4dddaSChristophe Leroy 
static_call_set_init(struct static_call_site * site)708fd4dddaSChristophe Leroy static inline void static_call_set_init(struct static_call_site *site)
718fd4dddaSChristophe Leroy {
728fd4dddaSChristophe Leroy 	site->key = (__static_call_key(site) | STATIC_CALL_SITE_INIT) -
738fd4dddaSChristophe Leroy 		    (long)&site->key;
748fd4dddaSChristophe Leroy }
758fd4dddaSChristophe Leroy 
static_call_site_cmp(const void * _a,const void * _b)768fd4dddaSChristophe Leroy static int static_call_site_cmp(const void *_a, const void *_b)
778fd4dddaSChristophe Leroy {
788fd4dddaSChristophe Leroy 	const struct static_call_site *a = _a;
798fd4dddaSChristophe Leroy 	const struct static_call_site *b = _b;
808fd4dddaSChristophe Leroy 	const struct static_call_key *key_a = static_call_key(a);
818fd4dddaSChristophe Leroy 	const struct static_call_key *key_b = static_call_key(b);
828fd4dddaSChristophe Leroy 
838fd4dddaSChristophe Leroy 	if (key_a < key_b)
848fd4dddaSChristophe Leroy 		return -1;
858fd4dddaSChristophe Leroy 
868fd4dddaSChristophe Leroy 	if (key_a > key_b)
878fd4dddaSChristophe Leroy 		return 1;
888fd4dddaSChristophe Leroy 
898fd4dddaSChristophe Leroy 	return 0;
908fd4dddaSChristophe Leroy }
918fd4dddaSChristophe Leroy 
static_call_site_swap(void * _a,void * _b,int size)928fd4dddaSChristophe Leroy static void static_call_site_swap(void *_a, void *_b, int size)
938fd4dddaSChristophe Leroy {
948fd4dddaSChristophe Leroy 	long delta = (unsigned long)_a - (unsigned long)_b;
958fd4dddaSChristophe Leroy 	struct static_call_site *a = _a;
968fd4dddaSChristophe Leroy 	struct static_call_site *b = _b;
978fd4dddaSChristophe Leroy 	struct static_call_site tmp = *a;
988fd4dddaSChristophe Leroy 
998fd4dddaSChristophe Leroy 	a->addr = b->addr  - delta;
1008fd4dddaSChristophe Leroy 	a->key  = b->key   - delta;
1018fd4dddaSChristophe Leroy 
1028fd4dddaSChristophe Leroy 	b->addr = tmp.addr + delta;
1038fd4dddaSChristophe Leroy 	b->key  = tmp.key  + delta;
1048fd4dddaSChristophe Leroy }
1058fd4dddaSChristophe Leroy 
static_call_sort_entries(struct static_call_site * start,struct static_call_site * stop)1068fd4dddaSChristophe Leroy static inline void static_call_sort_entries(struct static_call_site *start,
1078fd4dddaSChristophe Leroy 					    struct static_call_site *stop)
1088fd4dddaSChristophe Leroy {
1098fd4dddaSChristophe Leroy 	sort(start, stop - start, sizeof(struct static_call_site),
1108fd4dddaSChristophe Leroy 	     static_call_site_cmp, static_call_site_swap);
1118fd4dddaSChristophe Leroy }
1128fd4dddaSChristophe Leroy 
static_call_key_has_mods(struct static_call_key * key)1138fd4dddaSChristophe Leroy static inline bool static_call_key_has_mods(struct static_call_key *key)
1148fd4dddaSChristophe Leroy {
1158fd4dddaSChristophe Leroy 	return !(key->type & 1);
1168fd4dddaSChristophe Leroy }
1178fd4dddaSChristophe Leroy 
static_call_key_next(struct static_call_key * key)1188fd4dddaSChristophe Leroy static inline struct static_call_mod *static_call_key_next(struct static_call_key *key)
1198fd4dddaSChristophe Leroy {
1208fd4dddaSChristophe Leroy 	if (!static_call_key_has_mods(key))
1218fd4dddaSChristophe Leroy 		return NULL;
1228fd4dddaSChristophe Leroy 
1238fd4dddaSChristophe Leroy 	return key->mods;
1248fd4dddaSChristophe Leroy }
1258fd4dddaSChristophe Leroy 
static_call_key_sites(struct static_call_key * key)1268fd4dddaSChristophe Leroy static inline struct static_call_site *static_call_key_sites(struct static_call_key *key)
1278fd4dddaSChristophe Leroy {
1288fd4dddaSChristophe Leroy 	if (static_call_key_has_mods(key))
1298fd4dddaSChristophe Leroy 		return NULL;
1308fd4dddaSChristophe Leroy 
1318fd4dddaSChristophe Leroy 	return (struct static_call_site *)(key->type & ~1);
1328fd4dddaSChristophe Leroy }
1338fd4dddaSChristophe Leroy 
__static_call_update(struct static_call_key * key,void * tramp,void * func)1348fd4dddaSChristophe Leroy void __static_call_update(struct static_call_key *key, void *tramp, void *func)
1358fd4dddaSChristophe Leroy {
1368fd4dddaSChristophe Leroy 	struct static_call_site *site, *stop;
1378fd4dddaSChristophe Leroy 	struct static_call_mod *site_mod, first;
1388fd4dddaSChristophe Leroy 
1398fd4dddaSChristophe Leroy 	cpus_read_lock();
1408fd4dddaSChristophe Leroy 	static_call_lock();
1418fd4dddaSChristophe Leroy 
1428fd4dddaSChristophe Leroy 	if (key->func == func)
1438fd4dddaSChristophe Leroy 		goto done;
1448fd4dddaSChristophe Leroy 
1458fd4dddaSChristophe Leroy 	key->func = func;
1468fd4dddaSChristophe Leroy 
1478fd4dddaSChristophe Leroy 	arch_static_call_transform(NULL, tramp, func, false);
1488fd4dddaSChristophe Leroy 
1498fd4dddaSChristophe Leroy 	/*
1508fd4dddaSChristophe Leroy 	 * If uninitialized, we'll not update the callsites, but they still
1518fd4dddaSChristophe Leroy 	 * point to the trampoline and we just patched that.
1528fd4dddaSChristophe Leroy 	 */
1538fd4dddaSChristophe Leroy 	if (WARN_ON_ONCE(!static_call_initialized))
1548fd4dddaSChristophe Leroy 		goto done;
1558fd4dddaSChristophe Leroy 
1568fd4dddaSChristophe Leroy 	first = (struct static_call_mod){
1578fd4dddaSChristophe Leroy 		.next = static_call_key_next(key),
1588fd4dddaSChristophe Leroy 		.mod = NULL,
1598fd4dddaSChristophe Leroy 		.sites = static_call_key_sites(key),
1608fd4dddaSChristophe Leroy 	};
1618fd4dddaSChristophe Leroy 
1628fd4dddaSChristophe Leroy 	for (site_mod = &first; site_mod; site_mod = site_mod->next) {
1638fd4dddaSChristophe Leroy 		bool init = system_state < SYSTEM_RUNNING;
1648fd4dddaSChristophe Leroy 		struct module *mod = site_mod->mod;
1658fd4dddaSChristophe Leroy 
1668fd4dddaSChristophe Leroy 		if (!site_mod->sites) {
1678fd4dddaSChristophe Leroy 			/*
1688fd4dddaSChristophe Leroy 			 * This can happen if the static call key is defined in
1698fd4dddaSChristophe Leroy 			 * a module which doesn't use it.
1708fd4dddaSChristophe Leroy 			 *
1718fd4dddaSChristophe Leroy 			 * It also happens in the has_mods case, where the
1728fd4dddaSChristophe Leroy 			 * 'first' entry has no sites associated with it.
1738fd4dddaSChristophe Leroy 			 */
1748fd4dddaSChristophe Leroy 			continue;
1758fd4dddaSChristophe Leroy 		}
1768fd4dddaSChristophe Leroy 
1778fd4dddaSChristophe Leroy 		stop = __stop_static_call_sites;
1788fd4dddaSChristophe Leroy 
1798fd4dddaSChristophe Leroy 		if (mod) {
1808fd4dddaSChristophe Leroy #ifdef CONFIG_MODULES
1818fd4dddaSChristophe Leroy 			stop = mod->static_call_sites +
1828fd4dddaSChristophe Leroy 			       mod->num_static_call_sites;
1838fd4dddaSChristophe Leroy 			init = mod->state == MODULE_STATE_COMING;
1848fd4dddaSChristophe Leroy #endif
1858fd4dddaSChristophe Leroy 		}
1868fd4dddaSChristophe Leroy 
1878fd4dddaSChristophe Leroy 		for (site = site_mod->sites;
1888fd4dddaSChristophe Leroy 		     site < stop && static_call_key(site) == key; site++) {
1898fd4dddaSChristophe Leroy 			void *site_addr = static_call_addr(site);
1908fd4dddaSChristophe Leroy 
1918fd4dddaSChristophe Leroy 			if (!init && static_call_is_init(site))
1928fd4dddaSChristophe Leroy 				continue;
1938fd4dddaSChristophe Leroy 
1948fd4dddaSChristophe Leroy 			if (!kernel_text_address((unsigned long)site_addr)) {
1958fd4dddaSChristophe Leroy 				/*
1968fd4dddaSChristophe Leroy 				 * This skips patching built-in __exit, which
1978fd4dddaSChristophe Leroy 				 * is part of init_section_contains() but is
1988fd4dddaSChristophe Leroy 				 * not part of kernel_text_address().
1998fd4dddaSChristophe Leroy 				 *
2008fd4dddaSChristophe Leroy 				 * Skipping built-in __exit is fine since it
2018fd4dddaSChristophe Leroy 				 * will never be executed.
2028fd4dddaSChristophe Leroy 				 */
2038fd4dddaSChristophe Leroy 				WARN_ONCE(!static_call_is_init(site),
2048fd4dddaSChristophe Leroy 					  "can't patch static call site at %pS",
2058fd4dddaSChristophe Leroy 					  site_addr);
2068fd4dddaSChristophe Leroy 				continue;
2078fd4dddaSChristophe Leroy 			}
2088fd4dddaSChristophe Leroy 
2098fd4dddaSChristophe Leroy 			arch_static_call_transform(site_addr, NULL, func,
2108fd4dddaSChristophe Leroy 						   static_call_is_tail(site));
2118fd4dddaSChristophe Leroy 		}
2128fd4dddaSChristophe Leroy 	}
2138fd4dddaSChristophe Leroy 
2148fd4dddaSChristophe Leroy done:
2158fd4dddaSChristophe Leroy 	static_call_unlock();
2168fd4dddaSChristophe Leroy 	cpus_read_unlock();
2178fd4dddaSChristophe Leroy }
2188fd4dddaSChristophe Leroy EXPORT_SYMBOL_GPL(__static_call_update);
2198fd4dddaSChristophe Leroy 
__static_call_init(struct module * mod,struct static_call_site * start,struct static_call_site * stop)2208fd4dddaSChristophe Leroy static int __static_call_init(struct module *mod,
2218fd4dddaSChristophe Leroy 			      struct static_call_site *start,
2228fd4dddaSChristophe Leroy 			      struct static_call_site *stop)
2238fd4dddaSChristophe Leroy {
2248fd4dddaSChristophe Leroy 	struct static_call_site *site;
2258fd4dddaSChristophe Leroy 	struct static_call_key *key, *prev_key = NULL;
2268fd4dddaSChristophe Leroy 	struct static_call_mod *site_mod;
2278fd4dddaSChristophe Leroy 
2288fd4dddaSChristophe Leroy 	if (start == stop)
2298fd4dddaSChristophe Leroy 		return 0;
2308fd4dddaSChristophe Leroy 
2318fd4dddaSChristophe Leroy 	static_call_sort_entries(start, stop);
2328fd4dddaSChristophe Leroy 
2338fd4dddaSChristophe Leroy 	for (site = start; site < stop; site++) {
2348fd4dddaSChristophe Leroy 		void *site_addr = static_call_addr(site);
2358fd4dddaSChristophe Leroy 
2368fd4dddaSChristophe Leroy 		if ((mod && within_module_init((unsigned long)site_addr, mod)) ||
2378fd4dddaSChristophe Leroy 		    (!mod && init_section_contains(site_addr, 1)))
2388fd4dddaSChristophe Leroy 			static_call_set_init(site);
2398fd4dddaSChristophe Leroy 
2408fd4dddaSChristophe Leroy 		key = static_call_key(site);
2418fd4dddaSChristophe Leroy 		if (key != prev_key) {
2428fd4dddaSChristophe Leroy 			prev_key = key;
2438fd4dddaSChristophe Leroy 
2448fd4dddaSChristophe Leroy 			/*
2458fd4dddaSChristophe Leroy 			 * For vmlinux (!mod) avoid the allocation by storing
2468fd4dddaSChristophe Leroy 			 * the sites pointer in the key itself. Also see
2478fd4dddaSChristophe Leroy 			 * __static_call_update()'s @first.
2488fd4dddaSChristophe Leroy 			 *
2498fd4dddaSChristophe Leroy 			 * This allows architectures (eg. x86) to call
2508fd4dddaSChristophe Leroy 			 * static_call_init() before memory allocation works.
2518fd4dddaSChristophe Leroy 			 */
2528fd4dddaSChristophe Leroy 			if (!mod) {
2538fd4dddaSChristophe Leroy 				key->sites = site;
2548fd4dddaSChristophe Leroy 				key->type |= 1;
2558fd4dddaSChristophe Leroy 				goto do_transform;
2568fd4dddaSChristophe Leroy 			}
2578fd4dddaSChristophe Leroy 
2588fd4dddaSChristophe Leroy 			site_mod = kzalloc(sizeof(*site_mod), GFP_KERNEL);
2598fd4dddaSChristophe Leroy 			if (!site_mod)
2608fd4dddaSChristophe Leroy 				return -ENOMEM;
2618fd4dddaSChristophe Leroy 
2628fd4dddaSChristophe Leroy 			/*
2638fd4dddaSChristophe Leroy 			 * When the key has a direct sites pointer, extract
2648fd4dddaSChristophe Leroy 			 * that into an explicit struct static_call_mod, so we
2658fd4dddaSChristophe Leroy 			 * can have a list of modules.
2668fd4dddaSChristophe Leroy 			 */
2678fd4dddaSChristophe Leroy 			if (static_call_key_sites(key)) {
2688fd4dddaSChristophe Leroy 				site_mod->mod = NULL;
2698fd4dddaSChristophe Leroy 				site_mod->next = NULL;
2708fd4dddaSChristophe Leroy 				site_mod->sites = static_call_key_sites(key);
2718fd4dddaSChristophe Leroy 
2728fd4dddaSChristophe Leroy 				key->mods = site_mod;
2738fd4dddaSChristophe Leroy 
2748fd4dddaSChristophe Leroy 				site_mod = kzalloc(sizeof(*site_mod), GFP_KERNEL);
2758fd4dddaSChristophe Leroy 				if (!site_mod)
2768fd4dddaSChristophe Leroy 					return -ENOMEM;
2778fd4dddaSChristophe Leroy 			}
2788fd4dddaSChristophe Leroy 
2798fd4dddaSChristophe Leroy 			site_mod->mod = mod;
2808fd4dddaSChristophe Leroy 			site_mod->sites = site;
2818fd4dddaSChristophe Leroy 			site_mod->next = static_call_key_next(key);
2828fd4dddaSChristophe Leroy 			key->mods = site_mod;
2838fd4dddaSChristophe Leroy 		}
2848fd4dddaSChristophe Leroy 
2858fd4dddaSChristophe Leroy do_transform:
2868fd4dddaSChristophe Leroy 		arch_static_call_transform(site_addr, NULL, key->func,
2878fd4dddaSChristophe Leroy 				static_call_is_tail(site));
2888fd4dddaSChristophe Leroy 	}
2898fd4dddaSChristophe Leroy 
2908fd4dddaSChristophe Leroy 	return 0;
2918fd4dddaSChristophe Leroy }
2928fd4dddaSChristophe Leroy 
addr_conflict(struct static_call_site * site,void * start,void * end)2938fd4dddaSChristophe Leroy static int addr_conflict(struct static_call_site *site, void *start, void *end)
2948fd4dddaSChristophe Leroy {
2958fd4dddaSChristophe Leroy 	unsigned long addr = (unsigned long)static_call_addr(site);
2968fd4dddaSChristophe Leroy 
2978fd4dddaSChristophe Leroy 	if (addr <= (unsigned long)end &&
2988fd4dddaSChristophe Leroy 	    addr + CALL_INSN_SIZE > (unsigned long)start)
2998fd4dddaSChristophe Leroy 		return 1;
3008fd4dddaSChristophe Leroy 
3018fd4dddaSChristophe Leroy 	return 0;
3028fd4dddaSChristophe Leroy }
3038fd4dddaSChristophe Leroy 
__static_call_text_reserved(struct static_call_site * iter_start,struct static_call_site * iter_stop,void * start,void * end,bool init)3048fd4dddaSChristophe Leroy static int __static_call_text_reserved(struct static_call_site *iter_start,
3058fd4dddaSChristophe Leroy 				       struct static_call_site *iter_stop,
3068fd4dddaSChristophe Leroy 				       void *start, void *end, bool init)
3078fd4dddaSChristophe Leroy {
3088fd4dddaSChristophe Leroy 	struct static_call_site *iter = iter_start;
3098fd4dddaSChristophe Leroy 
3108fd4dddaSChristophe Leroy 	while (iter < iter_stop) {
3118fd4dddaSChristophe Leroy 		if (init || !static_call_is_init(iter)) {
3128fd4dddaSChristophe Leroy 			if (addr_conflict(iter, start, end))
3138fd4dddaSChristophe Leroy 				return 1;
3148fd4dddaSChristophe Leroy 		}
3158fd4dddaSChristophe Leroy 		iter++;
3168fd4dddaSChristophe Leroy 	}
3178fd4dddaSChristophe Leroy 
3188fd4dddaSChristophe Leroy 	return 0;
3198fd4dddaSChristophe Leroy }
3208fd4dddaSChristophe Leroy 
3218fd4dddaSChristophe Leroy #ifdef CONFIG_MODULES
3228fd4dddaSChristophe Leroy 
__static_call_mod_text_reserved(void * start,void * end)3238fd4dddaSChristophe Leroy static int __static_call_mod_text_reserved(void *start, void *end)
3248fd4dddaSChristophe Leroy {
3258fd4dddaSChristophe Leroy 	struct module *mod;
3268fd4dddaSChristophe Leroy 	int ret;
3278fd4dddaSChristophe Leroy 
3288fd4dddaSChristophe Leroy 	preempt_disable();
3298fd4dddaSChristophe Leroy 	mod = __module_text_address((unsigned long)start);
3308fd4dddaSChristophe Leroy 	WARN_ON_ONCE(__module_text_address((unsigned long)end) != mod);
3318fd4dddaSChristophe Leroy 	if (!try_module_get(mod))
3328fd4dddaSChristophe Leroy 		mod = NULL;
3338fd4dddaSChristophe Leroy 	preempt_enable();
3348fd4dddaSChristophe Leroy 
3358fd4dddaSChristophe Leroy 	if (!mod)
3368fd4dddaSChristophe Leroy 		return 0;
3378fd4dddaSChristophe Leroy 
3388fd4dddaSChristophe Leroy 	ret = __static_call_text_reserved(mod->static_call_sites,
3398fd4dddaSChristophe Leroy 			mod->static_call_sites + mod->num_static_call_sites,
3408fd4dddaSChristophe Leroy 			start, end, mod->state == MODULE_STATE_COMING);
3418fd4dddaSChristophe Leroy 
3428fd4dddaSChristophe Leroy 	module_put(mod);
3438fd4dddaSChristophe Leroy 
3448fd4dddaSChristophe Leroy 	return ret;
3458fd4dddaSChristophe Leroy }
3468fd4dddaSChristophe Leroy 
tramp_key_lookup(unsigned long addr)3478fd4dddaSChristophe Leroy static unsigned long tramp_key_lookup(unsigned long addr)
3488fd4dddaSChristophe Leroy {
3498fd4dddaSChristophe Leroy 	struct static_call_tramp_key *start = __start_static_call_tramp_key;
3508fd4dddaSChristophe Leroy 	struct static_call_tramp_key *stop = __stop_static_call_tramp_key;
3518fd4dddaSChristophe Leroy 	struct static_call_tramp_key *tramp_key;
3528fd4dddaSChristophe Leroy 
3538fd4dddaSChristophe Leroy 	for (tramp_key = start; tramp_key != stop; tramp_key++) {
3548fd4dddaSChristophe Leroy 		unsigned long tramp;
3558fd4dddaSChristophe Leroy 
3568fd4dddaSChristophe Leroy 		tramp = (long)tramp_key->tramp + (long)&tramp_key->tramp;
3578fd4dddaSChristophe Leroy 		if (tramp == addr)
3588fd4dddaSChristophe Leroy 			return (long)tramp_key->key + (long)&tramp_key->key;
3598fd4dddaSChristophe Leroy 	}
3608fd4dddaSChristophe Leroy 
3618fd4dddaSChristophe Leroy 	return 0;
3628fd4dddaSChristophe Leroy }
3638fd4dddaSChristophe Leroy 
static_call_add_module(struct module * mod)3648fd4dddaSChristophe Leroy static int static_call_add_module(struct module *mod)
3658fd4dddaSChristophe Leroy {
3668fd4dddaSChristophe Leroy 	struct static_call_site *start = mod->static_call_sites;
3678fd4dddaSChristophe Leroy 	struct static_call_site *stop = start + mod->num_static_call_sites;
3688fd4dddaSChristophe Leroy 	struct static_call_site *site;
3698fd4dddaSChristophe Leroy 
3708fd4dddaSChristophe Leroy 	for (site = start; site != stop; site++) {
3718fd4dddaSChristophe Leroy 		unsigned long s_key = __static_call_key(site);
3728fd4dddaSChristophe Leroy 		unsigned long addr = s_key & ~STATIC_CALL_SITE_FLAGS;
3738fd4dddaSChristophe Leroy 		unsigned long key;
3748fd4dddaSChristophe Leroy 
3758fd4dddaSChristophe Leroy 		/*
3768fd4dddaSChristophe Leroy 		 * Is the key is exported, 'addr' points to the key, which
3778fd4dddaSChristophe Leroy 		 * means modules are allowed to call static_call_update() on
3788fd4dddaSChristophe Leroy 		 * it.
3798fd4dddaSChristophe Leroy 		 *
3808fd4dddaSChristophe Leroy 		 * Otherwise, the key isn't exported, and 'addr' points to the
3818fd4dddaSChristophe Leroy 		 * trampoline so we need to lookup the key.
3828fd4dddaSChristophe Leroy 		 *
3838fd4dddaSChristophe Leroy 		 * We go through this dance to prevent crazy modules from
3848fd4dddaSChristophe Leroy 		 * abusing sensitive static calls.
3858fd4dddaSChristophe Leroy 		 */
3868fd4dddaSChristophe Leroy 		if (!kernel_text_address(addr))
3878fd4dddaSChristophe Leroy 			continue;
3888fd4dddaSChristophe Leroy 
3898fd4dddaSChristophe Leroy 		key = tramp_key_lookup(addr);
3908fd4dddaSChristophe Leroy 		if (!key) {
3918fd4dddaSChristophe Leroy 			pr_warn("Failed to fixup __raw_static_call() usage at: %ps\n",
3928fd4dddaSChristophe Leroy 				static_call_addr(site));
3938fd4dddaSChristophe Leroy 			return -EINVAL;
3948fd4dddaSChristophe Leroy 		}
3958fd4dddaSChristophe Leroy 
3968fd4dddaSChristophe Leroy 		key |= s_key & STATIC_CALL_SITE_FLAGS;
3978fd4dddaSChristophe Leroy 		site->key = key - (long)&site->key;
3988fd4dddaSChristophe Leroy 	}
3998fd4dddaSChristophe Leroy 
4008fd4dddaSChristophe Leroy 	return __static_call_init(mod, start, stop);
4018fd4dddaSChristophe Leroy }
4028fd4dddaSChristophe Leroy 
static_call_del_module(struct module * mod)4038fd4dddaSChristophe Leroy static void static_call_del_module(struct module *mod)
4048fd4dddaSChristophe Leroy {
4058fd4dddaSChristophe Leroy 	struct static_call_site *start = mod->static_call_sites;
4068fd4dddaSChristophe Leroy 	struct static_call_site *stop = mod->static_call_sites +
4078fd4dddaSChristophe Leroy 					mod->num_static_call_sites;
4088fd4dddaSChristophe Leroy 	struct static_call_key *key, *prev_key = NULL;
4098fd4dddaSChristophe Leroy 	struct static_call_mod *site_mod, **prev;
4108fd4dddaSChristophe Leroy 	struct static_call_site *site;
4118fd4dddaSChristophe Leroy 
4128fd4dddaSChristophe Leroy 	for (site = start; site < stop; site++) {
4138fd4dddaSChristophe Leroy 		key = static_call_key(site);
414c0abbbe8SThomas Gleixner 
415c0abbbe8SThomas Gleixner 		/*
416c0abbbe8SThomas Gleixner 		 * If the key was not updated due to a memory allocation
417c0abbbe8SThomas Gleixner 		 * failure in __static_call_init() then treating key::sites
418c0abbbe8SThomas Gleixner 		 * as key::mods in the code below would cause random memory
419c0abbbe8SThomas Gleixner 		 * access and #GP. In that case all subsequent sites have
420c0abbbe8SThomas Gleixner 		 * not been touched either, so stop iterating.
421c0abbbe8SThomas Gleixner 		 */
422c0abbbe8SThomas Gleixner 		if (!static_call_key_has_mods(key))
423c0abbbe8SThomas Gleixner 			break;
424c0abbbe8SThomas Gleixner 
4258fd4dddaSChristophe Leroy 		if (key == prev_key)
4268fd4dddaSChristophe Leroy 			continue;
4278fd4dddaSChristophe Leroy 
4288fd4dddaSChristophe Leroy 		prev_key = key;
4298fd4dddaSChristophe Leroy 
4308fd4dddaSChristophe Leroy 		for (prev = &key->mods, site_mod = key->mods;
4318fd4dddaSChristophe Leroy 		     site_mod && site_mod->mod != mod;
4328fd4dddaSChristophe Leroy 		     prev = &site_mod->next, site_mod = site_mod->next)
4338fd4dddaSChristophe Leroy 			;
4348fd4dddaSChristophe Leroy 
4358fd4dddaSChristophe Leroy 		if (!site_mod)
4368fd4dddaSChristophe Leroy 			continue;
4378fd4dddaSChristophe Leroy 
4388fd4dddaSChristophe Leroy 		*prev = site_mod->next;
4398fd4dddaSChristophe Leroy 		kfree(site_mod);
4408fd4dddaSChristophe Leroy 	}
4418fd4dddaSChristophe Leroy }
4428fd4dddaSChristophe Leroy 
static_call_module_notify(struct notifier_block * nb,unsigned long val,void * data)4438fd4dddaSChristophe Leroy static int static_call_module_notify(struct notifier_block *nb,
4448fd4dddaSChristophe Leroy 				     unsigned long val, void *data)
4458fd4dddaSChristophe Leroy {
4468fd4dddaSChristophe Leroy 	struct module *mod = data;
4478fd4dddaSChristophe Leroy 	int ret = 0;
4488fd4dddaSChristophe Leroy 
4498fd4dddaSChristophe Leroy 	cpus_read_lock();
4508fd4dddaSChristophe Leroy 	static_call_lock();
4518fd4dddaSChristophe Leroy 
4528fd4dddaSChristophe Leroy 	switch (val) {
4538fd4dddaSChristophe Leroy 	case MODULE_STATE_COMING:
4548fd4dddaSChristophe Leroy 		ret = static_call_add_module(mod);
4558fd4dddaSChristophe Leroy 		if (ret) {
456e67534bdSThomas Gleixner 			pr_warn("Failed to allocate memory for static calls\n");
4578fd4dddaSChristophe Leroy 			static_call_del_module(mod);
4588fd4dddaSChristophe Leroy 		}
4598fd4dddaSChristophe Leroy 		break;
4608fd4dddaSChristophe Leroy 	case MODULE_STATE_GOING:
4618fd4dddaSChristophe Leroy 		static_call_del_module(mod);
4628fd4dddaSChristophe Leroy 		break;
4638fd4dddaSChristophe Leroy 	}
4648fd4dddaSChristophe Leroy 
4658fd4dddaSChristophe Leroy 	static_call_unlock();
4668fd4dddaSChristophe Leroy 	cpus_read_unlock();
4678fd4dddaSChristophe Leroy 
4688fd4dddaSChristophe Leroy 	return notifier_from_errno(ret);
4698fd4dddaSChristophe Leroy }
4708fd4dddaSChristophe Leroy 
4718fd4dddaSChristophe Leroy static struct notifier_block static_call_module_nb = {
4728fd4dddaSChristophe Leroy 	.notifier_call = static_call_module_notify,
4738fd4dddaSChristophe Leroy };
4748fd4dddaSChristophe Leroy 
4758fd4dddaSChristophe Leroy #else
4768fd4dddaSChristophe Leroy 
__static_call_mod_text_reserved(void * start,void * end)4778fd4dddaSChristophe Leroy static inline int __static_call_mod_text_reserved(void *start, void *end)
4788fd4dddaSChristophe Leroy {
4798fd4dddaSChristophe Leroy 	return 0;
4808fd4dddaSChristophe Leroy }
4818fd4dddaSChristophe Leroy 
4828fd4dddaSChristophe Leroy #endif /* CONFIG_MODULES */
4838fd4dddaSChristophe Leroy 
static_call_text_reserved(void * start,void * end)4848fd4dddaSChristophe Leroy int static_call_text_reserved(void *start, void *end)
4858fd4dddaSChristophe Leroy {
4868fd4dddaSChristophe Leroy 	bool init = system_state < SYSTEM_RUNNING;
4878fd4dddaSChristophe Leroy 	int ret = __static_call_text_reserved(__start_static_call_sites,
4888fd4dddaSChristophe Leroy 			__stop_static_call_sites, start, end, init);
4898fd4dddaSChristophe Leroy 
4908fd4dddaSChristophe Leroy 	if (ret)
4918fd4dddaSChristophe Leroy 		return ret;
4928fd4dddaSChristophe Leroy 
4938fd4dddaSChristophe Leroy 	return __static_call_mod_text_reserved(start, end);
4948fd4dddaSChristophe Leroy }
4958fd4dddaSChristophe Leroy 
static_call_init(void)4968fd4dddaSChristophe Leroy int __init static_call_init(void)
4978fd4dddaSChristophe Leroy {
4988fd4dddaSChristophe Leroy 	int ret;
4998fd4dddaSChristophe Leroy 
5007825451fSPeter Zijlstra 	/* See static_call_force_reinit(). */
5017825451fSPeter Zijlstra 	if (static_call_initialized == 1)
5028fd4dddaSChristophe Leroy 		return 0;
5038fd4dddaSChristophe Leroy 
5048fd4dddaSChristophe Leroy 	cpus_read_lock();
5058fd4dddaSChristophe Leroy 	static_call_lock();
5068fd4dddaSChristophe Leroy 	ret = __static_call_init(NULL, __start_static_call_sites,
5078fd4dddaSChristophe Leroy 				 __stop_static_call_sites);
5088fd4dddaSChristophe Leroy 	static_call_unlock();
5098fd4dddaSChristophe Leroy 	cpus_read_unlock();
5108fd4dddaSChristophe Leroy 
5118fd4dddaSChristophe Leroy 	if (ret) {
5128fd4dddaSChristophe Leroy 		pr_err("Failed to allocate memory for static_call!\n");
5138fd4dddaSChristophe Leroy 		BUG();
5148fd4dddaSChristophe Leroy 	}
5158fd4dddaSChristophe Leroy 
5168fd4dddaSChristophe Leroy #ifdef CONFIG_MODULES
5177825451fSPeter Zijlstra 	if (!static_call_initialized)
5188fd4dddaSChristophe Leroy 		register_module_notifier(&static_call_module_nb);
5198fd4dddaSChristophe Leroy #endif
5207825451fSPeter Zijlstra 
5217825451fSPeter Zijlstra 	static_call_initialized = 1;
5228fd4dddaSChristophe Leroy 	return 0;
5238fd4dddaSChristophe Leroy }
5248fd4dddaSChristophe Leroy early_initcall(static_call_init);
5258fd4dddaSChristophe Leroy 
5268fd4dddaSChristophe Leroy #ifdef CONFIG_STATIC_CALL_SELFTEST
5278fd4dddaSChristophe Leroy 
func_a(int x)5288fd4dddaSChristophe Leroy static int func_a(int x)
5298fd4dddaSChristophe Leroy {
5308fd4dddaSChristophe Leroy 	return x+1;
5318fd4dddaSChristophe Leroy }
5328fd4dddaSChristophe Leroy 
func_b(int x)5338fd4dddaSChristophe Leroy static int func_b(int x)
5348fd4dddaSChristophe Leroy {
5358fd4dddaSChristophe Leroy 	return x+2;
5368fd4dddaSChristophe Leroy }
5378fd4dddaSChristophe Leroy 
5388fd4dddaSChristophe Leroy DEFINE_STATIC_CALL(sc_selftest, func_a);
5398fd4dddaSChristophe Leroy 
5408fd4dddaSChristophe Leroy static struct static_call_data {
5418fd4dddaSChristophe Leroy       int (*func)(int);
5428fd4dddaSChristophe Leroy       int val;
5438fd4dddaSChristophe Leroy       int expect;
5448fd4dddaSChristophe Leroy } static_call_data [] __initdata = {
5458fd4dddaSChristophe Leroy       { NULL,   2, 3 },
5468fd4dddaSChristophe Leroy       { func_b, 2, 4 },
5478fd4dddaSChristophe Leroy       { func_a, 2, 3 }
5488fd4dddaSChristophe Leroy };
5498fd4dddaSChristophe Leroy 
test_static_call_init(void)5508fd4dddaSChristophe Leroy static int __init test_static_call_init(void)
5518fd4dddaSChristophe Leroy {
5528fd4dddaSChristophe Leroy       int i;
5538fd4dddaSChristophe Leroy 
5548fd4dddaSChristophe Leroy       for (i = 0; i < ARRAY_SIZE(static_call_data); i++ ) {
5558fd4dddaSChristophe Leroy 	      struct static_call_data *scd = &static_call_data[i];
5568fd4dddaSChristophe Leroy 
5578fd4dddaSChristophe Leroy               if (scd->func)
5588fd4dddaSChristophe Leroy                       static_call_update(sc_selftest, scd->func);
5598fd4dddaSChristophe Leroy 
5608fd4dddaSChristophe Leroy               WARN_ON(static_call(sc_selftest)(scd->val) != scd->expect);
5618fd4dddaSChristophe Leroy       }
5628fd4dddaSChristophe Leroy 
5638fd4dddaSChristophe Leroy       return 0;
5648fd4dddaSChristophe Leroy }
5658fd4dddaSChristophe Leroy early_initcall(test_static_call_init);
5668fd4dddaSChristophe Leroy 
5678fd4dddaSChristophe Leroy #endif /* CONFIG_STATIC_CALL_SELFTEST */
568