이 글은 김동현(Austin Kim)님의 '시스템 소프트웨어 개발을 위한 Arm 아키텍처의 구조와 원리' 책을 참고하여 작성하였음을 밝힙니다!
Armv7에는 동작모드라는 개념이 Armv7 아키텍처에서 가장 중요한 개념이라고 한다. 그 이유는 익셉션, 트러스트존과 같은 Armv7 아키텍처의 많은 기능들이 동작 모드 기반 위에서 설계되어 있기 때문이다.
- 동작모드는 어떻게 바뀔까?
- 동작모드가 바뀌면 이전 동작모드는 어떻게 확인할까?
- 이전 동작모드로는 어떻게 복귀할까?
- 동작모드를 활용해 OS커널은 어떻게 구성돼 있을까?
요 질문들을 던지면서 프로그래밍하고 코드를 분석하면 공부가 더 잘 될거라고 한다.
렛츠고!
1. Armv7 아키텍처의 동작 모드
PL과 동작 모드의 관계
Arm 코어에는 Privilege Level(특권 레벨)이라는 개념이 있다. 특권 레벨은 소프트웨어가 실행될 때 리소스에 접근하는 권한을 나타낸다. 범위는 PL0 ~ PL3 까지 총 4단계가 있으며 PL0이 가장 낮은 실행 권한이고, 올라갈수록 높은 권한을 갖는다.
- PL0: 일명 Unprivileged Level 이라고도 불린다. 낮은 실행 권한으로, PL0에서 유저 애플리케이션이 실행된다. PL0에서 시스템을 직접적으로 설정할 수 있는 권한은 없다. 예를 들면 MMU, 캐시, 인터럽트를 설정할 수 없다. Arm 아키텍처에서는 메모리에 접근할 때 권한을 체크하는데, PL0에서는 메모리 공간에 직접적으로 액세스하지 못하게 제약을 둔다.
- PL1: 유저 모드와 Hyp모드를 제외한 모든 모드(SVC, IRQ, FIQ, ABT, UND, SYS)가 PL1에서 실행되며, 보통 OS 시스템(커널)이 PL1에서 실행된다. PL1에서는 메모리에 제약 없이 접근할 수 있으며, 캐시나 MMU같은 시스템 리소스를 직접 설정할 수 있는 권한을 갖는다.
- PL2: 가상화가 있을 때만 등장하는 모드. 예를 들어 Linux에서 VM으로 Windows를 돌리는 것 처럼 하나의 SoC에서 여러 OS를 동시에 돌리는 경우 접근된다.
- PL3: 보안기능(Trust Zone)이 있을 때만 접근된다.
PL2, PL3은 가상화와 트러스트존을 배우고 나중에 더 알아보도록 하고, 이번엔 동작모드에 대해 자세히 알아보자!
- 유저 모드(USR)는 유저 애플리케이션이 실행되는 모드이고, 특권 레벨은 앞서 정리한 것 처럼 PL0이다.
- 슈퍼바이저모드(SVC)는 주로 운영체제 커널이 구동되고, PL1으로 실행된다. 주로 유저 모드에서 SVC 명령어를 실행하면 슈퍼바이저 모드에 진입된다.
- IRQ모드와 FIQ모드는 각각의 인터럽트같은 익셉션이 발생하면 진입하는 모드이다. 대부분의 OS에서는 IRQ 인터럽트가 발생하면 인터럽트를 직접 받아 처리하고, FIQ는 트러스트존의 시큐어 운영체제에서 라우팅돼 처리하도록 설정한다. 잘 이해가 가진 않지만 우선은 넘어가자.
- Abort(ABT)모드는 메모리 어보트 익셉션이 발생하면 진입하며
- Undef(UND)모드는 Arm 코어가 디코딩할 수 없는 Arm 아키텍처에서 정의되지 않은 명령어어가 실행되면 진입하는 모드이다.
- System(SYS)모드는 유저 모드의 레지스터 뷰를 공유하는 모드이다.
이러한 9가지 동작 모드들에는 구체적으로 어떻게 진입할까?
동작 모드는 익셉션이 유발되면 변경되며, 바뀌는 방식은 Arm 아키텍처에서 정의된 익셉션 유형에 따라 다르다. 아직은 잘 모르는 개념이니 그렇구나 하고 넘어가본다.
Q. 동작 모드가 있다는건 알았다. 실제 업무에선 이 개념이 어떤 방식으로 활용되는걸까?(책 p118)
A. 예를 들어서 블루투스 기능이 있는 MP3 플레이어를 개발한다고 해보자. 한가지 스펙이 제안됐는데, 블루투스를 연결할 때 인터럽트가 발생한다는 요구사항이다. 이럴 때는 IRQ모드에서 프로그램이 반응해 동작하도록 시스템을 구성해야한다.
만일 Linux를 Arm코어에 올린다면?? 유저모드에선 유저 애플리케이션이 구동되고 슈퍼바이저 모드에선 커널 시스템이 구동되도록 디자인하면 좋을 것이다! 이를 만드는데 개발자가 실수로 NULL 포인터 익셉션을 유발하는 코드를 작성한다면?? 이 코드가 실행되면 Arm 코어는 Data Abort 나 Prefetch Abort 익셉션을 유발하면서 ABT모드에 진입한다. 이런 조건에서는 시스템의 오류 정보를 출력하고 시스템을 리셋하거나 프로세스를 종료시키는 코드가 구현되면 좋을 것이다.
내가 개발하는 시스템의 특성에 맞춰서 Arm코어의 각 모드를 사용해주면 되는구나!!
요런 동작 모드를 선택할 때는 고려해야할 세가지 원칙이 있다고 한다.
- PL0에서 PL1으로 자유롭게 진입할 수 없고, PL0에서는 SVC 명령어를 실행해 익셉션(트랩)이 유발되어야 PL1에 진입할 수 있다.
- PL0에서는 MMU나 IRQ나 FIQ 인터럽트를 직접 설정할 수 없다. 이게 무슨 말이냐면, 하드웨어적으로 시스템을 설정하는 동작은 PL0에서 수행이 불가하고 PL1에서 수행해야한다는 말이다.
- PL1으로 정의된 동작 모드에서는 SPSR 레지스터의 모드 필드에 변경하려는 모드 비트를 설정한 후 `SUBS PC, LR`혹은 `MOVS PC, LR`과 같은 명령어를 실행하면 정의된 동작 모드로 스위칭된다. 다시말해, PL1에서 실행되는 동작모드끼리는 익셉션 없이 스위칭 할 수 있다는 것.
이처럼, Arm코어는 PL0~3라는 권한과 이에 매핑되는 동작 모드들을 바탕으로 작동하는 HW다. 또 각 동작 모드의 특성을 살리면 다양한 방식으로 시스템을 설계할 수 도 있다는 점 기억하자!
2. 동작 모드와 관련된 레지스터
동작 모드를 소프트웨어적으로 알아야 모드에 따라 이를 제어하는 코드를 작성할 수 있다. 소프트웨어적으로 동작 모드를 알 수 있는 방법이 있을까? 이를 위해 Arm코어는 다음의 기능을 제공한다.
- 현재 동작 모드를 CPSR 레지스터에 저장
- CPSR 레지스터를 읽어서 동작 모드를 체크
CPSR 레지스터의 어느 비트에 동작 모드가 저장되는지 살펴보고, CPSR 레지스터를 이용해 동작 모드를 읽는 어셈블리 명령어에 대해 알아보자!
CPSR 레지스터
Current Program Status Register(CPSR) 이름에서도 알 수 있듯이 요 레지스터에는 현재 Arm 프로세서의 세부 상태 정보를 저장한다. 레지스터 비트 구성은 아래와 같다.

