Blame view
kernel/params.c
22.8 KB
1a59d1b8e
|
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4c
|
2 3 |
/* Helpers for initial module or kernel cmdline parsing Copyright (C) 2001 Rusty Russell. |
1da177e4c
|
4 |
*/ |
1da177e4c
|
5 6 7 8 |
#include <linux/kernel.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/module.h> |
63a12d9d0
|
9 |
#include <linux/moduleparam.h> |
1da177e4c
|
10 11 |
#include <linux/device.h> #include <linux/err.h> |
4e57b6817
|
12 |
#include <linux/slab.h> |
26d052bfc
|
13 |
#include <linux/ctype.h> |
20657f66e
|
14 |
#include <linux/security.h> |
1da177e4c
|
15 |
|
cf2fde7b3
|
16 |
#ifdef CONFIG_SYSFS |
b51d23e4e
|
17 |
/* Protects all built-in parameters, modules use their own param_lock */ |
907b29eb4
|
18 |
static DEFINE_MUTEX(param_lock); |
b51d23e4e
|
19 |
/* Use the module's mutex, or if built-in use the built-in mutex */ |
20bdc2cfd
|
20 |
#ifdef CONFIG_MODULES |
b51d23e4e
|
21 |
#define KPARAM_MUTEX(mod) ((mod) ? &(mod)->param_lock : ¶m_lock) |
20bdc2cfd
|
22 23 24 |
#else #define KPARAM_MUTEX(mod) (¶m_lock) #endif |
cf2fde7b3
|
25 26 27 28 29 30 31 32 33 34 |
static inline void check_kparam_locked(struct module *mod) { BUG_ON(!mutex_is_locked(KPARAM_MUTEX(mod))); } #else static inline void check_kparam_locked(struct module *mod) { } #endif /* !CONFIG_SYSFS */ |
b51d23e4e
|
35 |
|
a1054322a
|
36 37 38 39 40 |
/* This just allows us to keep track of which parameters are kmalloced. */ struct kmalloced_param { struct list_head list; char val[]; }; |
a1054322a
|
41 |
static LIST_HEAD(kmalloced_params); |
b51d23e4e
|
42 |
static DEFINE_SPINLOCK(kmalloced_params_lock); |
a1054322a
|
43 44 45 46 47 48 49 50 |
static void *kmalloc_parameter(unsigned int size) { struct kmalloced_param *p; p = kmalloc(sizeof(*p) + size, GFP_KERNEL); if (!p) return NULL; |
b51d23e4e
|
51 |
spin_lock(&kmalloced_params_lock); |
a1054322a
|
52 |
list_add(&p->list, &kmalloced_params); |
b51d23e4e
|
53 |
spin_unlock(&kmalloced_params_lock); |
a1054322a
|
54 55 56 57 58 59 60 |
return p->val; } /* Does nothing if parameter wasn't kmalloced above. */ static void maybe_kfree_parameter(void *param) { struct kmalloced_param *p; |
b51d23e4e
|
61 |
spin_lock(&kmalloced_params_lock); |
a1054322a
|
62 63 64 65 66 67 68 |
list_for_each_entry(p, &kmalloced_params, list) { if (p->val == param) { list_del(&p->list); kfree(p); break; } } |
b51d23e4e
|
69 |
spin_unlock(&kmalloced_params_lock); |
a1054322a
|
70 |
} |
b1e4d20cb
|
71 |
static char dash2underscore(char c) |
1da177e4c
|
72 73 74 75 76 |
{ if (c == '-') return '_'; return c; } |
b1e4d20cb
|
77 |
bool parameqn(const char *a, const char *b, size_t n) |
1da177e4c
|
78 |
{ |
b1e4d20cb
|
79 80 81 82 83 84 85 86 87 88 89 90 |
size_t i; for (i = 0; i < n; i++) { if (dash2underscore(a[i]) != dash2underscore(b[i])) return false; } return true; } bool parameq(const char *a, const char *b) { return parameqn(a, b, strlen(a)+1); |
1da177e4c
|
91 |
} |
20657f66e
|
92 |
static bool param_check_unsafe(const struct kernel_param *kp) |
7a486d378
|
93 |
{ |
20657f66e
|
94 95 96 |
if (kp->flags & KERNEL_PARAM_FL_HWPARAM && security_locked_down(LOCKDOWN_MODULE_PARAMETERS)) return false; |
7a486d378
|
97 |
if (kp->flags & KERNEL_PARAM_FL_UNSAFE) { |
edc41b3c5
|
98 99 100 |
pr_notice("Setting dangerous option %s - tainting kernel ", kp->name); |
7a486d378
|
101 102 |
add_taint(TAINT_USER, LOCKDEP_STILL_OK); } |
20657f66e
|
103 104 |
return true; |
7a486d378
|
105 |
} |
1da177e4c
|
106 107 |
static int parse_one(char *param, char *val, |
9fb48c744
|
108 |
const char *doing, |
914dcaa84
|
109 |
const struct kernel_param *params, |
1da177e4c
|
110 |
unsigned num_params, |
026cee008
|
111 112 |
s16 min_level, s16 max_level, |
ecc861705
|
113 |
void *arg, |
9fb48c744
|
114 |
int (*handle_unknown)(char *param, char *val, |
ecc861705
|
115 |
const char *doing, void *arg)) |
1da177e4c
|
116 117 |
{ unsigned int i; |
907b29eb4
|
118 |
int err; |
1da177e4c
|
119 120 121 122 |
/* Find parameter */ for (i = 0; i < num_params; i++) { if (parameq(param, params[i].name)) { |
026cee008
|
123 124 125 |
if (params[i].level < min_level || params[i].level > max_level) return 0; |
25985edce
|
126 |
/* No one handled NULL, so do it here. */ |
ab013c5f6
|
127 |
if (!val && |
6a4c26431
|
128 |
!(params[i].ops->flags & KERNEL_PARAM_OPS_FL_NOARG)) |
2e9fb9953
|
129 |
return -EINVAL; |
9fb48c744
|
130 131 132 |
pr_debug("handling %s with %p ", param, params[i].ops->set); |
b51d23e4e
|
133 |
kernel_param_lock(params[i].mod); |
20657f66e
|
134 135 136 137 |
if (param_check_unsafe(¶ms[i])) err = params[i].ops->set(val, ¶ms[i]); else err = -EPERM; |
b51d23e4e
|
138 |
kernel_param_unlock(params[i].mod); |
907b29eb4
|
139 |
return err; |
1da177e4c
|
140 141 142 143 |
} } if (handle_unknown) { |
9fb48c744
|
144 145 |
pr_debug("doing %s: %s='%s' ", doing, param, val); |
ecc861705
|
146 |
return handle_unknown(param, val, doing, arg); |
1da177e4c
|
147 |
} |
9fb48c744
|
148 149 |
pr_debug("Unknown argument '%s' ", param); |
1da177e4c
|
150 151 |
return -ENOENT; } |
1da177e4c
|
152 |
/* Args looks like "foo=bar,bar2 baz=fuz wiz". */ |
51e158c12
|
153 154 155 156 157 158 |
char *parse_args(const char *doing, char *args, const struct kernel_param *params, unsigned num, s16 min_level, s16 max_level, |
ecc861705
|
159 160 161 |
void *arg, int (*unknown)(char *param, char *val, const char *doing, void *arg)) |
1da177e4c
|
162 |
{ |
74b22c465
|
163 |
char *param, *val, *err = NULL; |
1da177e4c
|
164 |
|
f36462f07
|
165 |
/* Chew leading spaces */ |
e7d2860b6
|
166 |
args = skip_spaces(args); |
f36462f07
|
167 |
|
1ef9eaf2b
|
168 |
if (*args) |
9fb48c744
|
169 170 |
pr_debug("doing %s, parsing ARGS: '%s' ", doing, args); |
1da177e4c
|
171 172 |
while (*args) { int ret; |
a416aba63
|
173 |
int irq_was_disabled; |
1da177e4c
|
174 175 |
args = next_arg(args, ¶m, &val); |
51e158c12
|
176 177 |
/* Stop at -- */ if (!val && strcmp(param, "--") == 0) |
74b22c465
|
178 |
return err ?: args; |
a416aba63
|
179 |
irq_was_disabled = irqs_disabled(); |
9fb48c744
|
180 |
ret = parse_one(param, val, doing, params, num, |
ecc861705
|
181 |
min_level, max_level, arg, unknown); |
b5f3abf95
|
182 183 184 185 |
if (irq_was_disabled && !irqs_disabled()) pr_warn("%s: option '%s' enabled irq's! ", doing, param); |
1da177e4c
|
186 |
switch (ret) { |
74b22c465
|
187 188 |
case 0: continue; |
1da177e4c
|
189 |
case -ENOENT: |
b5f3abf95
|
190 191 |
pr_err("%s: Unknown parameter `%s' ", doing, param); |
74b22c465
|
192 |
break; |
1da177e4c
|
193 |
case -ENOSPC: |
b5f3abf95
|
194 195 |
pr_err("%s: `%s' too large for parameter `%s' ", |
9fb48c744
|
196 |
doing, val ?: "", param); |
1da177e4c
|
197 198 |
break; default: |
b5f3abf95
|
199 200 |
pr_err("%s: `%s' invalid for parameter `%s' ", |
9fb48c744
|
201 |
doing, val ?: "", param); |
74b22c465
|
202 |
break; |
1da177e4c
|
203 |
} |
74b22c465
|
204 205 |
err = ERR_PTR(ret); |
1da177e4c
|
206 |
} |
74b22c465
|
207 |
return err; |
1da177e4c
|
208 209 210 |
} /* Lazy bastard, eh? */ |
88a88b320
|
211 |
#define STANDARD_PARAM_DEF(name, type, format, strtolfn) \ |
9bbb9e5a3
|
212 |
int param_set_##name(const char *val, const struct kernel_param *kp) \ |
1da177e4c
|
213 |
{ \ |
88a88b320
|
214 |
return strtolfn(val, 0, (type *)kp->arg); \ |
1da177e4c
|
215 |
} \ |
9bbb9e5a3
|
216 |
int param_get_##name(char *buffer, const struct kernel_param *kp) \ |
1da177e4c
|
217 |
{ \ |
96802e6b1
|
218 219 |
return scnprintf(buffer, PAGE_SIZE, format " ", \ |
f4940ab7c
|
220 |
*((type *)kp->arg)); \ |
a14fe249a
|
221 |
} \ |
9c27847dd
|
222 |
const struct kernel_param_ops param_ops_##name = { \ |
9bbb9e5a3
|
223 224 225 |
.set = param_set_##name, \ .get = param_get_##name, \ }; \ |
a14fe249a
|
226 |
EXPORT_SYMBOL(param_set_##name); \ |
9bbb9e5a3
|
227 228 |
EXPORT_SYMBOL(param_get_##name); \ EXPORT_SYMBOL(param_ops_##name) |
1da177e4c
|
229 |
|
7d8365771
|
230 231 232 233 234 235 236 237 238 |
STANDARD_PARAM_DEF(byte, unsigned char, "%hhu", kstrtou8); STANDARD_PARAM_DEF(short, short, "%hi", kstrtos16); STANDARD_PARAM_DEF(ushort, unsigned short, "%hu", kstrtou16); STANDARD_PARAM_DEF(int, int, "%i", kstrtoint); STANDARD_PARAM_DEF(uint, unsigned int, "%u", kstrtouint); STANDARD_PARAM_DEF(long, long, "%li", kstrtol); STANDARD_PARAM_DEF(ulong, unsigned long, "%lu", kstrtoul); STANDARD_PARAM_DEF(ullong, unsigned long long, "%llu", kstrtoull); STANDARD_PARAM_DEF(hexint, unsigned int, "%#08x", kstrtouint); |
1da177e4c
|
239 |
|
9bbb9e5a3
|
240 |
int param_set_charp(const char *val, const struct kernel_param *kp) |
1da177e4c
|
241 |
{ |
1da177e4c
|
242 |
if (strlen(val) > 1024) { |
b5f3abf95
|
243 244 |
pr_err("%s: string parameter too long ", kp->name); |
1da177e4c
|
245 246 |
return -ENOSPC; } |
a1054322a
|
247 248 249 |
maybe_kfree_parameter(*(char **)kp->arg); /* This is a hack. We can't kmalloc in early boot, and we |
e180a6b77
|
250 251 |
* don't need to; this mangled commandline is preserved. */ if (slab_is_available()) { |
a1054322a
|
252 |
*(char **)kp->arg = kmalloc_parameter(strlen(val)+1); |
d553ad864
|
253 |
if (!*(char **)kp->arg) |
e180a6b77
|
254 |
return -ENOMEM; |
a1054322a
|
255 |
strcpy(*(char **)kp->arg, val); |
e180a6b77
|
256 257 |
} else *(const char **)kp->arg = val; |
1da177e4c
|
258 259 |
return 0; } |
a14fe249a
|
260 |
EXPORT_SYMBOL(param_set_charp); |
1da177e4c
|
261 |
|
9bbb9e5a3
|
262 |
int param_get_charp(char *buffer, const struct kernel_param *kp) |
1da177e4c
|
263 |
{ |
96802e6b1
|
264 265 |
return scnprintf(buffer, PAGE_SIZE, "%s ", *((char **)kp->arg)); |
1da177e4c
|
266 |
} |
a14fe249a
|
267 |
EXPORT_SYMBOL(param_get_charp); |
1da177e4c
|
268 |
|
3d9c637f4
|
269 |
void param_free_charp(void *arg) |
a1054322a
|
270 271 272 |
{ maybe_kfree_parameter(*((char **)arg)); } |
3d9c637f4
|
273 |
EXPORT_SYMBOL(param_free_charp); |
a1054322a
|
274 |
|
9c27847dd
|
275 |
const struct kernel_param_ops param_ops_charp = { |
9bbb9e5a3
|
276 277 |
.set = param_set_charp, .get = param_get_charp, |
a1054322a
|
278 |
.free = param_free_charp, |
9bbb9e5a3
|
279 280 |
}; EXPORT_SYMBOL(param_ops_charp); |
fddd52012
|
281 |
/* Actually could be a bool or an int, for historical reasons. */ |
9bbb9e5a3
|
282 |
int param_set_bool(const char *val, const struct kernel_param *kp) |
1da177e4c
|
283 284 285 286 287 |
{ /* No equals means "set"... */ if (!val) val = "1"; /* One of =[yYnN01] */ |
8b8252813
|
288 |
return strtobool(val, kp->arg); |
1da177e4c
|
289 |
} |
a14fe249a
|
290 |
EXPORT_SYMBOL(param_set_bool); |
1da177e4c
|
291 |
|
9bbb9e5a3
|
292 |
int param_get_bool(char *buffer, const struct kernel_param *kp) |
1da177e4c
|
293 294 |
{ /* Y and N chosen as being relatively non-coder friendly */ |
96802e6b1
|
295 296 |
return sprintf(buffer, "%c ", *(bool *)kp->arg ? 'Y' : 'N'); |
1da177e4c
|
297 |
} |
a14fe249a
|
298 |
EXPORT_SYMBOL(param_get_bool); |
1da177e4c
|
299 |
|
9c27847dd
|
300 |
const struct kernel_param_ops param_ops_bool = { |
6a4c26431
|
301 |
.flags = KERNEL_PARAM_OPS_FL_NOARG, |
9bbb9e5a3
|
302 303 304 305 |
.set = param_set_bool, .get = param_get_bool, }; EXPORT_SYMBOL(param_ops_bool); |
d19f05d8a
|
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
int param_set_bool_enable_only(const char *val, const struct kernel_param *kp) { int err = 0; bool new_value; bool orig_value = *(bool *)kp->arg; struct kernel_param dummy_kp = *kp; dummy_kp.arg = &new_value; err = param_set_bool(val, &dummy_kp); if (err) return err; /* Don't let them unset it once it's set! */ if (!new_value && orig_value) return -EROFS; if (new_value) err = param_set_bool(val, kp); return err; } EXPORT_SYMBOL_GPL(param_set_bool_enable_only); const struct kernel_param_ops param_ops_bool_enable_only = { .flags = KERNEL_PARAM_OPS_FL_NOARG, .set = param_set_bool_enable_only, .get = param_get_bool, }; |
154be21c5
|
335 |
EXPORT_SYMBOL_GPL(param_ops_bool_enable_only); |
d19f05d8a
|
336 |
|
fddd52012
|
337 |
/* This one must be bool. */ |
9bbb9e5a3
|
338 |
int param_set_invbool(const char *val, const struct kernel_param *kp) |
1da177e4c
|
339 |
{ |
fddd52012
|
340 341 |
int ret; bool boolval; |
22e48eaf5
|
342 |
struct kernel_param dummy; |
1da177e4c
|
343 |
|
22e48eaf5
|
344 |
dummy.arg = &boolval; |
1da177e4c
|
345 346 |
ret = param_set_bool(val, &dummy); if (ret == 0) |
9a71af2c3
|
347 |
*(bool *)kp->arg = !boolval; |
1da177e4c
|
348 349 |
return ret; } |
a14fe249a
|
350 |
EXPORT_SYMBOL(param_set_invbool); |
1da177e4c
|
351 |
|
9bbb9e5a3
|
352 |
int param_get_invbool(char *buffer, const struct kernel_param *kp) |
1da177e4c
|
353 |
{ |
96802e6b1
|
354 355 |
return sprintf(buffer, "%c ", (*(bool *)kp->arg) ? 'N' : 'Y'); |
1da177e4c
|
356 |
} |
a14fe249a
|
357 |
EXPORT_SYMBOL(param_get_invbool); |
1da177e4c
|
358 |
|
9c27847dd
|
359 |
const struct kernel_param_ops param_ops_invbool = { |
9bbb9e5a3
|
360 361 362 363 |
.set = param_set_invbool, .get = param_get_invbool, }; EXPORT_SYMBOL(param_ops_invbool); |
69116f279
|
364 365 |
int param_set_bint(const char *val, const struct kernel_param *kp) { |
5104b7d76
|
366 367 |
/* Match bool exactly, by re-using it. */ struct kernel_param boolkp = *kp; |
69116f279
|
368 369 |
bool v; int ret; |
69116f279
|
370 |
boolkp.arg = &v; |
69116f279
|
371 372 373 374 375 376 377 |
ret = param_set_bool(val, &boolkp); if (ret == 0) *(int *)kp->arg = v; return ret; } EXPORT_SYMBOL(param_set_bint); |
9c27847dd
|
378 |
const struct kernel_param_ops param_ops_bint = { |
6a4c26431
|
379 |
.flags = KERNEL_PARAM_OPS_FL_NOARG, |
69116f279
|
380 381 382 383 |
.set = param_set_bint, .get = param_get_int, }; EXPORT_SYMBOL(param_ops_bint); |
9730b5b06
|
384 |
/* We break the rule and mangle the string. */ |
b51d23e4e
|
385 386 |
static int param_array(struct module *mod, const char *name, |
9871728b7
|
387 388 389 |
const char *val, unsigned int min, unsigned int max, void *elem, int elemsize, |
9bbb9e5a3
|
390 |
int (*set)(const char *, const struct kernel_param *kp), |
026cee008
|
391 |
s16 level, |
eb38a996e
|
392 |
unsigned int *num) |
1da177e4c
|
393 394 395 396 397 398 399 400 |
{ int ret; struct kernel_param kp; char save; /* Get the name right for errors. */ kp.name = name; kp.arg = elem; |
026cee008
|
401 |
kp.level = level; |
1da177e4c
|
402 |
|
1da177e4c
|
403 404 405 406 407 408 |
*num = 0; /* We expect a comma-separated list of values. */ do { int len; if (*num == max) { |
b5f3abf95
|
409 410 |
pr_err("%s: can only take %i arguments ", name, max); |
1da177e4c
|
411 412 413 414 415 416 417 |
return -EINVAL; } len = strcspn(val, ","); /* nul-terminate and parse */ save = val[len]; ((char *)val)[len] = '\0'; |
cf2fde7b3
|
418 |
check_kparam_locked(mod); |
1da177e4c
|
419 420 421 422 423 424 425 426 427 428 |
ret = set(val, &kp); if (ret != 0) return ret; kp.arg += elemsize; val += len+1; (*num)++; } while (save == ','); if (*num < min) { |
b5f3abf95
|
429 430 |
pr_err("%s: needs at least %i arguments ", name, min); |
1da177e4c
|
431 432 433 434 |
return -EINVAL; } return 0; } |
9bbb9e5a3
|
435 |
static int param_array_set(const char *val, const struct kernel_param *kp) |
1da177e4c
|
436 |
{ |
22e48eaf5
|
437 |
const struct kparam_array *arr = kp->arr; |
31143a120
|
438 |
unsigned int temp_num; |
1da177e4c
|
439 |
|
b51d23e4e
|
440 |
return param_array(kp->mod, kp->name, val, 1, arr->max, arr->elem, |
026cee008
|
441 |
arr->elemsize, arr->ops->set, kp->level, |
3c7d76e37
|
442 |
arr->num ?: &temp_num); |
1da177e4c
|
443 |
} |
9bbb9e5a3
|
444 |
static int param_array_get(char *buffer, const struct kernel_param *kp) |
1da177e4c
|
445 446 |
{ int i, off, ret; |
22e48eaf5
|
447 |
const struct kparam_array *arr = kp->arr; |
5104b7d76
|
448 |
struct kernel_param p = *kp; |
1da177e4c
|
449 |
|
1da177e4c
|
450 |
for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) { |
96802e6b1
|
451 452 |
/* Replace with comma */ |
1da177e4c
|
453 |
if (i) |
96802e6b1
|
454 |
buffer[off - 1] = ','; |
1da177e4c
|
455 |
p.arg = arr->elem + arr->elemsize * i; |
cf2fde7b3
|
456 |
check_kparam_locked(p.mod); |
9bbb9e5a3
|
457 |
ret = arr->ops->get(buffer + off, &p); |
1da177e4c
|
458 459 460 461 462 463 464 |
if (ret < 0) return ret; off += ret; } buffer[off] = '\0'; return off; } |
e6df34a44
|
465 466 467 468 469 470 471 472 473 |
static void param_array_free(void *arg) { unsigned int i; const struct kparam_array *arr = arg; if (arr->ops->free) for (i = 0; i < (arr->num ? *arr->num : arr->max); i++) arr->ops->free(arr->elem + arr->elemsize * i); } |
9c27847dd
|
474 |
const struct kernel_param_ops param_array_ops = { |
9bbb9e5a3
|
475 476 |
.set = param_array_set, .get = param_array_get, |
e6df34a44
|
477 |
.free = param_array_free, |
9bbb9e5a3
|
478 479 480 481 |
}; EXPORT_SYMBOL(param_array_ops); int param_set_copystring(const char *val, const struct kernel_param *kp) |
1da177e4c
|
482 |
{ |
22e48eaf5
|
483 |
const struct kparam_string *kps = kp->str; |
1da177e4c
|
484 485 |
if (strlen(val)+1 > kps->maxlen) { |
b5f3abf95
|
486 487 |
pr_err("%s: string doesn't fit in %u chars. ", |
1da177e4c
|
488 489 490 491 492 493 |
kp->name, kps->maxlen-1); return -ENOSPC; } strcpy(kps->string, val); return 0; } |
a14fe249a
|
494 |
EXPORT_SYMBOL(param_set_copystring); |
1da177e4c
|
495 |
|
9bbb9e5a3
|
496 |
int param_get_string(char *buffer, const struct kernel_param *kp) |
1da177e4c
|
497 |
{ |
22e48eaf5
|
498 |
const struct kparam_string *kps = kp->str; |
96802e6b1
|
499 500 |
return scnprintf(buffer, PAGE_SIZE, "%s ", kps->string); |
1da177e4c
|
501 |
} |
a14fe249a
|
502 |
EXPORT_SYMBOL(param_get_string); |
1da177e4c
|
503 |
|
9c27847dd
|
504 |
const struct kernel_param_ops param_ops_string = { |
9bbb9e5a3
|
505 506 507 508 |
.set = param_set_copystring, .get = param_get_string, }; EXPORT_SYMBOL(param_ops_string); |
1da177e4c
|
509 |
/* sysfs output in /sys/modules/XYZ/parameters/ */ |
350f82586
|
510 511 |
#define to_module_attr(n) container_of(n, struct module_attribute, attr) #define to_module_kobject(n) container_of(n, struct module_kobject, kobj) |
1da177e4c
|
512 |
|
1da177e4c
|
513 514 515 |
struct param_attribute { struct module_attribute mattr; |
9bbb9e5a3
|
516 |
const struct kernel_param *param; |
1da177e4c
|
517 518 519 520 |
}; struct module_param_attrs { |
9b473de87
|
521 |
unsigned int num; |
1da177e4c
|
522 |
struct attribute_group grp; |
fa29c9c11
|
523 |
struct param_attribute attrs[]; |
1da177e4c
|
524 |
}; |
ef665c1a0
|
525 |
#ifdef CONFIG_SYSFS |
350f82586
|
526 |
#define to_param_attr(n) container_of(n, struct param_attribute, mattr) |
1da177e4c
|
527 528 |
static ssize_t param_attr_show(struct module_attribute *mattr, |
4befb026c
|
529 |
struct module_kobject *mk, char *buf) |
1da177e4c
|
530 531 532 |
{ int count; struct param_attribute *attribute = to_param_attr(mattr); |
9bbb9e5a3
|
533 |
if (!attribute->param->ops->get) |
1da177e4c
|
534 |
return -EPERM; |
b51d23e4e
|
535 |
kernel_param_lock(mk->mod); |
9bbb9e5a3
|
536 |
count = attribute->param->ops->get(buf, attribute->param); |
b51d23e4e
|
537 |
kernel_param_unlock(mk->mod); |
1da177e4c
|
538 539 540 541 542 |
return count; } /* sysfs always hands a nul-terminated string in buf. We rely on that. */ static ssize_t param_attr_store(struct module_attribute *mattr, |
b51d23e4e
|
543 |
struct module_kobject *mk, |
1da177e4c
|
544 545 546 547 |
const char *buf, size_t len) { int err; struct param_attribute *attribute = to_param_attr(mattr); |
9bbb9e5a3
|
548 |
if (!attribute->param->ops->set) |
1da177e4c
|
549 |
return -EPERM; |
b51d23e4e
|
550 |
kernel_param_lock(mk->mod); |
20657f66e
|
551 552 553 554 |
if (param_check_unsafe(attribute->param)) err = attribute->param->ops->set(buf, attribute->param); else err = -EPERM; |
b51d23e4e
|
555 |
kernel_param_unlock(mk->mod); |
1da177e4c
|
556 557 558 559 |
if (!err) return len; return err; } |
ef665c1a0
|
560 |
#endif |
1da177e4c
|
561 562 563 564 565 566 |
#ifdef CONFIG_MODULES #define __modinit #else #define __modinit __init #endif |
ef665c1a0
|
567 |
#ifdef CONFIG_SYSFS |
b51d23e4e
|
568 |
void kernel_param_lock(struct module *mod) |
907b29eb4
|
569 |
{ |
b51d23e4e
|
570 |
mutex_lock(KPARAM_MUTEX(mod)); |
907b29eb4
|
571 |
} |
907b29eb4
|
572 |
|
b51d23e4e
|
573 |
void kernel_param_unlock(struct module *mod) |
907b29eb4
|
574 |
{ |
b51d23e4e
|
575 |
mutex_unlock(KPARAM_MUTEX(mod)); |
907b29eb4
|
576 |
} |
b51d23e4e
|
577 |
|
b51d23e4e
|
578 579 |
EXPORT_SYMBOL(kernel_param_lock); EXPORT_SYMBOL(kernel_param_unlock); |
907b29eb4
|
580 |
|
1da177e4c
|
581 |
/* |
9b473de87
|
582 583 |
* add_sysfs_param - add a parameter to sysfs * @mk: struct module_kobject |
630cc2b30
|
584 |
* @kp: the actual parameter definition to add to sysfs |
9b473de87
|
585 |
* @name: name of parameter |
1da177e4c
|
586 |
* |
9b473de87
|
587 588 589 |
* Create a kobject if for a (per-module) parameter if mp NULL, and * create file in sysfs. Returns an error on out of memory. Always cleans up * if there's an error. |
1da177e4c
|
590 |
*/ |
9b473de87
|
591 |
static __modinit int add_sysfs_param(struct module_kobject *mk, |
9bbb9e5a3
|
592 |
const struct kernel_param *kp, |
9b473de87
|
593 |
const char *name) |
1da177e4c
|
594 |
{ |
18eb74fa9
|
595 596 597 |
struct module_param_attrs *new_mp; struct attribute **new_attrs; unsigned int i; |
9b473de87
|
598 599 600 601 602 |
/* We don't bother calling this with invisible parameters. */ BUG_ON(!kp->perm); if (!mk->mp) { |
18eb74fa9
|
603 604 605 606 607 608 609 610 611 612 613 |
/* First allocation. */ mk->mp = kzalloc(sizeof(*mk->mp), GFP_KERNEL); if (!mk->mp) return -ENOMEM; mk->mp->grp.name = "parameters"; /* NULL-terminated attribute array. */ mk->mp->grp.attrs = kzalloc(sizeof(mk->mp->grp.attrs[0]), GFP_KERNEL); /* Caller will cleanup via free_module_param_attrs */ if (!mk->mp->grp.attrs) return -ENOMEM; |
9b473de87
|
614 |
} |
1da177e4c
|
615 |
|
18eb74fa9
|
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 |
/* Enlarge allocations. */ new_mp = krealloc(mk->mp, sizeof(*mk->mp) + sizeof(mk->mp->attrs[0]) * (mk->mp->num + 1), GFP_KERNEL); if (!new_mp) return -ENOMEM; mk->mp = new_mp; /* Extra pointer for NULL terminator */ new_attrs = krealloc(mk->mp->grp.attrs, sizeof(mk->mp->grp.attrs[0]) * (mk->mp->num + 2), GFP_KERNEL); if (!new_attrs) return -ENOMEM; mk->mp->grp.attrs = new_attrs; |
9b473de87
|
632 633 |
/* Tack new one on the end. */ |
c772be523
|
634 |
memset(&mk->mp->attrs[mk->mp->num], 0, sizeof(mk->mp->attrs[0])); |
18eb74fa9
|
635 636 637 |
sysfs_attr_init(&mk->mp->attrs[mk->mp->num].mattr.attr); mk->mp->attrs[mk->mp->num].param = kp; mk->mp->attrs[mk->mp->num].mattr.show = param_attr_show; |
b0a65b0cc
|
638 639 640 |
/* Do not allow runtime DAC changes to make param writable. */ if ((kp->perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) mk->mp->attrs[mk->mp->num].mattr.store = param_attr_store; |
574732c73
|
641 642 |
else mk->mp->attrs[mk->mp->num].mattr.store = NULL; |
18eb74fa9
|
643 644 645 |
mk->mp->attrs[mk->mp->num].mattr.attr.name = (char *)name; mk->mp->attrs[mk->mp->num].mattr.attr.mode = kp->perm; mk->mp->num++; |
9b473de87
|
646 647 |
/* Fix up all the pointers, since krealloc can move us */ |
18eb74fa9
|
648 649 650 |
for (i = 0; i < mk->mp->num; i++) mk->mp->grp.attrs[i] = &mk->mp->attrs[i].mattr.attr; mk->mp->grp.attrs[mk->mp->num] = NULL; |
9b473de87
|
651 |
return 0; |
9b473de87
|
652 |
} |
1da177e4c
|
653 |
|
d2441183d
|
654 |
#ifdef CONFIG_MODULES |
9b473de87
|
655 656 |
static void free_module_param_attrs(struct module_kobject *mk) { |
18eb74fa9
|
657 658 |
if (mk->mp) kfree(mk->mp->grp.attrs); |
9b473de87
|
659 660 |
kfree(mk->mp); mk->mp = NULL; |
1da177e4c
|
661 |
} |
1da177e4c
|
662 663 664 665 666 667 |
/* * module_param_sysfs_setup - setup sysfs support for one module * @mod: module * @kparam: module parameters (array) * @num_params: number of module parameters * |
9b473de87
|
668 669 |
* Adds sysfs entries for module parameters under * /sys/module/[mod->name]/parameters/ |
1da177e4c
|
670 671 |
*/ int module_param_sysfs_setup(struct module *mod, |
9bbb9e5a3
|
672 |
const struct kernel_param *kparam, |
1da177e4c
|
673 674 |
unsigned int num_params) { |
9b473de87
|
675 676 677 678 679 680 681 |
int i, err; bool params = false; for (i = 0; i < num_params; i++) { if (kparam[i].perm == 0) continue; err = add_sysfs_param(&mod->mkobj, &kparam[i], kparam[i].name); |
18eb74fa9
|
682 683 |
if (err) { free_module_param_attrs(&mod->mkobj); |
9b473de87
|
684 |
return err; |
18eb74fa9
|
685 |
} |
9b473de87
|
686 687 |
params = true; } |
1da177e4c
|
688 |
|
9b473de87
|
689 690 |
if (!params) return 0; |
1da177e4c
|
691 |
|
9b473de87
|
692 693 694 695 696 |
/* Create the param group. */ err = sysfs_create_group(&mod->mkobj.kobj, &mod->mkobj.mp->grp); if (err) free_module_param_attrs(&mod->mkobj); return err; |
1da177e4c
|
697 698 699 700 701 702 703 704 705 706 707 |
} /* * module_param_sysfs_remove - remove sysfs support for one module * @mod: module * * Remove sysfs entries for module parameters and the corresponding * kobject. */ void module_param_sysfs_remove(struct module *mod) { |
9b473de87
|
708 709 |
if (mod->mkobj.mp) { sysfs_remove_group(&mod->mkobj.kobj, &mod->mkobj.mp->grp); |
1da177e4c
|
710 711 |
/* We are positive that no one is using any param * attrs at this point. Deallocate immediately. */ |
9b473de87
|
712 |
free_module_param_attrs(&mod->mkobj); |
1da177e4c
|
713 714 715 |
} } #endif |
e180a6b77
|
716 717 |
void destroy_params(const struct kernel_param *params, unsigned num) { |
e6df34a44
|
718 719 720 721 722 |
unsigned int i; for (i = 0; i < num; i++) if (params[i].ops->free) params[i].ops->free(params[i].arg); |
e180a6b77
|
723 |
} |
e94965ed5
|
724 |
static struct module_kobject * __init locate_module_kobject(const char *name) |
1da177e4c
|
725 726 |
{ struct module_kobject *mk; |
9b473de87
|
727 728 |
struct kobject *kobj; int err; |
1da177e4c
|
729 |
|
9b473de87
|
730 731 |
kobj = kset_find_obj(module_kset, name); if (kobj) { |
9b473de87
|
732 |
mk = to_module_kobject(kobj); |
9b473de87
|
733 734 735 736 737 738 739 740 |
} else { mk = kzalloc(sizeof(struct module_kobject), GFP_KERNEL); BUG_ON(!mk); mk->mod = THIS_MODULE; mk->kobj.kset = module_kset; err = kobject_init_and_add(&mk->kobj, &module_ktype, NULL, "%s", name); |
88bfa3247
|
741 742 743 744 |
#ifdef CONFIG_MODULES if (!err) err = sysfs_create_file(&mk->kobj, &module_uevent.attr); #endif |
9b473de87
|
745 746 |
if (err) { kobject_put(&mk->kobj); |
b5f3abf95
|
747 748 |
pr_crit("Adding module '%s' to sysfs failed (%d), the system may be unstable. ", |
e94965ed5
|
749 |
name, err); |
e94965ed5
|
750 |
return NULL; |
9b473de87
|
751 |
} |
e94965ed5
|
752 753 |
/* So that we hold reference in both cases. */ |
9b473de87
|
754 |
kobject_get(&mk->kobj); |
74c5b597e
|
755 |
} |
9b473de87
|
756 |
|
e94965ed5
|
757 758 759 760 |
return mk; } static void __init kernel_add_sysfs_param(const char *name, |
63a12d9d0
|
761 |
const struct kernel_param *kparam, |
e94965ed5
|
762 763 764 765 766 767 768 769 770 771 772 773 |
unsigned int name_skip) { struct module_kobject *mk; int err; mk = locate_module_kobject(name); if (!mk) return; /* We need to remove old parameters before adding more. */ if (mk->mp) sysfs_remove_group(&mk->kobj, &mk->mp->grp); |
9b473de87
|
774 775 776 777 778 |
/* These should not fail at boot. */ err = add_sysfs_param(mk, kparam, kparam->name + name_skip); BUG_ON(err); err = sysfs_create_group(&mk->kobj, &mk->mp->grp); BUG_ON(err); |
f30c53a87
|
779 |
kobject_uevent(&mk->kobj, KOBJ_ADD); |
9b473de87
|
780 |
kobject_put(&mk->kobj); |
1da177e4c
|
781 782 783 |
} /* |
b634d130e
|
784 |
* param_sysfs_builtin - add sysfs parameters for built-in modules |
1da177e4c
|
785 786 787 788 789 790 |
* * Add module_parameters to sysfs for "modules" built into the kernel. * * The "module" name (KBUILD_MODNAME) is stored before a dot, the * "parameter" name is stored behind a dot in kernel_param->name. So, * extract the "module" name for all built-in kernel_param-eters, |
9b473de87
|
791 |
* and for all who have the same, call kernel_add_sysfs_param. |
1da177e4c
|
792 793 794 |
*/ static void __init param_sysfs_builtin(void) { |
63a12d9d0
|
795 |
const struct kernel_param *kp; |
9b473de87
|
796 797 |
unsigned int name_len; char modname[MODULE_NAME_LEN]; |
1da177e4c
|
798 |
|
9b473de87
|
799 |
for (kp = __start___param; kp < __stop___param; kp++) { |
1da177e4c
|
800 |
char *dot; |
9b473de87
|
801 802 |
if (kp->perm == 0) continue; |
1da177e4c
|
803 |
|
730b69d22
|
804 |
dot = strchr(kp->name, '.'); |
1da177e4c
|
805 |
if (!dot) { |
67e67ceaa
|
806 807 808 809 810 811 |
/* This happens for core_param() */ strcpy(modname, "kernel"); name_len = 0; } else { name_len = dot - kp->name + 1; strlcpy(modname, kp->name, name_len); |
1da177e4c
|
812 |
} |
67e67ceaa
|
813 |
kernel_add_sysfs_param(modname, kp, name_len); |
1da177e4c
|
814 |
} |
1da177e4c
|
815 |
} |
e94965ed5
|
816 |
ssize_t __modver_version_show(struct module_attribute *mattr, |
4befb026c
|
817 |
struct module_kobject *mk, char *buf) |
e94965ed5
|
818 819 820 |
{ struct module_version_attribute *vattr = container_of(mattr, struct module_version_attribute, mattr); |
f4940ab7c
|
821 822 |
return scnprintf(buf, PAGE_SIZE, "%s ", vattr->version); |
e94965ed5
|
823 |
} |
b4bc84280
|
824 825 |
extern const struct module_version_attribute *__start___modver[]; extern const struct module_version_attribute *__stop___modver[]; |
e94965ed5
|
826 827 828 |
static void __init version_sysfs_builtin(void) { |
b4bc84280
|
829 |
const struct module_version_attribute **p; |
e94965ed5
|
830 831 |
struct module_kobject *mk; int err; |
b4bc84280
|
832 833 |
for (p = __start___modver; p < __stop___modver; p++) { const struct module_version_attribute *vattr = *p; |
e94965ed5
|
834 835 836 |
mk = locate_module_kobject(vattr->module_name); if (mk) { err = sysfs_create_file(&mk->kobj, &vattr->mattr.attr); |
74c3dea35
|
837 |
WARN_ON_ONCE(err); |
e94965ed5
|
838 839 840 841 842 |
kobject_uevent(&mk->kobj, KOBJ_ADD); kobject_put(&mk->kobj); } } } |
1da177e4c
|
843 844 |
/* module-related sysfs stuff */ |
1da177e4c
|
845 |
|
1da177e4c
|
846 847 848 849 850 851 852 853 854 855 856 857 |
static ssize_t module_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct module_attribute *attribute; struct module_kobject *mk; int ret; attribute = to_module_attr(attr); mk = to_module_kobject(kobj); if (!attribute->show) |
70f2817a4
|
858 |
return -EIO; |
1da177e4c
|
859 |
|
4befb026c
|
860 |
ret = attribute->show(attribute, mk, buf); |
1da177e4c
|
861 |
|
1da177e4c
|
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 |
return ret; } static ssize_t module_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len) { struct module_attribute *attribute; struct module_kobject *mk; int ret; attribute = to_module_attr(attr); mk = to_module_kobject(kobj); if (!attribute->store) |
70f2817a4
|
877 |
return -EIO; |
1da177e4c
|
878 |
|
4befb026c
|
879 |
ret = attribute->store(attribute, mk, buf, len); |
1da177e4c
|
880 |
|
1da177e4c
|
881 882 |
return ret; } |
52cf25d0a
|
883 |
static const struct sysfs_ops module_sysfs_ops = { |
1da177e4c
|
884 885 886 |
.show = module_attr_show, .store = module_attr_store, }; |
270a6c4ca
|
887 888 889 890 891 892 893 894 |
static int uevent_filter(struct kset *kset, struct kobject *kobj) { struct kobj_type *ktype = get_ktype(kobj); if (ktype == &module_ktype) return 1; return 0; } |
9cd43611c
|
895 |
static const struct kset_uevent_ops module_uevent_ops = { |
270a6c4ca
|
896 897 |
.filter = uevent_filter, }; |
7405c1e15
|
898 |
struct kset *module_kset; |
823bccfc4
|
899 |
int module_sysfs_initialized; |
1da177e4c
|
900 |
|
942e44312
|
901 902 903 904 905 |
static void module_kobj_release(struct kobject *kobj) { struct module_kobject *mk = to_module_kobject(kobj); complete(mk->kobj_completion); } |
7405c1e15
|
906 |
struct kobj_type module_ktype = { |
942e44312
|
907 |
.release = module_kobj_release, |
1da177e4c
|
908 909 |
.sysfs_ops = &module_sysfs_ops, }; |
1da177e4c
|
910 911 912 913 914 |
/* * param_sysfs_init - wrapper for built-in params support */ static int __init param_sysfs_init(void) { |
7405c1e15
|
915 916 917 918 919 920 |
module_kset = kset_create_and_add("module", &module_uevent_ops, NULL); if (!module_kset) { printk(KERN_WARNING "%s (%d): error creating kset ", __FILE__, __LINE__); return -ENOMEM; |
d8c7649e9
|
921 |
} |
823bccfc4
|
922 |
module_sysfs_initialized = 1; |
1da177e4c
|
923 |
|
e94965ed5
|
924 |
version_sysfs_builtin(); |
1da177e4c
|
925 926 927 928 |
param_sysfs_builtin(); return 0; } |
d10be6d1b
|
929 |
subsys_initcall(param_sysfs_init); |
1da177e4c
|
930 |
|
7405c1e15
|
931 |
#endif /* CONFIG_SYSFS */ |