태그: VT-x

Intel Developer Manual – Virtual Machine Extensions(VMX) 소개

 

CPU는 VMX operation이라 불리는 동작을 통해 가상화를 지원한다. VMX operation에는 두 가지의 동작이 있다. 하나는 VMX root이고, 다른 하나는 VMX non-root이다.

VMX root : VMX가 작동하지 않을 때의 동작과 거의 유사하다. 주요한 차이점은 VMX 명령어를 지원한다는 것과, 특정 제어 레지스터로부터 값을 읽어들이는 것이 제한된다는 것이다.

VMX non-root: 몇 가지의 명령어가 제한되며, 가상화에 적합하도록 수정되었다. 그리고 특정 명령어(VMCALL 명령어 등)은 VM exits를 발생, 제어권을 VMM에게 돌려준다. 이때, 소프트웨어적으로 VMX non-root의 구동을 확인할 수 있는 방법은 없다. 그렇기 때문에 VMM은 게스트 소프트웨어가 자신이 가상 환경에서 실행되고 있다는 것을 알아차리지 못하게 할 수 있을 것이다. 실제로 소프트웨어가 특권 레벨 0에서 동작하고 있더라도, VMX는 게스트 소프트웨어가 원래의 특권 레벨에서 동작하도록 제한할 수 있다. 이것은 VMM의 개발을 쉽게 만들어 준다.

 

VMX 초기화

VMM이 VMX 모드로 진입하기 전에, CR4.VMXE[bit 13] = 1를 통해 VMX를 활성화해야 한다. 만약 CR4.VMXE = 0으로 VMXON을 실행하려 하면 Invaild-opcode exception을 발생시킨다.

VMX에 진입하면, CR4.VMXE를 초기화하는 것은 불가능하다. VMM은 VMX를 빠져나간 뒤 VMXOFF 명령어로 VMXE를 초기화 할 수 있다.

 

VMM 소프트웨어의 동작

  • VMXON 명령어를 통해 VMX 동작 모드로 진입한다.
  • VM Entry를 사용하여 VMM은 VM안의 게스트로 진입할 수 있다(한번에 하나씩)
  • VMM은 VMLAUNCH와 VMRESUME 명령어를 통해 VM entry를 발생시킨다. 그리고 VM exits를 사용하여 제어권을 다시 가져올 수 있다.
  • VM Exits는 VMM에 의해 지정된 엔트리 포인트로 제어권을 이동한다. VMM은 VM exits를 발생시켜 적절한 처리(non-root에서 수행할 수 없는 명령)을 하고, VM entry로 VM에 돌아온다.
  • VMM이 작동을 정지하고, VMX 동작에서 빠져나오려면 VMXOFF 명령어를 실행한다.

 

Virtual-Machine Control Structure(VMCS)

VMX non-root 동작과 VMX 전환은 VMCS라는 자료 구조체에 의하여 제어된다. 이것은 VMM이 메모리에 할당하는 4KB 크기의 구조체로, VM에 대한 정보들을 가지고 있다.

VMCS는 VMCS PointerVMPTR를 통해 접근할 수 있으며, 각 논리 프로세서에 하나씩 할당된다. VMPTR은 VMCS의 64비트 주소를 가리키고 있고, VMPTRST와 VMPTRLD명령을 통해 읽고 쓸 수 있다. VMM은 VMCS를 VMREAD, VMWRITE, VMCLEAR 명령어를 통해 제어한다. 이 때 VMPTR은 4KB Boundary에 맞게 정렬되어야 한다. 즉, 0~11비트의 값을 0으로 두어야 한다. 또한, 이 비트값들은 프로세서의 물리 주소값을 넘어서는 안 된다. 이 물리 주소값의 크기는 cpuid를 통해 구할 수 있다.

물리 주소 범위의 비트값이 EAX의 0~7비트를 통해 리턴된다.

VMM은 각각의 VM마다 별도의 VMCS를 사용할 수 있다. 만약 VM이 다중 프로세서를 가지고 있다면, 각 vCPU마다 하나의 VMCS를 설정할 수 있다.

 

VMCS는 크게 Current VMCS와 non-Current VMCS로 나뉜다. 각각의 VMCS들은 Active이거나 Inactive 상태일 수 있으며, Active 상태의 VMCS중 하나는 Current VMCS이어야 한다.

  • VMCS의 메모리 주소를 오퍼랜드로 하여 VMPTRLD를 실행할 경우, 해당 VMCS는 Active/Current VMCS가 된다. 다른 VMCS는 Active로 남으나, non-Current VMCS가 된다.
  • VMCS의 메모리 주소를 오퍼랜드로 하여 VMCLEAR을 실행할 경우, 해당 VMCS는 non-Active/non-Current/Clear VMCS 가 된다.
  • Active/Current/Clear VMCS의 주소를 오퍼랜드로 하여 VMLAUNCH를 실행할 수 있다.

즉, VMCLEAR -> VMPTRLD -> VMLAUNCH 순서로 VM을 활성화하게 된다.

 

VMX 동작 시의 제한

VMX 동작에서 프로세서는 CR0과 CR4의 몇몇 비트들을 고정시킨다. VMXON은 이 비트들에 다른 값이 들어갈 경우 동작하지 않는다.

그리고, IA32_FEATURE_CONTROL MSR (MSR address 3AH)또한 VMX 동작을 제어한다.

IA32_FEATURE_CONTROL MSR의 구조는 다음과 같다.

IA32_FEATURE_CONTROL