여기서 제일 오른쪽에 M[4:0]가 있다. 이는 Mode field의 약자로, 이 5bits에 현재 프로세서의 동작 모드를 나타내는 비트가 저장되며, 비트 값에 대한 매핑은 아래 테이블처럼 이뤄진다.

예를 들어서 현재 코어가 User모드라면 M[4:0]의 값은 0b10000 이고, SVC익셉션이 유발되어 해당 모드로 변경된다면 0b10011 이 될 것이다.
다만, 한가지 유념해야 할 점은 PL0 권한의 유저 모드(USR)에선 CPSR 레지스터 처럼 시스템 속성 정보에 접근하는데 제약이 있다는 점이다(PL1의 모드들은 어셈블리 명령어를 이용해 CPSR 명령어들을 읽을 수 있다).
+ SPSR 레지스터는 CPSR 레지스터의 복사본이다. 그렇다면 SPSR의 존재 이유는 무엇일까?
SPSR 레지스터는 정확히 말하면 익셉션이 유발돼 동작모드가 변경되기 이전의 CPSR 레지스터의 값을 저장한다. 따라서, SPSR 레지스터의 M[4:0]필드 값을 읽어 익셉션이 유발되기 이전 동작 모드로 복귀하는 방식으로 제어할 수 있다.
예를 들어, 유저 모드에서 애플리케이션이 실행되다가 인터럽트가 유발돼 IRQ 모드로 변경되는 실행 흐름을 따라가보자.
1. 유저 애플리케이션이 USR 모드에서 실행중,
2. IRQ 인터럽트 익셉션이 유발되면 IRQ 모드로 진입.
3. CPSR 레지스터의 M[4:0]는 IRQ 모드의 비트 값, SPSR_irq M[4:0] 비트에는 유저 모드의 정보를 저장.
4. 익셉션 핸들러에서 SPSR_irq 레지스터의 값을 백업한 후 인터럽트 처리를 마무리한 뒤 유저 모드로 복귀.
이렇게 활용할 수 있는것이다. 이러한 방식 이외에도 저자는 제품 요구사항과 Arm 코어 위에서 실행되는 OS 설계 방식에 따라 다양한 방식으로 제어할 수 있다고 한다.
3. 동작 모드를 바꾸는 명령어
시스템을 설계할 때는 임의로 동작 모드를 바꿔야할 상황이 있다고 한다. 내 마음대로 Arm 코어의 동작 모드를 바꾸기 위해선 해당 어셈블리 명령어를 사용해주면 된다. 그 방식에는 두가지가 있다!
- MSR CPSR_C, #MODE | I_BIT | F_BIT;
- MOVS나 SUBS 명령어
첫번째 방식을 사용해 동작 모드를 변경하는 방법을 알아보자.
3.1 MSR CPSR_C 명령어
이 명령어는 한마디로, PL1에서 동작모드를 전환할 때 사용하는 명령어이다.
Armv7 아키텍처에서는 익셉션이 유발되면 해당 익셉션에 대응하는 동작 모드로 진입한다. 그런데 PL1에서 실행되는 동작 모드끼리는 명령어로 동작 모드를 변경할 수 있다. 예를 들면, 부팅 과정에서 `MSR CPSR_C` 명령어를 실행하면 PL1으로 실행되는 동작 모드를 변경하면서 각 모드별 스택을 설정한다고 한다.
MSR CPSR_C 명령어로 동작모드를 변경하는 방법에 대해 알아보자.

