xref: /linux/kernel/params.c (revision a52079dad4718fa924ae81a939f8a665366f562b)
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 : &param_lock)
3520bdc2cfSStephen Rothwell #else
3620bdc2cfSStephen Rothwell #define KPARAM_MUTEX(mod)	(&param_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(&params[i]);
148907b29ebSRusty Russell 			err = params[i].ops->set(val, &params[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, &param, &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