첫 번째 비트(Bit 0)는 논리 프로세서가 초기화 될 때 0으로 설정된다. VMX를 사용하기 위해서는 두번째(Bit 1)혹은 세번째(Bit 2) 비트를 1로 설정해 주어야 한다.

Bit 1은 SMX 동작시의 VMX 활성화를, Bit 2는 SMX 비활성화 시 VMX 활성화 여부를 제어한다. 이 비트들을 0으로 설정하고 VMXON을 실행할 경우 일반 보호 예외를 발생시킨다.

 

VMX non-Root Instructions – VM Entries

 

VMLAUNCH와 VMRESUME 명령어를 통해 VMX non-Root 모드로 진입할 수 있다.

하드웨어 가상화 명령어(VT-x)란 무엇인가?

 

인텔 VT-x는 하드웨어 가상화를 구현하기 위해 만들어진 명령어입니다.

VT-x는 VMX(Virtual Machine Extension)이라고도 불리며, 전가상화(커널 수정이 없는 가상화) 환경에서 특권 명령을 수행하는데 드는 오버헤드를 감소시킬 수 있습니다.

이것을 이해하기 위해서는 먼저 CPU의 동작 레벨에 대해 이해할 필요가 있습니다.

X86-64 CPU는 내부적으로 Ring이라 불리는 실행 레벨을 가지고 있고, 0-3까지의 레벨이 존재합니다.

이 때, Ring-0를 특권 모드라고 부르며, 이 모드에서는 모든 명령어를 실행할 수 있습니다. 때문에 시스템 자원을 관리하는 OS 커널이 Ring-0에서 동작하게 됩니다.

Ring 1-2는 예약된(Reserved) 상태이고, 우리가 보통 사용하는 프로그램들은 Ring-3에서 동작하게 됩니다. 이 Ring-3를 비 특권 모드 또는 유저 모드라고 부르며, 하드웨어에 직접 접근하거나, 중요한 레지스터의 값을 변경하는 등의 명령어를 실행할 수 없습니다. 이것을 통해 우리는 잘못 만들어진 프로그램이나 악성 프로그램이 시스템 전체를 망가뜨리는 것을 막을 수 있습니다.

 

그런데 호스트 OS의 커널이 Ring-0를 물고 있는 상황에서 VM의 커널이 어떻게 특권 명령을 실행할 수 있을까요?

 

X86 이전에 등장한 가상화에서는 이것을 ‘Trap and Emulate’로 처리했습니다. 게스트 OS의 실행을 쭉 감시하고 있다, 특권 명령이 발생하면 VMM이 그것을 캐치(Trap), 하이퍼바이저에서 처리하고(Emulate) 결과값을 게스트 OS의 커널에 돌려줬습니다.

리소스 소모가 크고 느리긴 하지만 확실하게 호환성을 보장할 수 있는 방법이었습니다.

하지만 X86은 ‘Trap and Emulate’만으로 가상화를 구현하기에 무리가 있었습니다. 그것은 아키텍쳐 상의 특정 명령어가 특권 모드와 비 특권 모드에서의 동작이 다르게 짜여져 있었기 때문에 단순히 Ring-3에서 동작하는 게스트 OS에서 Trap이 발생하는지를 따지는 것으로 처리할 수 없었기 때문입니다.

 

이 문제에 대해 VMware는 이진 변환을, Xen은 Hypercall을 통한 처리를 제안했습니다. 이진 변환은 이러한 명령어를 하이퍼바이저가 ‘적절하게’ 변환시켜 처리한다는 발상이었고, Hypercall은 게스트 OS의 커널을 수정하여 그러한 명령어를 Hypercall로 대체, 커널에서 문제가 되는 명령이 발생했을 때, Hypercall을 호출하여 하이퍼바이저가 명령을 처리하게 만드는 개념이었습니다.

Hypercall이 호출되면 Ring-0에서 명령이 실행됨을 의미하고, 시스템 애플리케이션에서 명령을 실행하면 그대로 Ring-3에서의 동작을 하게 됩니다.

당연히 이진 변환보다 Xen이 사용하는 Hypercall의 성능이 더 좋았고, 전가상화와 대비되는 반가상화라는 명칭을 갖게 됩니다.

 

그런데, X86 가상화가 확산되니 CPU 제조사에서는 이것을 하드웨어적으로 처리할 방법을 제공하게 됩니다. 그것이 바로 인텔의 VT-x와 AMD의 AMD-V입니다.

VT-x/AMD-V가 활성화되면 Ring-0가 Root/non-Root로 나뉘게 됩니다.

그리고 하이퍼바이저가 Ring-0 Root에, 게스트 OS가 Ring-0(non-Root)에서 실행됩니다. 그 위에 Ring-3가 존재하며, 이것은 게스트 OS마다 독립된 환경을 가지게 됩니다.

 

같은 Ring-0이지만. non-Root에서는 몇가지 명령어가 제한됩니다. 대신, 명령어가 Ring-0에서 실행되는지(커널인지) Ring-3에서 실행되는지(사용자 애플리케이션인지)가 명확하게 드러나므로, 명령어를 변환하거나 OS의 커널을 수정할 필요가 없습니다.

특권이 필요한 명령어가 발생하면, 하이퍼바이저로 제어권을 넘겨서 처리하면 됩니다.

때문에 성능상에서 많은 이득을 볼 수 있고, 대부분의 하이퍼바이저들은 하드웨어 가상화를 이용하고 있습니다.

 

 

참고:  Xen으로 배우는 가상화 기술:CPU 가상화(2013/한빛미디어) (김태훈 외 2명 저)