강좌 & 팁
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]
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 처음 부분입니다.
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 함수 호출 전까지 정상적으로 진행되는지를 체크하기 위해서 다음과 같은 처리를 합니다.
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]
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 커널 부팅 추적을 할 예정이므로 그때
다시 한번 들어 보시는 것도 괜찮을 듯 합니다.