Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

73261
1501
219115


[ARMv8] EL1: el1_irq - 익셉션 벡터 코드와 ARM 스팩 문서 분석 익셉션(Exception) 소개

저번 포스트에서는 el1_sync 이라는 익셉션 벡터 코드를 분석했습니다.
생각보다 코드의 내용이 복잡하다라는 사실을 알 수 있었는데요. 주요 동작은 다음과 같습니다.

   ● 신드롬 레지스터를 읽음 
   ● 신드롬 레지스터의 값을 26만큼 왼쪽으로 비트 시프트한 값에 따라 다음 레이블을 호출 
        ** el1_da
        ** el1_sp_pc
        ** el1_undef

이번에는 리눅스 커널 드라이버가 실행되는 도중 인터럽트가 발생하면 ARM 프로세서 입장에서 어떤 게 처리되는지 알아보고, 이어서 el1_irq 레이블을 분석하겠습니다.

EL1에서 IRQ 익셉션 벡터 코드

익셉션 벡터 테이블의 시작 주소 기준으로(VBAR_ELn + 0x280) 바이트에 Synchronous 익셉션의 코드가 위치합니다.

이번 포스트에서 분석할 익셉션 벡터 테이블의 시작 주소는 ffffff8008082000이므로,
EL1에서 IRQ 익셉션이 발생하면 다음 코드 기준으로 ffffff8008082280 주소로 프로그램 카운터가 브랜치합니다.

ffffff8008082280:   d10503ff    sub sp, sp, #0x140
ffffff8008082284:   8b2063ff    add sp, sp, x0
ffffff8008082288:   cb2063e0    sub x0, sp, x0
ffffff800808228c:   37700080    tbnz    w0, #14, ffffff800808229c <vectors+0x29c>
ffffff8008082290:   cb2063e0    sub x0, sp, x0
ffffff8008082294:   cb2063ff    sub sp, sp, x0
ffffff8008082298:   1400042a    b   ffffff8008083340 <el1_irq>
ffffff800808229c:   d51bd040    msr tpidr_el0, x0
ffffff80080822a0:   cb2063e0    sub x0, sp, x0
ffffff80080822a4:   d51bd060    msr tpidrro_el0, x0
ffffff80080822a8:   d0006060    adrp    x0, ffffff8008c90000 <overflow_stack+0xd50>
ffffff80080822ac:   910ac01f    add sp, x0, #0x2b0
ffffff80080822b0:   d538d080    mrs x0, tpidr_el1
ffffff80080822b4:   8b2063ff    add sp, sp, x0
ffffff80080822b8:   d53bd040    mrs x0, tpidr_el0
ffffff80080822bc:   cb2063e0    sub x0, sp, x0
ffffff80080822c0:   f274cc1f    tst x0, #0xfffffffffffff000
ffffff80080822c4:   540028a1    b.ne    ffffff80080827d8 <__bad_stack>
ffffff80080822c8:   cb2063ff    sub sp, sp, x0
ffffff80080822cc:   d53bd060    mrs x0, tpidrro_el0
ffffff80080822d0:   1400041c    b   ffffff8008083340 <el1_irq>
ffffff80080822d4:   d503201f    nop
ffffff80080822d8:   d503201f    nop
ffffff80080822dc:   d503201f    nop

위 코드의 핵심은 "예외 처리 루틴을 체크한 다음에 el1_irq 레이블로 브랜치하는 동작"입니다.

el1_irq 레이블의 코드 분석

다음은 el1_irq 레이블 코드의 구현부입니다.

https://elixir.bootlin.com/linux/v5.4.30/source/arch/arm64/kernel/entry.S
01 el1_irq:
02     kernel_entry 1
03     gic_prio_irq_setup pmr=x20, tmp=x1
04     enable_da_f
05 
06 #ifdef CONFIG_ARM64_PSEUDO_NMI
07     test_irqs_unmasked  res=x0, pmr=x20
08     cbz x0, 1f
09     bl  asm_nmi_enter
10 1:
11 #endif
12
13 #ifdef CONFIG_TRACE_IRQFLAGS
14    bl  trace_hardirqs_off
15 #endif
16
17    irq_handler
18
19 #ifdef CONFIG_PREEMPT
20    ldr x24, [tsk, #TSK_TI_PREEMPT] // get preempt count
21 alternative_if ARM64_HAS_IRQ_PRIO_MASKING
22    /*
23     * DA_F were cleared at start of handling. If anything is set in DAIF,
24     * we come back from an NMI, so skip preemption
25     */
26    mrs x0, daif
27    orr x24, x24, x0
28 alternative_else_nop_endif
29    cbnz    x24, 1f             // preempt count != 0 || NMI return path
30    bl  arm64_preempt_schedule_irq  // irq en/disable is done inside
311:
32 #endif
33

코드를 보면 리눅스 커널 관점의 루틴이지, ARM 프로세서 관점으로 분석할만한 내용은 없습니다.
간단히 코드를 리뷰하겠습니다.

17번째 줄입니다.

17    irq_handler

은 인터럽트에 대한 처리를 위해 IRQ 서브 시스템을 호출합니다. 이 코드를 시작으로 호출되는 서브 루틴에서 인터럽트 핸들러가 호출됩니다.

19~30번째 줄을 보겠습니다.

19 #ifdef CONFIG_PREEMPT
20    ldr x24, [tsk, #TSK_TI_PREEMPT] // get preempt count
21 alternative_if ARM64_HAS_IRQ_PRIO_MASKING
22    /*
23     * DA_F were cleared at start of handling. If anything is set in DAIF,
24     * we come back from an NMI, so skip preemption
25     */
26    mrs x0, daif
27    orr x24, x24, x0
28 alternative_else_nop_endif
29    cbnz    x24, 1f             // preempt count != 0 || NMI return path
30    bl  arm64_preempt_schedule_irq  // irq en/disable is done inside

리눅스 커널의 Preemption과 관련된 루틴으로 태스크 디스크립터의 preempt_count 필드가 0이면,
arm64_preempt_schedule_irq() 함수를 호출하는 목적의 코드입니다.

Written by <디버깅을 통해 배우는 리눅스 커널의 구조와 원리> 저자








덧글

댓글 입력 영역