diff --git a/Cargo.toml b/Cargo.toml index 71d9a73d..3837dc42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [features] # Xen driver -xen = ["xenctrl", "xenstore", "xenforeignmemory", "libc"] +xen = ["xenctrl", "xenstore", "xenforeignmemory", "libc", "xenvmevent-sys", "xenevtchn"] # KVM driver kvm = ["kvmi"] # VirtualBox driver @@ -28,17 +28,21 @@ hyper-v = ["winapi", "widestring", "ntapi", "vid-sys"] log = "0.4.8" env_logger = "0.7.1" libc = { version = "0.2.58", optional = true } -xenctrl = { git = "https://github.com/Wenzel/xenctrl", optional = true } -xenstore = { git = "https://github.com/Wenzel/xenstore", optional = true } -xenforeignmemory = { git = "https://github.com/Wenzel/xenforeignmemory", optional = true } -kvmi = { version = "0.2.1", optional = true } +xenctrl = { git = "https://github.com/arnabcs17b006/xenctrl", branch = "mockable_crate", optional = true } +xenstore = { git = "https://github.com/arnabcs17b006/xenstore", branch = "mockable_crate", optional = true } +xenforeignmemory = { git = "https://github.com/arnabcs17b006/xenforeignmemory", branch = "mockable_crate", optional = true } +xenevtchn = { git = "https://github.com/arnabcs17b006/xenevtchn", branch = "mockable_crate", optional = true} +xenvmevent-sys = { git = "https://github.com/arnabcs17b006/xenvmevent-sys", branch = "derive_default", optional = true} +kvmi = { git = "https://github.com/Wenzel/kvmi", optional = true } fdp = { git = "https://github.com/Wenzel/fdp", optional = true } winapi = { version = "0.3.8", features = ["tlhelp32", "winnt", "handleapi", "securitybaseapi"], optional = true } widestring = { version = "0.4.0", optional = true } ntapi = { version = "0.3.3", optional = true } vid-sys = { version = "0.3.0", features = ["deprecated-apis"], optional = true } -bitflags = "1.2.1" cty = "0.2.1" +nix = "0.18.0" +bitflags = "1.2.1" +failure = "0.1.7" [dev-dependencies] ctrlc = "3.1.3" @@ -46,3 +50,4 @@ clap = "2.33.0" colored = "1.9.3" mockall = "0.7.1" test-case = "1.0.0" + diff --git a/examples/cr-events.rs b/examples/cr-events.rs index 8bd89c2e..4810748f 100644 --- a/examples/cr-events.rs +++ b/examples/cr-events.rs @@ -42,10 +42,11 @@ fn toggle_cr_intercepts(drv: &mut Box, vec_cr: &Vec, let intercept = InterceptType::Cr(*cr); let status_str = if enabled { "Enabling" } else { "Disabling" }; println!("{} intercept on {:?}", status_str, cr); - for vcpu in 0..drv.get_vcpu_count().unwrap() { - drv.toggle_intercept(vcpu, intercept, enabled) - .expect(&format!("Failed to enable {:?}", cr)); - } + //for vcpu in 0..drv.get_vcpu_count().unwrap() { + let vcpu = 0; + drv.toggle_intercept(vcpu, intercept, enabled) + .expect(&format!("Failed to enable {:?}", cr)); + //} } drv.resume().expect("Failed to resume VM"); @@ -101,7 +102,7 @@ fn main() { // listen let mut i: u64 = 0; while running.load(Ordering::SeqCst) { - let event = drv.listen(1000).expect("Failed to listen for events"); + let event = drv.listen(10).expect("Failed to listen for events"); match event { Some(ev) => { let (cr_type, new, old) = match ev.kind { @@ -120,8 +121,8 @@ fn main() { "[{}] {} - {}: old value: 0x{:x} new value: 0x{:x}", ev_nb_output, vcpu_output, cr_output, old, new ); - drv.reply_event(ev, EventReplyType::Continue) - .expect("Failed to send event reply"); + // drv.reply_event(ev, EventReplyType::Continue) + // .expect("Failed to send event reply"); i = i + 1; } None => println!("No events yet..."), diff --git a/examples/mem-events.rs b/examples/mem-events.rs index 30cd926a..1e4f39e8 100644 --- a/examples/mem-events.rs +++ b/examples/mem-events.rs @@ -6,11 +6,9 @@ use std::sync::Arc; use std::time::Instant; use microvmi::api::{ - Access, DriverInitParam, EventReplyType, EventType, InterceptType, Introspectable, + Access, DriverInitParam, EventReplyType, EventType, InterceptType, Introspectable, PAGE_SIZE, }; -const PAGE_SIZE: usize = 4096; - fn parse_args() -> ArgMatches<'static> { App::new(file!()) .version("0.1") @@ -25,7 +23,7 @@ fn toggle_pf_intercept(drv: &mut Box, enabled: bool) { let intercept = InterceptType::Pagefault; let status_str = if enabled { "Enabling" } else { "Disabling" }; println!("{} memory events", status_str); - for vcpu in 0..drv.get_vcpu_count().unwrap() { + for vcpu in 0..1 { drv.toggle_intercept(vcpu, intercept, enabled) .expect(&format!("Failed to enable page faults")); } @@ -40,6 +38,10 @@ fn main() { let domain_name = matches.value_of("vm_name").unwrap(); + let init_option = matches + .value_of("kvmi_socket") + .map(|socket| DriverInitParam::KVMiSocket(socket.into())); + // set CTRL-C handler let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); @@ -49,9 +51,6 @@ fn main() { .expect("Error setting Ctrl-C handler"); println!("Initialize Libmicrovmi"); - let init_option = matches - .value_of("kvmi_socket") - .map(|socket| DriverInitParam::KVMiSocket(socket.into())); let mut drv: Box = microvmi::init(domain_name, None, init_option); println!("Listen for memory events..."); // record elapsed time @@ -61,11 +60,11 @@ fn main() { //Code snippet to get page fault let max_addr = drv.get_max_physical_addr().unwrap(); - - for cur_addr in (0..max_addr).step_by(PAGE_SIZE) { + //println!("max_gpfn: {}", max_addr>>PAGE_SHIFT); + for cur_addr in (0..max_addr).step_by(PAGE_SIZE as usize) { let mut access: Access = drv.get_page_access(cur_addr).unwrap(); access &= !Access::X; - drv.set_page_access(cur_addr, access) + drv.set_page_access(cur_addr, !Access::X) .expect("failed to set page access"); } @@ -87,10 +86,10 @@ fn main() { let mut page_access = drv.get_page_access(gpa).expect("Failed to get page access"); //setting the access bits in the page due to which page fault occurred page_access |= pf_access; - drv.set_page_access(gpa, page_access) + drv.set_page_access(gpa, Access::RWX) .expect("Failed to set page access"); - drv.reply_event(ev, EventReplyType::Continue) - .expect("Failed to send event reply"); + //drv.reply_event(ev, EventReplyType::Continue) + // .expect("Failed to send event reply"); i = i + 1; } None => println!("No events yet..."), diff --git a/examples/msr-events.rs b/examples/msr-events.rs index d496cf6e..43beb667 100644 --- a/examples/msr-events.rs +++ b/examples/msr-events.rs @@ -109,8 +109,8 @@ fn main() { let event = drv.listen(1000).expect("Failed to listen for events"); match event { Some(ev) => { - let (msr_type, new, old) = match ev.kind { - EventType::Msr { msr_type, new, old } => (msr_type, new, old), + let (msr_type, value) = match ev.kind { + EventType::Msr { msr_type, value } => (msr_type, value), _ => panic!("not msr event"), }; let msr_color = "blue"; @@ -118,8 +118,8 @@ fn main() { let vcpu_output = format!("VCPU {}", ev.vcpu).yellow(); let msr_output = format!("0x{:x}", msr_type).color(msr_color); println!( - "[{}] {} - {}: old value: 0x{:x} new value: 0x{:x}", - ev_nb_output, vcpu_output, msr_output, old, new + "[{}] {} - {}: new value: 0x{:x}", + ev_nb_output, vcpu_output, msr_output, value, ); drv.reply_event(ev, EventReplyType::Continue) .expect("Failed to send event reply"); diff --git a/examples/regs-dump.rs b/examples/regs-dump.rs index 9495f9d3..68295913 100644 --- a/examples/regs-dump.rs +++ b/examples/regs-dump.rs @@ -33,7 +33,7 @@ fn main() { println!("pausing the VM"); drv.pause().expect("Failed to pause VM"); - let total_vcpu_count: u16 = drv.get_vcpu_count().expect("Failed to get vcpu count"); + let total_vcpu_count: u16 = 1; for vcpu in 0..total_vcpu_count { println!("dumping registers on VCPU {}", vcpu); let regs = drv.read_registers(vcpu).expect("Failed to read registers"); diff --git a/examples/singlestep-events.rs b/examples/singlestep-events.rs new file mode 100644 index 00000000..2d21fc36 --- /dev/null +++ b/examples/singlestep-events.rs @@ -0,0 +1,96 @@ +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use std::time::Instant; + +use clap::{App, Arg, ArgMatches}; +use colored::*; +use env_logger; + +use microvmi::api::*; + +fn parse_args() -> ArgMatches<'static> { + App::new(file!()) + .version("0.1") + .about("Watches singlestep VMI events") + .arg(Arg::with_name("vm_name").index(1).required(true)) + .get_matches() +} + +fn toggle_singlestep_interception(drv: &mut Box, enabled: bool) { + drv.pause().expect("Failed to pause VM"); + + let intercept = InterceptType::Singlestep; + let status_str = if enabled { "Enabling" } else { "Disabling" }; + println!("{} singlestep events", status_str); + for vcpu in 0..1 { + drv.toggle_intercept(vcpu, intercept, enabled) + .expect(&format!("Failed to enable singlestep")); + } + + drv.resume().expect("Failed to resume VM"); +} + +fn main() { + env_logger::init(); + + let matches = parse_args(); + + let domain_name = matches.value_of("vm_name").unwrap(); + + let init_option = matches + .value_of("kvmi_socket") + .map(|socket| DriverInitParam::KVMiSocket(socket.into())); + // set CTRL-C handler + let running = Arc::new(AtomicBool::new(true)); + let r = running.clone(); + ctrlc::set_handler(move || { + r.store(false, Ordering::SeqCst); + }) + .expect("Error setting Ctrl-C handler"); + + println!("Initialize Libmicrovmi"); + let mut drv: Box = microvmi::init(domain_name, None, init_option); + + //Enable singlestep interception + toggle_singlestep_interception(&mut drv, true); + + println!("Listen for singlestep events..."); + // record elapsed time + let start = Instant::now(); + // listen + let mut i: u64 = 0; + while running.load(Ordering::SeqCst) { + let event = drv.listen(1000).expect("Failed to listen for events"); + match event { + Some(ev) => { + let gpa = match ev.kind { + EventType::Singlestep { gpa } => (gpa), + _ => panic!("Not singlestep event"), + }; + let ev_nb_output = format!("{}", i).cyan(); + let vcpu_output = format!("VCPU {}", ev.vcpu).yellow(); + let singlestep_output = format!("singlestep occurred!").color("blue"); + println!( + "[{}] {} - {}: gpa = 0x{:x} ", + ev_nb_output, vcpu_output, singlestep_output, gpa + ); + //drv.reply_event(ev, EventReplyType::Continue) + // .expect("Failed to send event reply"); + i = i + 1; + } + None => println!("No events yet..."), + } + } + let duration = start.elapsed(); + + //disable singlestep interception + toggle_singlestep_interception(&mut drv, false); + + let ev_per_sec = i as f64 / duration.as_secs_f64(); + println!( + "Caught {} events in {:.2} seconds ({:.2} events/sec)", + i, + duration.as_secs_f64(), + ev_per_sec + ); +} diff --git a/src/api.rs b/src/api.rs index bc598423..e49adba9 100644 --- a/src/api.rs +++ b/src/api.rs @@ -62,7 +62,7 @@ impl TryInto for DriverInitParamFFI { ///an x86 segment register #[repr(C)] -#[derive(Debug)] +#[derive(Debug, Default)] pub struct SegmentReg { ///Stores the base address of a code segment pub base: u64, @@ -75,7 +75,7 @@ pub struct SegmentReg { /// x86 System Table Registers /// (GDTR, IDTR) #[repr(C)] -#[derive(Debug)] +#[derive(Debug, Default)] pub struct SystemTableReg { /// 32/64 bits linear base address pub base: u64, @@ -85,7 +85,7 @@ pub struct SystemTableReg { ///Represents all x86 registers on a specific VCPU #[repr(C)] -#[derive(Debug)] +#[derive(Debug, Default)] pub struct X86Registers { /// 8 byte general purpose register. pub rax: u64, @@ -314,11 +314,12 @@ pub enum InterceptType { /// Intercept when guest requests an access to a page for which the requested type of access is not granted. For example , guest tries to write on a read only page. Breakpoint, Pagefault, + Singlestep, } /// Various types of events along with their relevant attributes being handled by this driver #[repr(C)] -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum EventType { ///Cr register interception Cr { @@ -334,9 +335,7 @@ pub enum EventType { ///Type of model specific register msr_type: u32, /// new value after msr register has been intercepted by the guest. - new: u64, - /// old value before cr register has been intercepted by the guest. - old: u64, + value: u64, }, ///int3 interception Breakpoint { @@ -353,11 +352,16 @@ pub enum EventType { /// Acsess responsible for thr pagefault access: Access, }, + ///Singlestep event + Singlestep { + ///Physical memory address of the guest + gpa: u64, + }, } ///Types of x86 control registers are listed here #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum CrType { ///Has various control flags that modify the basic operation of the processor. Cr0, diff --git a/src/driver/kvm.rs b/src/driver/kvm.rs index 17262c67..2ff26be4 100644 --- a/src/driver/kvm.rs +++ b/src/driver/kvm.rs @@ -289,6 +289,7 @@ impl Introspectable for Kvm { .kvmi .control_events(vcpu, KVMiInterceptType::Pagefault, enabled)?) } + _ => unimplemented!(), } } @@ -309,10 +310,9 @@ impl Introspectable for Kvm { new, old, }, - KVMiEventType::Msr { msr_type, new, old } => EventType::Msr { + KVMiEventType::Msr { msr_type, new, old: _ } => EventType::Msr { msr_type, - new, - old, + value: new, }, KVMiEventType::Breakpoint {gpa, insn_len } => EventType::Breakpoint { gpa, diff --git a/src/driver/xen.rs b/src/driver/xen.rs index f2bee070..8c087976 100644 --- a/src/driver/xen.rs +++ b/src/driver/xen.rs @@ -1,26 +1,91 @@ use crate::api::{ - DriverInitParam, Introspectable, Registers, SegmentReg, SystemTableReg, X86Registers, + Access, CrType, DriverInitParam, Event, EventType, InterceptType, Introspectable, Registers, + SegmentReg, SystemTableReg, X86Registers, }; -use libc::{PROT_READ, PROT_WRITE}; +use std::convert::{From, TryFrom}; use std::error::Error; +use std::mem; + +use libc::{PROT_READ, PROT_WRITE}; +use nix::poll::PollFlags; +use nix::poll::{poll, PollFd}; +use std::convert::TryInto; use xenctrl::consts::{PAGE_SHIFT, PAGE_SIZE}; -use xenctrl::XenControl; -use xenstore::{XBTransaction, Xs, XsOpenFlags}; +use xenctrl::RING_HAS_UNCONSUMED_REQUESTS; +use xenctrl::{XenCr, XenEventType, XenIntrospectable, XenPageAccess}; +use xenevtchn::EventChannelSetup; +use xenforeignmemory::XenForeignMemoryIntrospectable; +use xenstore::{XBTransaction, XsIntrospectable, XsOpenFlags}; +use xenvmevent_sys::{ + vm_event_back_ring, vm_event_response_t, VM_EVENT_FLAG_VCPU_PAUSED, VM_EVENT_INTERFACE_VERSION, +}; + +impl TryFrom for XenPageAccess { + type Error = &'static str; + fn try_from(access: Access) -> Result { + match access { + Access::NIL => Ok(XenPageAccess::NIL), + Access::R => Ok(XenPageAccess::R), + Access::W => Ok(XenPageAccess::W), + Access::RW => Ok(XenPageAccess::RW), + Access::X => Ok(XenPageAccess::X), + Access::RX => Ok(XenPageAccess::RX), + Access::WX => Ok(XenPageAccess::WX), + Access::RWX => Ok(XenPageAccess::RWX), + _ => Err("invalid access value"), + } + } +} + +impl From for Access { + fn from(access: XenPageAccess) -> Self { + match access { + XenPageAccess::NIL => Access::NIL, + XenPageAccess::R => Access::R, + XenPageAccess::W => Access::W, + XenPageAccess::RW => Access::RW, + XenPageAccess::X => Access::X, + XenPageAccess::RX => Access::RX, + XenPageAccess::WX => Access::WX, + XenPageAccess::RWX => Access::RWX, + } + } +} -// unit struct #[derive(Debug)] -pub struct Xen { - xc: XenControl, - xen_fgn: xenforeignmemory::XenForeignMem, +pub struct Xen< + T: XenIntrospectable, + U: EventChannelSetup, + V: XenForeignMemoryIntrospectable, + W: XsIntrospectable, +> { + xc: T, + xev: U, + xen_fgn: V, + xs: W, dom_name: String, domid: u32, + back_ring: vm_event_back_ring, } -impl Xen { - pub fn new(domain_name: &str, _init_option: Option) -> Self { +impl< + T: XenIntrospectable, + U: EventChannelSetup, + V: XenForeignMemoryIntrospectable, + W: XsIntrospectable, + > Xen +{ + pub fn new( + domain_name: &str, + mut xc: T, + mut xev: U, + mut xen_fgn: V, + mut xs: W, + _init_option: Option, + ) -> Result> { debug!("init on {}", domain_name); + xs.init(XsOpenFlags::ReadOnly)?; // find domain name in xenstore - let xs = Xs::new(XsOpenFlags::ReadOnly).unwrap(); let mut found: bool = false; let mut cand_domid = 0; for domid_str in xs.directory(XBTransaction::Null, "/local/domain".to_string()) { @@ -35,24 +100,34 @@ impl Xen { if !found { panic!("Cannot find domain {}", domain_name); } - let xc = XenControl::new(None, None, 0).unwrap(); - let xen_fgn = xenforeignmemory::XenForeignMem::new().unwrap(); + + xc.init(None, None, 0)?; + let (_ring_page, back_ring, remote_port) = xc + .monitor_enable(cand_domid) + .expect("Failed to map event ring page"); + xev.init(cand_domid, remote_port)?; + xen_fgn.init()?; let xen = Xen { xc, + xev, xen_fgn, + xs, dom_name: domain_name.to_string(), domid: cand_domid, + back_ring, }; debug!("Initialized {:#?}", xen); - xen - } - - fn close(&mut self) { - debug!("close"); + Ok(xen) } } -impl Introspectable for Xen { +impl< + T: XenIntrospectable, + U: EventChannelSetup, + V: XenForeignMemoryIntrospectable, + W: XsIntrospectable, + > Introspectable for Xen +{ fn read_physical(&self, paddr: u64, buf: &mut [u8]) -> Result<(), Box> { let mut cur_paddr: u64; let mut offset: u64 = 0; @@ -79,7 +154,7 @@ impl Introspectable for Xen { count_mut -= read_len; buf_offset += read_len; // unmap page - self.xen_fgn.unmap(page).unwrap(); + self.xen_fgn.unmap(page)?; } Ok(()) } @@ -110,7 +185,7 @@ impl Introspectable for Xen { count_mut -= write_len; buf_offset += write_len; // unmap page - self.xen_fgn.unmap(page).unwrap(); + self.xen_fgn.unmap(page)?; } Ok(()) } @@ -145,54 +220,47 @@ impl Introspectable for Xen { cr0: hvm_cpu.cr0, cr3: hvm_cpu.cr3, cr4: hvm_cpu.cr4, - cr2: 0, - sysenter_cs: 0, - sysenter_esp: 0, - sysenter_eip: 0, - msr_efer: 0, - msr_star: 0, - msr_lstar: 0, - efer: 0, - apic_base: 0, + cr2: hvm_cpu.cr2, + sysenter_cs: hvm_cpu.sysenter_cs, + sysenter_esp: hvm_cpu.sysenter_esp, + sysenter_eip: hvm_cpu.sysenter_eip, + msr_efer: hvm_cpu.msr_efer, + msr_star: hvm_cpu.msr_star, + msr_lstar: hvm_cpu.msr_lstar, cs: SegmentReg { - base: 0, - limit: 0, - selector: 0, + base: hvm_cpu.cs_base, + limit: hvm_cpu.cs_limit, + selector: hvm_cpu.cs_sel.try_into().unwrap(), }, ds: SegmentReg { - base: 0, - limit: 0, - selector: 0, + base: hvm_cpu.ds_base, + limit: hvm_cpu.ds_limit, + selector: hvm_cpu.ds_sel.try_into().unwrap(), }, es: SegmentReg { - base: 0, - limit: 0, - selector: 0, + base: hvm_cpu.es_base, + limit: hvm_cpu.es_limit, + selector: hvm_cpu.es_sel.try_into().unwrap(), }, fs: SegmentReg { - base: 0, - limit: 0, - selector: 0, + base: hvm_cpu.fs_base, + limit: hvm_cpu.fs_limit, + selector: hvm_cpu.fs_sel.try_into().unwrap(), }, gs: SegmentReg { - base: 0, - limit: 0, - selector: 0, + base: hvm_cpu.gs_base, + limit: hvm_cpu.gs_limit, + selector: hvm_cpu.gs_sel.try_into().unwrap(), }, ss: SegmentReg { - base: 0, - limit: 0, - selector: 0, + base: hvm_cpu.ss_base, + limit: hvm_cpu.ss_limit, + selector: hvm_cpu.ss_sel.try_into().unwrap(), }, tr: SegmentReg { - base: 0, - limit: 0, - selector: 0, - }, - ldt: SegmentReg { - base: 0, - limit: 0, - selector: 0, + base: hvm_cpu.tr_base, + limit: hvm_cpu.tr_limit, + selector: hvm_cpu.tr_sel.try_into().unwrap(), }, idt: SystemTableReg { base: hvm_cpu.idtr_base, @@ -202,9 +270,179 @@ impl Introspectable for Xen { base: hvm_cpu.gdtr_base, limit: hvm_cpu.gdtr_limit as u16, }, + ..Default::default() })) } + fn write_registers(&self, vcpu: u16, reg: Registers) -> Result<(), Box> { + let (buffer, mut cpu, size) = self.xc.domain_hvm_getcontext(self.domid, vcpu)?; + match reg { + Registers::X86(x86_registers) => { + cpu.rax = x86_registers.rax; + cpu.rbx = x86_registers.rbx; + cpu.rcx = x86_registers.rcx; + cpu.rdx = x86_registers.rdx; + cpu.rsi = x86_registers.rsi; + cpu.rdi = x86_registers.rdi; + cpu.rsp = x86_registers.rsp; + cpu.rbp = x86_registers.rbp; + cpu.r8 = x86_registers.r8; + cpu.r9 = x86_registers.r9; + cpu.r10 = x86_registers.r10; + cpu.r11 = x86_registers.r11; + cpu.r12 = x86_registers.r12; + cpu.r13 = x86_registers.r13; + cpu.r14 = x86_registers.r14; + cpu.r15 = x86_registers.r15; + cpu.rip = x86_registers.rip; + cpu.rflags = x86_registers.rflags; + cpu.cr0 = x86_registers.cr0; + cpu.cr2 = x86_registers.cr2; + cpu.cr3 = x86_registers.cr3; + cpu.cr4 = x86_registers.cr4; + cpu.sysenter_cs = x86_registers.sysenter_cs; + cpu.sysenter_esp = x86_registers.sysenter_esp; + cpu.sysenter_eip = x86_registers.sysenter_eip; + cpu.msr_star = x86_registers.msr_star; + cpu.msr_lstar = x86_registers.msr_lstar; + cpu.msr_efer = x86_registers.msr_efer; + cpu.cs_base = x86_registers.cs.base; + cpu.ds_base = x86_registers.ds.base; + cpu.es_base = x86_registers.es.base; + cpu.fs_base = x86_registers.fs.base; + cpu.gs_base = x86_registers.gs.base; + cpu.ss_base = x86_registers.ss.base; + cpu.tr_base = x86_registers.tr.base; + cpu.cs_limit = x86_registers.cs.limit; + cpu.ds_limit = x86_registers.ds.limit; + cpu.es_limit = x86_registers.es.limit; + cpu.fs_limit = x86_registers.fs.limit; + cpu.gs_limit = x86_registers.gs.limit; + cpu.ss_limit = x86_registers.ss.limit; + cpu.tr_limit = x86_registers.tr.limit; + cpu.cs_sel = x86_registers.cs.selector.try_into().unwrap(); + cpu.ds_sel = x86_registers.ds.selector.try_into().unwrap(); + cpu.es_sel = x86_registers.es.selector.try_into().unwrap(); + cpu.fs_sel = x86_registers.fs.selector.try_into().unwrap(); + cpu.gs_sel = x86_registers.gs.selector.try_into().unwrap(); + cpu.ss_sel = x86_registers.ss.selector.try_into().unwrap(); + cpu.tr_sel = x86_registers.tr.selector.try_into().unwrap(); + } + } + self.xc + .domain_hvm_setcontext(self.domid, buffer, size.try_into().unwrap())?; + Ok(()) + } + + fn listen(&mut self, timeout: u32) -> Result, Box> { + let fd = self.xev.xenevtchn_fd()?; + let fd_struct = PollFd::new(fd, PollFlags::POLLIN | PollFlags::POLLERR); + let mut fds = [fd_struct]; + let mut vcpu: u16 = 0; + let mut event_type = unsafe { mem::MaybeUninit::::zeroed().assume_init() }; + let poll_result = poll(&mut fds, timeout.try_into().unwrap()).unwrap(); + let mut pending_event_port = -1; + if poll_result == 1 { + pending_event_port = self.xev.xenevtchn_pending()?; + if pending_event_port != -1 { + self.xev + .xenevtchn_unmask(pending_event_port.try_into().unwrap())?; + } + } + let back_ring_ptr = &mut self.back_ring; + let mut flag = false; + if poll_result > 0 + && self.xev.get_bind_port() == pending_event_port + && RING_HAS_UNCONSUMED_REQUESTS!(back_ring_ptr) != 0 + { + flag = true; + let req = self.xc.get_request(back_ring_ptr)?; + if req.version != VM_EVENT_INTERFACE_VERSION { + panic!("version mismatch"); + } + let xen_event_type = (self.xc.get_event_type(req))?; + event_type = match xen_event_type { + XenEventType::Cr { cr_type, new, old } => EventType::Cr { + cr_type: match cr_type { + XenCr::Cr0 => CrType::Cr0, + XenCr::Cr3 => CrType::Cr3, + XenCr::Cr4 => CrType::Cr4, + }, + new, + old, + }, + XenEventType::Msr { msr_type, value } => EventType::Msr { msr_type, value }, + XenEventType::Breakpoint { gpa, insn_len } => { + EventType::Breakpoint { gpa, insn_len } + } + XenEventType::Pagefault { gva, gpa, access } => EventType::Pagefault { + gva, + gpa, + access: access.into(), + }, + XenEventType::Singlestep { gpa } => EventType::Singlestep { gpa }, + }; + vcpu = req.vcpu_id.try_into().unwrap(); + let mut rsp = + unsafe { mem::MaybeUninit::::zeroed().assume_init() }; + rsp.reason = req.reason; + rsp.version = VM_EVENT_INTERFACE_VERSION; + rsp.vcpu_id = req.vcpu_id; + rsp.flags = req.flags & VM_EVENT_FLAG_VCPU_PAUSED; + self.xc.put_response(&mut rsp, &mut self.back_ring)?; + } + self.xev.xenevtchn_notify()?; + if flag { + Ok(Some(Event { + vcpu, + kind: event_type, + })) + } else { + Ok(None) + } + } + + fn get_page_access(&self, paddr: u64) -> Result> { + let access = self.xc.get_mem_access(self.domid, paddr >> PAGE_SHIFT)?; + Ok(access.into()) + } + + fn set_page_access(&self, paddr: u64, access: Access) -> Result<(), Box> { + Ok(self + .xc + .set_mem_access(self.domid, access.try_into().unwrap(), paddr >> PAGE_SHIFT)?) + } + + fn toggle_intercept( + &mut self, + _vcpu: u16, + intercept_type: InterceptType, + enabled: bool, + ) -> Result<(), Box> { + match intercept_type { + InterceptType::Cr(micro_cr_type) => { + let xen_cr = match micro_cr_type { + CrType::Cr0 => XenCr::Cr0, + CrType::Cr3 => XenCr::Cr3, + CrType::Cr4 => XenCr::Cr4, + }; + Ok(self + .xc + .monitor_write_ctrlreg(self.domid, xen_cr, enabled, true, true)?) + } + InterceptType::Msr(micro_msr_type) => { + Ok(self + .xc + .monitor_mov_to_msr(self.domid, micro_msr_type, enabled)?) + } + InterceptType::Breakpoint => { + Ok(self.xc.monitor_software_breakpoint(self.domid, enabled)?) + } + InterceptType::Pagefault => Ok(()), + InterceptType::Singlestep => Ok(self.xc.monitor_singlestep(self.domid, enabled)?), + } + } + fn pause(&mut self) -> Result<(), Box> { debug!("pause"); Ok(self.xc.domain_pause(self.domid)?) @@ -216,8 +454,1341 @@ impl Introspectable for Xen { } } -impl Drop for Xen { +impl< + T: XenIntrospectable, + U: EventChannelSetup, + V: XenForeignMemoryIntrospectable, + W: XsIntrospectable, + > Drop for Xen +{ fn drop(&mut self) { - self.close(); + debug!("Closing Xen driver"); + self.xc + .monitor_disable(self.domid) + .expect("Failed to unmap event ring page"); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use mockall::mock; + use mockall::predicate::eq; + use std::fmt::{Debug, Formatter}; + use std::os::raw::{c_int, c_uint}; + use xenctrl::consts::{PAGE_SHIFT, PAGE_SIZE}; + use xenctrl::{ + hvm_hw_cpu, vm_event_back_ring, vm_event_request_t, vm_event_response_t, vm_event_sring, + xentoollog_logger, XenCr, XenEventType, XenPageAccess, + }; + use xenevtchn::{evtchn_port_t, xenevtchn_port_or_error_t}; + use xenstore::{XBTransaction, XsOpenFlags}; + + #[test] + fn test_fail_to_create_xen_driver_if_xencontrol_init_returns_error() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + xencontrol_mock + .expect_init() + .returning(|_, _, _| Err(xenctrl::error::XcError::new("some error"))); + xenevtchn_mock.expect_init().returning(|_, _| Ok(())); + xenforeignmemory_mock.expect_init().returning(|| Ok(())); + xenstore_mock.expect_init().returning(|_| Ok(())); + let mut vec: Vec = Vec::new(); + let domain_name = "some_vm"; + let dom0 = "0"; + let string_dir: String = String::from(dom0); + let string_read: String = String::from(domain_name); + vec.push(string_dir); + xenstore_mock + .expect_directory() + .return_once(move |_, _| vec); + xenstore_mock + .expect_read() + .return_once(move |_, _| string_read); + + let result = Xen::new( + &domain_name, + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ); + + assert!(result.is_err(), "Expected error, got ok instead!"); + } + + #[test] + fn test_fail_to_create_xen_driver_if_xenforeignmemory_init_returns_error() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + xencontrol_mock.expect_init().returning(|_, _, _| Ok(())); + xenevtchn_mock.expect_init().returning(|_, _| Ok(())); + xenforeignmemory_mock.expect_init().returning(|| { + Err(failure::Error::from_boxed_compat(Box::new( + std::io::Error::new(std::io::ErrorKind::Other, "something went wrong"), + ))) + }); + xenstore_mock.expect_init().returning(|_| Ok(())); + let mut vec: Vec = Vec::new(); + let domain_name = "some_vm"; + let dom0 = "0"; + let string_dir: String = String::from(dom0); + vec.push(string_dir); + let string_read: String = String::from(domain_name); + xenstore_mock + .expect_directory() + .return_once(move |_, _| vec); + xenstore_mock + .expect_read() + .return_once(move |_, _| string_read); + xencontrol_mock + .expect_monitor_enable() + .return_once(move |_| { + let sring: vm_event_sring = Default::default(); + let bring: vm_event_back_ring = Default::default(); + Ok((sring, bring, 0)) + }); + + let result = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ); + + assert!(result.is_err(), "Expected error, got ok instead!"); + } + + #[test] + fn test_fail_to_create_xen_driver_if_xenstore_init_returns_error() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + xencontrol_mock.expect_init().returning(|_, _, _| Ok(())); + xenevtchn_mock.expect_init().returning(|_, _| Ok(())); + xenforeignmemory_mock.expect_init().returning(|| Ok(())); + xenstore_mock.expect_init().returning(|_| { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "something went wrong", + )) + }); + + let result = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ); + + assert!(result.is_err(), "Expected error, got ok instead!"); + } + + #[test] + fn test_fail_to_create_xen_driver_if_xenevtchn_init_returns_error() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + xencontrol_mock.expect_init().returning(|_, _, _| Ok(())); + xenevtchn_mock.expect_init().returning(|_, _| { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "something went wrong", + )) + }); + xenforeignmemory_mock.expect_init().returning(|| Ok(())); + xenstore_mock.expect_init().returning(|_| Ok(())); + let mut vec: Vec = Vec::new(); + let domain_name = "some_vm"; + let dom0 = "0"; + let string_dir: String = String::from(dom0); + let string_read: String = String::from(domain_name); + vec.push(string_dir); + xenstore_mock + .expect_directory() + .return_once(move |_, _| vec); + xenstore_mock + .expect_read() + .return_once(move |_, _| string_read); + xencontrol_mock + .expect_monitor_enable() + .return_once(move |_| { + let sring: vm_event_sring = Default::default(); + let bring: vm_event_back_ring = Default::default(); + Ok((sring, bring, 0)) + }); + + let result = Xen::new( + &domain_name, + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ); + + assert!(result.is_err(), "Expected error, got ok instead!"); + } + + fn setup( + xencontrol_mock: &mut MockXenControl, + xenevtchn_mock: &mut MockXenEventChannel, + xenforeignmemory_mock: &mut MockXenForeignMem, + xenstore_mock: &mut MockXs, + ) { + xencontrol_mock.expect_init().returning(|_, _, _| Ok(())); + xenevtchn_mock.expect_init().returning(|_, _| Ok(())); + xenforeignmemory_mock.expect_init().returning(|| Ok(())); + xenstore_mock.expect_init().returning(|_| Ok(())); + let mut vec: Vec = Vec::new(); + let domain_name = "some_vm"; + let dom0 = "0"; + let string_dir: String = String::from(dom0); + let string_read: String = String::from(domain_name); + vec.push(string_dir); + xenstore_mock + .expect_directory() + .return_once(move |_, _| vec); + xenstore_mock + .expect_read() + .return_once(move |_, _| string_read); + xencontrol_mock + .expect_monitor_enable() + .return_once(move |_| { + let sring: vm_event_sring = Default::default(); + let bring: vm_event_back_ring = Default::default(); + Ok((sring, bring, 0)) + }); + xencontrol_mock + .expect_monitor_disable() + .return_once(|_| Ok(())); + } + + #[test] + fn test_xen_driver_created_successfully() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + + let result = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ); + + assert!(result.is_ok(), "Expected ok, got error instead!"); + } + + #[test] + fn test_read_physical_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + static mut BUF: [u8; PAGE_SIZE as usize] = [0; PAGE_SIZE as usize]; + unsafe { + xenforeignmemory_mock + .expect_map() + .return_once(move |_, _, _| Ok(&mut BUF)); + } + xenforeignmemory_mock.expect_unmap().returning(|_| Ok(())); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + let mut buffer = [0; PAGE_SIZE as usize]; + + let result = xen.read_physical(paddr, &mut buffer); + + assert!(result.is_ok(), "Expected ok, got error instead!"); + } + + #[test] + fn test_read_physical_fails_if_xen_foreignmemory_map_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xenforeignmemory_mock.expect_map().returning(|_, _, _| { + Err(failure::Error::from_boxed_compat(Box::new( + std::io::Error::new(std::io::ErrorKind::Other, "something went wrong"), + ))) + }); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + let mut buffer = [0; PAGE_SIZE as usize]; + + let result = xen.read_physical(paddr, &mut buffer); + + assert!(result.is_err(), "Expected error, got ok instead!"); + } + + #[test] + fn test_read_physical_fails_if_xen_foreignmemory_unmap_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + static mut BUF: [u8; PAGE_SIZE as usize] = [0; PAGE_SIZE as usize]; + unsafe { + xenforeignmemory_mock + .expect_map() + .return_once(move |_, _, _| Ok(&mut BUF)); + } + xenforeignmemory_mock.expect_unmap().returning(|_| { + Err(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + "something went wrong", + ))) + }); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + let mut buffer = [0; PAGE_SIZE as usize]; + + let result = xen.read_physical(paddr, &mut buffer); + + assert!(result.is_err(), "Expected error, got ok instead!"); + } + + #[test] + fn test_read_registers_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + let cpu: hvm_hw_cpu = Default::default(); + xencontrol_mock + .expect_domain_hvm_getcontext_partial() + .returning(move |_, _| Ok(cpu)); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.read_registers(vcpu); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn test_read_registers_fails_if_domain_hvm_getcontext_partial_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_domain_hvm_getcontext_partial() + .returning(move |_, _| Err(xenctrl::error::XcError::new("some error"))); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.read_registers(vcpu); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn test_write_registers_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + let cpu: hvm_hw_cpu = Default::default(); + let size = 1024; + xencontrol_mock + .expect_domain_hvm_getcontext() + .return_once(move |_, _| { + let buf: *mut c_uint = std::ptr::null_mut(); + Ok((buf, cpu, size)) + }); + xencontrol_mock + .expect_domain_hvm_setcontext() + .returning(move |_, _, _| Ok(())); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + let x86_register_struct: X86Registers = Default::default(); + + let result = xen.write_registers(vcpu, Registers::X86(x86_register_struct)); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn test_write_registers_fails_if_domain_hvm_getcontext_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_domain_hvm_getcontext() + .returning(move |_, _| Err(xenctrl::error::XcError::new("some error"))); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + let x86_register_struct: X86Registers = Default::default(); + + let result = xen.write_registers(vcpu, Registers::X86(x86_register_struct)); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn test_write_registers_fails_if_domain_hvm_setcontext_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + let cpu: hvm_hw_cpu = Default::default(); + let size = 1024; + xencontrol_mock + .expect_domain_hvm_getcontext() + .return_once(move |_, _| { + let buf: *mut c_uint = std::ptr::null_mut(); + Ok((buf, cpu, size)) + }); + xencontrol_mock + .expect_domain_hvm_setcontext() + .returning(move |_, _, _| Err(xenctrl::error::XcError::new("some error"))); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + let x86_register_struct: X86Registers = Default::default(); + + let result = xen.write_registers(vcpu, Registers::X86(x86_register_struct)); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn test_write_physical_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + static mut BUF: [u8; PAGE_SIZE as usize] = [0; PAGE_SIZE as usize]; + unsafe { + xenforeignmemory_mock + .expect_map() + .return_once(move |_, _, _| Ok(&mut BUF)); + } + xenforeignmemory_mock.expect_unmap().returning(|_| Ok(())); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + let mut buffer = [0; PAGE_SIZE as usize]; + + let result = xen.write_physical(paddr, &mut buffer); + + assert!(result.is_ok(), "Expected ok, got error instead!"); + } + + /*#[test] + fn test_write_physical_fails_if_xen_foreignmemory_map_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup(&mut xencontrol_mock, &mut xenevtchn_mock, &mut xenforeignmemory_mock, &mut xenstore_mock); + static mut buf: [u8; PAGE_SIZE as usize] = [0; PAGE_SIZE as usize]; + unsafe {xenforeignmemory_mock.expect_map().return_once(move |_, _, _| Ok(&mut buf));} + xenforeignmemory_mock.expect_unmap().returning(|_| Ok(())); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ).expect("Failed to create driver"); + let paddr =0; + let mut buffer = [0;PAGE_SIZE as usize]; + let result = xen.write_physical(paddr, &mut buffer); + assert!(result.is_ok(), "Expected ok, got error instead!"); + }*/ + + #[test] + fn test_write_physical_fails_if_xen_foreignmemory_unmap_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + static mut BUF: [u8; PAGE_SIZE as usize] = [0; PAGE_SIZE as usize]; + unsafe { + xenforeignmemory_mock + .expect_map() + .return_once(move |_, _, _| Ok(&mut BUF)); + } + xenforeignmemory_mock.expect_unmap().returning(|_| { + Err(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + "something went wrong", + ))) + }); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + let mut buffer = [0; PAGE_SIZE as usize]; + + let result = xen.write_physical(paddr, &mut buffer); + + assert!(result.is_err(), "Expected error, got ok instead!"); + } + + #[test] + fn get_maximum_physical_address_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + let max_gfn = 1; + xencontrol_mock + .expect_domain_maximum_gpfn() + .returning(move |_| Ok(max_gfn)); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + + let max_addr = xen.get_max_physical_addr().unwrap(); + + assert_eq!(max_addr, max_gfn << PAGE_SHIFT); + } + + #[test] + fn get_maximum_physical_address_fails_if_xenctrl_domain_maximum_gpfn_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_domain_maximum_gpfn() + .returning(|_| Err(xenctrl::error::XcError::new("some error"))); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + + let result = xen.get_max_physical_addr(); + + assert!(result.is_err(), "Expected error, got ok instead!"); + } + + #[test] + fn test_pause_domain_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock.expect_domain_pause().returning(|_| Ok(())); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + + let result = xen.pause(); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn test_pause_domain_fails_if_xencontrol_domain_pause_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_domain_pause() + .returning(|_| Err(xenctrl::error::XcError::new("some error"))); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + + let result = xen.pause(); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn test_resume_domain_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_domain_unpause() + .returning(|_| Ok(())); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + + let result = xen.resume(); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn test_resume_domain_fails_if_xencontrol_domain_unpause_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_domain_unpause() + .returning(|_| Err(xenctrl::error::XcError::new("some error"))); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + + let result = xen.resume(); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn test_get_page_access_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_get_mem_access() + .returning(move |_, _| Ok(XenPageAccess::R)); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + + let access = xen.get_page_access(paddr).unwrap(); + + assert_eq!(Access::R, access); + } + + #[test] + fn test_get_page_access_fails_if_xencontrol_get_mem_access_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_get_mem_access() + .returning(|_, _| Err(xenctrl::error::XcError::new("some error"))); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + + let result = xen.get_page_access(paddr); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn test_set_page_access_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_set_mem_access() + .returning(move |_, _, _| Ok(())); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + + let result = xen.set_page_access(paddr, Access::R); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn test_set_page_access_fails_if_xencontrol_set_mem_access_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_set_mem_access() + .returning(|_, _, _| Err(xenctrl::error::XcError::new("some error"))); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + + let result = xen.set_page_access(paddr, Access::R); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn test_listen_events_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + let fd = 1; + xenevtchn_mock + .expect_xenevtchn_fd() + .return_once(move || Ok(fd)); + let port = 56; + xenevtchn_mock + .expect_xenevtchn_pending() + .return_once(move || Ok(port)); + xenevtchn_mock + .expect_xenevtchn_unmask() + .with(eq(port as u32)) + .returning(|_| Ok(())); + xenevtchn_mock + .expect_get_bind_port() + .return_once(move || port); + let request: vm_event_request_t = Default::default(); + xencontrol_mock + .expect_get_request() + .return_once(move |_| Ok(request)); + let cr_type = XenCr::Cr3; + let new = 1; + let old = 2; + xencontrol_mock + .expect_get_event_type() + .return_once(move |_| Ok(XenEventType::Cr { cr_type, new, old })); + xencontrol_mock + .expect_put_response() + .returning(|_, _| Ok(())); + xenevtchn_mock + .expect_xenevtchn_notify() + .returning(|| Ok(())); + let timeout = 1000; + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + + let result = xen.listen(timeout); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn toggle_cr_intercept_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_monitor_write_ctrlreg() + .returning(|_, _, _, _, _| Ok(())); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.toggle_intercept(vcpu, InterceptType::Cr(CrType::Cr3), true); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn toggle_cr_intercept_fails_if_monitor_write_ctrlreg_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_monitor_write_ctrlreg() + .returning(|_, _, _, _, _| Err(xenctrl::error::XcError::new("some error"))); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.toggle_intercept(vcpu, InterceptType::Cr(CrType::Cr3), true); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn toggle_msr_intercept_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_monitor_mov_to_msr() + .returning(|_, _, _| Ok(())); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.toggle_intercept(vcpu, InterceptType::Msr(0x175), true); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn toggle_msr_intercept_fails_if_monitor_write_ctrlreg_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_monitor_mov_to_msr() + .returning(|_, _, _| Err(xenctrl::error::XcError::new("some error"))); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.toggle_intercept(vcpu, InterceptType::Msr(0x175), true); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn toggle_singlestep_intercept_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_monitor_singlestep() + .returning(|_, _| Ok(())); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.toggle_intercept(vcpu, InterceptType::Singlestep, true); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn toggle_singlestep_intercept_fails_if_monitor_write_ctrlreg_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_monitor_singlestep() + .returning(|_, _| Err(xenctrl::error::XcError::new("some error"))); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.toggle_intercept(vcpu, InterceptType::Singlestep, true); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn toggle_breakpoint_intercept_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_monitor_software_breakpoint() + .returning(|_, _| Ok(())); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.toggle_intercept(vcpu, InterceptType::Breakpoint, true); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn toggle_breakpoint_intercept_fails_if_monitor_write_ctrlreg_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_monitor_software_breakpoint() + .returning(|_, _| Err(xenctrl::error::XcError::new("some error"))); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.toggle_intercept(vcpu, InterceptType::Breakpoint, true); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + mock! { + XenControl{} + trait Debug { + fn fmt<'a>(&self, f: &mut Formatter<'a>) -> std::fmt::Result; + } + trait XenIntrospectable: Debug { + fn init<'a> ( + &mut self, + logger: Option<&'a mut xentoollog_logger>, + dombuild_logger: Option<&'a mut xentoollog_logger>, + open_flags: u32, + ) -> Result<(), xenctrl::error::XcError>; + fn domain_hvm_getcontext_partial(&self, domid: u32, vcpu: u16) -> Result; + fn domain_hvm_setcontext(&self, domid: u32, buffer: *mut c_uint, size: usize) -> Result<(), xenctrl::error::XcError>; + fn domain_hvm_getcontext( + &self, + domid: u32, + vcpu: u16, + ) -> Result<(*mut c_uint, hvm_hw_cpu, u32), xenctrl::error::XcError>; + fn monitor_enable(&mut self, domid: u32) -> Result<(vm_event_sring, vm_event_back_ring, u32), xenctrl::error::XcError>; + fn get_request(&self, back_ring: &mut vm_event_back_ring) -> Result; + fn put_response( + &self, + rsp: &mut vm_event_response_t, + back_ring: &mut vm_event_back_ring, + ) -> Result<(), xenctrl::error::XcError>; + fn get_event_type(&self, req: vm_event_request_t) -> Result; + fn monitor_disable(&self, domid: u32) -> Result<(), xenctrl::error::XcError>; + fn domain_pause(&self, domid: u32) -> Result<(), xenctrl::error::XcError>; + fn domain_unpause(&self, domid: u32) -> Result<(), xenctrl::error::XcError>; + fn monitor_software_breakpoint(&self, domid: u32, enable: bool) -> Result<(), xenctrl::error::XcError>; + fn monitor_singlestep(&self, domid: u32, enable: bool) -> Result<(), xenctrl::error::XcError>; + fn monitor_mov_to_msr(&self, domid: u32, msr: u32, enable: bool) -> Result<(), xenctrl::error::XcError>; + fn monitor_write_ctrlreg( + &self, + domid: u32, + index: XenCr, + enable: bool, + sync: bool, + onchangeonly: bool, + ) -> Result<(), xenctrl::error::XcError>; + fn set_mem_access( + &self, + domid: u32, + access: XenPageAccess, + first_pfn: u64, + ) -> Result<(), xenctrl::error::XcError>; + fn get_mem_access(&self, domid: u32, pfn: u64) -> Result; + fn domain_maximum_gpfn(&self, domid: u32) -> Result; + fn close(&mut self) -> Result<(), xenctrl::error::XcError>; + } + } + + mock! { + XenEventChannel{} + trait Debug { + fn fmt<'a>(&self, f: &mut Formatter<'a>) -> std::fmt::Result; + } + trait EventChannelSetup: Debug { + fn init(&mut self, domid: u32, evtchn_port: u32) -> Result<(), std::io::Error>; + fn get_bind_port(&self) -> i32; + fn xenevtchn_pending(&self) -> Result; + fn xenevtchn_fd(&self) -> Result; + fn xenevtchn_unmask(&self, port: evtchn_port_t) -> Result<(), std::io::Error>; + fn xenevtchn_notify(&self) -> Result<(), std::io::Error>; + } + } + + mock! { + Xs{} + trait Debug { + fn fmt<'a>(&self, f: &mut Formatter<'a>) -> std::fmt::Result; + } + trait XsIntrospectable: Debug { + fn init(&mut self, open_type: XsOpenFlags) -> Result<(), std::io::Error>; + fn directory(&self, transaction: XBTransaction, path: String) -> Vec; + fn read(&self, transaction: XBTransaction, path: String) -> String; + fn close(&mut self); + } + } + + mock! { + XenForeignMem{} + trait Debug { + fn fmt<'a>(&self, f: &mut Formatter<'a>) -> std::fmt::Result; + } + trait XenForeignMemoryIntrospectable: Debug { + fn init(&mut self) -> Result<(), failure::Error>; + fn map<'a> (&'a self, domid: u32, prot: c_int, gfn: u64) -> Result<&'a mut [u8], failure::Error>; + fn unmap(&self, page: &mut [u8]) -> Result<(), Box>; + fn close(&mut self) -> Result<(), failure::Error>; + } } } diff --git a/src/lib.rs b/src/lib.rs index 1a93c4ed..1850ef24 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,8 @@ mod driver; extern crate log; #[macro_use] extern crate bitflags; +#[macro_use] +extern crate failure; use api::Introspectable; use api::{DriverInitParam, DriverType}; @@ -21,6 +23,14 @@ use driver::virtualbox::VBox; use driver::xen::Xen; #[cfg(feature = "kvm")] use kvmi::create_kvmi; +#[cfg(feature = "xen")] +use xenctrl::create_xen_control; +#[cfg(feature = "xen")] +use xenevtchn::create_xen_event_channel; +#[cfg(feature = "xen")] +use xenforeignmemory::create_xen_foreignmemory; +#[cfg(feature = "xen")] +use xenstore::create_xen_store; #[allow(unreachable_code)] pub fn init( @@ -45,9 +55,7 @@ pub fn init( Box::new(VBox::new(domain_name, init_option)) as Box } #[cfg(feature = "xen")] - DriverType::Xen => { - Box::new(Xen::new(domain_name, init_option)) as Box - } + DriverType::Xen => create_xen(domain_name, init_option), }, None => { // test Hyper-V @@ -71,7 +79,7 @@ pub fn init( // test Xen #[cfg(feature = "xen")] { - return Box::new(Xen::new(domain_name, init_option)) as Box; + return create_xen(domain_name, init_option); } // return Dummy if no other driver has been compiled Box::new(Dummy::new(domain_name, init_option)) as Box @@ -83,3 +91,18 @@ pub fn init( fn create_kvm(domain_name: &str, init_option: Option) -> Box { Box::new(Kvm::new(domain_name, create_kvmi(), init_option).unwrap()) as Box } + +#[cfg(feature = "xen")] +fn create_xen(domain_name: &str, init_option: Option) -> Box { + Box::new( + Xen::new( + domain_name, + create_xen_control(), + create_xen_event_channel(), + create_xen_foreignmemory(), + create_xen_store(), + init_option, + ) + .unwrap(), + ) as Box +}