ARM 커널 부팅 관련 파일들

 

커널 추적을 위한 디버그 함수는 준비 되었습니다.

그런데..

여러분은 어디서부터 추적해야 할지 암담하죠?

예…

저도 처음엔 그랬습니다.

지금요?

흑…

지금도 그래요…

그러나

이번 기회에 심플하게 어떤 파일들이 부팅 초기에 관여하는지를 초 간단 버전으로 보겠습니다.

정답을 미리 알려 드리는 것이 편하겠지요?

arch/arm/kernel/head.S
arch/arm/kernel/head-common.S
init/main.c

이 세가지 파일이 뒤져야 하는 핵심입니다.

그 외에 항상 보아야 하는 것은 플랫폼 셋업 파일이죠..

현재 IDP 보드로 살펴 보고 있으니

arch/arm/mach-pxa/idp.c

이 파일이 되겠습니다.

 

추적을 위한 어셈블러용 매크로 선언

커널이 어떤 위치를 통과 하는지 구경하려면 지정된 위치 정보를 문자열로 시리얼을 통해서 출력하면 좋겠죠?

그래서 이전에 시험했던 루틴을 조금 손봐서 만들어 봤습니다.

#define STRINGIFY(x)                    #x
#define TOSTRING(x)                     STRINGIFY(x)

#define TRACE_LINE(ra,rb,rc)            mov     ra,r0;                  \
                                        mov     rb,r1;                  \
                                        mov     rc,r2;                 \
                                                                        \
                                        adr     r0,9998f;               \
                                        bl      printascii ;            \
                                                                        \
                                        mov     r2,rc;                 \
                                        mov     r1,rb;                  \
                                        mov     r0,ra;                  \
                                                                        \
                                        b       9999f;                  \
                                9998: ;                                 \
                                        .ascii  "\n>>TRACE:";           \
                                        .ascii  __FILE__;               \
                                        .ascii  ":";                    \
                                        .asciz  TOSTRING(__LINE__);     \
                                        .align;                         \
                                9999: ;


이걸 쓸데는 TRACE_LINE 을 사용하고 ra,rb,rc 파라메터는
출력시 파괴되는 R0,R1,R2를 임시로 대피할 레지스터를 지정하면 됩니다.

부팅 초기에는 R8,R11,R12 레지스터를 추천합니다.

요건 어디에 놓으면 좋을까요?

헤더파일에 넣어 놓으면 좋지 않을까요?

그래서 다음 파일에 넣어 둡니다.

arch/arm/include/asm/ptrace.h

다음은 편집된 내용입니다.

 

[arch/arm/include/asm/ptrace.h]
 
  157 #define STRINGIFY(x)                    #x
    158 #define TOSTRING(x)                     STRINGIFY(x)
    159
    160 #define TRACE_LINE(ra,rb,rc)            mov     ra,r0;                  \
    161                                         mov     rb,r1;                  \
    162                                         mov     rc,r2;                 \
    163                                                                         \
    164                                         adr     r0,9998f;               \
    165                                         bl      printascii ;            \
    166                                                                         \
    167                                         mov     r2,rc;                 \
    168                                         mov     r1,rb;                  \
    169                                         mov     r0,ra;                  \
    170                                                                         \
    171                                         b       9999f;                  \
    172                                 9998: ;                                 \
    173                                         .ascii  "\n>>TRACE:";           \
    174                                         .ascii  __FILE__;               \
    175                                         .ascii  ":";                    \
    176                                         .asciz  TOSTRING(__LINE__);     \
    177                                         .align;                         \
    178                                 9999: ;
    179
    180 #endif
    181


arch/arm/kernel/head.S  추적

 

이 파일은 커널이 가장 처음 시작되는 부분이죠..

뭐 하는 파일이냐구요?

프로세서를 초기화 하는 것과 커널용 1차 MMU테이블 작성이 주된 일이죠

그럼 멈춘 원인을 발견하기 위해서는 어떤 부분을 찍어가야 할까요?

솔찍하게 말씀 드리면

이 글을 쓰는 시점에 저는 대략 원인을 알고 있습니다.

그런데 정답을 알려 주면 이전 강좌에 기껏 말한

커널 추적 디버그 함수를 쓸 일이 없잖아요..

그래서

