본문 바로가기

시스템 소프트웨어 개발을 위한 Arm 아키텍처의 구조와 원리/5장: Armv7 - 동작 모드

[Armv7-A] 익셉션 유발 시: SPSRs 레지스터 업데이트 방식 (동작모드, Arm)

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