hypervisor/
lib.rs

1/*
2Copyright (c) 2016 Saurav Sachidanand
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"), to deal
6in the Software without restriction, including without limitation the rights
7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8copies of the Software, and to permit persons to whom the Software is
9furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20THE SOFTWARE.
21*/
22
23/*!
24This is a Rust library that taps into functionality that enables
25hardware-accelerated execution of virtual machines on OS X.
26
27It binds to the `Hypervisor` framework on OS X, and exposes a safe Rust
28interface through the `hypervisor` module, and an unsafe foreign function
29interface through the `hypervisor::ffi` module.
30
31To use this library, you need
32
33* OS X Yosemite (10.10), or newer
34
35* an Intel processor with the VT-x feature set that includes Extended Page
36Tables (EPT) and Unrestricted Mode. To verify this, run and expect the following
37in your Terminal:
38
39  ```shell
40  $ sysctl kern.hv_support
41  kern.hv_support: 1
42  ```
43!*/
44
45extern crate libc;
46extern crate core;
47
48#[allow(non_camel_case_types)]
49pub mod ffi;
50pub mod consts;
51
52use self::core::fmt;
53use libc::*;
54
55use self::ffi::*;
56
57/// Error returned after every call
58pub enum Error {
59    /// Success
60    Success,
61    /// Error
62    Error,
63    /// Busy
64    Busy,
65    /// Bad argument
66    BadArg,
67    /// No resources
68    NoRes,
69    /// No device
70    NoDev,
71    /// Unsupported
72    Unsupp
73}
74
75impl fmt::Debug for Error {
76    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77        match *self {
78            Error::Success => write!(f, "Success"),
79            Error::Error   => write!(f, "Error"),
80            Error::Busy    => write!(f, "Busy"),
81            Error::BadArg  => write!(f, "Bad argument"),
82            Error::NoRes   => write!(f, "No resources"),
83            Error::NoDev   => write!(f, "No device"),
84            Error::Unsupp  => write!(f, "Unsupported"),
85        }
86    }
87}
88
89// Returns an Error for a hv_return_t
90fn match_error_code(code: hv_return_t) -> Error {
91    match code {
92        HV_SUCCESS      => Error::Success,
93        HV_BUSY         => Error::Busy,
94        HV_BAD_ARGUMENT => Error::BadArg,
95        HV_NO_RESOURCES => Error::NoRes,
96        HV_NO_DEVICE    => Error::NoDev,
97        HV_UNSUPPORTED  => Error::Unsupp,
98        _               => Error::Error
99    }
100}
101
102/// Creates a VM instance for the current Mach task
103pub fn create_vm() -> Error {
104    match_error_code(unsafe {
105        hv_vm_create(HV_VM_DEFAULT)
106    })
107}
108
109/// Destroys the VM instance associated with the current Mach task
110pub fn destroy_vm() -> Error {
111    match_error_code(unsafe {
112        hv_vm_destroy()
113    })
114}
115
116/// Guest physical memory region permissions
117pub enum MemPerm {
118    /// Read
119    Read,
120    /// Write (implies read)
121    Write,
122    /// Execute
123    Exec,
124    /// Execute and write (implies read)
125    ExecAndWrite,
126    /// Execute and read
127    ExecAndRead
128}
129
130#[allow(non_snake_case)]
131#[inline(always)]
132fn match_MemPerm(mem_perm: &MemPerm) -> uint64_t {
133    match mem_perm {
134        &MemPerm::Read         => HV_MEMORY_READ,
135        &MemPerm::Write        => HV_MEMORY_WRITE | HV_MEMORY_READ,
136        &MemPerm::Exec         => HV_MEMORY_EXEC,
137        &MemPerm::ExecAndWrite => HV_MEMORY_EXEC | HV_MEMORY_WRITE | HV_MEMORY_READ,
138        &MemPerm::ExecAndRead  => HV_MEMORY_EXEC | HV_MEMORY_READ,
139    }
140}
141
142/// Maps a region in the virtual address space of the current Mach task into the guest physical
143/// address space of the virutal machine
144pub fn map_mem(mem: &[u8], gpa: u64, mem_perm: &MemPerm) -> Error {
145    match_error_code(unsafe {
146        hv_vm_map(
147            mem.as_ptr() as *const c_void, gpa as hv_gpaddr_t, mem.len() as size_t,
148            match_MemPerm(mem_perm)
149        )
150    })
151}
152
153/// Unmaps a region in the guest physical address space of the virutal machine
154pub fn unmap_mem(gpa: u64, size: usize) -> Error {
155    match_error_code(unsafe {
156        hv_vm_unmap(gpa as hv_gpaddr_t, size as size_t)
157    })
158}
159
160/// Modifies the permissions of a region in the guest physical address space of the virtual
161/// machine
162pub fn protect_mem(gpa: u64, size: usize, mem_perm: &MemPerm) -> Error {
163    match_error_code(unsafe {
164        hv_vm_protect(gpa as hv_gpaddr_t, size as size_t, match_MemPerm(mem_perm))
165    })
166}
167
168/// Synchronizes the guest Timestamp-Counters (TSC) across all vCPUs
169///
170/// * `tsc` Guest TSC value
171pub fn sync_tsc(tsc: u64) -> Error {
172    match_error_code(unsafe {
173        hv_vm_sync_tsc(tsc as uint64_t)
174    })
175}
176
177/// Forces an immediate VMEXIT of a set of vCPUs
178///
179/// * `vcpu_ids` Array of vCPU IDs
180pub fn interrupt_vcpus(vcpu_ids: &[u32]) -> Error {
181    match_error_code(unsafe {
182        hv_vcpu_interrupt(vcpu_ids.as_ptr(), vcpu_ids.len() as c_uint)
183    })
184}
185
186/// Virtual CPU
187#[allow(non_camel_case_types)]
188pub struct vCPU {
189    /// Virtual CPU ID
190    pub id: u32
191}
192
193/// x86 architectural register
194#[allow(non_camel_case_types)]
195#[derive(Clone)]
196#[repr(C)]
197pub enum x86Reg {
198	RIP,
199	RFLAGS,
200	RAX,
201	RCX,
202	RDX,
203	RBX,
204	RSI,
205	RDI,
206	RSP,
207	RBP,
208	R8,
209	R9,
210	R10,
211	R11,
212	R12,
213	R13,
214	R14,
215	R15,
216	CS,
217	SS,
218	DS,
219	ES,
220	FS,
221	GS,
222	IDT_BASE,
223	IDT_LIMIT,
224	GDT_BASE,
225	GDT_LIMIT,
226	LDTR,
227	LDT_BASE,
228	LDT_LIMIT,
229	LDT_AR,
230	TR,
231	TSS_BASE,
232	TSS_LIMIT,
233	TSS_AR,
234	CR0,
235	CR1,
236	CR2,
237	CR3,
238	CR4,
239	DR0,
240	DR1,
241	DR2,
242	DR3,
243	DR4,
244	DR5,
245	DR6,
246	DR7,
247	TPR,
248	XCR0,
249	REGISTERS_MAX,
250}
251
252impl vCPU {
253
254    /// Creates a vCPU instance for the current thread
255    pub fn new() -> Result<vCPU, Error> {
256        let mut vcpuid: hv_vcpuid_t = 0;
257
258        let error = match_error_code(unsafe {
259            hv_vcpu_create(&mut vcpuid, HV_VCPU_DEFAULT)
260        });
261
262        match error {
263            Error::Success => Ok(vCPU {
264                id: vcpuid as u32
265            }),
266            _ => Err(error)
267        }
268    }
269
270    /// Destroys the vCPU instance associated with the current thread
271    pub fn destroy(&self) -> Error {
272        match_error_code(unsafe {
273            hv_vcpu_destroy(self.id as hv_vcpuid_t)
274        })
275    }
276
277    /// Executes the vCPU
278    pub fn run(&self) -> Error {
279        match_error_code(unsafe {
280            hv_vcpu_run(self.id as hv_vcpuid_t)
281        })
282    }
283
284    /// Forces an immediate VMEXIT of the vCPU
285    pub fn interrupt(&self) -> Error {
286        match_error_code(unsafe {
287            hv_vcpu_interrupt(&(self.id), 1 as c_uint)
288        })
289    }
290
291    /// Returns the cumulative execution time of the vCPU in nanoseconds
292    pub fn exec_time(&self) -> Result<u64, Error> {
293        let mut exec_time: uint64_t = 0;
294
295        let error = match_error_code(unsafe {
296            hv_vcpu_get_exec_time(self.id, &mut exec_time)
297        });
298
299        match error {
300            Error::Success => Ok(exec_time as u64),
301            _ => Err(error)
302        }
303    }
304
305    /// Forces flushing of cached vCPU state
306    pub fn flush(&self) -> Error {
307        match_error_code(unsafe {
308            hv_vcpu_flush(self.id as hv_vcpuid_t)
309        })
310    }
311
312    /// Invalidates the translation lookaside buffer (TLB) of the vCPU
313    pub fn invalidate_tlb(&self) -> Error {
314        match_error_code(unsafe {
315            hv_vcpu_invalidate_tlb(self.id as hv_vcpuid_t)
316        })
317    }
318
319    /// Enables an MSR to be used natively by the VM
320    pub fn enable_native_msr(&self, msr: u32, enable: bool) -> Error {
321        match_error_code(unsafe {
322            hv_vcpu_enable_native_msr(self.id as hv_vcpuid_t, msr as uint32_t, enable)
323        })
324    }
325
326    /// Returns the current value of an MSR of the vCPU
327    pub fn read_msr(&self, msr: u32) -> Result<u64, Error> {
328        let mut value: uint64_t = 0;
329
330        let error = match_error_code(unsafe {
331            hv_vcpu_read_msr(self.id as hv_vcpuid_t, msr as uint32_t, &mut value)
332        });
333
334        match error {
335            Error::Success => Ok(value as u64),
336            _ => Err(error)
337        }
338    }
339
340    /// Set the value of an MSR of the vCPU
341    pub fn write_msr(&self, msr: u32, value: u64) -> Error {
342        match_error_code(unsafe {
343            hv_vcpu_write_msr(self.id as hv_vcpuid_t, msr as uint32_t, &(value as uint64_t))
344        })
345    }
346
347    /// Returns the current value of an architectural x86 register
348    /// of the vCPU
349    pub fn read_register(&self, reg: &x86Reg) -> Result<u64, Error> {
350        let mut value: uint64_t = 0;
351
352        let error = match_error_code(unsafe {
353            hv_vcpu_read_register(self.id as hv_vcpuid_t, (*reg).clone(), &mut value)
354        });
355
356        match error {
357            Error::Success => Ok(value as u64),
358            _ => Err(error)
359        }
360    }
361
362    /// Sets the value of an architectural x86 register of the vCPU
363    pub fn write_register(&self, reg: &x86Reg, value: u64) -> Error {
364        match_error_code(unsafe {
365            hv_vcpu_write_register(self.id as hv_vcpuid_t, (*reg).clone(), value as uint64_t)
366        })
367    }
368
369    /// Returns the current value of a VMCS field of the vCPU
370    pub fn read_vmcs(&self, field: u32) -> Result<u64, Error> {
371        let mut value: uint64_t = 0;
372
373        let error = match_error_code(unsafe {
374            hv_vmx_vcpu_read_vmcs(self.id as hv_vcpuid_t, field as uint32_t, &mut value)
375        });
376
377        match error {
378            Error::Success => Ok(value as u64),
379            _ => Err(error)
380        }
381    }
382
383    /// Sets the value of a VMCS field of the vCPU
384    pub fn write_vmcs(&self, field: u32, value: u64) -> Error {
385        match_error_code(unsafe {
386            hv_vmx_vcpu_write_vmcs(self.id as hv_vcpuid_t, field as uint32_t, value as uint64_t)
387        })
388    }
389
390    /// Sets the address of the guest APIC for the vCPU in the
391    /// guest physical address space of the VM
392    pub fn set_apic_addr(&self, gpa: u64) -> Error {
393        match_error_code(unsafe {
394            hv_vmx_vcpu_set_apic_address(self.id as hv_vcpuid_t, gpa as uint64_t)
395        })
396    }
397
398    /// Reads the current architectural x86 floating point and SIMD state of the vCPU
399    pub fn read_fpstate(&self, buffer: &mut [u8]) -> Error {
400        match_error_code(unsafe {
401            hv_vcpu_read_fpstate(self.id as hv_vcpuid_t, buffer.as_mut_ptr() as *mut c_void,
402            buffer.len() as size_t)
403        })
404    }
405
406    /// Sets the architectural x86 floating point and SIMD state of the vCPU
407    pub fn write_fpstate(&self, buffer: &[u8]) -> Error {
408        match_error_code(unsafe {
409            hv_vcpu_write_fpstate(self.id as hv_vcpuid_t, buffer.as_ptr() as *const c_void,
410            buffer.len() as size_t)
411        })
412    }
413
414}
415
416impl fmt::Debug for vCPU {
417    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
418        write!(f, "vCPU ID: {}", (*self).id)
419    }
420}
421
422/// VMX cabability
423#[allow(non_camel_case_types)]
424#[derive(Clone)]
425#[repr(C)]
426pub enum VMXCap {
427    /// Pin-based VMX capabilities
428    PINBASED                                     = 0,
429    /// Primary proc-based VMX capabilities
430    PROCBASED                                    = 1,
431    /// Secondary proc-based VMX capabilities
432    PROCBASED2                                   = 2,
433    /// VM-entry VMX capabilities
434    ENTRY                                        = 3,
435    /// VM-exit VMX capabilities
436    EXIT                                         = 4,
437    /// VMX preemption timer frequency
438    PREEMPTION_TIMER                             = 32,
439}
440
441/// Reads a VMX capability of the host processor
442pub fn read_vmx_cap(vmx_cap: &VMXCap) -> Result<u64, Error> {
443    let mut value: uint64_t = 0;
444
445    let error = match_error_code(unsafe {
446        hv_vmx_read_capability((*vmx_cap).clone(), &mut value)
447    });
448
449    match error {
450        Error::Success => Ok(value as u64),
451        _ => Err(error)
452    }
453}