중요 포인터에 커널 표출 메시지를 삽입하고 통과 되지 않는 부분을 알려 주려 하는 것이지요

이 부분에서 통과 하지 않으면 이 부분이 아마도 원인일 것이다.

이런식으로요…

그리고 커널이 어떤 부팅 흐름을 가지는 가도 보여 주려는 것 이지요…

 

●  ENTRY(stext)

 

이 부분은 커널 시작되면서 진입하는 위치죠.

진입 여부는 이전 강좌에 표출하는 예제를 통해 확인 되었죠.

이 부분은 다음과 같은 처리를 합니다.

 

1) 프로세서 타입을 얻어 옵니다.
2) 머신 정보를 확인합니다.
3) 파라메터 테이블인 ATAG의 정렬상태와 같은 오류를 검사합니다.
4) 가장 기본적인 MMU 테이블을 만듭니다.

 

이게 어디까지 있냐면


 

bl __create_page_tables

 

이 함수가 호출되는 곳 까지 입니다.

그래서 이 부분 다음에 다음 같은 메시지를 끼어 넣고 표출해 봅니다.

 이전에 선언된 TRACE_LINE 이라는 매크로를 써 넣습니다.

다음은 TRACE_LINE을 넣은 head.S 처음 부분입니다.

 

   
     78         .section ".text.head", "ax"
     79 ENTRY(stext)
     80
     81         TRACE_LINE(r8,r11,r12)
     82
     83         msr     cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
     84                                                 @ and irqs disabled
     85         mrc     p15, 0, r9, c0, c0              @ get processor id
     86         bl      __lookup_processor_type         @ r5=procinfo r9=cpuid
     87         movs    r10, r5                         @ invalid processor (r5=0)?
     88         beq     __error_p                       @ yes, error 'p'
     89         bl      __lookup_machine_type           @ r5=machinfo
     90         movs    r8, r5                          @ invalid machine (r5=0)?
     91         beq     __error_a                       @ yes, error 'a'
     92         bl      __vet_atags
     93         bl      __create_page_tables
     94
     95      TRACE_LINE(r8,r11,r12)
     96
     97         /*
     98          * The following calls CPU specific code in a position independent
     99          * manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of
    100          * xxx_proc_info structure selected by __lookup_machine_type
    101          * above.  On return, the CPU will be ready for the MMU to be
    102          * turned on, and r0 will hold the CPU control register value.
    103          */
    104         ldr     r13, __switch_data              @ address to jump to after
    105                                                 @ mmu has been enabled
    106         adr     lr, __enable_mmu                @ return (PIC) address
    107         add     pc, r10, #PROCINFO_INITFUNC
    108
    109         TRACE_LINE(r8,r11,r12)
    110
    111 ENDPROC(stext)


커널이 실행되면 109 번 행에 넣은 부분은 절대로 출력 되면 안됩니다.

위 루틴은 프로세서 초기화를 수행하고 107 번 라인 부분이 수행 된 후

__mmap_switched  를 호출하기 때문입니다.


81 문장 라인번호를 출력하고 95번 행을 출력하지 않는다면 이 경우는 대부분 머신 ID 설정의 문제입니다.

이제 커널을 컴파일 하고 보드에 다운로드 하고 실행하면 다음과 같은 메시지가 나옵니다.

>>TRACE:arch/arm/kernel/head.S:81
>>TRACE:arch/arm/kernel/head.S:95

결국 여기까지는 정상적으로 진행 된다는 것이죠….


 

arch/arm/kernel/head-common.S   추적

head.S에서 기본적인 초기화 및 mmu가 활성화 된 이후에 head-common.S 로 부팅은 진행됩니다.

이 파일에 __mmap_switched  가 수행되는 데 이 루틴은 start_kernel 이라는 C 함수를 호출하기 전 C 함수 호출이 가능하도록 스택이나 BSS 와 같은 메모리 구조들을 정리합니다. 그리고 start_kernel 함수를 호출합니다.

