<kernel v5.0>
cmdline 인수로 받은 파라메터에 대해 구식 또는 신식 커널 파라메터 블럭에서에 연결된 설정 함수를 호출한다. 모듈(modprobe) 관련 파라메터가 있는 경우는 이 루틴에서 무시하고 매치되지 않은 unknown 파라메터는 값이 있는 경우 envp_init[] 배열에 추가하고 값이 없는 경우 argv_init[] 배열에 추가한다.
setup_kernel() 중간 부분
init/main.c
after_dashes = parse_args("Booting kernel",
static_command_line, __start___param,
__stop___param - __start___param,
-1, -1, &unknown_bootoption);
if (!IS_ERR_OR_NULL(after_dashes))
parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
set_init_arg);
- after_dashes = parse_args(“Booting kernel”, static_command_line, __start___param, __stop___param – __start___param, -1, -1, &unknown_bootoption);
- static_command_line을 파싱하고 param=value 형태로 다듬고 신형 커널 파라메터 블럭에서(__start___param ~ __stop___param)에서 각 커널파라메터에 매치되는 함수를 호출하고 매치되지 않는 경우 unknown_bootoption() 함수를 호출한다.
- 만일 파라메터가 “–“로 끝나는 경우 after_dashes에 “–” 이후의 문자열이 담긴다.
- if (!IS_ERR_OR_NULL(after_dashes))
- 파싱 하면서 “–“를 발견한 경우
- parse_args(“Setting init args”, after_dashes, NULL, 0, -1, -1, set_init_arg);
- “–” 뒤의 파라메터들을 argv_init[] 배열에 추가한다.
parse_args()
kernel/params.c
char *parse_args(const char *doing,
char *args,
const struct kernel_param *params,
unsigned num,
s16 min_level,
s16 max_level,
int (*unknown)(char *param, char *val, const char *doing))
cmdline을 파싱하여 각 커널파라메터에 대응하는 params 블럭에서 찾아서 매치되는 파라메터의 함수를 호출하고 매치되지 않는 경우 unknown을 호출한다. 또한 파라메터가 “–“로 끝나는 경우 “–” 이후의 문자열을 리턴한다.
- parse_args()를 호출하는 case
- parse_args(“early_options”, , , , , do_early_param);
- init/main.c – parse_early_options() 함수
- 참고: parse_early_param() | 문c
- parse_args(“Booting kernel”, static_command_line, __start___param, __stop___param – start___param, -1, -1, &unknown_bootoption);
- init/main.c – start_kernel() 함수
- parse_args(“Setting init args”, after_dashes, NULL, 0, -1, -1, set_init_arg);
- init/main.c – start_kernel() 함수
- parse_args(“dyndbg params”, cmdline, NULL, 0, 0, 0, &ddebug_dyndbg_boot_param_cb);
- lib/dynamic_debug.c – dynamic_debug_init() 함수
- parse_args(initcall_level_names[level], initcall_command_line, __start___param, __stop___param – __start___param, level, level, &repair_env_string);
- init/main.c – do_initcall_level() 함수
- parse_args(“early_options”, , , , , do_early_param);
unknown_bootoption()
init/main.c
/*
* Unknown boot options get handed to init, unless they look like
* unused parameters (modprobe will find them in /proc/cmdline).
*/
static int __init unknown_bootoption(char *param, char *val, const char *unused)
{
repair_env_string(param, val, unused);
/* Handle obsolete-style parameters */
if (obsolete_checksetup(param))
return 0;
/* Unused module parameter. */
if (strchr(param, '.') && (!val || strchr(param, '.') < val))
return 0;
if (panic_later)
return 0;
if (val) {
/* Environment option */
unsigned int i;
for (i = 0; envp_init[i]; i++) {
if (i == MAX_INIT_ENVS) {
panic_later = "env";
panic_param = param;
}
if (!strncmp(param, envp_init[i], val - param))
break;
}
envp_init[i] = param;
} else {
/* Command line option */
unsigned int i;
for (i = 0; argv_init[i]; i++) {
if (i == MAX_INIT_ARGS) {
panic_later = "init";
panic_param = param;
}
}
argv_init[i] = param;
}
return 0;
}
cmdline 인수로 받은 파라메터를 param=value 형태로 다듬고 구형 커널 파라메터 블럭(__setup_start ~ __setup_end) 에서 매치된 파라메터 중 early가 아닌 경우 해당 파라메터에 연결된 설정 함수를 호출한다. 또한 모듈(modprobe) 관련 파라메터가 있는 경우 일단 무시한다. 그 외에 매치되지 않은 unknown 파라메터는 값이 있는 경우 envp_init[] 배열에 추가하고 값이 없는 경우 argv_init[] 배열에 추가한다.
- repair_env_string(param, val, unused);
- param=val 및 param=”val”과 같은 형태는 ‘=’대신 파라메터 구분을 하기 위해 null이 입력되어 있는데 이를 다시 ‘=’문자로 치환하고 따옴표가 사용된 경우 param=val 형태가 되도록 따옴표를 제거한다.
- rpi2 변경된 예)
- dma.dmachans=0x7f35 bcm2708_fb.fbwidth=592 bcm2708_fb.fbheight=448 bcm2709.boardrev=0xa01041 bcm2709.serial=0x670ebdbf smsc95xx.macaddr=B8:27:EB:0E:BD:BF bcm2708_fb.fbswap=1 bcm2709.disk_led_gpio=47 bcm2709.disk_led_active_low=0 sdhci-bcm2708.emmc_clock_freq=250000000 vc_mem.mem_base=0x3dc00000 vc_mem.mem_size=0x3f000000 dwc_otg.lpm_enable=0 console=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p6 rootfstype=ext4 elevator=deadline rootwait
- if (obsolete_checksetup(param)) return 0;
- 구형 파라메터 블럭에서 매치된 파라메터가 있는 경우 해당 파라메터에 연결된 설정함수를 호출하고 리턴한다.
- if (strchr(param, ‘.’) && (!val || strchr(param, ‘.’) < val)) return 0;
- 모듈 파라메터가 사용된 경우 리턴한다.
- rpi2 예) dma.dmachans=0x7f35
- 참고: param: don’t complain about unused module parameters
- 모듈 파라메터가 사용된 경우 리턴한다.
val 값이 있는 경우 environment 옵션으로 등록한다.
- if (val) {
- val 값이 있는 경우
- for (i = 0; envp_init[i]; i++) {
- envp_init[] 배열에 등록된 엔트리 수 만큼 루프를 돈다.
- if (i == MAX_INIT_ENVS) { panic_later = “env”; panic_param = param;
- i가 MAX_INIT_ENVS에 도달하면 panic 관련 변수 설정을 한다.
- if (!strncmp(param, envp_init[i], val – param)) break;
- 파라메터명이 envp_init[]에 등록되어 있는 문자열과 같은 경우 break하여 루틴을 빠져나간다.
- envp_init[i] = param;
- envp_init[] 배열에 param을 추가한다.
val 값이 없는 경우 command line option으로 등록한다.
- for (i = 0; argv_init[i]; i++) {
- argv_init[] 배열에 등록된 엔트리 수 만큼 루프를 돈다.
- if (i == MAX_INIT_ARGS) { panic_later = “init”; panic_param = param; }
- i가 MAX_INIT_ARGS에 도달하면 panic 관련 변수 설정을 한다.
- argv_init[i] = param;
- argv_init[] 배열에 param을 추가한다.
repair_env_string()
init/main.c
static int __init repair_env_string(char *param, char *val, const char *unused)
{
if (val) {
/* param=val or param="val"? */
if (val == param+strlen(param)+1)
val[-1] = '=';
else if (val == param+strlen(param)+2) {
val[-2] = '=';
memmove(val-1, val, strlen(val)+1);
val--;
} else
BUG();
}
return 0;
}
param=val 및 param=”val”과 같은 형태는 ‘=’대신 파라메터 구분을 하기 위해 null이 입력되어 있는데 이를 다시 ‘=’문자로 치환하고 따옴표가 사용된 경우 param=val 형태가 되도록 따옴표를 제거한다.
- if (val == param+strlen(param)+1) val[-1] = ‘=’;
- param<null>val 과 같이 따옴표를 사용하지 않은 경우 val[-1]에 ‘=’를 대입한다.
- else if (val == param+strlen(param)+2) { val[-2] = ‘=’; memmove(val-1, val, strlen(val)+1); val–;
- param<null>”val”과 같이 따옴표를 사용한 경우 val[2]에 ‘=’를 대입하고 val 문자열을 1칸 앞으로 당긴다.
obsolete_checksetup()
init/main.c
static int __init obsolete_checksetup(char *line)
{
const struct obs_kernel_param *p;
int had_early_param = 0;
p = __setup_start;
do {
int n = strlen(p->str);
if (parameqn(line, p->str, n)) {
if (p->early) {
/* Already done in parse_early_param?
* (Needs exact match on param part).
* Keep iterating, as we can have early
* params and __setups of same names 8( */
if (line[n] == '\0' || line[n] == '=')
had_early_param = 1;
} else if (!p->setup_func) {
pr_warn("Parameter %s is obsolete, ignored\n",
p->str);
return 1;
} else if (p->setup_func(line + n))
return 1;
}
p++;
} while (p < __setup_end);
return had_early_param;
}
__setup_start ~ __setup_end 까지의 구식 커널 파라메터 블럭에서 매치된 파라메터 중 early가 아닌 경우 해당 파라메터에 연결된 설정 함수를 호출한다.
- if (parameqn(line, p->str, n)) {
- 인수 문자열과 파라메터 블럭의 문자열을 n 바이트만큼 비교하여 같으면
- if (p->early) { if (line[n] == ‘\0’ || line[n] == ‘=’) had_early_param = 1;
- early 파라메터이면 had_early_param에 1을 대입한다.
- } else if (!p->setup_func) { pr_warn(“Parameter %s is obsolete, ignored\n”, p->str); return 1;
- 매치된 파라메터의 setup_func이 등록되어 있지 않은 경우 경고 메시지를 출력하고 1을 리턴한다.
- } else if (p->setup_func(line + n)) return 1;
- 매치된 파라메터의 setup_func()에 인수 val을 준비하여 호출하고 1을 리턴한다.
- } while (p < __setup_end);
- 파라메터 블럭의 끝까지 루프를 돈다.
init/main.c
const char *envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
init/main.c
static const char *argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
set_init_arg()
init/main.c
/* Anything after -- gets handed straight to init. */
static int __init set_init_arg(char *param, char *val, const char *unused)
{
unsigned int i;
if (panic_later)
return 0;
repair_env_string(param, val, unused);
for (i = 0; argv_init[i]; i++) {
if (i == MAX_INIT_ARGS) {
panic_later = "init";
panic_param = param;
return 0;
}
}
argv_init[i] = param;
return 0;
}
- if (panic_later) return 0;
- panic_later가 지정된 경우 함수를 빠져나간다.
- repair_env_string(param, val, unused);
- param=val 및 param=”val”과 같은 형태는 ‘=’대신 파라메터 구분을 하기 위해 null이 입력되어 있는데 이를 다시 ‘=’문자로 치환하고 따옴표가 사용된 경우 param=val 형태가 되도록 따옴표를 제거한다.
- for (i = 0; argv_init[i]; i++) {
- argv_init[] 배열에 등록된 엔트리 수 만큼 루프를 돈다.
- if (i == MAX_INIT_ARGS) { panic_later = “init”; panic_param = param; return 0; }
- i가 MAX_INIT_ARGS에 도달하면 panic 관련 변수 설정을 하고 루틴을 빠져나온다.
- argv_init[i] = param;
- argv_init[] 배열에 param을 추가한다.
모듈(커널) 파라메터 등록
커널(모듈) 파라메터는 다음의 매크로로 등록한다.
- __setup()
- 구형(obs_kernel_param) 커널 파라메터를 사용함
- 참고: parse_early_param() | 문c
- core_param()
- module_param()
모듈 파라메터는 다음 두 가지의 방법 중 하나로 전달될 수 있다.
- 커널 cmdline
- 예) usbcore.blinkenlights=1
- modprobe 명령
- 예) $ modprobe usbcore blinkenlights=1
module_param()
include/linux/moduleparam.h
/**
* module_param - typesafe helper for a module/cmdline parameter
* @value: the variable to alter, and exposed parameter name.
* @type: the type of the parameter
* @perm: visibility in sysfs.
*
* @value becomes the module parameter, or (prefixed by KBUILD_MODNAME and a
* ".") the kernel commandline parameter. Note that - is changed to _, so
* the user can use "foo-bar=1" even for variable "foo_bar".
*
* @perm is 0 if the the variable is not to appear in sysfs, or 0444
* for world-readable, 0644 for root-writable, etc. Note that if it
* is writable, you may need to use kparam_block_sysfs_write() around
* accesses (esp. charp, which can be kfreed when it changes).
*
* The @type is simply pasted to refer to a param_ops_##type and a
* param_check_##type: for convenience many standard types are provided but
* you can create your own by defining those variables.
*
* Standard types are:
* byte, short, ushort, int, uint, long, ulong
* charp: a character pointer
* bool: a bool, values 0/1, y/n, Y/N.
* invbool: the above, only sense-reversed (N = true).
*/
#define module_param(name, type, perm) \
module_param_named(name, name, type, perm)
모듈(커널) 파라메터를 등록할 때 사용하는 매크로인다.
- module_param_unsafe() 함수를 사용하는 경우 커널에 위험성이 존재할 때 사용한다.
module_param_named()
include/linux/moduleparam.h
/**
* module_param_named - typesafe helper for a renamed module/cmdline parameter
* @name: a valid C identifier which is the parameter name.
* @value: the actual lvalue to alter.
* @type: the type of the parameter
* @perm: visibility in sysfs.
*
* Usually it's a good idea to have variable names and user-exposed names the
* same, but that's harder if the variable must be non-static or is inside a
* structure. This allows exposure under a different name.
*/
#define module_param_named(name, value, type, perm) \
param_check_##type(name, &(value)); \
module_param_cb(name, ¶m_ops_##type, &value, perm); \
__MODULE_PARM_TYPE(name, #type)
타입 체크 후 module_param_cb()를 호출한다.
module_param_call()
include/linux/moduleparam.h
/* Obsolete - use module_param_cb() */
#define module_param_call(name, set, get, arg, perm) \
static struct kernel_param_ops __param_ops_##name = \
{ .flags = 0, (void *)set, (void *)get }; \
__module_param_call(MODULE_PARAM_PREFIX, \
name, &__param_ops_##name, arg, \
(perm) + sizeof(__check_old_set_param(set))*0, -1, 0)
set, get 핸들러를 연결한 __param_ops_XXX 객체를 만들고 __module_param_call()을 호출한다.
- 예) module_param_call(policy, pcie_aspm_set_policy, pcie_aspm_get_policy, NULL, 0644);
- static struct kernel_param_ops __param_ops_policy = { .flags = 0, (void *) pcie_aspm_set_policy, (void *) pcie_aspm_get_policy };
- __module_param_call(“pcie_aspm.”, policy, ¶m_ops_policy, NULL, 0644 + sizeof(__check_old_set_param(set))*0, -1, 0)
module_param_cb()
include/linux/moduleparam.h
/**
* module_param_cb - general callback for a module/cmdline parameter
* @name: a valid C identifier which is the parameter name.
* @ops: the set & get operations for this parameter.
* @perm: visibility in sysfs.
*
* The ops can have NULL set or get functions.
*/
#define module_param_cb(name, ops, arg, perm) \
__module_param_call(MODULE_PARAM_PREFIX, name, ops, arg, perm, -1, 0)
module_param_cb() 매크로 함수를 사용한 곳의 모듈명이 MODULE_PARAM_PREFIX에 담겨 빌드되고 이를 가지고 __module_param_call() 매크로를 호출한다.
- 예) module_param_cb(skip_txen_test, ¶m_ops_uint, &skip_txen_test, 0644);
- __modul_param_call(“8250_core”, skip_txen_test, ¶m_ops_uint, &skip_txen_test, 0644, -1, 0)
__module_param_call()
include/linux/moduleparam.h
/* This is the fundamental function for registering boot/module
parameters. */
#define __module_param_call(prefix, name, ops, arg, perm, level, flags) \
/* Default value instead of permissions? */ \
static const char __param_str_##name[] = prefix #name; \
static struct kernel_param __moduleparam_const __param_##name \
__used \
__attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
= { __param_str_##name, ops, VERIFY_OCTAL_PERMISSIONS(perm), \
level, flags, { arg } }
모듈(커널) 파라메터 블럭에 파라메터와 핸들러들이 등록된다.
- 예) __modul_param_call(“8250_core”, skip_txen_test, ¶m_ops_uint, &skip_txen_test, 0644, -1, 0)
- static const char __param_str_skip_txen_test[] = “8250_core” skip_txen_test;
- static struct kernel_param const __param_skip_txen_test = { __param_str_skip_txen_test, ¶m_ops_uint, 0644, -1, 0, { &skip_txen_test } }
- __moduleparam_const
- ALPHA, IA64, PPC64 아키텍처는 아무일도 하지 않고 그 밖의 아키텍처는 const 이다.
구조체
kernel_param 구조체
struct kernel_param {
const char *name;
const struct kernel_param_ops *ops;
u16 perm;
s8 level;
u8 flags;
union {
void *arg;
const struct kparam_string *str;
const struct kparam_array *arr;
};
};
- flags
- KERNEL_PARAM_FL_UNSAFE(1)을 사용하는 경우 커널에 문제를 일으킬 수 있는 위험한 파라메터라는 것을 의미한다.
kernel_param_ops 구조체
include/linux/moduleparam.h
struct kernel_param_ops {
/* How the ops should behave */
unsigned int flags;
/* Returns 0, or -errno. arg is in kp->arg. */
int (*set)(const char *val, const struct kernel_param *kp);
/* Returns length written or -errno. Buffer is 4k (ie. be short!) */
int (*get)(char *buffer, const struct kernel_param *kp);
/* Optional function to free kp->arg when module unloaded. */
void (*free)(void *arg);
};
- flags
- KERNEL_PARAM_OPS_FL_NOARG(1)을 사용하는 경우 value 값 없는 param값만 허용한다.
kparam_string 구조체
include/linux/moduleparam.h
/* Special one for strings we want to copy into */
struct kparam_string {
unsigned int maxlen;
char *string;
};
kparam_array 구조체
include/linux/moduleparam.h
/* Special one for arrays */
struct kparam_array
{
unsigned int max;
unsigned int elemsize;
unsigned int *num;
const struct kernel_param_ops *ops;
void *elem;
};
참고
- Earlycon & Earlyprintk
- parse_early_param() | 문c
- parse_args() | 문c – 현재 글
- Kernel Parameters | kernel.org

오류발견했어요 ㅎㅎ
그 외에 매치되지 않은 unknown 파라메터는 값이 없는 경우 envp_init[] 배열에 추가하고 값이 있는 경우 argv_init[] 배열에 추가한다.
->
그 외에 매치되지 않은 unknown 파라메터는 값이 있는 경우 envp_init[] 배열에 추가하고 값이 없는 경우 argv_init[] 배열에 추가한다.
안녕하세요? 문영일입니다.
아이쿠~~ 제가 거꾸로 표현했군요.
오류를 잡아주셔서 감사합니다.