많은 분들이 생각하는 익셉션의 개념은 'Armv7 아키텍처의 익셉션'인 경우가 많아, Armv8 아키텍처를 배울 때 Armv7 아키텍처의 익셉션과 비슷할 것이라 예상합니다. 익셉션이 발생하면 지정한 주소로 프로그램 카운터를 브랜치하는 기본 개념은 Armv7/Armv8 아키텍처가 같으나, 익셉션을 처리하고 분류하는 체계와 세세한 처리 방식이 많이 다릅니다.

Armv8 아키텍처의 익셉션은 고성능 컴퓨터에서 적용되는 트러스트 존이나 가상화 시스템인 하이퍼바이저를 이해하기 위해 반드시 알아야 할 기반 지식이므로, 반드시 잘 알아야 둬야 합니다. 

익셉션(Exception)은 Armv8 아키텍처의 핵심 기능 중 하나입니다. Armv8 아키텍처에서 정의된 익셉션를 활용해 하이퍼바이저와 같은 가상 시스템을 설계할 수 있습니다. 또한 트러스트 존을 제대로 이해하려면 먼저 익셉션의 동작 방식을 알아야 합니다. 

Armv8 아키텍처에서 익셉션은 어떻게 정의내릴 수 있을까요? 기존에 소개된 Armv8 아키텍처의 익셉션과는 어떤 차이점이 있을까요? Armv8 아키텍처에서는 기존 Armv7 아키텍처와 비교해 익셉션의 종류를 분류하는 방식이 조금 다르지만, 익셉션의 동작 원리는 동일합니다. Armv8 아키텍처의 익셉션은 다음과 같이 설명할 수 있습니다.

 

“익셉션이란 Arm 코어가 명령어를 처리하다가 예외 사항이 발생할 때 이를 처리하는
방식이다. 익셉션이 발생하면 익셉션 벡터로 프로그램 카운터가 브랜치된다.”

 


위 문장을 읽으면 "어, Armv8 아키텍처의 익셉션은 Armv7 익셉션과 거의 같네"라는 생각이 들수 있습니다. 하지만 Armv8 아키텍처의 익셉션은 Armv7 익셉션과 비교했을 때 기본 개념은 같지만 익셉션을 분류하고 처리하는 방식이 다릅니다.

이제부터 Armv8 아키텍처의 익셉션에 대해서 배워 봅시다.

그 동안 Arm 아키텍처는 CPU 아키텍처 시장에서 '저전력', '소형 디바이스' 용으로 사용되는 CPU 아키텍처로 분류됐습니다. 최대한 적은 트렌지스터를 사용해 최소의 소모 전력으로 CPU를 설계했기 때문입니다. 물론 Arm 코어의 CPU 아키텍처도 최대한 심플하게 디자인했습니다.

익셉션의 구조 관점으로 보면, Armv7 아키텍처의 익셉션도 다음과 같이 심플하게 설계됐다고 볼 수 있습니다. 

   ❑ 익셉션 벡터가 4바이트 단위로 정렬
   ❑ 어떤 Arm 동작 모드에서 익셉션이 발생해도 익셉션 벡터로 브랜치
   ❑ 익셉션의 종류도 8개로 정의 내림

그런데, Armv8 아키텍처는 CPU 아키텍처 시장에 "우리는 고성능 컴퓨터나 클라우드 서버에 진출하겠다"라고 출사표를 던지고 설계한 것으로 보입니다. 기존의 소형 저전력 디바이스 용으로 설계된 CPU 아키텍처가 아니라 인텔의 x86과 경쟁할 수 있는 CPU 아키텍처를 설계한 것입니다.  

이런 면모는 Armv8 아키텍처의 익셉션에서 볼 수 있습니다. 이제 Armv8 아키텍처의 익셉션의 주요 특징을 Armv7 익셉션과 비교하면서 알아보겠습니다.

Armv7 아키텍처의 익셉션과 비교했을 때, Armv8 아키텍처에서는 익셉션을 분류하는 체계와 익셉션 벡터 테이블이 약간 다릅니다. 그 특징에 대해서 더 자세히 알아봅시다.

첫째, Armv8 아키텍처에서는 익셉션의 종류를 계층 구조로 재정의했습니다. 먼저 익셉션을 Synchronous와 Asynchronous와 같이 큰 카테고리로 분류하고, 하부 카테고리로 익셉션 클래스를 정의했습니다. Armv7 의 익셉션의 종류는 익셉션 클래스에서 확인할 수 있습니다.


표 9.1 Armv8 아키텍처에서 익셉션의 분류 체계

먼저 익셉션을 Synchronous와 Asynchronous와 같이 큰 카테고리로 분류합니다. Arm 코어가 명령어를 실행하다가 유발하는 익셉션을 Synchronous, 외부 인터럽트나 외부 메모리 어보트와 같이 외부에서 비동기적으로 유발되는 익셉션을 Asynchronous로 분류합니다. 이처럼 기존 Armv7 아키텍처의 익셉션과 비교해 익셉션을 분류하는 방식이 다릅니다.

둘째, 익셉션 레벨(EL)이란 개념을 도입해 이 기준으로 익셉션을 처리합니다. 익셉션이 유발된 익셉션 레벨 별로 익셉션 벡터 주소가 존재하며, 익셉션 레벨 별로 익셉션 링크 레지스터를 정의합니다.

다음 그림은 Armv8 아키텍처에서 익셉션 레벨 별로 익셉션 처리되는 구조를 나타냅니다.



