User 모드에서 Data abort를 유발하는 동작 코드 리뷰
1. Data abort 익셉션이 유발되면 0xFFFF0010 주소로 프로그램 카운터가 분기합니다.
NSR:FFFF0000|EA0003FF b 0xFFFF1004 ; vector_rst
NSR:FFFF0004|EA000465 b 0xFFFF11A0 ; vector_und
NSR:FFFF0008|E59FFFF0 ldr pc,0xFFFF1000
NSR:FFFF000C|EA000443 b 0xFFFF1120 ; vector_pabt
>> NSR:FFFF0010|EA000422 b 0xFFFF10A0 ; vector_dabt
이 때 CPSR과 SPSR_abt 레지스터의 값은 다음과 같습니다.
- CPSR: 0x1D7 (현재 동작 모드는 Abort mode)
- SPSR_abt: 0x10 (이전 동작 모드는 User mode)
vector_dabt 레이블의 구현부
NSR:FFFF10A0|vector_dabt: sub r14,r14,#0x8 ; r14,r14,#8
NSR:FFFF10A4| stm r13,{r0,r14} // r0와 r14 레지스터의 값을 프로세스의 스택에 푸시
// [1] 매우 중요한 동작
NSR:FFFF10A8| mrs r14,spsr // spsr의 값(spsr_abt과 같음)을 r14로 읽음
NSR:FFFF10AC| str r14,[r13,#0x8] // r14의 값(spsr_abt)을 프로세스의 스택에 푸시
// [2] 매우 중요한 동작
NSR:FFFF10B0| mrs r0,cpsr
NSR:FFFF10B4| eor r0,r0,#0x4 ; r0,r0,#4
NSR:FFFF10B8| msr spsr_cxsf,r0 // spsr.m를 supervisor 모드 필드로 변경
// spsr는 뱅크드 레지스터이므로, 어보트 모드에서
// spsr을 변경하면 spsr_abt가 함께 변경
NSR:FFFF10BC| and r14,r14,#0x0F ; r14,r14,#15
NSR:FFFF10C0| cpy r0,r13
NSR:FFFF10C4| ldr r14,[pc,+r14,lsl #0x2] // r14 레지스터에 분기할 레이브를 저장
// 이전 동작 모드가 user 모드이면: __dabt_usr로 분기
NSR:FFFF10C8| movs pc,r14
NSP:FFFF10CC| dcd 0xC0201CE0 ; __dabt_usr
NSP:FFFF10D0| dcd 0xC0201910 ; __dabt_invalid
__dabt_usr 레이블의 동작
NSR:C0201CE0|__dabt_usr: sub r13,r13,#0x48 ; r13,r13,#72
NSR:C0201CE4| stmib r13,{r1-r12}
NSR:C0201CE8| mrc p15,#0x0,r7,c1,c0,#0x0 ; p15,#0,r7,c1,c0,#0 (system control)
NSR:C0201CEC| ldr r8,0xC0201C20
...
NSR:C0201D1C| cpy r2,r13
NSR:C0201D20| bl 0xC021BB20 ; v7_early_abort
NSR:C0201D24| b 0xC0201FA4 ; ret_from_exception
- v7_early_abort 함수를 호출한 다음에 ret_from_exception 레이블로 분기
- 여기서 v7_early_abort는 data abort에 대한 서비스 루틴을 실행
ret_from_exception 레이블은 r9와 r8 레지스터를 업데이트한 다음에 ret_to_user 레이블 분기
NSR:C0201FA4|ret_from_exception: lsr r9,r13,#0x0D
NSR:C0201FA8| lsl r9,r9,#0x0D
NSR:C0201FAC| mov r8,#0x0 ; r8,#0
NSR:C0201FB0| b 0xC0201048 ; ret_to_user
ret_to_user 레이블에서는 유저 모드로 복귀
NSR:C0201048|ret_to_user: cpsid i
...
NSR:C0201068| cpy r2,r13
NSR:C020106C| ldr r1,[r2,#0x40] // '[2] 매우 중요한 동작'으로 위에 명시한 프로세스의 스택 주소에 이전에 저장된 spsr_abt의 값(0x10, 이전 동작 모드는 user 모드)을 r1레지스터로 읽음
NSR:C0201070| ldr r14,[r2,#0x3C]!
NSR:C0201074| tst r1,#0x8F ; r1,#143 // 예외 처리 확인
NSR:C0201078| bne 0xC0201094
NSR:C020107C| msr spsr_cxsf,r1 // spsr(spsr_svc)에 r1 레지스터를 저장
NSR:C0201080| strex r1,r2,[r2]
NSR:C0201084| ldmdb r2,{r0-r14}^ // 스택에 푸시된 값을 로딩해서 r0~r14 레지스터 업데이트, 스택에 되돌아갈 주소가 저장된 r14 값이 이미 푸시됨 - '[1] 매우 중요한 동작'으로 표기
NSR:C0201088| nop
NSR:C020108C| add r13,r13,#0x48 ; r13,r13,#72
NSR:C0201090| movs pc,r14 // 유저 모드로 복귀하면서 data abort가 유발된 프로그램 카운터로 분기
[정리]
- SPSR 레지스터의 값은 익셉션이 유발될 때 Arm 프로세서가 하드웨어적으로 업데이트합니다. 즉 익셉션이 유발된 시점의 CPSR의 값을 SPSR_<mode>에 업데이트합니다.
- movs, subs와 같은 동작 모드를 변경하는 명령어를 실행하면, movs 혹은 subs 명령어를 실행한 시점의 CPSR의 값이 SPSR_<mode>로 복사되지는 않습니다.
- SPSR 레지스터의 값은 msr spsr_cxsf,r0 명령어와 같이 어셈블리 명령어로 변경할 수 있습니다. 대부분 동작 모드를 변경하기 직전에 spsr 레지스터의 값을 변경합니다.
[ 관련 강의]
01/08/2025
'시스템 소프트웨어 개발을 위한 Arm 아키텍처의 구조와 원리 > 5장: Armv7 - 동작 모드' 카테고리의 다른 글
[Arm프로세서] Armv7: 어떤 동작 모드를 선택해야 할까? (0) | 2024.01.01 |
---|---|
[Arm프로세서] Armv7 동작 모드: PL와 Arm 동작 모드 소개 (0) | 2023.06.10 |
[Arm프로세서] Armv7 아키텍처의 Arm 동작 모드 소개 (0) | 2023.06.10 |
[ARM][ARMv7] 리눅스 커널: ARM 모드를 설정하는 어셈블리 코드 분석 (0) | 2023.06.10 |
[ARM프로세서] ARM 모드에 대한 소개 (0) | 2023.06.09 |