__setupマクロ
__setupマクロはカーネルパラメータによって、動作させるモジュールを定義するマクロです。
start_kernel()からparse_args()でカーネルパラメータを取得し、それを引数としてunknown_bootoption()がコールされます。
[root@localhost lkm]#cat hogehoge.c
#define __attribute_used__ __attribute__ ((__unused__))
#define __initdata __attribute__ ((__section__ (".data.init")))
#define __init __attribute__ ((__section__ (".init.text"))
struct obs_kernel_param {
const char *str;
int (*setup_func)(char *);
};
#define __setup(str, fn) \
static char __setup_str_##fn[] __initdata = str; \
static struct obs_kernel_param __setup_##fn \
__attribute_used__ \
__attribute__((__section__(".init.setup"))) \
= { __setup_str_##fn, fn }
static int init_hoge(void)
{
return 0;
}
__setup("abcd", init_hoge);
マクロ展開してみる。
[root@localhost lkm]#gcc -E hogehoge.c
struct obs_kernel_param {
const char *str;
int (*setup_func)(char *);
};
static int init_hoge(void)
{
return 0;
}
static char __setup_str_init_hoge[] __attribute__ ((__section__ (".data.init"))) = "abcd";
static struct obs_kernel_param __setup_init_hoge __attribute__((__unused__))
__attribute__((__section__(".init.setup"))) ={ __setup_str_init_hoge, init_hoge };
ちょっと引いてしまいそうですが、要は以下の内容で、__setup_str_init_hoge[]は.data.initに、 __setup_init_hogeは.init.setupセクションに配置する。と言う事です。__setup_init_hogeは、先の例では、abcd"およびinit_hoge()のアドレスが設定されています。なお、__attribute__((__unused__))はこの変数が参照されなくてもワーニングを表示させない。と言うものだそうです。
static char __setup_str_init_hoge[] = "abcd";
static struct obs_kernel_param __setup_init_hoge ={ __setup_str_init_hoge, init_hoge };
start_kernel()からparse_args()でカーネルパラメータを取得し、それを引数としてunknown_bootoption()がコールされます。
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern char saved_command_line[];
extern struct kernel_param __start___param[], __stop___param[];
:
:
printk("Kernel command line: %s\n", saved_command_line);
parse_args("Booting kernel", command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
:
:
}
unknown_bootoption()のobsolete_checksetup()で、カーネルパラメータにかかる処理を行います。OKなら0が返ってきますが、そうでないとパラーメータエラーとして移行の処理がなされます。
static int __init unknown_bootoption(char *param, char *val)
{
if (val)
val[-1] = '=';
if (obsolete_checksetup(param))
return 0;
if (strchr(param, '.') && (!val || strchr(param, '.') < val)) {
printk(KERN_ERR "Unknown boot option `%s': ignoring\n", param);
return 0;
}
:
:
return 0;
}
obsolete_checksetup()で__setup(str, fn)マクロで定義された初期化関数をコールします。__setup_start/__setup_endには.init.setupセクションのアドレスが設定されています(たぶん)。そのポインターをstruct obs_kernel_param pとして、カーネルパラメータとp->strが一致するp->setup_func()をコールしています。
static int __init obsolete_checksetup(char *line)
{
struct obs_kernel_param *p;
extern struct obs_kernel_param __setup_start, __setup_end;
p = &__setup_start;
do {
int n = strlen(p->str);
if (!strncmp(line,p->str,n)) {
if (p->setup_func(line+n))
return 1;
}
p++;
} while (p < &__setup_end);
return 0;
}
p->setup_func()の引数は、一致した終端+1以降を引数としています。従って__setup(hogehoge, fn)なら、""を引数で、__setup(hogehoge=, fn)なら、パラメータの設定した=以降の内容を引数としてfnをコールすると言う事です。



