[opentitantool] Add support for 'emulator' commands

Introduction of new command 'emulator' to opentitantool.
Now all backends supporting 'EMULATOR' capability can be controlled using emulator sub-commands.

Short description of 'emulator' sub commands:
'state' - Return current state of Emulator in human readable form.
'stop' - Stop Emulator sub-process
'start' - Start Emulator sub-process with provided arguments.

Example:
```
$ opentitantool --interface=ti50Emulator state

$ opentitantool --interface=ti50Emulator start \
    --apps-list=test_app --flash-list=/data/bank1.img,/data/bank2.img ...

$ opentitantool --interface=ti50Emulator stop
```

Signed-off-by: Michał Mazurek <maz@semihalf.com>
diff --git a/sw/host/opentitanlib/src/io/emu.rs b/sw/host/opentitanlib/src/io/emu.rs
index 523b9d6..fbe9cbd 100644
--- a/sw/host/opentitanlib/src/io/emu.rs
+++ b/sw/host/opentitanlib/src/io/emu.rs
@@ -6,6 +6,8 @@
 use anyhow::Result;
 use serde::{Deserialize, Serialize};
 use std::collections::HashMap;
+use std::fmt;
+use std::path::PathBuf;
 use thiserror::Error;
 
 /// Error related to the `Emulator` trait.
@@ -13,7 +15,7 @@
 pub enum EmuError {
     #[error("Invalid argument name: {0}")]
     InvalidArgumetName(String),
-    #[error("Argument nmae: {0} has invalid value: {1}")]
+    #[error("Argument name: {0} has invalid value: {1}")]
     InvalidArgumentValue(String, String),
     #[error("Start failed with cause: {0}")]
     StartFailureCause(String),
@@ -30,9 +32,33 @@
 pub enum EmuValue {
     Empty,
     String(String),
-    FilePath(String),
+    FilePath(PathBuf),
     StringList(Vec<String>),
-    FilePathList(Vec<String>),
+    FilePathList(Vec<PathBuf>),
+}
+
+impl From<String> for EmuValue {
+    fn from(s: String) -> Self {
+        EmuValue::String(s)
+    }
+}
+
+impl From<Vec<String>> for EmuValue {
+    fn from(str_array: Vec<String>) -> Self {
+        EmuValue::StringList(str_array)
+    }
+}
+
+impl From<PathBuf> for EmuValue {
+    fn from(p: PathBuf) -> Self {
+        EmuValue::FilePath(p)
+    }
+}
+
+impl From<Vec<PathBuf>> for EmuValue {
+    fn from(path_array: Vec<PathBuf>) -> Self {
+        EmuValue::FilePathList(path_array)
+    }
 }
 
 #[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
@@ -43,6 +69,17 @@
     Error,
 }
 