start_kernel 함수 호출 전까지 정상적으로 진행되는지를 체크하기 위해서 다음과 같은 처리를 합니다.

 

   
39 __mmap_switched:
     40
     41         TRACE_LINE(r8,r11,r12)
     42
     43         adr     r3, __switch_data + 4
     44
     45         ldmia   r3!, {r4, r5, r6, r7}
     46         cmp     r4, r5                          @ Copy data segment if needed
     47 1:      cmpne   r5, r6
     48         ldrne   fp, [r4], #4
     49         strne   fp, [r5], #4
     50         bne     1b
     51
     52         mov     fp, #0                          @ Clear BSS (and zero fp)
     53 1:      cmp     r6, r7
     54         strcc   fp, [r6],#4
     55         bcc     1b
     56
     57         ldmia   r3, {r4, r5, r6, r7, sp}
     58         str     r9, [r4]                        @ Save processor ID
     59         str     r1, [r5]                        @ Save machine type
     60         str     r2, [r6]                        @ Save atags pointer
     61         bic     r4, r0, #CR_A                   @ Clear 'A' bit
     62         stmia   r7, {r0, r4}                    @ Save control register values
     63
     64         TRACE_LINE(r8,r11,r12)
     65
     66         b       start_kernel
     67 ENDPROC(__mmap_switched)
     68


이제 커널을 컴파일 하고 보드에 다운로드 하고 실행하면 다음과 같은 메시지가 나옵니다.

 

>>TRACE:arch/arm/kernel/head.S:81
>>TRACE:arch/arm/kernel/head.S:95
>>TRACE:arch/arm/kernel/head-common.S:41
>>TRACE:arch/arm/kernel/head-common.S:64


결국 여기까지는 정상적으로 진행 된다는 것이죠….

 

추적을 위한 C 매크로 선언

C 에서 printk를 사용하지 않고 어셈블러에서 제공하는 시리얼용 디버그 용 함수를 사용하여 위치 정보를 문자열 출력하는  매크로 다음과 같이 만들었습니다.


extern void printascii( char *str );

#define STRINGIFY(x)                    #x
#define TOSTRING(x)                     STRINGIFY(x)

#define TRACE_LINE      printascii( "\n>>TRACE " __FILE__ ":");\
                                           printascii( __FUNCTION__ );\
                                           printascii( ":" TOSTRING(__LINE__) );


 이걸 어디에 놓으면 좋을까요?

다음 파일에 넣어 둡니다.

include/linux/init.h

다음은 편집된 내용입니다.

[include/linux/init.h]

   

160
    161 #ifndef __ASSEMBLY__
    162
    163 extern void printascii( char *str );
    164
    165 #define STRINGIFY(x)    #x
    166 #define TOSTRING(x)     STRINGIFY(x)
    167
    168 #define TRACE_LINE      printascii( "\n>>TRACE " __FILE__ ":");\
    169                         printascii( __FUNCTION__ );\
    170                         printascii( ":" TOSTRING(__LINE__) );
    171

init/main.c 추적 

 head-common.S  파일에 있는 __mmap_switched  가 마지막으로 호출하는 것은 init/main.c  파일에 start_kernel() 함수 입니다.

이 함수까지 오는지를 앞에서 만든 매크로를 활용하여 확인해 봅시다.

542 asmlinkage void __init start_kernel(void)
    543 {
    544         char * command_line;
    545         extern struct kernel_param __start___param[], __stop___param[];
    546
    547         TRACE_LINE
    548
    549         smp_setup_processor_id();
    550

 

커널을 올리고 부팅하면 다음과 같은 메시지가 표출 됩니다.

>>TRACE:arch/arm/kernel/head.S:81
>>TRACE:arch/arm/kernel/head.S:95
>>TRACE:arch/arm/kernel/head-common.S:41
>>TRACE:arch/arm/kernel/head-common.S:64
>>TRACE init/main.c:start_kernel:547


여기까지는 정상적으로 동작합니다.

아마도 인터럽트 , 디바이스 드라이버, 커널 커맨드에서 콘솔 지정에 문제가 일 가능성이 높습니다.

아예 부팅이 되지 않고 있는 것이 아니니까요 ..

더 추적하고 싶지만…

갈길이 바쁘니…

추적은 여기서 중지 합니다.

추적 방법을 알려 드렸으니 여러분 나름대로 쭈욱 찾아가 보는 것도 좋죠…

아니면 이번 세미나 때 제가 ARM 커널 부팅 추적을 할 예정이므로 그때
다시 한번 들어 보시는 것도 괜찮을 듯 합니다.