강좌 & 팁
너무 오래동안 진행이 멈추었던 점 용서해 주십시오
먹고 사니즘 땜시 스케쥴에 나름대로 어려움이 있었답니다.
ARM 커널 부팅 과정 디버깅 메시지 시리얼로 보기
서 손을 보아야 할지
고민 고민 했습니다.
이렇게 부팅이 되지 않는 경우에는 삽질이라는 과정이 필요한데
제 나이쯤 되면 이런 삽질이 영 달갑지 않습니다.
하드웨어에 도통한 분들이 스코프 들기 싫은 것처럼
커널 포팅에 도통한 저는 디버그 메시지 찍어 가면서
어디가 문제인지 알아 보는 것이 정말 싫거든요…
(하하 건방에 용서를 굽실 굽실… )
더구나 비축 분도 떨어졌죠… 요즘 쪼메 바빴거든요..
여러모로 암담합니다.
어찌되었든 커널 메시지를 뿌리는 printk 가 동작이 제대로 동작하지 않는 상황
즉 console 디바이스가 동작 하지 않는 경우에는 커널 부팅을 추적할 때
어떻게 해야 할지 막막 하지요…
그..러..나
ARM 리눅스 커널 만든 사람들 역시 이런 고생을 하지 않았겠습니까?
그들도 우리와 같은 개발자인데 말이죠
그런 이유로 이런 부분의 문제점을 해결하기 위한 장치를 마련 했답니다.
여러분은 이 방법을 알아야 디버깅이 가능합니다.
또한 커널이 압축이 해제 된 이후 어떻게 동작하는지 추적하려면
시리얼로 메시지를 표출하는 방법에 대하여 반드시 알아야 합니다.
제가 이 방법을 알려 드리겠습니다.
커널 컴파일 옵션 설정
이 과정은 이전에 설명한 부분인데 다시 한번 강조 해야 할 듯 합니다.
우선 커널 컴파일 옵션에서 다음과 같이 체크되어 있어야 합니다.
Kernel hacking --->
[*] Kernel low-level debugging functions
[A015_010_kernel_hacking_option.png]
[A015_020_kernel_hacking_low_level_option.png]
debug.S 관련 소스 수정
ARM 에서는 커널 부팅 중 console 장치가 아닌 시리얼 장치를 이용하여 부팅 단계에서 문자열을 출력 하고자 하다면 다음 파일이 이를 처리해 주는 루틴을 가지고 있습니다.
arch/arm/kernel/debug.S
이 파일에는 다음과 같은 함수가 선언되어 있습니다.
printhex8 : R0 또는 첫번째 매계변수의 값을 8 자리 16진수 문자열로 표시
printhex4 : R0 또는 첫번째 매계변수의 값을 4 자리 16진수 문자열로 표시
printhex2 : R0 또는 첫번째 매계변수의 값을 2 자리 16진수 문자열로 표시
printascii : R0 또는 첫번째 매계변수의 값이 가리키는 문자열 주소를 이용하여 \0 문자로 끝나는 문자열 표시
printch : R0 또는 첫번째 매계변수의 값에 해당하는 문자를 표시
이 함수들은 어셈블러 루틴에서 호출 할 수도 있고
C 함수로도 호출이 가능합니다.
이 함수들이 호출하는 매크로가 몇 개 있는데 각 보드마다 이 부분을 수정해 주어야 합니다.
다음과 같은 이름을 가지고 있고 처리하는 기능은 다음과 같습니다.
addruart : UART 장치의 선두 주소를 얻어 옵니다.
waituart : UART가 출력이 가능해 질 때까지 기다립니다.
senduart : UART 에 한 문자를 출력합니다.
busyuart : UART가 문자를 완전히 출력할 때까지 기다립니다.
이 함수는 어셈블러 매크로로 구성되고 개발자가 수정할 수 있습니다.
매크로는 각각 사용될 레지스터를 지정하면 됩니다.
우리는 우선적으로 adduart 매크로를 수정해야 합니다.
왜냐하면 설정된 IDP 보드는 시리얼 포트 0을 사용하지만 EZ-X5는 포트 2를 사용해야 하기 때문입니다.
그렇다면 이렇게 각각 다른 포트를 지정하는 것은 어디에 있을까요?
pxa 계열은 다음 헤더 파일에 선언하고 있습니다.
arch/arm/mach-pxa/include/mach/debug-macro.S
이 파일을 보면 addruart 매크로를 정의하고 있는데 rx 에 설정된 레지스터에 디버그용 시리얼 포트 주소 값을 설정합니다.
이때 주의 깊게 보아야 하는 것이 MMU 가 동작할 경우와 그렇지 않을 경우를 자동으로 설정하고 있는데 이때 사용되는 매크로가 io_p2v 라는 겁니다.
여기서는 그리 고민하지 않아도 됩니다만 그냥 커널의 어드레스 변환 함수로 한번쯤 기억하라고 말씀 드리는 겁니다.
어찌되었든 여기서 주소를 지정해 주어야 합니다.
원래는 이렇게 되어 있습니다.
.macro addruart,rx
mrc p15, 0, \rx, c1, c0
tst \rx, #1 @ MMU enabled?
moveq \rx, #0x40000000 @ physical
movne \rx, #io_p2v(0x40000000) @ virtual
orr \rx, \rx, #0x00100000
.endm
.macro addruart,rx
mrc p15, 0, \rx, c1, c0
tst \rx, #1 @ MMU enabled?
moveq \rx, #0x40000000 @ physical
movne \rx, #io_p2v(0x40000000) @ virtual
orr \rx, \rx, #0x00700000
.endm
head.S 와 디버그 문자열 출력 테스트
압축이 풀린 커널의 소스 중 가장 먼저 호출되는 위치는 다음 파일에 있습니다.
arch/arm/kernel/head.S
이 파일에 다음 부분이 커널의 최초 진입 점이죠
/*
* Kernel startup entry point.
* ---------------------------
*
* This is normally called from the decompressor code. The requirements
* are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
* r1 = machine nr, r2 = atags pointer.
*
* This code is mostly position independent, so if you link the kernel at
* 0xc0008000, you call this at __pa(0xc0008000).
*
* See linux/arch/arm/tools/mach-types for the complete list of machine
* numbers for r1.
*
* We're trying to keep crap to a minimum; DO NOT add any machine specific
* crap here - that's what the boot loader (or in extreme, well justified
* circumstances, zImage) is for.
*/
.section ".text.head", "ax"
ENTRY(stext)
이곳에 다음과 같이 TRACE OK 라는 문자열을 출력하는 루틴을 추가 합니다.
.section ".text.head", "ax"
ENTRY(stext)
// 추가 되는 소스 시작
adr r0, trace1_str
bl printascii
b __error
trace1_str : .asciz "\nTRACE OK"
.align
// 추가 되는 소스 끝
그리고 컴파일 한후 보드에 올려서 어떻게 출력되는지 확인 합니다.
[A015_030_kernel_debug_test_1.png]
정상적으로 표출되었죠?
후후..
일단 커널의 압축을 제대로 풀렸고 뭔가 진행은 된다는 것이 확인 되었네요..
도대체 어디서부터 멈추어 있는 것일까요?
오늘은 여기까지..
참.. 위 소스에 대한 설명은 굳이 하지 않겠습니다.
공부해 보세용.. 후후