Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory as file descriptor #195

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
7958af3
add Memory impl
Wenzel Apr 14, 2021
35400e6
memory: add unit tests for Seek
Wenzel Apr 14, 2021
823c1a7
microvmi: expose pause, resume and get_max_physical_addr
Wenzel Apr 19, 2021
52ccd6d
microvmi: remove max_addr field
Wenzel Apr 19, 2021
eadbcb4
microvmi: clippy fixes
Wenzel Apr 19, 2021
eafe09b
microvmi: fix driver import
Wenzel Apr 19, 2021
b4c646d
memory: fix performance issues in Seek
Wenzel Apr 19, 2021
e3c45a8
mem-dump: update buffer size
Wenzel Apr 19, 2021
c5a7e1d
memory: refcell impl
Wenzel Apr 19, 2021
f1dd5ae
memory: add PaddedMemory
Wenzel Apr 19, 2021
a92de1b
memory: fix tests
Wenzel Apr 19, 2021
e53d49b
capi: update with Microvmi struct
Wenzel Apr 19, 2021
aaaa5a4
python: adapt read methods
Wenzel Apr 19, 2021
3197da0
python: add padded read
Wenzel Apr 23, 2021
bc6f49d
memory: fix seek unit tests
Wenzel Apr 23, 2021
bb4bfa0
memory: add TODO
Wenzel Apr 23, 2021
1408ae4
add doc
Wenzel Apr 23, 2021
1efcd20
examples: fix cr-events compilation
Wenzel Apr 28, 2021
820d32d
examples: fix interrupt-events
Wenzel Apr 28, 2021
75116df
examples: remove unused import
Wenzel Apr 28, 2021
39c678b
examples: fix mem-events
Wenzel Apr 28, 2021
921ed60
examples: fix msr-events
Wenzel Apr 28, 2021
9ee376d
examples: fix pause
Wenzel Apr 28, 2021
ba48425
examples: fix regs-dump
Wenzel Apr 28, 2021
38cbc76
microvmi: fix imports
Wenzel Apr 28, 2021
556182c
api: replace read_physical by read_frame
Wenzel Apr 28, 2021
4f017ad
xen: impl read_frame
Wenzel Apr 28, 2021
1fbc863
kvm: impl read_frame
Wenzel Apr 28, 2021
47fb25c
clippy: allow upper case acronyms
Wenzel Apr 28, 2021
49a0b0a
examples: use read_exact to ignore the return value
Wenzel Apr 28, 2021
cd4dc16
virtualbox: impl read_frame
Wenzel Apr 29, 2021
3381be7
microvmi: disable doctest for new example
Wenzel Apr 30, 2021
9fef107
memory: remove duplicated Seek implementation
Wenzel May 19, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions examples/cr-events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use std::time::Instant;
use clap::{App, Arg, ArgMatches};
use colored::*;

use microvmi::api::{CrType, DriverInitParam, EventType, InterceptType, Introspectable};
use microvmi::api::{CrType, DriverInitParam, EventType, InterceptType};
use microvmi::Microvmi;

fn parse_args() -> ArgMatches<'static> {
App::new(file!())
Expand All @@ -32,7 +33,7 @@ fn parse_args() -> ArgMatches<'static> {
.get_matches()
}

