__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をコールすると言う事です。