use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::Instant; use clap::{App, ArgMatches}; use colored::*; use microvmi::api::events::{EventReplyType, EventType, InterceptType}; use microvmi::api::params::DriverInitParams; use microvmi::api::{Access, Introspectable}; use utilities::Clappable; const PAGE_SIZE: usize = 4096; fn parse_args() -> ArgMatches<'static> { App::new(file!()) .version("0.1") .about("Watches memory VMI events") .args(DriverInitParams::to_clap_args().as_ref()) .get_matches() } fn toggle_pf_intercept(drv: &mut Box, enabled: bool) { drv.pause().expect("Failed to pause VM"); 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() { drv.toggle_intercept(vcpu, intercept, enabled) .unwrap_or_else(|_| panic!("Failed to enable page faults")); } drv.resume().expect("Failed to resume VM"); } fn main() { env_logger::init(); let matches = parse_args(); // 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 init_params = DriverInitParams::from_matches(&matches); let mut drv: Box = microvmi::init(None, Some(init_params)).expect("Failed to init libmicrovmi"); println!("Listen for memory events..."); // record elapsed time let start = Instant::now(); toggle_pf_intercept(&mut drv, true); let mut i: u64 = 0; //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) { let mut access: Access = drv.get_page_access(cur_addr).unwrap(); access &= !Access::X; drv.set_page_access(cur_addr, access) .expect("failed to set page access"); } while running.load(Ordering::SeqCst) { let event = drv.listen(1000).expect("Failed to listen for events"); match event { Some(ev) => { let (gva, gpa, pf_access) = match ev.kind { EventType::Pagefault { gva, gpa, access } => (gva, gpa, access), _ => panic!("Not pf event"), }; let ev_nb_output = format!("{}", i).cyan(); let vcpu_output = format!("VCPU {}", ev.vcpu).yellow(); let pagefault_output = "pagefault occurred!".color("blue"); println!( "[{}] {} - {}: gva = 0x{:x} gpa = 0x{:x} access = {:?} ", ev_nb_output, vcpu_output, pagefault_output, gva, gpa, pf_access ); 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) .expect("Failed to set page access"); drv.reply_event(ev, EventReplyType::Continue) .expect("Failed to send event reply"); i += 1; } None => println!("No events yet..."), } } let duration = start.elapsed(); toggle_pf_intercept(&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 ); }