fn toggle_cr_intercepts(drv: &mut Box<dyn Introspectable>, vec_cr: &[CrType], enabled: bool) {
fn toggle_cr_intercepts(drv: &mut Microvmi, vec_cr: &[CrType], enabled: bool) {
drv.pause().expect("Failed to pause VM");

for cr in vec_cr {
Expand Down Expand Up @@ -88,8 +89,8 @@ fn main() {
let init_option = matches
.value_of("kvmi_socket")
.map(|socket| DriverInitParam::KVMiSocket(socket.into()));
let mut drv: Box<dyn Introspectable> =
microvmi::init(domain_name, None, init_option).expect("Failed to init libmicrovmi");
let mut drv =
Microvmi::new(domain_name, None, init_option).expect("Failed to init libmicrovmi");

// enable control register interception
toggle_cr_intercepts(&mut drv, &vec_cr, true);
Expand Down
13 changes: 7 additions & 6 deletions examples/interrupt-events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use std::time::Instant;
use clap::{App, Arg, ArgMatches};
use colored::*;

use microvmi::api::{DriverInitParam, EventReplyType, EventType, InterceptType, Introspectable};
use microvmi::api::{DriverInitParam, EventReplyType, EventType, InterceptType};
use microvmi::Microvmi;

fn parse_args() -> ArgMatches<'static> {
App::new(file!())
Expand All @@ -23,7 +24,7 @@ fn parse_args() -> ArgMatches<'static> {
.get_matches()
}

fn toggle_int3_interception(drv: &mut Box<dyn Introspectable>, enabled: bool) {
fn toggle_int3_interception(drv: &mut Microvmi, enabled: bool) {
drv.pause().expect("Failed to pause VM");

let intercept = InterceptType::Breakpoint;
Expand Down Expand Up @@ -56,10 +57,10 @@ fn main() {
.expect("Error setting Ctrl-C handler");

println!("Initialize Libmicrovmi");
let mut drv: Box<dyn Introspectable> =
microvmi::init(domain_name, None, init_option).expect("Failed to init libmicrovmi");
let mut drv =
Microvmi::new(domain_name, None, init_option).expect("Failed to init libmicrovmi");

//Enable int3 interception
// enable int3 interception
toggle_int3_interception(&mut drv, true);

println!("Listen for interrupt events...");
Expand Down Expand Up @@ -91,7 +92,7 @@ fn main() {
}
let duration = start.elapsed();

//disable int3 interception
// disable int3 interception
toggle_int3_interception(&mut drv, false);

let ev_per_sec = i as f64 / duration.as_secs_f64();
Expand Down
27 changes: 14 additions & 13 deletions examples/mem-dump.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use std::fs::File;
use std::io::Write;
use std::io::{Read, Write};
use std::path::Path;

use clap::{App, Arg, ArgMatches};
use indicatif::{ProgressBar, ProgressStyle};
use log::{debug, trace};
use log::trace;

use microvmi::api::{DriverInitParam, Introspectable};
use microvmi::api::DriverInitParam;
use microvmi::Microvmi;

const PAGE_SIZE: usize = 4096;
const BUFFER_SIZE: usize = 64535; // 64K

fn parse_args() -> ArgMatches<'static> {
App::new(file!())
Expand Down Expand Up @@ -55,8 +56,8 @@ fn main() {
let spinner = ProgressBar::new_spinner();
spinner.enable_steady_tick(200);
spinner.set_message("Initializing libmicrovmi...");
let mut drv: Box<dyn Introspectable> =
microvmi::init(domain_name, None, init_option).expect("Failed to init libmicrovmi");
let mut drv =
Microvmi::new(domain_name, None, init_option).expect("Failed to init libmicrovmi");
spinner.finish_and_clear();

println!("pausing the VM");
Expand All @@ -76,23 +77,23 @@ fn main() {
// redraw every 0.1% change, otherwise it becomes the bottleneck
bar.set_draw_delta(max_addr / 1000);

for cur_addr in (0..max_addr).step_by(PAGE_SIZE) {
for cur_addr in (0..max_addr).step_by(BUFFER_SIZE) {
trace!(
"reading {:#X} bytes of memory at {:#X}",
PAGE_SIZE,
BUFFER_SIZE,
cur_addr
);
// reset buffer each loop
let mut buffer: [u8; PAGE_SIZE] = [0; PAGE_SIZE];
let mut _bytes_read = 0;
drv.read_physical(cur_addr, &mut buffer, &mut _bytes_read)
.unwrap_or_else(|_| debug!("failed to read memory at {:#X}", cur_addr));
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
drv.padded_memory
.read_exact(&mut buffer)
.expect(&*format!("Failed to read memory at {:#X}", cur_addr));
dump_file
.write_all(&buffer)
.expect("failed to write to file");
// update bar
bar.set_prefix(&*format!("{:#X}", cur_addr));
bar.inc(PAGE_SIZE as u64);
bar.inc(BUFFER_SIZE as u64);
}
bar.finish();
println!(
Expand Down
11 changes: 5 additions & 6 deletions examples/mem-events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Instant;

use microvmi::api::{
Access, DriverInitParam, EventReplyType, EventType, InterceptType, Introspectable,
};
use microvmi::api::{Access, DriverInitParam, EventReplyType, EventType, InterceptType};
use microvmi::Microvmi;

const PAGE_SIZE: usize = 4096;

Expand All @@ -18,7 +17,7 @@ fn parse_args() -> ArgMatches<'static> {
.get_matches()
}

fn toggle_pf_intercept(drv: &mut Box<dyn Introspectable>, enabled: bool) {
fn toggle_pf_intercept(drv: &mut Microvmi, enabled: bool) {
drv.pause().expect("Failed to pause VM");

let intercept = InterceptType::Pagefault;
Expand Down Expand Up @@ -51,8 +50,8 @@ fn main() {
let init_option = matches
.value_of("kvmi_socket")
.map(|socket| DriverInitParam::KVMiSocket(socket.into()));
let mut drv: Box<dyn Introspectable> =
microvmi::init(domain_name, None, init_option).expect("Failed to init libmicrovmi");
let mut drv =
Microvmi::new(domain_name, None, init_option).expect("Failed to init libmicrovmi");
println!("Listen for memory events...");
// record elapsed time
let start = Instant::now();
Expand Down
9 changes: 5 additions & 4 deletions examples/msr-events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use std::sync::Arc;
use std::time::Instant;
use std::u32;

use microvmi::api::{DriverInitParam, EventReplyType, EventType, InterceptType, Introspectable};
use microvmi::api::{DriverInitParam, EventReplyType, EventType, InterceptType};
use microvmi::Microvmi;

// default set of MSRs to be intercepted
const DEFAULT_MSR: [u32; 6] = [0x174, 0x175, 0x176, 0xc0000080, 0xc0000081, 0xc0000082];
Expand Down Expand Up @@ -47,7 +48,7 @@ fn parse_args() -> ArgMatches<'static> {
.get_matches()
}

fn toggle_msr_intercepts(drv: &mut Box<dyn Introspectable>, vec_msr: &[u32], enabled: bool) {
fn toggle_msr_intercepts(drv: &mut Microvmi, vec_msr: &[u32], enabled: bool) {
drv.pause().expect("Failed to pause VM");

for msr in vec_msr {
Expand Down Expand Up @@ -94,8 +95,8 @@ fn main() {
.expect("Error setting Ctrl-C handler");

println!("Initialize Libmicrovmi");
let mut drv: Box<dyn Introspectable> =
microvmi::init(domain_name, None, init_option).expect("Failed to init libmicrovmi");
let mut drv =
Microvmi::new(domain_name, None, init_option).expect("Failed to init libmicrovmi");

toggle_msr_intercepts(&mut drv, &registers, true);

Expand Down
7 changes: 4 additions & 3 deletions examples/pause.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use std::{thread, time};

use clap::{App, Arg, ArgMatches};

use microvmi::api::{DriverInitParam, Introspectable};
use microvmi::api::DriverInitParam;
use microvmi::Microvmi;

fn parse_args() -> ArgMatches<'static> {
App::new(file!())
Expand Down Expand Up @@ -37,8 +38,8 @@ fn main() {
let init_option = matches
.value_of("kvmi_socket")
.map(|socket| DriverInitParam::KVMiSocket(socket.into()));
let mut drv: Box<dyn Introspectable> =
microvmi::init(domain_name, None, init_option).expect("Failed to init libmicrovmi");
let mut drv =
Microvmi::new(domain_name, None, init_option).expect("Failed to init libmicrovmi");

println!("pausing VM for {} seconds", timeout);
drv.pause().expect("Failed to pause VM");
Expand Down
7 changes: 4 additions & 3 deletions examples/regs-dump.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use clap::{App, Arg, ArgMatches};

use microvmi::api::{DriverInitParam, Introspectable};
use microvmi::api::DriverInitParam;
use microvmi::Microvmi;

fn parse_args() -> ArgMatches<'static> {
App::new(file!())
Expand Down Expand Up @@ -28,8 +29,8 @@ fn main() {
let init_option = matches
.value_of("kvmi_socket")
.map(|socket| DriverInitParam::KVMiSocket(socket.into()));
let mut drv: Box<dyn Introspectable> =
microvmi::init(domain_name, None, init_option).expect("Failed to init libmicrovmi");
let mut drv =
Microvmi::new(domain_name, None, init_option).expect("Failed to init libmicrovmi");

println!("pausing the VM");
drv.pause().expect("Failed to pause VM");
Expand Down
15 changes: 5 additions & 10 deletions python/microvmi/memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,8 @@ def read(self, size: int = ...) -> Optional[bytes]: # type: ignore
if size < 0:
# -1: read all bytes until EOF
raise NotImplementedError
data = bytearray(size)
for offset in range(0, size, PAGE_SIZE):
read_len = min(PAGE_SIZE, size - offset)
pos = self.tell()
chunk, _ = self._m.read_physical(pos, read_len)
end_offset = offset + read_len
data[offset:end_offset] = chunk
self.seek(read_len, SEEK_CUR)
self._log.debug("read return: len: %s, content: %s", len(data), data[:100])
return bytes(data)
pos = self.tell()
buffer, bytes_read = self._m.read_physical_padded(pos, size)
self.seek(size, SEEK_CUR)
self._log.debug("read return: len: %s, content: %s", len(buffer), buffer[:100])
return buffer
71 changes: 54 additions & 17 deletions python/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(clippy::upper_case_acronyms)]

mod errors;

use log::{debug, info};
Expand All @@ -7,8 +9,9 @@ use pyo3::prelude::*;

use errors::PyMicrovmiError;
use microvmi::api as rapi; // rust api
use microvmi::init;
use microvmi::Microvmi;
use pyo3::types::{PyByteArray, PyBytes};
use std::io::{Read, Seek, SeekFrom};

/// microvmi Python module declaration
#[pymodule]
Expand Down Expand Up @@ -74,7 +77,7 @@ impl DriverInitParam {
// compatible
#[pyclass(unsendable)]
struct MicrovmiExt {
driver: Box<dyn rapi::Introspectable>,
microvmi: Microvmi,
}

#[pymethods]
Expand Down Expand Up @@ -120,9 +123,9 @@ impl MicrovmiExt {
rapi::DriverInitParam::KVMiSocket(param.param_data_string)
}
});
let driver =
init(domain_name, rust_driver_type, rust_init_param).map_err(PyMicrovmiError::from)?;
Ok(MicrovmiExt { driver })
let microvmi = Microvmi::new(domain_name, rust_driver_type, rust_init_param)
.map_err(PyMicrovmiError::from)?;
Ok(MicrovmiExt { microvmi })
}

/// read VM physical memory starting from paddr, of a given size
Expand All @@ -134,16 +137,15 @@ impl MicrovmiExt {
/// Returns:
/// Tuple[bytes, int]: the read operation result and the amount bytes read
fn read_physical<'p>(
&self,
&mut self,
py: Python<'p>,
paddr: u64,
size: usize,
) -> PyResult<(&'p PyBytes, u64)> {
let mut bytes_read: u64 = 0;
self.microvmi.memory.seek(SeekFrom::Start(paddr))?;
let pybuffer: &PyBytes = PyBytes::new_with(py, size, |mut buffer| {
self.driver
.read_physical(paddr, &mut buffer, &mut bytes_read)
.ok();
bytes_read = self.microvmi.memory.read(&mut buffer).unwrap_or(0) as u64;
Ok(())
})?;

Expand All @@ -155,24 +157,59 @@ impl MicrovmiExt {
/// Args:
/// paddr (int): the physical address to start reading from
/// buffer (bytearray): the buffer to read into
fn read_physical_into(&self, paddr: u64, buffer: &PyByteArray) -> u64 {
fn read_physical_into(&mut self, paddr: u64, buffer: &PyByteArray) -> PyResult<u64> {
let mut_buf: &mut [u8] = unsafe { buffer.as_bytes_mut() };
// ignore read error
self.microvmi.memory.seek(SeekFrom::Start(paddr))?;
let bytes_read = self.microvmi.memory.read(mut_buf).unwrap_or(0) as u64;
Ok(bytes_read)
}

/// read VM physical memory starting from paddr, of a given size, adding padding if necessary
///
/// Args:
/// paddr: (int) physical address from where the read operation should start
/// size: (int) size of the read operation
///
/// Returns:
/// Tuple[bytes, int]: the read operation result and the amount bytes read
fn read_physical_padded<'p>(
&mut self,
py: Python<'p>,
paddr: u64,
size: usize,
) -> PyResult<(&'p PyBytes, u64)> {
let mut bytes_read: u64 = 0;
self.microvmi.padded_memory.seek(SeekFrom::Start(paddr))?;
let pybuffer: &PyBytes = PyBytes::new_with(py, size, |mut buffer| {
bytes_read = self.microvmi.padded_memory.read(&mut buffer).unwrap_or(0) as u64;
Ok(())
})?;

Ok((pybuffer, bytes_read))
}

/// read VM physical memory starting from paddr into the given buffer, adding padding if necessary
///
/// Args:
/// paddr (int): the physical address to start reading from
/// buffer (bytearray): the buffer to read into
fn read_physical_padded_into(&mut self, paddr: u64, buffer: &PyByteArray) -> PyResult<u64> {
let mut_buf: &mut [u8] = unsafe { buffer.as_bytes_mut() };
// ignore read error
self.driver
.read_physical(paddr, mut_buf, &mut bytes_read)
.ok();
bytes_read
self.microvmi.padded_memory.seek(SeekFrom::Start(paddr))?;
let bytes_read = self.microvmi.padded_memory.read(mut_buf).unwrap_or(0) as u64;
Ok(bytes_read)
}

/// pause the VM
fn pause(&mut self) -> PyResult<()> {
Ok(self.driver.pause().map_err(PyMicrovmiError::from)?)
Ok(self.microvmi.pause().map_err(PyMicrovmiError::from)?)
}

/// resume the VM
fn resume(&mut self) -> PyResult<()> {
Ok(self.driver.resume().map_err(PyMicrovmiError::from)?)
Ok(self.microvmi.resume().map_err(PyMicrovmiError::from)?)
}

/// get maximum physical address
Expand All @@ -181,7 +218,7 @@ impl MicrovmiExt {
/// int: the maximum physical address
fn get_max_physical_addr(&self) -> PyResult<u64> {
let max_addr = self
.driver
.microvmi
.get_max_physical_addr()
.map_err(PyMicrovmiError::from)?;
Ok(max_addr)
Expand Down
Loading