Topic 02: VM Exits, I/O Port Traps & Hypercall Interface


🎙️ Deep-Dive Spoken Script

Interviewer: “How do VM exits work in your hypervisor, and how did you implement hypercalls?”

If the guest executes a normal instruction, it continues running directly on the CPU. When it executes an instruction configured to cause a VM exit, control returns from the guest to KVM and then back to my userspace hypervisor.

The execution flow is:

$$\text{Guest Native Execution} \longrightarrow \text{VM Exit Trap} \longrightarrow \text{KVM Kernel Module} \longrightarrow \text{KVM\_RUN Returns} \longrightarrow \text{Userspace Handler}$$

KVM stores the exit information in the shared struct kvm_run structure.

In my project, I primarily handle two exit types:

The first is KVM_EXIT_HLT. The guest executes the hlt instruction when it has finished its execution, so I use this exit to terminate the VM execution loop.

The second is KVM_EXIT_IO. I use x86 in and out instructions as a simple hypercall mechanism between the guest and my hypervisor.

The guest is freestanding, so it cannot directly call host functions such as printf. Instead, I provide small assembly helpers that execute an in or out instruction on a specific I/O port.

For example, when the guest executes an out to my character-output port (0xE0), KVM generates a KVM_EXIT_IO. My hypervisor checks:

My hypervisor reads the character and prints it via putchar().

I designated different I/O ports for different hypercall services:


🔬 In-Depth Q&A Database

Q7: What is a VM Exit, and why is it essential to virtualization?

A VM Exit is a hardware-enforced CPU context transition from VMX non-root (guest) mode to VMX root (host/hypervisor) mode.

According to Popek-Goldberg virtualization requirements, all sensitive instructions (such as modifying CR0/CR3, accessing hardware I/O ports, or altering interrupt flags) must trap to the hypervisor so that the guest cannot compromise the host or other virtual machines.

Q8: How did you implement hypercalls using x86 I/O ports?

In freestanding guest code, I wrote inline assembly macros:

static inline void outb(uint16_t port, uint8_t val) {
    asm volatile("outb %0, %1" : : "a"(val), "Nd"(port) : "memory");
}

When this runs, x86 CPU hardware intercepts the privileged outb instruction, triggers a VM exit (EXIT_REASON_IO_INSTRUCTION), and transfers control to KVM. KVM formats kvm_run->exit_reason = KVM_EXIT_IO, sets direction, port, size, and data_offset, and returns from ioctl(KVM_RUN).

Q9: What is the difference between KVM_EXIT_IO_IN and KVM_EXIT_IO_OUT?