Background - arm
A1 Introduction to the Armv8 Architecture
A1.1 About Arm Architecture
Arm 아키텍처는 RISC로 reduced instruction set computer이다.
다음과 같은 특징이 있다.
- 많은 레지스터터 개수
- load/store 아키텍처, 바로 memory에 작성을 허용치 않고 무조건 register를 거쳐서 load/store이 진행된다.
- 간단한 addressing mode, 모든 store/load address는 register와 명령 필드로만 addressing된다.
매우 중요한 특징으로 Arm 아키텍처 퍼포먼스나 사이즈나 전력 소모량에 따라 여러 구현이 존재한다.
Armv8의 중요한 특징은 호환성이다.
- 64-bit Execution state, AArch64
- 32-bit Execution state, AArch32 - 또한 이전 버전의 Arm 아키텍처와 모두 호환된다.
A1.2 Architecture profiles
Arm은 다음과 같이 아키텍처 프로필을 나타낸다.
- A - Application profile
- MMU를 기반으로 Virtual Memory System Architecture를 지원한다.
- ex) Armv8-A 구현은 AArchv8-A로도 불린다.
- A64, A32, T32 명령어 세트를 지원한다.
- MMU를 기반으로 Virtual Memory System Architecture를 지원한다.
- R - Real-time profile
- MPU를 기반으로 Protected Memory System Architecture를 지원한다.
- A32, T32 명령어 세트를 지원한다.
- M - Microcontroller profile
- 저지연 인터럽트 처리를 위해 설계된 프로그래머를 위한 프로필이다.
- R 프로필 PMSA(Protected Memory System Architecture)의 다른 아키텍처를 구현한다.
- T32 변종 명령어 세트를 지원한다.
다음은 추가적으로 알아야할 정보들이다.
- MMU
- Memory Management Unit
- 가상 메모리 주소를 물리 메모리 주소로 변환한다.
- 다음과 같은 구조이다. 옛날에는 따로 CPU와 분리되어있었다.
- 동작 방식
- 가상 메모리를 2^n 비트의 크기로 나누고, 특정 페이지는 실제 물리 메모리의 하나의 프레임에 대응된다.
- CPU는 가상 주소를 MMU로 넘긴다.
- MMU는 TLB를 확인해서 변환 정보를 확인하고 만약 해당 데이터가 있다면 그 데이터를 이용한다.
- TLB에 원하는 데이터가 없다면 (TLB miss) 페이지 테이블을 이용해서 가상 메모리 주소의 뒤쪽 n 비트는 건들지 않고 앞쪽 나머지 비트를 물리 메모리의 프레임으로 변환한다.
- TLB나 페이지 테이블을 통해 실제로 주소를 가져오지 못하는 상황을 page fault라고 부른다.
- 이 경우엔 여유 공간에 페이지를 할당해서 연결시키거나, 디스크로 페이지를 내리고 할당한다.
- MPU
- Memory Protection Unit
- 4gb 메모리 공간 내에서 다양한 메모리 영역별로 권한을 설정할 수 있게 해주는 프로그래밍 가능한 유닛이다.
- 모든 메모리 액세스는 MPU에 의해 모니터링된다.
- 절대 명령어 fetch가 일어나지 않는 영역을 설정하여 Memory corruption으로 인해 악의적인 코드가 실행되는 것을 방지한다.
- 다음과 같은 모습으로 동작한다.
- MPU_MAIR에는 XN (Execute Never) 같은 속성들이 정의되고 MPU_RLAR은 MPU_MAIR 레지스터를 가리킨다.
- TrustZone이 지원된다면, Secure state에서의 MPU 레지스터들도 존재한다.
- 반대 Non-secure state에서도 이러한 MPU 레지스터들이 존재한다.
A1.3 Armv8 architectural concepts
armv8의 네 가지 중요한 feature가 있다.
- Execution state
- Instruction sets
- System registers
- Debug
A1.3.1 Execution state
Execution state는 Processing Element, PE의 실행 환경을 정의한다.
- Execution state
- 지원되는 레지스터의 widths - ex) 64bit, 32bit …
- 지원되는 명령어 세트
- 중요한 측면
- Execution model
- VMSA (Virtual Memory System Architecture)
- 프로그래머 모델
- AArch64
- 64 bit Execution state
- PC, SP, … , X30 레지스터, ELR
- 정확히는 SP 레지스터들과 Exception Link 레지스터들도 지원한다.
- ELR의 의의는 따로 직접 메모리에 Exception 발생시 리턴 주소를 기록하지 않고 따로 리턴 주소를 기록하는 레지스터를 둬서 퍼포먼스를 올리는 것에 있다.
- A64 라는 하나의 명령어 셋만 지원된다.
- armv8의 Exception model을 4개의 Exception level, EL0 ~ EL3를 정의한다.
- Execution privilege hierarchy 로 권한의 level을 지정한다.
- System register에 접미사를 붙여서 가장 낮은 Exception level만 레지스터에 접근할 수 있도록 한다.
- PE의 현재 state를 정의하는 PSTATE 원소의 개수를 정의한다.
- A64에선 이 PSTATE 원소를 수정하는 명령어가 지원된다.
- AArch32
- 32 bit Execution state
- 32-bit PC, SP, LR, … , 32 bit 범용 레지스터
- AArch64와 다르게 하나의 LR 레지스터를 ELR 레지스터로도 이용한다.
- SP도 하나이다.
- PE의 모드에 따라서 복수개의 banked 레지스터가 존재할 수 있다.
- 겉으로는 안보이지만 프로세서 모드가 변경됨에 따라 일대일로 매핑된다.
- A32, T32 명령어 세트가 지원된다.
- Armv7 Exception model을 지원한다.
- AArch64와 동일하게 PSTATE 를 수정하는 명령이 지원된다.
- APSR (Application Program Status Register)나 CPSR (Current Program Status Register)를 이용해서 접근한다.
- 공통적으로 Exception level에 대한 정보나 조건 플래그 등이 포함된다.
- PSTATE의 필드는 다음과 같다.
- CPSR은 현재 프로그램의 status를 저장하는 레지스터이다.
- CPSR은 다음과 같다.
- SPSR은 Saved Program Status Register로 CPSR을 백업해놓는 레지스터이다.
- exception이 발생했을때 SPSR에 CPSR을 백업하고 나중에 끝나면 CPSR을 CPSR로 복사해서 복원한다.
- PSTATE는 CPSR과 1:1 대응되는 개념은 아니지만 AArch64에선 CPSR 필드의 각 값을 PSTATE로 정의한다.
AArch64나 AArch32로 Execution state를 변경하는 것은 Exception level의 변경만으로도 수행이 가능하다.
다른 OS나 application, kernel, hypervisor가 다른 exception level이라면 다른 execution state에서 돌 수도 있다는 뜻이다.
A1.3.2 The Armv8 instruction sets
- AArch64
- A64
- 32 bit 인코딩을 이용한 고정 길이 명령어를 이용한다.
- A64
- AArch32
- A32
- 32 bit 인코딩을 이용한 고정 길이 명령어를 이용한다.
- T32
- 16 bit 와 32 bit 명령어 인코딩을 이용한 가변 길이 명령어를 이용한다.
- 이전 문서에선 Thumb mode라고도 말했다.
- A32
A1.3.3 System registers
대부분의 시스템 레지스터들은 <register_name>_ELx의 형태를 취하고 있다.
뒤에 x는 EL0같이 그 레지스터를 접근할 수 있는 최소의 Exception level이 명시되어있다.
B1 The AArch64 Application Level Programmers’ Model
B1.3 Software control features and EL0
EL0에서의 software control feature에 대해 설명한다.
B1.3.1 Exception handling
Arm 아키텍처에서 exception은 프로그램의 실행흐름을 바꾼다.
그리고 이를 처리하는 특정 exception handler는 EL0 보다 높은 exception level에서 동작하며 vector에 정의되어있다.
Exception 들은 다음을 포함한다.
- Interrupts.
- Memory system aborts.
- UNDEFINED 명령에 대한 실행으로 발생하는 exceptions.
- System calls.
- Secure monitor or Hypervisor traps.
- Debug exceptions.
D1 The AArch64 System Level Programmers’ Model
D1.1 Exception levels
Armv8-A 아키텍처는 EL0 ~ EL3 까지의 Exception level이 존재한다.
- ELn
- ELn에서 n이 증가할 수록 execution privilege는 증가한다.
- EL0에서의 실행은 unprivileged execution이라고 불린다.
- EL2부터는 가상화를 지원한다.
- EL3에선 Secure state와 Normal state 변경을 지원한다.
- EL3에선 Secure monitor를 포함한 저수준 펌웨어가 위치한다.
위와 같은 구조를 가지고 있다.
EL2와 EL3는 하이퍼바이저가 지원되는지, secure monitor가 지원되는지에 따라 없을수도 있을 수도 있다.
또한 PE는 무조건 연속적으로 Exception level을 구현할 필요가 없다.
예를 들어서 EL0, EL1, EL3만 구현하는 것도 가능하다.
위처럼 EL2만 빼고 구현되는 경우도 있다.
D1.1.1 Typical Exception level usage model
일반적으로 다음과 같은 전형적인 사용 모델이 존재한다.
- EL0 - Applications
- EL1 - OS kernel
- EL2 - Hypervisor
- EL3 - Secure Monitor
D1.2 Exception terminology
exception의 용어에 대해서 설명한다.
exception과 interrupt는 ARM에서 엄밀하게 구분하지 않는다.
왜냐하면 exception도 일종의 synchronous interrupt라고 부르기도 하기 때문이다.
B1.3.1에서 exceptions에는 interrupts도 포함된다고 명시해놓기도 했다.
-
exception - 프로그램 실행 도중 동기적으로 발생하는 이벤트이다.
- 딱 정해진 기준이 있는 synchronous interrupt이다.
- 예를 들어서 system call이나 division by zero 같은 명확한 기준이 존재한다.
- 딱 정해진 기준이 있는 synchronous interrupt이다.
-
interrupt - 비동기적으로 발생하는 이벤트이다.
- hardware interrupt
- 하드웨어에서 비동기적으로 이벤트가 일어나서 명령어 흐름이 방해받는 경우를 말한다.
- software interrupt
- 명령어로 실행되는 interrupt이다.
- ex) x86의 INT
그리고 여기서의 동기 비동기를 조금 다르게 보면 exception은 CPU 내부 요인에 의해 발생했고, interrupt는 외부 요인에 의해 발생했다고도 볼 수 있다.
- hardware interrupt
-
Interrupt Request (IRQ)
- 일반적인 interrupt가 여기에 속한다.
-
Fast Interrupt Request (FIQ)
- IRQ보다 우선순위가 높다.
- 별도의 레지스터가 있어 처리가 빠르다.
- 이는 banked register를 말한다.
- ex) R7_fiq, …, R14_fiq
- 이런식의 banked register를 둬서 context switching 비용을 아낄 수 있다.
- 빠른 처리를 위해 쓰이기도 한다.
D1.2.3 Exception level
ELn에서 n이 크면 높은 Exception level이라고 하고, 반대도 마찬가지다.
다음과 같이 표현한다.
- AArch64 Execution state에선 AArch64의 Exception level을 사용한다.
- AArch32 Execution state에선 AArch32의 Exception level을 사용한다.
D1.2.4 Definition of a precise exception
위 사진은 precise exception과 imprecise exception에 대해서 나와있다.
- precise exception
- exception handler가 PE state와 memory system state를 넘겨받는 경우이다.
- 이때 PE의 전 명령어들은 모두 실행되었다는 것이 보장된다.
- imprecise exception
- 전 명령들이 실행되었다는 보장이 없다.
- 현대 프로세서들은 파이프라이닝같은 병렬 실행을 통해 퍼포먼스를 향상시키기 이러한 imprecise exception도 발생할 수 있다.
AArch64의 LDP, STP 같이 하나 이상의 single-copy atomic memory 액세스를 하는 경우엔 레지스터나 메모리에 값이 반영이 안될 수 있다.
D1.2.5 Definitions of synchronous and asynchronous exceptions
-
synchronous exception
- exception이 명령어의 실행 or 실행 시도에 의해 생성되는 동기 exception이다.
- return address가 exception handler에게 넘어가는 것이 보장된다.
- 즉 어떤 명령어가 exception을 발생시켰는지 식별할 수 있다.
- precise exception에 해당한다.
-
asynchronous exception
- exception이 명령어 스트림의 실행 or 실행 시도에 의해 생성되는 경우이다.
- return address가 exception handler에게 넘어가는 것이 보장되지 않는다.
- 어떤 명령어가 exception을 발생시켰는지 식별할 수 없다.
- imprecise exception에 해당한다.
D1.3 Execution state
- AArch64 - 64bit execution state
- AArch32 - 32bit execution state
Exception levels는 Exception state를 이용한다.
예를 들어서 EL0, EL1, EL2는 모두 AArch32를 이용할 수 있고 EL3 혼자 AArch64에서 돌아갈 수 있다.
그 뜻은 다음과 같다.
- 다른 software 계층, 각자 다른 exception level에서 동작하는 application, os kernel, hypervisor 들은 다른 execution state에서 실행될 수 있다.
- PE는 execution state를 다음과 같은 경우에 한해서만 변경 가능하다 .
- Reset시
- Exception level의 변경
D1.4 Security state
Armv8-A 아키텍처는 두 가지 security states를 지원한다.
security states는 Non-secure state와 Secure state로 나뉜다.
- 물리 메모리 주소 공간 차이
- Secure state
- PE가 Non-secure physical address와 Secure physical address 모두 접근이 가능하다.
- Non-secure state
- PE는 오직 Non-secure physical address만 접근 가능하다.
- Secure system이 관리하는 자원에 접근은 불가능하다.
- Secure state
D1.4.1 The Armv8-A security model
- 만약 EL3가 구현에 포함되어있다면, 무조건 두 가지의 security states가 포함된다.
- EL3는 오직 Secure state에만 존재한다.
- Non-secure state에서 Secure state로의 전환은 오직 EL3로의 Exception에 의해서만 발생한다.
- FEAT_SEL2가 구현되지 않았다면 EL2는 Non-secure state에만 존재한다.
- 만약 구현되었다면, EL2는 secure state에서 존재한다.
- 어떻게 virtual address가 Non-secure physical address와 secure physical address로 변환되는지는 D5에서 후술한다.
D1.5 Virtualization
가상화 기술은 모두 가상화 소프트웨어의 성능을 높히고자 하드웨어단에서 여러 옵션을 추가한 것이다.
가상화 지원은 EL2를 포함한 구현에만 적용된다.
기본적인 가상화 모델은 다음과 같다.
- Hypervisor 는 EL2에서 돌아가며 EL0와 EL1을 포함하는 가상 머신의 switching을 담당한다.
- Guest OS는 EL1에서 동작한다.
- Application은 주로 EL0에서 동작한다.
하이퍼바이저는 VMID를 각자의 가상머신에 할당한다.
EL2는 Guest OS 관리와 다음과 같은 제어를 한다.
- Guest OS에 의해 레지스터 읽기가 일어나면 가상 값을 리턴한다.
- Various Trap operations
- 메모리 관리와 다른 레지스터에 접근한다.
- Trap된 operation은 EL2에 exception을 발생시키게된다.
- interrupt들의 라우팅
- 현재의 Guest OS에게 라우팅되는 경우가 있다.
- 현재 돌고 있지 않은 Guest OS에게 라우팅되는 경우가 있다.
- 그리고 마지막으로 하이퍼바이저에게 라우팅되는 경우가 존재한다.
Armv8.1은 Virtualization Host Extensions (VHE)을 통해 Type 2 hypervisor를 지원한다.
간단하게 설명하자면 Host OS가 EL2에서 동작하도록 하는 extension이다.
그런데 도대체 상식적으로 어떻게 Host OS가 EL2에서 동작할지 이해가 되지 않을 수 있는데, 이는 뒤에서 마저 설명한다.
EL2는 다음과 같은 요소들을 구현한다.
- 구현은 EL2와 EL2 변환 과정의 독립적인 메모리 액세스를 보장한다.
- FEAT_VHE가 지원되면 EL2, EL0에서의 액세스에 변환 과정을 구현한다.
- EL1&0의 translation regime는 크게 두 가지 스텝을 거친다.
-
- Virtual Address (VA)를 Intermediate Physical Address (IPA)로 변환한다.
- 이는 EL1에서 처리되는데, Guest OS는 이 IPA를 PA로 믿고 처리한다.
-
- IPA를 실제 PA에 매핑한다.
- 이는 EL2에서 처리되며 당연하지만 Guest OS는 이를 알 수 없다.
- 이를 stage-2 translation이라고도 부른다.
- 다이어그램으로 나타내면 다음과 같다.
- 이는 나중에 VMSA 아키텍처에 대해서 설명하면서 2 단계 변환이라는 용어로 다시 나온다.
-
- FEAT_NV가 지원된다면 Guest hypervisor가 EL1에서 돌 수 있게 된다. ( Nested virtualization )
Hypervisor type
따로 메뉴얼에 나오는 내용은 아니지만, 나중에 이해를 위해서 꼭 필요한 내용이다.
Type 1이 가장 퍼포먼스가 좋다.
Type 1은 베어메탈 방식이라고도 불리는데, 그 이유는 하드웨어 위에 하이퍼바이저가 설치되기 때문이다.
Type2는 Host OS 위에 하이퍼바이저가 설치되는데, 개인용 교육용으로 자주 쓰인다.
Hosted hypervisor라고도 자주 부른다..
추가적으로 요즘에 자주 보이는 컨테이너는 Host OS를 공유하고 그 위에 하이퍼바이저 대신 컨테이너 엔진을 통해 퍼포먼스를 높힌다.
하드웨어 수준으로는 가상화되지 않고 OS 수준으로 가상화가 된다.
그래서 Host OS와 Guest OS는 동일한 OS여야 한다.
이를 Type 3 hypervisor라고도 한다.
모두 이렇게 전형적인 type으로 분류되는건 아니지만 대부분은 이렇게 나눈다.
hypervisor mode
ARM에서 Hypervisor mode를 따로 추가하게 된 배경은 앞에서 설명한 type 1 hypervisor, standalone hypervisor 때문이다.
CPU가 user와 supervisor mode만 지원하는 상태에서 standalone hypervisor를 구조적으로 설계하려면 user와 supervisor mode 두 가지로 나눈것으로는 부족하다.
당연하겠지만 만약 hypervisor를 supervisor mode에서 돌리고 기존 guest os를 user mode에 올리면 supervisor mode를 염두에 두고 설계된 OS가 동작할 수 없게 된다.
그렇다고 Guest os와 hypervisor를 같은 supervisor mode에 공존하게 하면 기본적으로 격리를 염두에 두고 만들어진 hypervisor의 의미가 없어진다.
이런 문제를 해결하기 위해서 ARM은 Hypervisor mode를 따로 넣어서 Guest os와의 교통정리를 끝냈다.
그런데 KVM같은 Type 2 hypervisor, hosted hypervisor의 경우엔 좀 애매하다.
Host OS의 모듈의 형태로 동작하는 KVM의 특성상 hypervisor는 일부 기능을 무조건 Host os에게 가져와야한다.
그런데 전통적으로 Host OS는 EL1에서 동작했다.
Host os가 EL1에서 동작하면 KVM도 EL1에서 동작해야한다.
Guest os보다 더 높은 권한을 가지려면 무조건 EL2에서 동작해야하는데, 기존 Host os가 EL1에서 동작해서 문제가 발생한다.
이러한 문제를 해결하기 위해서 KVM은 구조를 Lowvisor와 Highvisor로 분할했다.
Lowvisor를 hypervisor mode에서 돌리고, VM의 명령을 trap해서 highvisor로 넘긴다.
이런 구조로 나눠버리면, EL1에서 돌도록 설계된 OS 별다른 문제없이 그대로 동작하면 되고 동시에 Hypervisor의 동작도 수행할 수 있다.
intel, amd, arm에서 하드웨어적으로 지원해주면, kernel module을 올려서 이러한 lowvisor까지 구현해줄 수 있다.
겉 모습으로는 기존 하이퍼바이저처럼 권한 체계가 분리된 것으로 보인다.
Highvisor가 커널에서 동작하기 때문에 VM을 돌리면서 필요한 기능은 host kernel code를 이용해서 처리할 수 있게 되었다.
그래서 이런식으로 trap이 이중으로 넘어간다.
이러한 Lowvisor은 EL2의 exception vector와 world switch에 대한 코드만 구현하면 되기 때문에 highvisor에 비해서 더 코드가 작다.
이런 구조가 동작하려면 bootloader에서 처음부터 Hypervisor mode에서 trap handler를 설치해서 나중에 KVM이 돌 수 있도록 해줘야한다.
논문 저자들은 vendor와 얘기해서 새로운 ABI를 어떻게 맞출지 고민하다가 결국 무산되었지만 좀 더 효율적이고 간단한 방법을 찾았다고 한다.
그냥 처음부터 ABI 신경쓰지 않고 bootloader recommendation으로 hypervisor mode로 부팅을 추가하고, kernel 코드를 조금만 수정해서 hypervisor mode 지원 유무를 파악하고 만약 지원된다면 그때 그냥 바로 hypervisor mode에 trap handler install을 해주면 되고 아니면 그냥 원래 legacy kernel 처럼 부팅하고 kvm 지원을 안하면 된다.
그러면 굳이 복잡한 bootloader ABI를 논의할 필요가 아예 없어진다.
Virtualization host extensions (VHE)
앞서 kvm을 예시로 type 2 hypervisor가 겪을 수 있는 문제를 어떻게 해결했는지에 대해서 알아보았다.
VHE는 앞서 Type 2 hypervisor를 지원하기 위해서 생겼다고 했다.
왜 이 얘기를 했냐면 원래 일반적으로 standalone hypervisor에선 다음과 같이 Exception level이 매핑된다.
만약 이런 구조가 type 2에서도 유지된다면 앞에서 다루었던 문제가 생긴다.
전통적으로 kernel은 EL1에서 돌고, 가상화 컨트롤은 EL2에서 담당했다.
이런 문제는 앞서 해결 방법을 이미 다루었다.
VHE는 이러한 문제 해결 방법을 더 최적화시키기 위해 개발되었다.
기존의 방법은 추가적인 context switching이 필요할 수 밖에 없기에 비효율적이다.
위와 같은 방식으로 설계하면 더 좋은 퍼포먼스를 얻을 수 있다.
전에는 kernel -> hypervisor -> guest로 이중 트랩이 걸렸었다면 이제는 host os가 hypervisor에서 돌게 되었으니 훨씬 빨라지게 된다.
Host OS를 EL2에서 동작시키기 위해서 ARM에선 HCR_EL2의 E2H와 TGE 비트를 추가했다.
- E2H - VHE의 활성화 여부를 결정한다.
- TGE - VHE가 활성화 되었을 때 EL0가 Guest인지 Host인지를 결정한다.
Host OS가 EL2에서 돌더라도 Host application은 EL0에서 돌기 때문에 이를 구분하려면 하드웨어적으로 TGE 비트를 만들어야할 필요가 생긴다.
다음 다이어그램처럼 구분이 된다.
위 다이어그램의 내용을 표로 정리하자면 다음처럼 정리할 수 있다.
VHE가 활성화되면 Virtual address space도 달라지게 된다.
VHE 이전에는 위처럼 EL0/EL1에는 두 개의 VA range를 지원했고 EL2, EL3에서는 전통적으로 application이 존재하지 않으므로 single VA range 만을 지원했다.
원래 EL0/EL1에선 ASID를 둬서 application별 주소 공간을 명시했다.
때문에 같은 VA에 대한 TLB를 flush할 필요가 없었다.
ASID에 대한 자세한 설명은 뒤에 D5.2.1에서 후술한다.
EL2에는 host application이 없어서 원래 ASID가 존재하지 않았다.
그런데 Host OS가 EL2에서 효율적으로 동작하려면 ASID 지원을 추가해야한다.
왜냐하면 FEAT_VHE가 활성화되면 커널이 EL2에서 돌아가기 때문에 EL1에서의 이유와 마찬가지로 context switching시에 VA에 대해서 PA로의 변환을 EL2에서 담당하기 때문이다.
MMU는 동일 VA에 대해서 다른 PA로 변환해야하기에 FEAT_VHE의 경우엔 ASID support가 EL2에도 필요할 수 있다는 것이다.
위 다이어그램은 E2H == 1 일 때의 모습이다.
HCR_EL2.TGE 비트가 Host OS 위에서 도는 EL0인지 Guest OS 위에서 도는 EL0인지를 결정한다.
VHE가 제대로 동작하려면 특정 register에 대한 액세스도 리다이렉팅해야한다.
당연한 이유지만 unmodified kernel은 EL1을 염두에 두고 개발되었다.
이를 EL2에서 돌리려면 E2H bit에 따라 TTBR0_EL1의 경우 TTBR0_EL2 혹은 TTBR0_EL1으로 리다이렉팅한다.
그런데 단순히 커널을 E2H에 따라 TTBR0_EL2로 리다이렉팅 해버리면, 커널 모듈식으로 붙어있는 하이퍼바이저는 EL1에 접근할 수가 없게 되는 문제가 발생한다.
원래 구조가 Guest OS들이 EL1에서 동작하기 때문에 EL2에서 무조건 EL1에 대한 접근이 필요하다.
이러한 문제를 해결하기 위해서 따로 EL12와 EL02 접미사를 가진 레지스터들을 추가했다.
그래서 위와 같이 EL2 하이퍼바이저에서 EL1 레지스터에 접근할 수 있게 된다.
D1.6 Registers for instruction processing and exception handling
Arm 아키텍처에서 레지스터는 다음 두 가지 카테고리로 분류된다.
- system control이나 status reporting을 제공하는 System register.
- 명령 처리에 이용되는 레지스터.
- ex) 연산이나 exception handling
D1.6.2 The stack pointer registers
AArch64 state에선 범용 레지스터 외에도 dedicated 스택 포인터 레지스터가 각자의 exception level에 따라 구현된다.
- SP_EL0, SP_EL1
- EL2가 구현되었다면, SP_EL2
- EL3가 구현되었다면, SP_EL3
이러한 스택 포인터들의 선택은 다음과 같이 이루어진다.
- EL0에선 PE는 SP_EL0를 이용한다.
- 다른 exception level에선 SP_EL0 혹은 그에 맞는 SP_ELx가 선택될 수 있다.
- 기본적으로 exception이 받아질때는 그 타겟 exception level에 맞는 SP_ELx가 선택된다.
- SP_EL0에 액세스하려면 PSTATE SP를 업데이트 함으로써 액세스할 수 있다.
다음과 같은 접미사를 붙여서 스택 포인터를 표현하기도 한다.
- t - SP_EL0 스택 포인터를 이용한다.
- h - SP_ELx 스택 포인터를 이용한다.
근데 여기서 궁금했던 점이있었다.
각자의 ELx에 대해서 SP_ELx 라는 물리 레지스터 하나씩 갖고 있으면 됐지 왜 SP_EL0이 또 필요한가라는 점이 궁금했다.
근데 이 답에 대한 힌트는 아래 문구에 있었다.
왜 이 두 가지를 굳이 나눠서 논리 SP_ELxt, SP_ELxh로 나눠서 7개의 논리 SP를 가지도록 만들었을까?
실제로는 물리 SP_ELx는 단 4개 밖에 없지만 이렇게 나눈 이유가 있었다.
일단 Exception은 같은 EL에서도 발생할 수 있다고 했었다.
exception이 raise되면 PE는 현재 exception level 이상, 타겟 exception level에 진입하고 SP_ELxh를 이용한다.
이때 handler는 최대한 PSTATE.SPSEL= 0으로 SP_ELxt를 이용해서 SP_EL0를 이용하려 시도한다.
각자의 SP에 대해서 물리적으로 SP_ELx는 필요하다.
AArch64에서 격리를 유지하면서 exception 발생시 바로 상위 exception level로 뛸때 동작할 스택이 저장되야하기 때문이다.
그런데 SP_ELxh를 계속 이용하지 않고 현재 SP를 저장하고 SP_EL0를 초기화해서 다시 이용하는 이유는 만약 SP_ELxh만을 SP로 이용했다면 nested exception이 발생했을 때 그전 SP_ELxh를 저장할 수 없기 때문이다.
SP_ELxh, handler SP를 핸들링에 이용하게 될 때 같은 exception level에 대해서 nested exception이 발생하면 본래의 SP_ELxh는 무조건 날라가는 문제가 생겨서 exception handler는 exception 발생시 최대한 빠르게 레지스터들 백업하고 SP_ELxt로 진입하려 시도한다.
D1.6.4 Saved Program Status Registers (SPSRs)
앞서 설명했던 SPSR의 용도를 생각해보면 당연히 얘도 Exception level 마다 존재한다.
- AArch64의 EL1에서 exception이 발생했을 때 SPSR_EL1을 이용한다.
- EL2가 구현되었다면 EL2에서의 exception은 SPSR_EL2를 이용한다.
- EL3가 구현되었다면 EL3에서의 exception은 SPSR_EL3를 이용한다.
특정 exception level에서 exception이 발생해서 PE가 처리해야할 때 PE state는 SPSR_ELx의 PSTATE에 저장된다.
즉 다음과 같은 의미를 가진다.
- exception에서 리턴할 때 PE state를 SPSR_ELx의 참조를 통해 복원할 수 있음을 나타낸다.
- exception 발생 당시의 PSTATE 값을 알 수 있다.
이때 저장되는 모든 PSTATE 필드들은 AArch32에서만 의미있거나 직접 읽기 쓰기 권한이 없더라도 저장된다.
AArch32에서만 의미있는 필드들은 exception이 AArch32 state에서 AArch64 state로 발생했을 때 저장된다.
D1.6.5 Exception Link Registers (ELRs)
exception link register는 exception return address를 저장한다.
PE가 exception을 받을 때 마다 ELR_ELx에 return address가 저장된다.
예를 들어서 EL1에 대한 exception이 발생했을 때에는 return address가 ELR_EL1에 저장되고 return하면 PC는 ELR에 저장된 return address로 복귀한다.
AArch64 state는 다음과 같은 ELR을 제공한다.
- EL1에 대한 exception은 ELR_EL1을 이용한다.
- EL2가 구현되었다면, EL2에 대한 exception은 ELR_EL2를 이용한다.
- EL3가 구현되었다면, EL3에 대한 exception은 ELR_EL3를 이용한다.
D1.7 Process state, PSTATE
Armv8-A 아키텍처에서 PSTATE는 process state를 나타낸다.
PSTATE 필드는 다음과 같다.
- The condition flags
- N - 음수 플래그
- Z - 제로 플래그
- C - 캐리 플래그
- V - 오버플로우 플래그
- The execution state controls
- SS - Software Step bit
- 좀 더 높은 exception level에서 lower exception level에 대해서 single-step 명령을 실행시킬 수 있다.
- 디버거의 single step 생각하면 된다.
- IL - Illegal Execution state bit
- PSTATE.IL이 1이면 어떤 명령이 시도되더라도 Illegal Execution state exception이 발생한다.
- 만약 EL0에서 IL이 1이 되면 EL1에서 이를 처리한다.
- nRW - Current Execution state
- AArch64에서 0으로 세팅된다.
- AArch64를 사용하는 ELx로의 Warm reset 혹은 Exception이 받아질때의 CPSR.nRW는 0이다.
- 반대로 AArch32 일때는 1이다.
- EL - Current Exception level
- AArch64로의 Warm reset시에는 이 필드는 가장 높은 Exception level을 가리키게 된다.
- SP - Stack pointer selection bit
- 전에 SP_ELxt SP_ELxh 두 가지 옵션이 지원된다고 설명했었는데, 이때 이 두 가지 옵션을 결정하는 비트가 PSTATE.SP 비트이다.
- 이때 t 접미사가 붙은 SP_ELxt는 SP_EL0를 가리킨다.
- Warm reset이나 AArch64 state로의 exception은 이 비트를 1로 만든다.
- 이는 SP_ELx가 선택된다는 뜻이다.
- SS - Software Step bit
- The exception mask bits
- D - Debug exception mask bit
- 이 비트가 설정되면 debug exception을 막는다.
- Warm reset이나 AArch64로의 exception을 받으면 1로 세팅된다.
- A, I, F - Asynchhronous exception mask bits
- A - SError, 시스템 에러 interrupt를 막는다.
- I - IRQ interrupt를 막는다.
- F - FIQ interrupt를 막는다.
- D - Debug exception mask bit
- Access control bits
- PAN - Privilege Acess Never state bit
- PAN가 1일때 어떠한 EL1 or EL2의 가상 메모리 주소 privileged data access
- PAN - Privilege Acess Never state bit
D1.9 Reset
- Cold reset
- PE가 실행중인 모든 로직을 리셋한다.
- 이러한 로직은 cold reset domain에 속한다고도 표현한다.
- power up시에 reset이 cold reset이다.
- Warm reset
- PE가 실행중인 일부 로직을 리셋한다.
- 이러한 로직은 warm reset domain에 속한다고도 표현한다.
warm reset에서 리셋되는 로직은 cold reset에서 무조건 리셋된다.
RMR_ELx 레지스터가 구현되어있다면, RMR_ELx.RR 비트를 1로 바꿈으로써 warm reset 요청을 할 수 있다.
이러한 리셋시에는 PE는 구현된 가장 높은 Exception level로 진입한다.
가장 높은 exception level이 동작할 때 다음과 같은 특징을 가진다.
- 무조건 Reset Management Register(RMR)이 구현되어있어야한다.
- RMR이 하나라면 가장 높은 exception level에 한해서 구현된다.
- Cold reset 시에 execution state는 configuration input signal에 의해 결정된다.
- Warm reset 시에는 execution state는 RMR_ELx.AA64에 의해 결정된다.
가장 높은 exception level이 AArch64 state로 설정되어있고, reset(warm, cold)시에 다음이 수행된다.
- SP_ELx가 선택된다.
- IMPLEMENTATION DEFINED인 PA address에서 실행을 시작한다.
- 가장 높은 exception level 레지스터 RVBAR_ELx가 그 주소를 가지고 있다.
- 당연하겠지만, RVBAR_EL0은 없다.
- 최소가 EL1이기 때문이다.
D1.9.1 PE state on reset to AArch64 state
reset 이후 대부분의 PE state는 UNKNOWN이다.
그래도 일부의 PE state는 정의되어있다.
PE가 reset을 하면서 PE state는 정의된대로 돌아가게 된다.
예를 들어서 PSTATE.{D, A, I, F} interrupt mask는 1로 세팅된다.
왜냐하면 reset 시에는 저러한 interrupt를 핸들링할 벡터가 설정되지 않았기 때문이다.
범용 레지스터, 부동 소수점, SIMD 들도 UNKNOWN 상태로 설정된다.
이런식으로 초기 세팅의 값으로 돌아가게 된다.
D1.15 System calls
System call은 SVC, HVC, SMC 명령에 의해 발생한다.
- The Supervisor Call (SVC) 명령은 user mode 프로그램이 os 서비스를 요청할 때 이용한다.
- EL1을 타겟팅하는 synchronous exception 이다.
- The Hypervisor Call (HVC) 명령은 guest OS가 hypervisor 서비스를 요청할 때 이용된다.
- EL2를 타겟팅하는 synchronous exception 이다.
- HVC 명령은 EL0와 Secure state EL1에서 정의되지 않는다.
- The Secure monitor Call (SMC) 명령은 Normal world가 Secure world 서비스를 요청할 때 이용된다.
- EL3를 타겟팅하는 synchronous exception 이다.
- 당연히 EL0에서 SMC는 정의되지 않는다.
다음과 같은 것들이 가능하다.
- EL2와 EL3는 Hypervisor call exception을 비활성화 할 수 있다.
- EL2는 SMC로 trap을 걸 수 있다.
- EL3는 Secure monitor call exception을 비활성화 할 수 있다.
D5 The AArch64 Virtual Memory System Architecture
D5.1 About the Virtual Memory System Architecture (VMSA)
MMU가 VA를 PA 변환한다.
이러한 VA가 PA로 변환되는 방식은 Exception level과 Security state에 따라 다르다.
MMU는 단순히 변환만 하는게 아니라 액세스 권한과 속성에 대한 검사도 진행한다.
이러한 변환 단계는 단일 단계일 수도 있고 두 가지 연속적인 단계가 필요할 수도 있다.
여기서 Exception level에 따라 독립적으로 주소 변환이 이루어진다는 말은 다음과 같다.
위와 같이 변환될때 Exception level과 security state에 따라 translation regime가 다른것을 확인할 수 있다.
각자의 가상 주소 공간을 가진다.
이런식으로 각자의 가상 공간의 테이블과 설정? 들을 translation regime라고 칭한다.
D5.1.1 Armv8 VMSA naming
VMSA 네이밍은 가능한 주소 변환 단계를 나타낸다.
- VMSAv8
- 아래 두 가지를 모두 아우르는 scheme이다.
- VMSAv8-32
- AArch32의 Exception level에서 핸들링되는 단일 단계 변환 scheme이다.
- 가끔 VA를 PA에 매핑하기 위해서 두 단계를 거치기도 한다.
- 이런 단계들은 모두 AArch32의 Exception level에서 처리된다.
- VMSAv8-64
- VMSAv8-32의 AArch64 버전이다.
D5.1.2 The Armv8 VMSA when some Exception levels are using AArch32
Higher exception level에선 AArch64를 사용하는 반면 Lower exception level에서는 AArch32를 사용할 수 있다고 앞에서 설명했다.
EL0에서 AArch32를 쓰더라도 좀 더 높은 exception level EL1에서는 AArch64를 쓰는 경우에선 EL0는 VMSAv8-64 방식을 이용한다.
D5.1.3 VMSA address types and address spaces
-
Virtual address (VA)
- 명령어 실행에 이용되는 주소이다.
- AArch64 state 에서 VA는 다음과 같은 최대 address width를 가진다.
- 48 bits.
- 52 bits. - FEAT_LVA가 구현되어 주소 변환시 64kb granule를 이용할 때.
- 52 bits. - 다음이 참일 때.
- FEAT_LPA2가 구현되었을 때.
- TCR_ELx.DS == 1 일때 translation regime가 바뀐다.
- TCR 레지스터는 Translation control register이다.
- 4kb나 16kb granule를를 사용할 때.
- 하나의 주소 변환 단계 (Translation stage) 가 하나의 VA 범위를 변환하는 경우
- 48-bit VA
- 0x0000000000000000 to 0x0000FFFFFFFFFFFF
- 52-bit VA
- 0x0000000000000000 to 0x000FFFFFFFFFFFFF
- 48-bit VA
- 하나의 주소 변환 단계가 두 개의 VA 범위로 나누어서 변환하는 경우
- bottom VA
- 48-bit VA
- 0x0000000000000000 to 0x0000FFFFFFFFFFFF
- 52-bit VA
- 0x0000000000000000 to 0x000FFFFFFFFFFFFF
- 48-bit VA
- top VA - 64bit를 모두 사용한다.
- 48-bit VA
- 0xFFFF000000000000 to 0xFFFFFFFFFFFFFFFF
- 52-bit VA
- 0xFFF0000000000000 to 0xFFFFFFFFFFFFFFFF
- 48-bit VA
- 이렇게 나눠놓은 이유는 두 개의 subrange에 대해서 다르게 처리해야 할 필요가 있어서 그런 것 같다.
- bottom VA
-
Intermediate physical address (IPA)
- 주소를 변환하면서 두 가지 주소 변환 단계가 있다.
- stage 1에서의 OA - 1 단계 변환의 출력 주소
- 이러한 1단계 변환에선 OS는 IPA를 PA로 착각한다.
- 처음에 변환되기 때문에 1단계 변환이라고 부른다.
- stage 2에서의 IA - 2 단계 변환의 입력 주소소
- IPA를 PA로 변환하는 단계이다.
- 1 단계 변환이 변환한 IPA를 PA로 변환해서 2단계 변환이라고 부른다.
- stage 1에서의 OA - 1 단계 변환의 출력 주소
- 변환 과정에서 IPA와 PA는 비슷하다.
- 가상머신으로 운영체제를 실행했을 때 이러한 두 계층의 주소 변환이 필요하게 되는데 첫 번째 계층의 출력 주소로 볼 수 있다.
- 주소를 변환하면서 두 가지 주소 변환 단계가 있다.
-
Physical address (PA)
- 실제 물리적인 주소이다.
- EL3에서 Secure EL1과 FEAT_SEL2가 활성화되면 Secure EL2 exception level은 Secure, Non-secure operation의 독립적인 PA space로 나눈다.
- Secure state에선 Secure PA space를 이용한다.
- Non-secure state에선 Non-secure PA space를 이용한다.
D5.1.4 Address tagging in AArch64 state
AArch64 state에서 address tagging을 지원한다.
여기서의 Address tagging과 Memory tagging extension을 혼동하면 안된다.
Address tagging의 VA의 상위 8비트는 다음을 결정할 때 무시된다.
- 변환 시스템이 활성화 되어 있을 때, 그 VA가 범위 밖인지 아닌지 여부.
- translation fault가 일어난다.
- 변환 시스템이 활성화 되어 있지 않을 때 주소가 범위 밖인지 여부.
- address size fault가 일어난다.
- TLB invalidation 명령 실행시 그 주소가 invalidation이 필요할지 여부.
1 단계 변환에서 두 개의 VA 범위를 지원할 때 다음이 참이다.
- VA의 bit[55]에 따라 다음과 같이 address tag를 사용할지 여부를 결정하는 register bit를 결정한다.
- VA[55] == 0
- TCR_ELx.TBI0 address tag를 사용할지 여부를 결정한다.
- 1 단계 변환이 활성화되었을 때 TTBR0_ELx는 변환 테이블(translation table)의 base address를 저장한다.
- TTBR은 Translation table base register로 x86의 CR3처럼 특정 페이지 테이블을 가리킨다.
- context switching이 일어날 때 마다 바뀌게 되는 특징이 있다.
- VA[55] == 1
- TCR_ELx.TBI1 address tag를 사용할지 여부를 결정한다.
- 1 단계 변환이 활성화되었을 때 TTBR1_ELx는 변환 테이블(translation table)의 base address를 저장한다.
이런식으로 두 개의 범위를 tagging을 통해 처리한다.
- VA[55] == 0
1단계 변환에서 오직 하나의 VA 범위를 지원할 때 다음이 참이다.
- 1단계 변환이 활성화 되었을 때 TCR_ELx.TBI가 address tag가 사용되는 여부를 결정한다.
- TTBR0_ELx는 변환 테이블의 base address를 저장한다.
왜 조건에 1 단계 변환의 활성화가 적혀있는지 의문을 가질 수 있는데, 그 이유는 TCR_ELx.TBIn bit는 해당 변환 단계의 사용 여부와 무관하게 address tags 사용 여부를 결정되기 때문이다.
또한 FEAT_PAuth가 구현되어있다면, TBIDn 비트가 TCR_ELx에 추가된다.
TCR_ELx.TBIn 비트가 켜져있다면, TBIDn 비트가 address tagging이 data address만 적용할 것인지 instruction address까지 적용할지 결정한다.
이러한 Address tag 활성화 비트는 다음과 같은 예시처럼 PC의 영향을 줄 수 있다.
- controlled exception level 에서, TCR_EL3.TBI는 다음과 같은 영향을 제어한다.
- EL3에서의 분기나 함수 return.
- EL3로의 exception.
- exception return, EL3로 debug state exit.
마지막 debug state에서 나갈때는 정확히 잘 이해가 되지 않는다.
TBIn 비트가 어떻게 제어되느냐에 따라 다음과 같은 영향을 가진다.
- 1 단계 번역이 두 가지의 VA range를 지원한다.
- PC에 로딩되는 주소 55비트가 1이고 TBIn 비트가 1이라면 bits[63:56] 부분의 PC는 bit 55에 대한 sign extension이 일어나게된다.
- 사실 이건 앞에서 범위 얘기하면서 다뤘던 얘기이다. 그냥 같은 얘기.
- 1 단계 번역이 하나의 VA range를 지원한다.
- 55비트가 1이여도 bits[63:56]는 0x00으로 고정된다.
깔끔하게 정리하면 다음과 같다.
TBI(Top byte ignorance) 비트에 따라 55 bit가 무시될지, 두 개의 VA range를 지원하는지 결정된다.
D5.2 The VMSAv8-64 address translation system
VMSAv8-64 주소 변환 시스템에 대해서 설명한다.
D5.2.1 About the VMSAv8-64 address translation system
MMU는 PE에 액세스에 대해 주소 변환과 액세스 권한 그리고 메모리 속성 결정과 체크를 담당한다.
기본적인 MMU의 동작 모델은 원하는 메모리의 액세스에 대한 정보, 예를 들어서 IA(Input Address)를 받아들여 결과를 리턴한다.
- 관련된 OA(Output Address)를 리턴한다.
- 변환을 완료할 수 없으면, exception이 생성되며 이 exception을 MMU fault라고 부른다.
- 시스템 레지스터를 이용해서 이러한 발생한 MMU fault를 알린다.
IA -> OA로 매핑을 하는 과정을 단일 단계 변환 (single stage translation) 이라고 한다.
VMSAv8 system에서 translation regime는 VA -> PA 로의 매핑에 하나 혹은 두 개의 단계를 거쳐 변환을 수행한다.
translation granule은 IA -> OA로의 매핑의 세분성을 나타내며 이는 다음 두 가지를 정의한다.
- 주소 변환의 단계의 page size
- IA -> OA 매핑의 최소 단위 메모리 블록.
- 특정 주소 변환 단계의 변환 테이블 (translation table)의 최대 크기
지금에선 변환 테이블의 최대 크기가 이해되지 않을 수 있다.
D5.2.4에서 후술한다.
MMU는 system register에 의해 주소 변환 단계마다 주소 변환이 제어된다.
변환 단계마다 address translations의 집합과 관련 메모리 속성이 memory mapped table에 존재하며 그 테이블을 translation tables라고 부른다.
하나의 translation table은 IA를 제한된 특정 비트들을 이용해서 주소를 변환한다.
그 이유는 하나의 address translation이 여러번의 lookup를 통해 변환을 해야할 수 있기 때문이다.
그리고 이러한 translation table entry들은 Translation lookaside buffer(TLB)에 캐싱된다.
메모리 데이터가 L1, L2, L3 캐시에 캐싱되듯이, 주소들은 TLB에 주소 변환에 대한 데이터가 캐싱된다고 볼 수 있다.
mips 사진이긴 하지만 table entry에는 ASID를 명시해서 다른 프로세스별 주소 공간을 명시한다.
이를 통해 context switching을 할 때 굳이 tlb flush를 통해 entry를 날리지 않고 유지시켜도 격리가 유지되게 된다.
그리고 위와 같이 코드를 공유하는 경우에 효율적으로 메모리 공간을 이용할 수 있다.
IA -> OA 매핑에 대한 정보를 정의하는 translation table entry에서는 다음과 같은 속성들도 정의한다.
- Secure state에서 발생한 메모리 액세스, 즉 액세스가 Secure 혹은 Non-secure address 맵에 접근하는지 여부
- 액세스 권한
- memory region property
- 메모리 타입이나 캐시 정책등이 이러한 property에 포함된다.
AArch64 아키텍처에서 translation regime는 다음으로 구성된다.
- 단일 단계 주소 변환
- input VA를 바로 output PA로 변환한다.
- 연속적인 2 단계 주소 변환
- Stage 1 - input VA -> output IPA
- Stage 2 - input IPA -> output PA
앞에서 이미 봤던 사진인데, 이번엔 좀 더 자세히 알아보겠다.
- EL1&0이고 EL2가 비활성화 되었을 때 - 1 단계 변환
- 같은 PA를 공유하는 OS가 없다.
- 그러므로 굳이 IPA를 둬서 2 단계 변환을 할 필요가 없다.
- HCR_EL2.{E2H, TGE}가 {0,0} 일 때 EL1이나 EL0일 때 이러한 변환 regime가 이용된다.
- HCR은 Hypervisor control register로 하이퍼 바이저( EL2 )의 동작에 대해 나타낸다.
- E2H는 VHE가 지원되지 않을 때를 뜻하니 단순 VA를 PA로만 변환하면 된다.
- SCR_EL3.NS == 1 일 때 Non-secure 메모리 액세스가 일어난다.
- SCR은 Secure configuration register이다.
- EL1&0이고 EL2가 활성화 되었을 때 - 2 단계 변환
- 같은 PA를 공유하는 OS가 있다.
- OS가 착각하는 PA를 의미한다.
- 즉 IPA.
- 같은 물리 메모리 주소에 대해 다른 값을 리턴해야할 필요가 생긴다.
- 그래서 2 단계 변환을 통해 guest os의 PA를 IPA로 속여서 변환해야한다.
- 같은 PA를 공유하는 OS가 있다.
- EL2나 EL3 - 1단계 변환
- 그냥 바로 접근해도 상관없기 때문에 단일 단계 변환을 수행한다.
- EL2&0 변환 - 1 단계 변환
- HCR_EL2.{E2H, TGE} 비트가 {1,1} 일 때 적용된다.
- 즉 host os가 EL2에서 돌고 있고 host application이 EL0에서 돌고 있을때이다.
- 이런 경우엔 1 단계 변환만으로도 충분하다.
- 왜냐하면 말은 EL2에서 EL0 변환이지만, 실제로는 FEAT_VHE에 의해서 표면적으로 그렇게 보일뿐이지 실제로의 변환은 단순 VA -> PA만 하면 끝이다.
- HCR_EL2.{E2H, TGE} 비트가 {1,1} 일 때 적용된다.
단일 단계 주소 변환에서 TTBR_ELx는 IA를 OA로 매핑하기 위한 첫 번째 translation table을 가리킨다.
두 개의 VA range를 지원하는 경우 각자의 VA range는 독립된 IA -> OA 매핑을 가진다.
이는 다음을 말한다.
- 두 개의 IA ranges를 지원하면 두 개의 translation tables가 지원된다.
- 하나의 IA range를 지원하면 하나의 translation table이 지원된다.
각자가 다른 table을 이용하게 되면서 각자 독립적인 매핑을 가지게 된다.
여기서 명심해야할 점은 두 개의 IA ranges를 지원하는건 오직 1 단계 변환만 가능하다.
바꿔말하면 두 개의 IA ranges를 지원한다면, 그 IA는 무조건 VA 라는 것이다.
VMSAv8-64에서 주소 변환 단계는 Exception level에 영향을 받는다.
VMSAv8-64에서 translation table안의 descriptor entry는 64 비트이다.
VMSAv8-64 translation table format은 다음을 제공한다.
- 4 단계 이상의 주소 lookups
- 4kb 혹은 16kb, 64kb인 translation granule 크기
- IA/OA
- 주로 48 비트이지만 특정 FEAT_LPA2같은 feature에 따라 52 비트까지도 늘어날 수 있다.
D5.2.3 Controlling address translation stages
위 표에 나와있듯이 각자의 변환 단계에 매칭되는 시스템 레지스터들이 존재한다.
- SCTLR (System control register)는 Exception level 별로 존재한다.
- 이는 Exception level 마다의 설정이 달라야하니 필요하다.
- TCR (Translation control register)는 Exception level 별로 존재하고 EL2에서 특정 하이퍼 바이저의 설정에 따라 추가적으로 VSTCR_EL2가 이용될 수 있으며 기본적으로 EL2가 구현되었다면 VTCR_EL2가 이용된다.
- TTBR (Translation table base register)는 Exception level 별로 존재하며 마찬가지로 EL2의 특정 옵션에 의해 추가적으로 이용될 수 있으며 EL2가 구현되었다면 VTTBR_EL2가 이용된다.
- 앞서 address tagging에 대해서 설명하면서 TCR_ELx.TBI0와 TCR_ELx.TBI1가 활성화 되었을 때 VA의 55 비트에 따라 TTBR0_ELx를 이용하는지 TTBR1_ELx를 이용지를 결정한다고 했었다.
ID_AA64MMFR0_EL1.PARange 비트는 물리 주소 크기를 정한다.
활성화된 주소 변환 단계는 각자 TCR_ELx 레지스터를 가지며 IPS 비트에 따라 최대 output address의 크기도 정한다.
활성화된 변환 단계는 마찬가지로 각자의 TCR_ELx 레지스터를 가지며 TCR_ELx의 TxSZ 필드가 input address size를 지정한다.
- 두 개의 VA ranges를 지원하는 경우
- TCR_ELx는 두 개의 TxSZ 필드를 가지며 각자 하나의 VA range에 대응한다.
- TCR_ELx.T0SZ는 lower VA인 TTBR0_ELx에 대응한다.
- TCR_ELx.T1SZ는 higher VA인 TTBR1_ELx에 대응한다.
- TCR_ELx는 두 개의 TxSZ 필드를 가지며 각자 하나의 VA range에 대응한다.
- 단일 VA range를 지원하는 경우
- TCR_ELx의 T0SZ를 이용하며 TTBR0_ELx가 변환에 이용된다.
위 사진 single VA range를 지원하는 변환 단계의 메모리 맵이다.
EL1&0 regime에서 EL2가 켜져있으면 여러 OS가 PA를 공유할 가능성이 있으므로 2 단계 변환을 수행한다.
위 사진은 두 개의 VA ranges를 지원할 때의 메모리 맵이다.
- TCR_ELx의 T0SZ를 이용하며 TTBR0_ELx가 변환에 이용된다.
D5.2.4 Memory translation granule size
granule size는 다음을 정의한다.
- 하나의 translation table의 최대 크기
- memory 페이지 크기
위 표와 같이 1 단계 변환에선 ID_AA64MMFR0_EL1의 TGran4, 16, 64 필드를 통해 어떤 granule size가 지원되는지 확인할 수 있다.
2 단계 변환에서도 앞선 1 단계 변환처럼 똑같이 필드를 통해 granule size를 확인할 수 있다.
위와 같은 범위를 가진다.
전에 granule size가 page size와 최대 translation table의 크기를 정의한다고 했기 때문에 entry 개수는 2^12 / 2^3 = 2^9 개의 entries를 가지게 된다.
D5.2.5 Translation tables and the translation process
Translation table walk는 하나 혹은 그 이상의 translation table lookups를 말한다.
Translation table walk는 VA를 PA로 변환하기 위해 필요한 일련의 lookup들이다.
translation table walk는 가장 처음으로 TTBR_ELx을 읽어서 table의 base 주소를 얻는다.
그리고 각자의 translation table lookup은 다음중 하나를 포함하는 descriptor를 리턴한다.
- table entry가 walk의 마지막 entry이면, 그 entry는 OA와 OA에 대한 권한, 속성을 포함한다.
- 추가적인 lookup이 필요하면, entry는 다음 lookup을 위한 base address를 포함한다.
- 추가적으로 secure translation regime를 이용할 경우 그 base address가 secure인지 non-secure인지를 나타낸다.
- 따로 명시적으로 non-secure address space라고 명시할 수도 있다.
- 여기서 hierarchical attributes 라는게 적용되는데, 이는 일부 속성이 좀 더 높은 레벨의 테이블 descriptor에서 명시될 수 있다.
- 이는 접근 권한, 실행 권한 그리고 물리 주소 공간에서도 적용된다.
- PXNTable(실행 권한)으로 예시를 들어보면 좀 더 높은 level의 table에 비트가 세팅되면 좀 더 낮은 레벨의 비트들은 무시되는 것을 알 수 있다. 또한 PXN을 비트를 클리어 시켜서 전 계층의 비트를 보존시킬 수도 있다.
- descriptor가 유효하지 않다면 translation fault가 발생한다.
- 앞서 PXN같은 용어가 나왔는데 추가적을 설명하자면 page table entry에서 다음과 같은 4 비트가 인코딩된다.
- UXN, Unprivileged Execute never - 이 페이지에선 EL0 코드를 실행할 수 없다.
- PXN, Privileged Execute never - 이 페이지에선 EL1 코드를 실행할 수 없다.
- AP 2bits, 01이면 커널과 유저모드 모두 접근가능하다.
단일 단계 변환의 translation table walk에 대한 그림이다.
D_Block은 granule보다 큰 블록에 대한 descriptor이고 D_Page는 granule 크기에 대한 descriptor이다.
실제로 구현시에는 동일한 구조를 이용하여 구현한다.
3 level lookups가 필요한 경우에선 마지막에 D_Block 대신 D_Page로 의미상 이용된다고 한다.
위의 포맷을 보면 둘다 아예 같게 생긴 것을 확인할 수 있다.
TTBR_ELx는 초기 lookup을 위한 translation table의 base address를 가지고 있다.
- EL2가 활성화 되었을 때 EL1&0 translation regime를 제외한 1 단계 변환은 TTBR_ELx의 output address나 어떠한 translation table descriptor에 의해 리턴된 translation table의 base address는 모두 PA이다.
- EL2가 활성화 되었을 때 EL1&0 translation의 1 단계 변환에서 TTBR_ELx의 output address나 어떠한 translation table descriptor에 의해 리턴된 translation table의 base address는 IPA이다. 그리고 2 단계 변환이 활성화 되어있다면 OA는 2 단계 변환의 대상이 된다.
D5.2.6 Overview of the VMSAv8-64 address translation stages
TCR_ELx의 TnSZ는 IA 범위를 정한다.
4kb granule 기준으로 위 표처럼 initial lookup level이 결정된다.
TnSZ에 따라 initial lookup level이 달라진다.
이렇게 찾아간다.
VTCR_EL2.T0SZ에 따라 IA 크기가 정해지고 initial lookup level이 결정된다.
16kb granule도 표로 정리되어있긴 하지만 생략하겠다.
D5.3 VMSAv8-64 Translation Table format descriptors
일반적으로 descriptor는 다음중 하나에 해당한다.
- An invalid or fault entry.
- next-level translation table을 가리키는 table entry.
- Accesss를 위한 memory의 properties를 정의하는 block entry.
- 예약된 format.
D5.3.1 VMSAv8-64 translation table level -1, level 0, level 1, and level 2 descriptor formats
사실 포맷은 granule size나 최대 OA size 같은 여러 요인에 따라 달라진다.
그런데 그냥 일반적인 경우 포맷은 거의 비슷하다.
그래서 실질적으로 포맷은 위와 같다.
D.5.3.2 Armv8 translation table level 3 descriptor formats
이런식으로 granule size에 따라 포맷은 달라지게 된다.
D5.3.3 Memory attribute fields in the VMSAv8-64 Translation Table format descriptors
stage 1 translation에서 위 비트들은 다음 translation table access를 위한 속성을 정의한다.
- NSTable
- secure state에서 메모리 액세스할때 NSTable bit를 확인한다.
- non-secure state에선 EL2나 EL2&0을 포함해서 RES0이 되고 PE는 이를 무시한다.
- APTable
- 후속 level lookup시에 적용되는 permission bit들이다.
- 아래와 같이 적용된다.
- UXNTable or XNTable
- 후속 level lookup시에 적용되는 XN limit이다.
- naming은 stage 1 translation regime가 두 VA ranges를 지원하는지 유무에 따라 다르다.
- Stage 1이 두 개의 VA ranges를 지원할 때
- 이 필드는 UXNTable로 불린다.
- lower level lookup에서 그 region에서 fetch된 instruction이 EL0에서 실행되는 것을 허용하는지를 결정한다.
- Stage 1이 하나의 VA range를 지원할 때
- XNTable로 명명된다.
- PXNTable
- Privileged execute never.
- 후속 levels of lookup에 관여한다.
- 이 필드는 항상 2 VA ranges를 지원하는 stage 1 translation에서만 valid 하다.
- 당연히 User, Kernel 나눠져야하니까 당연하다.
- 그래야 다른 translation table 쓰고 각기 다른 처리가 가능하다.
아까 앞에선 table descriptor였는데 얜 page & block descriptor의 경우이다.
1 stage block & page descriptor 모습이다.
메모리 속성이 upper, lower로 나눠진다.
- 마찬가지로 1 stage에서 두 VA ranges면 UXN 아니면 XN.
- PXN도 마찬가지.
- AP는 data access permission 관리한다.
- table descriptor format은 AP의 0 번째 비트를 정의하지 않는다.
- translation regime가 두 VA ranges를 지원하면 얘는 valid하며 singe VA range면 RES1이다.
- NS는 Non secure bit다.
2 stage block & page descriptor의 모습이다.
- 전이랑 비슷한데 여러 비트들이 많으니 생략한다.
- S2AP로 이름이 바뀐게 있는데, 그냥 stage 2 data access permission 비트로 AP랑 똑같다.
D5.4 Memory access control
access control field는 PE가 특정 주소에 접근할 때 그 주소의 접근을 컨트롤한다.
허용안하면 MMU fault.
이는 다음에 적용된다.
- single exception level에 대한 변환, EL3 translation regime같은 거.
- EL0 그리고 높은 exception level에 대한 변환, EL1&0 translation regime 같은 거.
D5.4.1 About access permissions
translation table descriptor는 data accesses에 대한 access permissions과 instruction fetches에 대한 access permissions를 정의한다.
PAN이나 PXN같은거에 대해서 더 자세하게 설명하는 섹션이다.
access permission 관련 비트들은 앞서 다뤘던 translation table format에 있었다.
- stage 1 translation, AP가 data access permission을 정의한다.
- stage 2에선 S2AP가 data access permission을 정의한다.
- UXN, XN, PXN 필드가 instruction fetches의 access controls를 정의한다.
D5.4.2 About PSTATE.PAN
PSTATE.PAN이 1이면 EL1, HCR_EL2.E2H가 1 일때 EL2에서 EL0이 액세스 가능한 가상 주소 액세스는 Permission fault를 생성한다.
PSTATE.PAN 0이면 그 기능은 꺼진다.
그냥 SMAP랑 똑같은 거 같다.
세부사항이 있지만 패스한다.
D5.4.3 About PSTATE.UAO
PSTATE.UAO가 1이면 unprivileged instruction load/store가 EL1혹은 HCR_EL2.{E2H, TGE}가 {1,1}일 때 EL2가 EL0가 아닌 현재 exception level에 적용되는 memory access permissions에 적용을 받는다.
세부사항은 패스한다.
D5.4.5 Data access permission controls
stage 1 translation 에서 AP는 두 개 비트를 포함한다.
- AP 1번 비트
- EL0와 더 높은 exception level 중에서 선택한다.
- AP 2번 비트
- read only인지 read/write 가능한지 선택한다.
다음 액세스 세팅을 나타낼 수 있게 된다.
- read only인지 read/write 가능한지 선택한다.
- 모든 레벨에서 read-only.
- 모든 레벨에서 read/write.
- EL0에선 access 불가능하지만 higher exception level에서 read-only.
- EL0에선 access 불가능하지만 read/write.
바로 앞에서 두 개 VA ranges를 지원할 때 higher exception level, EL0에 대한 access permission을 설명했다.
여기선 1 stage translation에서 single exception level에 대한 data access permission을 설명한다.
singe exception level이라 굳이 아까 1번 비트는 있을 필요없어서 reserved 값을 가진다.
위 표처럼 정의된다.
EL2 활성화시 secure / non-secure EL1&0 translation regime에서 S2AP는 다음 표처럼 정의된다.
EL2 활성화 되어있을 때 EL1&0 translation regime는 2 stage address translation이 필요하다.
S2AP는 1 단계 말고 2 단계 translation에 대한 access permission을 정의한다.
1 stage에서 AP랑 결합되기도 한다.
이렇게 후속 lookups에서 어떤 영향을 주는지 볼 수 있다.
D5.4.6 Access permissions for instruction execution
Execute-never controls는 어떤 memory region의 instruction이 실행될 수 있는지를 정의한다.
- UXN, Unprivileged execute-never, stage 1 only
- 당연히 1 stage가 두 VA ranges를 지원해야 UXN을 쓸 수 있다.
- 이유는 앞서 계속 설명했으니 그냥 생략한다.
- 0이면 EL0에서의 execution 허용한다.
- XN, Execute-never
- single VA range면 UXN이 아니라 XN으로 정의된다.
- UXN과 같은 자리다.
- 0이면 실행을 허용한다.
- 이 translation 적용되는 모든 exception level에 적용된다.
- single VA range면 UXN이 아니라 XN으로 정의된다.
- PXN, Privileged execute, stage 1 only
- 두 VA ranges 지원할 때 이용된다.
- single VA range는 res0으로 정의되고 무시된다.
- 0이면 실행이 허용된다.
- 그냥 SMEP랑 똑같다.
- XN, Execute-never, stage 2 only
- EL2같은거 구현되었을 때 EL1&0 regime 쓰면 2 stage로 해야한다.
- 두 번째 stage의 XN비트다.
- FEAT_XNX가 활성화되었을 때 이런식으로 또 나뉜다.
- FEAT_XNX 아니면 53bit res0이고 하나만 EL0, EL1에 대한 execution 제어를 담당한다.
- EL2같은거 구현되었을 때 EL1&0 regime 쓰면 2 stage로 해야한다.
- 추가적으로 SCTLR_ELx.WXN이 1이면 어떤 메모리던 간에 writable하면 execute-never하다.
앞에 내용들 종합해서 stage 1 access permission들 정리하면 다음과 같다.
위 표는 두 VA ranges를 지원할 때 경우이고 아래 표는 single VA range만 지원할 때의 표이다.
아까랑 똑같은데 AP의 1번 째 비트가 reserved 값이 되면서 무시된다.
Stage 2 instruction execution permissions는 아까 앞서 설명했듯이 stage 2 translation의 XN 필드에 의해 정의된다.
AP도 S2AP에 의해 정의된다.
G1.12 Handling exceptions that are taken to an Exception level using AArch32
AArch64와 exception vector tables 형태가 약간 다르다.
다음과 같은 mode로 변환되게 된다.
일반적으로 cps를 통해 다시 Supervisor로 돌린다거나 하는식으로 mode switch를 한다.
G5.4 The VMSAv8-32 Short-descriptor translation table format
AArch64와 비슷하지만 그냥 단순히 몇 번째 비트인지만 달라졌다.
위와 같은 형태이다.
G7.3VMSAv8-32 organization of registers in the (coproc==0b1111) encoding space
arm trustzone을 분석할 때 arm 모드로 동작하는 경우가 있다.
얘네들이 mrs, msr 같은 명령어를 이용해서 시스템 레지스터를 컨트롤한다.
위와 같이 테이블이 정의되어있는데, 여기에 맞춰서 mrs, msr을 읽으면 된다.