星期五, 12月 16, 2005

uclinux : 再找一次,Kernel command line: 的地方

想知道Kernel 怎麼依照boot argument 去找root file system,其實是想要再試試 root NFS。
boot message 有一行:
Kernel command line : root=/dev/blkmem/0
所以先找到"Kernel command line" 這一行是從那裡出來的,因為是boot messsage,所以從init 開始找:
$ cd init
$ grep "Kernel command line" *.c
結果告訴我是main.c,less main.c 用 " /Kernel command" 找到..
        setup_arch(&command_line);
printk("Kernel command line: %s\n", saved_command_line);
command_line 是local variable。所以取得位置是在setup_arch(),找..一個一個目錄的找..
        $ grep setup_arch arch -r
出現真的在arch 目錄。到arch/arm
        $ grep setup_arch * -r
原 來是在kernel/setup.c.. parse_cmdline( )把meminfo 的資料處理後,交給cmdline_p,parse_cmdline 處理boot argument的 "mem="和"initrd=" 兩個argument。所以setup_arch只是將boot argument 從public static variable中取出,交給這個cmdline_p,並且處了他需要知道的幾個boot argument。
include/linux/init.h:  extern struct kernel_parm__setup_start, __setup_end;
然後在
arch/armnommu/vmlinux-arm0.lds.in:   __setup_start = .;
和vmlinux.lds , vmlinux-armv.lds.in 也有。 所以應該又是用類似boot loader一樣,在link時將init argument -- init function pair locate 在image特定位置。

因為是lds - link script,所以找Makefile ,看看最後的link command.....
Linux 分成boot, init 和 ? N 大塊target。最後link起來成為linux kernel。
Makefile 最後將這部份產生System.map 這個map file,所以查查這個map file 裡面的symbol table,是不是有__setup_start,__setup_end這兩個mark...
900a2300 ? __setup_start
900a2308 ? __setup_debug_kernel
900a2310 ? __setup_quiet_kernel
900a2318 ? __setup_load_ramdisk
900a2320 ? __setup_readonly
900a2328 ? __setup_readwrite
900a2330 ? __setup_root_dev_setup
900a2338 ? __setup_root_data_setup
900a2340 ? __setup_fs_names_setup
900a2348 ? __setup_nohlt_setup
900a2350 ? __setup_hlt_setup
900a2358 ? __setup_reboot_setup
.......
900a23e0 ? __setup_netdev_boot_setup
900a23e8 ? __setup_netdev_boot_setup
900a23f0 ? __initcall_alignment_init
900a23f0 ? __initcall_start
900a23f0 ? __setup_end
果 然有,找一下__setup_root_dev_setup 這一個symbol : 在init/do_mounts.c,要用root_dev_setup 來找,因為__setup_ 開頭字樣是在所有function 宣告完後,用__setup ( )macro重新安排過,產生一個kernel_param 的structure 資料,放在 .setup.init 節區。
在include/linux/int.h 中定義了..
#define __setup(str, fn)   static char __setup_str_##fn[]                            __initdata = str;    static struct kernel_param  __setup_##fn  __attribute__((unused)) __initsetup = { __setup_str_##fn, fn }
.....
#define __initsetup __attribute__ ((unused,__section__ (".setup.init")))
這樣的定義,配合do_mounts.c中的..
static int __init root_dev_setup(char *line)
{
int i;
char ch;

ROOT_DEV = name_to_kdev_t(line);
memset (root_device_name, 0, sizeof root_device_name);
if (strncmp (line, "/dev/", 5) == 0) line += 5;
for (i = 0; i < sizeof root_device_name - 1; ++i)
{
ch = line[i];
if ( isspace (ch) || (ch == ',') || (ch == '\0') ) break;
root_device_name[i] = ch;
}
return 1;
}

__setup("root=", root_dev_setup);
將root_dev_setup function和"root=" 字串放在 .setup.init 節區。這樣,所有boot argument 字串和setup function,自動就會形成一個array,排列在__setup_start, __setup_end 之間。
init/main.c 就可以用:
static int __init checksetup(char *line)
{
struct kernel_param *p;

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;
}
一一check boot command字串內容是否有符合,call 對應的function。
所以我要找的應該是"root="字串,function是 root_dev_setup ---- name_to_kdev_t ,
使用root_dev_names[],裡面第一項就是"nfs",也就是說boot argument 是"root=/dev/nfs"
查這個name_to_kdev_t function就是要找到boot device的device number。
交給root_dev_setup設定給ROOT_DEV這個變數。

這樣查code發現,經過linker 作安排的code,要trace比較不容易。但是很多code好像都是這樣,像LinuxBIOS的init function也是。用function array的方式,一個一個call。
這樣的方法有一個問題是,array中的function 不可以有依賴關係。也就是 先後次序。因為用loader script 沒法安排啟動的順序。

沒有留言:

網誌存檔