Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

9365
557
421924


[Arm프로세서] Armv7: 익셉션이 유발된 후 Supervisor 모드로 변경 Armv7: 동작 모드

Armv7 아키텍처 기반에서 실행되는 리눅스 커널에서 Armv7 동작 모드의 특징을 잘 활용해 시스템을 디자인한 루틴을 많이 볼 수 있습니다. 그 중 대표적인 기법이 Supervisor 모드에서 커널이 실행되도록 구현한 것입니다. 이 내용을 읽으면 자연스럽게 다음과 같은 의문이 생깁니다.

    “인터럽트가 유발되면 Arm 아키텍처 관점으로 IRQ 모드에 진입하는데,
      IRQ 모드에서 인터럽트를 처리하는 것이 아닌가?”

그런데 인터럽트가 유발돼 IRQ 모드로 진입한 다음에 바로 Supervisor 모드로 Arm 동작 모드를 변경합니다. 리눅스 커널의 주요 기능은 하나의 Supervisor 모드에서 처리해야 심플하게 시스템을 디자인할 수 있기 때문입니다.

여기서 다시 의문이 생깁니다. 

    “메모리 어보트를 유발하는 명령어를 유발하면 Abort 모드나,
    Undef 모드로 진입하는데 이 때는 어떻게 처리하나?”
 
데이터 어보트나 프리 페치 어보트가 발생해 Abort 모드로 진입하거나 Undefined Instruction 어보트가 유발돼 Undef 모드로 진입해도, 공통으로 슈퍼바이저 모드로 진입하는 명령어가 실행됩니다. 모두 시스템을 심플하게 디자인하기 위한 목적입니다. 

이번에는 익셉션이 유발돼 진입된 Arm 동작 모드를 슈퍼바이저 모드로 변경하는 어셈블리 명령어를 분석하겠습니다.

https://elixir.bootlin.com/linux/v5.10/source/arch/arm/kernel/entry-armv.S
01 vector_\name:
02 .if \correction
03 sub lr, lr, #\correction
04 .endif
05
06 @
07 @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
08 @ (parent CPSR)
09 @
10 stmia sp, {r0, lr} @ save r0, lr
11 mrs lr, spsr
12 str lr, [sp, #8] @ save spsr
13
14 @
15 @ Prepare for SVC32 mode.  IRQs remain disabled.
16 @
17 mrs r0, cpsr
18 eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
19 msr spsr_cxsf, r0
20 ...
21 and lr, lr, #0x0f
22    ARM( ldr lr, [pc, lr, lsl #2] )
23 movs pc, lr @ branch to handler in SVC mode

[정보]
위 코드는 어셈블리 명령어를 전처리 포멧으로 입력할 수 있는 *.S 파일에 구현돼 있습니다.


이제부터 어셈블리 명령어를 분석하겠습니다. 먼저 17번째 줄을 보겠습니다.

17 mrs r0, cpsr

CPSR의 값을 r0 레지스터로 읽어 오는 동작입니다.

이번에는 18번째 줄을 분석하겠습니다.

18 eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)

cpsr의 M[4:0] 비트가 저장된 r0의 값과 eor 연산을 수행해 Supervisor 모드를 나타내는 비트로 변경하는 목적의 코드입니다. 18번째 줄을 실행하면 r0의 M[4:0]는 10011(0x13: 16진수)가 됩니다.

이어서 19번째 줄을 보겠습니다.

19 msr spsr_cxsf, r0

Supervisor 모드를 나타내는 M[4:0] 비트를 저장한 r0의 값을 spsr에 저장하는 동작입니다.   

마지막으로 23번째 줄을 보겠습니다.

23 movs pc, lr @ branch to handler in SVC mode

익셉션과 관련된 후속 처리를 하는 레이블로 브랜치를 하면서 Supervisor 모드로 변경하는 동작입니다. 

여기까지 분석한 코드의 포멧은 어셈블리 명령어의 전처리 포멧인데요. 위 코드가 빌드된 어셈블리 명령어는 다음과 같습니다.
 
01 crash> dis vector_irq
02 0xffff1020:  sub     lr, lr, #4
03 0xffff1024:  stm     sp, {r0, lr}
04 0xffff1028:  mrs     lr, SPSR
05 0xffff102c:  str     lr, [sp, #8]
06 0xffff1030:  mrs     r0, CPSR
07 0xffff1034:  eor     r0, r0, #1
08 0xffff1038:  msr     SPSR_fsxc, r0
09 0xffff103c:  and     lr, lr, #15
10 0xffff1040:  mov     r0, sp
11 0xffff1044:  ldr     lr, [pc, lr, lsl #2]
12 0xffff1048:  movs    pc, lr

06~07번째 줄에서 CPSR을 읽어 r0에 Supervisor 모드의 M[4:0] 설정합니다. 08번째 줄에서는 이 결과를 SPSR 레지스터에 적용합니다. 

이어서 12번째 줄을 보면 movs 명령어를 실행해 r14에 지정된 주소로 프로그램 카운터가 브랜치되면서 Supervisor 모드로 변경합니다.

이번 절에서는 'msr cpsr'와 'movs'/'subs' 명령어를 사용해 Arm 동작 모드를 변경하는 어셈블리 명령어를 소개했습니다. 리눅스 커널 이외에 RTOS나 부트로더에서 이번 절에 소개한 명령어를 활용해 Arm 동작 모드를 변경하니 잘 익혀둡시다.





핑백

덧글

댓글 입력 영역