parse_args()

<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);
    • 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() 함수

 

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;

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, &param_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, &param_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, &param_ops_uint, &skip_txen_test, 0644);
    • __modul_param_call(“8250_core”, skip_txen_test, &param_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, &param_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, &param_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;
};

 

참고

 

2 thoughts to “parse_args()”

  1. 오류발견했어요 ㅎㅎ
    그 외에 매치되지 않은 unknown 파라메터는 값이 없는 경우 envp_init[] 배열에 추가하고 값이 있는 경우 argv_init[] 배열에 추가한다.
    ->
    그 외에 매치되지 않은 unknown 파라메터는 값이 있는 경우 envp_init[] 배열에 추가하고 값이 없는 경우 argv_init[] 배열에 추가한다.

댓글 남기기