위 그림은 아까 말한 동작 모드를 바꿔주는 명령어
- MSR CPSR_C, #MODE | I_BIT | F_BIT;
를 실행했을 때 변경되는 비트 영역을 나타낸 것이다. 저 연산이 이해가 안가서 분해해보았다.
- MSR: Move to System Register 명렁어. 이름에서 알 수 있듯, 시스템 레지스터를 변경하는 기능을 수행한다.
- CPSR_C: CPSR 레지스터의 control bits가 포함된 하위 8비트(Bit 0~7)를 의미. MSR 명령어를 사용할 때 레지스터 전체가 아닌 특정 필드만 업데이트 하기 위해 '_C'를 붙여 사용한다.
- MODE: 부트로더나 커널 소스 내에서 USR_MODE(10000), FIQ_MODE(10001), SVC_MODE(10011) 라고 정의된 매크로. (정확히는 /arch/arm/include/uapi/asm/ptrace.h에 BIT macro들이 정의돼있고, 10000이 아닌 0x0000_0010 이렇게 hex로 정의되어있더라.)
- I_BIT: b1000_0000(0x0000_0080) 라고 정의된 매크로
- F_BIT: b0100_0000(0x0000_0040) 라고 정의된 매크로
- #MODE | I_BIT | F_BIT: 여기서 '|'은 OR 비트연산을 의미, '#'은 immediate value를 의미.
- immediate value: '즉치값'. 이 글에 개념을 정리해보았다.
결론적으로 저 명령어는 #MODE | I_BIT | F_BIT 값을, CPSR_C로 표현되는 CPSR의 하위 8비트 영역에, MSR(값을 복사)하라는 의미이다. 예시 코드도 봐보자(Arm 스펙 문서에 소개된 코드임을 밝힙니다).
MSR CPSR_c, #MODE_FIQ:OR:I_Bit:OR:F_Bit;
MOV SP, R0
SUB R0, R0, #FIQ_Stack_Size
MSR CPSR_c, #MODE_IRQ:OR:I_Bit:OR:F_Bit;
MOV SP, R0
이 코드는 동작 모드를 바꾼 다음에 각 동작 모드별로 스택을 설정하는 목적의 루틴이다.
여기서 MSR 명령어를 사용하는 첫째 줄을 해석해보자.
첫번째 줄의 우측 operand `#MODE_FIQ | I_Bit | F_Bit` 의 연산 결과는 아래처럼 이진수로 해석되어,
0001_0001 | 1000_0000 | 0100_0000 // 1101_0001
1101_0001과 동일하다.

