Repository: Wenzel/libmicrovmi (language: python)

Issue:
Design: Driver initialization argument handling
Opening a design issue to address the issue of passing initialization parameters to drivers.

Currently the library assumes that a `domain_name` parameter is required
https://github.com/Wenzel/libmicrovmi/blob/master/src/lib.rs#L30

and additional parameters can be passed via `Option<DriverInitParam>`.

# adding Memflow comes with new requirements

The issue comes from integrating Memflow.

Memflow has multiple connectors to choose from
- `qemu_procfs`
- `kvm`
- `pcileech`
- `coredump`

each one having a different set of parameters:
- `qemu_procfs`
  - required: none (will select the first qemu process)
  - optional: vm_name
- `kvm`
  - required: pid
- `pcileech`
  - required: device, default value: `FPGA`
  - optional: memap, path to a file
- coredump
  - required: filepath

also, we might want to initialize the memflow driver without specifying the connector, and let it scan and select the appropriate connector.

# Issue with the current implementation

- we require a `domain_name` even though it might no be necessary with memflow
- not related to memflow, but `DriverInitParam` can contain only a single enum for a single driver. It's impossible to pass multiple init args for multiple driver. (eg pass `--kvmi-unix-socket=xxx` and `--memflow-param-xxx=yyy` on the command line, in case one or the other is found)

# Proposed solution

I'm in favor a removing the `domain_name` / `DriverInitParam` and switching to a `HashMap<str, str>` for connector arguments.
This solves both issues
1. passing a `domain_name` is not required
2. you can pack multiple init args for multiple drivers in the hashmap