+impl fmt::Display for EmuState {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            EmuState::Off => write!(f, "Off"),
+            EmuState::On => write!(f, "On"),
+            EmuState::Busy => write!(f, "Busy"),
+            EmuState::Error => write!(f, "Error"),
+        }
+    }
+}
+
 /// A trait which represents a `Emulator` instance
 pub trait Emulator {
     /// Simple function with return `EmuState` representing current state of Emulator instance.
diff --git a/sw/host/opentitantool/BUILD b/sw/host/opentitantool/BUILD
index 4aef06a..df50d82 100644
--- a/sw/host/opentitantool/BUILD
+++ b/sw/host/opentitantool/BUILD
@@ -11,6 +11,7 @@
     srcs = [
         "src/command/bootstrap.rs",
         "src/command/console.rs",
+        "src/command/emulator.rs",
         "src/command/gpio.rs",
         "src/command/hello.rs",
         "src/command/i2c.rs",
diff --git a/sw/host/opentitantool/src/command/emulator.rs b/sw/host/opentitantool/src/command/emulator.rs
new file mode 100644
index 0000000..1b4d253
--- /dev/null
+++ b/sw/host/opentitantool/src/command/emulator.rs
@@ -0,0 +1,135 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+use anyhow::Result;
+use erased_serde::Serialize;
+use std::any::Any;
+use std::collections::HashMap;
+use std::path::PathBuf;
+use structopt::StructOpt;
+
+use opentitanlib::app::command::CommandDispatch;
+use opentitanlib::app::TransportWrapper;
+use opentitanlib::io::emu::{EmuState, EmuValue};
+use opentitanlib::transport::Capability;
+
+#[derive(Debug, StructOpt)]
+/// Get State of Emulator instance
+pub struct EmuGetState {}
+
+#[derive(serde::Serialize)]
+pub struct EmuGetStateResult {
+    pub state: EmuState,
+}
+
+impl CommandDispatch for EmuGetState {
+    fn run(
+        &self,
+        _context: &dyn Any,
+        transport: &TransportWrapper,
+    ) -> Result<Option<Box<dyn Serialize>>> {
+        transport
+            .capabilities()?
+            .request(Capability::EMULATOR)
+            .ok()?;
+        let emulator = transport.emulator()?;
+        let state = emulator.get_state()?;
+        Ok(Some(Box::new(EmuGetStateResult { state })))
+    }
+}
+
+#[derive(Debug, StructOpt)]
+/// Start Emulator instance
+pub struct EmuStart {
+    #[structopt(
+        long,
+        help = "Reset all presistent storage (For example: flash, otp, eeprom) to factory state"
+    )]
+    pub factory_reset: bool,
+    #[structopt(long, help = "Emulator executable file name")]
+    pub emulator_exec: Option<String>,
+    #[structopt(
+        long,
+        help = "List of application names that will be provided in flash images"
+    )]
+    pub apps_list: Option<Vec<String>>,
+    #[structopt(
+        long,
+        parse(from_os_str),
+        help = "List of file paths representing Flash images"
+    )]
+    pub flash_list: Option<Vec<PathBuf>>,
+    #[structopt(
+        long,
+        parse(from_os_str),
+        help = "Path to file representing Emulator version state"
+    )]
+    pub version_init_state: Option<PathBuf>,
+    #[structopt(
+        long,
+        parse(from_os_str),
+        help = "Path to file representing PMU initial state"
+    )]
+    pub pmu_init_state: Option<PathBuf>,
+}
+
+fn pack_args(
+    args_map: &mut HashMap<String, EmuValue>,
+    key: &str,
+    value: &Option<impl Into<EmuValue> + Clone>,
+) {
+    if let Some(value) = value {
+        args_map.insert(key.to_string(), value.clone().into());
+    }
+}
+
+impl CommandDispatch for EmuStart {
+    fn run(
+        &self,
+        _context: &dyn Any,
+        transport: &TransportWrapper,
+    ) -> Result<Option<Box<dyn Serialize>>> {
+        transport
+            .capabilities()?
+            .request(Capability::EMULATOR)
+            .ok()?;
+        let emulator = transport.emulator()?;
+        let mut args: HashMap<String, EmuValue> = HashMap::new();
+        pack_args(&mut args, "exec", &self.emulator_exec);
+        pack_args(&mut args, "app", &self.apps_list);
+        pack_args(&mut args, "flash", &self.flash_list);
+        pack_args(&mut args, "version_init_state", &self.version_init_state);
+        pack_args(&mut args, "pmu_init_state", &self.pmu_init_state);
+        emulator.start(self.factory_reset, &args)?;
+        Ok(None)
+    }
+}
+
+#[derive(Debug, StructOpt)]
+/// Stop Emulator instance
+pub struct EmuStop {}
+
+impl CommandDispatch for EmuStop {
+    fn run(
+        &self,
+        _context: &dyn Any,
+        transport: &TransportWrapper,
+    ) -> Result<Option<Box<dyn Serialize>>> {
+        transport
+            .capabilities()?
+            .request(Capability::EMULATOR)
+            .ok()?;
+        let emulator = transport.emulator()?;
+        emulator.stop()?;
+        Ok(None)
+    }
+}
+
+#[derive(Debug, StructOpt, CommandDispatch)]
+/// Commands for interacting with Emulator instance
+pub enum EmuCommand {
+    State(EmuGetState),
+    Start(EmuStart),
+    Stop(EmuStop),
+}
diff --git a/sw/host/opentitantool/src/command/mod.rs b/sw/host/opentitantool/src/command/mod.rs
index b0d09e5..fdad781 100644
--- a/sw/host/opentitantool/src/command/mod.rs
+++ b/sw/host/opentitantool/src/command/mod.rs
@@ -4,6 +4,7 @@
 
 pub mod bootstrap;
 pub mod console;
+pub mod emulator;
 pub mod gpio;
 pub mod hello;
 pub mod i2c;
diff --git a/sw/host/opentitantool/src/main.rs b/sw/host/opentitantool/src/main.rs
index d19c6d7..66e895f 100644
--- a/sw/host/opentitantool/src/main.rs
+++ b/sw/host/opentitantool/src/main.rs
@@ -26,6 +26,7 @@
     Console(command::console::Console),
 
     Gpio(command::gpio::GpioCommand),
+    Emulator(command::emulator::EmuCommand),
 
     I2c(command::i2c::I2cCommand),
     Image(command::image::Image),