ARM Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

203239
1625
172600


ARM64 - Data Abort Exception(익셉션) Vector 실행 이제는 ARM의 시대

이번 시간에는 Abort 즉 프로그램에 문제가 생겨서 Exception이 생겼을 때 동작 흐름에 대해
점검해보고자 해요.

ARM에 대한 자료와 세미나를 통해 Exception에 대해서 엄청나게 많이 들었잖아요.
뭐, data abort, prefetch abort, undefined abort 등등이죠.

이제 ARM64 아키텍처에서는 이 Abort를 어떻게 처리하고 있는 지 살펴보도록 할께요 

우선 아래 코드와 같이 익셉션 벡터가 정의되어 있구요.
그런데 abort가 발생할 때는 el1_sync 벡터로 프로그램 카운터가 이동하게 되어 있어요.
/*
 * Exception vectors.
 */

.align 11
ENTRY(vectors)
ventry el1_sync_invalid // Synchronous EL1t
ventry el1_irq_invalid // IRQ EL1t
ventry el1_fiq_invalid // FIQ EL1t
ventry el1_error_invalid // Error EL1t

ventry el1_sync // Synchronous EL1h  //<<--
ventry el1_irq // IRQ EL1h
ventry el1_fiq_invalid // FIQ EL1h
ventry el1_error_invalid // Error EL1h

ventry el0_sync // Synchronous 64-bit EL0
ventry el0_irq // IRQ 64-bit EL0
ventry el0_fiq_invalid // FIQ 64-bit EL0
ventry el0_error_invalid // Error 64-bit EL0

#ifdef CONFIG_COMPAT
ventry el0_sync_compat // Synchronous 32-bit EL0
ventry el0_irq_compat // IRQ 32-bit EL0
ventry el0_fiq_invalid_compat // FIQ 32-bit EL0
ventry el0_error_invalid_compat // Error 32-bit EL0
#else
ventry el0_sync_invalid // Synchronous 32-bit EL0
ventry el0_irq_invalid // IRQ 32-bit EL0
ventry el0_fiq_invalid // FIQ 32-bit EL0
ventry el0_error_invalid // Error 32-bit EL0
#endif
END(vectors)

그럼 보통 커널은 EL1(Exception Level 1)에서 돌고 있으니 el1_sync 코드를 살펴볼께요.

[1]: "mrs x1, esr_el1" 코어 프로세스 명령어로 신드롬 레지스터 정보를 얻어와요.
        보통 Abort가 발생할 때 ARM 프로세스가 Abort 정보를 저장해 놓거든요.
[2]: 26 비트만큼 레지스터를 왼쪽으로 Shift(ESR_ELx_EC_SHIFT) 시켜서 exception class 정보를 얻어와요. 
[3]: Data abort인 경우 el1_da 심볼로 점프
[4]: Undefined Instruction인 경우 el1_undef  심볼로 점프
[5]: stack alignment exception인 경우(보통 스택이 깨졌을 때) el1_sp_pc 심볼로 점프
[6]: pc alignment exception인 경우 el1_sp_pc 심볼로 점프
      ARM64 비트 아키텍쳐에서 프로그램 카운터의 마지막 바이트은 당연히 0x0, 0x8 중의 하나인데,
       다른 값이면 이 Exception Vector로 동작해요. 함수 포인터로 콜백함수를 처리할 때 오동작하는 경우가 있어요.
[7]: unknown exception인 경우 el1_undef  심볼로 점프
[8]: debug exception인 경우 el1_dbg 심볼로 점프
/*
 * EL1 mode handlers.
 */
.align 6
el1_sync:
kernel_entry 1
mrs x1, esr_el1 // read the syndrome register //<<--[1]
lsr x24, x1, #ESR_ELx_EC_SHIFT // exception class  //<<--[2]
cmp x24, #ESR_ELx_EC_DABT_CUR // data abort in EL1
b.eq el1_da //<<--[3]
cmp x24, #ESR_ELx_EC_SYS64 // configurable trap
b.eq el1_undef //<<--[4]
cmp x24, #ESR_ELx_EC_SP_ALIGN // stack alignment exception
b.eq el1_sp_pc //<<--[5]
cmp x24, #ESR_ELx_EC_PC_ALIGN // pc alignment exception
b.eq el1_sp_pc //<<--[6]
cmp x24, #ESR_ELx_EC_UNKNOWN // unknown exception in EL1
b.eq el1_undef //<<--[7]
cmp x24, #ESR_ELx_EC_BREAKPT_CUR // debug exception in EL1
b.ge el1_dbg //<<--[8]
b el1_inv

Data Abort
do_mem_abort() 함수로 점프하네요.
do_mem_abort -> arm64_notify_die -> die
el1_da:
/*
* Data abort handling
*/
mrs x3, far_el1
enable_dbg
// re-enable interrupts if they were enabled in the aborted context
tbnz x23, #7, 1f // PSR_I_BIT
enable_irq
1:
clear_address_tag x0, x3
mov x2, sp // struct pt_regs
bl do_mem_abort

// disable interrupts before pulling preserved data off the stack
disable_irq
kernel_exit 1

PC/Stack alligment abort
do_sp_pc_abort() 함수로 점프하는군요.
아래 코드 흐름으로 die 함수가 호출되어 각종 디버깅 정보(프로세스, 콜 트레이스)을 출력하고 리셋이 되죠.
do_sp_pc_abort -> arm64_notify_die -> die
el1_sp_pc:
/*
* Stack or PC alignment exception handling
*/
mrs x0, far_el1
enable_dbg
mov x2, sp
b do_sp_pc_abort

Undefined Instruction abort
아래 코드 흐름으로 die 함수가 호출되어 각종 디버깅 정보(프로세스, 콜 트레이스)을 출력하고 리셋되요.
do_undefinstr -> arm64_notify_die -> die
el1_undef:
/*
* Undefined instruction
*/
enable_dbg
mov x0, sp
b do_undefinstr

Debug exception
아래 코드 흐름으로 die가 처리되요.
do_debug_exception -> arm64_notify_die -> die
el1_dbg:
/*
* Debug exception handling
*/
cmp x24, #ESR_ELx_EC_BRK64 // if BRK64
cinc x24, x24, eq // set bit '0'
tbz x24, #0, el1_inv // EL1 only
mrs x0, far_el1
mov x2, sp // struct pt_regs
bl do_debug_exception
kernel_exit 1


핑백

덧글

댓글 입력 영역