KVM은 guest CPU 상태와 memory 번역을 번갈아 실행한다
QEMU는 장치와 VM 생명주기를 관리하고, KVM kernel module은 guest CPU가 native에 가깝게 실행되도록 EL2를 설정한다. guest가 스스로 처리할 수 있는 instruction은 계속 실행되고, privileged operation·Stage-2 fault·interrupt·MMIO 같은 사건에서 host로 나온다.
guest register와 pending exception을 담는 실행 단위.
guest physical address 범위를 userspace virtual memory와 연결한다.
guest IPA를 host PA로 번역하고 권한을 제한한다.
host가 처리해야 하는 사건과 userspace까지 전달할 사건을 구분한다.
struct kvm, kvm_vcpu, kvm_s2_mmu를 먼저 잡는다
| 객체 | 범위 | arm64 핵심 field |
|---|---|---|
| struct kvm | VM 전체 | arch.mmu, memslots, vgic와 VMID. |
| struct kvm_vcpu | 가상 CPU 하나 | arch.hw_mmu, sysregs, fault info, pending IRQ. |
| struct kvm_s2_mmu | Stage-2 translation | page-table object, VTCR, VMID와 nested MMU 관계. |
| struct kvm_run | Kernel↔QEMU 공유 ABI | exit_reason, MMIO data, system event와 debug 정보. |
struct kvm_s2_mmu와 struct kvm_vcpu_arch가 architecture state의 중심이다.
KVM_RUN에서 guest 진입까지
Host userspace → guest
QEMU ioctl(vcpu_fd, KVM_RUN)
→ kvm_arch_vcpu_ioctl_run(vcpu)
→ pending signal·timer·request 처리
→ vgic state와 guest sysreg 준비
→ kvm_arm_vcpu_enter_exit(vcpu)
→ kvm_call_hyp_ret(__kvm_vcpu_run)
→ guest world entry메인 loop는 kvm_arch_vcpu_ioctl_run()에 있다. guest에 한 번 들어갔다가 끝나는 함수가 아니라 exit를 처리하고 다시 들어가는 loop다.
VHE와 nVHE는 같은 목표를 다른 경계로 구현한다
| Mode | Host kernel 위치 | __kvm_vcpu_run |
|---|---|---|
| VHE | EL2 host register view 사용 | arch/arm64/kvm/hyp/vhe/switch.c |
| nVHE | host kernel은 EL1, 작은 hyp code는 EL2 | arch/arm64/kvm/hyp/nvhe/switch.c |
| pKVM | protected nVHE에서 host와 hyp 격리 강화 | hyp/nvhe/mem_protect.c 등 |
kvm_arm_vcpu_enter_exit()은 mode별 hyp call 세부를 감추고 공통 run loop에 반환한다.
Guest exit는 ESR의 EC를 handler table로 보낸다
guest가 trap되면 EL2는 ESR_EL2, FAR_EL2, HPFAR_EL2와 guest state를 저장한다. host의 handle_exit()은 exception index와 ESR EC를 보고 sysreg trap, WFx, HVC, instruction/data abort 등의 handler를 선택한다.
| Exit | Kernel 처리 | Userspace exit 여부 |
|---|---|---|
| WFI/WFE | block·yield 또는 조건부 재진입 | 대개 kernel 안에서 처리. |
| System register | emulation 또는 virtual state 갱신 | 대개 kernel 안에서 처리. |
| Stage-2 RAM fault | host page와 Stage-2 PTE 준비 | 해결되면 즉시 재진입. |
| Emulated MMIO | kvm_run에 주소·길이·방향 기록 | KVM_EXIT_MMIO로 QEMU 복귀 가능. |
Stage-2 Data Abort는 IPA를 복원해 memslot을 찾는다
Guest memory fault
guest VA → Stage-1 IPA 성공
→ Stage-2 IPA → PA 실패
→ ESR_EL2 DABT/IABT + HPFAR_EL2
→ handle_exit
→ kvm_handle_guest_abort
→ fault IPA 계산
→ memslot·HVA·VMA 검사
→ user_mem_abort 또는 MMIOkvm_handle_guest_abort()은 abort 종류, IPA 범위, nested 여부와 memslot을 검사한다.
guest VA나 FAR만으로 host physical page를 알 수 없다. HPFAR와 page offset, VTCR의 IPA 범위, memslot과 host VMA를 차례로 연결해야 한다.
user_mem_abort()가 host page와 Stage-2 PTE를 연결한다
- fault IPA를 GFN으로 바꾸고 memslot에서 HVA를 찾는다.
- host VMA와 fault type을 검사해 page size, writable·executable protection을 정한다.
- 필요하면 GUP나 fault-in으로 host physical page를 pin·resolve한다.
- huge mapping 가능 여부와 dirty logging·read-only 정책을 반영한다.
kvm_pgtable_stage2_map()으로 IPA→PA descriptor를 설치한다.- 필요한 cache maintenance와 Stage-2 TLB synchronization 뒤 guest를 재실행한다.
user_mem_abort()와 kvm_pgtable_stage2_map()을 이어서 읽는다.
RAM이 아닌 IPA는 QEMU device emulation으로 나갈 수 있다
memslot이 없거나 I/O bus에 등록된 영역이면 KVM이 load/store instruction의 size·direction·register를 해석한다. kernel 내 irqchip·timer처럼 처리 가능한 device는 안에서 끝나고, userspace device는 KVM_EXIT_MMIO로 QEMU에 전달된다.
QEMU가 read 값을 kvm_run.mmio.data에 채우고 다시 KVM_RUN하면 KVM이 guest register에 결과를 반영하고 fault instruction 다음으로 진행한다.
Dirty logging은 write permission을 이용해 변경 page를 찾는다
live migration 시작 시 Stage-2를 write-protect하고 첫 write fault에서 dirty bit를 기록하는 방식이 기본이다. hardware dirty-bit 기능이 있으면 trap 횟수를 줄일 수 있지만 log를 읽고 clear하는 ordering과 TLB invalidation이 필요하다.
HDBSS 같은 architecture 확장은 hardware가 Stage-2 dirty 상태를 기록하도록 돕는다. feature 존재, KVM 구현, userspace migration protocol 지원을 각각 확인해야 한다.
Linux v6.18.37 소스 지도
| 파일 | 심볼 | 책임 |
|---|---|---|
| arch/arm64/kvm/arm.c | kvm_arch_vcpu_ioctl_run, kvm_arm_vcpu_enter_exit | vCPU run loop |
| kvm/hyp/*/switch.c | __kvm_vcpu_run | EL2 state switch와 guest entry |
| kvm/handle_exit.c | handle_exit | ESR EC별 dispatch |
| kvm/mmu.c | kvm_handle_guest_abort, user_mem_abort | Stage-2 fault 해결 |
| kvm/hyp/pgtable.c | kvm_pgtable_stage2_map/unmap | Stage-2 descriptor 관리 |
| asm/kvm_host.h | kvm_s2_mmu, kvm_vcpu_arch | arm64 KVM 상태 구조 |
KVM exit와 fault를 직접 추적한다
Host 관찰
sudo trace-cmd record -e kvm -e kvmmmu -e irq ./run-vm.sh
sudo trace-cmd report
sudo perf kvm stat live
cat /sys/kernel/debug/kvm/* 2>/dev/null
qemu-system-aarch64 ... -d guest_errorsexit reason, fault IPA, memslot, userspace MMIO 여부를 같은 시간축으로 본다. exit 횟수가 많다는 사실만으로 KVM bug라고 판단하지 말고 WFI, timer, MMIO, Stage-2 fault 비율을 나눈다.
근거 자료
- Local sourceLinux v6.18.37 commit
0c503cf3dde2 - VirtualizationArmv8-A virtualization
- KVM ABIThe Definitive KVM API Documentation
- OverviewARMv9 가상화·플랫폼 해설