kernel 은 bootloader 부터 각 세부사항들을 parameter 로 전달 받게 됩니다.
U-boot 를 쓰신다면 bootloader command mode 에서 printenv 로 출력해 볼 수 있지요.
보통 다음과 같은 형태 입니다.
# printenv
bootcmd=if mmc rescan 1; then if run loaduimage; then run sdboot; else run ubifsboot; fi; else run ubifsboot; fi
bootdelay=0
baudrate=115200
ethaddr=8e:28:0f:fa:3c:39
ipaddr=192.168.129.3
serverip=192.168.129.1
gatewayip=192.168.129.1
netmask=255.255.255.0
updateb=onenand erase 0x0 80000; onenand write 32008000 0 80000; onenand erase 900000 100000; onenand write 32088000 900000 100000
updatek=onenand erase 0xc00000 0x600000; onenand write 0x31008000 0xc00000 0x600000
updateu=onenand erase 0x01560000 0x1eaa0000; onenand write 0x32000000 0x1260000 0x8C0000
flashboot=set bootargs root=/dev/mtdblock${rootfsblock} rootfstype=${rootfstype} ubi.mtd=${rootfsblock} ubi.mtd=${csablock} ubi.mtd=${modemblock} ${opts} ${lcdinfo} ${console} ${meminfo} ${mtdparts} ${bootmode} ${lpjinfo} ${uartpath}; run bootk
...
system 이 부팅하는데 필요한 중요한 내용들이 string 형식으로 저장되어 있습니다.
keren 은 부팅하면서 이러한 string 들을 parsing 하여 변수로 사용합니다.
이 과정에서 사용되는것이 early_param 매크로 입니다.
사용법 자체는 간단합니다.
static void __init early_mem(char **p)
{
...
}
__early_param("mem=", early_mem);
이렇게 등록만 해주면, boot param 중에 "mem=" 으로 시작되는 line 의 뒷 내용이 파라미터인 p에 저장되어서 early_mem 함수가 호출되게 됩니다.
사용법만 궁금하신 분들은 여기까지만,
이 이후는 호출 알고리즘까지 궁금해 하시는 분들을 위해서 입니다.
위에 까지만 보고 끝내시는 분은 발전이 없겠죠.?
우선 메크로 정의를 봅시다.
#define __early_param(name,fn) \
static struct early_params __early_##fn __used \
__attribute__((__section__(".early_param.init"))) = { name, fn }
static struct early_params __early_##fn __used \
__attribute__((__section__(".early_param.init"))) = { name, fn }
즉, 위 선언은 이렇게 해석 됩니다.
__early_param("mem=", early_mem);
static struct early_params __early_early_mem __used __attribute__((__section__(".early_param.init"))) = { "mem=", early_mem }
early_param 구조체는 단순하게 check 할 string 과 그에 대응해서 호출될 함수만을 인자고 가지고 있습니다.
struct early_params {
const char *arg;
void (*fn)(char **p);
};
전에 강좌에서 쓴적이 있는데 __section__ 은 해당 변수가 ram 에 위치할때 쓰일 symbol table 에서 특정 section 에 저장하라는 의미 입니다.
struct early_params 의 객체들을 .early_param.init section 들에 저장하고 있습니다.
.early_param.init 섹선을 찾아봅시니다.
< linux-2.6.29.4/arch/arm/kernel/vmlinux.lds >
...
*(.init.setup)
__setup_end = .;
__early_begin = .;
*(.early_param.init)
__early_end = .;
...
.early_param.init section 이 정의 되어 있고. 위 아래로 각각 __early_begin 과 __early_end 변수에 값을 대입하고 있습니다.
ldx 파일에서 '.' 은 현재 주소 입니다.
종종 사용되니 잊지 마세요.
자 이정도면 해당 mecro 의 의미도 해석되었고 ram 에 올리는 section 도 찾아 보았으니, 호출하는 곳을 살펴 봅시다. boot param 은 다음 위치에서 parsing 됩니다.
init_thread_union(asm) --> start_kernel() --> setup_arch() --> parse_cmdline()
static void __init parse_cmdline(char **cmdline_p, char *from)
{
...
extern struct early_params __early_begin, __early_end;
struct early_params *p;
...
for (p = &__early_begin; p < &__early_end; p++) {
int arglen = strlen(p->arg);
if (memcmp(from, p->arg, arglen) == 0) {
if (to != command_line)
to -= 1;
from += arglen;
p->fn(&from);
...
}
}
}
함수를 보면 위에 lds 에서 저장했던 __early_begin과 __early_end 를 사용해서 저장된 early_param 들을 for 문으로 탐색하고 있습니다.
각각을 탐색하면서 미리 저장된 string 을 memcmp 로 비교해 가면서 일치한다면 저장된 fn 을 호출해 주는 것이지요. 이로써 비교적 단순하게 parsing 함수들을 호출 할 수 있습니다.
= 2010.12.22 내용 추가 =
__early_param() 과 비슷한 역할을 하는 또하나의 커맨드가 있습니다.
__setup() 입니다.
boot param 중 "console=" 과 "earlycon=" 를 parse 할때 사용됩니다.
위에서 자세히 분석을 하였으니 setup() 은 단순히 관련 소스들만 올려봅니다.
#define __setup_param(str, unique_id, fn, early) \
static const char __setup_str_##unique_id[] __initconst \
__aligned(1) = str; \
static struct obs_kernel_param __setup_##unique_id \
__used __section(.init.setup) \
__attribute__((aligned((sizeof(long))))) \
= { __setup_str_##unique_id, fn, early }
#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)
static const char __setup_str_##unique_id[] __initconst \
__aligned(1) = str; \
static struct obs_kernel_param __setup_##unique_id \
__used __section(.init.setup) \
__attribute__((aligned((sizeof(long))))) \
= { __setup_str_##unique_id, fn, early }
#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)
< vmlinux.lds >
__setup_start = .;
*(.init.setup)
__setup_end = .;
__early_begin = .;
*(.early_param.init)
__early_end = .;
__setup_start = .;
*(.init.setup)
__setup_end = .;
__early_begin = .;
*(.early_param.init)
__early_end = .;
< init/main.c >
static int __init do_early_param(char *param, char *val)
{
...
for (p = __setup_start; p < __setup_end; p++) {
p->setup_func(val)
}
...
}
static int __init do_early_param(char *param, char *val)
{
...
for (p = __setup_start; p < __setup_end; p++) {
p->setup_func(val)
}
...
}
< init/main.c >
asmlinkage void __init start_kernel(void)
{
...
setup_arch(&command_line); // -->parse_cmdline()
...
parse_early_param(); // -> parse_early_options(tmp_cmdline); -> do_early_param()
...
}
asmlinkage void __init start_kernel(void)
{
...
setup_arch(&command_line); // -->parse_cmdline()
...
parse_early_param(); // -> parse_early_options(tmp_cmdline); -> do_early_param()
...
}
'Programming > Linux_Kernel' 카테고리의 다른 글
GPIO control 을 user 에 넘기는 세련된 방법 (0) | 2010.12.14 |
---|---|
linux file system 용어 - UBI (0) | 2010.11.26 |
memory mapping 전에 serial print 사용하기 (0) | 2010.11.23 |
kernel 초기화시 지켜야 할 Ram memory size align 단위 (2) | 2010.11.12 |
register_mtd_user() - partition에 융통성 있는 driver 구현하기 (0) | 2010.10.20 |