그리고 이 연산 결과는 '#'키워드에 의해 즉치값으로서 메모리 접근 없이 위 그림처럼 CPSR_c 영역에 load된다!
3.2 SUBS, MOVS 명령어
이 명령어들은 익셉션 리턴할 때 사용할 수 있는 명령어들이다.
생각보다 간단한 동작을 갖는다. 이름 답게 각각 SUB명령과 MOV명령을 수행한 뒤, 딱 한가지 더 무언가를 수행하는 명령어들이다.
`SUBS PC, LR, #<const>`은 다음과 같이 동작한다.
- `SUB PC, LR, #<const>`를 실행한다.
- SPSR_mode값을 CPSR으로 옮긴다.
이렇게 기존 SUB 명령을 수행하고 SPSR_mode 레지스터값을 CPSR로 복귀시켜주는 것이다.
`MOVS PC, LR`도 같다.
- LR값을 PC로 옮겨준다.
- SPSR_mode값을 CPSR으로 옮긴다.
정리해보면, 동작 모드를 변경하는 명령어는 크게 두가지로, 시스템 레지스터를 변경하는 MSR opcode를 이용해 CPSR의 하위 1byte를 변경해 동작 모드를 변경하거나(반드시 PL1 이상의 모드에서 실행해야함), SUBS나 MOVS를 이용한 익셉션 리턴으로 PC값을 LR로 변경하고 CPSR을 SPSR_mode로 치환하는 방식이 있다!
'Embedded Systems > Arm 아키텍처' 카테고리의 다른 글
| [Arm] 익셉션에 대해 공부해보자! (0) | 2026.04.02 |
|---|---|
| [Arm] '#' immediate value(즉치값) 너는 누구니? (0) | 2026.03.26 |
| [ARM] Arm 아키텍처의 메모리모델 - 노멀 메모리, 디바이스 메모리 (0) | 2026.03.24 |
| [ARM] 익셉션? 너는 누구니? (0) | 2026.03.17 |