From 0b813fa1224e4c31caf4dbab0bd8146c4d038f4e Mon Sep 17 00:00:00 2001 From: Timos Ampelikiotis Date: Thu, 3 Oct 2024 11:00:30 +0300 Subject: 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 --- .../files/vhost-device-can-0.1.0/src/backend.rs | 268 +++ .../files/vhost-device-can-0.1.0/src/can.rs | 393 ++++ .../files/vhost-device-can-0.1.0/src/main.rs | 96 + .../files/vhost-device-can-0.1.0/src/vhu_can.rs | 2145 ++++++++++++++++++++ .../files/vhost-device-can-0.1.0/src/virtio_can.rs | 144 ++ 5 files changed, 3046 insertions(+) create mode 100755 meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/backend.rs create mode 100755 meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/can.rs create mode 100755 meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/main.rs create mode 100755 meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/vhu_can.rs create mode 100755 meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/virtio_can.rs (limited to 'meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src') 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 +// +// 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 = std::result::Result; + +#[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), +} + +#[derive(PartialEq, Debug)] +pub struct VuCanConfig { + pub socket_path: PathBuf, + pub socket_count: u32, + pub can_devices: Vec, +} + +impl VuCanConfig { + pub fn generate_socket_paths(&self) -> Vec { + 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 +// +// 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 = std::result::Result; + +#[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, + pub rx_event_fd: EventFd, + rx_fifo: Queue, + pub status: bool, + pub ctrl_state: u8, +} + +impl CanController { + // Creates a new controller corresponding to `device`. + pub(crate) fn new(can_name: String) -> Result { + 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>) -> JoinHandle> { + 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 { + 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(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>) -> 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 = 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 +// +// 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 = std::result::Result; +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> { + 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 for VuCanConfig { + type Error = Error; + + fn try_from(args: CanArgs) -> Result { + 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 +// +// 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 = std::result::Result; + +#[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 for io::Error { + fn from(e: Error) -> Self { + io::Error::new(io::ErrorKind::Other, e) + } +} + +pub(crate) struct VhostUserCanBackend { + controller: Arc>, + acked_features: u64, + event_idx: bool, + pub(crate) exit_event: EventFd, + mem: Option>, +} + +type CanDescriptorChain = DescriptorChain>>; + +impl VhostUserCanBackend { + pub(crate) fn new(controller: Arc>) -> Result { + 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 { + 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 { + 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, + vring: &VringRwLock, + ) -> Result { + 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::() + .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, + vring: &VringRwLock, + ) -> Result { + 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::() + .map_err(|_| Error::DescriptorReadFailed)?; + + let data_len = request_header.length.to_native() as usize; + let mut request_data: Vec = Vec::new(); + for _i in 0..data_len { + let data_byte = reader + .read_obj::() + .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, + vring: &VringRwLock, + ) -> Result { + 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 = 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 = 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 = 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>>>, + ) { + 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 { + // 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) -> 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 { + 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::::new(), &vring) + .expect("Fail to examin empty rx vring")); + assert!(vu_can_backend + .process_tx_requests(Vec::::new(), &vring) + .expect("Fail to examin empty tx vring")); + assert!(vu_can_backend + .process_ctrl_requests(Vec::::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, + 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::() + .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::(); + 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::(); + 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::(); + 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::(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::(); + 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::(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::(); + 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::(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::(); + 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::(); + 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::(); + 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::(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::(); + 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::(); + 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::(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::(); + 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::(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::(); + 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::(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 +// +// 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 {} -- cgit 1.2.3-korg