diff options
author | Michele Paolino <m.paolino@virtualopensystems.com> | 2023-11-14 18:07:09 +0100 |
---|---|---|
committer | Michele Paolino <m.paolino@virtualopensystems.com> | 2023-12-13 10:27:55 +0000 |
commit | b05512b81bbe1c90b71da9c571e742f162fc0575 (patch) | |
tree | dea9b67ff4dae3e227943382dd7b0b0565dc39d1 /meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/vhu_can.rs | |
parent | 31d4011207be008333c1945f18bb922b13816462 (diff) |
CAN, GPIO, RNG vhost-devices for virtio-loopback [v6]quillback_16.92.0quillback/16.92.016.92.0
This patch adds key components of the viritio-loopback architecture:
- kernel-module-virtio-loopback: the virtio loopback kernel driver
- CAN, GPIO and RNG vhost-user devices from meta-virtualization (commit a215d8320edee0a317a6511e7e2efa5bba867486)
Notes:
- libgpiod, comes from meta-openembedded commit 3029554ceb0b0bb52a8d8ec3f0a75c5113662fe6
- cleaned eg-virt from unused drivers (kernel-module-virtio-video)
Bug-AGL: SPEC-4834
V2 changes:
- related meta-virtualization commit message added in the cover letter
- updated libgpio recipe to v2.1
- SPEC reference added in cover letter
v3
- add vhost-device-can preliminary version. This is placed here with
the objective to share the link when proposing the new device to
the rust-vmm/vhost-device community
- remove cargo-update-recipe-crates includes in bb file because it is
not supported by the rust mixin layer
- vhost-device folder README changes
v4
- fixed libgpiod required version
- tested ref hw and qemu x86/64 builds
- vsock, scsi and i2c rust devices removed from the build as they
are not yet integrated in virtiod-loopback
- cleaned-up kernel modules kernel-module-virtio-video and
gstreamer1.0-plugins-bad
- virtio-loopback-driver set to 2-or-later
v5
- Merge with Jan-Simon version v4:
- remove broken kernel-module-virtio-video
- use FEATURE_PACKAGES instead of IMAGE_INSTALL:append
- rename virtio-loopback-driver.bb to
kernel-module-virtio-loopback_git.bb for consistency
v6
- adding version in the title
- removing MODULE_GIT_REPOSITORY in kernel-modules
Change-Id: Id6cc58e777b9edad03b6c50d0dddaac8601edeaf
Signed-off-by: Michele Paolino <m.paolino@virtualopensystems.com>
Signed-off-by: Jan-Simon Moeller <jsmoeller@linuxfoundation.org>
Diffstat (limited to 'meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/vhu_can.rs')
-rw-r--r-- | meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/vhu_can.rs | 732 |
1 files changed, 732 insertions, 0 deletions
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/vhu_can.rs b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/vhu_can.rs new file mode 100644 index 00000000..6aa12488 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/vhu_can.rs @@ -0,0 +1,732 @@ +// vhost device can +// +// Copyright 2023 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::{warn, error}; +use std::mem::size_of; +use std::slice::from_raw_parts; +use std::sync::{Arc, RwLock}; +use std::{ + convert, + io::{self, Result as IoResult}, +}; +use std::os::fd::AsRawFd; +use thiserror::Error as ThisError; +use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures}; +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, Bytes, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryLoadGuard, + GuestMemoryMmap, Le16, Le32, +}; +use vmm_sys_util::epoll::EventSet; +use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; +use crate::can::{ + CanController, CAN_EFF_FLAG, CAN_RTR_FLAG, CAN_ERR_FLAG, CAN_SFF_MASK, + CAN_EFF_MASK, VIRTIO_CAN_FLAGS_FD, VIRTIO_CAN_FLAGS_RTR, + VIRTIO_CAN_FLAGS_EXTENDED, VIRTIO_CAN_TX, VIRTIO_CAN_RX, + CAN_FRMF_TYPE_FD, CAN_ERR_BUSOFF, +}; +use vhost_user_backend::VringEpollHandler; + +/// 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_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_STATUS_OK: u8 = 0x0; +pub const VIRTIO_CAN_STATUS_ERR: u8 = 0x1; + +/// CAN Control messages +const VIRTIO_CAN_SET_CTRL_MODE_START: u16 = 0x0201; +const VIRTIO_CAN_SET_CTRL_MODE_STOP: u16 = 0x0202; + +/// 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 = 4; + +type Result<T> = std::result::Result<T, Error>; + +#[derive(Copy, Clone, Debug, PartialEq, ThisError)] +/// Errors related to vhost-device-gpio-daemon. +pub(crate) enum Error { + #[error("Failed to handle event, didn't match EPOLLIN")] + HandleEventNotEpollIn, + #[error("Failed to handle unknown event")] + HandleEventUnknown, + #[error("Received unexpected write only descriptor at index {0}")] + UnexpectedWriteOnlyDescriptor(usize), + #[error("Received unexpected readable descriptor at index {0}")] + UnexpectedReadableDescriptor(usize), + #[error("Invalid descriptor count {0}")] + UnexpectedDescriptorCount(usize), + #[error("Invalid descriptor size, expected: {0}, found: {1}")] + UnexpectedDescriptorSize(usize, u32), + #[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("Classic CAN frames not negotiated")] + UnexpectedClassicFlag, +} + +impl convert::From<Error> for io::Error { + fn from(e: Error) -> Self { + io::Error::new(io::ErrorKind::Other, e) + } +} + +/// 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)] +struct VirtioCanTxResponse { + 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)] +#[repr(C)] +pub struct VirtioCanFrame { + pub msg_type: Le16, + pub length: Le16, /* 0..8 CC, 0..64 CANFD, 0..2048 CANXL, 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], +} + +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 "asd" with default value (0 in this case) + } + } +} + +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioCanFrame {} + +#[derive(Copy, Clone, Default)] +#[repr(C)] +struct VirtioCanCtrlRequest { + 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)] +struct VirtioCanCtrlResponse { + 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 {} + +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: 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 process_ctrl_requests( + &self, + requests: Vec<CanDescriptorChain>, + vring: &VringRwLock + ) -> Result<bool> { + dbg!("process_ctrl_requests"); + + if requests.is_empty() { + return Ok(true); + } + + for desc_chain in requests { + let descriptors: Vec<_> = desc_chain.clone().collect(); + + if descriptors.len() < 1 { + warn!("Error::UnexpectedDescriptorCount"); + return Err(Error::UnexpectedDescriptorCount(descriptors.len())); + } + + println!("descriptors.len(): {:?}", descriptors.len()); + + let desc_request = descriptors[0]; + if desc_request.is_write_only() { + warn!("Error::UnexpectedWriteOnlyDescriptor"); + return Err(Error::UnexpectedWriteOnlyDescriptor(0)); + } + + if desc_request.len() as usize != size_of::<VirtioCanCtrlRequest>() { + println!("UnexpectedDescriptorSize, len = {:?}", desc_request.len()); + return Err(Error::UnexpectedDescriptorSize( + size_of::<VirtioCanCtrlRequest>(), + desc_request.len(), + )); + } + + let request = desc_chain + .memory() + .read_obj::<VirtioCanCtrlRequest>(desc_request.addr()) + .map_err(|_| Error::DescriptorReadFailed)?; + + + match request.msg_type.into() { + VIRTIO_CAN_SET_CTRL_MODE_START => { + println!("VIRTIO_CAN_SET_CTRL_MODE_START"); + //vcan->busoff = false; + Ok(()) + } + VIRTIO_CAN_SET_CTRL_MODE_STOP => { + println!("VIRTIO_CAN_SET_CTRL_MODE_STOP"); + //vcan->busoff = false; + Ok(()) + } + _ => { + println!("Ctrl queue: msg type 0x{:?} unknown", request.msg_type); + return Err(Error::HandleEventUnknown.into()) + }, + }?; + + let desc_response = descriptors[1]; + if !desc_response.is_write_only() { + println!("This is not wirtable"); + return Err(Error::UnexpectedReadableDescriptor(1)); + } + + let response = VIRTIO_CAN_STATUS_OK; + + desc_chain + .memory() + .write_slice(response.as_slice(), desc_response.addr()) + .map_err(|_| Error::DescriptorWriteFailed)?; + + if vring.add_used(desc_chain.head_index(), desc_response.len()).is_err() { + println!("Couldn't return used descriptors to the ring"); + warn!("Couldn't return used descriptors to the ring"); + } + } + + Ok(true) + } + + fn process_tx_requests( + &self, + requests: Vec<CanDescriptorChain>, + vring: &VringRwLock + ) -> Result<bool> { + dbg!("process_tx_requests"); + + if requests.is_empty() { + return Ok(true); + } + + println!("requests.len: {:?}", requests.len()); + for desc_chain in requests { + let descriptors: Vec<_> = desc_chain.clone().collect(); + + if descriptors.len() != 2 { + println!("Error::UnexpectedDescriptorCount"); + return Err(Error::UnexpectedDescriptorCount(descriptors.len())); + } + + let desc_request = descriptors[0]; + if desc_request.is_write_only() { + println!("Error::UnexpectedReadOnlyDescriptor"); + return Err(Error::UnexpectedWriteOnlyDescriptor(0)); + } + + if desc_request.len() as usize != size_of::<VirtioCanFrame>() { + println!("Tx UnexpectedDescriptorSize, len = {:?}", desc_request.len()); + //return Err(Error::UnexpectedDescriptorSize( + // size_of::<VirtioCanFrame>(), + // desc_request.len(), + //)); + } + + let request = desc_chain + .memory() + .read_obj::<VirtioCanFrame>(desc_request.addr()) + .map_err(|_| Error::DescriptorReadFailed)?; + + CanController::print_can_frame(request); + + let msg_type = request.msg_type.to_native(); + let mut can_id = request.can_id.to_native(); + let mut flags = request.flags.to_native(); + let mut length = request.length.to_native(); + + if msg_type != VIRTIO_CAN_TX { + warn!("TX: Message type 0x{:x} unknown\n", msg_type); + return Err(Error::UnexpectedCanMsgType(msg_type)); + } + + if (flags & VIRTIO_CAN_FLAGS_FD) != 0 { + if length > 64 { + println!("Cut sdu_len from {:?} to 64\n", request.length); + length = 64; + } + } else { + if length > 8 { + println!("Cut sdu_len from {:?} to 8\n", request.length); + length = 8; + } + } + + /* + * Copy Virtio frame structure to qemu frame structure and + * check while doing this whether the frame type was negotiated + */ + if (flags & VIRTIO_CAN_FLAGS_EXTENDED) != 0 { + flags &= CAN_EFF_MASK; + flags |= CAN_EFF_FLAG; + } else { + flags &= CAN_SFF_MASK; + } + + if (flags & 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 |= flags | CAN_RTR_FLAG; + } + + if (flags & 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 { + if !self.check_features(VIRTIO_CAN_F_CAN_CLASSIC) { + warn!("TX: Classic frames not negotiated\n"); + return Err(Error::UnexpectedClassicFlag); + } + flags = 0; + } + + let mut corrected_request = VirtioCanFrame::default(); + corrected_request.msg_type = msg_type.into(); + corrected_request.can_id = can_id.into(); + corrected_request.flags = flags.into(); + corrected_request.length = length.into(); + corrected_request.sdu.copy_from_slice(&request.sdu[0..64]); + + let desc_response = descriptors[1]; + if !desc_response.is_write_only() { + println!("Error::UnexpectedWriteOnlyDescriptor"); + return Err(Error::UnexpectedReadableDescriptor(1)); + } + + let response = match self.controller.write().unwrap().operation(corrected_request) { + Ok(result) => { + result + } + Err(_) => { + warn!("We got an error from controller send func"); + VIRTIO_CAN_STATUS_ERR + } + }; + + desc_chain + .memory() + .write_slice(response.as_slice(), desc_response.addr()) + .map_err(|_| Error::DescriptorWriteFailed)?; + + if vring.add_used(desc_chain.head_index(), desc_response.len()).is_err() { + println!("Couldn't return used descriptors to the ring"); + warn!("Couldn't return used descriptors to the ring"); + } + } + + Ok(true) + } + + fn process_rx_requests( + &mut self, + requests: Vec<CanDescriptorChain>, + vring: &VringRwLock + ) -> Result<bool> { + dbg!("process_rx_requests"); + + if requests.is_empty() { + return Ok(true); + } + + let desc_chain = &requests[0]; + let descriptors: Vec<_> = desc_chain.clone().collect(); + + if descriptors.len() != 1 { + println!("Error::UnexpectedDescriptorCount"); + return Err(Error::UnexpectedDescriptorCount(descriptors.len())); + } + + let desc_response = descriptors[0]; + if !desc_response.is_write_only() { + return Err(Error::UnexpectedReadableDescriptor(1)); + } + + let mut response = match self.controller.write().unwrap().pop() { + Ok(item) => item, + Err(_) => return Err(Error::HandleEventUnknown), + }; + + CanController::print_can_frame(response); + + if (response.can_id.to_native() & CAN_ERR_FLAG) != 0 { + if (response.can_id.to_native() & CAN_ERR_BUSOFF) != 0 { + warn!("Got BusOff error frame, device does a local bus off\n"); + //vcan->busoff = true; + } else { + println!("Dropping error frame 0x{:x}\n", response.can_id.to_native()); + } + return Ok(true); + } + + let mut can_rx = VirtioCanFrame::default(); + can_rx.msg_type = VIRTIO_CAN_RX.into(); + can_rx.can_id = response.can_id; + can_rx.length = response.length; + can_rx.flags = (can_rx.flags.to_native() | VIRTIO_CAN_FLAGS_FD).into(); + + if (response.flags.to_native() & 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); + } + } + if (response.can_id.to_native() & CAN_RTR_FLAG) != 0 && + !self.check_features(VIRTIO_CAN_F_RTR_FRAMES) { + warn!("Drop non-supported RTR frame"); + return Err(Error::UnexpectedRtrFlag); + } + + if (response.can_id.to_native() & CAN_EFF_FLAG) != 0 { + can_rx.flags = VIRTIO_CAN_FLAGS_EXTENDED.into(); + can_rx.can_id = (response.can_id.to_native() & CAN_EFF_MASK).into(); + } else { + can_rx.can_id = (response.can_id.to_native() & CAN_SFF_MASK).into(); + } + if (response.can_id.to_native() & CAN_RTR_FLAG) != 0 { + can_rx.flags = (can_rx.flags.to_native() & VIRTIO_CAN_FLAGS_RTR).into(); + } + + // HACK AHEAD: Vcan can not be comfigured as CANFD interface, but 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_in_name = self.controller.read().unwrap().can_in_name.clone(); + if self.check_features(VIRTIO_CAN_F_CAN_FD) && + response.length.to_native() > 8 && can_in_name == "vcan0" { + response.flags = (response.flags.to_native() | CAN_FRMF_TYPE_FD).into(); + warn!("\n\n\nCANFD VCAN0\n\n"); + } + + if (response.flags.to_native() & CAN_FRMF_TYPE_FD) != 0 { + can_rx.flags = (can_rx.flags.to_native() | VIRTIO_CAN_FLAGS_FD).into(); + if response.length.to_native() > 64 { + warn!("%s(): Cut length from {} to 64\n", response.length.to_native()); + can_rx.length = 64.into(); + } + } else { + if response.length.to_native() > 8 { + warn!("%s(): Cut length from {} to 8\n", response.length.to_native()); + can_rx.length = 8.into(); + } + } + + can_rx.sdu.copy_from_slice(&response.sdu[0..64]); + CanController::print_can_frame(can_rx); + + desc_chain + .memory() + .write_slice(can_rx.as_slice(), desc_response.addr()) + .map_err(|_| Error::DescriptorWriteFailed)?; + + if vring.add_used(desc_chain.head_index(), desc_response.len()).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<()> { + dbg!("process_ctrl_queue"); + 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<()> { + dbg!("process_tx_queue"); + 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(|_| { + println!("signal_used_queue error"); + Error::NotificationFailed + })?; + } + + Ok(()) + } + + /// Process the messages in the vring and dispatch replies + fn process_rx_queue(&mut self, vring: &VringRwLock) -> Result<()> { + dbg!("process_rx_queue"); + 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(|_| { + println!("NotificationFailed"); + Error::NotificationFailed + })?; + } + Ok(()) + } + + fn process_rx_queue_dump(&mut self, _vring: &VringRwLock) -> Result<()> { + dbg!("Do nothing, if you reach that point!"); + Ok(()) + } + + /// Set self's VringWorker. + pub(crate) fn set_vring_worker( + &self, + vring_worker: &Arc<VringEpollHandler<Arc<RwLock<VhostUserCanBackend>>, VringRwLock, ()>>, + ) { + 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)) + 4u64 as u64) + .unwrap(); + } +} + +/// VhostUserBackendMut trait methods +impl VhostUserBackendMut<VringRwLock, ()> + for VhostUserCanBackend +{ + fn num_queues(&self) -> usize { + println!("num_queues: {:?}", NUM_QUEUES); + NUM_QUEUES + } + + fn max_queue_size(&self) -> usize { + println!("max_queue_size: {:?}", QUEUE_SIZE); + QUEUE_SIZE + } + + fn features(&self) -> u64 { + // this matches the current libvhost defaults except VHOST_F_LOG_ALL + let features = 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(); + + println!("vhu_can->features: {:x}", features); + features + } + + fn acked_features(&mut self, _features: u64) { + println!("\nacked_features: 0x{:x}\n", _features); + self.acked_features = _features; + } + + fn protocol_features(&self) -> VhostUserProtocolFeatures { + let protocol_features = VhostUserProtocolFeatures::MQ + | VhostUserProtocolFeatures::CONFIG + | VhostUserProtocolFeatures::REPLY_ACK; + //| VhostUserProtocolFeatures::STATUS + + println!("protocol_features: {:x}", protocol_features); + protocol_features + } + + 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. + dbg!("vhu_can->get_config"); + 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) { + dbg!(self.event_idx = enabled); + } + + fn update_memory(&mut self, mem: GuestMemoryAtomic<GuestMemoryMmap>) -> IoResult<()> { + dbg!("update_memory\n"); + self.mem = Some(mem); + Ok(()) + } + + fn handle_event( + &mut self, + device_event: u16, + evset: EventSet, + vrings: &[VringRwLock], + _thread_id: usize, + ) -> IoResult<bool> { + dbg!("\nhandle_event:"); + + if evset != EventSet::IN { + return Err(Error::HandleEventNotEpollIn.into()); + } + if device_event == RX_QUEUE { + println!("RX_QUEUE\n"); + return Ok(false); + }; + let vring = if device_event != BACKEND_EFD { + &vrings[device_event as usize] + } else { + println!("BACKEND_EFD\n"); + 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 queue_idx { + match device_event { + CTRL_QUEUE => self.process_ctrl_queue(vring), + TX_QUEUE => self.process_tx_queue(vring), + RX_QUEUE => self.process_rx_queue_dump(vring), + BACKEND_EFD => self.process_rx_queue(vring), + _ => Err(Error::HandleEventUnknown.into()), + }?; + 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_dump(vring), + BACKEND_EFD => self.process_rx_queue(vring), + _ => Err(Error::HandleEventUnknown.into()), + }?; + } + Ok(false) + } + + fn exit_event(&self, _thread_index: usize) -> Option<EventFd> { + dbg!("exit_event\n"); + self.exit_event.try_clone().ok() + } +} + |