Looking at Memflow's own implementation of [ConnectorArgs](https://github.com/memflow/memflow/blob/master/memflow/src/connector/args.rs#L34), they wrapped a `HashMap`.
Also they added a convenient way to set a [`default`](https://github.com/memflow/memflow/blob/master/memflow/src/connector/args.rs#L47) value.

Question now, if we go this way, is how to reflect from the examples the arguments that each driver can take (required and optional) ?
Ideally, each driver would explain what its requirements are.

 


Discussion / hints:
An idea to allow each driver to describe what possible initialization parameters it can take:

`api.rs`
~~~rust
/// Describe a driver initialization parameter
#[derive(Debug)]
pub struct DriverInitParamDesc {
    name: String,
    description: String,
    required: bool,
}

pub trait Introspectable {
    /// Retrieve the possible initialization arguments for this Driver
    fn get_possible_args() -> Vec<DriverInitParam> where Self: Sized {
        // default: no parameters required
        Vec::new()
    }
}
~~~

`lib.rs`
~~~rust
fn possible_init_args() -> HashMap<DriverType, Vec<DriverInitParamDesc> {
   let map = HashMap::new();
    for drv_type in DriverType::into_enum_iter() {
         match drv_type {
              // pseudo code
              DriverType::KVM => {
                  map.insert(drv_type, KVM::get_possible_args())
              }
         }
    }
    map
}
~~~

On the user side:
`example.rs`
~~~rust
use microvmi::api::DriverParams;
use microvmi::{init_driver, possible_init_args}

fn main() {
	if ("--help") {
            // get all possible init args, to display them for --help usage
	    let poss_args = possible_init_args();
	    // display them
	    // for x in poss_args.iter() .... 
            //     x.display()
            return;
	}
	
	// prepare driver init
	let init_params = DriverParams::with_default("windows_10")
	    // add arg for KVM driver
            .insert("kvmi_unix_socket", "/tmp/introspector")
	    // add arg for memflow driver
	    .insert("memflow_pid", "45617")
	let driver = init_driver(None, &init_params)
}
~~~
The idea looks good to me. I have one small note though: Why use a hash map? Wouldn't it be nicer to use a complex enum where each enum variant wraps an individual struct for those driver init params?
I like enums because their a give a strong type definition instead of a losely defined string that wouldn't be checked at compile-time.

However, it would be difficilt to express the init parameter combinatory with fully qualified enums:

Lets assume we want to initialize by passing an enum:
~~~rust
pub enum XenInitParams {
    // Xen can be initialized with a vm_name
    Default { vm_name: String },
}
~~~
~~~rust

use microvmi::init_driver;
use microvmi::api::XenInitParams;

let init_params  = XenInitParams::Default(String::from("Windows10"));
let driver = init_driver(None, &init_params);
~~~

The situation get less intuitive for KVM:
~~~rust
// both of the enum variant will have to take a vm_name mandatory field
// not very clear from an API user point of view, but okay
pub enum KVMInitParams {
	UnixSocket {vm_name: String, socket_path: String },
	VSock {vm_name: String, port: u32 },
}
~~~

But the problem arises with memflow.
Remember that a memflow driver can be initialized like this:
- no connector name, no arguments
- no connector name, arguments
- connector name, no arguments
- connector name, arguments

and each connector has a set of specific arguments:
- `qemu_procfs`
  - required: none (will select the first qemu process)
  - optional: vm_name
- `kvm`
  - required: pid
- `pcileech`
  - required: device, default value: `FPGA`
  - optional: memap, path to a file
- coredump
  - required: filepath

We could sketch an enum like this:
~~~rust
pub enum MemflowInitParams {
	Default { connector_name: Option<String>, connector_args: Option<HashMap<str, str>>},
}
~~~

but it loosely defines what parameters are actually accepted by the memflow driver.
Also, another issue here is that we can only pass one enum at init, and we forbid the possibility of supplying multiple initialization parameters for multiple drivers at the same time.

If I read from a configuration file, and I want to initialize KVM with a given unix socket and memflow with a QEMU pid, depending on each one of them will be detected at runtime, i would need to express that possibility.
Update: the memflow connector_args could be rewritten with a more expressive enum like this:
~~~rust
pub enum MemflowConnectorParams {
	// optional vm_name, otherwise will search for the first QEMU process
	QEMUProcFs { vm_name: Option<String>},
	KVM { pid: u32 },
	// default value for device: "FPGA"
	PCILeech { device: Option<String>, memmap: Option<Path> },
	Coredump { filepath: Path },
}

pub enum MemflowInitParams {
	Default { connector_name: Option<String>, connector_args: Option<MemflowConnectorParams>},
}
~~~
Update, thinking again on your proposal, to reflect the possibility of passing mutiple initialization parameters for multiple drivers:

~~~rust
use std::path::PathBuf;

/// Describes Xen initialization parameters
pub enum XenInitParams {
    Default { vm_name: String },
}

/// Describes KVM initialization parameters
pub enum KVMInitParams {
    UnixSocket {
        vm_name: String,
        socket_path: PathBuf,
    },
    VSock {
        vm_name: String,
        port: u32,
    },
}

/// Describes Memflow connector parameters
pub enum MemflowConnectorParams {
    // optional vm_name, otherwise will search for the first QEMU process
    QEMUProcFs {
        vm_name: Option<String>,
    },
    KVM {
        pid: u32,
    },
    // default value for device: "FPGA"
    PCILeech {
        device: Option<String>,
        memmap: Option<PathBuf>,
    },
    Coredump {
        filepath: PathBuf,
    },
}


/// Describes Memflow initialization parameters
pub enum MemflowInitParams {
    Default {
        connector_name: Option<String>,
        connector_args: Option<MemflowConnectorParams>,
    },
}

/// This struct is used to pass all initialization parameters for all drivers
/// when calling init_driver()
#[derive(Default)]
pub struct MicrovmiInitParams {
    xen: Option<XenInitParams>,
    kvm: Option<KVMInitParams>,
    memflow: Option<MemflowInitParams>,
}

fn main() {
    let init_params_kvm = KVMInitParams::UnixSocket {
        vm_name: String::from("windows10"),
        socket_path: PathBuf::from(r"/tmp/introspector"),
    };
    let init_params_xen = XenInitParams::Default {
        vm_name: String::from("windows10"),
    };
    let init_params = MicrovmiInitParams {
        kvm: Some(init_params_kvm),
        xen: Some(init_params_xen),
        ..Default::default()
    };
    let driver = init_driver(None, init_params);
}
~~~
Update: we can pack the common parameters inside the `MicrovmiInitParams` struct:

~~~rust
use std::error::Error;
use std::path::PathBuf;

pub enum DriverType {}

pub fn init_driver(
    driver_type: Option<DriverType>,
    driver_init_params: Option<MicrovmiInitParams>,
) -> Result<(), Box<dyn Error>> {
    unimplemented!();
}

/// Describes Xen initialization parameters
pub enum XenInitParams {}

/// Describes KVM initialization parameters
pub enum KVMInitParams {
    UnixSocket { socket_path: PathBuf },
    VSock { port: u32 },
}

/// Describes Memflow connector parameters
pub enum MemflowConnectorParams {
    // optional vm_name, otherwise will search for the first QEMU process
    QEMUProcFs {
        vm_name: Option<String>,
    },
    KVM {
        pid: u32,
    },
    // default value for device: "FPGA"
    PCILeech {
        device: Option<String>,
        memmap: Option<PathBuf>,
    },
    Coredump {
        filepath: PathBuf,
    },
}

/// Describes Memflow initialization parameters
pub enum MemflowInitParams {
    Default {
        connector_name: Option<String>,
        connector_args: Option<MemflowConnectorParams>,
    },
}

pub struct CommonInitParams {
    vm_name: String,
}

/// This struct is used to pass all initialization parameters for all drivers
/// when calling init_driver()
#[derive(Default)]
pub struct MicrovmiInitParams {
    common: Option<CommonInitParams>,
    xen: Option<XenInitParams>,
    kvm: Option<KVMInitParams>,
    memflow: Option<MemflowInitParams>,
}

fn main() {
    let common_init_params = CommonInitParams {
        vm_name: String::from("windows10"),
    };
    let init_params_kvm = KVMInitParams::UnixSocket {
        socket_path: PathBuf::from(r"/tmp/introspector"),
    };
    let init_params = MicrovmiInitParams {
        common: Some(common_init_params),
        kvm: Some(init_params_kvm),
        ..Default::default()
    };
    let driver = init_driver(None, Some(init_params));
}
~~~

You can find the design code on [rust playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=18c3af2d6e3a77f9ebd173b9765a2880)

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 54852c09..90d0d21b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -241,7 +241,7 @@ jobs:
         working-directory: python
 
       - name: smoke test
-        run: python3 -c 'from microvmi import Microvmi, DriverType, DriverInitParam'
+        run: python3 -c 'from microvmi import Microvmi, DriverType, CommonInitParamsPy, DriverInitParamsPy, KVMInitParamsPy'
 
       # setup docker cache to speedup rest of the job
       - name: Docker Layer Caching
diff --git a/Cargo.toml b/Cargo.toml
index 1a8a3932..1438e06c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -44,6 +44,7 @@ ntapi = { version = "0.3", optional = true }
 vid-sys = { version = "=0.3.0", features = ["deprecated-apis"], optional = true }
 
 [dev-dependencies]
+utilities = { path = "utilities" }
 ctrlc = "3.1"
 clap = "2.33"
 colored = "2.0"
diff --git a/c_examples/mem-dump.c b/c_examples/mem-dump.c
index af08f6ed..40ebd1f5 100644
--- a/c_examples/mem-dump.c
+++ b/c_examples/mem-dump.c
@@ -44,7 +44,13 @@ int main(int argc, char* argv[]) {
     }
     microvmi_envlogger_init();
     const char* init_error = NULL;
-    void* driver = microvmi_init(argv[1], NULL, NULL, &init_error);
+    void* vm_name = argv[1];
+    DriverInitParamsFFI init_params = {
+        .common = {
+            .vm_name = vm_name
+        }
+    };
+    void* driver = microvmi_init(NULL, &init_params, &init_error);
     if (!driver) {
         fprintf(stderr, "%s\n", init_error);
         rs_cstring_free((char*)init_error);
diff --git a/c_examples/pause.c b/c_examples/pause.c
index f5fe47b9..9fc4e4cb 100644
--- a/c_examples/pause.c
+++ b/c_examples/pause.c
@@ -32,7 +32,13 @@ int main(int argc, char* argv[]) {
     }
     microvmi_envlogger_init();
     const char* init_error = NULL;
-    void* driver = microvmi_init(argv[1], NULL, NULL, &init_error);
+        void* vm_name = argv[1];
+        DriverInitParamsFFI init_params = {
+            .common = {
+                .vm_name = vm_name
+            }
+        };
+    void* driver = microvmi_init(NULL, &init_params, &init_error);
     if (!driver) {
         fprintf(stderr, "%s\n", init_error);
         rs_cstring_free((char*)init_error);
diff --git a/c_examples/regs-dump.c b/c_examples/regs-dump.c
index ae65615e..0f52bca4 100644
--- a/c_examples/regs-dump.c
+++ b/c_examples/regs-dump.c
@@ -42,7 +42,13 @@ int main(int argc, char* argv[]) {
     }
     microvmi_envlogger_init();
     const char* init_error = NULL;
-    void* driver = microvmi_init(argv[1], NULL, NULL, &init_error);
+    void* vm_name = argv[1];
+    DriverInitParamsFFI init_params = {
+        .common = {
+            .vm_name = vm_name
+        }
+    };
+    void* driver = microvmi_init(NULL, &init_params, &init_error);
     if (!driver) {
         fprintf(stderr, "%s\n", init_error);
         rs_cstring_free((char*)init_error);
diff --git a/doc/src/reference/python_api.md b/doc/src/reference/python_api.md
index 9294ad85..a0b787bd 100644
--- a/doc/src/reference/python_api.md
+++ b/doc/src/reference/python_api.md
@@ -3,24 +3,45 @@
 ### Initializing libmicrovmi
 
 ~~~Python
-from microvmi import Microvmi
-
-micro = Microvmi("Windows10")
+from microvmi import Microvmi, DriverInitParamsPy, CommonInitParamsPy
+
+# setup common params
+common = CommonInitParamsPy()
+common.vm_name = "windows10"
+# setup main init_params
+init_params = DriverInitParamsPy()
+init_params.common = common
+micro = Microvmi(None, init_params)
 ~~~
 
 ### Specifying the hypervisor
 
 ~~~Python
-from microvmi import Microvmi, DriverType
-
-micro = Microvmi("Windows10", DriverType.XEN)
+from microvmi import Microvmi, DriverType, DriverInitParamsPy, CommonInitParamsPy
+
+# setup common params
+common = CommonInitParamsPy()
+common.vm_name = "windows10"
+# setup main init_params
+init_params = DriverInitParamsPy()
+init_params.common = common
+micro = Microvmi(DriverType.XEN, init_params)
 ~~~
 
 ### Adding driver initialization parameters
 
 ~~~Python
-from microvmi import Microvmi, DriverType, DriverInitParam
-
-init_param = DriverInitParam.kvmi_unix_socket("/tmp/introspector")
-micro = Microvmi("Windows10", DriverType.KVM, init_param)
+from microvmi import Microvmi, DriverInitParamsPy, CommonInitParamsPy, KVMInitParamsPy
+
+# setup common params
+common = CommonInitParamsPy()
+common.vm_name = "windows10"
+# setup kvm params
+kvm = KVMInitParamsPy()
+kvm.unix_socket = "/tmp/introspector"
+# setup main init_params
+init_params = DriverInitParamsPy()
+init_params.common = common
+init_params.kvm = kvm
+micro = Microvmi(DriverType.KVM, init_params)
 ~~~
diff --git a/doc/src/tutorial/integration/volatility3.md b/doc/src/tutorial/integration/volatility3.md
index 3a49b5c6..26bf595e 100644
--- a/doc/src/tutorial/integration/volatility3.md
+++ b/doc/src/tutorial/integration/volatility3.md
@@ -39,15 +39,19 @@ venv/lib/python3.7/site-packages/microvmi/volatility
 
 The libmicrovmi handler for volatility is a URL handler with the following syntax:
 
-    vmi://[hypervisor]/vm_name
+    vmi://[hypervisor]/?param1=value1...
 
 The hypervisor part is optional. If not specified, it will default to try every builtin driver available.
 
 Additional driver parameters can be specified.
 
+To pass the VM name:
+
+    vmi:///?vm_name=windows10
+
 To pass the KVMi socket:
 
-    vmi:///vm_name?kvmi_unix_socket=/tmp/introspector
+    vmi:///?vm_name=windows10&kvm_unix_socket=/tmp/introspector
 
 ## Running volatility3
 
@@ -57,7 +61,7 @@ Let's put all of this together and run volatility3 combined with libmicrovmi.
 - `--single-location vmi://` url
 
 ~~~
-(venv) $ vol -p <plugin_dir> --single-location vmi:///vm_name <volatility plugin>
+(venv) $ vol -p <plugin_dir> --single-location vmi:///?vm_name=windows10 <volatility plugin>
 ~~~
 
 ### Example listing processes on Xen
@@ -65,12 +69,6 @@ Let's put all of this together and run volatility3 combined with libmicrovmi.
 ~~~bash
 (venv) $ sudo -E ./venv/bin/vol \  # running volatility3 as root (required by the Xen driver)
     -p venv/lib/python3.7/site-packages/microvmi/volatility \  # path to the microvmi connection plugin
-    --single-location vmi:///winxp \  # specify the resource location
+    --single-location vmi:///?vm_name=winxp \  # specify the resource location
     windows.pslist.PsList  # volatility's pslist plugin
 ~~~
-
-🎥 [asciicast](https://asciinema.org/a/6YOXUkEwt53uYcU5rXxoWaFLq)
-
-### Example listing processes on KVM
-
-🎥 [asciicast](https://asciinema.org/a/DTyjM0rnq26RYbFX7hbS7jXvP)
diff --git a/examples/cr-events.rs b/examples/cr-events.rs
index 08ffe501..3d9da8f5 100644
--- a/examples/cr-events.rs
+++ b/examples/cr-events.rs
@@ -5,14 +5,17 @@ use std::time::Instant;
 use clap::{App, Arg, ArgMatches};
 use colored::*;
 
-use microvmi::api::{CrType, DriverInitParam, EventType, InterceptType, Introspectable};
+use microvmi::api::events::{CrType, EventType, InterceptType};
+use microvmi::api::params::DriverInitParams;
+use microvmi::api::Introspectable;
+use utilities::Clappable;
 
 fn parse_args() -> ArgMatches<'static> {
     App::new(file!())
         .version("0.3")
         .author("Mathieu Tarral")
         .about("Watches control register VMI events")
-        .arg(Arg::with_name("vm_name").index(1).required(true))
+        .args(DriverInitParams::to_clap_args().as_ref())
         .arg(
             Arg::with_name("register")
                 .multiple(true)
@@ -21,14 +24,6 @@ fn parse_args() -> ArgMatches<'static> {
                 .default_value("3")
                 .help("control register to intercept. Possible values: [0 3 4]"),
         )
-        .arg(
-            Arg::with_name("kvmi_socket")
-                .short("k")
-                .takes_value(true)
-                .help(
-                "pass additional KVMi socket initialization parameter required for the KVM driver",
-            ),
-        )
         .get_matches()
 }
 
@@ -71,7 +66,6 @@ fn main() {
 
     let matches = parse_args();
 
-    let domain_name = matches.value_of("vm_name").unwrap();
     let registers: Vec<_> = matches.values_of("register").unwrap().collect();
 
     let vec_cr = get_cr(registers);
@@ -85,11 +79,9 @@ 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 init_params = DriverInitParams::from_matches(&matches);
     let mut drv: Box<dyn Introspectable> =
-        microvmi::init(domain_name, None, init_option).expect("Failed to init libmicrovmi");
+        microvmi::init(None, Some(init_params)).expect("Failed to init libmicrovmi");
 
     // enable control register interception
     toggle_cr_intercepts(&mut drv, &vec_cr, true);
diff --git a/examples/interrupt-events.rs b/examples/interrupt-events.rs
index d081eafd..04b92e64 100644
--- a/examples/interrupt-events.rs
+++ b/examples/interrupt-events.rs
@@ -2,24 +2,19 @@ use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::Arc;
 use std::time::Instant;
 
-use clap::{App, Arg, ArgMatches};
+use clap::{App, ArgMatches};
 use colored::*;
 
-use microvmi::api::{DriverInitParam, EventReplyType, EventType, InterceptType, Introspectable};
+use microvmi::api::events::{EventReplyType, EventType, InterceptType};
+use microvmi::api::params::DriverInitParams;
+use microvmi::api::Introspectable;
+use utilities::Clappable;
 
 fn parse_args() -> ArgMatches<'static> {
     App::new(file!())
         .version("0.1")
         .about("Watches interrupt VMI events")
-        .arg(Arg::with_name("vm_name").index(1).required(true))
-        .arg(
-            Arg::with_name("kvmi_socket")
-                .short("k")
-                .takes_value(true)
-                .help(
-                "pass additional KVMi socket initialization parameter required for the KVM driver",
-            ),
-        )
+        .args(DriverInitParams::to_clap_args().as_ref())
         .get_matches()
 }
 
@@ -42,11 +37,6 @@ fn main() {
 
     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();
@@ -56,8 +46,9 @@ fn main() {
     .expect("Error setting Ctrl-C handler");
 
     println!("Initialize Libmicrovmi");
+    let init_params = DriverInitParams::from_matches(&matches);
     let mut drv: Box<dyn Introspectable> =
-        microvmi::init(domain_name, None, init_option).expect("Failed to init libmicrovmi");
+        microvmi::init(None, Some(init_params)).expect("Failed to init libmicrovmi");
 
     //Enable int3 interception
     toggle_int3_interception(&mut drv, true);
diff --git a/examples/mem-dump.rs b/examples/mem-dump.rs
index 71cd62be..b4012306 100644
--- a/examples/mem-dump.rs
+++ b/examples/mem-dump.rs
@@ -6,7 +6,10 @@ use clap::{App, Arg, ArgMatches};
 use indicatif::{ProgressBar, ProgressStyle};
 use log::{debug, trace};
 
-use microvmi::api::{DriverInitParam, Introspectable};
+use microvmi::api::params::DriverInitParams;
+use microvmi::api::Introspectable;
+
+use utilities::Clappable;
 
 const PAGE_SIZE: usize = 4096;
 
@@ -15,15 +18,7 @@ fn parse_args() -> ArgMatches<'static> {
         .version("0.1")
         .author("Mathieu Tarral")
         .about("Dumps VM physical memory")
-        .arg(Arg::with_name("vm_name").index(1).required(true))
-        .arg(
-            Arg::with_name("kvmi_socket")
-                .short("k")
-                .takes_value(true)
-                .help(
-                "pass additional KVMi socket initialization parameter required for the KVM driver",
-            ),
-        )
+        .args(DriverInitParams::to_clap_args().as_ref())
         .arg(
             Arg::with_name("output")
                 .short("o")
@@ -37,8 +32,12 @@ fn main() {
     env_logger::init();
 
     let matches = parse_args();
-    let domain_name = matches.value_of("vm_name").unwrap();
 
+    let init_params = DriverInitParams::from_matches(&matches);
+    let domain_name: String = init_params
+        .common
+        .clone()
+        .map_or(String::from("unknown_vm_name"), |v| v.vm_name);
     let dump_path = Path::new(
         matches
             .value_of("output")
@@ -48,15 +47,12 @@ fn main() {
     let mut dump_file = File::create(&dump_path).expect("Fail to open dump file");
     dump_path.canonicalize().unwrap();
 
-    let init_option = matches
-        .value_of("kvmi_socket")
-        .map(|socket| DriverInitParam::KVMiSocket(socket.into()));
-
     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");
+        microvmi::init(None, Some(init_params)).expect("Failed to init libmicrovmi");
     spinner.finish_and_clear();
 
     println!("pausing the VM");
diff --git a/examples/mem-events.rs b/examples/mem-events.rs
index 61f2130e..af41c38d 100644
--- a/examples/mem-events.rs
+++ b/examples/mem-events.rs
@@ -1,12 +1,14 @@
-use clap::{App, Arg, ArgMatches};
-use colored::*;
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::Arc;
 use std::time::Instant;
 
-use microvmi::api::{
-    Access, DriverInitParam, EventReplyType, EventType, InterceptType, Introspectable,
-};
+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;
 
@@ -14,7 +16,7 @@ fn parse_args() -> ArgMatches<'static> {
     App::new(file!())
         .version("0.1")
         .about("Watches memory VMI events")
-        .arg(Arg::with_name("vm_name").index(1).required(true))
+        .args(DriverInitParams::to_clap_args().as_ref())
         .get_matches()
 }
 
@@ -37,8 +39,6 @@ fn main() {
 
     let matches = parse_args();
 
-    let domain_name = matches.value_of("vm_name").unwrap();
-
     // set CTRL-C handler
     let running = Arc::new(AtomicBool::new(true));
     let r = running.clone();
@@ -48,11 +48,9 @@ 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 init_params = DriverInitParams::from_matches(&matches);
     let mut drv: Box<dyn Introspectable> =
-        microvmi::init(domain_name, None, init_option).expect("Failed to init libmicrovmi");
+        microvmi::init(None, Some(init_params)).expect("Failed to init libmicrovmi");
     println!("Listen for memory events...");
     // record elapsed time
     let start = Instant::now();
diff --git a/examples/msr-events.rs b/examples/msr-events.rs
index 41da8511..4a29e444 100644
--- a/examples/msr-events.rs
+++ b/examples/msr-events.rs
@@ -1,11 +1,15 @@
-use clap::{App, Arg, ArgMatches};
-use colored::*;
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::Arc;
 use std::time::Instant;
 use std::u32;
 
-use microvmi::api::{DriverInitParam, EventReplyType, EventType, InterceptType, Introspectable};
+use clap::{App, Arg, ArgMatches};
+use colored::*;
+
+use microvmi::api::events::{EventReplyType, EventType, InterceptType};
+use microvmi::api::params::DriverInitParams;
+use microvmi::api::Introspectable;
+use utilities::Clappable;
 
 // default set of MSRs to be intercepted
 const DEFAULT_MSR: [u32; 6] = [0x174, 0x175, 0x176, 0xc0000080, 0xc0000081, 0xc0000082];
@@ -23,12 +27,6 @@ fn parse_args() -> ArgMatches<'static> {
                 - MSR_LSTAR:              0xc0000082\
                 ",
         )
-        .arg(
-            Arg::with_name("vm_name")
-                .help("VM name")
-                .index(1)
-                .required(true),
-        )
         .arg(
             Arg::with_name("MSR index")
                 .help("Specific set of MSRs to be intercepted")
@@ -36,14 +34,7 @@ fn parse_args() -> ArgMatches<'static> {
                 .takes_value(true)
                 .short("r"),
         )
-        .arg(
-            Arg::with_name("kvmi_socket")
-                .short("k")
-                .takes_value(true)
-                .help(
-                "pass additional KVMi socket initialization parameter required for the KVM driver",
-            ),
-        )
+        .args(DriverInitParams::to_clap_args().as_ref())
         .get_matches()
 }
 
@@ -68,7 +59,6 @@ fn main() {
 
     let matches = parse_args();
 
-    let domain_name = matches.value_of("vm_name").unwrap();
     let mut registers: Vec<u32> = matches.values_of("register").map_or(Vec::new(), |v| {
         v.map(|s| {
             s.parse::<u32>()
@@ -81,10 +71,6 @@ fn main() {
         registers = DEFAULT_MSR.to_vec();
     }
 
-    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();
@@ -94,8 +80,9 @@ fn main() {
     .expect("Error setting Ctrl-C handler");
 
     println!("Initialize Libmicrovmi");
+    let init_params = DriverInitParams::from_matches(&matches);
     let mut drv: Box<dyn Introspectable> =
-        microvmi::init(domain_name, None, init_option).expect("Failed to init libmicrovmi");
+        microvmi::init(None, Some(init_params)).expect("Failed to init libmicrovmi");
 
     toggle_msr_intercepts(&mut drv, &registers, true);
 
diff --git a/examples/pause.rs b/examples/pause.rs
index c4025b9d..bb841b50 100644
--- a/examples/pause.rs
+++ b/examples/pause.rs
@@ -1,29 +1,23 @@
+use clap::{App, Arg, ArgMatches};
 use std::{thread, time};
 
-use clap::{App, Arg, ArgMatches};
+use microvmi::api::params::DriverInitParams;
+use microvmi::api::Introspectable;
 
-use microvmi::api::{DriverInitParam, Introspectable};
+use utilities::Clappable;
 
 fn parse_args() -> ArgMatches<'static> {
     App::new(file!())
         .version("0.1")
         .author("Mathieu Tarral")
         .about("Pauses and resumes the VM")
-        .arg(Arg::with_name("vm_name").index(1).required(true))
         .arg(
             Arg::with_name("timeout")
                 .takes_value(true)
                 .default_value("5")
                 .help("pause the VM during timeout seconds"),
         )
-        .arg(
-            Arg::with_name("kvmi_socket")
-                .short("k")
-                .takes_value(true)
-                .help(
-                "pass additional KVMi socket initialization parameter required for the KVM driver",
-            ),
-        )
+        .args(DriverInitParams::to_clap_args().as_ref())
         .get_matches()
 }
 
@@ -31,14 +25,12 @@ fn main() {
     env_logger::init();
 
     let matches = parse_args();
-    let domain_name = matches.value_of("vm_name").unwrap();
     let timeout = matches.value_of("timeout").unwrap().parse::<u64>().unwrap();
 
-    let init_option = matches
-        .value_of("kvmi_socket")
-        .map(|socket| DriverInitParam::KVMiSocket(socket.into()));
+    // get driver params
+    let init_params = DriverInitParams::from_matches(&matches);
     let mut drv: Box<dyn Introspectable> =
-        microvmi::init(domain_name, None, init_option).expect("Failed to init libmicrovmi");
+        microvmi::init(None, Some(init_params)).expect("Failed to init libmicrovmi");
 
     println!("pausing VM for {} seconds", timeout);
     drv.pause().expect("Failed to pause VM");
diff --git a/examples/regs-dump.rs b/examples/regs-dump.rs
index 6d0e218d..cc54d8ca 100644
--- a/examples/regs-dump.rs
+++ b/examples/regs-dump.rs
@@ -1,6 +1,9 @@
 use clap::{App, Arg, ArgMatches};
 
-use microvmi::api::{DriverInitParam, Introspectable};
+use microvmi::api::params::DriverInitParams;
+use microvmi::api::Introspectable;
+
+use utilities::Clappable;
 
 fn parse_args() -> ArgMatches<'static> {
     App::new(file!())
@@ -8,14 +11,7 @@ fn parse_args() -> ArgMatches<'static> {
         .author("Mathieu Tarral")
         .about("Dumps the state of registers on VCPU 0")
         .arg(Arg::with_name("vm_name").index(1).required(true))
-        .arg(
-            Arg::with_name("kvmi_socket")
-                .short("k")
-                .takes_value(true)
-                .help(
-                "pass additional KVMi socket initialization parameter required for the KVM driver",
-            ),
-        )
+        .args(DriverInitParams::to_clap_args().as_ref())
         .get_matches()
 }
 
@@ -23,13 +19,9 @@ 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()));
+    let init_params = DriverInitParams::from_matches(&matches);
     let mut drv: Box<dyn Introspectable> =
-        microvmi::init(domain_name, None, init_option).expect("Failed to init libmicrovmi");
+        microvmi::init(None, Some(init_params)).expect("Failed to init libmicrovmi");
 
     println!("pausing the VM");
     drv.pause().expect("Failed to pause VM");
diff --git a/python/examples/mem-dump.py b/python/examples/mem-dump.py
index 08848436..3e35de51 100755
--- a/python/examples/mem-dump.py
+++ b/python/examples/mem-dump.py
@@ -3,8 +3,9 @@
 import argparse
 import logging
 from contextlib import contextmanager
+from typing import Optional
 
-from microvmi import Microvmi
+from microvmi import CommonInitParamsPy, DriverInitParamsPy, KVMInitParamsPy, Microvmi
 from rich import print
 from rich.progress import BarColumn, Progress, TextColumn
 
@@ -22,14 +23,25 @@ def pause_ctxt(micro: Microvmi):
 
 def parse_args():
     parser = argparse.ArgumentParser()
-    parser.add_argument("vm_name", help="The VM name to dump memory from")
+    parser.add_argument("-d", "--vm_name", type=str, help="The VM name to dump memory from")
+    parser.add_argument("-k", "--kvm-unix-socket", type=str, help="The Unix socket path for the KVM driver")
     parser.add_argument("-o", "--output-file", help="Dump file path", default="{vm_name}.dump")
     args = parser.parse_args()
     return args
 
 
-def dump_mem(vm_name, output_file):
-    micro = Microvmi(vm_name)
+def dump_mem(vm_name: Optional[str], kvm_unix_socket: Optional[str], output_file: Optional[str]):
+    # prepare drivers init params
+    init_params = DriverInitParamsPy()
+    common = CommonInitParamsPy()
+    common.vm_name = vm_name
+    init_params.common = common
+    if kvm_unix_socket:
+        kvm = KVMInitParamsPy()
+        kvm.unix_socket = kvm_unix_socket
+        init_params.kvm = kvm
+    # init libmicrovmi
+    micro = Microvmi(None, init_params)
     destination = output_file.format(vm_name=vm_name)
     with pause_ctxt(micro):
         max_addr = micro.max_addr
@@ -58,8 +70,9 @@ def main():
 
     try:
         vm_name = args.vm_name
+        kvm_unix_socket = args.kvm_unix_socket
         output_file = args.output_file
-        dump_mem(vm_name, output_file)
+        dump_mem(vm_name, kvm_unix_socket, output_file)
     except KeyboardInterrupt:
         logging.critical("Cancelled by CTRL-C")
 
diff --git a/python/microvmi/__init__.py b/python/microvmi/__init__.py
index bc8e2553..898f4a46 100644
--- a/python/microvmi/__init__.py
+++ b/python/microvmi/__init__.py
@@ -1,3 +1,3 @@
 from microvmi.microvmi import DriverType, Microvmi
 
-from .pymicrovmi import DriverInitParam
+from .pymicrovmi import CommonInitParamsPy, DriverInitParamsPy, KVMInitParamsPy
diff --git a/python/microvmi/microvmi.py b/python/microvmi/microvmi.py
index 3e3d8b8c..da6d1071 100644
--- a/python/microvmi/microvmi.py
+++ b/python/microvmi/microvmi.py
@@ -3,7 +3,7 @@
 
 from microvmi.memory import PaddedPhysicalMemoryIO, PhysicalMemoryIO
 
-from .pymicrovmi import DriverInitParam, MicrovmiExt
+from .pymicrovmi import DriverInitParamsPy, MicrovmiExt
 
 
 class DriverType(Enum):
@@ -19,20 +19,33 @@ class Microvmi:
 
     def __init__(
         self,
-        domain_name: str,
         driver_type: DriverType = None,
-        drv_init_param: DriverInitParam = None,
+        init_params: DriverInitParamsPy = None,
     ):
         """
         Initialize a Microvmi instance
 
         Args:
-            domain_name (str): the domain name
             driver_type (int, optional): the hypervisor driver type on which the library should be initialized
-            init_param (DriverInitParam, optional): additional initialization parameters for driver initialization
+            init_params (DriverInitParamsPy, optional): initialization parameters for driver initialization
+
+        Examples:
+            from microvmi import Microvmi, DriverInitParamsPy, CommonInitParamsPy, KVMInitParamsPy
+            # setup common params
+            common = CommonInitParamsPy()
+            common.vm_name = "windows10"
+            # setup kvm params
+            kvm = KVMInitParamsPy()
+            kvm.unix_socket = "/tmp/introspector"
+            # setup main init_params
+            init_params = DriverInitParamsPy()
+            init_params.common = common
+            init_params.kvm = kvm
+            # init
+            m = Microvmi(None, init_params)
         """
         drv_type_ext: Optional[int] = driver_type.value if driver_type is not None else None
-        self._micro = MicrovmiExt(domain_name, drv_type_ext, drv_init_param)
+        self._micro = MicrovmiExt(drv_type_ext, init_params)
         self._memory = PhysicalMemoryIO(self._micro)
         self._padded_memory = PaddedPhysicalMemoryIO(self._micro)
 
diff --git a/python/microvmi/volatility/vmi_handler.py b/python/microvmi/volatility/vmi_handler.py
index f0cd7a6b..37c99f0b 100644
--- a/python/microvmi/volatility/vmi_handler.py
+++ b/python/microvmi/volatility/vmi_handler.py
@@ -1,9 +1,9 @@
 import logging
-from typing import Any, List, Optional, Tuple
+from typing import Any, Dict, List, Optional, Tuple
 from urllib.parse import parse_qs, urlparse
 from urllib.request import BaseHandler, Request
 
-from microvmi import DriverInitParam, DriverType, Microvmi
+from microvmi import CommonInitParamsPy, DriverInitParamsPy, DriverType, KVMInitParamsPy, Microvmi
 
 # to be used by volatility, the VMIHandler should inherit from VolatilityHandler
 # in order to be non cacheable
@@ -31,15 +31,11 @@ class VMIHandler(VolatilityHandler):
     Documentation: https://wenzel.github.io/libmicrovmi/
 
     Syntax is defined here:
-        vmi://<hypervisor>/<vm_name>?param1=value1&param2=value2
+        vmi://<hypervisor>/?param1=value1&param2=value2
     """
 
     SCHEME = "vmi"
 
-    """Map of driver initialization parameter keys to the associated DriverInitParam functions
-    exposed by the pymicrovmi python extension"""
-    DRIVER_INIT_PARAM_MAP = {"kvmi_unix_socket": DriverInitParam.kvmi_unix_socket}
-
     @classmethod
     def non_cached_schemes(cls) -> List[str]:
         return [VMIHandler.SCHEME]
@@ -48,29 +44,27 @@ def non_cached_schemes(cls) -> List[str]:
     def vmi_open(req: Request) -> Optional[Any]:
         """Handles the request if it's the VMI scheme"""
         logging.getLogger("microvmi").setLevel(logging.WARNING)
-        vm_name, driver_type, init_param = url_to_driver_parameters(req.full_url)
+        driver_type, init_params = url_to_driver_parameters(req.full_url)
         # this method is called multiple times
         # just return if already initialized instance
         global micro
         if micro is not None:
             return micro.padded_memory
         # init Microvmi
-        micro = Microvmi(vm_name, driver_type, init_param)
+        micro = Microvmi(driver_type, init_params)
         return micro.padded_memory
 
 
-def url_to_driver_parameters(url: str) -> Tuple[str, Optional[DriverType], Optional[DriverInitParam]]:
+def url_to_driver_parameters(url: str) -> Tuple[Optional[DriverType], Optional[DriverInitParamsPy]]:
     """Parses a given request and extracts the Microvmi driver initialization parameters"""
     parsed_url = urlparse(url)
     # scheme
     _validate_scheme(parsed_url.scheme)
-    # domain
-    vm_name: str = _parse_vm_name(parsed_url.path)
     # hypervisor
     driver_type: Optional[DriverType] = _parse_hypervisor(parsed_url.netloc)
     # init params
-    init_param: Optional[DriverInitParam] = _parse_driver_init_params(parsed_url.query)
-    return vm_name, driver_type, init_param
+    init_params: Optional[DriverInitParamsPy] = _parse_driver_init_params(parsed_url.query)
+    return driver_type, init_params
 
 
 def _validate_scheme(scheme: str):
@@ -99,20 +93,24 @@ def _parse_hypervisor(netloc: str) -> Optional[DriverType]:
     return None
 
 
-def _parse_driver_init_params(query: str) -> Optional[DriverInitParam]:
+def _parse_driver_init_params(query: str) -> Optional[DriverInitParamsPy]:
     if not query:
         return None
-    url_params = parse_qs(query, strict_parsing=True)
+    url_params: Dict[str, List[str]] = parse_qs(query, strict_parsing=True)
     if not url_params:
         return None
-    if len(url_params) > 1:
-        raise MicrovmiHandlerError("Only one driver initialization parameter is supported")
-    try:
-        key = list(url_params.keys())[0]
-        init_param_func = VMIHandler.DRIVER_INIT_PARAM_MAP[key]
-    except KeyError as e:
-        raise MicrovmiHandlerError(
-            f"Unknown driver initialization parameter. Allow init parameters: {VMIHandler.DRIVER_INIT_PARAM_MAP.keys()}"
-        ) from e
-    else:
-        return init_param_func(url_params[key][0])
+    common = None
+    kvm = None
+    for param, list_value in url_params.items():
+        if param == "vm_name":
+            common = CommonInitParamsPy()
+            common.vm_name = list_value[0]
+        elif param == "kvm_unix_socket":
+            kvm = KVMInitParamsPy()
+            kvm.unix_socket = list_value[0]
+        else:
+            raise MicrovmiHandlerError(f"Unknown driver initialization parameter: {param}")
+    init_params = DriverInitParamsPy()
+    init_params.common = common
+    init_params.kvm = kvm
+    return init_params
diff --git a/python/src/lib.rs b/python/src/lib.rs
index 386b73d9..9236ba15 100644
--- a/python/src/lib.rs
+++ b/python/src/lib.rs
@@ -1,14 +1,17 @@
 mod errors;
+mod params;
 
 use log::{debug, info};
 
 use pyo3::exceptions::PyValueError;
 use pyo3::prelude::*;
+use pyo3::types::{PyByteArray, PyBytes};
 
 use errors::PyMicrovmiError;
 use microvmi::api as rapi; // rust api
+use microvmi::api::params as rparams; // rust params
 use microvmi::init;
-use pyo3::types::{PyByteArray, PyBytes};
+use params::{CommonInitParamsPy, DriverInitParamsPy, KVMInitParamsPy};
 
 /// microvmi Python module declaration
 #[pymodule]
@@ -18,7 +21,9 @@ fn pymicrovmi(_py: Python, m: &PyModule) -> PyResult<()> {
 
     m.add_class::<MicrovmiExt>()?;
     m.add_class::<DriverType>()?;
-    m.add_class::<DriverInitParam>()?;
+    m.add_class::<DriverInitParamsPy>()?;
+    m.add_class::<CommonInitParamsPy>()?;
+    m.add_class::<KVMInitParamsPy>()?;
 
     Ok(())
 }
@@ -37,37 +42,6 @@ impl DriverType {
     const XEN: u32 = 2;
 }
 
-// this will not be exported to Python
-#[derive(Debug, Copy, Clone)]
-enum DriverInitParamType {
-    KVMiUnixSocket = 0,
-}
-
-// exposing DriverInitParam to Python is bit more complicated
-// as it's an enum with associated data
-// the proposed implementation is exposing static method returning an
-// initialized instance of DriverInitParam struct
-/// Manages additional driver initialization parameters
-#[pyclass]
-#[derive(Debug, Clone)]
-struct DriverInitParam {
-    pub param_type: DriverInitParamType,
-    #[pyo3(get)]
-    pub param_data_string: String,
-}
-
-#[pymethods]
-impl DriverInitParam {
-    /// initialize a DriverInitParam for the KVM driver, with a Unix socket
-    #[staticmethod]
-    fn kvmi_unix_socket(socket: &str) -> Self {
-        DriverInitParam {
-            param_type: DriverInitParamType::KVMiUnixSocket,
-            param_data_string: socket.to_string(),
-        }
-    }
-}
-
 /// Main class to interact with libmicrovmi
 // A class marked as unsendable will panic when accessed by another thread.
 // TODO: make Introspectable trait inherit Send trait, and make the drivers implementation
@@ -81,27 +55,22 @@ struct MicrovmiExt {
 impl MicrovmiExt {
     // enums are not available in PyO3 (yet)
     // TODO: docstring is not exposed in Python (bug ?)
-    /// initializes libmicrovmi from the specified domain name
+    /// initializes libmicrovmi
     /// if driver_type is None, every driver compiled in libmicrovmi will be tested,
     /// and the first one that succeeds will be returned, or an error
     ///
     /// Args:
-    ///     domain_name (str): the domain name
     ///     driver_type (int, optional): the hypervisor driver type on which the library should be initialized.
-    ///     init_param (DriverInitParam, optional): additional initialization parameters for driver initialization
+    ///     init_param (DriverInitParamPy, optional): initialization parameters for driver initialization
     #[new]
-    #[args(domain_name, driver_type = "None", init_param = "None")]
-    fn new(
-        domain_name: &str,
-        driver_type: Option<u32>,
-        init_param: Option<DriverInitParam>,
-    ) -> PyResult<Self> {
+    #[args(driver_type = "None", init_params = "None")]
+    fn new(driver_type: Option<u32>, init_params: Option<DriverInitParamsPy>) -> PyResult<Self> {
         info!("Microvmi Python init");
-        // convert Python DriverType to rust API DriverType
         debug!(
             "Microvmi Python init driver_type: {:?}, init_param: {:?}",
-            driver_type, init_param
+            driver_type, init_params
         );
+        // convert Python DriverType to rust API DriverType
         let rust_driver_type = driver_type
             .map(|drv_type| match drv_type {
                 DriverType::KVM => Ok(rapi::DriverType::KVM),
@@ -113,15 +82,17 @@ impl MicrovmiExt {
                 ))),
             })
             .transpose()?;
-        // convert Python DriverInitParam to rust API DriverinitParam
-        let rust_init_param: Option<rapi::DriverInitParam> =
-            init_param.map(|param| match param.param_type {
-                DriverInitParamType::KVMiUnixSocket => {
-                    rapi::DriverInitParam::KVMiSocket(param.param_data_string)
-                }
-            });
-        let driver =
-            init(domain_name, rust_driver_type, rust_init_param).map_err(PyMicrovmiError::from)?;
+        let rust_init_params = init_params.map(|v| rparams::DriverInitParams {
+            common: v
+                .common
+                .map(|k| rparams::CommonInitParams { vm_name: k.vm_name }),
+            kvm: v.kvm.map(|k| rparams::KVMInitParams::UnixSocket {
+                path: k.unix_socket,
+            }),
+            ..Default::default()
+        });
+
+        let driver = init(rust_driver_type, rust_init_params).map_err(PyMicrovmiError::from)?;
         Ok(MicrovmiExt { driver })
     }
 
diff --git a/python/src/params.rs b/python/src/params.rs
new file mode 100644
index 00000000..933c7e63
--- /dev/null
+++ b/python/src/params.rs
@@ -0,0 +1,69 @@
+/// This modules defines the driver initialization parameters to be exposed from Python
+use pyo3::prelude::*;
+
+/// equivalent of `CommonInitParams` for Python
+#[pyclass]
+#[derive(Default, Debug, Clone)]
+pub struct CommonInitParamsPy {
+    #[pyo3(get, set)]
+    pub vm_name: String,
+}
+
+#[pymethods]
+impl CommonInitParamsPy {
+    #[new]
+    fn new() -> Self {
+        Self::default()
+    }
+}
+
+/// equivalent of `KVMInitParams` for Python
+#[pyclass]
+#[derive(Default, Debug, Clone)]
+pub struct KVMInitParamsPy {
+    #[pyo3(get, set)]
+    pub unix_socket: String,
+}
+
+#[pymethods]
+impl KVMInitParamsPy {
+    #[new]
+    fn new() -> Self {
+        Self::default()
+    }
+}
+
+/// equivalent of `DriverInitParams` for Python
+///
+/// # Examples
+///
+/// Usage from Python
+/// ```Python
+/// from microvmi import DriverInitParamsPy, CommonInitParamsPy, KVMInitParamsPy
+/// # setup common params
+/// common = CommonInitParamsPy()
+/// common.vm_name = "windows10"
+/// # setup kvm params
+/// kvm = KVMInitParamsPy()
+/// kvm.unix_socket = "/tmp/introspector"
+/// # finalize
+/// init_params = DriverInitParamsPy()
+/// init_params.common = common
+/// init_params.kvm = kvm
+/// ```
+#[pyclass]
+#[derive(Default, Debug, Clone)]
+pub struct DriverInitParamsPy {
+    #[pyo3(get, set)]
+    pub common: Option<CommonInitParamsPy>,
+    #[pyo3(get, set)]
+    pub kvm: Option<KVMInitParamsPy>,
+}
+
+#[pymethods]
+impl DriverInitParamsPy {
+    #[new]
+    fn new() -> Self {
+        Self::default()
+    }
+}
diff --git a/src/api.rs b/src/api.rs
deleted file mode 100644
index 64eacc44..00000000
--- a/src/api.rs
+++ /dev/null
@@ -1,387 +0,0 @@
-use std::convert::TryInto;
-use std::error::Error;
-use std::ffi::{CStr, IntoStringError};
-
-use enum_iterator::IntoEnumIterator;
-
-use crate::capi::DriverInitParamFFI;
-
-bitflags! {
-    pub struct Access: u32 {
-        const R=0b00000001;
-        const W=0b00000010;
-        const X=0b00000100;
-        const NIL=0b00000000;
-        const RW=Self::R.bits | Self::W.bits;
-        const WX=Self::W.bits | Self::X.bits;
-        const RX=Self::R.bits | Self::X.bits;
-        const RWX=Self::R.bits | Self::W.bits | Self::X.bits;
-    }
-}
-
-///Represents the available hypervisor VMI drivers supported by libmicrovmi
-#[repr(C)]
-#[derive(Debug, Copy, Clone, PartialEq, IntoEnumIterator)]
-pub enum DriverType {
-    KVM,
-    VirtualBox,
-    Xen,
-}
-
-/// Supports passing initialization parameters to the driver
-///
-/// Some drivers can support optional extra initialization parameters.
-///
-/// This is required to initialize the KVM driver, which needs a `domain_name` and
-/// a `kvm_socket` parameters.
-///
-/// This is equivalent to LibVMI's `vmi_init_data_type_t`
-#[repr(C)]
-#[derive(Debug, Clone)]
-pub enum DriverInitParam {
-    KVMiSocket(String),
-}
-
-impl TryInto<DriverInitParam> for DriverInitParamFFI {
-    type Error = IntoStringError;
-
-    fn try_into(self) -> Result<DriverInitParam, Self::Error> {
-        Ok(match self {
-            DriverInitParamFFI::KVMiSocket(cstr_socket) => DriverInitParam::KVMiSocket(
-                unsafe { CStr::from_ptr(cstr_socket) }
-                    .to_owned()
-                    .into_string()?,
-            ),
-        })
-    }
-}
-
-///an x86 segment register
-#[repr(C)]
-#[derive(Debug, Default)]
-pub struct SegmentReg {
-    ///Stores the base address of a code segment
-    pub base: u64,
-    ///Used as a threshold for offset. If offset added to base is more than limit, GP fault is generated. Otherwise a linear physical address is created.
-    pub limit: u32,
-    ///Represents 16 bit segment selector consisting of a 2-bit Requested Privilege Level (RPL), a 1-bit Table Indicator (TI), and a 13-bit index.
-    pub selector: u16,
-}
-
-/// x86 System Table Registers
-/// (GDTR, IDTR)
-#[repr(C)]
-#[derive(Debug, Default)]
-pub struct SystemTableReg {
-    /// 32/64 bits linear base address
-    pub base: u64,
-    /// 16 bits table limit
-    pub limit: u16,
-}
-
-///Represents all x86 registers on a specific VCPU
-#[repr(C)]
-#[derive(Debug, Default)]
-pub struct X86Registers {
-    /// 8 byte general purpose register.
-    pub rax: u64,
-    /// 8 byte general purpose register.
-    pub rbx: u64,
-    /// 8 byte general purpose register.
-    pub rcx: u64,
-    /// 8 byte general purpose register.
-    pub rdx: u64,
-    /// 8 byte general purpose register.
-    pub rsi: u64,
-    /// 8 byte general purpose register.
-    pub rdi: u64,
-    /// 8 byte general purpose register.
-    pub rsp: u64,
-    /// 8 byte general purpose register.
-    pub rbp: u64,
-    /// 8 byte general purpose register.
-    pub r8: u64,
-    /// 8 byte general purpose register.
-    pub r9: u64,
-    /// 8 byte general purpose register.
-    pub r10: u64,
-    /// 8 byte general purpose register.
-    pub r11: u64,
-    /// 8 byte general purpose register.
-    pub r12: u64,
-    /// 8 byte general purpose register.
-    pub r13: u64,
-    /// 8 byte general purpose register.
-    pub r14: u64,
-    /// 8 byte general purpose register.
-    pub r15: u64,
-    /// 8 byte general purpose register.
-    pub rip: u64,
-    /// 8 byte general purpose register.
-    pub rflags: u64,
-    ///Has various control flags that modify the basic operation of the processor.
-    pub cr0: u64,
-    ///Contains a value called Page Fault Linear Address (PFLA). When a page fault occurs, the address the program attempted to access is stored in the CR2 register. CR2 register cannot be intercepted by the guest operating system.
-    pub cr2: u64,
-    ///CR3 enables the processor to translate linear addresses into physical addresses by locating the page directory and page tables for the current task. Typically, the upper 20 bits of CR3 become the page directory base register (PDBR), which stores the physical address of the first page directory entry.
-    pub cr3: u64,
-    ///Used in protected mode to control operations such as virtual-8086 support, enabling I/O breakpoints, page size extension and machine-check exceptions.
-    pub cr4: u64,
-    ///Contains the 32-bit segment selector for the privilege level 0 code segment. Its index value is 0x174.
-    pub sysenter_cs: u64,
-    ///Contains the 32-bit offset into the privilege level 0 code segment to the first instruction of the selected operating procedure or routine. Its index value is 0x175.
-    pub sysenter_esp: u64,
-    ///Contains the 32-bit stack pointer for the privilege level 0 stack. Its index value is 0x176.
-    pub sysenter_eip: u64,
-    ///Extended Feature Enable Register (EFER) allows enabling the SYSCALL/SYSRET instruction, and later for entering and exiting long mode. Its index value is 0xc0000080.
-    pub msr_efer: u64,
-    ///Used to set the handler for SYSCALL and /or SYSRET instructions used for system calls. Its index value is 0xc0000081.
-    pub msr_star: u64,
-    ///Used to set the handler for SYSCALL and /or SYSRET instructions used for system calls. Its index value is 0xc0000082.
-    pub msr_lstar: u64,
-    ///Extened Feature Enable Register
-    pub efer: u64,
-    ///Advanced Programmable Interrupt Control Register
-    pub apic_base: u64,
-    ///Code segment register
-    pub cs: SegmentReg,
-    ///Data segment register
-    pub ds: SegmentReg,
-    ///Extra segment register, customizable by the programmer.
-    pub es: SegmentReg,
-    ///Points to TIB(Thread Information block) of a process.
-    pub fs: SegmentReg,
-    ///Points to TLS(Thread Local Storage) of a process.
-    pub gs: SegmentReg,
-    ///Stack Segment Register
-    pub ss: SegmentReg,
-    /// Task Register
-    pub tr: SegmentReg,
-    /// Local descriptor table register
-    pub ldt: SegmentReg,
-    pub idt: SystemTableReg,
-    pub gdt: SystemTableReg,
-}
-
-#[repr(C)]
-#[derive(Debug)]
-pub enum Registers {
-    X86(X86Registers),
-}
-
-pub const PAGE_SHIFT: u32 = 12;
-pub const PAGE_SIZE: u32 = 4096;
-
-pub trait Introspectable {
-    /// Retrieve the number of VCPUs.
-    ///
-    fn get_vcpu_count(&self) -> Result<u16, Box<dyn Error>> {
-        unimplemented!();
-    }
-
-    /// read the physical memory, starting from paddr, into buf
-    ///
-    /// # Arguments
-    ///
-    /// * 'paddr' - the physical address to read from
-    /// * 'buf' - the data read from memory
-    /// * 'bytes_read' - the number of bytes read
-    ///
-    fn read_physical(
-        &self,
-        _paddr: u64,
-        _buf: &mut [u8],
-        _bytes_read: &mut u64,
-    ) -> Result<(), Box<dyn Error>> {
-        unimplemented!();
-    }
-
-    /// Modify contents of physical memory, starting at paddr, from buf
-    ///
-    /// # Arguments
-    ///
-    /// * 'paddr' - the physical address to write into
-    /// * 'buf' - the data to be written into memory
-    ///
-    fn write_physical(&self, _paddr: u64, _buf: &[u8]) -> Result<(), Box<dyn Error>> {
-        unimplemented!();
-    }
-
-    /// Get the maximum physical address
-    ///
-    /// Returns maximum physical address in 64 bit unsigned integer format.
-    ///
-    fn get_max_physical_addr(&self) -> Result<u64, Box<dyn Error>> {
-        unimplemented!();
-    }
-
-    /// Read register values
-    ///
-    /// # Arguments
-    /// * 'vcpu' - vcpu id for which the value of registers are to be dumped as the argument
-    ///
-    fn read_registers(&self, _vcpu: u16) -> Result<Registers, Box<dyn Error>> {
-        unimplemented!();
-    }
-
-    ///get page access
-    ///
-    /// # Arguments
-    /// * 'paddr' - physical address of the page whose access we want to know.
-    ///
-    fn get_page_access(&self, _paddr: u64) -> Result<Access, Box<dyn Error>> {
-        unimplemented!();
-    }
-
-    ///set page access
-    ///
-    /// # Arguments
-    /// * 'paddr' - physical address of the page whose access we want to set
-    /// * 'access' - access flags to be set on the given page
-    ///
-    fn set_page_access(&self, _paddr: u64, _access: Access) -> Result<(), Box<dyn Error>> {
-        unimplemented!();
-    }
-
-    /// Write register values
-    ///
-    /// # Arguments
-    /// * 'vcpu' - vcpu id for which the value of registers are to be set
-    /// * 'reg' - Registers enum having values to be set
-    ///
-    fn write_registers(&self, _vcpu: u16, _reg: Registers) -> Result<(), Box<dyn Error>> {
-        unimplemented!();
-    }
-
-    /// Used to pause the VM
-    ///
-    fn pause(&mut self) -> Result<(), Box<dyn Error>> {
-        unimplemented!();
-    }
-
-    /// Used to resume the VM
-    ///
-    fn resume(&mut self) -> Result<(), Box<dyn Error>> {
-        unimplemented!();
-    }
-
-    /// Used to enable/disable an event interception
-    ///
-    /// # Arguments
-    /// * 'vcpu' - vcpu id for which we are to enable/disable intercept monitoring
-    /// * 'intercept_type' - to specify event type for which to raise flag
-    /// * 'enabled' - flag to specify whether to enable/disable event monitoring
-    ///
-    fn toggle_intercept(
-        &mut self,
-        _vcpu: u16,
-        _intercept_type: InterceptType,
-        _enabled: bool,
-    ) -> Result<(), Box<dyn Error>> {
-        unimplemented!();
-    }
-
-    /// Listen and return the next event, or None
-    ///
-    /// # Arguments
-    /// * 'timeout' - Time for which it will wait for a new event
-    ///
-    fn listen(&mut self, _timeout: u32) -> Result<Option<Event>, Box<dyn Error>> {
-        unimplemented!();
-    }
-
-    /// Send reply corresponding to the current event being popped
-    ///
-    /// # Arguments
-    /// * 'event'
-    /// * 'reply_type'
-    ///
-    fn reply_event(
-        &mut self,
-        _event: Event,
-        _reply_type: EventReplyType,
-    ) -> Result<(), Box<dyn Error>> {
-        unimplemented!();
-    }
-
-    /// Return the concrete DriverType
-    fn get_driver_type(&self) -> DriverType;
-}
-
-/// Various types of intercepts handled by libmicrovmi
-#[repr(C)]
-#[derive(Debug, Copy, Clone)]
-pub enum InterceptType {
-    /// Intercept when value of cr register is changed by the guest
-    Cr(CrType),
-    /// Intercept when value of msr register is changed by the guest
-    Msr(u32),
-    /// 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,
-}
-
-/// Various types of events along with their relevant attributes being handled by this driver
-#[repr(C)]
-#[derive(Debug)]
-pub enum EventType {
-    ///Cr register interception
-    Cr {
-        ///Type of control register
-        cr_type: CrType,
-        /// new value after cr register has been intercepted by the guest.
-        new: u64,
-        /// old value before cr register has been intercepted by the guest.
-        old: u64,
-    },
-    ///Msr register interception
-    Msr {
-        ///Type of model specific register
-        msr_type: u32,
-        /// new value after msr register has been intercepted by the guest.
-        value: u64,
-    },
-    ///int3 interception
-    Breakpoint {
-        /// Physical memory address of the guest
-        gpa: u64,
-        /// instruction length. Generally it should be one. Anything other than one implies malicious guest.
-        insn_len: u8,
-    },
-    Pagefault {
-        /// Virtual memory address of the guest
-        gva: u64,
-        /// Physical memory address of the guest
-        gpa: u64,
-        /// Acsess responsible for thr pagefault
-        access: Access,
-    },
-}
-
-///Types of x86 control registers are listed here
-#[repr(C)]
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub enum CrType {
-    ///Has various control flags that modify the basic operation of the processor.
-    Cr0,
-    ///Contains a value called Page Fault Linear Address (PFLA). When a page fault occurs, the address the program attempted to access is stored in the CR2 register. CR2 register cannot be intercepted by the guest operating system.
-    Cr3,
-    ///Used in protected mode to control operations such as virtual-8086 support, enabling I/O breakpoints, page size extension and machine-check exceptions.
-    Cr4,
-}
-
-///This provides an abstraction of event which the hypervisor reports and using which we introspect the guest
-#[repr(C)]
-pub struct Event {
-    ///vcpu on which the event is detected
-    pub vcpu: u16,
-    /// Type of event detected
-    pub kind: EventType,
-}
-
-///Reply provided to the hypervisor after detecting an event
-#[repr(C)]
-#[derive(Debug)]
-pub enum EventReplyType {
-    Continue,
-}
diff --git a/src/api/events.rs b/src/api/events.rs
new file mode 100644
index 00000000..2e0770c9
--- /dev/null
+++ b/src/api/events.rs
@@ -0,0 +1,79 @@
+use crate::api::Access;
+
+/// Various types of intercepts handled by libmicrovmi
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub enum InterceptType {
+    /// Intercept when value of cr register is changed by the guest
+    Cr(CrType),
+    /// Intercept when value of msr register is changed by the guest
+    Msr(u32),
+    /// 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,
+}
+
+/// Various types of events along with their relevant attributes being handled by this driver
+#[repr(C)]
+#[derive(Debug)]
+pub enum EventType {
+    ///Cr register interception
+    Cr {
+        ///Type of control register
+        cr_type: CrType,
+        /// new value after cr register has been intercepted by the guest.
+        new: u64,
+        /// old value before cr register has been intercepted by the guest.
+        old: u64,
+    },
+    ///Msr register interception
+    Msr {
+        ///Type of model specific register
+        msr_type: u32,
+        /// new value after msr register has been intercepted by the guest.
+        value: u64,
+    },
+    ///int3 interception
+    Breakpoint {
+        /// Physical memory address of the guest
+        gpa: u64,
+        /// instruction length. Generally it should be one. Anything other than one implies malicious guest.
+        insn_len: u8,
+    },
+    Pagefault {
+        /// Virtual memory address of the guest
+        gva: u64,
+        /// Physical memory address of the guest
+        gpa: u64,
+        /// Acsess responsible for thr pagefault
+        access: Access,
+    },
+}
+
+///Types of x86 control registers are listed here
+#[repr(C)]
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub enum CrType {
+    ///Has various control flags that modify the basic operation of the processor.
+    Cr0,
+    ///Contains a value called Page Fault Linear Address (PFLA). When a page fault occurs, the address the program attempted to access is stored in the CR2 register. CR2 register cannot be intercepted by the guest operating system.
+    Cr3,
+    ///Used in protected mode to control operations such as virtual-8086 support, enabling I/O breakpoints, page size extension and machine-check exceptions.
+    Cr4,
+}
+
+///This provides an abstraction of event which the hypervisor reports and using which we introspect the guest
+#[repr(C)]
+pub struct Event {
+    ///vcpu on which the event is detected
+    pub vcpu: u16,
+    /// Type of event detected
+    pub kind: EventType,
+}
+
+///Reply provided to the hypervisor after detecting an event
+#[repr(C)]
+#[derive(Debug)]
+pub enum EventReplyType {
+    Continue,
+}
diff --git a/src/api/mod.rs b/src/api/mod.rs
new file mode 100644
index 00000000..08f3214a
--- /dev/null
+++ b/src/api/mod.rs
@@ -0,0 +1,184 @@
+use enum_iterator::IntoEnumIterator;
+use std::error::Error;
+
+use events::{Event, EventReplyType, InterceptType};
+use registers::Registers;
+
+pub mod events;
+pub mod params;
+pub mod registers;
+
+bitflags! {
+    pub struct Access: u32 {
+        const R=0b00000001;
+        const W=0b00000010;
+        const X=0b00000100;
+        const NIL=0b00000000;
+        const RW=Self::R.bits | Self::W.bits;
+        const WX=Self::W.bits | Self::X.bits;
+        const RX=Self::R.bits | Self::X.bits;
+        const RWX=Self::R.bits | Self::W.bits | Self::X.bits;
+    }
+}
+
+///Represents the available hypervisor VMI drivers supported by libmicrovmi
+#[repr(C)]
+#[derive(Debug, Copy, Clone, PartialEq, IntoEnumIterator)]
+pub enum DriverType {
+    KVM,
+    VirtualBox,
+    Xen,
+}
+
+// impl TryInto<DriverInitParam> for DriverInitParamFFI {
+//     type Error = IntoStringError;
+//
+//     fn try_into(self) -> Result<DriverInitParam, Self::Error> {
+//         Ok(match self {
+//             DriverInitParamFFI::KVMiSocket(cstr_socket) => DriverInitParam::KVMiSocket(
+//                 unsafe { CStr::from_ptr(cstr_socket) }
+//                     .to_owned()
+//                     .into_string()?,
+//             ),
+//         })
+//     }
+// }
+
+pub const PAGE_SHIFT: u32 = 12;
+pub const PAGE_SIZE: u32 = 4096;
+
+pub trait Introspectable {
+    /// Retrieve the number of VCPUs.
+    ///
+    fn get_vcpu_count(&self) -> Result<u16, Box<dyn Error>> {
+        unimplemented!();
+    }
+
+    /// read the physical memory, starting from paddr, into buf
+    ///
+    /// # Arguments
+    ///
+    /// * 'paddr' - the physical address to read from
+    /// * 'buf' - the data read from memory
+    /// * 'bytes_read' - the number of bytes read
+    ///
+    fn read_physical(
+        &self,
+        _paddr: u64,
+        _buf: &mut [u8],
+        _bytes_read: &mut u64,
+    ) -> Result<(), Box<dyn Error>> {
+        unimplemented!();
+    }
+
+    /// Modify contents of physical memory, starting at paddr, from buf
+    ///
+    /// # Arguments
+    ///
+    /// * 'paddr' - the physical address to write into
+    /// * 'buf' - the data to be written into memory
+    ///
+    fn write_physical(&self, _paddr: u64, _buf: &[u8]) -> Result<(), Box<dyn Error>> {
+        unimplemented!();
+    }
+
+    /// Get the maximum physical address
+    ///
+    /// Returns maximum physical address in 64 bit unsigned integer format.
+    ///
+    fn get_max_physical_addr(&self) -> Result<u64, Box<dyn Error>> {
+        unimplemented!();
+    }
+
+    /// Read register values
+    ///
+    /// # Arguments
+    /// * 'vcpu' - vcpu id for which the value of registers are to be dumped as the argument
+    ///
+    fn read_registers(&self, _vcpu: u16) -> Result<Registers, Box<dyn Error>> {
+        unimplemented!();
+    }
+
+    ///get page access
+    ///
+    /// # Arguments
+    /// * 'paddr' - physical address of the page whose access we want to know.
+    ///
+    fn get_page_access(&self, _paddr: u64) -> Result<Access, Box<dyn Error>> {
+        unimplemented!();
+    }
+
+    ///set page access
+    ///
+    /// # Arguments
+    /// * 'paddr' - physical address of the page whose access we want to set
+    /// * 'access' - access flags to be set on the given page
+    ///
+    fn set_page_access(&self, _paddr: u64, _access: Access) -> Result<(), Box<dyn Error>> {
+        unimplemented!();
+    }
+
+    /// Write register values
+    ///
+    /// # Arguments
+    /// * 'vcpu' - vcpu id for which the value of registers are to be set
+    /// * 'reg' - Registers enum having values to be set
+    ///
+    fn write_registers(&self, _vcpu: u16, _reg: Registers) -> Result<(), Box<dyn Error>> {
+        unimplemented!();
+    }
+
+    /// Used to pause the VM
+    ///
+    fn pause(&mut self) -> Result<(), Box<dyn Error>> {
+        unimplemented!();
+    }
+
+    /// Used to resume the VM
+    ///
+    fn resume(&mut self) -> Result<(), Box<dyn Error>> {
+        unimplemented!();
+    }
+
+    /// Used to enable/disable an event interception
+    ///
+    /// # Arguments
+    /// * 'vcpu' - vcpu id for which we are to enable/disable intercept monitoring
+    /// * 'intercept_type' - to specify event type for which to raise flag
+    /// * 'enabled' - flag to specify whether to enable/disable event monitoring
+    ///
+    fn toggle_intercept(
+        &mut self,
+        _vcpu: u16,
+        _intercept_type: InterceptType,
+        _enabled: bool,
+    ) -> Result<(), Box<dyn Error>> {
+        unimplemented!();
+    }
+
+    /// Listen and return the next event, or None
+    ///
+    /// # Arguments
+    /// * 'timeout' - Time for which it will wait for a new event
+    ///
+    fn listen(&mut self, _timeout: u32) -> Result<Option<Event>, Box<dyn Error>> {
+        unimplemented!();
+    }
+
+    /// Send reply corresponding to the current event being popped
+    ///
+    /// # Arguments
+    /// * 'event'
+    /// * 'reply_type'
+    ///
+    fn reply_event(
+        &mut self,
+        _event: Event,
+        _reply_type: EventReplyType,
+    ) -> Result<(), Box<dyn Error>> {
+        unimplemented!();
+    }
+
+    /// Return the concrete DriverType
+    fn get_driver_type(&self) -> DriverType;
+}
diff --git a/src/api/params.rs b/src/api/params.rs
new file mode 100644
index 00000000..e5ca0726
--- /dev/null
+++ b/src/api/params.rs
@@ -0,0 +1,34 @@
+/// This module describes initialization parameters for all libmicrovmi drivers
+
+/// Xen initialization parameters
+#[derive(Debug, Clone, PartialEq)]
+pub enum XenInitParams {}
+
+/// KVM initialization parameters
+#[derive(Debug, Clone, PartialEq)]
+pub enum KVMInitParams {
+    UnixSocket { path: String },
+}
+
+/// VirtualBox initialization parameters
+#[derive(Debug, Clone, PartialEq)]
+pub enum VBoxInitParams {}
+
+/// Common initialization parameters
+///
+/// These parameters are shared by two or more drivers, and are stored in this struct
+/// to avoid duplication and simplify the API
+#[repr(C)]
+#[derive(Default, Debug, Clone, PartialEq)]
+pub struct CommonInitParams {
+    pub vm_name: String,
+}
+
+/// This struct is used to specify the initialization parameters for all drivers
+#[derive(Default, Debug, Clone, PartialEq)]
+pub struct DriverInitParams {
+    pub common: Option<CommonInitParams>,
+    pub xen: Option<XenInitParams>,
+    pub kvm: Option<KVMInitParams>,
+    pub virtualbox: Option<VBoxInitParams>,
+}
diff --git a/src/api/registers.rs b/src/api/registers.rs
new file mode 100644
index 00000000..fdcc6cb0
--- /dev/null
+++ b/src/api/registers.rs
@@ -0,0 +1,112 @@
+///an x86 segment register
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct SegmentReg {
+    ///Stores the base address of a code segment
+    pub base: u64,
+    ///Used as a threshold for offset. If offset added to base is more than limit, GP fault is generated. Otherwise a linear physical address is created.
+    pub limit: u32,
+    ///Represents 16 bit segment selector consisting of a 2-bit Requested Privilege Level (RPL), a 1-bit Table Indicator (TI), and a 13-bit index.
+    pub selector: u16,
+}
+
+/// x86 System Table Registers
+/// (GDTR, IDTR)
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct SystemTableReg {
+    /// 32/64 bits linear base address
+    pub base: u64,
+    /// 16 bits table limit
+    pub limit: u16,
+}
+
+///Represents all x86 registers on a specific VCPU
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct X86Registers {
+    /// 8 byte general purpose register.
+    pub rax: u64,
+    /// 8 byte general purpose register.
+    pub rbx: u64,
+    /// 8 byte general purpose register.
+    pub rcx: u64,
+    /// 8 byte general purpose register.
+    pub rdx: u64,
+    /// 8 byte general purpose register.
+    pub rsi: u64,
+    /// 8 byte general purpose register.
+    pub rdi: u64,
+    /// 8 byte general purpose register.
+    pub rsp: u64,
+    /// 8 byte general purpose register.
+    pub rbp: u64,
+    /// 8 byte general purpose register.
+    pub r8: u64,
+    /// 8 byte general purpose register.
+    pub r9: u64,
+    /// 8 byte general purpose register.
+    pub r10: u64,
+    /// 8 byte general purpose register.
+    pub r11: u64,
+    /// 8 byte general purpose register.
+    pub r12: u64,
+    /// 8 byte general purpose register.
+    pub r13: u64,
+    /// 8 byte general purpose register.
+    pub r14: u64,
+    /// 8 byte general purpose register.
+    pub r15: u64,
+    /// 8 byte general purpose register.
+    pub rip: u64,
+    /// 8 byte general purpose register.
+    pub rflags: u64,
+    ///Has various control flags that modify the basic operation of the processor.
+    pub cr0: u64,
+    ///Contains a value called Page Fault Linear Address (PFLA). When a page fault occurs, the address the program attempted to access is stored in the CR2 register. CR2 register cannot be intercepted by the guest operating system.
+    pub cr2: u64,
+    ///CR3 enables the processor to translate linear addresses into physical addresses by locating the page directory and page tables for the current task. Typically, the upper 20 bits of CR3 become the page directory base register (PDBR), which stores the physical address of the first page directory entry.
+    pub cr3: u64,
+    ///Used in protected mode to control operations such as virtual-8086 support, enabling I/O breakpoints, page size extension and machine-check exceptions.
+    pub cr4: u64,
+    ///Contains the 32-bit segment selector for the privilege level 0 code segment. Its index value is 0x174.
+    pub sysenter_cs: u64,
+    ///Contains the 32-bit offset into the privilege level 0 code segment to the first instruction of the selected operating procedure or routine. Its index value is 0x175.
+    pub sysenter_esp: u64,
+    ///Contains the 32-bit stack pointer for the privilege level 0 stack. Its index value is 0x176.
+    pub sysenter_eip: u64,
+    ///Extended Feature Enable Register (EFER) allows enabling the SYSCALL/SYSRET instruction, and later for entering and exiting long mode. Its index value is 0xc0000080.
+    pub msr_efer: u64,
+    ///Used to set the handler for SYSCALL and /or SYSRET instructions used for system calls. Its index value is 0xc0000081.
+    pub msr_star: u64,
+    ///Used to set the handler for SYSCALL and /or SYSRET instructions used for system calls. Its index value is 0xc0000082.
+    pub msr_lstar: u64,
+    ///Extened Feature Enable Register
+    pub efer: u64,
+    ///Advanced Programmable Interrupt Control Register
+    pub apic_base: u64,
+    ///Code segment register
+    pub cs: SegmentReg,
+    ///Data segment register
+    pub ds: SegmentReg,
+    ///Extra segment register, customizable by the programmer.
+    pub es: SegmentReg,
+    ///Points to TIB(Thread Information block) of a process.
+    pub fs: SegmentReg,
+    ///Points to TLS(Thread Local Storage) of a process.
+    pub gs: SegmentReg,
+    ///Stack Segment Register
+    pub ss: SegmentReg,
+    /// Task Register
+    pub tr: SegmentReg,
+    /// Local descriptor table register
+    pub ldt: SegmentReg,
+    pub idt: SystemTableReg,
+    pub gdt: SystemTableReg,
+}
+
+#[repr(C)]
+#[derive(Debug)]
+pub enum Registers {
+    X86(X86Registers),
+}
diff --git a/src/capi.rs b/src/capi.rs
index e1869521..40af63a6 100644
--- a/src/capi.rs
+++ b/src/capi.rs
@@ -1,17 +1,70 @@
-use crate::api::{DriverInitParam, DriverType, Introspectable, Registers};
-use crate::init;
+use std::ffi::{c_void, CStr, CString, IntoStringError};
+use std::slice;
+
 use bitflags::_core::ptr::null_mut;
 use cty::{c_char, size_t, uint16_t, uint64_t, uint8_t};
-use std::convert::TryInto;
-use std::ffi::{c_void, CStr, CString};
-use std::slice;
 
-/// Support passing initialization options
-/// similar to DriverInitParam, however this enum offers C API compatibility
+use crate::api::params::{CommonInitParams, DriverInitParams, KVMInitParams};
+use crate::api::registers::Registers;
+use crate::api::{DriverType, Introspectable};
+use crate::init;
+use std::convert::TryFrom;
+
+/// equivalent of `CommonInitParams` with C compatibility
+#[repr(C)]
+#[derive(Debug, Clone)]
+pub struct CommonInitParamsFFI {
+    pub vm_name: *const c_char,
+}
+
+/// equivalent of `KVMInitParams` with C compatibility
 #[repr(C)]
-#[derive(Debug)]
-pub enum DriverInitParamFFI {
-    KVMiSocket(*const c_char),
+#[derive(Debug, Clone)]
+pub enum KVMInitParamsFFI {
+    UnixSocket { path: *const c_char },
+}
+
+/// equivalent of `DriverInitParam` with C API compatibility
+#[repr(C)]
+#[derive(Debug, Clone)]
+pub struct DriverInitParamsFFI {
+    common: CommonInitParamsFFI,
+    kvm: KVMInitParamsFFI,
+}
+
+// convert from FFI type to Rust type
+impl TryFrom<DriverInitParamsFFI> for DriverInitParams {
+    type Error = IntoStringError;
+
+    fn try_from(value: DriverInitParamsFFI) -> Result<Self, Self::Error> {
+        // build common params
+        let vm_name = if value.common.vm_name.is_null() {
+            None
+        } else {
+            Some(
+                unsafe { CStr::from_ptr(value.common.vm_name) }
+                    .to_owned()
+                    .into_string()?,
+            )
+        };
+        let common = vm_name.map(|v| CommonInitParams { vm_name: v });
+        // build kvm params
+        let kvm_socket = match value.kvm {
+            KVMInitParamsFFI::UnixSocket { path } => {
+                if path.is_null() {
+                    None
+                } else {
+                    Some(unsafe { CStr::from_ptr(path) }.to_owned().into_string()?)
+                }
+            }
+        };
+        let kvm = kvm_socket.map(|v| KVMInitParams::UnixSocket { path: v });
+        Ok(DriverInitParams {
+            common,
+            kvm,
+            ..Default::default()
+        })
+    }
 }
 
 /// This API allows a C program to initialize the logging system in libmicrovmi.
@@ -36,26 +89,27 @@ pub unsafe extern "C" fn microvmi_envlogger_init() {
 /// The init_error pointer should be freed with rs_cstring_free()
 #[no_mangle]
 pub unsafe extern "C" fn microvmi_init(
-    domain_name: *const c_char,
     driver_type: *const DriverType,
-    driver_init_option: *const DriverInitParamFFI,
+    init_params: *const DriverInitParamsFFI,
     init_error: *mut *const c_char,
 ) -> *mut c_void {
-    let safe_domain_name = CStr::from_ptr(domain_name).to_string_lossy().into_owned();
+    // check driver type
     let optional_driver_type: Option<DriverType> = if driver_type.is_null() {
         None
     } else {
         Some(driver_type.read())
     };
-    let init_option: Option<DriverInitParam> = if driver_init_option.is_null() {
+    // check init params
+    let optional_init_params = if init_params.is_null() {
         None
     } else {
         Some(
-            DriverInitParamFFI::try_into(driver_init_option.read())
-                .expect("Failed to convert DriverInitParam C struct to Rust equivalent"),
+            DriverInitParams::try_from(init_params.read())
+                .expect("Failed to convert DriverInitParam C struct to Rust"),
         )
     };
-    match init(&safe_domain_name, optional_driver_type, init_option) {
+
+    match init(optional_driver_type, optional_init_params) {
         Ok(driver) => Box::into_raw(Box::new(driver)) as *mut c_void,
         Err(err) => {
             if !init_error.is_null() {
diff --git a/src/driver/kvm.rs b/src/driver/kvm.rs
index 447d403d..f47044c1 100644
--- a/src/driver/kvm.rs
+++ b/src/driver/kvm.rs
@@ -1,9 +1,3 @@
-#[cfg(test)] // only needed for tests
-use kvmi::errors::KVMiError;
-use kvmi::{
-    kvm_dtable, kvm_regs, kvm_segment, KVMIntrospectable, KVMiCr, KVMiEvent, KVMiEventReply,
-    KVMiEventType, KVMiInterceptType, KVMiPageAccess, SocketType,
-};
 use std::convert::From;
 use std::convert::TryFrom;
 use std::convert::TryInto;
@@ -11,11 +5,18 @@ use std::error::Error;
 use std::mem;
 use std::vec::Vec;
 
-use crate::api::{
-    Access, CrType, DriverInitParam, DriverType, Event, EventReplyType, EventType, InterceptType,
-    Introspectable, Registers, SegmentReg, SystemTableReg, X86Registers,
-};
 use kvmi::constants::PAGE_SIZE;
+#[cfg(test)] // only needed for tests
+use kvmi::errors::KVMiError;
+use kvmi::{
+    kvm_dtable, kvm_regs, kvm_segment, KVMIntrospectable, KVMiCr, KVMiEvent, KVMiEventReply,
+    KVMiEventType, KVMiInterceptType, KVMiPageAccess, SocketType,
+};
+
+use crate::api::events::{CrType, Event, EventReplyType, EventType, InterceptType};
+use crate::api::params::{DriverInitParams, KVMInitParams};
+use crate::api::registers::{Registers, SegmentReg, SystemTableReg, X86Registers};
+use crate::api::{Access, DriverType, Introspectable};
 
 impl TryFrom<Access> for KVMiPageAccess {
     type Error = &'static str;
@@ -103,20 +104,23 @@ pub struct Kvm<T: KVMIntrospectable> {
 
 #[derive(thiserror::Error, Debug)]
 pub enum KVMDriverError {
+    #[error("KVM driver requires a VM name parameter")]
+    MissingVMName,
     #[error("KVM driver initialization requires an additional socket parameter")]
     MissingSocketParameter,
 }
 
 impl<T: KVMIntrospectable> Kvm<T> {
-    pub fn new(
-        domain_name: &str,
-        mut kvmi: T,
-        init_option: Option<DriverInitParam>,
-    ) -> Result<Self, Box<dyn Error>> {
-        let DriverInitParam::KVMiSocket(socket_path) =
-            init_option.ok_or(KVMDriverError::MissingSocketParameter)?;
-        debug!("init on {} (socket: {})", domain_name, socket_path);
-        let unix_socket = SocketType::UnixSocket(socket_path);
+    pub fn new(mut kvmi: T, init_params: DriverInitParams) -> Result<Self, Box<dyn Error>> {
+        let domain_name = init_params
+            .common
+            .ok_or(KVMDriverError::MissingVMName)?
+            .vm_name;
+        let KVMInitParams::UnixSocket { path } = init_params
+            .kvm
+            .ok_or(KVMDriverError::MissingSocketParameter)?;
+        debug!("init on {} (socket: {})", domain_name, path);
+        let unix_socket = SocketType::UnixSocket(path);
         kvmi.init(unix_socket)?;
         let mut kvm = Kvm {
             kvmi,
@@ -369,13 +373,17 @@ impl<T: KVMIntrospectable> Drop for Kvm<T> {
 
 #[cfg(test)]
 mod tests {
-    use super::*;
+    use std::fmt::{Debug, Formatter};
+
     use kvmi::{kvm_regs, kvm_sregs, KvmMsrs};
     use mockall::mock;
     use mockall::predicate::{eq, function};
-    use std::fmt::{Debug, Formatter};
     use test_case::test_case;
 
+    use crate::api::params::CommonInitParams;
+
+    use super::*;
+
     #[test]
     fn test_fail_to_create_kvm_driver_if_kvmi_init_returns_error() {
         let mut kvmi_mock = MockKVMi::default();
@@ -387,9 +395,16 @@ mod tests {
         });
 
         let result = Kvm::new(
-            "some_vm",
             kvmi_mock,
-            Some(DriverInitParam::KVMiSocket("/tmp/introspector".to_string())),
+            DriverInitParams {
+                common: Some(CommonInitParams {
+                    vm_name: String::from("some_vm"),
+                }),
+                kvm: Some(KVMInitParams::UnixSocket {
+                    path: "/tmp/introspector".to_string(),
+                }),
+                ..Default::default()
+            },
         );
 
         assert!(result.is_err(), "Expected error, got ok instead!");
@@ -462,9 +477,16 @@ mod tests {
         }
 
         let result = Kvm::new(
-            "some_vm",
             kvmi_mock,
-            Some(DriverInitParam::KVMiSocket("/tmp/introspector".to_string())),
+            DriverInitParams {
+                common: Some(CommonInitParams {
+                    vm_name: String::from("some_vm"),
+                }),
+                kvm: Some(KVMInitParams::UnixSocket {
+                    path: "/tmp/introspector".to_string(),
+                }),
+                ..Default::default()
+            },
         );
 
         assert!(result.is_ok(), "Expected ok, got error instead!");
diff --git a/src/driver/virtualbox.rs b/src/driver/virtualbox.rs
index 01e98700..6f1e9829 100644
--- a/src/driver/virtualbox.rs
+++ b/src/driver/virtualbox.rs
@@ -2,24 +2,30 @@ use std::error::Error;
 
 use fdp::{RegisterType, FDP};
 
-use crate::api::{
-    DriverInitParam, DriverType, Introspectable, Registers, SegmentReg, SystemTableReg,
-    X86Registers,
-};
+use crate::api::params::DriverInitParams;
+use crate::api::registers::{Registers, SegmentReg, SystemTableReg, X86Registers};
+use crate::api::{DriverType, Introspectable};
 
-// unit struct
 #[derive(Debug)]
 pub struct VBox {
     fdp: FDP,
 }
 
+#[derive(thiserror::Error, Debug)]
+pub enum VBoxDriverError {
+    #[error("VirtualBox driver requires a VM name parameter")]
+    MissingVMName,
+}
+
 impl VBox {
-    pub fn new(
-        domain_name: &str,
-        _init_option: Option<DriverInitParam>,
-    ) -> Result<Self, Box<dyn Error>> {
+    pub fn new(init_params: DriverInitParams) -> Result<Self, Box<dyn Error>> {
+        let domain_name = init_params
+            .common
+            .ok_or(VBoxDriverError::MissingVMName)?
+            .vm_name;
+
         // init FDP
-        let fdp = FDP::new(domain_name)?;
+        let fdp = FDP::new(&domain_name)?;
         Ok(VBox { fdp })
     }
 }
diff --git a/src/driver/xen.rs b/src/driver/xen.rs
index 4466ab4d..fc3fb0b6 100644
--- a/src/driver/xen.rs
+++ b/src/driver/xen.rs
@@ -1,10 +1,3 @@
-use crate::api::{
-    CrType, DriverInitParam, DriverType, Event, EventType, InterceptType, Introspectable,
-    Registers, SegmentReg, SystemTableReg, X86Registers,
-};
-use libc::{PROT_READ, PROT_WRITE};
-use nix::poll::PollFlags;
-use nix::poll::{poll, PollFd};
 use std::convert::Infallible;
 use std::convert::TryInto;
 use std::error::Error;
@@ -12,6 +5,10 @@ use std::io::Error as IoError;
 use std::io::ErrorKind;
 use std::mem;
 use std::num::TryFromIntError;
+
+use libc::{PROT_READ, PROT_WRITE};
+use nix::poll::PollFlags;
+use nix::poll::{poll, PollFd};
 use xenctrl::consts::{PAGE_SHIFT, PAGE_SIZE};
 use xenctrl::error::XcError;
 use xenctrl::RING_HAS_UNCONSUMED_REQUESTS;
@@ -23,6 +20,11 @@ use xenvmevent_sys::{
     vm_event_back_ring, vm_event_response_t, VM_EVENT_FLAG_VCPU_PAUSED, VM_EVENT_INTERFACE_VERSION,
 };
 
+use crate::api::events::{CrType, Event, EventType, InterceptType};
+use crate::api::params::DriverInitParams;
+use crate::api::registers::{Registers, SegmentReg, SystemTableReg, X86Registers};
+use crate::api::{DriverType, Introspectable};
+
 #[derive(Debug)]
 pub struct Xen {
     xc: XenControl,
@@ -35,6 +37,8 @@ pub struct Xen {
 
 #[derive(thiserror::Error, Debug)]
 pub enum XenDriverError {
+    #[error("Xen driver requires a VM name parameter")]
+    MissingVMName,
     #[error("failed to read xenstore entry {0}: {1}")]
     XenstoreReadError(String, IoError),
     #[error("event version mismatch: {0} <-> {1}")]
@@ -54,11 +58,11 @@ pub enum XenDriverError {
 }
 
 impl Xen {
-    pub fn new(
-        domain_name: &str,
-        _init_option: Option<DriverInitParam>,
-    ) -> Result<Self, Box<dyn Error>> {
-        debug!("init on {}", domain_name);
+    pub fn new(init_params: DriverInitParams) -> Result<Self, Box<dyn Error>> {
+        let domain_name = init_params
+            .common
+            .ok_or(XenDriverError::MissingVMName)?
+            .vm_name;
         // find domain name in xenstore
         let xs = Xs::new(XsOpenFlags::ReadOnly)?;
         let mut found: bool = false;
@@ -102,7 +106,7 @@ impl Xen {
             xc,
             xev,
             xen_fgn,
-            dom_name: domain_name.to_string(),
+            dom_name: domain_name,
             domid: cand_domid,
             back_ring,
         };
diff --git a/src/lib.rs b/src/lib.rs
index e848452d..a850dcf5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,6 +1,8 @@
 //! libmicrovmi is a cross-platform unified virtual machine introsection interface, following a simple design to keep interoperability at heart.
 //!
 //! Click on this [book 📖](https://libmicrovmi.github.io/) to find our project documentation.
+//!
+//! The library's entrypoint is the [init](fn.init.html) function.
 
 #![allow(clippy::upper_case_acronyms)]
 
@@ -16,8 +18,9 @@ extern crate bitflags;
 
 use enum_iterator::IntoEnumIterator;
 
+use api::params::DriverInitParams;
+use api::DriverType;
 use api::Introspectable;
-use api::{DriverInitParam, DriverType};
 #[cfg(feature = "kvm")]
 use driver::kvm::Kvm;
 #[cfg(feature = "virtualbox")]
@@ -28,10 +31,45 @@ use errors::MicrovmiError;
 #[cfg(feature = "kvm")]
 use kvmi::create_kvmi;
 
+/// libmicrovmi initialization entrypoint
+///
+/// This function will initialize a libmicrovmi driver and call the hypervisor VMI API.
+/// It returns a `Box<dyn Introspectable>` trait object, which implements the [Introspectable](api/trait.Introspectable.html) trait.
+///
+/// # Arguments
+/// * `driver_type`: optional driver type to initialize. If None, all compiled drivers will be initialized one by one. The first that succeeds will be returned.
+/// * `init_params`: optional driver initialization parameters
+///
+/// # Examples
+/// ```no_run
+/// use microvmi::init;
+/// // 1 - attempt to init all drivers, without any init parameters
+/// let drv = init(None, None);
+///
+/// // 2 - add parameters: vm_name
+/// // a `vm_name` parameter is required for multiple drivers: Xen, KVM, VirtualBox
+/// use microvmi::api::params::{DriverInitParams, CommonInitParams};
+/// let init_params = DriverInitParams {
+///     common: Some(CommonInitParams { vm_name: String::from("windows10")}),
+///     ..Default::default()
+/// };
+/// let drv = init(None, Some(init_params));
+///
+/// // 3 - add parameters: KVM specific params
+/// // KVM requires an additional unix socket to be specified
+/// // and specify the KVM driver only
+/// use microvmi::api::params::KVMInitParams;
+/// use microvmi::api::DriverType;
+/// let init_params = DriverInitParams {
+///     common: Some(CommonInitParams { vm_name: String::from("windows10")}),
+///     kvm: Some(KVMInitParams::UnixSocket {path: String::from("/tmp/introspector")}),
+///     ..Default::default()
+/// };
+/// let drv = init(Some(DriverType::KVM), Some(init_params));
+/// ```
 pub fn init(
-    domain_name: &str,
     driver_type: Option<DriverType>,
-    init_option: Option<DriverInitParam>,
+    init_params: Option<DriverInitParams>,
 ) -> Result<Box<dyn Introspectable>, MicrovmiError> {
     info!("Microvmi init");
     match driver_type {
@@ -39,7 +77,7 @@ pub fn init(
             // for each possible DriverType
             for drv_type in DriverType::into_enum_iter() {
                 // try to init
-                match init_driver(domain_name, drv_type, init_option.clone()) {
+                match init_driver(drv_type, init_params.clone()) {
                     Ok(driver) => {
                         return Ok(driver);
                     }
@@ -51,29 +89,27 @@ pub fn init(
             }
             Err(MicrovmiError::NoDriverAvailable)
         }
-        Some(drv_type) => init_driver(domain_name, drv_type, init_option),
+        Some(drv_type) => init_driver(drv_type, init_params),
     }
 }
 
 /// Initialize a given driver type
 /// return None if the requested driver has not been compiled in libmicrovmi
 fn init_driver(
-    _domain_name: &str,
     driver_type: DriverType,
-    _init_option: Option<DriverInitParam>,
+    init_params_option: Option<DriverInitParams>,
 ) -> Result<Box<dyn Introspectable>, MicrovmiError> {
+    let _init_params = init_params_option.unwrap_or(DriverInitParams {
+        ..Default::default()
+    });
     #[allow(clippy::match_single_binding)]
     match driver_type {
         #[cfg(feature = "kvm")]
-        DriverType::KVM => Ok(Box::new(Kvm::new(
-            _domain_name,
-            create_kvmi(),
-            _init_option,
-        )?)),
+        DriverType::KVM => Ok(Box::new(Kvm::new(create_kvmi(), _init_params)?)),
         #[cfg(feature = "virtualbox")]
-        DriverType::VirtualBox => Ok(Box::new(VBox::new(_domain_name, _init_option)?)),
+        DriverType::VirtualBox => Ok(Box::new(VBox::new(_init_params)?)),
         #[cfg(feature = "xen")]
-        DriverType::Xen => Ok(Box::new(Xen::new(_domain_name, _init_option)?)),
+        DriverType::Xen => Ok(Box::new(Xen::new(_init_params)?)),
         #[allow(unreachable_patterns)]
         _ => Err(MicrovmiError::DriverNotCompiled(driver_type)),
     }
diff --git a/utilities/Cargo.toml b/utilities/Cargo.toml
new file mode 100644
index 00000000..0da1bb90
--- /dev/null
+++ b/utilities/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "utilities"
+version = "0.1.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+microvmi = { path = ".." }
+clap = "2.33"
diff --git a/utilities/src/lib.rs b/utilities/src/lib.rs
new file mode 100644
index 00000000..75e88cf9
--- /dev/null
+++ b/utilities/src/lib.rs
@@ -0,0 +1,77 @@
+/// This crate implements utilities and common code shared by libmicrovmi examples
+use clap::{Arg, ArgMatches};
+use microvmi::api::params::{CommonInitParams, DriverInitParams, KVMInitParams};
+
+/// This trait allows to convert a struct to Clap's command line arguments
+/// and to parse back the matches into the struct
+pub trait Clappable {
+    /// produces an equivalent of the struct as vector of Clap arguments
+    fn to_clap_args<'a, 'b>() -> Vec<Arg<'a, 'b>>;
+    /// builds a new struct from Clap matches
+    fn from_matches(matches: &ArgMatches) -> Self;
+}
+
+impl Clappable for DriverInitParams {
+    fn to_clap_args<'a, 'b>() -> Vec<Arg<'a, 'b>> {
+        vec![
+            // common
+            Arg::with_name("vm_name")
+                .long("vm_name")
+                .takes_value(true)
+                .help("Driver parameter (required for Xen, KVM, VirtualBox): VM name"),
+            // kvm
+            Arg::with_name("kvm_unix_socket")
+                .long("kvm_unix_socket")
+                .takes_value(true)
+                .help("Driver parameter (required for KVM): KVM unix socket path"),
+        ]
+    }
+
+    fn from_matches(matches: &ArgMatches) -> Self {
+        let common = matches.value_of("vm_name").map(|s| CommonInitParams {
+            vm_name: String::from(s),
+        });
+        let kvm = matches
+            .value_of("kvm_unix_socket")
+            .map(|s| KVMInitParams::UnixSocket {
+                path: String::from(s),
+            });
+        DriverInitParams {
+            common,
+            kvm,
+            ..Default::default()
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::Clappable;
+    use clap::App;
+    use microvmi::api::params::{DriverInitParams, KVMInitParams};
+
+    #[test]
+    fn test_common_vm_name() {
+        let cmdline = vec!["test", "--vm_name=windows10"];
+        let matches = App::new("test")
+            .args(DriverInitParams::to_clap_args().as_ref())
+            .get_matches_from(cmdline);
+        let params = DriverInitParams::from_matches(&matches);
+        assert_eq!("windows10", params.common.unwrap().vm_name)
+    }
+
+    #[test]
+    fn test_kvm_unix_socket() {
+        let cmdline = vec!["test", "--kvm_unix_socket=/tmp/introspector"];
+        let matches = App::new("test")
+            .args(DriverInitParams::to_clap_args().as_ref())
+            .get_matches_from(cmdline);
+        let params = DriverInitParams::from_matches(&matches);
+        assert_eq!(
+            KVMInitParams::UnixSocket {
+                path: String::from("/tmp/introspector")
+            },
+            params.kvm.unwrap()
+        );
+    }
+}


Test patch:
diff --git a/python/tests/unit/test_handler.py b/python/tests/unit/test_handler.py
index 60fb05e3..77984cd0 100644
--- a/python/tests/unit/test_handler.py
+++ b/python/tests/unit/test_handler.py
@@ -4,49 +4,43 @@
 
 
 def test_valid_scheme():
-    url = "vmi:///vm_name"
+    url = "vmi:///"
     url_to_driver_parameters(url)
 
 
 def test_invalid_scheme():
-    url = "http:///vm_name"
-    with pytest.raises(MicrovmiHandlerError):
-        url_to_driver_parameters(url)
-
-
-def test_empty_vm_name():
-    url = "vmi:///"
+    url = "http:///"
     with pytest.raises(MicrovmiHandlerError):
         url_to_driver_parameters(url)
 
 
 def test_parse_vm_name():
     expected = "windows10"
-    url = f"vmi:///{expected}"
-    value, *rest = url_to_driver_parameters(url)
-    assert expected == value
+    url = f"vmi:///?vm_name={expected}"
+    *rest, init_params = url_to_driver_parameters(url)
+    assert expected == init_params.common.vm_name
 
 
 def test_parse_vm_name_spaces():
     expected = "windows 10 20H1"
-    url = f"vmi:///{expected}"
-    value, *rest = url_to_driver_parameters(url)
-    assert expected == value
+    url = f"vmi:///?vm_name={expected}"
+    *rest, init_params = url_to_driver_parameters(url)
+    assert expected == init_params.common.vm_name
 
 
 def test_parse_hypervisor_none():
     expected = None
     url = "vmi:///vm_name"
-    _, value, _ = url_to_driver_parameters(url)
-    assert expected == value
+    drv_type, _ = url_to_driver_parameters(url)
+    assert expected == drv_type
 
 
 @pytest.mark.parametrize("drv_type_enum_member", [d for d in DriverType])
 def test_parse_hypervisor_each(drv_type_enum_member):
     expected = drv_type_enum_member
     url = f"vmi://{drv_type_enum_member.name}/vm_name"
-    _, value, _ = url_to_driver_parameters(url)
-    assert expected == value
+    drv_type, _ = url_to_driver_parameters(url)
+    assert expected == drv_type
 
 
 def test_parse_hypervisor_bad():
@@ -55,24 +49,17 @@ def test_parse_hypervisor_bad():
         url_to_driver_parameters(url)
 
 
-def test_parse_init_param_none():
-    expected = None
-    url = "vmi:///vm_name"
-    _, _, value = url_to_driver_parameters(url)
-    assert expected == value
-
-
 def test_parse_init_param_kvmi_socket():
     socket = "/tmp/introspector"
     # TODO: missing getter to test init_param type
-    url = f"vmi:///vm_name?kvmi_unix_socket={socket}"
-    _, _, value = url_to_driver_parameters(url)
-    assert socket == value.param_data_string
+    url = f"vmi://?kvm_unix_socket={socket}"
+    _, init_params = url_to_driver_parameters(url)
+    assert socket == init_params.kvm.unix_socket
 
 
-def test_parse_init_param_unkown_key():
+def test_parse_init_param_unknown_key():
     key = "unkown_config_key"
     socket = "/tmp/introspector"
-    url = f"vmi:///vm_name?{key}={socket}"
+    url = f"vmi://?{key}={socket}"
     with pytest.raises(MicrovmiHandlerError):
         url_to_driver_parameters(url)
diff --git a/tests/common/config.rs b/tests/common/config.rs
index 149c917f..f09abaf6 100644
--- a/tests/common/config.rs
+++ b/tests/common/config.rs
@@ -1,5 +1,5 @@
 pub static VM_NAME: &str = "winxp";
-pub static VM_VCPU_COUNT: u16 = 2;
+pub static VM_VCPU_COUNT: u16 = 1;
 pub static VIRSH_URI: &str = "qemu:///system";
 pub static KVMI_SOCKET: &str = "/tmp/introspector";
 pub static TIMEOUT: u64 = 20;
diff --git a/tests/common/kvm.rs b/tests/common/kvm.rs
index 6e5f5dee..e0dc31fc 100644
--- a/tests/common/kvm.rs
+++ b/tests/common/kvm.rs
@@ -1,7 +1,8 @@
 use std::process::{Command, Stdio};
 
 use log::debug;
-use microvmi::api::{DriverInitParam, DriverType, Introspectable};
+use microvmi::api::params::{CommonInitParams, DriverInitParams, KVMInitParams};
+use microvmi::api::{DriverType, Introspectable};
 use microvmi::init;
 
 use super::config::{KVMI_SOCKET, VIRSH_URI, VM_NAME};
@@ -30,9 +31,16 @@ impl Context for KVM {
 
     fn init_driver(&self) -> Box<dyn Introspectable> {
         init(
-            VM_NAME,
             Some(DriverType::KVM),
-            Some(DriverInitParam::KVMiSocket(String::from(KVMI_SOCKET))),
+            Some(DriverInitParams {
+                common: Some(CommonInitParams {
+                    vm_name: String::from(VM_NAME),
+                }),
+                kvm: Some(KVMInitParams::UnixSocket {
+                    path: String::from(KVMI_SOCKET),
+                }),
+                ..Default::default()
+            }),
         )
         .expect("Failed to init libmicrovmi")
     }
diff --git a/tests/tests/events.rs b/tests/tests/events.rs
index 62b8cd06..40a01139 100644
--- a/tests/tests/events.rs
+++ b/tests/tests/events.rs
@@ -1,5 +1,7 @@
+use microvmi::api::events::{CrType, EventReplyType, EventType, InterceptType};
+use microvmi::api::Introspectable;
+
 use super::IntegrationTest;
-use microvmi::api::{CrType, EventReplyType, EventType, InterceptType, Introspectable};
 
 fn intercept_cr3_one(mut drv: Box<dyn Introspectable>) {
     for vcpu in 0..drv.get_vcpu_count().unwrap() - 1 {