Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

11105
637
415734


[Arm프로세서] Armv8 익셉션 레벨과 privilege level Armv8: Exception Level(EL)

Armv8 아키텍처는 PL0-PL3까지 4개의 privilege levels(접근 권한)을 제공합니다. 여기서 소개하는 privilege levels은 Armv7 아키텍처에서 다룬 내용과 거의 유사합니다.

[정보]
privilege levels은 Arm 아키텍처에서만 지원할까요? 그렇지는 않습니다.

x86을 포함한 대부분 CPU 아키텍처는 privilege levels와 같은 기능을 지원하는데, 주로 운영체제를 설계할 때 결함이 있을 수 있는 유저 애플리케이션으로부터 시스템을 보호하기 위한 용도로 사용됩니다. 리눅스 커널과 같은 운영체제 커널은 privileged level에서 실행이 되는데, privileged level에서만 주요 시스템 설정(시스템 레지스터, 인터럽트, 캐시 접근)을 할 수 있게 제한을 둡니다. 유저 애플리케이션이 실행될 때는 privileged level 레벨이 아닌 unprivileged level로 실행해, 시스템의 세부 설정을 할 수 없도록 설계합니다.

운영체제 커널의 세부 동작을 잘 모르는 유저 애플리케이션 개발자가 privileged level이 없는 환경에서 애플리케이션을 개발한다고 가정해볼까요? 유저 애플리케이션에서 메모리를 제대로 관리하면 괜찮겠지만 실수로 코드를 잘못 작성해서 커널 자료구조나 함수가 위치한 메모리 공간을 오염시키면 어떻게 될까요? 혹은 해커들이 고의로 시스템을 오동작시키기 위한 명령어를 실행하는 애플리케이션을 제작하면 어떤 결과를 초래할까요? 커널 패닉으로 시스템이 리부팅하거나 예상치 못한 시스템 오동작을 보게 될 가능성이 높습니다.  

따라서 유저 애플리케이션은 unprivileged level로 실행되도록 시스템을 디자인해, 유저 애플리케이션에서 시스템 메모리 공간에 직접 접근하지 못하게 제약을 둡니다. 만약 시스템 메모리에 접근을 해야 하는 상황이면 시스템 콜을 통해 privileged level로 실행되는 운영체제 커널을 통해 간접적으로 메모리에 접근하도록 시스템을 설계합니다. 

  
EL0에서는 유저 애플리케이션이 실행되며 unprivileged 레벨 혹은 PL0로 실행됩니다. PL0에서 실행되는 EL0은 다음과 같은 특징을 지닙니다.

   * EL0에서는 인터럽트, MMU, 캐시 기능을 설정할 수 없다.
   * EL0에서는 메모리 접근 권한에 제약이 있다. 

EL0은 unprivileged level 혹은 PL0 권한이 부여된 익셉션 레벨입니다.

이어서 운영체제의 커널이 구동되는 EL1은 PL1 권한이 있는 익셉션 레벨입니다.
EL1에서 구동되는 커널에서는 인터럽트, MMU, 캐시 설정과 같이 시스템을 설정할 수 있습니다.

PL2는 EL2에게 부여되는 권한 레벨로 게스트 OS끼리 스위칭하고 게스트 OS의 시스템 리소스에 접근할 수 있습니다. 일반적으로 EL2에서 하이퍼바이저가 실행되며, 하이퍼바이저는 PL2 권한 레벨로 실행됩니다. PL2 권한이 있는 EL2에서는 EL1에서 실행되는 레지스터에 접근할 수 있습니다.

다음은 EL2에서 실행되는 XEN 하이퍼바이저에서 EL1 레지스터에 직접 접근하는 어셈블리 명령어입니다.

