aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2021-04-08 13:11:20 +0530
committerViresh Kumar <viresh.kumar@linaro.org>2021-04-28 17:04:59 +0530
commitb1a8d89ccc1690edfec65d11c58b6218f6d0eadc (patch)
tree6bdc6b39c5a586e9f556c039732ac6b2e0b3ea34
vhost-user-i2c: Initialize repository
This patch initializes the vhost-user-i2c repository. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
-rw-r--r--.gitignore3
-rw-r--r--Cargo.toml21
-rw-r--r--src/cli.yaml42
-rw-r--r--src/i2c.rs37
-rw-r--r--src/main.rs72
-rw-r--r--src/vhu_i2c.rs153
6 files changed, 328 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..46f3847
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/target
+Cargo.lock
+*.swp
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..94a3cc1
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "vhost-user-i2c"
+version = "0.1.0"
+authors = ["Viresh Kumar <viresh.kumar@linaro.org>"]
+edition = "2018"
+description = "Virtio I2C backend daemon"
+license = "Apache-2.0"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+libc = ">=0.2.39"
+clap = { version = "2.33.3", features = ["yaml"] }
+epoll = "4.3"
+log = ">=0.4.6"
+vm-memory = ">=0.3.0"
+virtio-bindings = ">=0.1"
+
+# currently these are based of checkouts
+vhost = { git = "https://github.com/rust-vmm/vhost" }
+vhost-user-backend = { git = "https://github.com/rust-vmm/vhost-user-backend" }
diff --git a/src/cli.yaml b/src/cli.yaml
new file mode 100644
index 0000000..20dc718
--- /dev/null
+++ b/src/cli.yaml
@@ -0,0 +1,42 @@
+name: vhost-user-i2c
+# TODO, work out if we can get this via the crate_version!() and crate_authors!() macros
+version: "0.1.0"
+author: "Viresh Kumar <viresh.kumar@linaro.org>"
+about: Virtio I2C backend daemon.
+
+settings:
+ - ArgRequiredElseHelp
+
+args:
+ # Debug and Verbosity
+ - verbose:
+ long: verbose
+ short: v
+ takes_value: false
+ multiple: true
+ help: Set levels of verbosity
+ # Connection to socket
+ - socket:
+ long: socket-path
+ short: s
+ value_name: FILE
+ takes_value: true
+ help: Location of vhost-user Unix domain socket, incompatible with --fd
+ # I2C device list on host
+ - devices:
+ long: device-list
+ short: l
+ #FIXME
+ value_name: PATH
+ takes_value: true
+ help: List of I2C bus and clients in format <bus>:<client_addr>[:<client_addr>][,<bus>:<client_addr>[:<client_addr>]]
+
+groups:
+ - group1:
+ args:
+ - socket
+ required: true
+ - group2:
+ args:
+ - devices
+ required: true
diff --git a/src/i2c.rs b/src/i2c.rs
new file mode 100644
index 0000000..7f0b4bf
--- /dev/null
+++ b/src/i2c.rs
@@ -0,0 +1,37 @@
+// I2C backend device
+//
+// Copyright 2021 Linaro Ltd. All Rights Reserved.
+// Viresh Kumar <viresh.kumar@linaro.org>
+//
+// SPDX-License-Identifier: Apache-2.0
+
+use std::io::{Result};
+
+// Local I2C definitions
+const MAX_I2C_VDEV: usize = 1 << 7;
+const I2C_INVALID_ADAPTER: u32 = 0xFFFFFFFF;
+
+#[derive(Debug, Copy, Clone)]
+struct I2cAdapter {
+ fd: i32,
+ bus: u32,
+ smbus: bool,
+}
+
+pub struct I2cBackend {
+ adapters: Vec<I2cAdapter>,
+ adapter_map: [u32; MAX_I2C_VDEV],
+}
+
+impl I2cBackend {
+ pub fn new(_list: &str) -> Result<I2cBackend> {
+ let adapter_map: [u32; MAX_I2C_VDEV] = [I2C_INVALID_ADAPTER; MAX_I2C_VDEV];
+ let adapters: Vec<I2cAdapter> = Vec::new();
+
+
+ Ok(I2cBackend {
+ adapters,
+ adapter_map,
+ })
+ }
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..7d2cace
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,72 @@
+// VIRTIO I2C Emulation via vhost-user
+//
+// Copyright 2021 Linaro Ltd. All Rights Reserved.
+// Viresh Kumar <viresh.kumar@linaro.org>
+//
+// SPDX-License-Identifier: Apache-2.0
+
+mod i2c;
+mod vhu_i2c;
+
+use log::{error};
+use std::process::exit;
+use std::sync::{Arc, RwLock};
+use clap::{load_yaml, App};
+use vhost::vhost_user::Listener;
+use vhost_user_backend::{VhostUserDaemon};
+use i2c::I2cBackend;
+use vhu_i2c::VhostUserI2c;
+
+fn main() {
+ let yaml = load_yaml!("cli.yaml");
+ let cmd_args = App::from_yaml(yaml).get_matches();
+ let mut verbose = false;
+
+ let socket = match cmd_args.value_of("socket") {
+ Some(path) => path,
+ None => {
+ error!("Failed to retrieve vhost-user socket path");
+ exit(-1);
+ }
+ };
+
+ let listener = Listener::new(socket, true).unwrap();
+
+ let i2c_backend = match cmd_args.value_of("devices") {
+ Some(list) => match I2cBackend::new(list) {
+ Ok(s) => s,
+ Err(e) => {
+ println!("Failed to get I2cBackend: {}", e);
+ exit(-1);
+ }
+ },
+ None => {
+ println!("Failed to retrieve device-list");
+ exit(-1);
+ }
+ };
+
+ if cmd_args.is_present("verbose") {
+ verbose = true;
+ }
+
+ let vui2c = Arc::new(RwLock::new(
+ VhostUserI2c::new(i2c_backend).unwrap(),
+ ));
+
+ let mut daemon =
+ VhostUserDaemon::new(String::from("vhost-user-i2c-backend"), vui2c.clone()).unwrap();
+
+ if let Err(e) = daemon.start(listener) {
+ error!("Failed to start daemon: {:?}", e);
+ exit(-1);
+ }
+
+ if verbose {
+ println!("Daemon started");
+ }
+
+ if let Err(e) = daemon.wait() {
+ error!("Waiting for daemon failed: {:?}", e);
+ }
+}
diff --git a/src/vhu_i2c.rs b/src/vhu_i2c.rs
new file mode 100644
index 0000000..5854ba0
--- /dev/null
+++ b/src/vhu_i2c.rs
@@ -0,0 +1,153 @@
+// vhost user i2c device
+//
+// Copyright 2021 Linaro Ltd. All Rights Reserved.
+// Viresh Kumar <viresh.kumar@linaro.org>
+//
+// SPDX-License-Identifier: Apache-2.0
+
+use std::sync::{Arc, RwLock};
+use std::{convert, error, fmt, io};
+use vhost::vhost_user::message::*;
+use vhost_user_backend::{VhostUserBackend, Vring};
+use virtio_bindings::bindings::virtio_net::*;
+use virtio_bindings::bindings::virtio_ring::{VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC};
+use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap};
+use crate::i2c::*;
+
+const QUEUE_SIZE: usize = 1024;
+const NUM_QUEUES: usize = 1;
+
+type Result<T> = std::result::Result<T, Error>;
+type VhostUserBackendResult<T> = std::result::Result<T, std::io::Error>;
+
+#[derive(Debug)]
+pub enum Error {
+ // Failed to handle event other than input event.
+ HandleEventNotEpollIn,
+ // Failed to handle unknown event.
+ HandleEventUnknownEvent,
+ // Guest gave us a write only descriptor that protocol says to read from.
+ UnexpectedWriteOnlyDescriptor,
+ // Guest gave us a readable descriptor that protocol says to only write to.
+ UnexpectedReadDescriptor,
+ // Only 3 descriptors were expected.
+ UnexpectedDescriptorFound,
+ // Invalid descriptor
+ UnexpectedDescriptorSize,
+}
+impl error::Error for Error {}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "vhost-user-i2c error: {:?}", self)
+ }
+}
+
+impl convert::From<Error> for io::Error {
+ fn from(e: Error) -> Self {
+ io::Error::new(io::ErrorKind::Other, e)
+ }
+}
+
+pub struct VhostUserI2c {
+ backend: I2cBackend,
+ event_idx: bool,
+ mem: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
+}
+
+impl VhostUserI2c {
+ pub fn new(backend: I2cBackend) -> Result<Self> {
+ Ok(VhostUserI2c
+ {
+ backend: backend,
+ event_idx: false,
+ mem: None
+ })
+ }
+
+ // Process the messages in the vring and dispatch replies
+ fn process_queue(
+ &self,
+ _vring: &mut Vring,
+ ) -> Result<bool> {
+ Ok(true)
+ }
+}
+
+// VhostUserBackend trait methods
+impl VhostUserBackend for VhostUserI2c {
+ fn num_queues(&self) -> usize {
+ NUM_QUEUES
+ }
+
+ fn max_queue_size(&self) -> usize {
+ QUEUE_SIZE
+ }
+
+ fn features(&self) -> u64 {
+ // this matches the current libvhost defaults except VHOST_F_LOG_ALL
+ 1 << VIRTIO_F_VERSION_1
+ | 1 << VIRTIO_F_NOTIFY_ON_EMPTY
+ | 1 << VIRTIO_RING_F_INDIRECT_DESC
+ | 1 << VIRTIO_RING_F_EVENT_IDX
+ | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits()
+ }
+
+ fn protocol_features(&self) -> VhostUserProtocolFeatures {
+ VhostUserProtocolFeatures::MQ
+ }
+
+ fn set_event_idx(&mut self, enabled: bool) {
+ dbg!(self.event_idx = enabled);
+ }
+
+ fn update_memory(
+ &mut self,
+ mem: GuestMemoryAtomic<GuestMemoryMmap>,
+ ) -> VhostUserBackendResult<()> {
+ dbg!(self.mem = Some(mem));
+ Ok(())
+ }
+
+ fn handle_event(
+ &self,
+ device_event: u16,
+ evset: epoll::Events,
+ vrings: &[Arc<RwLock<Vring>>],
+ _thread_id: usize,
+ ) -> VhostUserBackendResult<bool> {
+ if evset != epoll::Events::EPOLLIN {
+ return Err(Error::HandleEventNotEpollIn.into());
+ }
+
+ match device_event {
+ 0 => {
+ let mut vring = vrings[0].write().unwrap();
+
+ if self.event_idx {
+ // vm-virtio's Queue implementation only checks avail_index
+ // once, so to properly support EVENT_IDX we need to keep
+ // calling process_queue() until it stops finding new
+ // requests on the queue.
+ loop {
+ vring.mut_queue().disable_notification().unwrap();
+ self.process_queue(&mut vring)?;
+ if !vring.mut_queue().enable_notification().unwrap() {
+ break;
+ }
+ }
+ } else {
+ // Without EVENT_IDX, a single call is enough.
+ self.process_queue(&mut vring)?;
+ }
+
+ }
+
+ _ => {
+ dbg!("unhandled device_event:", device_event);
+ return Err(Error::HandleEventUnknownEvent.into());
+ }
+ }
+ Ok(false)
+ }
+}