summaryrefslogtreecommitdiffstats
path: root/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src
diff options
context:
space:
mode:
authorTimos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>2024-10-03 11:00:30 +0300
committerJan-Simon Moeller <jsmoeller@linuxfoundation.org>2024-11-07 16:40:10 +0000
commit0b813fa1224e4c31caf4dbab0bd8146c4d038f4e (patch)
treed65dcbb60673cd1a553e469c87316bf5cff90481 /meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src
parent70c6b997f65a4ea87003b7dcb3c282c590c6cec2 (diff)
Update vhost-device-can and align it with rust-vmm implementation
[v2]: Remove recipe for socketcan-rs lib. Update vhost-device-can recipe to clone and be built with a selected version of socketcan-rs. [v1]: Update vhost-device-can device and align it with the vhost-device upstream implementation (commit: 42fa1204ec) - https://github.com/rust-vmm/vhost-device/tree/main/staging/vhost-device-can Bug-AGL: SPEC-4966 Change-Id: I9b7f5cb5d84c77198bc0c8b8cebc256fb5df6926 Signed-off-by: Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
Diffstat (limited to 'meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src')
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/backend.rs268
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/can.rs393
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/main.rs96
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/vhu_can.rs2145
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/virtio_can.rs144
5 files changed, 3046 insertions, 0 deletions
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/backend.rs b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/backend.rs
new file mode 100755
index 00000000..ce8d8511
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/backend.rs
@@ -0,0 +1,268 @@
+// VIRTIO CAN Emulation via vhost-user
+//
+// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+//
+// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
+
+use log::{error, info, warn};
+use std::any::Any;
+use std::collections::HashMap;
+use std::path::PathBuf;
+use std::sync::{Arc, RwLock};
+use std::thread;
+
+use thiserror::Error as ThisError;
+use vhost_user_backend::VhostUserDaemon;
+use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap};
+
+use crate::can::CanController;
+use crate::vhu_can::VhostUserCanBackend;
+
+pub(crate) type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Debug, ThisError)]
+/// Errors related to low level CAN helpers
+pub(crate) enum Error {
+ #[error("Invalid socket count: {0}")]
+ SocketCountInvalid(usize),
+ #[error("Could not find can devices")]
+ CouldNotFindCANDevs,
+ #[error("Could not create can controller: {0}")]
+ CouldNotCreateCanController(crate::can::Error),
+ #[error("Could not create can controller output socket: {0}")]
+ FailCreateCanControllerSocket(crate::can::Error),
+ #[error("Could not create can backend: {0}")]
+ CouldNotCreateBackend(crate::vhu_can::Error),
+ #[error("Could not create daemon: {0}")]
+ CouldNotCreateDaemon(vhost_user_backend::Error),
+ #[error("Fatal error: {0}")]
+ ServeFailed(vhost_user_backend::Error),
+ #[error("Thread `{0}` panicked")]
+ ThreadPanic(String, Box<dyn Any + Send>),
+}
+
+#[derive(PartialEq, Debug)]
+pub struct VuCanConfig {
+ pub socket_path: PathBuf,
+ pub socket_count: u32,
+ pub can_devices: Vec<String>,
+}
+
+impl VuCanConfig {
+ pub fn generate_socket_paths(&self) -> Vec<PathBuf> {
+ let socket_file_name = self
+ .socket_path
+ .file_name()
+ .expect("socket_path has no filename.");
+ let socket_file_parent = self
+ .socket_path
+ .parent()
+ .expect("socket_path has no parent directory.");
+
+ let make_socket_path = |i: u32| -> PathBuf {
+ let mut file_name = socket_file_name.to_os_string();
+ file_name.push(std::ffi::OsStr::new(&i.to_string()));
+ socket_file_parent.join(&file_name)
+ };
+
+ (0..self.socket_count).map(make_socket_path).collect()
+ }
+}
+
+/// This is the public API through which an external program starts the
+/// vhost-device-can backend server.
+pub(crate) fn start_backend_server(socket: PathBuf, can_devs: String) -> Result<()> {
+ loop {
+ let controller =
+ CanController::new(can_devs.clone()).map_err(Error::CouldNotCreateCanController)?;
+ let lockable_controller = Arc::new(RwLock::new(controller));
+ let vu_can_backend = Arc::new(RwLock::new(
+ VhostUserCanBackend::new(lockable_controller.clone())
+ .map_err(Error::CouldNotCreateBackend)?,
+ ));
+ lockable_controller
+ .write()
+ .unwrap()
+ .open_can_socket()
+ .map_err(Error::FailCreateCanControllerSocket)?;
+
+ let read_handle = CanController::start_read_thread(lockable_controller.clone());
+
+ let mut daemon = VhostUserDaemon::new(
+ String::from("vhost-device-can-backend"),
+ vu_can_backend.clone(),
+ GuestMemoryAtomic::new(GuestMemoryMmap::new()),
+ )
+ .map_err(Error::CouldNotCreateDaemon)?;
+
+ // Start the read thread -- need to handle it after termination
+ let vring_workers = daemon.get_epoll_handlers();
+ vu_can_backend
+ .read()
+ .unwrap()
+ .set_vring_worker(&vring_workers[0]);
+
+ daemon.serve(&socket).map_err(Error::ServeFailed)?;
+
+ // Terminate the thread which reads CAN messages from "can_devs"
+ lockable_controller.write().unwrap().exit_read_thread();
+
+ // Wait for read thread to exit
+ match read_handle.join() {
+ Ok(_) => info!("The read thread returned successfully"),
+ Err(e) => warn!("The read thread returned the error: {:?}", e),
+ }
+ }
+}
+
+pub fn start_backend(config: VuCanConfig) -> Result<()> {
+ let mut handles = HashMap::new();
+ let (senders, receiver) = std::sync::mpsc::channel();
+
+ for (thread_id, (socket, can_devs)) in config
+ .generate_socket_paths()
+ .into_iter()
+ .zip(config.can_devices.iter().cloned())
+ .map(|(a, b)| (a, b.to_string()))
+ .enumerate()
+ {
+ println!(
+ "thread_id: {}, socket: {:?}, can_devs: {:?}",
+ thread_id, socket, can_devs,
+ );
+
+ let name = format!("vhu-can-{}", can_devs);
+ let sender = senders.clone();
+ let handle = thread::Builder::new()
+ .name(name.clone())
+ .spawn(move || {
+ let result =
+ std::panic::catch_unwind(move || start_backend_server(socket, can_devs));
+
+ // Notify the main thread that we are done.
+ sender.send(thread_id).unwrap();
+
+ result.map_err(|e| Error::ThreadPanic(name, e))?
+ })
+ .unwrap();
+ handles.insert(thread_id, handle);
+ }
+
+ while !handles.is_empty() {
+ let thread_id = receiver.recv().unwrap();
+ handles
+ .remove(&thread_id)
+ .unwrap()
+ .join()
+ .map_err(std::panic::resume_unwind)
+ .unwrap()?;
+ }
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::backend::Error::FailCreateCanControllerSocket;
+ use crate::can::Error::SocketOpen;
+ use crate::CanArgs;
+ use assert_matches::assert_matches;
+
+ #[test]
+ fn test_can_valid_configuration() {
+ let valid_args = CanArgs {
+ socket_path: "/tmp/vhost.sock".to_string().into(),
+ can_devices: "can0".to_string(),
+ socket_count: 1,
+ };
+
+ assert_matches!(
+ VuCanConfig::try_from(valid_args),
+ Err(Error::CouldNotFindCANDevs)
+ );
+ }
+
+ #[test]
+ fn test_can_valid_mult_device_configuration() {
+ let valid_args = CanArgs {
+ socket_path: "/tmp/vhost.sock".to_string().into(),
+ can_devices: "can0 can1".to_string(),
+ socket_count: 2,
+ };
+
+ assert_matches!(
+ VuCanConfig::try_from(valid_args),
+ Err(Error::CouldNotFindCANDevs)
+ );
+ }
+
+ #[test]
+ fn test_can_invalid_socket_configuration() {
+ let invalid_args = CanArgs {
+ socket_path: "/tmp/vhost.sock".to_string().into(),
+ can_devices: "can0".to_string(),
+ socket_count: 0,
+ };
+
+ assert_matches!(
+ VuCanConfig::try_from(invalid_args),
+ Err(Error::SocketCountInvalid(0))
+ );
+ }
+
+ #[test]
+ fn test_can_invalid_mult_socket_configuration_1() {
+ let invalid_args = CanArgs {
+ socket_path: "/tmp/vhost.sock".to_string().into(),
+ can_devices: "can0".to_string(),
+ socket_count: 2,
+ };
+
+ assert_matches!(
+ VuCanConfig::try_from(invalid_args),
+ Err(Error::SocketCountInvalid(2))
+ );
+ }
+
+ #[test]
+ fn test_can_invalid_mult_socket_configuration_2() {
+ let invalid_args = CanArgs {
+ socket_path: "/tmp/vhost.sock".to_string().into(),
+ can_devices: "can0 can1".to_string(),
+ socket_count: 1,
+ };
+
+ assert_matches!(
+ VuCanConfig::try_from(invalid_args),
+ Err(Error::SocketCountInvalid(1))
+ );
+ }
+
+ #[test]
+ fn test_can_valid_configuration_start_backend_fail() {
+ // Instantiate the struct with the provided values
+ let config = VuCanConfig {
+ socket_path: PathBuf::from("/tmp/vhost.sock"),
+ socket_count: 1,
+ can_devices: vec!["can0".to_string()],
+ };
+
+ assert_matches!(
+ start_backend(config),
+ Err(FailCreateCanControllerSocket(SocketOpen))
+ );
+ }
+
+ #[test]
+ fn test_can_valid_configuration_start_backend_server_fail() {
+ let socket_path = PathBuf::from("/tmp/vhost.sock");
+ let can_devs = "can0".to_string();
+
+ assert_matches!(
+ start_backend_server(socket_path, can_devs),
+ Err(FailCreateCanControllerSocket(SocketOpen))
+ );
+ }
+}
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/can.rs b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/can.rs
new file mode 100755
index 00000000..6fc03550
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/can.rs
@@ -0,0 +1,393 @@
+// CAN backend device
+//
+// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+//
+// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
+
+use log::{error, info, trace, warn};
+use std::sync::{Arc, RwLock};
+
+use std::thread::{spawn, JoinHandle};
+use thiserror::Error as ThisError;
+use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
+extern crate queues;
+use queues::*;
+extern crate socketcan;
+use crate::virtio_can::{
+ VirtioCanConfig, VirtioCanFrame, CAN_CS_STARTED, CAN_CS_STOPPED, CAN_EFF_FLAG,
+ CAN_FRMF_TYPE_FD, VIRTIO_CAN_RX,
+};
+use socketcan::{
+ CanAnyFrame, CanDataFrame, CanFdFrame, CanFdSocket, EmbeddedFrame, ExtendedId, Frame, Id,
+ Socket, StandardId,
+};
+
+type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Copy, Clone, Debug, PartialEq, ThisError)]
+/// Errors related to low level can helpers
+pub(crate) enum Error {
+ #[error("Can open socket operation failed")]
+ SocketOpen,
+ #[error("Can write socket operation failed")]
+ SocketWrite,
+ #[error("Can read socket operation failed")]
+ SocketRead,
+ #[error("Pop can element operation failed")]
+ PopFailed,
+ #[error("Queue is empty")]
+ QueueEmpty,
+ #[error("Creating Eventfd for CAN events failed")]
+ EventFdFailed,
+ #[error("Push can element operation failed")]
+ PushFailed,
+ #[error("No output interface available")]
+ NoOutputInterface,
+}
+
+#[derive(Debug)]
+pub(crate) struct CanController {
+ pub config: VirtioCanConfig,
+ pub can_name: String,
+ pub can_socket: Option<CanFdSocket>,
+ pub rx_event_fd: EventFd,
+ rx_fifo: Queue<VirtioCanFrame>,
+ pub status: bool,
+ pub ctrl_state: u8,
+}
+
+impl CanController {
+ // Creates a new controller corresponding to `device`.
+ pub(crate) fn new(can_name: String) -> Result<CanController> {
+ let can_name = can_name.to_owned();
+ info!("can_name: {:?}", can_name);
+
+ let rx_fifo = Queue::new();
+ let rx_efd = EventFd::new(EFD_NONBLOCK).map_err(|_| Error::EventFdFailed)?;
+
+ Ok(CanController {
+ config: VirtioCanConfig { status: 0x0.into() },
+ can_name,
+ can_socket: None,
+ rx_event_fd: rx_efd,
+ rx_fifo,
+ status: true,
+ ctrl_state: CAN_CS_STOPPED,
+ })
+ }
+
+ pub fn print_can_frame(canframe: VirtioCanFrame) {
+ trace!("canframe.msg_type 0x{:x}", canframe.msg_type.to_native());
+ trace!("canframe.can_id 0x{:x}", canframe.can_id.to_native());
+ trace!("canframe.length {}", canframe.length.to_native());
+ trace!("canframe.flags 0x{:x}", canframe.flags.to_native());
+ if canframe.length.to_native() == 0 {
+ trace!("[]");
+ return;
+ }
+ trace!("[");
+ let last_elem = canframe.length.to_native() as usize - 1;
+ for (index, sdu) in canframe.sdu.iter().enumerate() {
+ if index == last_elem {
+ trace!("0x{:x}", sdu);
+ break;
+ }
+ trace!("0x{:x}, ", sdu);
+ }
+ trace!("]");
+ }
+
+ pub fn start_read_thread(controller: Arc<RwLock<CanController>>) -> JoinHandle<Result<()>> {
+ spawn(move || CanController::read_can_socket(controller))
+ }
+
+ pub fn push(&mut self, rx_elem: VirtioCanFrame) -> Result<()> {
+ match self.rx_fifo.add(rx_elem) {
+ Ok(_) => Ok(()),
+ _ => Err(Error::PushFailed),
+ }
+ }
+
+ pub fn rx_is_empty(&mut self) -> bool {
+ self.rx_fifo.size() == 0
+ }
+
+ pub fn pop(&mut self) -> Result<VirtioCanFrame> {
+ if self.rx_fifo.size() < 1 {
+ return Err(Error::QueueEmpty);
+ }
+
+ match self.rx_fifo.remove() {
+ Ok(item) => Ok(item),
+ _ => Err(Error::PopFailed),
+ }
+ }
+
+ pub fn open_can_socket(&mut self) -> Result<()> {
+ self.can_socket = match CanFdSocket::open(&self.can_name) {
+ Ok(socket) => Some(socket),
+ Err(_) => {
+ warn!("Error opening CAN socket");
+ return Err(Error::SocketOpen);
+ }
+ };
+ Ok(())
+ }
+
+ // Helper function to process frame
+ fn process_frame<F: Frame>(frame: F, is_fd: bool) -> VirtioCanFrame {
+ VirtioCanFrame {
+ msg_type: VIRTIO_CAN_RX.into(),
+ can_id: frame.id_word().into(),
+ length: (frame.data().len() as u16).into(),
+ reserved: 0.into(),
+ flags: if is_fd {
+ CAN_FRMF_TYPE_FD.into()
+ } else {
+ 0.into()
+ },
+ sdu: {
+ let mut sdu_data: [u8; 64] = [0; 64];
+ sdu_data[..frame.data().len()].copy_from_slice(frame.data());
+ sdu_data
+ },
+ }
+ }
+
+ pub fn read_can_socket(controller: Arc<RwLock<CanController>>) -> Result<()> {
+ let can_name = &controller.read().unwrap().can_name.clone();
+ dbg!("Start reading from {} socket!", &can_name);
+ let socket = match CanFdSocket::open(can_name) {
+ Ok(socket) => socket,
+ Err(_) => {
+ warn!("Error opening CAN socket");
+ return Err(Error::SocketOpen);
+ }
+ };
+
+ // Set non-blocking otherwise the device will not restart immediatelly
+ // when the VM closes, and a new canfd message needs to be received for
+ // restart to happen.
+ // This caused by the fact that the thread is stacked in read function
+ // and does not go to the next loop to check the status condition.
+ socket
+ .set_nonblocking(true)
+ .expect("Cannot set nonblocking");
+
+ // Receive CAN messages
+ loop {
+ // If the status variable is false then break and exit.
+ if !controller.read().unwrap().status {
+ dbg!("exit read can thread");
+ return Ok(());
+ }
+
+ if let Ok(frame) = socket.read_frame() {
+ // If ctrl_state is stopped, consume the received CAN/FD frame
+ // and loop till the ctrl_state changes to started or the thread
+ // to exit.
+ if controller.read().unwrap().ctrl_state != CAN_CS_STARTED {
+ trace!("CAN/FD frame is received but not saved!");
+ continue;
+ }
+
+ // Match and process frame variants
+ let read_can_frame = match frame {
+ CanAnyFrame::Normal(frame) => {
+ trace!("Received CAN frame: {:?}", frame);
+ Self::process_frame(frame, false)
+ }
+ CanAnyFrame::Fd(frame) => {
+ trace!("Received CAN FD frame: {:?}", frame);
+ Self::process_frame(frame, true)
+ }
+ CanAnyFrame::Remote(frame) => {
+ trace!("Received Remote CAN frame: {:?}", frame);
+ Self::process_frame(frame, false)
+ }
+ CanAnyFrame::Error(frame) => {
+ trace!("Received Error frame: {:?}", frame);
+ Self::process_frame(frame, false)
+ }
+ };
+
+ match controller.write().unwrap().push(read_can_frame) {
+ Ok(_) => warn!("New Can frame was received"),
+ Err(_) => {
+ warn!("Error read/push CAN frame");
+ return Err(Error::SocketRead);
+ }
+ };
+
+ controller
+ .write()
+ .unwrap()
+ .rx_event_fd
+ .write(1)
+ .expect("Fail to write on rx_event_fd");
+ }
+ }
+ }
+
+ pub(crate) fn exit_read_thread(&mut self) {
+ trace!("Exit can read thread\n");
+ self.status = false;
+ }
+
+ pub(crate) fn config(&mut self) -> &VirtioCanConfig {
+ &self.config
+ }
+
+ pub(crate) fn can_out(&self, tx_request: VirtioCanFrame) -> Result<()> {
+ // Create a CAN frame with a specific CAN-ID and the data buffer
+ let can_id: Id = if (tx_request.can_id.to_native() & CAN_EFF_FLAG) != 0 {
+ // SAFETY: Use new_unchecked cause checks have been taken place
+ // to prior stage. Also flags have beem already added on can_id
+ // so tnew will fail (can_id + can_flags) > 29 bits
+ unsafe { Id::Extended(ExtendedId::new_unchecked(tx_request.can_id.into())) }
+ } else {
+ // SAFETY: Use new_unchecked cause checks have been taken place
+ // to prior stage. Also flags have beem already added on can_id
+ // so tnew will fail (can_id + can_flags) > 11 bits
+ unsafe {
+ Id::Standard(StandardId::new_unchecked(
+ tx_request.can_id.to_native() as u16
+ ))
+ }
+ };
+
+ // Grab the data to be tranfered
+ let data_len = tx_request.length.to_native() as usize;
+ let data: Vec<u8> = tx_request.sdu.iter().cloned().take(data_len).collect();
+
+ // Format CAN/FD frame
+ let frame: CanAnyFrame = if (tx_request.flags.to_native() & CAN_FRMF_TYPE_FD) != 0 {
+ CanAnyFrame::Fd(CanFdFrame::new(can_id, &data).expect("Fail to create CanFdFrame"))
+ } else {
+ CanAnyFrame::Normal(CanDataFrame::new(can_id, &data).expect("Fail to create CanFrame"))
+ };
+
+ // Send the CAN/FD frame
+ let socket = self.can_socket.as_ref().ok_or("No available device");
+
+ match socket {
+ Ok(socket) => match socket.write_frame(&frame) {
+ Ok(_) => Ok(()),
+ Err(_) => {
+ warn!("Error write CAN socket");
+ Err(Error::SocketWrite)
+ }
+ },
+ Err(_) => Err(Error::NoOutputInterface),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::vhu_can::VhostUserCanBackend;
+ use std::sync::{Arc, RwLock};
+
+ #[test]
+ fn test_can_controller_creation() {
+ let can_name = "can".to_string();
+
+ let controller = CanController::new(can_name.clone()).unwrap();
+ assert_eq!(controller.can_name, can_name);
+ }
+
+ #[test]
+ fn test_can_controller_push_and_pop() {
+ let can_name = "can".to_string();
+ let mut controller = CanController::new(can_name.clone()).unwrap();
+
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_RX.into(),
+ can_id: 123.into(),
+ length: 64.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ // Test push
+ controller.push(frame).unwrap();
+
+ // Test pop
+ let pop_result = controller.pop().unwrap();
+ assert_eq!(pop_result, frame);
+ }
+
+ #[test]
+ fn test_can_controller_config() {
+ let can_name = "can".to_string();
+ let mut controller = CanController::new(can_name.clone()).unwrap();
+
+ // Test config
+ let config = controller.config();
+ assert_eq!(config.status.to_native(), 0);
+ }
+
+ #[test]
+ fn test_can_controller_operation() {
+ let can_name = "can".to_string();
+ let mut controller = CanController::new(can_name.clone()).unwrap();
+
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_RX.into(),
+ can_id: 123.into(),
+ length: 64.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ match controller.open_can_socket() {
+ Ok(_) => {
+ // Test operation
+ let operation_result = controller.can_out(frame);
+ assert!(operation_result.is_ok());
+ }
+ Err(_) => warn!("There is no CAN interface with {} name", can_name),
+ }
+ }
+
+ #[test]
+ fn test_can_controller_start_read_thread() {
+ let can_name = "can".to_string();
+ let controller = CanController::new(can_name.clone()).unwrap();
+ let arc_controller = Arc::new(RwLock::new(controller));
+
+ // Test start_read_thread
+ let thread_handle = CanController::start_read_thread(arc_controller.clone());
+ assert!(thread_handle.join().is_ok());
+ }
+
+ #[test]
+ fn test_can_open_socket_fail() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ assert_eq!(
+ controller.write().unwrap().open_can_socket(),
+ Err(Error::SocketOpen)
+ );
+ }
+
+ #[test]
+ fn test_can_read_socket_fail() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ assert_eq!(
+ CanController::read_can_socket(controller),
+ Err(Error::SocketOpen)
+ );
+ }
+}
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/main.rs b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/main.rs
new file mode 100755
index 00000000..97386884
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/main.rs
@@ -0,0 +1,96 @@
+// VIRTIO CAN Emulation via vhost-user
+//
+// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+//
+// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
+
+mod backend;
+mod can;
+mod vhu_can;
+mod virtio_can;
+
+use clap::Parser;
+use log::{error, info};
+use socketcan::{CanSocket, Socket};
+use std::convert::TryFrom;
+use std::path::PathBuf;
+use std::process::exit;
+
+pub(crate) type Result<T> = std::result::Result<T, Error>;
+use crate::backend::{start_backend, Error, VuCanConfig};
+
+#[derive(Parser, Debug)]
+#[clap(author, version, about, long_about = None)]
+struct CanArgs {
+ /// Location of vhost-user Unix domain socket. This is suffixed by 0,1,2..socket_count-1.
+ #[clap(short, long, value_name = "SOCKET")]
+ socket_path: PathBuf,
+
+ /// A can device name to be used for reading (ex. vcan, can0, can1, ... etc.)
+ #[clap(short = 'd', long)]
+ can_devices: String,
+
+ /// Number of guests (sockets) to connect to.
+ #[clap(short = 'c', long, default_value_t = 1)]
+ socket_count: u32,
+}
+
+fn check_can_devices(can_devices: &[String]) -> Result<()> {
+ for can_dev in can_devices {
+ if CanSocket::open(can_dev).is_err() {
+ info!("There is no interface with the following name {}", can_dev);
+ return Err(Error::CouldNotFindCANDevs);
+ }
+ }
+ Ok(())
+}
+
+fn parse_can_devices(input: &CanArgs) -> Result<Vec<String>> {
+ let can_devices_vec: Vec<&str> = input.can_devices.split_whitespace().collect();
+ let can_devices: Vec<_> = can_devices_vec.iter().map(|x| x.to_string()).collect();
+
+ if (can_devices.len() as u32) != input.socket_count {
+ info!(
+ "Number of CAN/FD devices ({}) not equal with socket count {}",
+ input.can_devices, input.socket_count
+ );
+ return Err(Error::SocketCountInvalid(
+ input.socket_count.try_into().unwrap(),
+ ));
+ }
+
+ match check_can_devices(&can_devices) {
+ Ok(_) => Ok(can_devices),
+ Err(_) => Err(Error::CouldNotFindCANDevs),
+ }
+}
+
+impl TryFrom<CanArgs> for VuCanConfig {
+ type Error = Error;
+
+ fn try_from(args: CanArgs) -> Result<Self> {
+ if args.socket_count == 0 {
+ return Err(Self::Error::SocketCountInvalid(0));
+ }
+
+ let can_devices = match parse_can_devices(&args) {
+ Ok(can_devs) => can_devs,
+ Err(e) => return Err(e),
+ };
+
+ Ok(VuCanConfig {
+ socket_path: args.socket_path,
+ socket_count: args.socket_count,
+ can_devices,
+ })
+ }
+}
+
+fn main() {
+ env_logger::init();
+ if let Err(e) = VuCanConfig::try_from(CanArgs::parse()).and_then(start_backend) {
+ error!("{e}");
+ exit(1);
+ }
+}
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/vhu_can.rs b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/vhu_can.rs
new file mode 100755
index 00000000..b53f57d1
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/vhu_can.rs
@@ -0,0 +1,2145 @@
+// vhost device can
+//
+// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+//
+// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
+
+use crate::can::CanController;
+use crate::can::Error::QueueEmpty;
+use crate::virtio_can::{
+ VirtioCanCtrlRequest, VirtioCanFrame, VirtioCanHeader, CANFD_VALID_LENGTHS, CAN_CS_STARTED,
+ CAN_CS_STOPPED, CAN_EFF_FLAG, CAN_EFF_MASK, CAN_ERR_BUSOFF, CAN_ERR_FLAG, CAN_FRMF_TYPE_FD,
+ CAN_RTR_FLAG, CAN_SFF_MASK, VIRTIO_CAN_FLAGS_EXTENDED, VIRTIO_CAN_FLAGS_FD,
+ VIRTIO_CAN_FLAGS_RTR, VIRTIO_CAN_FLAGS_VALID_MASK, VIRTIO_CAN_F_CAN_CLASSIC,
+ VIRTIO_CAN_F_CAN_FD, VIRTIO_CAN_F_RTR_FRAMES, VIRTIO_CAN_RESULT_NOT_OK, VIRTIO_CAN_RESULT_OK,
+ VIRTIO_CAN_RX, VIRTIO_CAN_SET_CTRL_MODE_START, VIRTIO_CAN_SET_CTRL_MODE_STOP,
+ VIRTIO_CAN_S_CTRL_BUSOFF, VIRTIO_CAN_TX,
+};
+use log::{error, trace, warn};
+use std::os::fd::AsRawFd;
+use std::slice::from_raw_parts;
+use std::sync::{Arc, RwLock};
+use std::{
+ convert,
+ io::{self, Result as IoResult},
+};
+use thiserror::Error as ThisError;
+use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures};
+use vhost_user_backend::VringEpollHandler;
+use vhost_user_backend::{VhostUserBackendMut, VringRwLock, VringT};
+use virtio_bindings::bindings::virtio_config::{VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_VERSION_1};
+use virtio_bindings::bindings::virtio_ring::{
+ VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC,
+};
+use virtio_queue::{DescriptorChain, QueueOwnedT};
+use vm_memory::{
+ ByteValued, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryLoadGuard, GuestMemoryMmap,
+};
+use vmm_sys_util::epoll::EventSet;
+use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
+
+/// Virtio configuration
+const QUEUE_SIZE: usize = 64;
+const NUM_QUEUES: usize = 3;
+
+/// Queues
+const TX_QUEUE: u16 = 0;
+const RX_QUEUE: u16 = 1;
+const CTRL_QUEUE: u16 = 2;
+const BACKEND_EFD: u16 = (NUM_QUEUES + 1) as u16;
+
+type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Copy, Clone, Debug, PartialEq, ThisError)]
+/// Errors related to vhost-device-can-daemon.
+pub(crate) enum Error {
+ #[error("Failed to handle event, didn't match EPOLLIN")]
+ HandleEventNotEpollIn,
+ #[error("Failed to handle unknown event")]
+ HandleEventUnknown,
+ #[error("Descriptor not found")]
+ DescriptorNotFound,
+ #[error("Descriptor read failed")]
+ DescriptorReadFailed,
+ #[error("Descriptor write failed")]
+ DescriptorWriteFailed,
+ #[error("Failed to send notification")]
+ NotificationFailed,
+ #[error("Failed to create new EventFd")]
+ EventFdFailed,
+ #[error("Unknown can message type: {0}")]
+ UnexpectedCanMsgType(u16),
+ #[error("RTR frames not negotiated")]
+ UnexpectedRtrFlag,
+ #[error("Can FD frames not negotiated")]
+ UnexpectedFdFlag,
+ #[error("Unexpected CAN ID bigger than: {0} bits")]
+ UnexpectedCanId(u16),
+ #[error("Invalid CAN flags: {0}")]
+ InvalidCanFlags(u32),
+ #[error("Invalid CAN[FD] length: {0}")]
+ InvalidCanLength(u32),
+ #[error("Classic CAN frames not negotiated")]
+ UnexpectedClassicFlag,
+ #[error("Bus off error received")]
+ BusoffRxFrame,
+ #[error("Rx CAN frame has unknown error")]
+ RxFrameUnknownFail,
+}
+
+impl convert::From<Error> for io::Error {
+ fn from(e: Error) -> Self {
+ io::Error::new(io::ErrorKind::Other, e)
+ }
+}
+
+pub(crate) struct VhostUserCanBackend {
+ controller: Arc<RwLock<CanController>>,
+ acked_features: u64,
+ event_idx: bool,
+ pub(crate) exit_event: EventFd,
+ mem: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
+}
+
+type CanDescriptorChain = DescriptorChain<GuestMemoryLoadGuard<GuestMemoryMmap<()>>>;
+
+impl VhostUserCanBackend {
+ pub(crate) fn new(controller: Arc<RwLock<CanController>>) -> Result<Self> {
+ Ok(VhostUserCanBackend {
+ controller,
+ event_idx: false,
+ acked_features: 0x0,
+ exit_event: EventFd::new(EFD_NONBLOCK).map_err(|_| Error::EventFdFailed)?,
+ mem: None,
+ })
+ }
+
+ fn check_features(&self, features: u16) -> bool {
+ (self.acked_features & (1 << features)) != 0
+ }
+
+ fn check_tx_frame(&self, request: VirtioCanFrame) -> Result<VirtioCanFrame> {
+ let msg_type = request.msg_type.to_native();
+ let mut can_id = request.can_id.to_native();
+ let length = request.length.to_native();
+ let mut flags = 0;
+
+ if msg_type != VIRTIO_CAN_TX {
+ warn!("TX: Message type 0x{:x} unknown\n", msg_type);
+ return Err(Error::UnexpectedCanMsgType(msg_type));
+ }
+
+ // Check for undefined bits in frame's flags
+ let invalid_can_flags: u32 = request.flags.to_native() & !VIRTIO_CAN_FLAGS_VALID_MASK;
+ if invalid_can_flags != 0 {
+ return Err(Error::InvalidCanFlags(invalid_can_flags));
+ }
+
+ // If VIRTIO_CAN_FLAGS_EXTENDED has negotiated then use extended CAN ID
+ if (request.flags.to_native() & VIRTIO_CAN_FLAGS_EXTENDED) != 0 {
+ // If we have a extended frame there is no way to check if
+ // can_id > 29 bits. The reason is that bit 29, 30, 31 are
+ // dedicated to CAN specific flags (EFF, RTR, ERR).
+ if can_id > CAN_EFF_MASK {
+ return Err(Error::UnexpectedCanId(29_u16));
+ }
+ can_id |= CAN_EFF_FLAG;
+ } else {
+ // If we have a standard frame and can_id > 11 bits then fail
+ if can_id > CAN_SFF_MASK {
+ return Err(Error::UnexpectedCanId(11_u16));
+ }
+ }
+
+ // Remote transfer request is used only with classic CAN
+ if (request.flags.to_native() & VIRTIO_CAN_FLAGS_RTR) != 0 {
+ if !self.check_features(VIRTIO_CAN_F_CAN_CLASSIC)
+ || !self.check_features(VIRTIO_CAN_F_RTR_FRAMES)
+ {
+ warn!("TX: RTR frames not negotiated");
+ return Err(Error::UnexpectedRtrFlag);
+ }
+ can_id |= CAN_RTR_FLAG;
+ }
+
+ // One of VIRTIO_CAN_F_CAN_CLASSIC and VIRTIO_CAN_F_CAN_FD must be negotiated
+ // Check if VIRTIO_CAN_F_CAN_FD is negotiated when the frame is CANFD
+ if (request.flags.to_native() & VIRTIO_CAN_FLAGS_FD) != 0 {
+ if !self.check_features(VIRTIO_CAN_F_CAN_FD) {
+ warn!("TX: FD frames not negotiated\n");
+ return Err(Error::UnexpectedFdFlag);
+ }
+ flags = CAN_FRMF_TYPE_FD;
+ } else {
+ // Check if VIRTIO_CAN_F_CAN_CLASSIC is negotiated when the frame is CAN
+ if !self.check_features(VIRTIO_CAN_F_CAN_CLASSIC) {
+ warn!("TX: Classic frames not negotiated\n");
+ return Err(Error::UnexpectedClassicFlag);
+ }
+ }
+
+ // Check if CAN/FD length is out-of-range (based on negotiated features)
+ if (request.flags.to_native() & VIRTIO_CAN_FLAGS_FD) != 0 {
+ if length > 8 && !CANFD_VALID_LENGTHS.contains(&length.into()) {
+ return Err(Error::InvalidCanLength(length.into()));
+ }
+ } else if length > 8 {
+ return Err(Error::InvalidCanLength(length.into()));
+ }
+
+ Ok(VirtioCanFrame {
+ msg_type: msg_type.into(),
+ can_id: can_id.into(),
+ length: length.into(),
+ reserved: 0.into(),
+ flags: flags.into(),
+ sdu: request.sdu[0..64].try_into().unwrap(),
+ })
+ }
+
+ fn check_rx_frame(&self, response: VirtioCanFrame) -> Result<VirtioCanFrame> {
+ CanController::print_can_frame(response);
+
+ let mut can_rx = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_RX.into(),
+ can_id: response.can_id,
+ length: response.length,
+ reserved: 0.into(),
+ flags: response.flags,
+ sdu: [0; 64],
+ };
+ let res_len = response.length.to_native();
+ let res_can_id = response.can_id.to_native();
+ let mut res_flags = response.flags.to_native();
+
+ // If we receive an error message check if that's a busoff.
+ // If no just drop the message, otherwise update config and return.
+ if (res_can_id & CAN_ERR_FLAG) != 0 {
+ if (res_can_id & CAN_ERR_BUSOFF) != 0 {
+ // TODO: Trigger a config_change notification
+ self.controller.write().unwrap().config.status = VIRTIO_CAN_S_CTRL_BUSOFF.into();
+ self.controller.write().unwrap().ctrl_state = CAN_CS_STOPPED;
+ warn!("Got BusOff error frame, device does a local bus off\n");
+ return Err(Error::BusoffRxFrame);
+ } else {
+ trace!("Dropping error frame 0x{:x}\n", response.can_id.to_native());
+ return Err(Error::RxFrameUnknownFail);
+ }
+ }
+
+ // One of VIRTIO_CAN_F_CAN_CLASSIC and VIRTIO_CAN_F_CAN_FD must be negotiated
+ if (res_flags & CAN_FRMF_TYPE_FD) != 0 {
+ if !self.check_features(VIRTIO_CAN_F_CAN_FD) {
+ warn!("Drop non-supported CAN FD frame");
+ return Err(Error::UnexpectedFdFlag);
+ }
+ } else if !self.check_features(VIRTIO_CAN_F_CAN_CLASSIC) {
+ warn!("Drop non-supported CAN classic frame");
+ return Err(Error::UnexpectedClassicFlag);
+ }
+
+ // Add VIRTIO_CAN_FLAGS_EXTENDED in flag if the received frame
+ // had an extended CAN ID.
+ // Based on virtio-spec v1.4, the 3 MSB of can_id should be set to 0.
+ if (res_can_id & CAN_EFF_FLAG) != 0 {
+ can_rx.flags = VIRTIO_CAN_FLAGS_EXTENDED.into();
+ can_rx.can_id = (res_can_id & CAN_EFF_MASK).into();
+ } else {
+ can_rx.flags = 0.into();
+ can_rx.can_id = (res_can_id & CAN_SFF_MASK).into();
+ }
+
+ // Remote transfer request is used only with classic CAN
+ if (res_can_id & CAN_RTR_FLAG) != 0 {
+ if !self.check_features(VIRTIO_CAN_F_RTR_FRAMES)
+ || !self.check_features(VIRTIO_CAN_F_CAN_CLASSIC)
+ {
+ warn!("Drop non-supported RTR frame");
+ return Err(Error::UnexpectedRtrFlag);
+ }
+ // If remote transfer request is enabled add the according flag
+ can_rx.flags = (can_rx.flags.to_native() | VIRTIO_CAN_FLAGS_RTR).into();
+ }
+
+ // Treat Vcan interface as CANFD if MTU is set to 64 bytes.
+ //
+ // Vcan can not be configured as CANFD interface, but it is
+ // possible to configure its MTU to 64 bytes. So if a messages
+ // bigger than 8 bytes is being received we consider it as
+ // CANFD message.
+ let can_name = self.controller.read().unwrap().can_name.clone();
+ if self.check_features(VIRTIO_CAN_F_CAN_FD) && res_len > 8 && can_name == "vcan0" {
+ res_flags |= CAN_FRMF_TYPE_FD;
+ warn!("\n\n\nCANFD VCAN0\n\n");
+ }
+
+ // Check if CAN/FD length is out-of-range (based on negotiated features)
+ if (res_flags & CAN_FRMF_TYPE_FD) != 0 {
+ if res_len > 8 && !CANFD_VALID_LENGTHS.contains(&res_len.into()) {
+ return Err(Error::InvalidCanLength(res_len.into()));
+ }
+ can_rx.flags = (can_rx.flags.to_native() | VIRTIO_CAN_FLAGS_FD).into();
+ } else if res_len > 8 {
+ return Err(Error::InvalidCanLength(res_len.into()));
+ }
+
+ can_rx.sdu.copy_from_slice(&response.sdu[0..64]);
+ CanController::print_can_frame(can_rx);
+
+ Ok(can_rx)
+ }
+
+ fn process_ctrl_requests(
+ &self,
+ requests: Vec<CanDescriptorChain>,
+ vring: &VringRwLock,
+ ) -> Result<bool> {
+ if requests.is_empty() {
+ return Ok(true);
+ }
+
+ for desc_chain in requests {
+ let atomic_mem = self.mem.as_ref().unwrap().memory();
+
+ let mut reader = desc_chain
+ .clone()
+ .reader(&atomic_mem)
+ .map_err(|_| Error::DescriptorReadFailed)?;
+
+ let mut writer = desc_chain
+ .clone()
+ .writer(&atomic_mem)
+ .map_err(|_| Error::DescriptorWriteFailed)?;
+
+ let request = reader
+ .read_obj::<VirtioCanCtrlRequest>()
+ .map_err(|_| Error::DescriptorReadFailed)?;
+
+ // This implementation requires the CAN devices to be already in UP state
+ // before starting. This code does not trigger state changes [UP/DOWN] of
+ // the host CAN devices.
+ let response = match request.msg_type.into() {
+ VIRTIO_CAN_SET_CTRL_MODE_START => {
+ if self.controller.write().unwrap().ctrl_state == CAN_CS_STARTED {
+ VIRTIO_CAN_RESULT_NOT_OK
+ } else {
+ // TODO: Trigger a config_change notification (optional)
+ self.controller.write().unwrap().config.status = 0.into();
+ self.controller.write().unwrap().ctrl_state = CAN_CS_STARTED;
+ VIRTIO_CAN_RESULT_OK
+ }
+ }
+ VIRTIO_CAN_SET_CTRL_MODE_STOP => {
+ if self.controller.write().unwrap().ctrl_state == CAN_CS_STOPPED {
+ VIRTIO_CAN_RESULT_NOT_OK
+ } else {
+ // TODO: Trigger a config_change notification (optional)
+ self.controller.write().unwrap().config.status = 0.into();
+ self.controller.write().unwrap().ctrl_state = CAN_CS_STOPPED;
+ VIRTIO_CAN_RESULT_OK
+ }
+ }
+ _ => {
+ trace!("Ctrl queue: msg type 0x{:?} unknown", request.msg_type);
+ VIRTIO_CAN_RESULT_NOT_OK
+ }
+ };
+
+ writer
+ .write_obj(response)
+ .map_err(|_| Error::DescriptorWriteFailed)?;
+
+ if vring
+ .add_used(desc_chain.head_index(), writer.bytes_written() as u32)
+ .is_err()
+ {
+ warn!("Couldn't return used descriptors to the ring");
+ }
+ }
+
+ Ok(true)
+ }
+
+ fn process_tx_requests(
+ &self,
+ requests: Vec<CanDescriptorChain>,
+ vring: &VringRwLock,
+ ) -> Result<bool> {
+ if requests.is_empty() {
+ return Ok(true);
+ }
+
+ for desc_chain in requests {
+ let atomic_mem = self.mem.as_ref().unwrap().memory();
+
+ let mut reader = desc_chain
+ .clone()
+ .reader(&atomic_mem)
+ .map_err(|_| Error::DescriptorReadFailed)?;
+
+ let mut writer = desc_chain
+ .clone()
+ .writer(&atomic_mem)
+ .map_err(|_| Error::DescriptorWriteFailed)?;
+
+ let request_header = reader
+ .read_obj::<VirtioCanHeader>()
+ .map_err(|_| Error::DescriptorReadFailed)?;
+
+ let data_len = request_header.length.to_native() as usize;
+ let mut request_data: Vec<u8> = Vec::new();
+ for _i in 0..data_len {
+ let data_byte = reader
+ .read_obj::<u8>()
+ .map_err(|_| Error::DescriptorReadFailed)?;
+ request_data.push(data_byte);
+ }
+
+ let request = VirtioCanFrame {
+ msg_type: request_header.msg_type,
+ can_id: request_header.can_id,
+ length: request_header.length,
+ reserved: request_header.reserved,
+ flags: request_header.flags,
+ sdu: {
+ let mut sdu_data: [u8; 64] = [0; 64];
+ sdu_data[..request_header.length.to_native() as usize]
+ .copy_from_slice(request_data.as_slice());
+ sdu_data
+ },
+ };
+ CanController::print_can_frame(request);
+
+ let response = if self.controller.read().unwrap().ctrl_state == CAN_CS_STOPPED {
+ trace!("Device is stopped!");
+ VIRTIO_CAN_RESULT_NOT_OK
+ } else {
+ let _response = match self.check_tx_frame(request) {
+ Ok(frame) => {
+ CanController::print_can_frame(frame);
+
+ // If the VIRTIO_CAN_F_CAN_LATE_TX_ACK is negotiated sent the
+ // frame and wait for it to be sent.
+ // TODO: Otherwise send it asynchronously.
+ match self.controller.write().unwrap().can_out(frame) {
+ Ok(_) => VIRTIO_CAN_RESULT_OK,
+ Err(_) => {
+ warn!("we got an error from controller send func");
+ VIRTIO_CAN_RESULT_NOT_OK
+ }
+ }
+ }
+ Err(e) => {
+ warn!("The tx frame had the following error: {}", e);
+ VIRTIO_CAN_RESULT_NOT_OK
+ }
+ };
+
+ // If the device cannot send the frame either because socket does not
+ // exist or the writing the frame fails for another unknown reason
+ // then behave as receiving a busoff error.
+ if _response == VIRTIO_CAN_RESULT_NOT_OK {
+ trace!("Change controller status to STOPPED and busoff to true");
+ // TODO: Trigger a config_change notification
+ self.controller.write().unwrap().config.status =
+ VIRTIO_CAN_S_CTRL_BUSOFF.into();
+ self.controller.write().unwrap().ctrl_state = CAN_CS_STOPPED;
+ }
+
+ _response
+ };
+
+ writer
+ .write_obj(response)
+ .map_err(|_| Error::DescriptorWriteFailed)?;
+
+ if vring
+ .add_used(desc_chain.head_index(), writer.bytes_written() as u32)
+ .is_err()
+ {
+ warn!("Couldn't return used descriptors to the ring");
+ }
+ }
+
+ Ok(true)
+ }
+
+ pub fn process_rx_requests(
+ &mut self,
+ requests: Vec<CanDescriptorChain>,
+ vring: &VringRwLock,
+ ) -> Result<bool> {
+ if requests.is_empty() {
+ return Ok(true);
+ }
+
+ // This flag, if true, requests a new tx buffer from the front-end
+ let mut req_rx_buf = false;
+
+ for desc_chain in requests {
+ let atomic_mem = self.mem.as_ref().unwrap().memory();
+ let mut writer = desc_chain
+ .clone()
+ .writer(&atomic_mem)
+ .map_err(|_| Error::DescriptorWriteFailed)?;
+
+ let response = match self.controller.write().unwrap().pop() {
+ Ok(item) => {
+ // If one or more frames have been received then request
+ // a new rx buffer.
+ req_rx_buf = true;
+ item
+ }
+ Err(QueueEmpty) => {
+ return Ok(req_rx_buf);
+ }
+ Err(_) => {
+ return Err(Error::HandleEventUnknown);
+ }
+ };
+
+ let can_rx = match self.check_rx_frame(response) {
+ Ok(frame) => frame,
+ Err(e) => {
+ warn!("The tx frame had the following error: {}", e);
+ return Err(e);
+ }
+ };
+
+ writer
+ .write_obj(can_rx)
+ .map_err(|_| Error::DescriptorWriteFailed)?;
+
+ if vring
+ .add_used(desc_chain.head_index(), writer.bytes_written() as u32)
+ .is_err()
+ {
+ warn!("Couldn't return used descriptors to the ring");
+ }
+ }
+
+ Ok(true)
+ }
+
+ /// Process the messages in the vring and dispatch replies
+ fn process_ctrl_queue(&mut self, vring: &VringRwLock) -> Result<()> {
+ let requests: Vec<CanDescriptorChain> = vring
+ .get_mut()
+ .get_queue_mut()
+ .iter(self.mem.as_ref().unwrap().memory())
+ .map_err(|_| Error::DescriptorNotFound)?
+ .collect();
+
+ if self.process_ctrl_requests(requests, vring)? {
+ // Send notification once all the requests are processed
+ vring
+ .signal_used_queue()
+ .map_err(|_| Error::NotificationFailed)?;
+ }
+ Ok(())
+ }
+
+ /// Process the messages in the vring and dispatch replies
+ fn process_tx_queue(&self, vring: &VringRwLock) -> Result<()> {
+ let requests: Vec<CanDescriptorChain> = vring
+ .get_mut()
+ .get_queue_mut()
+ .iter(self.mem.as_ref().unwrap().memory())
+ .map_err(|_| Error::DescriptorNotFound)?
+ .collect();
+
+ if self.process_tx_requests(requests, vring)? {
+ // Send notification once all the requests are processed
+ vring
+ .signal_used_queue()
+ .map_err(|_| Error::NotificationFailed)?;
+ }
+
+ Ok(())
+ }
+
+ /// Process the messages in the vring and dispatch replies
+ fn process_rx_queue(&mut self, vring: &VringRwLock) -> Result<()> {
+ let requests: Vec<CanDescriptorChain> = vring
+ .get_mut()
+ .get_queue_mut()
+ .iter(self.mem.as_ref().unwrap().memory())
+ .map_err(|_| Error::DescriptorNotFound)?
+ .collect();
+
+ if self.process_rx_requests(requests, vring)? {
+ // Send notification once all the requests are processed
+ vring
+ .signal_used_queue()
+ .map_err(|_| Error::NotificationFailed)?;
+ }
+ Ok(())
+ }
+
+ /// Set self's VringWorker.
+ pub(crate) fn set_vring_worker(
+ &self,
+ vring_worker: &Arc<VringEpollHandler<Arc<RwLock<VhostUserCanBackend>>>>,
+ ) {
+ let rx_event_fd = self.controller.read().unwrap().rx_event_fd.as_raw_fd();
+ vring_worker
+ .register_listener(rx_event_fd, EventSet::IN, u64::from(BACKEND_EFD))
+ .expect("Fail to register new handler");
+ }
+}
+
+/// VhostUserBackendMut trait methods
+impl VhostUserBackendMut for VhostUserCanBackend {
+ type Vring = VringRwLock;
+ type Bitmap = ();
+
+ fn num_queues(&self) -> usize {
+ NUM_QUEUES
+ }
+
+ fn max_queue_size(&self) -> usize {
+ QUEUE_SIZE
+ }
+
+ fn features(&self) -> u64 {
+ 1 << VIRTIO_F_VERSION_1
+ | 1 << VIRTIO_F_NOTIFY_ON_EMPTY
+ | 1 << VIRTIO_RING_F_EVENT_IDX
+ | 1 << VIRTIO_CAN_F_CAN_CLASSIC
+ | 1 << VIRTIO_CAN_F_CAN_FD
+ | 1 << VIRTIO_RING_F_INDIRECT_DESC
+ | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits()
+ }
+
+ fn acked_features(&mut self, _features: u64) {
+ self.acked_features = _features;
+ }
+
+ fn protocol_features(&self) -> VhostUserProtocolFeatures {
+ VhostUserProtocolFeatures::MQ
+ | VhostUserProtocolFeatures::CONFIG
+ | VhostUserProtocolFeatures::REPLY_ACK
+ }
+
+ fn get_config(&self, offset: u32, size: u32) -> Vec<u8> {
+ // SAFETY: The layout of the structure is fixed and can be initialized by
+ // reading its content from byte array.
+ unsafe {
+ from_raw_parts(
+ self.controller
+ .write()
+ .unwrap()
+ .config()
+ .as_slice()
+ .as_ptr()
+ .offset(offset as isize) as *const _ as *const _,
+ size as usize,
+ )
+ .to_vec()
+ }
+ }
+
+ fn set_event_idx(&mut self, enabled: bool) {
+ self.event_idx = enabled;
+ }
+
+ fn update_memory(&mut self, mem: GuestMemoryAtomic<GuestMemoryMmap>) -> IoResult<()> {
+ self.mem = Some(mem);
+ Ok(())
+ }
+
+ fn handle_event(
+ &mut self,
+ device_event: u16,
+ evset: EventSet,
+ vrings: &[VringRwLock],
+ _thread_id: usize,
+ ) -> IoResult<()> {
+ if evset != EventSet::IN {
+ return Err(Error::HandleEventNotEpollIn.into());
+ }
+
+ // If the device is in STOPPED state only TX and CTRL messages can be handled
+ if (self.controller.read().unwrap().ctrl_state == CAN_CS_STOPPED)
+ && ((device_event == RX_QUEUE) || (device_event == BACKEND_EFD))
+ {
+ trace!("Device is stopped!");
+ if device_event == BACKEND_EFD {
+ let _ = self.controller.write().unwrap().rx_event_fd.read();
+ }
+ return Ok(());
+ }
+
+ if device_event == RX_QUEUE && self.controller.write().unwrap().rx_is_empty() {
+ return Ok(());
+ };
+
+ let vring = if device_event != BACKEND_EFD {
+ &vrings[device_event as usize]
+ } else {
+ let _ = self.controller.write().unwrap().rx_event_fd.read();
+ &vrings[RX_QUEUE as usize]
+ };
+
+ 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_request_queue() until it stops finding
+ // new requests on the queue.
+ loop {
+ vring.disable_notification().unwrap();
+ match device_event {
+ CTRL_QUEUE => self.process_ctrl_queue(vring),
+ TX_QUEUE => self.process_tx_queue(vring),
+ RX_QUEUE => self.process_rx_queue(vring),
+ BACKEND_EFD => self.process_rx_queue(vring),
+ _ => Err(Error::HandleEventUnknown),
+ }?;
+ if !vring.enable_notification().unwrap() {
+ break;
+ }
+ }
+ } else {
+ // Without EVENT_IDX, a single call is enough.
+ match device_event {
+ CTRL_QUEUE => self.process_ctrl_queue(vring),
+ TX_QUEUE => self.process_tx_queue(vring),
+ RX_QUEUE => self.process_rx_queue(vring),
+ BACKEND_EFD => self.process_rx_queue(vring),
+ _ => Err(Error::HandleEventUnknown),
+ }?;
+ }
+ Ok(())
+ }
+
+ fn exit_event(&self, _thread_index: usize) -> Option<EventFd> {
+ self.exit_event.try_clone().ok()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::virtio_can::{VirtioCanCtrlResponse, VirtioCanTxResponse};
+ use std::mem::size_of;
+ use virtio_bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE};
+ use virtio_queue::{mock::MockSplitQueue, Descriptor, Queue};
+ use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryMmap, Le16, Le32};
+
+ #[test]
+ fn test_virtio_can_tx_response_default() {
+ let response = VirtioCanTxResponse::default();
+ assert_eq!(response.result, 0);
+ }
+
+ #[test]
+ fn test_virtio_can_ctrl_request_default() {
+ let request = VirtioCanCtrlRequest::default();
+ assert_eq!(request.msg_type, Le16::default());
+ }
+
+ #[test]
+ fn test_virtio_can_ctrl_response_default() {
+ let response = VirtioCanCtrlResponse::default();
+ assert_eq!(response.result, 0);
+ }
+
+ #[test]
+ fn test_virtio_can_frame_default() {
+ let frame = VirtioCanFrame::default();
+ assert_eq!(frame.msg_type, Le16::default());
+ assert_eq!(frame.length, Le16::default());
+ assert_eq!(frame.reserved, Le32::default());
+ assert_eq!(frame.flags, Le32::default());
+ assert_eq!(frame.can_id, Le32::default());
+ assert_eq!(frame.sdu, [0; 64]);
+ }
+
+ #[test]
+ fn test_virtio_can_empty_requests() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+ let mem = GuestMemoryAtomic::new(
+ GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(),
+ );
+ let vring = VringRwLock::new(mem.clone(), 0x1000).unwrap();
+
+ // Empty descriptor chain should be ignored
+ assert!(vu_can_backend
+ .process_rx_requests(Vec::<CanDescriptorChain>::new(), &vring)
+ .expect("Fail to examin empty rx vring"));
+ assert!(vu_can_backend
+ .process_tx_requests(Vec::<CanDescriptorChain>::new(), &vring)
+ .expect("Fail to examin empty tx vring"));
+ assert!(vu_can_backend
+ .process_ctrl_requests(Vec::<CanDescriptorChain>::new(), &vring)
+ .expect("Fail to examin empty ctrl vring"));
+ }
+
+ #[test]
+ fn test_virtio_can_empty_handle_request() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+ let mem = GuestMemoryAtomic::new(
+ GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(),
+ );
+ vu_can_backend.update_memory(mem.clone()).unwrap();
+
+ let vring = VringRwLock::new(mem, 0x1000).unwrap();
+ vring.set_queue_info(0x100, 0x200, 0x300).unwrap();
+ vring.set_queue_ready(true);
+ let list_vrings = [vring.clone(), vring.clone(), vring.clone(), vring.clone()];
+
+ vu_can_backend
+ .handle_event(RX_QUEUE, EventSet::IN, &list_vrings, 0)
+ .unwrap();
+
+ vu_can_backend
+ .handle_event(TX_QUEUE, EventSet::IN, &list_vrings, 0)
+ .unwrap();
+
+ vu_can_backend
+ .handle_event(CTRL_QUEUE, EventSet::IN, &list_vrings, 0)
+ .unwrap();
+
+ vu_can_backend
+ .handle_event(BACKEND_EFD, EventSet::IN, &list_vrings, 0)
+ .unwrap();
+ }
+
+ fn build_desc_chain_mem(
+ mem: &GuestMemoryMmap,
+ count: u16,
+ flags: Vec<u16>,
+ len: u32,
+ ) -> CanDescriptorChain {
+ let vq = MockSplitQueue::new(mem, 16);
+ let mut desc_vec = Vec::new();
+
+ //Create a descriptor chain with count descriptors.
+ for i in 0..count {
+ let desc_flags = if i < count - 1 {
+ flags[i as usize] | VRING_DESC_F_NEXT as u16
+ } else {
+ flags[i as usize] & !VRING_DESC_F_NEXT as u16
+ };
+
+ let desc = Descriptor::new((0x100 * (i + 1)) as u64, len, desc_flags, i + 1);
+ desc_vec.push(desc);
+ }
+
+ vq.add_desc_chains(&desc_vec, 0).unwrap();
+
+ // Create descriptor chain from pre-filled memory
+ vq.create_queue::<Queue>()
+ .unwrap()
+ .iter(GuestMemoryAtomic::new(mem.clone()).memory())
+ .unwrap()
+ .next()
+ .unwrap()
+ }
+
+ #[test]
+ fn test_virtio_can_ctrl_request() {
+ // Initialize can device and vhost-device-can backend
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Test 1: Since we provide only one write only, an error will be trigger
+ // the reader will have 0 data.
+ let can_mes_len = size_of::<VirtioCanCtrlRequest>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 1,
+ vec![VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_ctrl_requests(vec![desc_chain], &vring)
+ .unwrap_err(),
+ Error::DescriptorReadFailed
+ );
+
+ // Test 2: Two write only descriptors are available, so the reader
+ // will complain for having 0 data
+ let can_mes_len = size_of::<VirtioCanCtrlRequest>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![VRING_DESC_F_WRITE as u16, VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_ctrl_requests(vec![desc_chain], &vring)
+ .unwrap_err(),
+ Error::DescriptorReadFailed
+ );
+
+ // Test 3: The reader descriptor has two bytes of data
+ // but are initialized to 0 -> Unknown control message
+ let can_mes_len = size_of::<VirtioCanCtrlRequest>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![0, VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ assert!(vu_can_backend
+ .process_ctrl_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+
+ let can_frame_res = desc_chain
+ .memory()
+ .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64))
+ .map_err(|_| Error::DescriptorReadFailed)
+ .unwrap();
+
+ assert_eq!(VIRTIO_CAN_RESULT_NOT_OK, can_frame_res);
+
+ // Test 4: Successfull test for VIRTIO_CAN_SET_CTRL_MODE_START
+ let can_mes_len = size_of::<VirtioCanCtrlRequest>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![0, VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ let ctrl_msg: u16 = VIRTIO_CAN_SET_CTRL_MODE_START;
+ desc_chain
+ .memory()
+ .write_obj(ctrl_msg, vm_memory::GuestAddress(0x100_u64))
+ .unwrap();
+
+ // Write a value on the writer which does not represent known messages
+ desc_chain
+ .memory()
+ .write_obj(5, vm_memory::GuestAddress(0x200_u64))
+ .unwrap();
+
+ assert!(vu_can_backend
+ .process_ctrl_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+
+ let can_frame_res = desc_chain
+ .memory()
+ .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64))
+ .map_err(|_| Error::DescriptorReadFailed)
+ .unwrap();
+
+ assert_eq!(VIRTIO_CAN_RESULT_OK, can_frame_res);
+
+ // Test 5: Successfull test for VIRTIO_CAN_SET_CTRL_MODE_STOP
+ let can_mes_len = size_of::<VirtioCanCtrlRequest>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![0, VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ let ctrl_msg: u16 = VIRTIO_CAN_SET_CTRL_MODE_STOP;
+ desc_chain
+ .memory()
+ .write_obj(ctrl_msg, vm_memory::GuestAddress(0x100_u64))
+ .unwrap();
+
+ // Write a value on the writer which does not represent known messages
+ desc_chain
+ .memory()
+ .write_obj(5, vm_memory::GuestAddress(0x200_u64))
+ .unwrap();
+
+ assert!(vu_can_backend
+ .process_ctrl_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+
+ let can_frame_res = desc_chain
+ .memory()
+ .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64))
+ .map_err(|_| Error::DescriptorReadFailed)
+ .unwrap();
+
+ assert_eq!(VIRTIO_CAN_RESULT_OK, can_frame_res);
+
+ // Test 6: Since we provide only one read only, an error will be trigger
+ // about the writer having 0 data
+ let can_mes_len = size_of::<VirtioCanCtrlRequest>();
+ let desc_chain = build_desc_chain_mem(&mem, 1, vec![0], can_mes_len.try_into().unwrap());
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ let ctrl_msg: u16 = VIRTIO_CAN_SET_CTRL_MODE_START;
+ desc_chain
+ .memory()
+ .write_obj(ctrl_msg, vm_memory::GuestAddress(0x100_u64))
+ .unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_ctrl_requests(vec![desc_chain], &vring)
+ .unwrap_err(),
+ Error::DescriptorWriteFailed
+ );
+
+ // Test 7: Two read only descriptors are available, so the writer
+ // will complain for having 0 data
+ let can_mes_len = size_of::<VirtioCanCtrlRequest>();
+ let desc_chain = build_desc_chain_mem(&mem, 2, vec![0, 0], can_mes_len.try_into().unwrap());
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ let ctrl_msg: u16 = VIRTIO_CAN_SET_CTRL_MODE_STOP;
+ desc_chain
+ .memory()
+ .write_obj(ctrl_msg, vm_memory::GuestAddress(0x100_u64))
+ .unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_ctrl_requests(vec![desc_chain], &vring)
+ .unwrap_err(),
+ Error::DescriptorWriteFailed
+ );
+
+ // Test 8: If we interchange the write read descriptors then
+ // it will fail!
+
+ let can_mes_len = size_of::<VirtioCanCtrlRequest>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![VRING_DESC_F_WRITE as u16, 0],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ let ctrl_msg: u16 = VIRTIO_CAN_SET_CTRL_MODE_START;
+ desc_chain
+ .memory()
+ .write_obj(ctrl_msg, vm_memory::GuestAddress(0x200_u64))
+ .unwrap();
+
+ // Write a value on the writer which does not represent known messages
+ desc_chain
+ .memory()
+ .write_obj(5, vm_memory::GuestAddress(0x100_u64))
+ .unwrap();
+
+ assert!(vu_can_backend
+ .process_ctrl_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+
+ let can_frame_res = desc_chain
+ .memory()
+ .read_obj::<u8>(vm_memory::GuestAddress(0x100_u64))
+ .map_err(|_| Error::DescriptorReadFailed)
+ .unwrap();
+
+ assert_eq!(VIRTIO_CAN_RESULT_OK, can_frame_res);
+ }
+
+ #[test]
+ fn test_virtio_can_check_tx_unknown_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 0.into(),
+ length: 0.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::UnexpectedCanMsgType(0)
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_check_tx_can_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ // Test 1: UnexpectedClassicFlag
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 4.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::UnexpectedClassicFlag
+ );
+
+ // Test 2: Return the same length
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC);
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap().length,
+ frame.length
+ );
+
+ // Test 3: Return Error::InvalidCanLength(frame_length) when length is out-of-range (>8)
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 40.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::InvalidCanLength(40)
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_check_tx_canfd_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller).expect("Could not build vhucan device");
+
+ // Enable VIRTIO_CAN_F_CAN_FD feature
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_FD);
+
+ // Test 1: If no VIRTIO_CAN_FLAGS_FD in flag return UnexpectedClassicFlag
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 12.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::UnexpectedClassicFlag
+ );
+
+ // Test 2: If VIRTIO_CAN_FLAGS_FD is in flag check if return message has
+ // CAN_FRMF_TYPE_FD in flags.
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 12.into(),
+ reserved: 0.into(),
+ flags: VIRTIO_CAN_FLAGS_FD.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend
+ .check_tx_frame(frame)
+ .unwrap()
+ .flags
+ .to_native()
+ & CAN_FRMF_TYPE_FD,
+ CAN_FRMF_TYPE_FD
+ );
+
+ // Test 3: If 1 is not a valid CAN flag return InvalidCanFlags(1).
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 1.into(),
+ length: 12.into(),
+ reserved: 0.into(),
+ flags: 0x1.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::InvalidCanFlags(1)
+ );
+
+ // Test 4: receive frame with the same length if length is < 64 but not
+ // one of [12, 16, 20, 24, 32, 48, 64] -> InvalidCanLength(40)]
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 40.into(),
+ reserved: 0.into(),
+ flags: VIRTIO_CAN_FLAGS_FD.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::InvalidCanLength(40)
+ );
+
+ // Test 5: receive frame with length is > 64
+ // then -> InvalidCanLength(frame_length).
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 80.into(),
+ reserved: 0.into(),
+ flags: VIRTIO_CAN_FLAGS_FD.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::InvalidCanLength(80)
+ );
+
+ // Test 6: receive frame with length in [12, 16, 20, 24, 32, 48, 64]
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 48.into(),
+ reserved: 0.into(),
+ flags: VIRTIO_CAN_FLAGS_FD.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(vu_can_backend.check_tx_frame(frame).unwrap().length, 48);
+ }
+
+ #[test]
+ fn test_virtio_can_check_tx_rtr_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ // Test 1: Take a valid CAN / CANFD message and try to enable RTR in flags.
+ // the test should fail because VIRTIO_CAN_F_CAN_CLASSIC and
+ // VIRTIO_CAN_F_RTR_FRAMES are not negotiated.
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: VIRTIO_CAN_FLAGS_RTR.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::UnexpectedRtrFlag
+ );
+
+ // Test 2: Take a valid CAN / CANFD message and try to enable RTR in flags.
+ // the test should fail because VIRTIO_CAN_F_CAN_CLASSIC is not negotiated.
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_RTR_FRAMES);
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::UnexpectedRtrFlag
+ );
+
+ // Test 3: Take a valid CAN / CANFD message and try to enable RTR in flags.
+ // the test should succeed because VIRTIO_CAN_F_CAN_CLASSIC is negotiated.
+ vu_can_backend
+ .acked_features((1 << VIRTIO_CAN_F_RTR_FRAMES) | (1 << VIRTIO_CAN_F_CAN_CLASSIC));
+
+ assert_eq!(
+ vu_can_backend
+ .check_tx_frame(frame)
+ .unwrap()
+ .can_id
+ .to_native()
+ & CAN_RTR_FLAG,
+ CAN_RTR_FLAG
+ );
+
+ // Test 4: Take a valid CAN / CANFD message and try to enable RTR in flags.
+ // the test should fail because VIRTIO_CAN_F_CAN_CLASSIC is not negotiated,
+ // and RTR does not work with VIRTIO_CAN_F_CAN_FD.
+ vu_can_backend.acked_features((1 << VIRTIO_CAN_F_RTR_FRAMES) | (1 << VIRTIO_CAN_F_CAN_FD));
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::UnexpectedRtrFlag
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_check_tx_eff_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ // This test is valid for both CAN & CANFD messages, so for simplicity
+ // we will check only CAN case.
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC);
+
+ // Test 1: Received message should not have CAN_EFF_FLAG in can_id
+ let mut frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend
+ .check_tx_frame(frame)
+ .unwrap()
+ .can_id
+ .to_native()
+ & CAN_EFF_FLAG,
+ 0
+ );
+
+ // Test 2: If VIRTIO_CAN_FLAGS_EXTENDED is NOT enabled, CAN ID should
+ // be smaller than 11 bits
+ frame.can_id = (CAN_SFF_MASK + 1).into(); // CAN_SFF_MASK = 0x7FFU
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::UnexpectedCanId(11)
+ );
+
+ // Test 3: Received message should have CAN_EFF_MASK in can_id
+ frame.flags = VIRTIO_CAN_FLAGS_EXTENDED.into();
+ assert_eq!(
+ vu_can_backend
+ .check_tx_frame(frame)
+ .unwrap()
+ .can_id
+ .to_native()
+ & CAN_EFF_FLAG,
+ CAN_EFF_FLAG
+ );
+
+ // Test 4: If VIRTIO_CAN_FLAGS_EXTENDED is enabled, CAN ID should be
+ // smaller than 29 bits
+ frame.can_id = (CAN_EFF_MASK + 1).into(); // CAN_EFF_MASK = 0x1FFFFFFFU
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::UnexpectedCanId(29)
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_tx_general_tests() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Any number of descriptor different than 2 will generate an error
+ let count = 1;
+
+ // Test 1: Provide only write only descriptor -> Fail
+ let desc_chain = build_desc_chain_mem(&mem, count, vec![VRING_DESC_F_WRITE as u16], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap_err(),
+ Error::DescriptorReadFailed
+ );
+
+ // Test 2: Provide only read only descriptor -> Fail
+ let desc_chain = build_desc_chain_mem(&mem, 1, vec![0], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap_err(),
+ Error::DescriptorWriteFailed
+ );
+
+ // Test 3: Provide only write only descriptors -> Fail
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![VRING_DESC_F_WRITE as u16, VRING_DESC_F_WRITE as u16],
+ 0x200,
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap_err(),
+ Error::DescriptorReadFailed
+ );
+
+ // Test 4: Provide only read only descriptors -> Fail
+ let desc_chain = build_desc_chain_mem(&mem, 2, vec![0, 0], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+ assert_eq!(
+ vu_can_backend
+ .process_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap_err(),
+ Error::DescriptorWriteFailed
+ );
+
+ // Test 5: Provide correct descriptors but 0 size -> Fail
+ let desc_chain = build_desc_chain_mem(&mem, 2, vec![0, VRING_DESC_F_WRITE as u16], 0x0);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap_err(),
+ Error::DescriptorReadFailed
+ );
+
+ // Test 6: Provide correct descriptors and size
+ let can_mes_len = size_of::<VirtioCanFrame>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![0, VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+ assert!(vu_can_backend
+ .process_tx_requests(vec![desc_chain], &vring)
+ .unwrap());
+ }
+
+ #[test]
+ fn test_virtio_can_tx_device_stopped_test() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Enable VIRTIO_CAN_F_CAN_CLASSIC feature
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC);
+
+ let can_mes_len = size_of::<VirtioCanFrame>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![0, VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 123.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ desc_chain
+ .memory()
+ .write_obj(frame, vm_memory::GuestAddress(0x100_u64))
+ .unwrap();
+
+ desc_chain
+ .memory()
+ .write_obj(5, vm_memory::GuestAddress(0x200_u64))
+ .unwrap();
+
+ assert!(vu_can_backend
+ .process_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+
+ let can_frame_res = desc_chain
+ .memory()
+ .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64))
+ .map_err(|_| Error::DescriptorReadFailed)
+ .unwrap();
+
+ assert_eq!(VIRTIO_CAN_RESULT_NOT_OK, can_frame_res);
+ }
+
+ #[test]
+ fn test_virtio_can_tx_device_started_test_send_fail() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Enable VIRTIO_CAN_F_CAN_CLASSIC feature
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC);
+
+ // Start the device
+ controller.write().unwrap().ctrl_state = CAN_CS_STARTED;
+
+ let can_mes_len = size_of::<VirtioCanFrame>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![0, VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 123.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ desc_chain
+ .memory()
+ .write_obj(frame, vm_memory::GuestAddress(0x100_u64))
+ .unwrap();
+
+ desc_chain
+ .memory()
+ .write_obj(5, vm_memory::GuestAddress(0x200_u64))
+ .unwrap();
+
+ assert!(vu_can_backend
+ .process_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+
+ let can_frame_res = desc_chain
+ .memory()
+ .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64))
+ .map_err(|_| Error::DescriptorReadFailed)
+ .unwrap();
+
+ assert_eq!(VIRTIO_CAN_RESULT_NOT_OK, can_frame_res);
+ }
+
+ #[test]
+ fn test_virtio_can_tx_device_started_check_frame_fail() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Start the device
+ controller.write().unwrap().ctrl_state = CAN_CS_STARTED;
+
+ let can_mes_len = size_of::<VirtioCanFrame>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![0, VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 123.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ desc_chain
+ .memory()
+ .write_obj(frame, vm_memory::GuestAddress(0x100_u64))
+ .unwrap();
+
+ desc_chain
+ .memory()
+ .write_obj(5, vm_memory::GuestAddress(0x200_u64))
+ .unwrap();
+
+ assert!(vu_can_backend
+ .process_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+
+ let can_frame_res = desc_chain
+ .memory()
+ .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64))
+ .map_err(|_| Error::DescriptorReadFailed)
+ .unwrap();
+
+ assert_eq!(VIRTIO_CAN_RESULT_NOT_OK, can_frame_res);
+ }
+
+ #[test]
+ fn test_virtio_can_check_rx_err_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: CAN_ERR_FLAG.into(),
+ length: 0.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::RxFrameUnknownFail
+ );
+
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: (CAN_ERR_FLAG | CAN_ERR_BUSOFF).into(),
+ length: 0.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::BusoffRxFrame
+ );
+
+ assert_eq!(
+ controller.read().unwrap().config.status.to_native(),
+ VIRTIO_CAN_S_CTRL_BUSOFF
+ );
+ assert_eq!(controller.read().unwrap().ctrl_state, CAN_CS_STOPPED);
+ }
+
+ #[test]
+ fn test_virtio_can_check_rx_features_not_negotiated() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 0.into(),
+ length: 0.into(),
+ reserved: 0.into(),
+ flags: CAN_FRMF_TYPE_FD.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::UnexpectedFdFlag
+ );
+
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 0.into(),
+ length: 0.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::UnexpectedClassicFlag
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_check_rx_eff_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ // This test is valid for both CAN & CANFD messages, so for simplicity
+ // we will check only CAN case.
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC);
+
+ // Test 1: Received message should not have CAN_EFF_FLAG in can_id
+ let mut frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend
+ .check_rx_frame(frame)
+ .unwrap()
+ .can_id
+ .to_native()
+ & CAN_EFF_FLAG,
+ 0
+ );
+
+ // Test 2: Received message's can_id should be smaller than CAN_SFF_MASK
+ frame.can_id = (CAN_SFF_MASK + 1).into(); // CAN_SFF_MASK = 0x7FFU
+ assert!(
+ vu_can_backend
+ .check_rx_frame(frame)
+ .unwrap()
+ .can_id
+ .to_native()
+ < CAN_SFF_MASK,
+ );
+
+ // Test 3: Received message should have CAN_EFF_MASK in can_id
+ frame.can_id = CAN_EFF_FLAG.into();
+ assert_eq!(
+ vu_can_backend
+ .check_rx_frame(frame)
+ .unwrap()
+ .flags
+ .to_native()
+ & VIRTIO_CAN_FLAGS_EXTENDED,
+ VIRTIO_CAN_FLAGS_EXTENDED
+ );
+
+ // Test 4: Received message's can_id should be equal to CAN_EFF_MASK,
+ // since the 3 MSB have been zeroed.
+ frame.can_id = (frame.can_id.to_native() | CAN_EFF_MASK).into(); // CAN_EFF_MASK = 0x1FFFFFFFU
+ assert!(
+ vu_can_backend
+ .check_rx_frame(frame)
+ .unwrap()
+ .can_id
+ .to_native()
+ == CAN_EFF_MASK
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_check_rx_rtr_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: CAN_RTR_FLAG.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ // Test 1: Take a valid CAN / CANFD message and try to enable RTR in flags.
+ // the test should fail because VIRTIO_CAN_F_CAN_CLASSIC is not negotiated.
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC);
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::UnexpectedRtrFlag
+ );
+
+ // Test 2: Take a valid CAN / CANFD message and try to enable RTR in flags.
+ // the test should succeed because VIRTIO_CAN_F_CAN_CLASSIC is negotiated.
+ vu_can_backend
+ .acked_features((1 << VIRTIO_CAN_F_RTR_FRAMES) | (1 << VIRTIO_CAN_F_CAN_CLASSIC));
+
+ assert_eq!(
+ vu_can_backend
+ .check_rx_frame(frame)
+ .unwrap()
+ .flags
+ .to_native()
+ & VIRTIO_CAN_FLAGS_RTR,
+ VIRTIO_CAN_FLAGS_RTR
+ );
+
+ // Test 3: Take a valid CAN / CANFD message and try to enable RTR in flags.
+ // the test should fail because VIRTIO_CAN_F_CAN_CLASSIC is not negotiated,
+ // and RTR does not work with VIRTIO_CAN_F_CAN_FD.
+
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: CAN_RTR_FLAG.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: CAN_FRMF_TYPE_FD.into(), // Mark it as CAN FD frame
+ sdu: [0; 64],
+ };
+
+ vu_can_backend.acked_features((1 << VIRTIO_CAN_F_RTR_FRAMES) | (1 << VIRTIO_CAN_F_CAN_FD));
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::UnexpectedRtrFlag
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_check_rx_can_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ // Test 1: UnexpectedClassicFlag
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 4.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::UnexpectedClassicFlag
+ );
+
+ // Test 2: Return the same length
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC);
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap().length,
+ frame.length
+ );
+
+ // Test 3: Return the length equal to 8
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 40.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::InvalidCanLength(40)
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_check_rx_canfd_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ // Enable VIRTIO_CAN_F_CAN_FD feature
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_FD);
+
+ // Test 1: If no CAN_FRMF_TYPE_FD in flags return UnexpectedClassicFlag
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 0.into(),
+ length: 40.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::UnexpectedClassicFlag
+ );
+
+ // Test 2: If VIRTIO_CAN_FLAGS_FD is in flag check if return message has
+ // VIRTIO_CAN_FLAGS_FD in flags.
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 0.into(),
+ length: 48.into(),
+ reserved: 0.into(),
+ flags: CAN_FRMF_TYPE_FD.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend
+ .check_rx_frame(frame)
+ .unwrap()
+ .flags
+ .to_native()
+ & VIRTIO_CAN_FLAGS_FD,
+ VIRTIO_CAN_FLAGS_FD
+ );
+
+ // Test 3: receive frame with the same length if length is < 64
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap().length,
+ frame.length
+ );
+
+ // Test 4: receive frame with length out-of-range: length is > 64
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 0.into(),
+ length: 80.into(),
+ reserved: 0.into(),
+ flags: CAN_FRMF_TYPE_FD.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::InvalidCanLength(80)
+ );
+
+ // Test 5: receive frame with length out-of-range: length no in
+ // list: [12, 16, 20, 24, 32, 48, 64]
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 0.into(),
+ length: 62.into(),
+ reserved: 0.into(),
+ flags: CAN_FRMF_TYPE_FD.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::InvalidCanLength(62)
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_check_rx_canfd_vcan0() {
+ let controller =
+ CanController::new("vcan0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ // Enable VIRTIO_CAN_F_CAN_FD feature
+ vu_can_backend.acked_features((1 << VIRTIO_CAN_F_CAN_FD) | (1 << VIRTIO_CAN_F_CAN_CLASSIC));
+
+ // If VIRTIO_CAN_F_CAN_FD and VIRTIO_CAN_F_CAN_CLASSIC are negotiated
+ // and interface is "vcan0" check if return message has
+ // VIRTIO_CAN_FLAGS_FD in flags and has been treated as CANFD frame.
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 0.into(),
+ length: 48.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend
+ .check_rx_frame(frame)
+ .unwrap()
+ .flags
+ .to_native()
+ & VIRTIO_CAN_FLAGS_FD,
+ VIRTIO_CAN_FLAGS_FD
+ );
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap().length,
+ frame.length
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_rx_request() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Note: The following test are focusing only to simple CAN messages.
+
+ // Test 1: This should succeed because there is no element inserted in the
+ // CAN/FD frames' queue
+ let desc_chain = build_desc_chain_mem(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+ assert!(!vu_can_backend
+ .process_rx_requests(vec![desc_chain], &vring)
+ .unwrap(),);
+
+ // Test 2: This should fail because there is a simple CAN frame
+ // inserted in the CAN/FD frames' queue, but this does not
+ // pass the checks.
+
+ // Push a new can message into the can.rs queue
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 123.into(),
+ length: 64.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ controller.write().unwrap().push(frame).unwrap();
+
+ let desc_chain = build_desc_chain_mem(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+ assert_eq!(
+ vu_can_backend
+ .process_rx_requests(vec![desc_chain], &vring)
+ .unwrap_err(),
+ Error::UnexpectedClassicFlag
+ );
+
+ // Test 3: This should succeed because there is a simple CAN frame
+ // inserted in the CAN/FD frames' queue, and this does
+ // pass the checks.
+
+ // Enable VIRTIO_CAN_F_CAN_CLASSIC feature
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC);
+
+ // Push a new can message into the can.rs queue
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 123.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ controller.write().unwrap().push(frame).unwrap();
+
+ let desc_chain = build_desc_chain_mem(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+ assert!(vu_can_backend
+ .process_rx_requests(vec![desc_chain], &vring)
+ .unwrap());
+
+ // Test 4: This should fail because the descriptor is read-only
+ let desc_chain = build_desc_chain_mem(&mem, 1, vec![0], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ // Push a new can message into the can.rs queue
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 123.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+ controller.write().unwrap().push(frame).unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_rx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap_err(),
+ Error::DescriptorWriteFailed
+ );
+
+ // Test 5: This should fail because the descriptor length is less
+ // than VirtioCanFrame size.
+ let desc_chain = build_desc_chain_mem(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x10);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ // Push a new can message into the can.rs queue
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 123.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+ controller.write().unwrap().push(frame).unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_rx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap_err(),
+ Error::DescriptorWriteFailed
+ );
+ }
+}
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/virtio_can.rs b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/virtio_can.rs
new file mode 100755
index 00000000..768dc12a
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/virtio_can.rs
@@ -0,0 +1,144 @@
+// CAN virtio bindings
+//
+// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+//
+// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
+
+use vm_memory::{ByteValued, Le16, Le32};
+
+/// CAN FRAME Flags and Masks
+pub(crate) const CAN_EFF_FLAG: u32 = 0x80000000; // EFF/SFF is set in the MSB
+pub(crate) const CAN_RTR_FLAG: u32 = 0x40000000; // remote transmission request
+pub(crate) const CAN_ERR_FLAG: u32 = 0x20000000; // error message frame
+pub(crate) const CAN_SFF_MASK: u32 = 0x000007FF; // standard frame format (SFF)
+pub(crate) const CAN_EFF_MASK: u32 = 0x1FFFFFFF; // extended frame format (EFF)
+#[allow(dead_code)]
+pub(crate) const CAN_FRMF_BRS: u32 = 0x01; // bit rate switch (2nd bitrate for data)
+#[allow(dead_code)]
+pub(crate) const CAN_FRMF_ESI: u32 = 0x02; // error state ind. of transmitting node
+pub(crate) const CAN_FRMF_TYPE_FD: u32 = 0x10; // internal bit ind. of CAN FD frame
+pub(crate) const CAN_ERR_BUSOFF: u32 = 0x00000040; // bus off
+
+/// CANFD frame's valid data lengths
+pub(crate) const CANFD_VALID_LENGTHS: [u32; 7] = [12, 16, 20, 24, 32, 48, 64];
+
+/// CAN controller states
+pub(crate) const CAN_CS_STARTED: u8 = 0x01;
+pub(crate) const CAN_CS_STOPPED: u8 = 0x02;
+
+/// CAN flags to determine type of CAN FRAME Id
+pub(crate) const VIRTIO_CAN_FLAGS_EXTENDED: u32 = 0x8000;
+pub(crate) const VIRTIO_CAN_FLAGS_FD: u32 = 0x4000;
+pub(crate) const VIRTIO_CAN_FLAGS_RTR: u32 = 0x2000;
+
+pub(crate) const VIRTIO_CAN_FLAGS_VALID_MASK: u32 =
+ VIRTIO_CAN_FLAGS_EXTENDED | VIRTIO_CAN_FLAGS_FD | VIRTIO_CAN_FLAGS_RTR;
+
+pub(crate) const VIRTIO_CAN_TX: u16 = 0x0001;
+pub(crate) const VIRTIO_CAN_RX: u16 = 0x0101;
+
+/// Feature bit numbers
+pub const VIRTIO_CAN_F_CAN_CLASSIC: u16 = 0;
+pub const VIRTIO_CAN_F_CAN_FD: u16 = 1;
+pub const VIRTIO_CAN_S_CTRL_BUSOFF: u16 = 2; /* Controller BusOff */
+#[allow(dead_code)]
+pub const VIRTIO_CAN_F_LATE_TX_ACK: u16 = 2;
+pub const VIRTIO_CAN_F_RTR_FRAMES: u16 = 3;
+
+/// Possible values of the status field
+pub const VIRTIO_CAN_RESULT_OK: u8 = 0x0;
+pub const VIRTIO_CAN_RESULT_NOT_OK: u8 = 0x1;
+
+/// CAN Control messages
+pub const VIRTIO_CAN_SET_CTRL_MODE_START: u16 = 0x0201;
+pub const VIRTIO_CAN_SET_CTRL_MODE_STOP: u16 = 0x0202;
+
+/// Virtio Can Configuration
+#[derive(Copy, Clone, Debug, Default, PartialEq)]
+#[repr(C)]
+pub(crate) struct VirtioCanConfig {
+ /// CAN controller status
+ pub(crate) status: Le16,
+}
+
+// SAFETY: The layout of the structure is fixed and can be initialized by
+// reading its content from byte array.
+unsafe impl ByteValued for VirtioCanConfig {}
+
+/// Virtio CAN Request / Response messages
+///
+/// The response message is a stream of bytes, where first byte represents the
+/// status, and rest is message specific data.
+
+#[derive(Copy, Clone, Default)]
+#[repr(C)]
+pub struct VirtioCanTxResponse {
+ pub result: i8,
+}
+
+// SAFETY: The layout of the structure is fixed and can be initialized by
+// reading its content from byte array.
+unsafe impl ByteValued for VirtioCanTxResponse {}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+#[repr(C)]
+pub struct VirtioCanFrame {
+ pub msg_type: Le16,
+ pub length: Le16, /* 0..8 CC, 0..64 CAN­FD, 0..2048 CAN­XL, 12 bits */
+ pub reserved: Le32, /* May be needed in part for CAN XL priority */
+ pub flags: Le32,
+ pub can_id: Le32,
+ pub sdu: [u8; 64],
+}
+
+// SAFETY: The layout of the structure is fixed and can be initialized by
+// reading its content from byte array.
+unsafe impl ByteValued for VirtioCanFrame {}
+
+impl Default for VirtioCanFrame {
+ fn default() -> Self {
+ VirtioCanFrame {
+ msg_type: Le16::default(),
+ length: Le16::default(),
+ reserved: Le32::default(),
+ flags: Le32::default(),
+ can_id: Le32::default(),
+ sdu: [0; 64], // Initialize "sdu" with default value (0 in this case)
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Default)]
+#[repr(C)]
+pub struct VirtioCanHeader {
+ pub msg_type: Le16,
+ pub length: Le16, /* 0..8 CC, 0..64 CAN­FD, 0..2048 CAN­XL, 12 bits */
+ pub reserved: Le32, /* May be needed in part for CAN XL priority */
+ pub flags: Le32,
+ pub can_id: Le32,
+}
+
+// SAFETY: The layout of the structure is fixed and can be initialized by
+// reading its content from byte array.
+unsafe impl ByteValued for VirtioCanHeader {}
+
+#[derive(Copy, Clone, Default)]
+#[repr(C)]
+pub struct VirtioCanCtrlRequest {
+ pub msg_type: Le16,
+}
+
+// SAFETY: The layout of the structure is fixed and can be initialized by
+// reading its content from byte array.
+unsafe impl ByteValued for VirtioCanCtrlRequest {}
+
+#[derive(Copy, Clone, Default)]
+#[repr(C)]
+pub struct VirtioCanCtrlResponse {
+ pub result: i8,
+}
+
+// SAFETY: The layout of the structure is fixed and can be initialized by
+// reading its content from byte array.
+unsafe impl ByteValued for VirtioCanCtrlResponse {}