This is Vinix's native hardware virtualization backend. It exposes the
Vinix-specific /dev/hypervisor API, not Linux's /dev/kvm or KVM_* ioctls.
QEMU's KVM accelerator cannot use this device. For running Vinix as a guest
under Linux KVM, see Virtualization and KVM support.
Vinix enables Intel VT-x on every x86_64 CPU during SMP bring-up. If the CPU,
firmware, or outer hypervisor does not provide VMX, Vinix logs the reason and
continues booting normally. On supported systems an EPT-backed real-mode guest
is run as a boot-time self-test before /dev/hypervisor is registered.
The initial interface intentionally targets small firmware, boot-loader, and
device-model experiments. Each VM has one vCPU and up to 1 GiB of guest RAM,
backed by separate 4 KiB physical pages and a four-level EPT. Sizes are rounded
up to a page; creation fails with ENOMEM if the complete allocation cannot
be made. Guest execution stops for HLT, I/O,
CPUID, external interrupts, EPT faults, and the other architectural VM exits;
userspace decides how to emulate each exit.
Include <vinix/hypervisor.h>, open /dev/hypervisor once per VM, then:
- Check
VINIX_HV_GET_API_VERSION. - Pass
struct vinix_hv_createtoVINIX_HV_CREATE_VM. - Load guest bytes with
write(2)or a sharedmmap(2)of the descriptor. - Set the initial RIP, RSP, and RFLAGS with
VINIX_HV_SET_ENTRY. - Set general registers if needed with
VINIX_HV_SET_REGISTERS. - Call
VINIX_HV_RUNand emulate the returnedstruct vinix_hv_exit. - For an emulated instruction, pass its reported length to
VINIX_HV_ADVANCE_RIPbefore running again.
Guest memory supports unaligned reads/writes across physical-page boundaries.
Only shared mappings within the rounded RAM size are accepted; private mappings
return EINVAL. A mapping retains the VM after its last descriptor is closed,
including across fork. Concurrent creation on one session returns EBUSY.
The VM and its pages are released after the final descriptor or mapping is closed. When that release occurs under a kernel spinlock, the existing maintenance reaper owns cleanup after its five-second retirement grace; a large RAM tree is not freed under the caller's pagemap lock. VMCS ownership is cleared after every exit, so a vCPU may resume on a different scheduler CPU. The backend requires INVEPT support and flushes EPT translations before each entry, including migration and reuse of an old EPT root's physical page.
The memory checks boot the production allocator with a test-only device that disables VM entry. They qualify 1 GiB allocation, rollback, I/O and mapping lifetimes; real VM entry still requires Intel VMX hardware or a host exposing nested VT-x. This larger memory limit does not provide additional vCPUs, AMD/ARM hardware backends or a complete device model.
This example executes an OUT 0xe9, AL followed by HLT:
#include <fcntl.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <vinix/hypervisor.h>
int main(void) {
static const uint8_t guest[] = {
0xba, 0xe9, 0x00, /* mov dx, 0xe9 */
0xb0, 0x56, /* mov al, 'V' */
0xee, /* out dx, al */
0xf4 /* hlt */
};
int fd = open("/dev/hypervisor", O_RDWR);
struct vinix_hv_create create = { .memory_size = 64 * 1024 };
struct vinix_hv_entry entry = { .rip = 0x1000, .rsp = 0x8000, .rflags = 2 };
struct vinix_hv_exit exit;
if (fd < 0 || ioctl(fd, VINIX_HV_GET_API_VERSION) != VINIX_HV_API_VERSION)
return 1;
if (ioctl(fd, VINIX_HV_CREATE_VM, &create) ||
lseek(fd, 0x1000, SEEK_SET) < 0 ||
write(fd, guest, sizeof(guest)) != sizeof(guest) ||
ioctl(fd, VINIX_HV_SET_ENTRY, &entry))
return 1;
for (;;) {
if (ioctl(fd, VINIX_HV_RUN, &exit))
return 1;
if (exit.reason == VINIX_HV_EXIT_HLT)
break;
if (exit.reason != VINIX_HV_EXIT_IO)
return 1;
if (ioctl(fd, VINIX_HV_ADVANCE_RIP, &exit.instruction_length))
return 1;
}
close(fd);
return 0;
}AMD SVM, multi-vCPU guests, interrupt injection, and a complete device model are outside this first backend and can be added without changing the VMX entry/exit ABI.