본문 바로가기

Programming/Linux_Kernel

boot param의 해석 - __early_param, __sertup mecro 분석


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 }


즉, 위 선언은 이렇게 해석 됩니다.

__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)

< vmlinux.lds >

  __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)
     }
...
}

< 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()
...
}