This is a simple kernel-based virtual machine for linux to understand AMD-V(AMD Virtualization) technology.
Run the make env
to generate the YAKVM environment image.
With this image, you can try kernel-based virtual machine in the image.
Run the make run
to boot up the qemu with yakvm.ko in /mnt/shares on guest.
Run the make debug
to debug the YAKVM on the yakvm environment
Run the make test
to run the tests on the yakvm environment
┌────────┐ fd
│emulator├────────┬───┬───┬─────────┬──┬───┐
└────▲───┘ │ │ │ │ │ │
│ │ │ │ │ │ │
fd│ │ │ │ │ │ │
user space │ │ │ │ │ │ │
───────────────────────┼────────────┼───┼───┼─────────┼──┼───┤
kernel space │ │ │ │ │ │ │
│ │ │ │ │ │ │
┌────▼─────┐ ┌───┼───▼───┼───┐ │ │ │
│/dev/yakvm│ │ │ │ │ │ │ │
└────▲─────┘ │ ┌─▼──┐ ┌──▼─┐ │ │ │ │
│ │ │vcpu│ │vcpu│ │ │ │ │
│ │ └────┘ └────┘ │ ┌───┼──▼───┼────┐
│ │virtual machine│ │ │ │ │
│ └──────▲────────┘ │ ┌─▼──┐ ┌─▼──┐ │
│ │ │ │vcpu│ │vcpu│ │
│ │ │ └────┘ └────┘ │
┌─────▼──────┐ │ │virtual machine│
│yakvm driver│ │ └──────▲────────┘
└─────┬──────┘ │ │
│ │ │
└───────────────┴─────────────────┘
┌───────────────────┐ ┌───────────────────┐
│ │ │ │
│ ┌──────────────┐ │ │ ┌──────────────┐ │
│ │ hsave area │ │ │ │ hsave area │ │
│ └──────────────┘ │ │ └──────────────┘ │
│ │ │ │
│ ┌───────────────┐ │ │ ┌───────────────┐ │
│ │Virtual Machine│ │vmrun vmrun│ │Virtual Machine│ │
│ │ Control Block ◄─┼─────┐ ┌────┼─► Control Block │ │
│ │ struct vmcb │ │ │ │ │ │ struct vmcb │ │
│ └───────────────┘ │ │ │ │ └───────────────┘ │
│ virtual cpu │ │ │ │ virtual cpu │
└─────────┬─────────┘ │ │ └─────────┬─────────┘
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│vmexit ┌┴──┴┐ vmexit│
└──────────────►host◄─────────────┘
└────┘
host must complete the following steps to execute the virtual machine:
- enable svm as yakvm_cpu_svm_enable()
- allocate the vmcb and initialize its control area for intercepting and state-save area for guest state saving as yakvm_vcpu_init_vmcb()
- reserve the hsave area as yakvm_create_vcpu() and record its address in VM_HSAVE_PA msr as yakvm_vcpu_run() to save host state
- execute the
clgi; vmload; vmrun; vmsave; stgi
to perform the atomic state switch as _yakvm_vcpu_run()
┌────────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────┐ │
│ │ │ │
│ │ ┌──────────────────────┐ │ ┌────────────────────────────┐ │
│ │ │guest physical address├────┼────▲──►│host physical address │ │
│ │ └──────────────────────┘ │ │ └────────────────────────────┘ │
│ │ │ │ │
│ │ ┌─────────────────────────────┐ │ │ │
│ │ │Virtual Machine Control Block│ │ ┌─┴────────────────┐ │
│ │ │ struct vmcb ├─┼─►│nested page table │ │
│ │ └─────────────────────────────┘ │ └──────────────────┘ │
│ │ virtual cpu │ │
│ └─────────────────────────────────┘ │
│ │
│ host │
└────────────────────────────────────────────────────────────────────────────┘
host must complete the following steps to supported memory virtualization
- enable NP_ENABLE bit in the vmcb as yakvm_vcpu_init_vmcb()
- create the nested page table entry to map gpa with hpa as yakvm_vm_ioctl_mmap_page()
Considering there are two ways to access devices under x86 architecture: Port I/O(PIO) and Memory Mapped I/O(MMIO), we should virtualizing there two methods.
PIO virtualization can be achieved by configuring the vmcb as yakvm_vcpu_init_vmcb() to intercept PIO as yakvm_cpu_handle_ioio().
For MMIO, remove the _PAGE_PRESENT flag from the corresponding MMIO memory's Nested Paging Table entry as yakvm_vmm_npt_create() to intercept MMIO as yakvm_vcpu_handle_mmio().