그림 9.1 Armv8 아키텍처에서 익셉션 레벨 별로 익셉션이 처리되는 흐름

그림의 윗 부분을 먼저 보겠습니다. 유저 애플리케이션이 구동되는 EL0에서 익셉션이 발생하면 다음 순서로 처리됩니다.
  
    1.유저 애플리케이션이 구동되는 EL0에서 익셉션이 발생
    2.EL1으로 진입
    3.EL0용 익셉션 벡터로 브랜치 

이어서 그림의 아랫 부분은 운영체제의 커널이 구동되는 EL1에서 익셉션이 발생하면 처리되는 흐름입니다. EL1에서 익셉션이 유발되면 다음 순서로 처리됩니다.

   1. 운영체제 커널이 구동되는 EL1에서 익셉션이 발생
   2. EL1용 익셉션 벡터로 브랜치

이처럼 Arm코어는 익셉션이 유발되면 익셉션이 발생한 익셉션 레벨 별로 지정된 익셉션 벡터로 프로그램 카운터를 브랜치합니다.

[정보]
EL0과 EL1은 각각 Armv7 아키텍처의 User 모드, Supervisor 모드에 대응됩니다.
Armv7 아키텍처의 User 모드에서는 유저 애플리케이션이 구동하고, Supervisor 모드에서는 운영체제의 커널이 동작합니다. 마찬가지로, Armv8 아키텍처의 EL0에서는 유저 애플리케이션이 실행되고, EL1에서는 운영체제의 커널이 동작합니다.  

Armv7 아키텍처에서는 어떤 Arm 동작 모드에서 익셉션이 유발돼도 익셉션 종류 별로 지정된 익셉션 벡터로 프로그램 카운터가 브랜치됩니다. 예를 들어 유저 애플리케이션이 실행되는 User 모드나 운영체제의 커널이 구동되는 슈퍼바이저 모드에서 데이터 어보트가 유발되면, 데이터 어보트 익셉션에 해당되는 익셉션 벡터로 프로그램 카운터가 브랜치됩니다. 

그래서 익셉션 핸들러에서 익셉션이 유발된 시점의 Arm 동작 모드를 읽어서 이를 세분화하는 명령어를 입력해야 합니다. 예를 들어, 리눅스 커널인 경우 다음 코드와 같이 Arm 동작 모드를 읽어서 세분화해 처리하는 동작을 확인할 수 있습니다. 

다음은 Armv7 아키텍처 기반 리눅스 커널에서 구현된 Undefined Instruction 익셉션 핸들러의 구현부입니다.

01 NSR:FFFF11A0|E88D4001        vector_und:   stm     r13,{r0,r14}
02 NSR:FFFF11A4|E14FE000                      mrs     r14,spsr
03 NSR:FFFF11A8|E58DE008                      str     r14,[r13,#0x8]
04 NSR:FFFF11AC|E10F0000                      mrs     r0,cpsr
05 NSR:FFFF11B0|E2200008                      eor     r0,r0,#0x8       ; r0,r0,#8
06 NSR:FFFF11B4|E16FF000                      msr     spsr_cxsf,r0
07 NSR:FFFF11B8|E20EE00F                      and     r14,r14,#0x0F    ; r14,r14,#15
08 NSR:FFFF11BC|E1A0000D                      cpy     r0,r13
09 NSR:FFFF11C0|E79FE10E                      ldr     r14,[pc,+r14,lsl #0x2]
10 NSR:FFFF11C4|E1B0F00E                      movs    pc,r14
11 NSP:FFFF11C8|C010F680                      dcd     0xC010F680       ; __und_usr
12 NSP:FFFF11CC|C010F130                      dcd     0xC010F130       ; __und_invalid
13 NSP:FFFF11D0|C010F130                      dcd     0xC010F130       ; __und_invalid
14 NSP:FFFF11D4|C010F2C0                      dcd     0xC010F2C0       ; __und_svc

02번째 줄은 spsr 레지스터를 읽어 r14 레지스터에 저장하는 명령어입니다.
r14 레지스터는 익셉션이 발생한 시점의 Arm 동작 모드가 저장돼 있는데, 이 값에 따라 다른 레이블로 브랜치하는 코드(01~10번째 줄)가 실행됩니다.

그런데 Armv8 아키텍처에서는 익셉션이 발생한 익셉션 레벨 기준으로 익셉션 벡터가 브랜치되므로, 구지 소프트웨어적으로 익셉션이 유발된 시점의 익셉션 레벨에 따라 처리할 필요가 없습니다. 

넷째, 기존 Armv7의 익셉션 벡터 주소는 워드(32비트 기준: 4바이트)로 정렬됐으나, Armv8의 경우 0x80바이트 단위로 정렬됐습니다. 익셉션 벡터 주소에서 익셉션를 바로 처리할 수 있는 명령어를 실행할 수 있습니다.

다음은 Arm 사에서 배포한 문서에 담긴 익셉션 벡터 테이블입니다.
 

그림 9.2 Armv8 아키텍처의 익셉션 벡터 테이블

위 그림에서 박스로 표기된 부분을 보면 0x000, 0x080, 0x100, 0x180이 보이는데 이는 익셉션의 종류 별 익셉션 벡터 오프셋을 나타냅니다. 이처럼 Armv8 아키텍처는 익셉션의 종류 별 오프셋 주소의 사이즈가 0x080 바이트입니다.

 


[정보]
익셉션 벡터 베이스 주소에 익셉션 벡터 오프셋 주소를 더한 주소로 프로그램 카운터가 브랜치됩니다.

+ Recent posts