11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /* Helpers for initial module or kernel cmdline parsing
31da177e4SLinus Torvalds Copyright (C) 2001 Rusty Russell.
41da177e4SLinus Torvalds
51da177e4SLinus Torvalds */
61da177e4SLinus Torvalds #include <linux/kernel.h>
7def7b92eSChristophe JAILLET #include <linux/kstrtox.h>
81da177e4SLinus Torvalds #include <linux/string.h>
91da177e4SLinus Torvalds #include <linux/errno.h>
101da177e4SLinus Torvalds #include <linux/module.h>
1163a12d9dSGeert Uytterhoeven #include <linux/moduleparam.h>
121da177e4SLinus Torvalds #include <linux/device.h>
131da177e4SLinus Torvalds #include <linux/err.h>
144e57b681STim Schmielau #include <linux/slab.h>
1526d052bfSPeter Oberparleiter #include <linux/ctype.h>
1620657f66SDavid Howells #include <linux/security.h>
171da177e4SLinus Torvalds
18cf2fde7bSRusty Russell #ifdef CONFIG_SYSFS
19b51d23e4SDan Streetman /* Protects all built-in parameters, modules use their own param_lock */
20907b29ebSRusty Russell static DEFINE_MUTEX(param_lock);
21907b29ebSRusty Russell
22b51d23e4SDan Streetman /* Use the module's mutex, or if built-in use the built-in mutex */
2320bdc2cfSStephen Rothwell #ifdef CONFIG_MODULES
24b51d23e4SDan Streetman #define KPARAM_MUTEX(mod) ((mod) ? &(mod)->param_lock : ¶m_lock)
2520bdc2cfSStephen Rothwell #else
2620bdc2cfSStephen Rothwell #define KPARAM_MUTEX(mod) (¶m_lock)
2720bdc2cfSStephen Rothwell #endif
28cf2fde7bSRusty Russell
check_kparam_locked(struct module * mod)29cf2fde7bSRusty Russell static inline void check_kparam_locked(struct module *mod)
30cf2fde7bSRusty Russell {
31cf2fde7bSRusty Russell BUG_ON(!mutex_is_locked(KPARAM_MUTEX(mod)));
32cf2fde7bSRusty Russell }
33cf2fde7bSRusty Russell #else
check_kparam_locked(struct module * mod)34cf2fde7bSRusty Russell static inline void check_kparam_locked(struct module *mod)
35cf2fde7bSRusty Russell {
36cf2fde7bSRusty Russell }
37cf2fde7bSRusty Russell #endif /* !CONFIG_SYSFS */
38b51d23e4SDan Streetman
39a1054322SRusty Russell /* This just allows us to keep track of which parameters are kmalloced. */
40a1054322SRusty Russell struct kmalloced_param {
41a1054322SRusty Russell struct list_head list;
42a1054322SRusty Russell char val[];
43a1054322SRusty Russell };
44a1054322SRusty Russell static LIST_HEAD(kmalloced_params);
45b51d23e4SDan Streetman static DEFINE_SPINLOCK(kmalloced_params_lock);
46a1054322SRusty Russell
kmalloc_parameter(unsigned int size)47a1054322SRusty Russell static void *kmalloc_parameter(unsigned int size)
48a1054322SRusty Russell {
49a1054322SRusty Russell struct kmalloced_param *p;
50a1054322SRusty Russell
51a1054322SRusty Russell p = kmalloc(sizeof(*p) + size, GFP_KERNEL);
52a1054322SRusty Russell if (!p)
53a1054322SRusty Russell return NULL;
54a1054322SRusty Russell
55b51d23e4SDan Streetman spin_lock(&kmalloced_params_lock);
56a1054322SRusty Russell list_add(&p->list, &kmalloced_params);
57b51d23e4SDan Streetman spin_unlock(&kmalloced_params_lock);
58b51d23e4SDan Streetman
59a1054322SRusty Russell return p->val;
60a1054322SRusty Russell }
61a1054322SRusty Russell
62a1054322SRusty Russell /* Does nothing if parameter wasn't kmalloced above. */
maybe_kfree_parameter(void * param)63a1054322SRusty Russell static void maybe_kfree_parameter(void *param)
64a1054322SRusty Russell {
65a1054322SRusty Russell struct kmalloced_param *p;
66a1054322SRusty Russell
67b51d23e4SDan Streetman spin_lock(&kmalloced_params_lock);
68a1054322SRusty Russell list_for_each_entry(p, &kmalloced_params, list) {
69a1054322SRusty Russell if (p->val == param) {
70a1054322SRusty Russell list_del(&p->list);
71a1054322SRusty Russell kfree(p);
72a1054322SRusty Russell break;
73a1054322SRusty Russell }
74a1054322SRusty Russell }
75b51d23e4SDan Streetman spin_unlock(&kmalloced_params_lock);
76a1054322SRusty Russell }
77a1054322SRusty Russell
dash2underscore(char c)78b1e4d20cSMichal Schmidt static char dash2underscore(char c)
791da177e4SLinus Torvalds {
801da177e4SLinus Torvalds if (c == '-')
811da177e4SLinus Torvalds return '_';
821da177e4SLinus Torvalds return c;
831da177e4SLinus Torvalds }
841da177e4SLinus Torvalds
parameqn(const char * a,const char * b,size_t n)85b1e4d20cSMichal Schmidt bool parameqn(const char *a, const char *b, size_t n)
861da177e4SLinus Torvalds {
87b1e4d20cSMichal Schmidt size_t i;
88b1e4d20cSMichal Schmidt
89b1e4d20cSMichal Schmidt for (i = 0; i < n; i++) {
90b1e4d20cSMichal Schmidt if (dash2underscore(a[i]) != dash2underscore(b[i]))
91b1e4d20cSMichal Schmidt return false;
92b1e4d20cSMichal Schmidt }
93b1e4d20cSMichal Schmidt return true;
94b1e4d20cSMichal Schmidt }
95b1e4d20cSMichal Schmidt
parameq(const char * a,const char * b)96b1e4d20cSMichal Schmidt bool parameq(const char *a, const char *b)
97b1e4d20cSMichal Schmidt {
98b1e4d20cSMichal Schmidt return parameqn(a, b, strlen(a)+1);
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds
param_check_unsafe(const struct kernel_param * kp)10120657f66SDavid Howells static bool param_check_unsafe(const struct kernel_param *kp)
1027a486d37SRusty Russell {
10320657f66SDavid Howells if (kp->flags & KERNEL_PARAM_FL_HWPARAM &&
10420657f66SDavid Howells security_locked_down(LOCKDOWN_MODULE_PARAMETERS))
10520657f66SDavid Howells return false;
10620657f66SDavid Howells
1077a486d37SRusty Russell if (kp->flags & KERNEL_PARAM_FL_UNSAFE) {
108edc41b3cSChris Wilson pr_notice("Setting dangerous option %s - tainting kernel\n",
1097a486d37SRusty Russell kp->name);
1107a486d37SRusty Russell add_taint(TAINT_USER, LOCKDEP_STILL_OK);
1117a486d37SRusty Russell }
11220657f66SDavid Howells
11320657f66SDavid Howells return true;
1147a486d37SRusty Russell }
1157a486d37SRusty Russell
parse_one(char * param,char * val,const char * doing,const struct kernel_param * params,unsigned num_params,s16 min_level,s16 max_level,void * arg,int (* handle_unknown)(char * param,char * val,const char * doing,void * arg))1161da177e4SLinus Torvalds static int parse_one(char *param,
1171da177e4SLinus Torvalds char *val,
1189fb48c74SJim Cromie const char *doing,
119914dcaa8SRusty Russell const struct kernel_param *params,
1201da177e4SLinus Torvalds unsigned num_params,
121026cee00SPawel Moll s16 min_level,
122026cee00SPawel Moll s16 max_level,
123ecc86170SLuis R. Rodriguez void *arg,
1249fb48c74SJim Cromie int (*handle_unknown)(char *param, char *val,
125ecc86170SLuis R. Rodriguez const char *doing, void *arg))
1261da177e4SLinus Torvalds {
1271da177e4SLinus Torvalds unsigned int i;
128907b29ebSRusty Russell int err;
1291da177e4SLinus Torvalds
1301da177e4SLinus Torvalds /* Find parameter */
1311da177e4SLinus Torvalds for (i = 0; i < num_params; i++) {
1321da177e4SLinus Torvalds if (parameq(param, params[i].name)) {
133026cee00SPawel Moll if (params[i].level < min_level
134026cee00SPawel Moll || params[i].level > max_level)
135026cee00SPawel Moll return 0;
1362e9fb995SRusty Russell /* No one handled NULL, so do it here. */
137ab013c5fSSteven Rostedt if (!val &&
1386a4c2643SJani Nikula !(params[i].ops->flags & KERNEL_PARAM_OPS_FL_NOARG))
1392e9fb995SRusty Russell return -EINVAL;
1409fb48c74SJim Cromie pr_debug("handling %s with %p\n", param,
1419bbb9e5aSRusty Russell params[i].ops->set);
142b51d23e4SDan Streetman kernel_param_lock(params[i].mod);
14320657f66SDavid Howells if (param_check_unsafe(¶ms[i]))
144907b29ebSRusty Russell err = params[i].ops->set(val, ¶ms[i]);
14520657f66SDavid Howells else
14620657f66SDavid Howells err = -EPERM;
147b51d23e4SDan Streetman kernel_param_unlock(params[i].mod);
148907b29ebSRusty Russell return err;
1491da177e4SLinus Torvalds }
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds
1521da177e4SLinus Torvalds if (handle_unknown) {
1539fb48c74SJim Cromie pr_debug("doing %s: %s='%s'\n", doing, param, val);
154ecc86170SLuis R. Rodriguez return handle_unknown(param, val, doing, arg);
1551da177e4SLinus Torvalds }
1561da177e4SLinus Torvalds
1579fb48c74SJim Cromie pr_debug("Unknown argument '%s'\n", param);
1581da177e4SLinus Torvalds return -ENOENT;
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds
1611da177e4SLinus Torvalds /* Args looks like "foo=bar,bar2 baz=fuz wiz". */
parse_args(const char * doing,char * args,const struct kernel_param * params,unsigned num,s16 min_level,s16 max_level,void * arg,int (* unknown)(char * param,char * val,const char * doing,void * arg))16251e158c1SRusty Russell char *parse_args(const char *doing,
1631da177e4SLinus Torvalds char *args,
164914dcaa8SRusty Russell const struct kernel_param *params,
1651da177e4SLinus Torvalds unsigned num,
166026cee00SPawel Moll s16 min_level,
167026cee00SPawel Moll s16 max_level,
168ecc86170SLuis R. Rodriguez void *arg,
169ecc86170SLuis R. Rodriguez int (*unknown)(char *param, char *val,
170ecc86170SLuis R. Rodriguez const char *doing, void *arg))
1711da177e4SLinus Torvalds {
17274b22c46SOleg Nesterov char *param, *val, *err = NULL;
1731da177e4SLinus Torvalds
174f36462f0SRusty Russell /* Chew leading spaces */
175e7d2860bSAndré Goddard Rosa args = skip_spaces(args);
176f36462f0SRusty Russell
1771ef9eaf2SJim Cromie if (*args)
1789fb48c74SJim Cromie pr_debug("doing %s, parsing ARGS: '%s'\n", doing, args);
1799fb48c74SJim Cromie
1801da177e4SLinus Torvalds while (*args) {
1811da177e4SLinus Torvalds int ret;
182a416aba6SArd van Breemen int irq_was_disabled;
1831da177e4SLinus Torvalds
1841da177e4SLinus Torvalds args = next_arg(args, ¶m, &val);
18551e158c1SRusty Russell /* Stop at -- */
18651e158c1SRusty Russell if (!val && strcmp(param, "--") == 0)
18774b22c46SOleg Nesterov return err ?: args;
188a416aba6SArd van Breemen irq_was_disabled = irqs_disabled();
1899fb48c74SJim Cromie ret = parse_one(param, val, doing, params, num,
190ecc86170SLuis R. Rodriguez min_level, max_level, arg, unknown);
191b5f3abf9SJim Cromie if (irq_was_disabled && !irqs_disabled())
192b5f3abf9SJim Cromie pr_warn("%s: option '%s' enabled irq's!\n",
193b5f3abf9SJim Cromie doing, param);
194b5f3abf9SJim Cromie
1951da177e4SLinus Torvalds switch (ret) {
19674b22c46SOleg Nesterov case 0:
19774b22c46SOleg Nesterov continue;
1981da177e4SLinus Torvalds case -ENOENT:
199b5f3abf9SJim Cromie pr_err("%s: Unknown parameter `%s'\n", doing, param);
20074b22c46SOleg Nesterov break;
2011da177e4SLinus Torvalds case -ENOSPC:
202b5f3abf9SJim Cromie pr_err("%s: `%s' too large for parameter `%s'\n",
2039fb48c74SJim Cromie doing, val ?: "", param);
2041da177e4SLinus Torvalds break;
2051da177e4SLinus Torvalds default:
206b5f3abf9SJim Cromie pr_err("%s: `%s' invalid for parameter `%s'\n",
2079fb48c74SJim Cromie doing, val ?: "", param);
20874b22c46SOleg Nesterov break;
2091da177e4SLinus Torvalds }
2101da177e4SLinus Torvalds
21174b22c46SOleg Nesterov err = ERR_PTR(ret);
21274b22c46SOleg Nesterov }
21374b22c46SOleg Nesterov
21474b22c46SOleg Nesterov return err;
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds
2171da177e4SLinus Torvalds /* Lazy bastard, eh? */
21888a88b32SFelipe Contreras #define STANDARD_PARAM_DEF(name, type, format, strtolfn) \
2199bbb9e5aSRusty Russell int param_set_##name(const char *val, const struct kernel_param *kp) \
2201da177e4SLinus Torvalds { \
22188a88b32SFelipe Contreras return strtolfn(val, 0, (type *)kp->arg); \
2221da177e4SLinus Torvalds } \
2239bbb9e5aSRusty Russell int param_get_##name(char *buffer, const struct kernel_param *kp) \
2241da177e4SLinus Torvalds { \
22596802e6bSJean Delvare return scnprintf(buffer, PAGE_SIZE, format "\n", \
226f4940ab7SChen Gang *((type *)kp->arg)); \
227a14fe249SRusty Russell } \
2289c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_##name = { \
2299bbb9e5aSRusty Russell .set = param_set_##name, \
2309bbb9e5aSRusty Russell .get = param_get_##name, \
2319bbb9e5aSRusty Russell }; \
232a14fe249SRusty Russell EXPORT_SYMBOL(param_set_##name); \
2339bbb9e5aSRusty Russell EXPORT_SYMBOL(param_get_##name); \
2349bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_##name)
2359bbb9e5aSRusty Russell
2361da177e4SLinus Torvalds
23788a88b32SFelipe Contreras STANDARD_PARAM_DEF(byte, unsigned char, "%hhu", kstrtou8);
23888a88b32SFelipe Contreras STANDARD_PARAM_DEF(short, short, "%hi", kstrtos16);
23988a88b32SFelipe Contreras STANDARD_PARAM_DEF(ushort, unsigned short, "%hu", kstrtou16);
24088a88b32SFelipe Contreras STANDARD_PARAM_DEF(int, int, "%i", kstrtoint);
24188a88b32SFelipe Contreras STANDARD_PARAM_DEF(uint, unsigned int, "%u", kstrtouint);
24288a88b32SFelipe Contreras STANDARD_PARAM_DEF(long, long, "%li", kstrtol);
24388a88b32SFelipe Contreras STANDARD_PARAM_DEF(ulong, unsigned long, "%lu", kstrtoul);
244b4210b81SHannes Reinecke STANDARD_PARAM_DEF(ullong, unsigned long long, "%llu", kstrtoull);
2457d836577SPaul Menzel STANDARD_PARAM_DEF(hexint, unsigned int, "%#08x", kstrtouint);
2461da177e4SLinus Torvalds
param_set_uint_minmax(const char * val,const struct kernel_param * kp,unsigned int min,unsigned int max)2472a14c9aeSSagi Grimberg int param_set_uint_minmax(const char *val, const struct kernel_param *kp,
2482a14c9aeSSagi Grimberg unsigned int min, unsigned int max)
2492a14c9aeSSagi Grimberg {
2502a14c9aeSSagi Grimberg unsigned int num;
2512a14c9aeSSagi Grimberg int ret;
2522a14c9aeSSagi Grimberg
2532a14c9aeSSagi Grimberg if (!val)
2542a14c9aeSSagi Grimberg return -EINVAL;
2552a14c9aeSSagi Grimberg ret = kstrtouint(val, 0, &num);
2562a14c9aeSSagi Grimberg if (ret)
2572a14c9aeSSagi Grimberg return ret;
2582a14c9aeSSagi Grimberg if (num < min || num > max)
2592a14c9aeSSagi Grimberg return -EINVAL;
2602a14c9aeSSagi Grimberg *((unsigned int *)kp->arg) = num;
2612a14c9aeSSagi Grimberg return 0;
2622a14c9aeSSagi Grimberg }
2632a14c9aeSSagi Grimberg EXPORT_SYMBOL_GPL(param_set_uint_minmax);
2642a14c9aeSSagi Grimberg
param_set_charp(const char * val,const struct kernel_param * kp)2659bbb9e5aSRusty Russell int param_set_charp(const char *val, const struct kernel_param *kp)
2661da177e4SLinus Torvalds {
2671da177e4SLinus Torvalds if (strlen(val) > 1024) {
268b5f3abf9SJim Cromie pr_err("%s: string parameter too long\n", kp->name);
2691da177e4SLinus Torvalds return -ENOSPC;
2701da177e4SLinus Torvalds }
2711da177e4SLinus Torvalds
272a1054322SRusty Russell maybe_kfree_parameter(*(char **)kp->arg);
273a1054322SRusty Russell
274a1054322SRusty Russell /* This is a hack. We can't kmalloc in early boot, and we
275e180a6b7SRusty Russell * don't need to; this mangled commandline is preserved. */
276e180a6b7SRusty Russell if (slab_is_available()) {
277a1054322SRusty Russell *(char **)kp->arg = kmalloc_parameter(strlen(val)+1);
278d553ad86SRusty Russell if (!*(char **)kp->arg)
279e180a6b7SRusty Russell return -ENOMEM;
280a1054322SRusty Russell strcpy(*(char **)kp->arg, val);
281e180a6b7SRusty Russell } else
282e180a6b7SRusty Russell *(const char **)kp->arg = val;
283e180a6b7SRusty Russell
2841da177e4SLinus Torvalds return 0;
2851da177e4SLinus Torvalds }
286a14fe249SRusty Russell EXPORT_SYMBOL(param_set_charp);
2871da177e4SLinus Torvalds
param_get_charp(char * buffer,const struct kernel_param * kp)2889bbb9e5aSRusty Russell int param_get_charp(char *buffer, const struct kernel_param *kp)
2891da177e4SLinus Torvalds {
29096802e6bSJean Delvare return scnprintf(buffer, PAGE_SIZE, "%s\n", *((char **)kp->arg));
2911da177e4SLinus Torvalds }
292a14fe249SRusty Russell EXPORT_SYMBOL(param_get_charp);
2931da177e4SLinus Torvalds
param_free_charp(void * arg)2943d9c637fSDan Streetman void param_free_charp(void *arg)
295a1054322SRusty Russell {
296a1054322SRusty Russell maybe_kfree_parameter(*((char **)arg));
297a1054322SRusty Russell }
2983d9c637fSDan Streetman EXPORT_SYMBOL(param_free_charp);
299a1054322SRusty Russell
3009c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_charp = {
3019bbb9e5aSRusty Russell .set = param_set_charp,
3029bbb9e5aSRusty Russell .get = param_get_charp,
303a1054322SRusty Russell .free = param_free_charp,
3049bbb9e5aSRusty Russell };
3059bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_charp);
3069bbb9e5aSRusty Russell
307fddd5201SRusty Russell /* Actually could be a bool or an int, for historical reasons. */
param_set_bool(const char * val,const struct kernel_param * kp)3089bbb9e5aSRusty Russell int param_set_bool(const char *val, const struct kernel_param *kp)
3091da177e4SLinus Torvalds {
3101da177e4SLinus Torvalds /* No equals means "set"... */
3111da177e4SLinus Torvalds if (!val) val = "1";
3121da177e4SLinus Torvalds
3131da177e4SLinus Torvalds /* One of =[yYnN01] */
314def7b92eSChristophe JAILLET return kstrtobool(val, kp->arg);
315fddd5201SRusty Russell }
316a14fe249SRusty Russell EXPORT_SYMBOL(param_set_bool);
317fddd5201SRusty Russell
param_get_bool(char * buffer,const struct kernel_param * kp)3189bbb9e5aSRusty Russell int param_get_bool(char *buffer, const struct kernel_param *kp)
3191da177e4SLinus Torvalds {
3201da177e4SLinus Torvalds /* Y and N chosen as being relatively non-coder friendly */
32196802e6bSJean Delvare return sprintf(buffer, "%c\n", *(bool *)kp->arg ? 'Y' : 'N');
3221da177e4SLinus Torvalds }
323a14fe249SRusty Russell EXPORT_SYMBOL(param_get_bool);
3241da177e4SLinus Torvalds
3259c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_bool = {
3266a4c2643SJani Nikula .flags = KERNEL_PARAM_OPS_FL_NOARG,
3279bbb9e5aSRusty Russell .set = param_set_bool,
3289bbb9e5aSRusty Russell .get = param_get_bool,
3299bbb9e5aSRusty Russell };
3309bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_bool);
3319bbb9e5aSRusty Russell
param_set_bool_enable_only(const char * val,const struct kernel_param * kp)332d19f05d8SLuis R. Rodriguez int param_set_bool_enable_only(const char *val, const struct kernel_param *kp)
333d19f05d8SLuis R. Rodriguez {
334*9ce170ceSLi zeming int err;
335d19f05d8SLuis R. Rodriguez bool new_value;
336d19f05d8SLuis R. Rodriguez bool orig_value = *(bool *)kp->arg;
337d19f05d8SLuis R. Rodriguez struct kernel_param dummy_kp = *kp;
338d19f05d8SLuis R. Rodriguez
339d19f05d8SLuis R. Rodriguez dummy_kp.arg = &new_value;
340d19f05d8SLuis R. Rodriguez
341d19f05d8SLuis R. Rodriguez err = param_set_bool(val, &dummy_kp);
342d19f05d8SLuis R. Rodriguez if (err)
343d19f05d8SLuis R. Rodriguez return err;
344d19f05d8SLuis R. Rodriguez
345d19f05d8SLuis R. Rodriguez /* Don't let them unset it once it's set! */
346d19f05d8SLuis R. Rodriguez if (!new_value && orig_value)
347d19f05d8SLuis R. Rodriguez return -EROFS;
348d19f05d8SLuis R. Rodriguez
349d19f05d8SLuis R. Rodriguez if (new_value)
350d19f05d8SLuis R. Rodriguez err = param_set_bool(val, kp);
351d19f05d8SLuis R. Rodriguez
352d19f05d8SLuis R. Rodriguez return err;
353d19f05d8SLuis R. Rodriguez }
354d19f05d8SLuis R. Rodriguez EXPORT_SYMBOL_GPL(param_set_bool_enable_only);
355d19f05d8SLuis R. Rodriguez
356d19f05d8SLuis R. Rodriguez const struct kernel_param_ops param_ops_bool_enable_only = {
357d19f05d8SLuis R. Rodriguez .flags = KERNEL_PARAM_OPS_FL_NOARG,
358d19f05d8SLuis R. Rodriguez .set = param_set_bool_enable_only,
359d19f05d8SLuis R. Rodriguez .get = param_get_bool,
360d19f05d8SLuis R. Rodriguez };
361154be21cSLuis R. Rodriguez EXPORT_SYMBOL_GPL(param_ops_bool_enable_only);
362d19f05d8SLuis R. Rodriguez
363fddd5201SRusty Russell /* This one must be bool. */
param_set_invbool(const char * val,const struct kernel_param * kp)3649bbb9e5aSRusty Russell int param_set_invbool(const char *val, const struct kernel_param *kp)
3651da177e4SLinus Torvalds {
366fddd5201SRusty Russell int ret;
367fddd5201SRusty Russell bool boolval;
36822e48eafSJan Beulich struct kernel_param dummy;
3691da177e4SLinus Torvalds
37022e48eafSJan Beulich dummy.arg = &boolval;
3711da177e4SLinus Torvalds ret = param_set_bool(val, &dummy);
3721da177e4SLinus Torvalds if (ret == 0)
3739a71af2cSRusty Russell *(bool *)kp->arg = !boolval;
3741da177e4SLinus Torvalds return ret;
3751da177e4SLinus Torvalds }
376a14fe249SRusty Russell EXPORT_SYMBOL(param_set_invbool);
3771da177e4SLinus Torvalds
param_get_invbool(char * buffer,const struct kernel_param * kp)3789bbb9e5aSRusty Russell int param_get_invbool(char *buffer, const struct kernel_param *kp)
3791da177e4SLinus Torvalds {
38096802e6bSJean Delvare return sprintf(buffer, "%c\n", (*(bool *)kp->arg) ? 'N' : 'Y');
3811da177e4SLinus Torvalds }
382a14fe249SRusty Russell EXPORT_SYMBOL(param_get_invbool);
3831da177e4SLinus Torvalds
3849c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_invbool = {
3859bbb9e5aSRusty Russell .set = param_set_invbool,
3869bbb9e5aSRusty Russell .get = param_get_invbool,
3879bbb9e5aSRusty Russell };
3889bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_invbool);
3899bbb9e5aSRusty Russell
param_set_bint(const char * val,const struct kernel_param * kp)39069116f27SRusty Russell int param_set_bint(const char *val, const struct kernel_param *kp)
39169116f27SRusty Russell {
3925104b7d7SDan Streetman /* Match bool exactly, by re-using it. */
3935104b7d7SDan Streetman struct kernel_param boolkp = *kp;
39469116f27SRusty Russell bool v;
39569116f27SRusty Russell int ret;
39669116f27SRusty Russell
39769116f27SRusty Russell boolkp.arg = &v;
39869116f27SRusty Russell
39969116f27SRusty Russell ret = param_set_bool(val, &boolkp);
40069116f27SRusty Russell if (ret == 0)
40169116f27SRusty Russell *(int *)kp->arg = v;
40269116f27SRusty Russell return ret;
40369116f27SRusty Russell }
40469116f27SRusty Russell EXPORT_SYMBOL(param_set_bint);
40569116f27SRusty Russell
4069c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_bint = {
4076a4c2643SJani Nikula .flags = KERNEL_PARAM_OPS_FL_NOARG,
40869116f27SRusty Russell .set = param_set_bint,
40969116f27SRusty Russell .get = param_get_int,
41069116f27SRusty Russell };
41169116f27SRusty Russell EXPORT_SYMBOL(param_ops_bint);
41269116f27SRusty Russell
4139730b5b0SBert Wesarg /* We break the rule and mangle the string. */
param_array(struct module * mod,const char * name,const char * val,unsigned int min,unsigned int max,void * elem,int elemsize,int (* set)(const char *,const struct kernel_param * kp),s16 level,unsigned int * num)414b51d23e4SDan Streetman static int param_array(struct module *mod,
415b51d23e4SDan Streetman const char *name,
4161da177e4SLinus Torvalds const char *val,
4171da177e4SLinus Torvalds unsigned int min, unsigned int max,
4181da177e4SLinus Torvalds void *elem, int elemsize,
4199bbb9e5aSRusty Russell int (*set)(const char *, const struct kernel_param *kp),
420026cee00SPawel Moll s16 level,
421eb38a996SRichard Knutsson unsigned int *num)
4221da177e4SLinus Torvalds {
4231da177e4SLinus Torvalds int ret;
4241da177e4SLinus Torvalds struct kernel_param kp;
4251da177e4SLinus Torvalds char save;
4261da177e4SLinus Torvalds
4271da177e4SLinus Torvalds /* Get the name right for errors. */
4281da177e4SLinus Torvalds kp.name = name;
4291da177e4SLinus Torvalds kp.arg = elem;
430026cee00SPawel Moll kp.level = level;
4311da177e4SLinus Torvalds
4321da177e4SLinus Torvalds *num = 0;
4331da177e4SLinus Torvalds /* We expect a comma-separated list of values. */
4341da177e4SLinus Torvalds do {
4351da177e4SLinus Torvalds int len;
4361da177e4SLinus Torvalds
4371da177e4SLinus Torvalds if (*num == max) {
438b5f3abf9SJim Cromie pr_err("%s: can only take %i arguments\n", name, max);
4391da177e4SLinus Torvalds return -EINVAL;
4401da177e4SLinus Torvalds }
4411da177e4SLinus Torvalds len = strcspn(val, ",");
4421da177e4SLinus Torvalds
4431da177e4SLinus Torvalds /* nul-terminate and parse */
4441da177e4SLinus Torvalds save = val[len];
4451da177e4SLinus Torvalds ((char *)val)[len] = '\0';
446cf2fde7bSRusty Russell check_kparam_locked(mod);
4471da177e4SLinus Torvalds ret = set(val, &kp);
4481da177e4SLinus Torvalds
4491da177e4SLinus Torvalds if (ret != 0)
4501da177e4SLinus Torvalds return ret;
4511da177e4SLinus Torvalds kp.arg += elemsize;
4521da177e4SLinus Torvalds val += len+1;
4531da177e4SLinus Torvalds (*num)++;
4541da177e4SLinus Torvalds } while (save == ',');
4551da177e4SLinus Torvalds
4561da177e4SLinus Torvalds if (*num < min) {
457b5f3abf9SJim Cromie pr_err("%s: needs at least %i arguments\n", name, min);
4581da177e4SLinus Torvalds return -EINVAL;
4591da177e4SLinus Torvalds }
4601da177e4SLinus Torvalds return 0;
4611da177e4SLinus Torvalds }
4621da177e4SLinus Torvalds
param_array_set(const char * val,const struct kernel_param * kp)4639bbb9e5aSRusty Russell static int param_array_set(const char *val, const struct kernel_param *kp)
4641da177e4SLinus Torvalds {
46522e48eafSJan Beulich const struct kparam_array *arr = kp->arr;
46631143a12SBert Wesarg unsigned int temp_num;
4671da177e4SLinus Torvalds
468b51d23e4SDan Streetman return param_array(kp->mod, kp->name, val, 1, arr->max, arr->elem,
469026cee00SPawel Moll arr->elemsize, arr->ops->set, kp->level,
4703c7d76e3SRusty Russell arr->num ?: &temp_num);
4711da177e4SLinus Torvalds }
4721da177e4SLinus Torvalds
param_array_get(char * buffer,const struct kernel_param * kp)4739bbb9e5aSRusty Russell static int param_array_get(char *buffer, const struct kernel_param *kp)
4741da177e4SLinus Torvalds {
4751da177e4SLinus Torvalds int i, off, ret;
47622e48eafSJan Beulich const struct kparam_array *arr = kp->arr;
4775104b7d7SDan Streetman struct kernel_param p = *kp;
4781da177e4SLinus Torvalds
4791da177e4SLinus Torvalds for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) {
48096802e6bSJean Delvare /* Replace \n with comma */
4811da177e4SLinus Torvalds if (i)
48296802e6bSJean Delvare buffer[off - 1] = ',';
4831da177e4SLinus Torvalds p.arg = arr->elem + arr->elemsize * i;
484cf2fde7bSRusty Russell check_kparam_locked(p.mod);
4859bbb9e5aSRusty Russell ret = arr->ops->get(buffer + off, &p);
4861da177e4SLinus Torvalds if (ret < 0)
4871da177e4SLinus Torvalds return ret;
4881da177e4SLinus Torvalds off += ret;
4891da177e4SLinus Torvalds }
4901da177e4SLinus Torvalds buffer[off] = '\0';
4911da177e4SLinus Torvalds return off;
4921da177e4SLinus Torvalds }
4931da177e4SLinus Torvalds
param_array_free(void * arg)494e6df34a4SRusty Russell static void param_array_free(void *arg)
495e6df34a4SRusty Russell {
496e6df34a4SRusty Russell unsigned int i;
497e6df34a4SRusty Russell const struct kparam_array *arr = arg;
498e6df34a4SRusty Russell
499e6df34a4SRusty Russell if (arr->ops->free)
500e6df34a4SRusty Russell for (i = 0; i < (arr->num ? *arr->num : arr->max); i++)
501e6df34a4SRusty Russell arr->ops->free(arr->elem + arr->elemsize * i);
502e6df34a4SRusty Russell }
503e6df34a4SRusty Russell
5049c27847dSLuis R. Rodriguez const struct kernel_param_ops param_array_ops = {
5059bbb9e5aSRusty Russell .set = param_array_set,
5069bbb9e5aSRusty Russell .get = param_array_get,
507e6df34a4SRusty Russell .free = param_array_free,
5089bbb9e5aSRusty Russell };
5099bbb9e5aSRusty Russell EXPORT_SYMBOL(param_array_ops);
5109bbb9e5aSRusty Russell
param_set_copystring(const char * val,const struct kernel_param * kp)5119bbb9e5aSRusty Russell int param_set_copystring(const char *val, const struct kernel_param *kp)
5121da177e4SLinus Torvalds {
51322e48eafSJan Beulich const struct kparam_string *kps = kp->str;
5141da177e4SLinus Torvalds
5151da177e4SLinus Torvalds if (strlen(val)+1 > kps->maxlen) {
516b5f3abf9SJim Cromie pr_err("%s: string doesn't fit in %u chars.\n",
5171da177e4SLinus Torvalds kp->name, kps->maxlen-1);
5181da177e4SLinus Torvalds return -ENOSPC;
5191da177e4SLinus Torvalds }
5201da177e4SLinus Torvalds strcpy(kps->string, val);
5211da177e4SLinus Torvalds return 0;
5221da177e4SLinus Torvalds }
523a14fe249SRusty Russell EXPORT_SYMBOL(param_set_copystring);
5241da177e4SLinus Torvalds
param_get_string(char * buffer,const struct kernel_param * kp)5259bbb9e5aSRusty Russell int param_get_string(char *buffer, const struct kernel_param *kp)
5261da177e4SLinus Torvalds {
52722e48eafSJan Beulich const struct kparam_string *kps = kp->str;
52896802e6bSJean Delvare return scnprintf(buffer, PAGE_SIZE, "%s\n", kps->string);
5291da177e4SLinus Torvalds }
530a14fe249SRusty Russell EXPORT_SYMBOL(param_get_string);
5311da177e4SLinus Torvalds
5329c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_string = {
5339bbb9e5aSRusty Russell .set = param_set_copystring,
5349bbb9e5aSRusty Russell .get = param_get_string,
5359bbb9e5aSRusty Russell };
5369bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_string);
5379bbb9e5aSRusty Russell
5381da177e4SLinus Torvalds /* sysfs output in /sys/modules/XYZ/parameters/ */
539350f8258SEdward Z. Yang #define to_module_attr(n) container_of(n, struct module_attribute, attr)
540350f8258SEdward Z. Yang #define to_module_kobject(n) container_of(n, struct module_kobject, kobj)
5411da177e4SLinus Torvalds
5421da177e4SLinus Torvalds struct param_attribute
5431da177e4SLinus Torvalds {
5441da177e4SLinus Torvalds struct module_attribute mattr;
5459bbb9e5aSRusty Russell const struct kernel_param *param;
5461da177e4SLinus Torvalds };
5471da177e4SLinus Torvalds
5481da177e4SLinus Torvalds struct module_param_attrs
5491da177e4SLinus Torvalds {
5509b473de8SRusty Russell unsigned int num;
5511da177e4SLinus Torvalds struct attribute_group grp;
552fa29c9c1SGustavo A. R. Silva struct param_attribute attrs[];
5531da177e4SLinus Torvalds };
5541da177e4SLinus Torvalds
555ef665c1aSRandy Dunlap #ifdef CONFIG_SYSFS
556350f8258SEdward Z. Yang #define to_param_attr(n) container_of(n, struct param_attribute, mattr)
5571da177e4SLinus Torvalds
param_attr_show(struct module_attribute * mattr,struct module_kobject * mk,char * buf)5581da177e4SLinus Torvalds static ssize_t param_attr_show(struct module_attribute *mattr,
5594befb026SKay Sievers struct module_kobject *mk, char *buf)
5601da177e4SLinus Torvalds {
5611da177e4SLinus Torvalds int count;
5621da177e4SLinus Torvalds struct param_attribute *attribute = to_param_attr(mattr);
5631da177e4SLinus Torvalds
5649bbb9e5aSRusty Russell if (!attribute->param->ops->get)
5651da177e4SLinus Torvalds return -EPERM;
5661da177e4SLinus Torvalds
567b51d23e4SDan Streetman kernel_param_lock(mk->mod);
5689bbb9e5aSRusty Russell count = attribute->param->ops->get(buf, attribute->param);
569b51d23e4SDan Streetman kernel_param_unlock(mk->mod);
5701da177e4SLinus Torvalds return count;
5711da177e4SLinus Torvalds }
5721da177e4SLinus Torvalds
5731da177e4SLinus Torvalds /* sysfs always hands a nul-terminated string in buf. We rely on that. */
param_attr_store(struct module_attribute * mattr,struct module_kobject * mk,const char * buf,size_t len)5741da177e4SLinus Torvalds static ssize_t param_attr_store(struct module_attribute *mattr,
575b51d23e4SDan Streetman struct module_kobject *mk,
5761da177e4SLinus Torvalds const char *buf, size_t len)
5771da177e4SLinus Torvalds {
5781da177e4SLinus Torvalds int err;
5791da177e4SLinus Torvalds struct param_attribute *attribute = to_param_attr(mattr);
5801da177e4SLinus Torvalds
5819bbb9e5aSRusty Russell if (!attribute->param->ops->set)
5821da177e4SLinus Torvalds return -EPERM;
5831da177e4SLinus Torvalds
584b51d23e4SDan Streetman kernel_param_lock(mk->mod);
58520657f66SDavid Howells if (param_check_unsafe(attribute->param))
5869bbb9e5aSRusty Russell err = attribute->param->ops->set(buf, attribute->param);
58720657f66SDavid Howells else
58820657f66SDavid Howells err = -EPERM;
589b51d23e4SDan Streetman kernel_param_unlock(mk->mod);
5901da177e4SLinus Torvalds if (!err)
5911da177e4SLinus Torvalds return len;
5921da177e4SLinus Torvalds return err;
5931da177e4SLinus Torvalds }
594ef665c1aSRandy Dunlap #endif
5951da177e4SLinus Torvalds
5961da177e4SLinus Torvalds #ifdef CONFIG_MODULES
5971da177e4SLinus Torvalds #define __modinit
5981da177e4SLinus Torvalds #else
5991da177e4SLinus Torvalds #define __modinit __init
6001da177e4SLinus Torvalds #endif
6011da177e4SLinus Torvalds
602ef665c1aSRandy Dunlap #ifdef CONFIG_SYSFS
kernel_param_lock(struct module * mod)603b51d23e4SDan Streetman void kernel_param_lock(struct module *mod)
604907b29ebSRusty Russell {
605b51d23e4SDan Streetman mutex_lock(KPARAM_MUTEX(mod));
606907b29ebSRusty Russell }
607907b29ebSRusty Russell
kernel_param_unlock(struct module * mod)608b51d23e4SDan Streetman void kernel_param_unlock(struct module *mod)
609907b29ebSRusty Russell {
610b51d23e4SDan Streetman mutex_unlock(KPARAM_MUTEX(mod));
611907b29ebSRusty Russell }
612b51d23e4SDan Streetman
613b51d23e4SDan Streetman EXPORT_SYMBOL(kernel_param_lock);
614b51d23e4SDan Streetman EXPORT_SYMBOL(kernel_param_unlock);
615907b29ebSRusty Russell
6161da177e4SLinus Torvalds /*
6179b473de8SRusty Russell * add_sysfs_param - add a parameter to sysfs
6189b473de8SRusty Russell * @mk: struct module_kobject
619630cc2b3SJean Delvare * @kp: the actual parameter definition to add to sysfs
6209b473de8SRusty Russell * @name: name of parameter
6211da177e4SLinus Torvalds *
6229b473de8SRusty Russell * Create a kobject if for a (per-module) parameter if mp NULL, and
6239b473de8SRusty Russell * create file in sysfs. Returns an error on out of memory. Always cleans up
6249b473de8SRusty Russell * if there's an error.
6251da177e4SLinus Torvalds */
add_sysfs_param(struct module_kobject * mk,const struct kernel_param * kp,const char * name)6269b473de8SRusty Russell static __modinit int add_sysfs_param(struct module_kobject *mk,
6279bbb9e5aSRusty Russell const struct kernel_param *kp,
6289b473de8SRusty Russell const char *name)
6291da177e4SLinus Torvalds {
63018eb74faSRusty Russell struct module_param_attrs *new_mp;
63118eb74faSRusty Russell struct attribute **new_attrs;
63218eb74faSRusty Russell unsigned int i;
6331da177e4SLinus Torvalds
6349b473de8SRusty Russell /* We don't bother calling this with invisible parameters. */
6359b473de8SRusty Russell BUG_ON(!kp->perm);
6369b473de8SRusty Russell
6379b473de8SRusty Russell if (!mk->mp) {
63818eb74faSRusty Russell /* First allocation. */
63918eb74faSRusty Russell mk->mp = kzalloc(sizeof(*mk->mp), GFP_KERNEL);
64018eb74faSRusty Russell if (!mk->mp)
64118eb74faSRusty Russell return -ENOMEM;
64218eb74faSRusty Russell mk->mp->grp.name = "parameters";
64318eb74faSRusty Russell /* NULL-terminated attribute array. */
64418eb74faSRusty Russell mk->mp->grp.attrs = kzalloc(sizeof(mk->mp->grp.attrs[0]),
6459b473de8SRusty Russell GFP_KERNEL);
64618eb74faSRusty Russell /* Caller will cleanup via free_module_param_attrs */
64718eb74faSRusty Russell if (!mk->mp->grp.attrs)
64818eb74faSRusty Russell return -ENOMEM;
6491da177e4SLinus Torvalds }
6501da177e4SLinus Torvalds
65118eb74faSRusty Russell /* Enlarge allocations. */
65218eb74faSRusty Russell new_mp = krealloc(mk->mp,
65318eb74faSRusty Russell sizeof(*mk->mp) +
65418eb74faSRusty Russell sizeof(mk->mp->attrs[0]) * (mk->mp->num + 1),
65518eb74faSRusty Russell GFP_KERNEL);
65618eb74faSRusty Russell if (!new_mp)
65718eb74faSRusty Russell return -ENOMEM;
65818eb74faSRusty Russell mk->mp = new_mp;
65918eb74faSRusty Russell
66018eb74faSRusty Russell /* Extra pointer for NULL terminator */
66118eb74faSRusty Russell new_attrs = krealloc(mk->mp->grp.attrs,
66218eb74faSRusty Russell sizeof(mk->mp->grp.attrs[0]) * (mk->mp->num + 2),
66318eb74faSRusty Russell GFP_KERNEL);
66418eb74faSRusty Russell if (!new_attrs)
66518eb74faSRusty Russell return -ENOMEM;
66618eb74faSRusty Russell mk->mp->grp.attrs = new_attrs;
6679b473de8SRusty Russell
6689b473de8SRusty Russell /* Tack new one on the end. */
669c772be52SRusty Russell memset(&mk->mp->attrs[mk->mp->num], 0, sizeof(mk->mp->attrs[0]));
67018eb74faSRusty Russell sysfs_attr_init(&mk->mp->attrs[mk->mp->num].mattr.attr);
67118eb74faSRusty Russell mk->mp->attrs[mk->mp->num].param = kp;
67218eb74faSRusty Russell mk->mp->attrs[mk->mp->num].mattr.show = param_attr_show;
673b0a65b0cSKees Cook /* Do not allow runtime DAC changes to make param writable. */
674b0a65b0cSKees Cook if ((kp->perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
67518eb74faSRusty Russell mk->mp->attrs[mk->mp->num].mattr.store = param_attr_store;
676574732c7SRusty Russell else
677574732c7SRusty Russell mk->mp->attrs[mk->mp->num].mattr.store = NULL;
67818eb74faSRusty Russell mk->mp->attrs[mk->mp->num].mattr.attr.name = (char *)name;
67918eb74faSRusty Russell mk->mp->attrs[mk->mp->num].mattr.attr.mode = kp->perm;
68018eb74faSRusty Russell mk->mp->num++;
6819b473de8SRusty Russell
6829b473de8SRusty Russell /* Fix up all the pointers, since krealloc can move us */
68318eb74faSRusty Russell for (i = 0; i < mk->mp->num; i++)
68418eb74faSRusty Russell mk->mp->grp.attrs[i] = &mk->mp->attrs[i].mattr.attr;
68518eb74faSRusty Russell mk->mp->grp.attrs[mk->mp->num] = NULL;
6869b473de8SRusty Russell return 0;
6871da177e4SLinus Torvalds }
6889b473de8SRusty Russell
689d2441183SLinus Torvalds #ifdef CONFIG_MODULES
free_module_param_attrs(struct module_kobject * mk)6909b473de8SRusty Russell static void free_module_param_attrs(struct module_kobject *mk)
6919b473de8SRusty Russell {
69218eb74faSRusty Russell if (mk->mp)
6939b473de8SRusty Russell kfree(mk->mp->grp.attrs);
6949b473de8SRusty Russell kfree(mk->mp);
6959b473de8SRusty Russell mk->mp = NULL;
6961da177e4SLinus Torvalds }
6971da177e4SLinus Torvalds
6981da177e4SLinus Torvalds /*
6991da177e4SLinus Torvalds * module_param_sysfs_setup - setup sysfs support for one module
7001da177e4SLinus Torvalds * @mod: module
7011da177e4SLinus Torvalds * @kparam: module parameters (array)
7021da177e4SLinus Torvalds * @num_params: number of module parameters
7031da177e4SLinus Torvalds *
7049b473de8SRusty Russell * Adds sysfs entries for module parameters under
7059b473de8SRusty Russell * /sys/module/[mod->name]/parameters/
7061da177e4SLinus Torvalds */
module_param_sysfs_setup(struct module * mod,const struct kernel_param * kparam,unsigned int num_params)7071da177e4SLinus Torvalds int module_param_sysfs_setup(struct module *mod,
7089bbb9e5aSRusty Russell const struct kernel_param *kparam,
7091da177e4SLinus Torvalds unsigned int num_params)
7101da177e4SLinus Torvalds {
7119b473de8SRusty Russell int i, err;
7129b473de8SRusty Russell bool params = false;
7131da177e4SLinus Torvalds
7149b473de8SRusty Russell for (i = 0; i < num_params; i++) {
7159b473de8SRusty Russell if (kparam[i].perm == 0)
7169b473de8SRusty Russell continue;
7179b473de8SRusty Russell err = add_sysfs_param(&mod->mkobj, &kparam[i], kparam[i].name);
71818eb74faSRusty Russell if (err) {
71918eb74faSRusty Russell free_module_param_attrs(&mod->mkobj);
7209b473de8SRusty Russell return err;
72118eb74faSRusty Russell }
7229b473de8SRusty Russell params = true;
7239b473de8SRusty Russell }
7241da177e4SLinus Torvalds
7259b473de8SRusty Russell if (!params)
7261da177e4SLinus Torvalds return 0;
7279b473de8SRusty Russell
7289b473de8SRusty Russell /* Create the param group. */
7299b473de8SRusty Russell err = sysfs_create_group(&mod->mkobj.kobj, &mod->mkobj.mp->grp);
7309b473de8SRusty Russell if (err)
7319b473de8SRusty Russell free_module_param_attrs(&mod->mkobj);
7329b473de8SRusty Russell return err;
7331da177e4SLinus Torvalds }
7341da177e4SLinus Torvalds
7351da177e4SLinus Torvalds /*
7361da177e4SLinus Torvalds * module_param_sysfs_remove - remove sysfs support for one module
7371da177e4SLinus Torvalds * @mod: module
7381da177e4SLinus Torvalds *
7391da177e4SLinus Torvalds * Remove sysfs entries for module parameters and the corresponding
7401da177e4SLinus Torvalds * kobject.
7411da177e4SLinus Torvalds */
module_param_sysfs_remove(struct module * mod)7421da177e4SLinus Torvalds void module_param_sysfs_remove(struct module *mod)
7431da177e4SLinus Torvalds {
7449b473de8SRusty Russell if (mod->mkobj.mp) {
7459b473de8SRusty Russell sysfs_remove_group(&mod->mkobj.kobj, &mod->mkobj.mp->grp);
7461da177e4SLinus Torvalds /* We are positive that no one is using any param
7471da177e4SLinus Torvalds * attrs at this point. Deallocate immediately. */
7489b473de8SRusty Russell free_module_param_attrs(&mod->mkobj);
7491da177e4SLinus Torvalds }
7501da177e4SLinus Torvalds }
7511da177e4SLinus Torvalds #endif
7521da177e4SLinus Torvalds
destroy_params(const struct kernel_param * params,unsigned num)753e180a6b7SRusty Russell void destroy_params(const struct kernel_param *params, unsigned num)
754e180a6b7SRusty Russell {
755e6df34a4SRusty Russell unsigned int i;
756e6df34a4SRusty Russell
757e6df34a4SRusty Russell for (i = 0; i < num; i++)
758e6df34a4SRusty Russell if (params[i].ops->free)
759e6df34a4SRusty Russell params[i].ops->free(params[i].arg);
760e180a6b7SRusty Russell }
761e180a6b7SRusty Russell
locate_module_kobject(const char * name)762e94965edSDmitry Torokhov static struct module_kobject * __init locate_module_kobject(const char *name)
7631da177e4SLinus Torvalds {
7641da177e4SLinus Torvalds struct module_kobject *mk;
7659b473de8SRusty Russell struct kobject *kobj;
7669b473de8SRusty Russell int err;
7671da177e4SLinus Torvalds
7689b473de8SRusty Russell kobj = kset_find_obj(module_kset, name);
7699b473de8SRusty Russell if (kobj) {
7709b473de8SRusty Russell mk = to_module_kobject(kobj);
7719b473de8SRusty Russell } else {
772dd392710SPekka J Enberg mk = kzalloc(sizeof(struct module_kobject), GFP_KERNEL);
773dd392710SPekka J Enberg BUG_ON(!mk);
7741da177e4SLinus Torvalds
7751da177e4SLinus Torvalds mk->mod = THIS_MODULE;
7767405c1e1SGreg Kroah-Hartman mk->kobj.kset = module_kset;
7779b473de8SRusty Russell err = kobject_init_and_add(&mk->kobj, &module_ktype, NULL,
7789b473de8SRusty Russell "%s", name);
77988bfa324SKay Sievers #ifdef CONFIG_MODULES
78088bfa324SKay Sievers if (!err)
78188bfa324SKay Sievers err = sysfs_create_file(&mk->kobj, &module_uevent.attr);
78288bfa324SKay Sievers #endif
7839b473de8SRusty Russell if (err) {
784e43b9192SGreg Kroah-Hartman kobject_put(&mk->kobj);
785b5f3abf9SJim Cromie pr_crit("Adding module '%s' to sysfs failed (%d), the system may be unstable.\n",
786e94965edSDmitry Torokhov name, err);
787e94965edSDmitry Torokhov return NULL;
78874c5b597SGreg Kroah-Hartman }
789e94965edSDmitry Torokhov
790e94965edSDmitry Torokhov /* So that we hold reference in both cases. */
7919b473de8SRusty Russell kobject_get(&mk->kobj);
7929b473de8SRusty Russell }
7939b473de8SRusty Russell
794e94965edSDmitry Torokhov return mk;
795e94965edSDmitry Torokhov }
796e94965edSDmitry Torokhov
kernel_add_sysfs_param(const char * name,const struct kernel_param * kparam,unsigned int name_skip)797e94965edSDmitry Torokhov static void __init kernel_add_sysfs_param(const char *name,
79863a12d9dSGeert Uytterhoeven const struct kernel_param *kparam,
799e94965edSDmitry Torokhov unsigned int name_skip)
800e94965edSDmitry Torokhov {
801e94965edSDmitry Torokhov struct module_kobject *mk;
802e94965edSDmitry Torokhov int err;
803e94965edSDmitry Torokhov
804e94965edSDmitry Torokhov mk = locate_module_kobject(name);
805e94965edSDmitry Torokhov if (!mk)
806e94965edSDmitry Torokhov return;
807e94965edSDmitry Torokhov
808e94965edSDmitry Torokhov /* We need to remove old parameters before adding more. */
809e94965edSDmitry Torokhov if (mk->mp)
810e94965edSDmitry Torokhov sysfs_remove_group(&mk->kobj, &mk->mp->grp);
811e94965edSDmitry Torokhov
8129b473de8SRusty Russell /* These should not fail at boot. */
8139b473de8SRusty Russell err = add_sysfs_param(mk, kparam, kparam->name + name_skip);
8149b473de8SRusty Russell BUG_ON(err);
8159b473de8SRusty Russell err = sysfs_create_group(&mk->kobj, &mk->mp->grp);
8169b473de8SRusty Russell BUG_ON(err);
817f30c53a8SKay Sievers kobject_uevent(&mk->kobj, KOBJ_ADD);
8189b473de8SRusty Russell kobject_put(&mk->kobj);
8191da177e4SLinus Torvalds }
8201da177e4SLinus Torvalds
8211da177e4SLinus Torvalds /*
822b634d130SJean Delvare * param_sysfs_builtin - add sysfs parameters for built-in modules
8231da177e4SLinus Torvalds *
8241da177e4SLinus Torvalds * Add module_parameters to sysfs for "modules" built into the kernel.
8251da177e4SLinus Torvalds *
8261da177e4SLinus Torvalds * The "module" name (KBUILD_MODNAME) is stored before a dot, the
8271da177e4SLinus Torvalds * "parameter" name is stored behind a dot in kernel_param->name. So,
8281da177e4SLinus Torvalds * extract the "module" name for all built-in kernel_param-eters,
8299b473de8SRusty Russell * and for all who have the same, call kernel_add_sysfs_param.
8301da177e4SLinus Torvalds */
param_sysfs_builtin(void)8311da177e4SLinus Torvalds static void __init param_sysfs_builtin(void)
8321da177e4SLinus Torvalds {
83363a12d9dSGeert Uytterhoeven const struct kernel_param *kp;
8349b473de8SRusty Russell unsigned int name_len;
8359b473de8SRusty Russell char modname[MODULE_NAME_LEN];
8361da177e4SLinus Torvalds
8379b473de8SRusty Russell for (kp = __start___param; kp < __stop___param; kp++) {
8381da177e4SLinus Torvalds char *dot;
8391da177e4SLinus Torvalds
8409b473de8SRusty Russell if (kp->perm == 0)
8419b473de8SRusty Russell continue;
8421da177e4SLinus Torvalds
843730b69d2SRusty Russell dot = strchr(kp->name, '.');
8441da177e4SLinus Torvalds if (!dot) {
84567e67ceaSRusty Russell /* This happens for core_param() */
84667e67ceaSRusty Russell strcpy(modname, "kernel");
84767e67ceaSRusty Russell name_len = 0;
84867e67ceaSRusty Russell } else {
84967e67ceaSRusty Russell name_len = dot - kp->name + 1;
85033457938SAzeem Shaikh strscpy(modname, kp->name, name_len);
8511da177e4SLinus Torvalds }
85267e67ceaSRusty Russell kernel_add_sysfs_param(modname, kp, name_len);
8531da177e4SLinus Torvalds }
8541da177e4SLinus Torvalds }
8551da177e4SLinus Torvalds
__modver_version_show(struct module_attribute * mattr,struct module_kobject * mk,char * buf)856e94965edSDmitry Torokhov ssize_t __modver_version_show(struct module_attribute *mattr,
8574befb026SKay Sievers struct module_kobject *mk, char *buf)
858e94965edSDmitry Torokhov {
859e94965edSDmitry Torokhov struct module_version_attribute *vattr =
860e94965edSDmitry Torokhov container_of(mattr, struct module_version_attribute, mattr);
861e94965edSDmitry Torokhov
862f4940ab7SChen Gang return scnprintf(buf, PAGE_SIZE, "%s\n", vattr->version);
863e94965edSDmitry Torokhov }
864e94965edSDmitry Torokhov
865b112082cSJohan Hovold extern const struct module_version_attribute __start___modver[];
866b112082cSJohan Hovold extern const struct module_version_attribute __stop___modver[];
867e94965edSDmitry Torokhov
version_sysfs_builtin(void)868e94965edSDmitry Torokhov static void __init version_sysfs_builtin(void)
869e94965edSDmitry Torokhov {
870b112082cSJohan Hovold const struct module_version_attribute *vattr;
871e94965edSDmitry Torokhov struct module_kobject *mk;
872e94965edSDmitry Torokhov int err;
873e94965edSDmitry Torokhov
874b112082cSJohan Hovold for (vattr = __start___modver; vattr < __stop___modver; vattr++) {
875e94965edSDmitry Torokhov mk = locate_module_kobject(vattr->module_name);
876e94965edSDmitry Torokhov if (mk) {
877e94965edSDmitry Torokhov err = sysfs_create_file(&mk->kobj, &vattr->mattr.attr);
87874c3dea3SRusty Russell WARN_ON_ONCE(err);
879e94965edSDmitry Torokhov kobject_uevent(&mk->kobj, KOBJ_ADD);
880e94965edSDmitry Torokhov kobject_put(&mk->kobj);
881e94965edSDmitry Torokhov }
882e94965edSDmitry Torokhov }
883e94965edSDmitry Torokhov }
8841da177e4SLinus Torvalds
8851da177e4SLinus Torvalds /* module-related sysfs stuff */
8861da177e4SLinus Torvalds
module_attr_show(struct kobject * kobj,struct attribute * attr,char * buf)8871da177e4SLinus Torvalds static ssize_t module_attr_show(struct kobject *kobj,
8881da177e4SLinus Torvalds struct attribute *attr,
8891da177e4SLinus Torvalds char *buf)
8901da177e4SLinus Torvalds {
8911da177e4SLinus Torvalds struct module_attribute *attribute;
8921da177e4SLinus Torvalds struct module_kobject *mk;
8931da177e4SLinus Torvalds int ret;
8941da177e4SLinus Torvalds
8951da177e4SLinus Torvalds attribute = to_module_attr(attr);
8961da177e4SLinus Torvalds mk = to_module_kobject(kobj);
8971da177e4SLinus Torvalds
8981da177e4SLinus Torvalds if (!attribute->show)
89970f2817aSDmitry Torokhov return -EIO;
9001da177e4SLinus Torvalds
9014befb026SKay Sievers ret = attribute->show(attribute, mk, buf);
9021da177e4SLinus Torvalds
9031da177e4SLinus Torvalds return ret;
9041da177e4SLinus Torvalds }
9051da177e4SLinus Torvalds
module_attr_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t len)9061da177e4SLinus Torvalds static ssize_t module_attr_store(struct kobject *kobj,
9071da177e4SLinus Torvalds struct attribute *attr,
9081da177e4SLinus Torvalds const char *buf, size_t len)
9091da177e4SLinus Torvalds {
9101da177e4SLinus Torvalds struct module_attribute *attribute;
9111da177e4SLinus Torvalds struct module_kobject *mk;
9121da177e4SLinus Torvalds int ret;
9131da177e4SLinus Torvalds
9141da177e4SLinus Torvalds attribute = to_module_attr(attr);
9151da177e4SLinus Torvalds mk = to_module_kobject(kobj);
9161da177e4SLinus Torvalds
9171da177e4SLinus Torvalds if (!attribute->store)
91870f2817aSDmitry Torokhov return -EIO;
9191da177e4SLinus Torvalds
9204befb026SKay Sievers ret = attribute->store(attribute, mk, buf, len);
9211da177e4SLinus Torvalds
9221da177e4SLinus Torvalds return ret;
9231da177e4SLinus Torvalds }
9241da177e4SLinus Torvalds
92552cf25d0SEmese Revfy static const struct sysfs_ops module_sysfs_ops = {
9261da177e4SLinus Torvalds .show = module_attr_show,
9271da177e4SLinus Torvalds .store = module_attr_store,
9281da177e4SLinus Torvalds };
9291da177e4SLinus Torvalds
uevent_filter(const struct kobject * kobj)930c45a88bbSGreg Kroah-Hartman static int uevent_filter(const struct kobject *kobj)
931270a6c4cSKay Sievers {
932ee6d3dd4SWedson Almeida Filho const struct kobj_type *ktype = get_ktype(kobj);
933270a6c4cSKay Sievers
934270a6c4cSKay Sievers if (ktype == &module_ktype)
935270a6c4cSKay Sievers return 1;
936270a6c4cSKay Sievers return 0;
937270a6c4cSKay Sievers }
938270a6c4cSKay Sievers
9399cd43611SEmese Revfy static const struct kset_uevent_ops module_uevent_ops = {
940270a6c4cSKay Sievers .filter = uevent_filter,
941270a6c4cSKay Sievers };
942270a6c4cSKay Sievers
9437405c1e1SGreg Kroah-Hartman struct kset *module_kset;
9441da177e4SLinus Torvalds
module_kobj_release(struct kobject * kobj)945942e4431SLi Zhong static void module_kobj_release(struct kobject *kobj)
946942e4431SLi Zhong {
947942e4431SLi Zhong struct module_kobject *mk = to_module_kobject(kobj);
948942e4431SLi Zhong complete(mk->kobj_completion);
949942e4431SLi Zhong }
950942e4431SLi Zhong
951042edf1eSThomas Weißschuh const struct kobj_type module_ktype = {
952942e4431SLi Zhong .release = module_kobj_release,
9531da177e4SLinus Torvalds .sysfs_ops = &module_sysfs_ops,
9541da177e4SLinus Torvalds };
9551da177e4SLinus Torvalds
9561da177e4SLinus Torvalds /*
95796a1a241SRasmus Villemoes * param_sysfs_init - create "module" kset
95896a1a241SRasmus Villemoes *
95996a1a241SRasmus Villemoes * This must be done before the initramfs is unpacked and
96096a1a241SRasmus Villemoes * request_module() thus becomes possible, because otherwise the
96196a1a241SRasmus Villemoes * module load would fail in mod_sysfs_init.
9621da177e4SLinus Torvalds */
param_sysfs_init(void)9631da177e4SLinus Torvalds static int __init param_sysfs_init(void)
9641da177e4SLinus Torvalds {
9657405c1e1SGreg Kroah-Hartman module_kset = kset_create_and_add("module", &module_uevent_ops, NULL);
9667405c1e1SGreg Kroah-Hartman if (!module_kset) {
9677405c1e1SGreg Kroah-Hartman printk(KERN_WARNING "%s (%d): error creating kset\n",
9687405c1e1SGreg Kroah-Hartman __FILE__, __LINE__);
9697405c1e1SGreg Kroah-Hartman return -ENOMEM;
970d8c7649eSRandy Dunlap }
9711da177e4SLinus Torvalds
97296a1a241SRasmus Villemoes return 0;
97396a1a241SRasmus Villemoes }
97496a1a241SRasmus Villemoes subsys_initcall(param_sysfs_init);
97596a1a241SRasmus Villemoes
97696a1a241SRasmus Villemoes /*
97796a1a241SRasmus Villemoes * param_sysfs_builtin_init - add sysfs version and parameter
97896a1a241SRasmus Villemoes * attributes for built-in modules
97996a1a241SRasmus Villemoes */
param_sysfs_builtin_init(void)98096a1a241SRasmus Villemoes static int __init param_sysfs_builtin_init(void)
98196a1a241SRasmus Villemoes {
98296a1a241SRasmus Villemoes if (!module_kset)
98396a1a241SRasmus Villemoes return -ENOMEM;
98496a1a241SRasmus Villemoes
985e94965edSDmitry Torokhov version_sysfs_builtin();
9861da177e4SLinus Torvalds param_sysfs_builtin();
9871da177e4SLinus Torvalds
9881da177e4SLinus Torvalds return 0;
9891da177e4SLinus Torvalds }
99096a1a241SRasmus Villemoes late_initcall(param_sysfs_builtin_init);
9911da177e4SLinus Torvalds
9927405c1e1SGreg Kroah-Hartman #endif /* CONFIG_SYSFS */
993