diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2021-04-08 13:11:20 +0530 |
---|---|---|
committer | Viresh Kumar <viresh.kumar@linaro.org> | 2021-04-28 17:04:59 +0530 |
commit | b1a8d89ccc1690edfec65d11c58b6218f6d0eadc (patch) | |
tree | 6bdc6b39c5a586e9f556c039732ac6b2e0b3ea34 |
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-- | .gitignore | 3 | ||||
-rw-r--r-- | Cargo.toml | 21 | ||||
-rw-r--r-- | src/cli.yaml | 42 | ||||
-rw-r--r-- | src/i2c.rs | 37 | ||||
-rw-r--r-- | src/main.rs | 72 | ||||
-rw-r--r-- | src/vhu_i2c.rs | 153 |
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) + } +} |