01 0000000000269ba4 <guest_sync_slowpath>:
02  269ba4: d10083ff  sub sp, sp, #0x20
03  269ba8: a9bf77fc  stp x28, x29, [sp, #-16]!
...
04  269c00: d53c4116  mrs x22, sp_el1
05  269c04: d5384037  mrs x23, elr_el1
06  269c08: a9005eb6  stp x22, x23, [x21]
...
07  269c68: a9405eb6  ldp x22, x23, [x21]
08  269c6c: d51c4116  msr sp_el1, x22
09  269c70: d5184037  msr elr_el1, x23
10  269c74: 14000174  b 26a244 <return_from_trap>

04~05번째 줄은 sp_el1과 elr_el1 레지스터를 읽는 명령어이고, 08~09번째 줄은 sp_el1과 elr_el1 레지스터를 설정하는 명령어입니다.

여기서 sp_el1은 EL1에서 접근하는 스택 포인터 레지스터, elr_el1은 EL1에서 접근하는 익셉션 링크 레지스터입니다. 그런데 PL2 권한으로 실행되는 EL2에서는 EL1에서 설정된 sp_el1 혹은 elr_el1 레지스터에 접근할 수 있습니다.

마지막으로 PL3는 EL3에게 부여되는 권한 레벨로 highest privileged level이라고도 합니다. 시스템을 모두 설정할 수 있고, 익셉션 레벨에 존재하는 모든 레지스터에 엑세스할 수 있습니다. 

[정보]
대부분 시스템에서 부팅하는 과정에서 가장 먼저 실행되는 소프트웨어는 부트로더입니다. 부트로더가 EL3에서 실행하도록 설정해, 부트로더에서 시스템을 구성하는 기능을 설정합니다. 


이제까지 설명한 익셉션 레벨 별 privileged level은 다음 표로 정리할 수 있습니다.


표 4.2 Armv8 아키텍처의 익셉션 레벨과 PL  

이번에는 멀티 운영체제가 동시다발적으로 실행되는 하이퍼바이저와 트러스트존 기능이 구현된 전체 구조를 보면서 익셉션 레벨을 정리해봅시다. 

 

그림 4.1 Armv8 익셉션 레벨과 주요 실행 흐름

먼저 그림의 가장 윗 부분은 EL0에서 실행되는 유저 애플리케이션을 나타냅니다.

EL0에서 EL1로 진입하려면 그림에서 ①로 표시된 부분과 같이 슈퍼바이저 콜을 실행하는 svc 명령어를 실행해야 합니다.

이어서 EL0의 아랫 부분에 있는 EL1을 보겠습니다. EL1에는 운영체제의 커널이 구동되며, EL1은 PL1 권한으로 실행됩니다. 만약 EL1(운영체제 커널)에서 EL2(하이퍼바이저)로 진입하려면 그림에서 ②로 표시된 부분과 같이 하이퍼바이저 콜을 실행하는 hvc 명령어를 실행해야 합니다.

다음으로 EL1의 아랫 부분을 보면 EL2이 보입니다. EL2에는 다양한 게스트 OS(운영체제)을 제어하는 하이퍼바이저가 구동되며, EL2은 PL2 권한으로 실행됩니다. 그림에서는 게스트 OS1과 게스트 OS2가 존재하는데, 하이퍼바이저는 2개 게스트 OS가 동시다발적으로 실행되도록 실행 흐름을 관리하는 역할을 수행합니다.

다음으로 EL2의 아래 부분에 있는 EL3을 봅시다. EL3은 highest privileged 권한, 즉 시스템의 모든 기능(레지스터, 메모리, 캐시)을 설정할 수 있는 가장 높은 권한으로 실행됩니다. 보통 부팅 과정에서 highest privileged 권한인 EL3로 설정된 상태에서 시스템의 여러 리소스(메모리, 시스템 레지스터)를 설정합니다.

만약 EL1(운영체제 커널)에서 EL3(Secure Monitor)로 진입하려면 그림에서 ③로 표시된 부분과 같이 시큐어 모니터 콜을 실행하는 smc 명령어를 실행해야 합니다.
 
Armv8 아키텍처의 근간이 되는 익셉션 레벨에 대해 알아 봤습니다. 이번 포스트에서 소개한 익셉션 레벨은 Armv8 아키텍처에서 가장 중요한 내용이니 반드시 기억합시다. 

다음 포스트에서 익셉션 레벨에는 어떻게 진입하는지 살펴보겠습니다.

#Reference Armv8: 익셉션 레벨(ELx)


Written by <디버깅을 통해 배우는 리눅스 커널의 구조와 원리> 저자
* 2021년 대한민국 학술원 선정 우수도서



---



핑백

덧글

댓글 입력 영역