11da177e4SLinus Torvalds /* Helpers for initial module or kernel cmdline parsing 21da177e4SLinus Torvalds Copyright (C) 2001 Rusty Russell. 31da177e4SLinus Torvalds 41da177e4SLinus Torvalds This program is free software; you can redistribute it and/or modify 51da177e4SLinus Torvalds it under the terms of the GNU General Public License as published by 61da177e4SLinus Torvalds the Free Software Foundation; either version 2 of the License, or 71da177e4SLinus Torvalds (at your option) any later version. 81da177e4SLinus Torvalds 91da177e4SLinus Torvalds This program is distributed in the hope that it will be useful, 101da177e4SLinus Torvalds but WITHOUT ANY WARRANTY; without even the implied warranty of 111da177e4SLinus Torvalds MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 121da177e4SLinus Torvalds GNU General Public License for more details. 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds You should have received a copy of the GNU General Public License 151da177e4SLinus Torvalds along with this program; if not, write to the Free Software 161da177e4SLinus Torvalds Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 171da177e4SLinus Torvalds */ 181da177e4SLinus Torvalds #include <linux/kernel.h> 191da177e4SLinus Torvalds #include <linux/string.h> 201da177e4SLinus Torvalds #include <linux/errno.h> 211da177e4SLinus Torvalds #include <linux/module.h> 2263a12d9dSGeert Uytterhoeven #include <linux/moduleparam.h> 231da177e4SLinus Torvalds #include <linux/device.h> 241da177e4SLinus Torvalds #include <linux/err.h> 254e57b681STim Schmielau #include <linux/slab.h> 2626d052bfSPeter Oberparleiter #include <linux/ctype.h> 271da177e4SLinus Torvalds 28cf2fde7bSRusty Russell #ifdef CONFIG_SYSFS 29b51d23e4SDan Streetman /* Protects all built-in parameters, modules use their own param_lock */ 30907b29ebSRusty Russell static DEFINE_MUTEX(param_lock); 31907b29ebSRusty Russell 32b51d23e4SDan Streetman /* Use the module's mutex, or if built-in use the built-in mutex */ 3320bdc2cfSStephen Rothwell #ifdef CONFIG_MODULES 34b51d23e4SDan Streetman #define KPARAM_MUTEX(mod) ((mod) ? &(mod)->param_lock : ¶m_lock) 3520bdc2cfSStephen Rothwell #else 3620bdc2cfSStephen Rothwell #define KPARAM_MUTEX(mod) (¶m_lock) 3720bdc2cfSStephen Rothwell #endif 38cf2fde7bSRusty Russell 39cf2fde7bSRusty Russell static inline void check_kparam_locked(struct module *mod) 40cf2fde7bSRusty Russell { 41cf2fde7bSRusty Russell BUG_ON(!mutex_is_locked(KPARAM_MUTEX(mod))); 42cf2fde7bSRusty Russell } 43cf2fde7bSRusty Russell #else 44cf2fde7bSRusty Russell static inline void check_kparam_locked(struct module *mod) 45cf2fde7bSRusty Russell { 46cf2fde7bSRusty Russell } 47cf2fde7bSRusty Russell #endif /* !CONFIG_SYSFS */ 48b51d23e4SDan Streetman 49a1054322SRusty Russell /* This just allows us to keep track of which parameters are kmalloced. */ 50a1054322SRusty Russell struct kmalloced_param { 51a1054322SRusty Russell struct list_head list; 52a1054322SRusty Russell char val[]; 53a1054322SRusty Russell }; 54a1054322SRusty Russell static LIST_HEAD(kmalloced_params); 55b51d23e4SDan Streetman static DEFINE_SPINLOCK(kmalloced_params_lock); 56a1054322SRusty Russell 57a1054322SRusty Russell static void *kmalloc_parameter(unsigned int size) 58a1054322SRusty Russell { 59a1054322SRusty Russell struct kmalloced_param *p; 60a1054322SRusty Russell 61a1054322SRusty Russell p = kmalloc(sizeof(*p) + size, GFP_KERNEL); 62a1054322SRusty Russell if (!p) 63a1054322SRusty Russell return NULL; 64a1054322SRusty Russell 65b51d23e4SDan Streetman spin_lock(&kmalloced_params_lock); 66a1054322SRusty Russell list_add(&p->list, &kmalloced_params); 67b51d23e4SDan Streetman spin_unlock(&kmalloced_params_lock); 68b51d23e4SDan Streetman 69a1054322SRusty Russell return p->val; 70a1054322SRusty Russell } 71a1054322SRusty Russell 72a1054322SRusty Russell /* Does nothing if parameter wasn't kmalloced above. */ 73a1054322SRusty Russell static void maybe_kfree_parameter(void *param) 74a1054322SRusty Russell { 75a1054322SRusty Russell struct kmalloced_param *p; 76a1054322SRusty Russell 77b51d23e4SDan Streetman spin_lock(&kmalloced_params_lock); 78a1054322SRusty Russell list_for_each_entry(p, &kmalloced_params, list) { 79a1054322SRusty Russell if (p->val == param) { 80a1054322SRusty Russell list_del(&p->list); 81a1054322SRusty Russell kfree(p); 82a1054322SRusty Russell break; 83a1054322SRusty Russell } 84a1054322SRusty Russell } 85b51d23e4SDan Streetman spin_unlock(&kmalloced_params_lock); 86a1054322SRusty Russell } 87a1054322SRusty Russell 88b1e4d20cSMichal Schmidt static char dash2underscore(char c) 891da177e4SLinus Torvalds { 901da177e4SLinus Torvalds if (c == '-') 911da177e4SLinus Torvalds return '_'; 921da177e4SLinus Torvalds return c; 931da177e4SLinus Torvalds } 941da177e4SLinus Torvalds 95b1e4d20cSMichal Schmidt bool parameqn(const char *a, const char *b, size_t n) 961da177e4SLinus Torvalds { 97b1e4d20cSMichal Schmidt size_t i; 98b1e4d20cSMichal Schmidt 99b1e4d20cSMichal Schmidt for (i = 0; i < n; i++) { 100b1e4d20cSMichal Schmidt if (dash2underscore(a[i]) != dash2underscore(b[i])) 101b1e4d20cSMichal Schmidt return false; 102b1e4d20cSMichal Schmidt } 103b1e4d20cSMichal Schmidt return true; 104b1e4d20cSMichal Schmidt } 105b1e4d20cSMichal Schmidt 106b1e4d20cSMichal Schmidt bool parameq(const char *a, const char *b) 107b1e4d20cSMichal Schmidt { 108b1e4d20cSMichal Schmidt return parameqn(a, b, strlen(a)+1); 1091da177e4SLinus Torvalds } 1101da177e4SLinus Torvalds 1117a486d37SRusty Russell static void param_check_unsafe(const struct kernel_param *kp) 1127a486d37SRusty Russell { 1137a486d37SRusty Russell if (kp->flags & KERNEL_PARAM_FL_UNSAFE) { 1147a486d37SRusty Russell pr_warn("Setting dangerous option %s - tainting kernel\n", 1157a486d37SRusty Russell kp->name); 1167a486d37SRusty Russell add_taint(TAINT_USER, LOCKDEP_STILL_OK); 1177a486d37SRusty Russell } 1187a486d37SRusty Russell } 1197a486d37SRusty Russell 1201da177e4SLinus Torvalds static int parse_one(char *param, 1211da177e4SLinus Torvalds char *val, 1229fb48c74SJim Cromie const char *doing, 123914dcaa8SRusty Russell const struct kernel_param *params, 1241da177e4SLinus Torvalds unsigned num_params, 125026cee00SPawel Moll s16 min_level, 126026cee00SPawel Moll s16 max_level, 127ecc86170SLuis R. Rodriguez void *arg, 1289fb48c74SJim Cromie int (*handle_unknown)(char *param, char *val, 129ecc86170SLuis R. Rodriguez const char *doing, void *arg)) 1301da177e4SLinus Torvalds { 1311da177e4SLinus Torvalds unsigned int i; 132907b29ebSRusty Russell int err; 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds /* Find parameter */ 1351da177e4SLinus Torvalds for (i = 0; i < num_params; i++) { 1361da177e4SLinus Torvalds if (parameq(param, params[i].name)) { 137026cee00SPawel Moll if (params[i].level < min_level 138026cee00SPawel Moll || params[i].level > max_level) 139026cee00SPawel Moll return 0; 1402e9fb995SRusty Russell /* No one handled NULL, so do it here. */ 141ab013c5fSSteven Rostedt if (!val && 1426a4c2643SJani Nikula !(params[i].ops->flags & KERNEL_PARAM_OPS_FL_NOARG)) 1432e9fb995SRusty Russell return -EINVAL; 1449fb48c74SJim Cromie pr_debug("handling %s with %p\n", param, 1459bbb9e5aSRusty Russell params[i].ops->set); 146b51d23e4SDan Streetman kernel_param_lock(params[i].mod); 1477a486d37SRusty Russell param_check_unsafe(¶ms[i]); 148907b29ebSRusty Russell err = params[i].ops->set(val, ¶ms[i]); 149b51d23e4SDan Streetman kernel_param_unlock(params[i].mod); 150907b29ebSRusty Russell return err; 1511da177e4SLinus Torvalds } 1521da177e4SLinus Torvalds } 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds if (handle_unknown) { 1559fb48c74SJim Cromie pr_debug("doing %s: %s='%s'\n", doing, param, val); 156ecc86170SLuis R. Rodriguez return handle_unknown(param, val, doing, arg); 1571da177e4SLinus Torvalds } 1581da177e4SLinus Torvalds 1599fb48c74SJim Cromie pr_debug("Unknown argument '%s'\n", param); 1601da177e4SLinus Torvalds return -ENOENT; 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds 1631da177e4SLinus Torvalds /* You can use " around spaces, but can't escape ". */ 1641da177e4SLinus Torvalds /* Hyphens and underscores equivalent in parameter names. */ 1651da177e4SLinus Torvalds static char *next_arg(char *args, char **param, char **val) 1661da177e4SLinus Torvalds { 1671da177e4SLinus Torvalds unsigned int i, equals = 0; 1681da177e4SLinus Torvalds int in_quote = 0, quoted = 0; 1691da177e4SLinus Torvalds char *next; 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds if (*args == '"') { 1721da177e4SLinus Torvalds args++; 1731da177e4SLinus Torvalds in_quote = 1; 1741da177e4SLinus Torvalds quoted = 1; 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds for (i = 0; args[i]; i++) { 17826d052bfSPeter Oberparleiter if (isspace(args[i]) && !in_quote) 1791da177e4SLinus Torvalds break; 1801da177e4SLinus Torvalds if (equals == 0) { 1811da177e4SLinus Torvalds if (args[i] == '=') 1821da177e4SLinus Torvalds equals = i; 1831da177e4SLinus Torvalds } 1841da177e4SLinus Torvalds if (args[i] == '"') 1851da177e4SLinus Torvalds in_quote = !in_quote; 1861da177e4SLinus Torvalds } 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds *param = args; 1891da177e4SLinus Torvalds if (!equals) 1901da177e4SLinus Torvalds *val = NULL; 1911da177e4SLinus Torvalds else { 1921da177e4SLinus Torvalds args[equals] = '\0'; 1931da177e4SLinus Torvalds *val = args + equals + 1; 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds /* Don't include quotes in value. */ 1961da177e4SLinus Torvalds if (**val == '"') { 1971da177e4SLinus Torvalds (*val)++; 1981da177e4SLinus Torvalds if (args[i-1] == '"') 1991da177e4SLinus Torvalds args[i-1] = '\0'; 2001da177e4SLinus Torvalds } 201b9cc4489SRusty Russell } 2021da177e4SLinus Torvalds if (quoted && args[i-1] == '"') 2031da177e4SLinus Torvalds args[i-1] = '\0'; 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds if (args[i]) { 2061da177e4SLinus Torvalds args[i] = '\0'; 2071da177e4SLinus Torvalds next = args + i + 1; 2081da177e4SLinus Torvalds } else 2091da177e4SLinus Torvalds next = args + i; 210f36462f0SRusty Russell 211f36462f0SRusty Russell /* Chew up trailing spaces. */ 212e7d2860bSAndré Goddard Rosa return skip_spaces(next); 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds /* Args looks like "foo=bar,bar2 baz=fuz wiz". */ 21651e158c1SRusty Russell char *parse_args(const char *doing, 2171da177e4SLinus Torvalds char *args, 218914dcaa8SRusty Russell const struct kernel_param *params, 2191da177e4SLinus Torvalds unsigned num, 220026cee00SPawel Moll s16 min_level, 221026cee00SPawel Moll s16 max_level, 222ecc86170SLuis R. Rodriguez void *arg, 223ecc86170SLuis R. Rodriguez int (*unknown)(char *param, char *val, 224ecc86170SLuis R. Rodriguez const char *doing, void *arg)) 2251da177e4SLinus Torvalds { 22674b22c46SOleg Nesterov char *param, *val, *err = NULL; 2271da177e4SLinus Torvalds 228f36462f0SRusty Russell /* Chew leading spaces */ 229e7d2860bSAndré Goddard Rosa args = skip_spaces(args); 230f36462f0SRusty Russell 2311ef9eaf2SJim Cromie if (*args) 2329fb48c74SJim Cromie pr_debug("doing %s, parsing ARGS: '%s'\n", doing, args); 2339fb48c74SJim Cromie 2341da177e4SLinus Torvalds while (*args) { 2351da177e4SLinus Torvalds int ret; 236a416aba6SArd van Breemen int irq_was_disabled; 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds args = next_arg(args, ¶m, &val); 23951e158c1SRusty Russell /* Stop at -- */ 24051e158c1SRusty Russell if (!val && strcmp(param, "--") == 0) 24174b22c46SOleg Nesterov return err ?: args; 242a416aba6SArd van Breemen irq_was_disabled = irqs_disabled(); 2439fb48c74SJim Cromie ret = parse_one(param, val, doing, params, num, 244ecc86170SLuis R. Rodriguez min_level, max_level, arg, unknown); 245b5f3abf9SJim Cromie if (irq_was_disabled && !irqs_disabled()) 246b5f3abf9SJim Cromie pr_warn("%s: option '%s' enabled irq's!\n", 247b5f3abf9SJim Cromie doing, param); 248b5f3abf9SJim Cromie 2491da177e4SLinus Torvalds switch (ret) { 25074b22c46SOleg Nesterov case 0: 25174b22c46SOleg Nesterov continue; 2521da177e4SLinus Torvalds case -ENOENT: 253b5f3abf9SJim Cromie pr_err("%s: Unknown parameter `%s'\n", doing, param); 25474b22c46SOleg Nesterov break; 2551da177e4SLinus Torvalds case -ENOSPC: 256b5f3abf9SJim Cromie pr_err("%s: `%s' too large for parameter `%s'\n", 2579fb48c74SJim Cromie doing, val ?: "", param); 2581da177e4SLinus Torvalds break; 2591da177e4SLinus Torvalds default: 260b5f3abf9SJim Cromie pr_err("%s: `%s' invalid for parameter `%s'\n", 2619fb48c74SJim Cromie doing, val ?: "", param); 26274b22c46SOleg Nesterov break; 2631da177e4SLinus Torvalds } 2641da177e4SLinus Torvalds 26574b22c46SOleg Nesterov err = ERR_PTR(ret); 26674b22c46SOleg Nesterov } 26774b22c46SOleg Nesterov 26874b22c46SOleg Nesterov return err; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds 2711da177e4SLinus Torvalds /* Lazy bastard, eh? */ 27288a88b32SFelipe Contreras #define STANDARD_PARAM_DEF(name, type, format, strtolfn) \ 2739bbb9e5aSRusty Russell int param_set_##name(const char *val, const struct kernel_param *kp) \ 2741da177e4SLinus Torvalds { \ 27588a88b32SFelipe Contreras return strtolfn(val, 0, (type *)kp->arg); \ 2761da177e4SLinus Torvalds } \ 2779bbb9e5aSRusty Russell int param_get_##name(char *buffer, const struct kernel_param *kp) \ 2781da177e4SLinus Torvalds { \ 279f4940ab7SChen Gang return scnprintf(buffer, PAGE_SIZE, format, \ 280f4940ab7SChen Gang *((type *)kp->arg)); \ 281a14fe249SRusty Russell } \ 2829c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_##name = { \ 2839bbb9e5aSRusty Russell .set = param_set_##name, \ 2849bbb9e5aSRusty Russell .get = param_get_##name, \ 2859bbb9e5aSRusty Russell }; \ 286a14fe249SRusty Russell EXPORT_SYMBOL(param_set_##name); \ 2879bbb9e5aSRusty Russell EXPORT_SYMBOL(param_get_##name); \ 2889bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_##name) 2899bbb9e5aSRusty Russell 2901da177e4SLinus Torvalds 29188a88b32SFelipe Contreras STANDARD_PARAM_DEF(byte, unsigned char, "%hhu", kstrtou8); 29288a88b32SFelipe Contreras STANDARD_PARAM_DEF(short, short, "%hi", kstrtos16); 29388a88b32SFelipe Contreras STANDARD_PARAM_DEF(ushort, unsigned short, "%hu", kstrtou16); 29488a88b32SFelipe Contreras STANDARD_PARAM_DEF(int, int, "%i", kstrtoint); 29588a88b32SFelipe Contreras STANDARD_PARAM_DEF(uint, unsigned int, "%u", kstrtouint); 29688a88b32SFelipe Contreras STANDARD_PARAM_DEF(long, long, "%li", kstrtol); 29788a88b32SFelipe Contreras STANDARD_PARAM_DEF(ulong, unsigned long, "%lu", kstrtoul); 298b4210b81SHannes Reinecke STANDARD_PARAM_DEF(ullong, unsigned long long, "%llu", kstrtoull); 2991da177e4SLinus Torvalds 3009bbb9e5aSRusty Russell int param_set_charp(const char *val, const struct kernel_param *kp) 3011da177e4SLinus Torvalds { 3021da177e4SLinus Torvalds if (strlen(val) > 1024) { 303b5f3abf9SJim Cromie pr_err("%s: string parameter too long\n", kp->name); 3041da177e4SLinus Torvalds return -ENOSPC; 3051da177e4SLinus Torvalds } 3061da177e4SLinus Torvalds 307a1054322SRusty Russell maybe_kfree_parameter(*(char **)kp->arg); 308a1054322SRusty Russell 309a1054322SRusty Russell /* This is a hack. We can't kmalloc in early boot, and we 310e180a6b7SRusty Russell * don't need to; this mangled commandline is preserved. */ 311e180a6b7SRusty Russell if (slab_is_available()) { 312a1054322SRusty Russell *(char **)kp->arg = kmalloc_parameter(strlen(val)+1); 313d553ad86SRusty Russell if (!*(char **)kp->arg) 314e180a6b7SRusty Russell return -ENOMEM; 315a1054322SRusty Russell strcpy(*(char **)kp->arg, val); 316e180a6b7SRusty Russell } else 317e180a6b7SRusty Russell *(const char **)kp->arg = val; 318e180a6b7SRusty Russell 3191da177e4SLinus Torvalds return 0; 3201da177e4SLinus Torvalds } 321a14fe249SRusty Russell EXPORT_SYMBOL(param_set_charp); 3221da177e4SLinus Torvalds 3239bbb9e5aSRusty Russell int param_get_charp(char *buffer, const struct kernel_param *kp) 3241da177e4SLinus Torvalds { 325f4940ab7SChen Gang return scnprintf(buffer, PAGE_SIZE, "%s", *((char **)kp->arg)); 3261da177e4SLinus Torvalds } 327a14fe249SRusty Russell EXPORT_SYMBOL(param_get_charp); 3281da177e4SLinus Torvalds 3293d9c637fSDan Streetman void param_free_charp(void *arg) 330a1054322SRusty Russell { 331a1054322SRusty Russell maybe_kfree_parameter(*((char **)arg)); 332a1054322SRusty Russell } 3333d9c637fSDan Streetman EXPORT_SYMBOL(param_free_charp); 334a1054322SRusty Russell 3359c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_charp = { 3369bbb9e5aSRusty Russell .set = param_set_charp, 3379bbb9e5aSRusty Russell .get = param_get_charp, 338a1054322SRusty Russell .free = param_free_charp, 3399bbb9e5aSRusty Russell }; 3409bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_charp); 3419bbb9e5aSRusty Russell 342fddd5201SRusty Russell /* Actually could be a bool or an int, for historical reasons. */ 3439bbb9e5aSRusty Russell int param_set_bool(const char *val, const struct kernel_param *kp) 3441da177e4SLinus Torvalds { 3451da177e4SLinus Torvalds /* No equals means "set"... */ 3461da177e4SLinus Torvalds if (!val) val = "1"; 3471da177e4SLinus Torvalds 3481da177e4SLinus Torvalds /* One of =[yYnN01] */ 3498b825281SRusty Russell return strtobool(val, kp->arg); 350fddd5201SRusty Russell } 351a14fe249SRusty Russell EXPORT_SYMBOL(param_set_bool); 352fddd5201SRusty Russell 3539bbb9e5aSRusty Russell int param_get_bool(char *buffer, const struct kernel_param *kp) 3541da177e4SLinus Torvalds { 3551da177e4SLinus Torvalds /* Y and N chosen as being relatively non-coder friendly */ 3568b825281SRusty Russell return sprintf(buffer, "%c", *(bool *)kp->arg ? 'Y' : 'N'); 3571da177e4SLinus Torvalds } 358a14fe249SRusty Russell EXPORT_SYMBOL(param_get_bool); 3591da177e4SLinus Torvalds 3609c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_bool = { 3616a4c2643SJani Nikula .flags = KERNEL_PARAM_OPS_FL_NOARG, 3629bbb9e5aSRusty Russell .set = param_set_bool, 3639bbb9e5aSRusty Russell .get = param_get_bool, 3649bbb9e5aSRusty Russell }; 3659bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_bool); 3669bbb9e5aSRusty Russell 367d19f05d8SLuis R. Rodriguez int param_set_bool_enable_only(const char *val, const struct kernel_param *kp) 368d19f05d8SLuis R. Rodriguez { 369d19f05d8SLuis R. Rodriguez int err = 0; 370d19f05d8SLuis R. Rodriguez bool new_value; 371d19f05d8SLuis R. Rodriguez bool orig_value = *(bool *)kp->arg; 372d19f05d8SLuis R. Rodriguez struct kernel_param dummy_kp = *kp; 373d19f05d8SLuis R. Rodriguez 374d19f05d8SLuis R. Rodriguez dummy_kp.arg = &new_value; 375d19f05d8SLuis R. Rodriguez 376d19f05d8SLuis R. Rodriguez err = param_set_bool(val, &dummy_kp); 377d19f05d8SLuis R. Rodriguez if (err) 378d19f05d8SLuis R. Rodriguez return err; 379d19f05d8SLuis R. Rodriguez 380d19f05d8SLuis R. Rodriguez /* Don't let them unset it once it's set! */ 381d19f05d8SLuis R. Rodriguez if (!new_value && orig_value) 382d19f05d8SLuis R. Rodriguez return -EROFS; 383d19f05d8SLuis R. Rodriguez 384d19f05d8SLuis R. Rodriguez if (new_value) 385d19f05d8SLuis R. Rodriguez err = param_set_bool(val, kp); 386d19f05d8SLuis R. Rodriguez 387d19f05d8SLuis R. Rodriguez return err; 388d19f05d8SLuis R. Rodriguez } 389d19f05d8SLuis R. Rodriguez EXPORT_SYMBOL_GPL(param_set_bool_enable_only); 390d19f05d8SLuis R. Rodriguez 391d19f05d8SLuis R. Rodriguez const struct kernel_param_ops param_ops_bool_enable_only = { 392d19f05d8SLuis R. Rodriguez .flags = KERNEL_PARAM_OPS_FL_NOARG, 393d19f05d8SLuis R. Rodriguez .set = param_set_bool_enable_only, 394d19f05d8SLuis R. Rodriguez .get = param_get_bool, 395d19f05d8SLuis R. Rodriguez }; 396154be21cSLuis R. Rodriguez EXPORT_SYMBOL_GPL(param_ops_bool_enable_only); 397d19f05d8SLuis R. Rodriguez 398fddd5201SRusty Russell /* This one must be bool. */ 3999bbb9e5aSRusty Russell int param_set_invbool(const char *val, const struct kernel_param *kp) 4001da177e4SLinus Torvalds { 401fddd5201SRusty Russell int ret; 402fddd5201SRusty Russell bool boolval; 40322e48eafSJan Beulich struct kernel_param dummy; 4041da177e4SLinus Torvalds 40522e48eafSJan Beulich dummy.arg = &boolval; 4061da177e4SLinus Torvalds ret = param_set_bool(val, &dummy); 4071da177e4SLinus Torvalds if (ret == 0) 4089a71af2cSRusty Russell *(bool *)kp->arg = !boolval; 4091da177e4SLinus Torvalds return ret; 4101da177e4SLinus Torvalds } 411a14fe249SRusty Russell EXPORT_SYMBOL(param_set_invbool); 4121da177e4SLinus Torvalds 4139bbb9e5aSRusty Russell int param_get_invbool(char *buffer, const struct kernel_param *kp) 4141da177e4SLinus Torvalds { 4159a71af2cSRusty Russell return sprintf(buffer, "%c", (*(bool *)kp->arg) ? 'N' : 'Y'); 4161da177e4SLinus Torvalds } 417a14fe249SRusty Russell EXPORT_SYMBOL(param_get_invbool); 4181da177e4SLinus Torvalds 4199c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_invbool = { 4209bbb9e5aSRusty Russell .set = param_set_invbool, 4219bbb9e5aSRusty Russell .get = param_get_invbool, 4229bbb9e5aSRusty Russell }; 4239bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_invbool); 4249bbb9e5aSRusty Russell 42569116f27SRusty Russell int param_set_bint(const char *val, const struct kernel_param *kp) 42669116f27SRusty Russell { 4275104b7d7SDan Streetman /* Match bool exactly, by re-using it. */ 4285104b7d7SDan Streetman struct kernel_param boolkp = *kp; 42969116f27SRusty Russell bool v; 43069116f27SRusty Russell int ret; 43169116f27SRusty Russell 43269116f27SRusty Russell boolkp.arg = &v; 43369116f27SRusty Russell 43469116f27SRusty Russell ret = param_set_bool(val, &boolkp); 43569116f27SRusty Russell if (ret == 0) 43669116f27SRusty Russell *(int *)kp->arg = v; 43769116f27SRusty Russell return ret; 43869116f27SRusty Russell } 43969116f27SRusty Russell EXPORT_SYMBOL(param_set_bint); 44069116f27SRusty Russell 4419c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_bint = { 4426a4c2643SJani Nikula .flags = KERNEL_PARAM_OPS_FL_NOARG, 44369116f27SRusty Russell .set = param_set_bint, 44469116f27SRusty Russell .get = param_get_int, 44569116f27SRusty Russell }; 44669116f27SRusty Russell EXPORT_SYMBOL(param_ops_bint); 44769116f27SRusty Russell 4489730b5b0SBert Wesarg /* We break the rule and mangle the string. */ 449b51d23e4SDan Streetman static int param_array(struct module *mod, 450b51d23e4SDan Streetman const char *name, 4511da177e4SLinus Torvalds const char *val, 4521da177e4SLinus Torvalds unsigned int min, unsigned int max, 4531da177e4SLinus Torvalds void *elem, int elemsize, 4549bbb9e5aSRusty Russell int (*set)(const char *, const struct kernel_param *kp), 455026cee00SPawel Moll s16 level, 456eb38a996SRichard Knutsson unsigned int *num) 4571da177e4SLinus Torvalds { 4581da177e4SLinus Torvalds int ret; 4591da177e4SLinus Torvalds struct kernel_param kp; 4601da177e4SLinus Torvalds char save; 4611da177e4SLinus Torvalds 4621da177e4SLinus Torvalds /* Get the name right for errors. */ 4631da177e4SLinus Torvalds kp.name = name; 4641da177e4SLinus Torvalds kp.arg = elem; 465026cee00SPawel Moll kp.level = level; 4661da177e4SLinus Torvalds 4671da177e4SLinus Torvalds *num = 0; 4681da177e4SLinus Torvalds /* We expect a comma-separated list of values. */ 4691da177e4SLinus Torvalds do { 4701da177e4SLinus Torvalds int len; 4711da177e4SLinus Torvalds 4721da177e4SLinus Torvalds if (*num == max) { 473b5f3abf9SJim Cromie pr_err("%s: can only take %i arguments\n", name, max); 4741da177e4SLinus Torvalds return -EINVAL; 4751da177e4SLinus Torvalds } 4761da177e4SLinus Torvalds len = strcspn(val, ","); 4771da177e4SLinus Torvalds 4781da177e4SLinus Torvalds /* nul-terminate and parse */ 4791da177e4SLinus Torvalds save = val[len]; 4801da177e4SLinus Torvalds ((char *)val)[len] = '\0'; 481cf2fde7bSRusty Russell check_kparam_locked(mod); 4821da177e4SLinus Torvalds ret = set(val, &kp); 4831da177e4SLinus Torvalds 4841da177e4SLinus Torvalds if (ret != 0) 4851da177e4SLinus Torvalds return ret; 4861da177e4SLinus Torvalds kp.arg += elemsize; 4871da177e4SLinus Torvalds val += len+1; 4881da177e4SLinus Torvalds (*num)++; 4891da177e4SLinus Torvalds } while (save == ','); 4901da177e4SLinus Torvalds 4911da177e4SLinus Torvalds if (*num < min) { 492b5f3abf9SJim Cromie pr_err("%s: needs at least %i arguments\n", name, min); 4931da177e4SLinus Torvalds return -EINVAL; 4941da177e4SLinus Torvalds } 4951da177e4SLinus Torvalds return 0; 4961da177e4SLinus Torvalds } 4971da177e4SLinus Torvalds 4989bbb9e5aSRusty Russell static int param_array_set(const char *val, const struct kernel_param *kp) 4991da177e4SLinus Torvalds { 50022e48eafSJan Beulich const struct kparam_array *arr = kp->arr; 50131143a12SBert Wesarg unsigned int temp_num; 5021da177e4SLinus Torvalds 503b51d23e4SDan Streetman return param_array(kp->mod, kp->name, val, 1, arr->max, arr->elem, 504026cee00SPawel Moll arr->elemsize, arr->ops->set, kp->level, 5053c7d76e3SRusty Russell arr->num ?: &temp_num); 5061da177e4SLinus Torvalds } 5071da177e4SLinus Torvalds 5089bbb9e5aSRusty Russell static int param_array_get(char *buffer, const struct kernel_param *kp) 5091da177e4SLinus Torvalds { 5101da177e4SLinus Torvalds int i, off, ret; 51122e48eafSJan Beulich const struct kparam_array *arr = kp->arr; 5125104b7d7SDan Streetman struct kernel_param p = *kp; 5131da177e4SLinus Torvalds 5141da177e4SLinus Torvalds for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) { 5151da177e4SLinus Torvalds if (i) 5161da177e4SLinus Torvalds buffer[off++] = ','; 5171da177e4SLinus Torvalds p.arg = arr->elem + arr->elemsize * i; 518cf2fde7bSRusty Russell check_kparam_locked(p.mod); 5199bbb9e5aSRusty Russell ret = arr->ops->get(buffer + off, &p); 5201da177e4SLinus Torvalds if (ret < 0) 5211da177e4SLinus Torvalds return ret; 5221da177e4SLinus Torvalds off += ret; 5231da177e4SLinus Torvalds } 5241da177e4SLinus Torvalds buffer[off] = '\0'; 5251da177e4SLinus Torvalds return off; 5261da177e4SLinus Torvalds } 5271da177e4SLinus Torvalds 528e6df34a4SRusty Russell static void param_array_free(void *arg) 529e6df34a4SRusty Russell { 530e6df34a4SRusty Russell unsigned int i; 531e6df34a4SRusty Russell const struct kparam_array *arr = arg; 532e6df34a4SRusty Russell 533e6df34a4SRusty Russell if (arr->ops->free) 534e6df34a4SRusty Russell for (i = 0; i < (arr->num ? *arr->num : arr->max); i++) 535e6df34a4SRusty Russell arr->ops->free(arr->elem + arr->elemsize * i); 536e6df34a4SRusty Russell } 537e6df34a4SRusty Russell 5389c27847dSLuis R. Rodriguez const struct kernel_param_ops param_array_ops = { 5399bbb9e5aSRusty Russell .set = param_array_set, 5409bbb9e5aSRusty Russell .get = param_array_get, 541e6df34a4SRusty Russell .free = param_array_free, 5429bbb9e5aSRusty Russell }; 5439bbb9e5aSRusty Russell EXPORT_SYMBOL(param_array_ops); 5449bbb9e5aSRusty Russell 5459bbb9e5aSRusty Russell int param_set_copystring(const char *val, const struct kernel_param *kp) 5461da177e4SLinus Torvalds { 54722e48eafSJan Beulich const struct kparam_string *kps = kp->str; 5481da177e4SLinus Torvalds 5491da177e4SLinus Torvalds if (strlen(val)+1 > kps->maxlen) { 550b5f3abf9SJim Cromie pr_err("%s: string doesn't fit in %u chars.\n", 5511da177e4SLinus Torvalds kp->name, kps->maxlen-1); 5521da177e4SLinus Torvalds return -ENOSPC; 5531da177e4SLinus Torvalds } 5541da177e4SLinus Torvalds strcpy(kps->string, val); 5551da177e4SLinus Torvalds return 0; 5561da177e4SLinus Torvalds } 557a14fe249SRusty Russell EXPORT_SYMBOL(param_set_copystring); 5581da177e4SLinus Torvalds 5599bbb9e5aSRusty Russell int param_get_string(char *buffer, const struct kernel_param *kp) 5601da177e4SLinus Torvalds { 56122e48eafSJan Beulich const struct kparam_string *kps = kp->str; 5621da177e4SLinus Torvalds return strlcpy(buffer, kps->string, kps->maxlen); 5631da177e4SLinus Torvalds } 564a14fe249SRusty Russell EXPORT_SYMBOL(param_get_string); 5651da177e4SLinus Torvalds 5669c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_string = { 5679bbb9e5aSRusty Russell .set = param_set_copystring, 5689bbb9e5aSRusty Russell .get = param_get_string, 5699bbb9e5aSRusty Russell }; 5709bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_string); 5719bbb9e5aSRusty Russell 5721da177e4SLinus Torvalds /* sysfs output in /sys/modules/XYZ/parameters/ */ 573350f8258SEdward Z. Yang #define to_module_attr(n) container_of(n, struct module_attribute, attr) 574350f8258SEdward Z. Yang #define to_module_kobject(n) container_of(n, struct module_kobject, kobj) 5751da177e4SLinus Torvalds 5761da177e4SLinus Torvalds struct param_attribute 5771da177e4SLinus Torvalds { 5781da177e4SLinus Torvalds struct module_attribute mattr; 5799bbb9e5aSRusty Russell const struct kernel_param *param; 5801da177e4SLinus Torvalds }; 5811da177e4SLinus Torvalds 5821da177e4SLinus Torvalds struct module_param_attrs 5831da177e4SLinus Torvalds { 5849b473de8SRusty Russell unsigned int num; 5851da177e4SLinus Torvalds struct attribute_group grp; 5861da177e4SLinus Torvalds struct param_attribute attrs[0]; 5871da177e4SLinus Torvalds }; 5881da177e4SLinus Torvalds 589ef665c1aSRandy Dunlap #ifdef CONFIG_SYSFS 590350f8258SEdward Z. Yang #define to_param_attr(n) container_of(n, struct param_attribute, mattr) 5911da177e4SLinus Torvalds 5921da177e4SLinus Torvalds static ssize_t param_attr_show(struct module_attribute *mattr, 5934befb026SKay Sievers struct module_kobject *mk, char *buf) 5941da177e4SLinus Torvalds { 5951da177e4SLinus Torvalds int count; 5961da177e4SLinus Torvalds struct param_attribute *attribute = to_param_attr(mattr); 5971da177e4SLinus Torvalds 5989bbb9e5aSRusty Russell if (!attribute->param->ops->get) 5991da177e4SLinus Torvalds return -EPERM; 6001da177e4SLinus Torvalds 601b51d23e4SDan Streetman kernel_param_lock(mk->mod); 6029bbb9e5aSRusty Russell count = attribute->param->ops->get(buf, attribute->param); 603b51d23e4SDan Streetman kernel_param_unlock(mk->mod); 6041da177e4SLinus Torvalds if (count > 0) { 6051da177e4SLinus Torvalds strcat(buf, "\n"); 6061da177e4SLinus Torvalds ++count; 6071da177e4SLinus Torvalds } 6081da177e4SLinus Torvalds return count; 6091da177e4SLinus Torvalds } 6101da177e4SLinus Torvalds 6111da177e4SLinus Torvalds /* sysfs always hands a nul-terminated string in buf. We rely on that. */ 6121da177e4SLinus Torvalds static ssize_t param_attr_store(struct module_attribute *mattr, 613b51d23e4SDan Streetman struct module_kobject *mk, 6141da177e4SLinus Torvalds const char *buf, size_t len) 6151da177e4SLinus Torvalds { 6161da177e4SLinus Torvalds int err; 6171da177e4SLinus Torvalds struct param_attribute *attribute = to_param_attr(mattr); 6181da177e4SLinus Torvalds 6199bbb9e5aSRusty Russell if (!attribute->param->ops->set) 6201da177e4SLinus Torvalds return -EPERM; 6211da177e4SLinus Torvalds 622b51d23e4SDan Streetman kernel_param_lock(mk->mod); 6237a486d37SRusty Russell param_check_unsafe(attribute->param); 6249bbb9e5aSRusty Russell err = attribute->param->ops->set(buf, attribute->param); 625b51d23e4SDan Streetman kernel_param_unlock(mk->mod); 6261da177e4SLinus Torvalds if (!err) 6271da177e4SLinus Torvalds return len; 6281da177e4SLinus Torvalds return err; 6291da177e4SLinus Torvalds } 630ef665c1aSRandy Dunlap #endif 6311da177e4SLinus Torvalds 6321da177e4SLinus Torvalds #ifdef CONFIG_MODULES 6331da177e4SLinus Torvalds #define __modinit 6341da177e4SLinus Torvalds #else 6351da177e4SLinus Torvalds #define __modinit __init 6361da177e4SLinus Torvalds #endif 6371da177e4SLinus Torvalds 638ef665c1aSRandy Dunlap #ifdef CONFIG_SYSFS 639b51d23e4SDan Streetman void kernel_param_lock(struct module *mod) 640907b29ebSRusty Russell { 641b51d23e4SDan Streetman mutex_lock(KPARAM_MUTEX(mod)); 642907b29ebSRusty Russell } 643907b29ebSRusty Russell 644b51d23e4SDan Streetman void kernel_param_unlock(struct module *mod) 645907b29ebSRusty Russell { 646b51d23e4SDan Streetman mutex_unlock(KPARAM_MUTEX(mod)); 647907b29ebSRusty Russell } 648b51d23e4SDan Streetman 649b51d23e4SDan Streetman EXPORT_SYMBOL(kernel_param_lock); 650b51d23e4SDan Streetman EXPORT_SYMBOL(kernel_param_unlock); 651907b29ebSRusty Russell 6521da177e4SLinus Torvalds /* 6539b473de8SRusty Russell * add_sysfs_param - add a parameter to sysfs 6549b473de8SRusty Russell * @mk: struct module_kobject 6559b473de8SRusty Russell * @kparam: the actual parameter definition to add to sysfs 6569b473de8SRusty Russell * @name: name of parameter 6571da177e4SLinus Torvalds * 6589b473de8SRusty Russell * Create a kobject if for a (per-module) parameter if mp NULL, and 6599b473de8SRusty Russell * create file in sysfs. Returns an error on out of memory. Always cleans up 6609b473de8SRusty Russell * if there's an error. 6611da177e4SLinus Torvalds */ 6629b473de8SRusty Russell static __modinit int add_sysfs_param(struct module_kobject *mk, 6639bbb9e5aSRusty Russell const struct kernel_param *kp, 6649b473de8SRusty Russell const char *name) 6651da177e4SLinus Torvalds { 66618eb74faSRusty Russell struct module_param_attrs *new_mp; 66718eb74faSRusty Russell struct attribute **new_attrs; 66818eb74faSRusty Russell unsigned int i; 6691da177e4SLinus Torvalds 6709b473de8SRusty Russell /* We don't bother calling this with invisible parameters. */ 6719b473de8SRusty Russell BUG_ON(!kp->perm); 6729b473de8SRusty Russell 6739b473de8SRusty Russell if (!mk->mp) { 67418eb74faSRusty Russell /* First allocation. */ 67518eb74faSRusty Russell mk->mp = kzalloc(sizeof(*mk->mp), GFP_KERNEL); 67618eb74faSRusty Russell if (!mk->mp) 67718eb74faSRusty Russell return -ENOMEM; 67818eb74faSRusty Russell mk->mp->grp.name = "parameters"; 67918eb74faSRusty Russell /* NULL-terminated attribute array. */ 68018eb74faSRusty Russell mk->mp->grp.attrs = kzalloc(sizeof(mk->mp->grp.attrs[0]), 6819b473de8SRusty Russell GFP_KERNEL); 68218eb74faSRusty Russell /* Caller will cleanup via free_module_param_attrs */ 68318eb74faSRusty Russell if (!mk->mp->grp.attrs) 68418eb74faSRusty Russell return -ENOMEM; 6851da177e4SLinus Torvalds } 6861da177e4SLinus Torvalds 68718eb74faSRusty Russell /* Enlarge allocations. */ 68818eb74faSRusty Russell new_mp = krealloc(mk->mp, 68918eb74faSRusty Russell sizeof(*mk->mp) + 69018eb74faSRusty Russell sizeof(mk->mp->attrs[0]) * (mk->mp->num + 1), 69118eb74faSRusty Russell GFP_KERNEL); 69218eb74faSRusty Russell if (!new_mp) 69318eb74faSRusty Russell return -ENOMEM; 69418eb74faSRusty Russell mk->mp = new_mp; 69518eb74faSRusty Russell 69618eb74faSRusty Russell /* Extra pointer for NULL terminator */ 69718eb74faSRusty Russell new_attrs = krealloc(mk->mp->grp.attrs, 69818eb74faSRusty Russell sizeof(mk->mp->grp.attrs[0]) * (mk->mp->num + 2), 69918eb74faSRusty Russell GFP_KERNEL); 70018eb74faSRusty Russell if (!new_attrs) 70118eb74faSRusty Russell return -ENOMEM; 70218eb74faSRusty Russell mk->mp->grp.attrs = new_attrs; 7039b473de8SRusty Russell 7049b473de8SRusty Russell /* Tack new one on the end. */ 705c772be52SRusty Russell memset(&mk->mp->attrs[mk->mp->num], 0, sizeof(mk->mp->attrs[0])); 70618eb74faSRusty Russell sysfs_attr_init(&mk->mp->attrs[mk->mp->num].mattr.attr); 70718eb74faSRusty Russell mk->mp->attrs[mk->mp->num].param = kp; 70818eb74faSRusty Russell mk->mp->attrs[mk->mp->num].mattr.show = param_attr_show; 709b0a65b0cSKees Cook /* Do not allow runtime DAC changes to make param writable. */ 710b0a65b0cSKees Cook if ((kp->perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) 71118eb74faSRusty Russell mk->mp->attrs[mk->mp->num].mattr.store = param_attr_store; 712574732c7SRusty Russell else 713574732c7SRusty Russell mk->mp->attrs[mk->mp->num].mattr.store = NULL; 71418eb74faSRusty Russell mk->mp->attrs[mk->mp->num].mattr.attr.name = (char *)name; 71518eb74faSRusty Russell mk->mp->attrs[mk->mp->num].mattr.attr.mode = kp->perm; 71618eb74faSRusty Russell mk->mp->num++; 7179b473de8SRusty Russell 7189b473de8SRusty Russell /* Fix up all the pointers, since krealloc can move us */ 71918eb74faSRusty Russell for (i = 0; i < mk->mp->num; i++) 72018eb74faSRusty Russell mk->mp->grp.attrs[i] = &mk->mp->attrs[i].mattr.attr; 72118eb74faSRusty Russell mk->mp->grp.attrs[mk->mp->num] = NULL; 7229b473de8SRusty Russell return 0; 7231da177e4SLinus Torvalds } 7249b473de8SRusty Russell 725d2441183SLinus Torvalds #ifdef CONFIG_MODULES 7269b473de8SRusty Russell static void free_module_param_attrs(struct module_kobject *mk) 7279b473de8SRusty Russell { 72818eb74faSRusty Russell if (mk->mp) 7299b473de8SRusty Russell kfree(mk->mp->grp.attrs); 7309b473de8SRusty Russell kfree(mk->mp); 7319b473de8SRusty Russell mk->mp = NULL; 7321da177e4SLinus Torvalds } 7331da177e4SLinus Torvalds 7341da177e4SLinus Torvalds /* 7351da177e4SLinus Torvalds * module_param_sysfs_setup - setup sysfs support for one module 7361da177e4SLinus Torvalds * @mod: module 7371da177e4SLinus Torvalds * @kparam: module parameters (array) 7381da177e4SLinus Torvalds * @num_params: number of module parameters 7391da177e4SLinus Torvalds * 7409b473de8SRusty Russell * Adds sysfs entries for module parameters under 7419b473de8SRusty Russell * /sys/module/[mod->name]/parameters/ 7421da177e4SLinus Torvalds */ 7431da177e4SLinus Torvalds int module_param_sysfs_setup(struct module *mod, 7449bbb9e5aSRusty Russell const struct kernel_param *kparam, 7451da177e4SLinus Torvalds unsigned int num_params) 7461da177e4SLinus Torvalds { 7479b473de8SRusty Russell int i, err; 7489b473de8SRusty Russell bool params = false; 7491da177e4SLinus Torvalds 7509b473de8SRusty Russell for (i = 0; i < num_params; i++) { 7519b473de8SRusty Russell if (kparam[i].perm == 0) 7529b473de8SRusty Russell continue; 7539b473de8SRusty Russell err = add_sysfs_param(&mod->mkobj, &kparam[i], kparam[i].name); 75418eb74faSRusty Russell if (err) { 75518eb74faSRusty Russell free_module_param_attrs(&mod->mkobj); 7569b473de8SRusty Russell return err; 75718eb74faSRusty Russell } 7589b473de8SRusty Russell params = true; 7599b473de8SRusty Russell } 7601da177e4SLinus Torvalds 7619b473de8SRusty Russell if (!params) 7621da177e4SLinus Torvalds return 0; 7639b473de8SRusty Russell 7649b473de8SRusty Russell /* Create the param group. */ 7659b473de8SRusty Russell err = sysfs_create_group(&mod->mkobj.kobj, &mod->mkobj.mp->grp); 7669b473de8SRusty Russell if (err) 7679b473de8SRusty Russell free_module_param_attrs(&mod->mkobj); 7689b473de8SRusty Russell return err; 7691da177e4SLinus Torvalds } 7701da177e4SLinus Torvalds 7711da177e4SLinus Torvalds /* 7721da177e4SLinus Torvalds * module_param_sysfs_remove - remove sysfs support for one module 7731da177e4SLinus Torvalds * @mod: module 7741da177e4SLinus Torvalds * 7751da177e4SLinus Torvalds * Remove sysfs entries for module parameters and the corresponding 7761da177e4SLinus Torvalds * kobject. 7771da177e4SLinus Torvalds */ 7781da177e4SLinus Torvalds void module_param_sysfs_remove(struct module *mod) 7791da177e4SLinus Torvalds { 7809b473de8SRusty Russell if (mod->mkobj.mp) { 7819b473de8SRusty Russell sysfs_remove_group(&mod->mkobj.kobj, &mod->mkobj.mp->grp); 7821da177e4SLinus Torvalds /* We are positive that no one is using any param 7831da177e4SLinus Torvalds * attrs at this point. Deallocate immediately. */ 7849b473de8SRusty Russell free_module_param_attrs(&mod->mkobj); 7851da177e4SLinus Torvalds } 7861da177e4SLinus Torvalds } 7871da177e4SLinus Torvalds #endif 7881da177e4SLinus Torvalds 789e180a6b7SRusty Russell void destroy_params(const struct kernel_param *params, unsigned num) 790e180a6b7SRusty Russell { 791e6df34a4SRusty Russell unsigned int i; 792e6df34a4SRusty Russell 793e6df34a4SRusty Russell for (i = 0; i < num; i++) 794e6df34a4SRusty Russell if (params[i].ops->free) 795e6df34a4SRusty Russell params[i].ops->free(params[i].arg); 796e180a6b7SRusty Russell } 797e180a6b7SRusty Russell 798e94965edSDmitry Torokhov static struct module_kobject * __init locate_module_kobject(const char *name) 7991da177e4SLinus Torvalds { 8001da177e4SLinus Torvalds struct module_kobject *mk; 8019b473de8SRusty Russell struct kobject *kobj; 8029b473de8SRusty Russell int err; 8031da177e4SLinus Torvalds 8049b473de8SRusty Russell kobj = kset_find_obj(module_kset, name); 8059b473de8SRusty Russell if (kobj) { 8069b473de8SRusty Russell mk = to_module_kobject(kobj); 8079b473de8SRusty Russell } else { 808dd392710SPekka J Enberg mk = kzalloc(sizeof(struct module_kobject), GFP_KERNEL); 809dd392710SPekka J Enberg BUG_ON(!mk); 8101da177e4SLinus Torvalds 8111da177e4SLinus Torvalds mk->mod = THIS_MODULE; 8127405c1e1SGreg Kroah-Hartman mk->kobj.kset = module_kset; 8139b473de8SRusty Russell err = kobject_init_and_add(&mk->kobj, &module_ktype, NULL, 8149b473de8SRusty Russell "%s", name); 81588bfa324SKay Sievers #ifdef CONFIG_MODULES 81688bfa324SKay Sievers if (!err) 81788bfa324SKay Sievers err = sysfs_create_file(&mk->kobj, &module_uevent.attr); 81888bfa324SKay Sievers #endif 8199b473de8SRusty Russell if (err) { 820e43b9192SGreg Kroah-Hartman kobject_put(&mk->kobj); 821b5f3abf9SJim Cromie pr_crit("Adding module '%s' to sysfs failed (%d), the system may be unstable.\n", 822e94965edSDmitry Torokhov name, err); 823e94965edSDmitry Torokhov return NULL; 82474c5b597SGreg Kroah-Hartman } 825e94965edSDmitry Torokhov 826e94965edSDmitry Torokhov /* So that we hold reference in both cases. */ 8279b473de8SRusty Russell kobject_get(&mk->kobj); 8289b473de8SRusty Russell } 8299b473de8SRusty Russell 830e94965edSDmitry Torokhov return mk; 831e94965edSDmitry Torokhov } 832e94965edSDmitry Torokhov 833e94965edSDmitry Torokhov static void __init kernel_add_sysfs_param(const char *name, 83463a12d9dSGeert Uytterhoeven const struct kernel_param *kparam, 835e94965edSDmitry Torokhov unsigned int name_skip) 836e94965edSDmitry Torokhov { 837e94965edSDmitry Torokhov struct module_kobject *mk; 838e94965edSDmitry Torokhov int err; 839e94965edSDmitry Torokhov 840e94965edSDmitry Torokhov mk = locate_module_kobject(name); 841e94965edSDmitry Torokhov if (!mk) 842e94965edSDmitry Torokhov return; 843e94965edSDmitry Torokhov 844e94965edSDmitry Torokhov /* We need to remove old parameters before adding more. */ 845e94965edSDmitry Torokhov if (mk->mp) 846e94965edSDmitry Torokhov sysfs_remove_group(&mk->kobj, &mk->mp->grp); 847e94965edSDmitry Torokhov 8489b473de8SRusty Russell /* These should not fail at boot. */ 8499b473de8SRusty Russell err = add_sysfs_param(mk, kparam, kparam->name + name_skip); 8509b473de8SRusty Russell BUG_ON(err); 8519b473de8SRusty Russell err = sysfs_create_group(&mk->kobj, &mk->mp->grp); 8529b473de8SRusty Russell BUG_ON(err); 853f30c53a8SKay Sievers kobject_uevent(&mk->kobj, KOBJ_ADD); 8549b473de8SRusty Russell kobject_put(&mk->kobj); 8551da177e4SLinus Torvalds } 8561da177e4SLinus Torvalds 8571da177e4SLinus Torvalds /* 858b634d130SJean Delvare * param_sysfs_builtin - add sysfs parameters for built-in modules 8591da177e4SLinus Torvalds * 8601da177e4SLinus Torvalds * Add module_parameters to sysfs for "modules" built into the kernel. 8611da177e4SLinus Torvalds * 8621da177e4SLinus Torvalds * The "module" name (KBUILD_MODNAME) is stored before a dot, the 8631da177e4SLinus Torvalds * "parameter" name is stored behind a dot in kernel_param->name. So, 8641da177e4SLinus Torvalds * extract the "module" name for all built-in kernel_param-eters, 8659b473de8SRusty Russell * and for all who have the same, call kernel_add_sysfs_param. 8661da177e4SLinus Torvalds */ 8671da177e4SLinus Torvalds static void __init param_sysfs_builtin(void) 8681da177e4SLinus Torvalds { 86963a12d9dSGeert Uytterhoeven const struct kernel_param *kp; 8709b473de8SRusty Russell unsigned int name_len; 8719b473de8SRusty Russell char modname[MODULE_NAME_LEN]; 8721da177e4SLinus Torvalds 8739b473de8SRusty Russell for (kp = __start___param; kp < __stop___param; kp++) { 8741da177e4SLinus Torvalds char *dot; 8751da177e4SLinus Torvalds 8769b473de8SRusty Russell if (kp->perm == 0) 8779b473de8SRusty Russell continue; 8781da177e4SLinus Torvalds 879730b69d2SRusty Russell dot = strchr(kp->name, '.'); 8801da177e4SLinus Torvalds if (!dot) { 88167e67ceaSRusty Russell /* This happens for core_param() */ 88267e67ceaSRusty Russell strcpy(modname, "kernel"); 88367e67ceaSRusty Russell name_len = 0; 88467e67ceaSRusty Russell } else { 88567e67ceaSRusty Russell name_len = dot - kp->name + 1; 88667e67ceaSRusty Russell strlcpy(modname, kp->name, name_len); 8871da177e4SLinus Torvalds } 88867e67ceaSRusty Russell kernel_add_sysfs_param(modname, kp, name_len); 8891da177e4SLinus Torvalds } 8901da177e4SLinus Torvalds } 8911da177e4SLinus Torvalds 892e94965edSDmitry Torokhov ssize_t __modver_version_show(struct module_attribute *mattr, 8934befb026SKay Sievers struct module_kobject *mk, char *buf) 894e94965edSDmitry Torokhov { 895e94965edSDmitry Torokhov struct module_version_attribute *vattr = 896e94965edSDmitry Torokhov container_of(mattr, struct module_version_attribute, mattr); 897e94965edSDmitry Torokhov 898f4940ab7SChen Gang return scnprintf(buf, PAGE_SIZE, "%s\n", vattr->version); 899e94965edSDmitry Torokhov } 900e94965edSDmitry Torokhov 901b4bc8428SDmitry Torokhov extern const struct module_version_attribute *__start___modver[]; 902b4bc8428SDmitry Torokhov extern const struct module_version_attribute *__stop___modver[]; 903e94965edSDmitry Torokhov 904e94965edSDmitry Torokhov static void __init version_sysfs_builtin(void) 905e94965edSDmitry Torokhov { 906b4bc8428SDmitry Torokhov const struct module_version_attribute **p; 907e94965edSDmitry Torokhov struct module_kobject *mk; 908e94965edSDmitry Torokhov int err; 909e94965edSDmitry Torokhov 910b4bc8428SDmitry Torokhov for (p = __start___modver; p < __stop___modver; p++) { 911b4bc8428SDmitry Torokhov const struct module_version_attribute *vattr = *p; 912b4bc8428SDmitry Torokhov 913e94965edSDmitry Torokhov mk = locate_module_kobject(vattr->module_name); 914e94965edSDmitry Torokhov if (mk) { 915e94965edSDmitry Torokhov err = sysfs_create_file(&mk->kobj, &vattr->mattr.attr); 91674c3dea3SRusty Russell WARN_ON_ONCE(err); 917e94965edSDmitry Torokhov kobject_uevent(&mk->kobj, KOBJ_ADD); 918e94965edSDmitry Torokhov kobject_put(&mk->kobj); 919e94965edSDmitry Torokhov } 920e94965edSDmitry Torokhov } 921e94965edSDmitry Torokhov } 9221da177e4SLinus Torvalds 9231da177e4SLinus Torvalds /* module-related sysfs stuff */ 9241da177e4SLinus Torvalds 9251da177e4SLinus Torvalds static ssize_t module_attr_show(struct kobject *kobj, 9261da177e4SLinus Torvalds struct attribute *attr, 9271da177e4SLinus Torvalds char *buf) 9281da177e4SLinus Torvalds { 9291da177e4SLinus Torvalds struct module_attribute *attribute; 9301da177e4SLinus Torvalds struct module_kobject *mk; 9311da177e4SLinus Torvalds int ret; 9321da177e4SLinus Torvalds 9331da177e4SLinus Torvalds attribute = to_module_attr(attr); 9341da177e4SLinus Torvalds mk = to_module_kobject(kobj); 9351da177e4SLinus Torvalds 9361da177e4SLinus Torvalds if (!attribute->show) 93770f2817aSDmitry Torokhov return -EIO; 9381da177e4SLinus Torvalds 9394befb026SKay Sievers ret = attribute->show(attribute, mk, buf); 9401da177e4SLinus Torvalds 9411da177e4SLinus Torvalds return ret; 9421da177e4SLinus Torvalds } 9431da177e4SLinus Torvalds 9441da177e4SLinus Torvalds static ssize_t module_attr_store(struct kobject *kobj, 9451da177e4SLinus Torvalds struct attribute *attr, 9461da177e4SLinus Torvalds const char *buf, size_t len) 9471da177e4SLinus Torvalds { 9481da177e4SLinus Torvalds struct module_attribute *attribute; 9491da177e4SLinus Torvalds struct module_kobject *mk; 9501da177e4SLinus Torvalds int ret; 9511da177e4SLinus Torvalds 9521da177e4SLinus Torvalds attribute = to_module_attr(attr); 9531da177e4SLinus Torvalds mk = to_module_kobject(kobj); 9541da177e4SLinus Torvalds 9551da177e4SLinus Torvalds if (!attribute->store) 95670f2817aSDmitry Torokhov return -EIO; 9571da177e4SLinus Torvalds 9584befb026SKay Sievers ret = attribute->store(attribute, mk, buf, len); 9591da177e4SLinus Torvalds 9601da177e4SLinus Torvalds return ret; 9611da177e4SLinus Torvalds } 9621da177e4SLinus Torvalds 96352cf25d0SEmese Revfy static const struct sysfs_ops module_sysfs_ops = { 9641da177e4SLinus Torvalds .show = module_attr_show, 9651da177e4SLinus Torvalds .store = module_attr_store, 9661da177e4SLinus Torvalds }; 9671da177e4SLinus Torvalds 968270a6c4cSKay Sievers static int uevent_filter(struct kset *kset, struct kobject *kobj) 969270a6c4cSKay Sievers { 970270a6c4cSKay Sievers struct kobj_type *ktype = get_ktype(kobj); 971270a6c4cSKay Sievers 972270a6c4cSKay Sievers if (ktype == &module_ktype) 973270a6c4cSKay Sievers return 1; 974270a6c4cSKay Sievers return 0; 975270a6c4cSKay Sievers } 976270a6c4cSKay Sievers 9779cd43611SEmese Revfy static const struct kset_uevent_ops module_uevent_ops = { 978270a6c4cSKay Sievers .filter = uevent_filter, 979270a6c4cSKay Sievers }; 980270a6c4cSKay Sievers 9817405c1e1SGreg Kroah-Hartman struct kset *module_kset; 982823bccfcSGreg Kroah-Hartman int module_sysfs_initialized; 9831da177e4SLinus Torvalds 984942e4431SLi Zhong static void module_kobj_release(struct kobject *kobj) 985942e4431SLi Zhong { 986942e4431SLi Zhong struct module_kobject *mk = to_module_kobject(kobj); 987942e4431SLi Zhong complete(mk->kobj_completion); 988942e4431SLi Zhong } 989942e4431SLi Zhong 9907405c1e1SGreg Kroah-Hartman struct kobj_type module_ktype = { 991942e4431SLi Zhong .release = module_kobj_release, 9921da177e4SLinus Torvalds .sysfs_ops = &module_sysfs_ops, 9931da177e4SLinus Torvalds }; 9941da177e4SLinus Torvalds 9951da177e4SLinus Torvalds /* 9961da177e4SLinus Torvalds * param_sysfs_init - wrapper for built-in params support 9971da177e4SLinus Torvalds */ 9981da177e4SLinus Torvalds static int __init param_sysfs_init(void) 9991da177e4SLinus Torvalds { 10007405c1e1SGreg Kroah-Hartman module_kset = kset_create_and_add("module", &module_uevent_ops, NULL); 10017405c1e1SGreg Kroah-Hartman if (!module_kset) { 10027405c1e1SGreg Kroah-Hartman printk(KERN_WARNING "%s (%d): error creating kset\n", 10037405c1e1SGreg Kroah-Hartman __FILE__, __LINE__); 10047405c1e1SGreg Kroah-Hartman return -ENOMEM; 1005d8c7649eSRandy Dunlap } 1006823bccfcSGreg Kroah-Hartman module_sysfs_initialized = 1; 10071da177e4SLinus Torvalds 1008e94965edSDmitry Torokhov version_sysfs_builtin(); 10091da177e4SLinus Torvalds param_sysfs_builtin(); 10101da177e4SLinus Torvalds 10111da177e4SLinus Torvalds return 0; 10121da177e4SLinus Torvalds } 1013d10be6d1SMark Huang subsys_initcall(param_sysfs_init); 10141da177e4SLinus Torvalds 10157405c1e1SGreg Kroah-Hartman #endif /* CONFIG_SYSFS */ 1016