162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * jump label support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009 Jason Baron <jbaron@redhat.com> 662306a36Sopenharmony_ci * Copyright (C) 2011 Peter Zijlstra 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/memory.h> 1062306a36Sopenharmony_ci#include <linux/uaccess.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/list.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/sort.h> 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/static_key.h> 1762306a36Sopenharmony_ci#include <linux/jump_label_ratelimit.h> 1862306a36Sopenharmony_ci#include <linux/bug.h> 1962306a36Sopenharmony_ci#include <linux/cpu.h> 2062306a36Sopenharmony_ci#include <asm/sections.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* mutex to protect coming/going of the jump_label table */ 2362306a36Sopenharmony_cistatic DEFINE_MUTEX(jump_label_mutex); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_civoid jump_label_lock(void) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci mutex_lock(&jump_label_mutex); 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_civoid jump_label_unlock(void) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci mutex_unlock(&jump_label_mutex); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int jump_label_cmp(const void *a, const void *b) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci const struct jump_entry *jea = a; 3862306a36Sopenharmony_ci const struct jump_entry *jeb = b; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* 4162306a36Sopenharmony_ci * Entrires are sorted by key. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci if (jump_entry_key(jea) < jump_entry_key(jeb)) 4462306a36Sopenharmony_ci return -1; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (jump_entry_key(jea) > jump_entry_key(jeb)) 4762306a36Sopenharmony_ci return 1; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* 5062306a36Sopenharmony_ci * In the batching mode, entries should also be sorted by the code 5162306a36Sopenharmony_ci * inside the already sorted list of entries, enabling a bsearch in 5262306a36Sopenharmony_ci * the vector. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci if (jump_entry_code(jea) < jump_entry_code(jeb)) 5562306a36Sopenharmony_ci return -1; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (jump_entry_code(jea) > jump_entry_code(jeb)) 5862306a36Sopenharmony_ci return 1; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void jump_label_swap(void *a, void *b, int size) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci long delta = (unsigned long)a - (unsigned long)b; 6662306a36Sopenharmony_ci struct jump_entry *jea = a; 6762306a36Sopenharmony_ci struct jump_entry *jeb = b; 6862306a36Sopenharmony_ci struct jump_entry tmp = *jea; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci jea->code = jeb->code - delta; 7162306a36Sopenharmony_ci jea->target = jeb->target - delta; 7262306a36Sopenharmony_ci jea->key = jeb->key - delta; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci jeb->code = tmp.code + delta; 7562306a36Sopenharmony_ci jeb->target = tmp.target + delta; 7662306a36Sopenharmony_ci jeb->key = tmp.key + delta; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void 8062306a36Sopenharmony_cijump_label_sort_entries(struct jump_entry *start, struct jump_entry *stop) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci unsigned long size; 8362306a36Sopenharmony_ci void *swapfn = NULL; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE)) 8662306a36Sopenharmony_ci swapfn = jump_label_swap; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci size = (((unsigned long)stop - (unsigned long)start) 8962306a36Sopenharmony_ci / sizeof(struct jump_entry)); 9062306a36Sopenharmony_ci sort(start, size, sizeof(struct jump_entry), jump_label_cmp, swapfn); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void jump_label_update(struct static_key *key); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* 9662306a36Sopenharmony_ci * There are similar definitions for the !CONFIG_JUMP_LABEL case in jump_label.h. 9762306a36Sopenharmony_ci * The use of 'atomic_read()' requires atomic.h and its problematic for some 9862306a36Sopenharmony_ci * kernel headers such as kernel.h and others. Since static_key_count() is not 9962306a36Sopenharmony_ci * used in the branch statements as it is for the !CONFIG_JUMP_LABEL case its ok 10062306a36Sopenharmony_ci * to have it be a function here. Similarly, for 'static_key_enable()' and 10162306a36Sopenharmony_ci * 'static_key_disable()', which require bug.h. This should allow jump_label.h 10262306a36Sopenharmony_ci * to be included from most/all places for CONFIG_JUMP_LABEL. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ciint static_key_count(struct static_key *key) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci /* 10762306a36Sopenharmony_ci * -1 means the first static_key_slow_inc() is in progress. 10862306a36Sopenharmony_ci * static_key_enabled() must return true, so return 1 here. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci int n = atomic_read(&key->enabled); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return n >= 0 ? n : 1; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(static_key_count); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* 11762306a36Sopenharmony_ci * static_key_fast_inc_not_disabled - adds a user for a static key 11862306a36Sopenharmony_ci * @key: static key that must be already enabled 11962306a36Sopenharmony_ci * 12062306a36Sopenharmony_ci * The caller must make sure that the static key can't get disabled while 12162306a36Sopenharmony_ci * in this function. It doesn't patch jump labels, only adds a user to 12262306a36Sopenharmony_ci * an already enabled static key. 12362306a36Sopenharmony_ci * 12462306a36Sopenharmony_ci * Returns true if the increment was done. Unlike refcount_t the ref counter 12562306a36Sopenharmony_ci * is not saturated, but will fail to increment on overflow. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cibool static_key_fast_inc_not_disabled(struct static_key *key) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci int v; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci STATIC_KEY_CHECK_USE(key); 13262306a36Sopenharmony_ci /* 13362306a36Sopenharmony_ci * Negative key->enabled has a special meaning: it sends 13462306a36Sopenharmony_ci * static_key_slow_inc() down the slow path, and it is non-zero 13562306a36Sopenharmony_ci * so it counts as "enabled" in jump_label_update(). Note that 13662306a36Sopenharmony_ci * atomic_inc_unless_negative() checks >= 0, so roll our own. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci v = atomic_read(&key->enabled); 13962306a36Sopenharmony_ci do { 14062306a36Sopenharmony_ci if (v <= 0 || (v + 1) < 0) 14162306a36Sopenharmony_ci return false; 14262306a36Sopenharmony_ci } while (!likely(atomic_try_cmpxchg(&key->enabled, &v, v + 1))); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return true; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(static_key_fast_inc_not_disabled); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cibool static_key_slow_inc_cpuslocked(struct static_key *key) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci lockdep_assert_cpus_held(); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* 15362306a36Sopenharmony_ci * Careful if we get concurrent static_key_slow_inc() calls; 15462306a36Sopenharmony_ci * later calls must wait for the first one to _finish_ the 15562306a36Sopenharmony_ci * jump_label_update() process. At the same time, however, 15662306a36Sopenharmony_ci * the jump_label_update() call below wants to see 15762306a36Sopenharmony_ci * static_key_enabled(&key) for jumps to be updated properly. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci if (static_key_fast_inc_not_disabled(key)) 16062306a36Sopenharmony_ci return true; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci jump_label_lock(); 16362306a36Sopenharmony_ci if (atomic_read(&key->enabled) == 0) { 16462306a36Sopenharmony_ci atomic_set(&key->enabled, -1); 16562306a36Sopenharmony_ci jump_label_update(key); 16662306a36Sopenharmony_ci /* 16762306a36Sopenharmony_ci * Ensure that if the above cmpxchg loop observes our positive 16862306a36Sopenharmony_ci * value, it must also observe all the text changes. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ci atomic_set_release(&key->enabled, 1); 17162306a36Sopenharmony_ci } else { 17262306a36Sopenharmony_ci if (WARN_ON_ONCE(!static_key_fast_inc_not_disabled(key))) { 17362306a36Sopenharmony_ci jump_label_unlock(); 17462306a36Sopenharmony_ci return false; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci jump_label_unlock(); 17862306a36Sopenharmony_ci return true; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cibool static_key_slow_inc(struct static_key *key) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci bool ret; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci cpus_read_lock(); 18662306a36Sopenharmony_ci ret = static_key_slow_inc_cpuslocked(key); 18762306a36Sopenharmony_ci cpus_read_unlock(); 18862306a36Sopenharmony_ci return ret; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(static_key_slow_inc); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_civoid static_key_enable_cpuslocked(struct static_key *key) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci STATIC_KEY_CHECK_USE(key); 19562306a36Sopenharmony_ci lockdep_assert_cpus_held(); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (atomic_read(&key->enabled) > 0) { 19862306a36Sopenharmony_ci WARN_ON_ONCE(atomic_read(&key->enabled) != 1); 19962306a36Sopenharmony_ci return; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci jump_label_lock(); 20362306a36Sopenharmony_ci if (atomic_read(&key->enabled) == 0) { 20462306a36Sopenharmony_ci atomic_set(&key->enabled, -1); 20562306a36Sopenharmony_ci jump_label_update(key); 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * See static_key_slow_inc(). 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci atomic_set_release(&key->enabled, 1); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci jump_label_unlock(); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(static_key_enable_cpuslocked); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_civoid static_key_enable(struct static_key *key) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci cpus_read_lock(); 21862306a36Sopenharmony_ci static_key_enable_cpuslocked(key); 21962306a36Sopenharmony_ci cpus_read_unlock(); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(static_key_enable); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_civoid static_key_disable_cpuslocked(struct static_key *key) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci STATIC_KEY_CHECK_USE(key); 22662306a36Sopenharmony_ci lockdep_assert_cpus_held(); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (atomic_read(&key->enabled) != 1) { 22962306a36Sopenharmony_ci WARN_ON_ONCE(atomic_read(&key->enabled) != 0); 23062306a36Sopenharmony_ci return; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci jump_label_lock(); 23462306a36Sopenharmony_ci if (atomic_cmpxchg(&key->enabled, 1, 0)) 23562306a36Sopenharmony_ci jump_label_update(key); 23662306a36Sopenharmony_ci jump_label_unlock(); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(static_key_disable_cpuslocked); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_civoid static_key_disable(struct static_key *key) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci cpus_read_lock(); 24362306a36Sopenharmony_ci static_key_disable_cpuslocked(key); 24462306a36Sopenharmony_ci cpus_read_unlock(); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(static_key_disable); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic bool static_key_slow_try_dec(struct static_key *key) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci int val; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci val = atomic_fetch_add_unless(&key->enabled, -1, 1); 25362306a36Sopenharmony_ci if (val == 1) 25462306a36Sopenharmony_ci return false; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* 25762306a36Sopenharmony_ci * The negative count check is valid even when a negative 25862306a36Sopenharmony_ci * key->enabled is in use by static_key_slow_inc(); a 25962306a36Sopenharmony_ci * __static_key_slow_dec() before the first static_key_slow_inc() 26062306a36Sopenharmony_ci * returns is unbalanced, because all other static_key_slow_inc() 26162306a36Sopenharmony_ci * instances block while the update is in progress. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci WARN(val < 0, "jump label: negative count!\n"); 26462306a36Sopenharmony_ci return true; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic void __static_key_slow_dec_cpuslocked(struct static_key *key) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci lockdep_assert_cpus_held(); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (static_key_slow_try_dec(key)) 27262306a36Sopenharmony_ci return; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci jump_label_lock(); 27562306a36Sopenharmony_ci if (atomic_dec_and_test(&key->enabled)) 27662306a36Sopenharmony_ci jump_label_update(key); 27762306a36Sopenharmony_ci jump_label_unlock(); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic void __static_key_slow_dec(struct static_key *key) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci cpus_read_lock(); 28362306a36Sopenharmony_ci __static_key_slow_dec_cpuslocked(key); 28462306a36Sopenharmony_ci cpus_read_unlock(); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_civoid jump_label_update_timeout(struct work_struct *work) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct static_key_deferred *key = 29062306a36Sopenharmony_ci container_of(work, struct static_key_deferred, work.work); 29162306a36Sopenharmony_ci __static_key_slow_dec(&key->key); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(jump_label_update_timeout); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_civoid static_key_slow_dec(struct static_key *key) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci STATIC_KEY_CHECK_USE(key); 29862306a36Sopenharmony_ci __static_key_slow_dec(key); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(static_key_slow_dec); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_civoid static_key_slow_dec_cpuslocked(struct static_key *key) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci STATIC_KEY_CHECK_USE(key); 30562306a36Sopenharmony_ci __static_key_slow_dec_cpuslocked(key); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_civoid __static_key_slow_dec_deferred(struct static_key *key, 30962306a36Sopenharmony_ci struct delayed_work *work, 31062306a36Sopenharmony_ci unsigned long timeout) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci STATIC_KEY_CHECK_USE(key); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (static_key_slow_try_dec(key)) 31562306a36Sopenharmony_ci return; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci schedule_delayed_work(work, timeout); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__static_key_slow_dec_deferred); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_civoid __static_key_deferred_flush(void *key, struct delayed_work *work) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci STATIC_KEY_CHECK_USE(key); 32462306a36Sopenharmony_ci flush_delayed_work(work); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__static_key_deferred_flush); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_civoid jump_label_rate_limit(struct static_key_deferred *key, 32962306a36Sopenharmony_ci unsigned long rl) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci STATIC_KEY_CHECK_USE(key); 33262306a36Sopenharmony_ci key->timeout = rl; 33362306a36Sopenharmony_ci INIT_DELAYED_WORK(&key->work, jump_label_update_timeout); 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(jump_label_rate_limit); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic int addr_conflict(struct jump_entry *entry, void *start, void *end) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci if (jump_entry_code(entry) <= (unsigned long)end && 34062306a36Sopenharmony_ci jump_entry_code(entry) + jump_entry_size(entry) > (unsigned long)start) 34162306a36Sopenharmony_ci return 1; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int __jump_label_text_reserved(struct jump_entry *iter_start, 34762306a36Sopenharmony_ci struct jump_entry *iter_stop, void *start, void *end, bool init) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct jump_entry *iter; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci iter = iter_start; 35262306a36Sopenharmony_ci while (iter < iter_stop) { 35362306a36Sopenharmony_ci if (init || !jump_entry_is_init(iter)) { 35462306a36Sopenharmony_ci if (addr_conflict(iter, start, end)) 35562306a36Sopenharmony_ci return 1; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci iter++; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci#ifndef arch_jump_label_transform_static 36462306a36Sopenharmony_cistatic void arch_jump_label_transform_static(struct jump_entry *entry, 36562306a36Sopenharmony_ci enum jump_label_type type) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci /* nothing to do on most architectures */ 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci#endif 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic inline struct jump_entry *static_key_entries(struct static_key *key) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci WARN_ON_ONCE(key->type & JUMP_TYPE_LINKED); 37462306a36Sopenharmony_ci return (struct jump_entry *)(key->type & ~JUMP_TYPE_MASK); 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic inline bool static_key_type(struct static_key *key) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci return key->type & JUMP_TYPE_TRUE; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic inline bool static_key_linked(struct static_key *key) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci return key->type & JUMP_TYPE_LINKED; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic inline void static_key_clear_linked(struct static_key *key) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci key->type &= ~JUMP_TYPE_LINKED; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic inline void static_key_set_linked(struct static_key *key) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci key->type |= JUMP_TYPE_LINKED; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci/*** 39862306a36Sopenharmony_ci * A 'struct static_key' uses a union such that it either points directly 39962306a36Sopenharmony_ci * to a table of 'struct jump_entry' or to a linked list of modules which in 40062306a36Sopenharmony_ci * turn point to 'struct jump_entry' tables. 40162306a36Sopenharmony_ci * 40262306a36Sopenharmony_ci * The two lower bits of the pointer are used to keep track of which pointer 40362306a36Sopenharmony_ci * type is in use and to store the initial branch direction, we use an access 40462306a36Sopenharmony_ci * function which preserves these bits. 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_cistatic void static_key_set_entries(struct static_key *key, 40762306a36Sopenharmony_ci struct jump_entry *entries) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci unsigned long type; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci WARN_ON_ONCE((unsigned long)entries & JUMP_TYPE_MASK); 41262306a36Sopenharmony_ci type = key->type & JUMP_TYPE_MASK; 41362306a36Sopenharmony_ci key->entries = entries; 41462306a36Sopenharmony_ci key->type |= type; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic enum jump_label_type jump_label_type(struct jump_entry *entry) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct static_key *key = jump_entry_key(entry); 42062306a36Sopenharmony_ci bool enabled = static_key_enabled(key); 42162306a36Sopenharmony_ci bool branch = jump_entry_is_branch(entry); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* See the comment in linux/jump_label.h */ 42462306a36Sopenharmony_ci return enabled ^ branch; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic bool jump_label_can_update(struct jump_entry *entry, bool init) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci /* 43062306a36Sopenharmony_ci * Cannot update code that was in an init text area. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ci if (!init && jump_entry_is_init(entry)) 43362306a36Sopenharmony_ci return false; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (!kernel_text_address(jump_entry_code(entry))) { 43662306a36Sopenharmony_ci /* 43762306a36Sopenharmony_ci * This skips patching built-in __exit, which 43862306a36Sopenharmony_ci * is part of init_section_contains() but is 43962306a36Sopenharmony_ci * not part of kernel_text_address(). 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * Skipping built-in __exit is fine since it 44262306a36Sopenharmony_ci * will never be executed. 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_ci WARN_ONCE(!jump_entry_is_init(entry), 44562306a36Sopenharmony_ci "can't patch jump_label at %pS", 44662306a36Sopenharmony_ci (void *)jump_entry_code(entry)); 44762306a36Sopenharmony_ci return false; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return true; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci#ifndef HAVE_JUMP_LABEL_BATCH 45462306a36Sopenharmony_cistatic void __jump_label_update(struct static_key *key, 45562306a36Sopenharmony_ci struct jump_entry *entry, 45662306a36Sopenharmony_ci struct jump_entry *stop, 45762306a36Sopenharmony_ci bool init) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci for (; (entry < stop) && (jump_entry_key(entry) == key); entry++) { 46062306a36Sopenharmony_ci if (jump_label_can_update(entry, init)) 46162306a36Sopenharmony_ci arch_jump_label_transform(entry, jump_label_type(entry)); 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci#else 46562306a36Sopenharmony_cistatic void __jump_label_update(struct static_key *key, 46662306a36Sopenharmony_ci struct jump_entry *entry, 46762306a36Sopenharmony_ci struct jump_entry *stop, 46862306a36Sopenharmony_ci bool init) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci for (; (entry < stop) && (jump_entry_key(entry) == key); entry++) { 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (!jump_label_can_update(entry, init)) 47362306a36Sopenharmony_ci continue; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (!arch_jump_label_transform_queue(entry, jump_label_type(entry))) { 47662306a36Sopenharmony_ci /* 47762306a36Sopenharmony_ci * Queue is full: Apply the current queue and try again. 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_ci arch_jump_label_transform_apply(); 48062306a36Sopenharmony_ci BUG_ON(!arch_jump_label_transform_queue(entry, jump_label_type(entry))); 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci arch_jump_label_transform_apply(); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci#endif 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_civoid __init jump_label_init(void) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci struct jump_entry *iter_start = __start___jump_table; 49062306a36Sopenharmony_ci struct jump_entry *iter_stop = __stop___jump_table; 49162306a36Sopenharmony_ci struct static_key *key = NULL; 49262306a36Sopenharmony_ci struct jump_entry *iter; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* 49562306a36Sopenharmony_ci * Since we are initializing the static_key.enabled field with 49662306a36Sopenharmony_ci * with the 'raw' int values (to avoid pulling in atomic.h) in 49762306a36Sopenharmony_ci * jump_label.h, let's make sure that is safe. There are only two 49862306a36Sopenharmony_ci * cases to check since we initialize to 0 or 1. 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_ci BUILD_BUG_ON((int)ATOMIC_INIT(0) != 0); 50162306a36Sopenharmony_ci BUILD_BUG_ON((int)ATOMIC_INIT(1) != 1); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (static_key_initialized) 50462306a36Sopenharmony_ci return; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci cpus_read_lock(); 50762306a36Sopenharmony_ci jump_label_lock(); 50862306a36Sopenharmony_ci jump_label_sort_entries(iter_start, iter_stop); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci for (iter = iter_start; iter < iter_stop; iter++) { 51162306a36Sopenharmony_ci struct static_key *iterk; 51262306a36Sopenharmony_ci bool in_init; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* rewrite NOPs */ 51562306a36Sopenharmony_ci if (jump_label_type(iter) == JUMP_LABEL_NOP) 51662306a36Sopenharmony_ci arch_jump_label_transform_static(iter, JUMP_LABEL_NOP); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci in_init = init_section_contains((void *)jump_entry_code(iter), 1); 51962306a36Sopenharmony_ci jump_entry_set_init(iter, in_init); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci iterk = jump_entry_key(iter); 52262306a36Sopenharmony_ci if (iterk == key) 52362306a36Sopenharmony_ci continue; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci key = iterk; 52662306a36Sopenharmony_ci static_key_set_entries(key, iter); 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci static_key_initialized = true; 52962306a36Sopenharmony_ci jump_label_unlock(); 53062306a36Sopenharmony_ci cpus_read_unlock(); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci#ifdef CONFIG_MODULES 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cienum jump_label_type jump_label_init_type(struct jump_entry *entry) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct static_key *key = jump_entry_key(entry); 53862306a36Sopenharmony_ci bool type = static_key_type(key); 53962306a36Sopenharmony_ci bool branch = jump_entry_is_branch(entry); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* See the comment in linux/jump_label.h */ 54262306a36Sopenharmony_ci return type ^ branch; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistruct static_key_mod { 54662306a36Sopenharmony_ci struct static_key_mod *next; 54762306a36Sopenharmony_ci struct jump_entry *entries; 54862306a36Sopenharmony_ci struct module *mod; 54962306a36Sopenharmony_ci}; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic inline struct static_key_mod *static_key_mod(struct static_key *key) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci WARN_ON_ONCE(!static_key_linked(key)); 55462306a36Sopenharmony_ci return (struct static_key_mod *)(key->type & ~JUMP_TYPE_MASK); 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci/*** 55862306a36Sopenharmony_ci * key->type and key->next are the same via union. 55962306a36Sopenharmony_ci * This sets key->next and preserves the type bits. 56062306a36Sopenharmony_ci * 56162306a36Sopenharmony_ci * See additional comments above static_key_set_entries(). 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_cistatic void static_key_set_mod(struct static_key *key, 56462306a36Sopenharmony_ci struct static_key_mod *mod) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci unsigned long type; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci WARN_ON_ONCE((unsigned long)mod & JUMP_TYPE_MASK); 56962306a36Sopenharmony_ci type = key->type & JUMP_TYPE_MASK; 57062306a36Sopenharmony_ci key->next = mod; 57162306a36Sopenharmony_ci key->type |= type; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic int __jump_label_mod_text_reserved(void *start, void *end) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct module *mod; 57762306a36Sopenharmony_ci int ret; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci preempt_disable(); 58062306a36Sopenharmony_ci mod = __module_text_address((unsigned long)start); 58162306a36Sopenharmony_ci WARN_ON_ONCE(__module_text_address((unsigned long)end) != mod); 58262306a36Sopenharmony_ci if (!try_module_get(mod)) 58362306a36Sopenharmony_ci mod = NULL; 58462306a36Sopenharmony_ci preempt_enable(); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (!mod) 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci ret = __jump_label_text_reserved(mod->jump_entries, 59062306a36Sopenharmony_ci mod->jump_entries + mod->num_jump_entries, 59162306a36Sopenharmony_ci start, end, mod->state == MODULE_STATE_COMING); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci module_put(mod); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return ret; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic void __jump_label_mod_update(struct static_key *key) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct static_key_mod *mod; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci for (mod = static_key_mod(key); mod; mod = mod->next) { 60362306a36Sopenharmony_ci struct jump_entry *stop; 60462306a36Sopenharmony_ci struct module *m; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* 60762306a36Sopenharmony_ci * NULL if the static_key is defined in a module 60862306a36Sopenharmony_ci * that does not use it 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_ci if (!mod->entries) 61162306a36Sopenharmony_ci continue; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci m = mod->mod; 61462306a36Sopenharmony_ci if (!m) 61562306a36Sopenharmony_ci stop = __stop___jump_table; 61662306a36Sopenharmony_ci else 61762306a36Sopenharmony_ci stop = m->jump_entries + m->num_jump_entries; 61862306a36Sopenharmony_ci __jump_label_update(key, mod->entries, stop, 61962306a36Sopenharmony_ci m && m->state == MODULE_STATE_COMING); 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic int jump_label_add_module(struct module *mod) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct jump_entry *iter_start = mod->jump_entries; 62662306a36Sopenharmony_ci struct jump_entry *iter_stop = iter_start + mod->num_jump_entries; 62762306a36Sopenharmony_ci struct jump_entry *iter; 62862306a36Sopenharmony_ci struct static_key *key = NULL; 62962306a36Sopenharmony_ci struct static_key_mod *jlm, *jlm2; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* if the module doesn't have jump label entries, just return */ 63262306a36Sopenharmony_ci if (iter_start == iter_stop) 63362306a36Sopenharmony_ci return 0; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci jump_label_sort_entries(iter_start, iter_stop); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci for (iter = iter_start; iter < iter_stop; iter++) { 63862306a36Sopenharmony_ci struct static_key *iterk; 63962306a36Sopenharmony_ci bool in_init; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci in_init = within_module_init(jump_entry_code(iter), mod); 64262306a36Sopenharmony_ci jump_entry_set_init(iter, in_init); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci iterk = jump_entry_key(iter); 64562306a36Sopenharmony_ci if (iterk == key) 64662306a36Sopenharmony_ci continue; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci key = iterk; 64962306a36Sopenharmony_ci if (within_module((unsigned long)key, mod)) { 65062306a36Sopenharmony_ci static_key_set_entries(key, iter); 65162306a36Sopenharmony_ci continue; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci jlm = kzalloc(sizeof(struct static_key_mod), GFP_KERNEL); 65462306a36Sopenharmony_ci if (!jlm) 65562306a36Sopenharmony_ci return -ENOMEM; 65662306a36Sopenharmony_ci if (!static_key_linked(key)) { 65762306a36Sopenharmony_ci jlm2 = kzalloc(sizeof(struct static_key_mod), 65862306a36Sopenharmony_ci GFP_KERNEL); 65962306a36Sopenharmony_ci if (!jlm2) { 66062306a36Sopenharmony_ci kfree(jlm); 66162306a36Sopenharmony_ci return -ENOMEM; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci preempt_disable(); 66462306a36Sopenharmony_ci jlm2->mod = __module_address((unsigned long)key); 66562306a36Sopenharmony_ci preempt_enable(); 66662306a36Sopenharmony_ci jlm2->entries = static_key_entries(key); 66762306a36Sopenharmony_ci jlm2->next = NULL; 66862306a36Sopenharmony_ci static_key_set_mod(key, jlm2); 66962306a36Sopenharmony_ci static_key_set_linked(key); 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci jlm->mod = mod; 67262306a36Sopenharmony_ci jlm->entries = iter; 67362306a36Sopenharmony_ci jlm->next = static_key_mod(key); 67462306a36Sopenharmony_ci static_key_set_mod(key, jlm); 67562306a36Sopenharmony_ci static_key_set_linked(key); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci /* Only update if we've changed from our initial state */ 67862306a36Sopenharmony_ci if (jump_label_type(iter) != jump_label_init_type(iter)) 67962306a36Sopenharmony_ci __jump_label_update(key, iter, iter_stop, true); 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci return 0; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic void jump_label_del_module(struct module *mod) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct jump_entry *iter_start = mod->jump_entries; 68862306a36Sopenharmony_ci struct jump_entry *iter_stop = iter_start + mod->num_jump_entries; 68962306a36Sopenharmony_ci struct jump_entry *iter; 69062306a36Sopenharmony_ci struct static_key *key = NULL; 69162306a36Sopenharmony_ci struct static_key_mod *jlm, **prev; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci for (iter = iter_start; iter < iter_stop; iter++) { 69462306a36Sopenharmony_ci if (jump_entry_key(iter) == key) 69562306a36Sopenharmony_ci continue; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci key = jump_entry_key(iter); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (within_module((unsigned long)key, mod)) 70062306a36Sopenharmony_ci continue; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* No memory during module load */ 70362306a36Sopenharmony_ci if (WARN_ON(!static_key_linked(key))) 70462306a36Sopenharmony_ci continue; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci prev = &key->next; 70762306a36Sopenharmony_ci jlm = static_key_mod(key); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci while (jlm && jlm->mod != mod) { 71062306a36Sopenharmony_ci prev = &jlm->next; 71162306a36Sopenharmony_ci jlm = jlm->next; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci /* No memory during module load */ 71562306a36Sopenharmony_ci if (WARN_ON(!jlm)) 71662306a36Sopenharmony_ci continue; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (prev == &key->next) 71962306a36Sopenharmony_ci static_key_set_mod(key, jlm->next); 72062306a36Sopenharmony_ci else 72162306a36Sopenharmony_ci *prev = jlm->next; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci kfree(jlm); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci jlm = static_key_mod(key); 72662306a36Sopenharmony_ci /* if only one etry is left, fold it back into the static_key */ 72762306a36Sopenharmony_ci if (jlm->next == NULL) { 72862306a36Sopenharmony_ci static_key_set_entries(key, jlm->entries); 72962306a36Sopenharmony_ci static_key_clear_linked(key); 73062306a36Sopenharmony_ci kfree(jlm); 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic int 73662306a36Sopenharmony_cijump_label_module_notify(struct notifier_block *self, unsigned long val, 73762306a36Sopenharmony_ci void *data) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct module *mod = data; 74062306a36Sopenharmony_ci int ret = 0; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci cpus_read_lock(); 74362306a36Sopenharmony_ci jump_label_lock(); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci switch (val) { 74662306a36Sopenharmony_ci case MODULE_STATE_COMING: 74762306a36Sopenharmony_ci ret = jump_label_add_module(mod); 74862306a36Sopenharmony_ci if (ret) { 74962306a36Sopenharmony_ci WARN(1, "Failed to allocate memory: jump_label may not work properly.\n"); 75062306a36Sopenharmony_ci jump_label_del_module(mod); 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci break; 75362306a36Sopenharmony_ci case MODULE_STATE_GOING: 75462306a36Sopenharmony_ci jump_label_del_module(mod); 75562306a36Sopenharmony_ci break; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci jump_label_unlock(); 75962306a36Sopenharmony_ci cpus_read_unlock(); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci return notifier_from_errno(ret); 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_cistatic struct notifier_block jump_label_module_nb = { 76562306a36Sopenharmony_ci .notifier_call = jump_label_module_notify, 76662306a36Sopenharmony_ci .priority = 1, /* higher than tracepoints */ 76762306a36Sopenharmony_ci}; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic __init int jump_label_init_module(void) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci return register_module_notifier(&jump_label_module_nb); 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ciearly_initcall(jump_label_init_module); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci#endif /* CONFIG_MODULES */ 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci/*** 77862306a36Sopenharmony_ci * jump_label_text_reserved - check if addr range is reserved 77962306a36Sopenharmony_ci * @start: start text addr 78062306a36Sopenharmony_ci * @end: end text addr 78162306a36Sopenharmony_ci * 78262306a36Sopenharmony_ci * checks if the text addr located between @start and @end 78362306a36Sopenharmony_ci * overlaps with any of the jump label patch addresses. Code 78462306a36Sopenharmony_ci * that wants to modify kernel text should first verify that 78562306a36Sopenharmony_ci * it does not overlap with any of the jump label addresses. 78662306a36Sopenharmony_ci * Caller must hold jump_label_mutex. 78762306a36Sopenharmony_ci * 78862306a36Sopenharmony_ci * returns 1 if there is an overlap, 0 otherwise 78962306a36Sopenharmony_ci */ 79062306a36Sopenharmony_ciint jump_label_text_reserved(void *start, void *end) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci bool init = system_state < SYSTEM_RUNNING; 79362306a36Sopenharmony_ci int ret = __jump_label_text_reserved(__start___jump_table, 79462306a36Sopenharmony_ci __stop___jump_table, start, end, init); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (ret) 79762306a36Sopenharmony_ci return ret; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci#ifdef CONFIG_MODULES 80062306a36Sopenharmony_ci ret = __jump_label_mod_text_reserved(start, end); 80162306a36Sopenharmony_ci#endif 80262306a36Sopenharmony_ci return ret; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_cistatic void jump_label_update(struct static_key *key) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci struct jump_entry *stop = __stop___jump_table; 80862306a36Sopenharmony_ci bool init = system_state < SYSTEM_RUNNING; 80962306a36Sopenharmony_ci struct jump_entry *entry; 81062306a36Sopenharmony_ci#ifdef CONFIG_MODULES 81162306a36Sopenharmony_ci struct module *mod; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (static_key_linked(key)) { 81462306a36Sopenharmony_ci __jump_label_mod_update(key); 81562306a36Sopenharmony_ci return; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci preempt_disable(); 81962306a36Sopenharmony_ci mod = __module_address((unsigned long)key); 82062306a36Sopenharmony_ci if (mod) { 82162306a36Sopenharmony_ci stop = mod->jump_entries + mod->num_jump_entries; 82262306a36Sopenharmony_ci init = mod->state == MODULE_STATE_COMING; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci preempt_enable(); 82562306a36Sopenharmony_ci#endif 82662306a36Sopenharmony_ci entry = static_key_entries(key); 82762306a36Sopenharmony_ci /* if there are no users, entry can be NULL */ 82862306a36Sopenharmony_ci if (entry) 82962306a36Sopenharmony_ci __jump_label_update(key, entry, stop, init); 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci#ifdef CONFIG_STATIC_KEYS_SELFTEST 83362306a36Sopenharmony_cistatic DEFINE_STATIC_KEY_TRUE(sk_true); 83462306a36Sopenharmony_cistatic DEFINE_STATIC_KEY_FALSE(sk_false); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic __init int jump_label_test(void) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci int i; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 84162306a36Sopenharmony_ci WARN_ON(static_key_enabled(&sk_true.key) != true); 84262306a36Sopenharmony_ci WARN_ON(static_key_enabled(&sk_false.key) != false); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci WARN_ON(!static_branch_likely(&sk_true)); 84562306a36Sopenharmony_ci WARN_ON(!static_branch_unlikely(&sk_true)); 84662306a36Sopenharmony_ci WARN_ON(static_branch_likely(&sk_false)); 84762306a36Sopenharmony_ci WARN_ON(static_branch_unlikely(&sk_false)); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci static_branch_disable(&sk_true); 85062306a36Sopenharmony_ci static_branch_enable(&sk_false); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci WARN_ON(static_key_enabled(&sk_true.key) == true); 85362306a36Sopenharmony_ci WARN_ON(static_key_enabled(&sk_false.key) == false); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci WARN_ON(static_branch_likely(&sk_true)); 85662306a36Sopenharmony_ci WARN_ON(static_branch_unlikely(&sk_true)); 85762306a36Sopenharmony_ci WARN_ON(!static_branch_likely(&sk_false)); 85862306a36Sopenharmony_ci WARN_ON(!static_branch_unlikely(&sk_false)); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci static_branch_enable(&sk_true); 86162306a36Sopenharmony_ci static_branch_disable(&sk_false); 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci return 0; 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ciearly_initcall(jump_label_test); 86762306a36Sopenharmony_ci#endif /* STATIC_KEYS_SELFTEST */ 868