[−][src]Crate kvm_ioctls
A safe wrapper around the kernel's KVM interface.
This crate offers safe wrappers for:
- system ioctls using the
Kvm
structure - VM ioctls using the
VmFd
structure - vCPU ioctls using the
VcpuFd
structure - device ioctls using the
DeviceFd
structure
Platform support
- x86_64
- arm64 (experimental)
NOTE: The list of available ioctls is not extensive.
Example - Running a VM on x86_64
In this example we are creating a Virtual Machine (VM) with one vCPU. On the vCPU we are running x86_64 specific code. This example is based on the LWN article on using the KVM API.
To get code running on the vCPU we are going through the following steps:
- Instantiate KVM. This is used for running system specific ioctls.
- Use the KVM object to create a VM. The VM is used for running VM specific ioctls.
- Initialize the guest memory for the created VM. In this dummy example we are adding only one memory region and write the code in one memory page.
- Create a vCPU using the VM object. The vCPU is used for running vCPU specific ioctls.
- Setup x86 specific general purpose registers and special registers. For details about how and why these registers are set, please check the LWN article on which this example is built.
- Run the vCPU code in a loop and check the exit reasons.
extern crate kvm_ioctls; extern crate kvm_bindings; use kvm_ioctls::{Kvm, VmFd, VcpuFd}; use kvm_ioctls::VcpuExit; #[cfg(target_arch = "x86_64")] fn main(){ use std::io::Write; use std::slice; use std::ptr::null_mut; use kvm_bindings::KVM_MEM_LOG_DIRTY_PAGES; use kvm_bindings::kvm_userspace_memory_region; // 1. Instantiate KVM. let kvm = Kvm::new().unwrap(); // 2. Create a VM. let vm = kvm.create_vm().unwrap(); // 3. Initialize Guest Memory. let mem_size = 0x4000; let guest_addr: u64 = 0x1000; let load_addr: *mut u8 = unsafe { libc::mmap( null_mut(), mem_size, libc::PROT_READ | libc::PROT_WRITE, libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE, -1, 0, ) as *mut u8 }; let slot = 0; // When initializing the guest memory slot specify the // `KVM_MEM_LOG_DIRTY_PAGES` to enable the dirty log. let mem_region = kvm_userspace_memory_region { slot, guest_phys_addr: guest_addr, memory_size: mem_size as u64, userspace_addr: load_addr as u64, flags: KVM_MEM_LOG_DIRTY_PAGES, }; vm.set_user_memory_region(mem_region).unwrap(); let x86_code = [ 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */ 0x00, 0xd8, /* add %bl, %al */ 0x04, b'0', /* add $'0', %al */ 0xee, /* out %al, %dx */ 0xec, /* in %dx, %al */ 0xc6, 0x06, 0x00, 0x80, 0x00, /* movl $0, (0x8000); This generates a MMIO Write.*/ 0x8a, 0x16, 0x00, 0x80, /* movl (0x8000), %dl; This generates a MMIO Read.*/ 0xf4, /* hlt */ ]; // Write the code in the guest memory. This will generate a dirty page. unsafe { let mut slice = slice::from_raw_parts_mut(load_addr, mem_size); slice.write(&x86_code).unwrap(); } // 4. Create one vCPU. let vcpu_fd = vm.create_vcpu(0).unwrap(); // 5. Initialize general purpose and special registers. let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); vcpu_sregs.cs.base = 0; vcpu_sregs.cs.selector = 0; vcpu_fd.set_sregs(&vcpu_sregs).unwrap(); let mut vcpu_regs = vcpu_fd.get_regs().unwrap(); vcpu_regs.rip = guest_addr; vcpu_regs.rax = 2; vcpu_regs.rbx = 3; vcpu_regs.rflags = 2; vcpu_fd.set_regs(&vcpu_regs).unwrap(); // 6. Run code on the vCPU. loop { match vcpu_fd.run().expect("run failed") { VcpuExit::IoIn(addr, data) => { println!( "Received an I/O in exit. Address: {:#x}. Data: {:#x}", addr, data[0], ); } VcpuExit::IoOut(addr, data) => { println!( "Received an I/O out exit. Address: {:#x}. Data: {:#x}", addr, data[0], ); } VcpuExit::MmioRead(addr, data) => { println!( "Received an MMIO Read Request for the address {:#x}.", addr, ); } VcpuExit::MmioWrite(addr, data) => { println!( "Received an MMIO Write Request to the address {:#x}.", addr, ); } VcpuExit::Hlt => { // The code snippet dirties 1 page when it is loaded in memory let dirty_pages_bitmap = vm.get_dirty_log(slot, mem_size).unwrap(); let dirty_pages = dirty_pages_bitmap .into_iter() .map(|page| page.count_ones()) .fold(0, |dirty_page_count, i| dirty_page_count + i); assert_eq!(dirty_pages, 1); break; } r => panic!("Unexpected exit reason: {:?}", r), } } } #[cfg(not(target_arch = "x86_64"))] fn main() { println!("This code example only works on x86_64."); }
Macros
ioctl_io_nr | Declare an ioctl that transfers no data. |
ioctl_ioc_nr | Raw macro to declare a function that returns an ioctl number. |
ioctl_ior_nr | Declare an ioctl that reads data. |
ioctl_iow_nr | Declare an ioctl that writes data. |
ioctl_iowr_nr | Declare an ioctl that reads and writes data. |
Structs
CpuId | Wrapper over the |
DeviceFd | Wrapper over the file descriptor obtained when creating an emulated device in the kernel. |
Kvm | Wrapper over KVM system ioctls. |
KvmRunWrapper | Safe wrapper over the |
NoDatamatch | Helper structure for disabling datamatch. |
VcpuFd | Wrapper over KVM vCPU ioctls. |
VmFd | Wrapper over KVM VM ioctls. |
Enums
Cap | Capabilities exposed by KVM. |
IoEventAddress | An address either in programmable I/O space or in memory mapped I/O space. |
VcpuExit | Reasons for vCPU exits. |
Constants
MAX_KVM_CPUID_ENTRIES | Maximum number of CPUID entries that can be returned by a call to KVM ioctls. |
Type Definitions
Result | A specialized |