From b05512b81bbe1c90b71da9c571e742f162fc0575 Mon Sep 17 00:00:00 2001 From: Michele Paolino Date: Tue, 14 Nov 2023 18:07:09 +0100 Subject: CAN, GPIO, RNG vhost-devices for virtio-loopback [v6] 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 Signed-off-by: Jan-Simon Moeller --- .../vhost-device-can-0.1.0/CHANGELOG.md | 15 + .../vhost-device-can-0.1.0/Cargo.toml | 35 + .../vhost-device-can-0.1.0/LICENSE | 0 .../vhost-device-can-0.1.0/README.md | 66 ++ .../vhost-device-can-0.1.0/src/backend.rs | 170 +++++ .../vhost-device-can-0.1.0/src/can.rs | 264 ++++++++ .../vhost-device-can-0.1.0/src/main.rs | 26 + .../vhost-device-can-0.1.0/src/vhu_can.rs | 732 +++++++++++++++++++++ .../vhost-device-can/vhost-device-can-crates.inc | 83 +++ .../vhost-device-can/vhost-device-can_0.1.0.bb | 18 + .../recipes-extended/vhost-device/README.md | 12 + .../vhost-device/vhost-device-gpio-crates.inc | 184 ++++++ .../vhost-device/vhost-device-gpio_0.1.0.bb | 22 + .../vhost-device/vhost-device-rng-crates.inc | 158 +++++ .../vhost-device/vhost-device-rng_0.1.0.bb | 16 + 15 files changed, 1801 insertions(+) create mode 100644 meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/CHANGELOG.md create mode 100644 meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/Cargo.toml create mode 100644 meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/LICENSE create mode 100644 meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/README.md create mode 100644 meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/backend.rs create mode 100644 meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/can.rs create mode 100644 meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/main.rs create mode 100644 meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/vhu_can.rs create mode 100644 meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-crates.inc create mode 100644 meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can_0.1.0.bb create mode 100644 meta-egvirt/recipes-extended/vhost-device/README.md create mode 100644 meta-egvirt/recipes-extended/vhost-device/vhost-device-gpio-crates.inc create mode 100644 meta-egvirt/recipes-extended/vhost-device/vhost-device-gpio_0.1.0.bb create mode 100644 meta-egvirt/recipes-extended/vhost-device/vhost-device-rng-crates.inc create mode 100644 meta-egvirt/recipes-extended/vhost-device/vhost-device-rng_0.1.0.bb (limited to 'meta-egvirt/recipes-extended') diff --git a/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/CHANGELOG.md b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/CHANGELOG.md new file mode 100644 index 00000000..51d3f040 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog +## [Unreleased] + +### Added + +### Changed + +### Fixed + +### Deprecated + +## [0.1.0] + +First release + diff --git a/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/Cargo.toml b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/Cargo.toml new file mode 100644 index 00000000..08d012c2 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "vhost-device-can" +version = "0.0.1" +authors = ["Timos Ampelikiotis "] +description = "vhost can backend device" +repository = "https://github.com/rust-vmm/vhost-device" +readme = "README.md" +keywords = ["can", "vhost", "virt", "backend"] +license = "Apache-2.0 OR BSD-3-Clause" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"] + +[dependencies] +queues = "1.0.2" +socketcan = "3.2" +clap = { version = "4.2.5", features = ["derive"] } +env_logger = "0.10" +libc = "0.2" +log = "0.4" +thiserror = "1.0" +vhost = { version = "0.8", features = ["vhost-user-slave"] } +vhost-user-backend = "0.10" +virtio-bindings = "0.2.1" +virtio-queue = "0.9" +vm-memory = "0.12" +vmm-sys-util = "0.11" + +[dev-dependencies] +assert_matches = "1.5" +virtio-queue = { version = "0.9", features = ["test-utils"] } +vm-memory = { version = "0.12", features = ["backend-mmap", "backend-atomic"] } diff --git a/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/LICENSE b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/LICENSE new file mode 100644 index 00000000..e69de29b diff --git a/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/README.md b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/README.md new file mode 100644 index 00000000..fd50c274 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/README.md @@ -0,0 +1,66 @@ +# vhost-device-can - CAN emulation backend daemon + +## Description +This program is a vhost-user backend that emulates a VirtIO CAN device. +The device's binary takes three (3) parameters: a socket, a 'can-out' and a +'can-in' device name. The socket is commonly used across all vhost-devices to +communicate with the vhost-user frontend device. + +The 'can-out' represents +the actual CAN/FD device appears in the host system which vhost-device-can will +forward the messages from the frontend side. Finaly, the 'can-in' is again a +CAN/FD device connected on the host systems and vhost-device-can reads CAN/FD +frames and sends them to the frontend. The 'can-in' and 'can-out' can be find +by "ip link show" command. Also, the vhost-device-can may have the same CAN/FD +device name for both 'can-in' and 'can-out', if the user desires to setup a +loopback configuration. + + +This program is tested with Virtio-loopback's `-device vhost-user-can`. +Examples section below. + +## Synopsis + +**vhost-device-can** [*OPTIONS*] + +## Options + +.. program:: vhost-device-gpio + +.. option:: -h, --help + + Print help. + +.. option:: -s, --socket-path=PATH + +.. option:: -i, --can-int='CAN/FD interface name' + + The name of the input CAN interface to retrive CAN frames by + +.. option:: -o, --can-out='CAN/FD interface name' + + The name of the ouput CAN interface to send the CAN frames + +## Examples + +The daemon should be started first: + +:: + + host# vhost-device-can --socket-path=can.sock --can-in="can0" --can-out="can1" + +The virtio-looback invocation needs to insert the [virtio-loopback-transport](https://git.virtualopensystems.com/virtio-loopback/loopback_driver/-/tree/epsilon-release) driver +and then start the [virito-loopback-adapter](https://git.virtualopensystems.com/virtio-loopback/adapter_app/-/tree/epsilon-release) which is the intermediate between +vhost-device and virtio-loopback-transport driver. + +:: + + host# sudo insmod loopback_driver.ko + host# sudo ./adapter -s /path/to/can.sock0 -d vhucan + +## License + +This project is licensed under either of + +- [Apache License](http://www.apache.org/licenses/LICENSE-2.0), Version 2.0 +- [BSD-3-Clause License](https://opensource.org/licenses/BSD-3-Clause) diff --git a/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/backend.rs b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/backend.rs new file mode 100644 index 00000000..177c76cc --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/backend.rs @@ -0,0 +1,170 @@ +// VIRTIO CAN Emulation via vhost-user +// +// Copyright 2023 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::process::exit; +use std::sync::{Arc, RwLock}; +use std::thread::{spawn, JoinHandle}; + +use clap::Parser; +use thiserror::Error as ThisError; +use vhost::{vhost_user, vhost_user::Listener}; +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("Failed to join threads")] + FailedJoiningThreads, + #[error("Could not create can controller: {0}")] + CouldNotCreateCanController(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), +} + +#[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)] + socket_path: String, + + /// A can device name to be used for reading (ex. vcan, can0, can1, ... etc.) + #[clap(short = 'i', long)] + can_in: String, + + /// A can device name to be used for writing (ex. vcan, can0, can1, ... etc.) + #[clap(short = 'o', long)] + can_out: String, + + /// Number of guests (sockets) to connect to. + #[clap(short = 'c', long, default_value_t = 1)] + socket_count: u32, +} + +#[derive(PartialEq, Debug)] +struct CanConfiguration { + socket_path: String, + socket_count: u32, + can_in: String, + can_out: String, +} + +impl TryFrom for CanConfiguration { + type Error = Error; + + fn try_from(args: CanArgs) -> Result { + + if args.socket_count == 0 { + return Err(Error::SocketCountInvalid(0)); + } + + let can_in = args.can_in.trim().to_string(); + let can_out = args.can_out.trim().to_string(); + + Ok(CanConfiguration { + socket_path: args.socket_path, + socket_count: args.socket_count, + can_in, + can_out, + }) + } +} + +fn start_backend(args: CanArgs) -> Result<()> { + + println!("start_backend function!\n"); + + let config = CanConfiguration::try_from(args).unwrap(); + let mut handles = Vec::new(); + + for _ in 0..config.socket_count { + let socket = config.socket_path.to_owned(); + let can_in = config.can_in.to_owned(); + let can_out = config.can_out.to_owned(); + + let handle: JoinHandle> = spawn(move || loop { + // A separate thread is spawned for each socket and can connect to a separate guest. + // These are run in an infinite loop to not require the daemon to be restarted once a + // guest exits. + // + // There isn't much value in complicating code here to return an error from the + // threads, and so the code uses unwrap() instead. The panic on a thread won't cause + // trouble to other threads/guests or the main() function and should be safe for the + // daemon. + + let controller = + CanController::new(can_in.clone(), can_out.clone()).map_err(Error::CouldNotCreateCanController)?; + let shared_controller_1 = Arc::new(RwLock::new(controller)); + let shared_controller_2 = shared_controller_1.clone(); + let vu_can_backend = Arc::new(RwLock::new( + VhostUserCanBackend::new(shared_controller_1).map_err(Error::CouldNotCreateBackend)?, + )); + let _read_hanlde = CanController::start_read_thread(shared_controller_2); + + 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]); + + let listener = Listener::new(socket.clone(), true).unwrap(); + daemon.start(listener).unwrap(); + + match daemon.wait() { + Ok(()) => { + info!("Stopping cleanly."); + } + Err(vhost_user_backend::Error::HandleRequest( + vhost_user::Error::PartialMessage | vhost_user::Error::Disconnected, + )) => { + info!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug."); + } + Err(e) => { + warn!("Error running daemon: {:?}", e); + } + } + + // No matter the result, we need to shut down the worker thread. + vu_can_backend.read().unwrap().exit_event.write(1).unwrap(); + }); + + handles.push(handle); + } + + for handle in handles { + handle.join().map_err(|_| Error::FailedJoiningThreads)??; + } + + Ok(()) +} + +pub(crate) fn can_init() { + env_logger::init(); + println!("Can_init function!"); + if let Err(e) = start_backend(CanArgs::parse()) { + error!("{e}"); + exit(1); + } +} diff --git a/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/can.rs b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/can.rs new file mode 100644 index 00000000..228266f6 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/can.rs @@ -0,0 +1,264 @@ +// CAN backend device +// +// Copyright 2023 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved. +// Timos Ampelikiotis +// +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use log::{warn, error}; +use std::sync::{Arc, RwLock}; + +use thiserror::Error as ThisError; +use vm_memory::{ByteValued, Le16}; + +extern crate socketcan; +use socketcan::{ + CanFdSocket, CanFdFrame, CanAnyFrame, EmbeddedFrame, Socket, + Frame, StandardId, +}; + +use std::thread::{JoinHandle, spawn}; +use vmm_sys_util::eventfd::{EFD_NONBLOCK, EventFd}; + +extern crate queues; +use queues::*; + +use crate::vhu_can::{VirtioCanFrame, VIRTIO_CAN_STATUS_OK}; + +type Result = std::result::Result; + +#[derive(Copy, Clone, Debug, PartialEq, ThisError)] +/// Errors related to low level gpio helpers +pub(crate) enum Error { + //#[error("Can not enabled yet")] + //CanNotEnabled, + #[error("Can open socket operation failed")] + CanSocketFailed, + #[error("Can write socket operation failed")] + CanSocketWriteFailed, + #[error("Can read socket operation failed")] + CanSocketReadFailed, + #[error("Pop can element operation failed")] + CanPopFailed, + #[error("Creating Eventfd for CAN events failed")] + CanEventFdFailed, + #[error("CanQueueFailed")] + CanQueueFailed, +} + +/* CAN flags to determine type of CAN 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_TX: u16 = 0x0001; +pub(crate) const VIRTIO_CAN_RX: u16 = 0x0101; + +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) */ + +//pub(crate) const CAN_FRMF_BRS: u32 = 0x01; /* bit rate switch (2nd bitrate for data) */ +//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 */ + +/// 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 {} + +#[derive(Debug)] +pub(crate) struct CanController { + config: VirtioCanConfig, + pub can_in_name: String, + pub can_out_name: String, + can_out_socket: CanFdSocket, + pub rx_event_fd: EventFd, + rx_fifo: Queue, +} + +impl CanController { + // Creates a new controller corresponding to `device`. + pub(crate) fn new(can_in_name: String, can_out_name: String) -> Result { + + let can_in_name = can_in_name.to_owned(); + println!("can_in_name: {:?}", can_in_name); + + let can_out_name = can_out_name.to_owned(); + println!("can_out_name: {:?}", can_out_name); + + let can_out_socket = Self::open_can_sockets(can_out_name.clone()); + + let rx_fifo = Queue::new(); + + let rx_efd = EventFd::new(EFD_NONBLOCK).map_err(|_| Error::CanEventFdFailed)?; + + Ok(CanController { + config: VirtioCanConfig { + status: 0x0.into(), + }, + can_in_name, + can_out_name, + can_out_socket, + rx_event_fd: rx_efd, + rx_fifo + }) + } + + pub fn print_can_frame (canframe: VirtioCanFrame) { + println!("canframe.msg_type 0x{:x}", canframe.msg_type.to_native()); + println!("canframe.can_id 0x{:x}", canframe.can_id.to_native()); + println!("canframe.length {}", canframe.length.to_native()); + println!("canframe.flags 0x{:x}", canframe.flags.to_native()); + if canframe.length.to_native() == 0 { + println!("[]"); + return; + } + print!("["); + let last_elem = canframe.length.to_native() as usize - 1; + for (index, sdu) in canframe.sdu.iter().enumerate() { + print!("0x{:x}, ", sdu); + if index == last_elem { + print!("0x{:x}", sdu); + break; + } + } + println!("]"); + } + + /* FIXME: This thread is not handle after termination */ + 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(()), // Successfully added, so return Ok(()) + _ => Err(Error::CanQueueFailed), // Handle other errors + } + } + + pub fn pop(&mut self) -> Result { + match self.rx_fifo.remove() { + Ok(item) => Ok(item), + _ => Err(Error::CanPopFailed), + } + } + + fn open_can_sockets (can_out_name: String) -> CanFdSocket { + let can_out_socket = match CanFdSocket::open(&can_out_name) { + Ok(socket) => socket, + Err(_) => { + warn!("Error opening CAN socket"); + panic!("Failed to open CAN socket."); + //return Err(Error::CanSocketFailed); + } + }; + + can_out_socket + } + + pub fn read_can_socket (controller: Arc>) -> Result<()> { + let can_in_name = &controller.read().unwrap().can_in_name.clone(); + dbg!("Start reading from {} socket!", &can_in_name); + let socket = match CanFdSocket::open(&can_in_name) { + Ok(socket) => socket, + Err(_) => { + warn!("Error opening CAN socket"); + return Err(Error::CanSocketFailed); + } + }; + + // Receive CAN messages + loop { + if let Ok(frame) = socket.read_frame() { + + let mut controller = controller.write().unwrap(); + match frame { + CanAnyFrame::Normal(frame) => { + // Regular CAN frame + println!("Received CAN message: {:?}", frame); + } + CanAnyFrame::Fd(frame) => { + // CAN FD frame + println!("Received CAN FD message: {:?}", frame); + + let read_can_frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_RX.into(), + can_id: frame.raw_id().into(), + length: (frame.data().len() as u16).into(), + reserved: 0.into(), + flags: frame.id_flags().bits().into(), + sdu: { + let mut sdu_data: [u8; 64] = [0; 64]; + for i in 0..frame.data().len() { + sdu_data[i] = frame.data()[i]; + } + sdu_data + }, + }; + + match controller.push(read_can_frame) { + Ok(_) => warn!("New Can frame was received"), + Err(_) => { + warn!("Error read/push CAN frame"); + return Err(Error::CanSocketReadFailed); + } + } + } + CanAnyFrame::Remote(frame) => { + // Remote CAN frame + println!("Received Remote CAN message: {:?}", frame); + } + CanAnyFrame::Error(frame) => { + // Error frame + println!("Received Error frame: {:?}", frame); + } + } + + controller.rx_event_fd.write(1).unwrap(); + } + } + } + + pub(crate) fn config(&self) -> &VirtioCanConfig { + log::trace!("Get config\n"); + &self.config + } + + pub(crate) fn operation(&self, tx_request: VirtioCanFrame) -> Result { + log::trace!("Can operation\n"); + + // Create a CAN frame with a specific CAN-ID and the data buffer + let can_id = StandardId::new(tx_request.can_id.to_native().try_into().unwrap()).unwrap(); + let data_len = tx_request.length.to_native() as usize; + + let data: Vec = tx_request.sdu.iter().cloned().take(data_len).collect(); + let frame = CanFdFrame::new(can_id, &data).unwrap(); + + // Send the CAN frame + let write_result = self.can_out_socket.write_frame(&frame); + match write_result { + Ok(_) => Ok(VIRTIO_CAN_STATUS_OK), + Err(_) => { + warn!("Error write CAN socket"); + Err(Error::CanSocketWriteFailed) + } + } + } +} + diff --git a/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/main.rs b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/main.rs new file mode 100644 index 00000000..e89934ea --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/main.rs @@ -0,0 +1,26 @@ +// VIRTIO CAN Emulation via vhost-user +// +// Copyright 2023 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved. +// Timos Ampelikiotis +// +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +#[cfg(target_env = "gnu")] +mod backend; +#[cfg(target_env = "gnu")] +mod can; +#[cfg(target_env = "gnu")] +mod vhu_can; + +#[cfg(target_env = "gnu")] +fn main() { + println!("Hello to main vhost-user-can"); + backend::can_init() +} + +// Rust vmm container (https://github.com/rust-vmm/rust-vmm-container) doesn't +// have tools to do a musl build at the moment, and adding that support is +// tricky as well to the container. Skip musl builds until the time pre-built +// libgpiod library is available for musl. +#[cfg(target_env = "musl")] +fn main() {} 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 +// +// 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 = std::result::Result; + +#[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 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 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], +} + +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>, + 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: 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, + vring: &VringRwLock + ) -> Result { + 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::() { + println!("UnexpectedDescriptorSize, len = {:?}", desc_request.len()); + return Err(Error::UnexpectedDescriptorSize( + size_of::(), + desc_request.len(), + )); + } + + let request = desc_chain + .memory() + .read_obj::(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, + vring: &VringRwLock + ) -> Result { + 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::() { + println!("Tx UnexpectedDescriptorSize, len = {:?}", desc_request.len()); + //return Err(Error::UnexpectedDescriptorSize( + // size_of::(), + // desc_request.len(), + //)); + } + + let request = desc_chain + .memory() + .read_obj::(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, + vring: &VringRwLock + ) -> Result { + 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>, 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 + 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 { + // 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) -> 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 { + 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 { + dbg!("exit_event\n"); + self.exit_event.try_clone().ok() + } +} + diff --git a/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-crates.inc b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-crates.inc new file mode 100644 index 00000000..cf73bc44 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-crates.inc @@ -0,0 +1,83 @@ +SRC_URI += " \ + crate://crates.io/serde_derive/1.0.193 \ + crate://crates.io/autocfg/1.1.0 \ + crate://crates.io/syn/1.0.109 \ + crate://crates.io/serde/1.0.193 \ + crate://crates.io/memoffset/0.7.0 \ + crate://crates.io/static_assertions/1.1.0 \ + crate://crates.io/pin-utils/0.1.0 \ + crate://crates.io/cfg-if/1.0.0 \ + crate://crates.io/neli-proc-macros/0.1.3 \ + crate://crates.io/byteorder/1.5.0 \ + crate://crates.io/either/1.9.0 \ + crate://crates.io/nix/0.26.0 \ + crate://crates.io/neli/0.6.4 \ + crate://crates.io/nb/1.1.0 \ + crate://crates.io/itertools/0.10.0 \ + crate://crates.io/hex/0.4.3 \ + crate://crates.io/embedded-can/0.4.1 \ + crate://crates.io/byte_conv/0.1.1 \ + crate://crates.io/queues/1.1.0 \ + crate://crates.io/socketcan/3.3.0 \ + crate://crates.io/aho-corasick/1.0.2 \ + crate://crates.io/anstream/0.3.2 \ + crate://crates.io/anstyle/1.0.1 \ + crate://crates.io/anstyle-parse/0.2.1 \ + crate://crates.io/anstyle-query/1.0.0 \ + crate://crates.io/anstyle-wincon/1.0.1 \ + crate://crates.io/arc-swap/1.6.0 \ + crate://crates.io/assert_matches/1.5.0 \ + crate://crates.io/bitflags/1.3.2 \ + crate://crates.io/bitflags/2.3.3 \ + crate://crates.io/cc/1.0.79 \ + crate://crates.io/clap/4.2.5 \ + crate://crates.io/clap_builder/4.2.5 \ + crate://crates.io/clap_derive/4.2.0 \ + crate://crates.io/clap_lex/0.4.0 \ + crate://crates.io/colorchoice/1.0.0 \ + crate://crates.io/env_logger/0.10.0 \ + crate://crates.io/errno/0.3.1 \ + crate://crates.io/errno-dragonfly/0.1.2 \ + crate://crates.io/heck/0.4.1 \ + crate://crates.io/hermit-abi/0.3.2 \ + crate://crates.io/humantime/2.1.0 \ + crate://crates.io/is-terminal/0.4.9 \ + crate://crates.io/libc/0.2.147 \ + crate://crates.io/linux-raw-sys/0.4.3 \ + crate://crates.io/log/0.4.19 \ + crate://crates.io/memchr/2.5.0 \ + crate://crates.io/once_cell/1.18.0 \ + crate://crates.io/proc-macro2/1.0.67 \ + crate://crates.io/quote/1.0.29 \ + crate://crates.io/regex/1.9.1 \ + crate://crates.io/regex-automata/0.3.2 \ + crate://crates.io/regex-syntax/0.7.4 \ + crate://crates.io/rustix/0.38.3 \ + crate://crates.io/strsim/0.10.0 \ + crate://crates.io/syn/2.0.28 \ + crate://crates.io/termcolor/1.2.0 \ + crate://crates.io/thiserror/1.0.41 \ + crate://crates.io/thiserror-impl/1.0.41 \ + crate://crates.io/unicode-ident/1.0.11 \ + crate://crates.io/utf8parse/0.2.1 \ + crate://crates.io/vhost/0.8.0 \ + crate://crates.io/vhost-user-backend/0.10.0 \ + crate://crates.io/virtio-bindings/0.2.1 \ + crate://crates.io/virtio-queue/0.9.0 \ + crate://crates.io/vm-memory/0.12.0 \ + crate://crates.io/vmm-sys-util/0.11.1 \ + crate://crates.io/winapi/0.3.9 \ + crate://crates.io/winapi-i686-pc-windows-gnu/0.4.0 \ + crate://crates.io/winapi-util/0.1.5 \ + crate://crates.io/winapi-x86_64-pc-windows-gnu/0.4.0 \ + crate://crates.io/windows-sys/0.48.0 \ + crate://crates.io/windows-targets/0.48.1 \ + crate://crates.io/windows_aarch64_gnullvm/0.48.0 \ + crate://crates.io/windows_aarch64_msvc/0.48.0 \ + crate://crates.io/windows_i686_gnu/0.48.0 \ + crate://crates.io/windows_i686_msvc/0.48.0 \ + crate://crates.io/windows_x86_64_gnu/0.48.0 \ + crate://crates.io/windows_x86_64_gnullvm/0.48.0 \ + crate://crates.io/windows_x86_64_msvc/0.48.0 \ +" + diff --git a/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can_0.1.0.bb b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can_0.1.0.bb new file mode 100644 index 00000000..c18e7517 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can_0.1.0.bb @@ -0,0 +1,18 @@ +SUMMARY = "vhost CAN backend device" +DESCRIPTION = "A vhost-user backend that emulates a VirtIO CAN device" +HOMEPAGE = "https://gerrit.automotivelinux.org" + +FILESEXTRAPATHS:prepend := "${THISDIR}:" +EXTRAPATHS:prepend := "${THISDIR}:" + +SRC_URI = " file://. " + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "\ + file://LICENSE;md5=d41d8cd98f00b204e9800998ecf8427e \ +" + +inherit cargo +inherit pkgconfig + +include vhost-device-can-crates.inc diff --git a/meta-egvirt/recipes-extended/vhost-device/README.md b/meta-egvirt/recipes-extended/vhost-device/README.md new file mode 100644 index 00000000..950ffca9 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device/README.md @@ -0,0 +1,12 @@ +# AGL info +This folder contains meta-virtualization recipes from master. We copy them here +waiting for vhost-device for being available for kirkstone (or AGL moving +forward). + +# vhost-device: A collection of vhost-user devices + +[vhost-device](https://github.com/rust-vmm/vhost-device) provides a series of +daemons that implement the +[vhost-user protocol](https://qemu-project.gitlab.io/qemu/interop/vhost-user.html) +for various virtio device types. + diff --git a/meta-egvirt/recipes-extended/vhost-device/vhost-device-gpio-crates.inc b/meta-egvirt/recipes-extended/vhost-device/vhost-device-gpio-crates.inc new file mode 100644 index 00000000..4d73c1fd --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device/vhost-device-gpio-crates.inc @@ -0,0 +1,184 @@ +# Autogenerated with 'bitbake -c update_crates vhost-device-gpio' + +# from Cargo.lock +SRC_URI += " \ + crate://crates.io/aho-corasick/1.0.2 \ + crate://crates.io/anstream/0.3.2 \ + crate://crates.io/anstyle/1.0.1 \ + crate://crates.io/anstyle-parse/0.2.1 \ + crate://crates.io/anstyle-query/1.0.0 \ + crate://crates.io/anstyle-wincon/1.0.1 \ + crate://crates.io/arc-swap/1.6.0 \ + crate://crates.io/assert_matches/1.5.0 \ + crate://crates.io/bindgen/0.63.0 \ + crate://crates.io/bitflags/1.3.2 \ + crate://crates.io/bitflags/2.3.3 \ + crate://crates.io/cc/1.0.79 \ + crate://crates.io/cexpr/0.6.0 \ + crate://crates.io/cfg-if/1.0.0 \ + crate://crates.io/clang-sys/1.6.1 \ + crate://crates.io/clap/4.3.19 \ + crate://crates.io/clap_builder/4.3.19 \ + crate://crates.io/clap_derive/4.3.12 \ + crate://crates.io/clap_lex/0.5.0 \ + crate://crates.io/colorchoice/1.0.0 \ + crate://crates.io/either/1.8.1 \ + crate://crates.io/env_logger/0.10.0 \ + crate://crates.io/errno/0.2.8 \ + crate://crates.io/errno/0.3.1 \ + crate://crates.io/errno-dragonfly/0.1.2 \ + crate://crates.io/glob/0.3.1 \ + crate://crates.io/heck/0.3.3 \ + crate://crates.io/heck/0.4.1 \ + crate://crates.io/hermit-abi/0.3.2 \ + crate://crates.io/humantime/2.1.0 \ + crate://crates.io/intmap/2.0.0 \ + crate://crates.io/is-terminal/0.4.9 \ + crate://crates.io/lazy_static/1.4.0 \ + crate://crates.io/lazycell/1.3.0 \ + crate://crates.io/libc/0.2.147 \ + crate://crates.io/libgpiod/0.1.0 \ + crate://crates.io/libgpiod-sys/0.1.0 \ + crate://crates.io/libloading/0.7.4 \ + crate://crates.io/linux-raw-sys/0.4.3 \ + crate://crates.io/log/0.4.19 \ + crate://crates.io/memchr/2.5.0 \ + crate://crates.io/minimal-lexical/0.2.1 \ + crate://crates.io/nom/7.1.3 \ + crate://crates.io/once_cell/1.18.0 \ + crate://crates.io/peeking_take_while/0.1.2 \ + crate://crates.io/pkg-config/0.3.27 \ + crate://crates.io/proc-macro2/1.0.63 \ + crate://crates.io/quote/1.0.29 \ + crate://crates.io/regex/1.9.1 \ + crate://crates.io/regex-automata/0.3.2 \ + crate://crates.io/regex-syntax/0.7.4 \ + crate://crates.io/rustc-hash/1.1.0 \ + crate://crates.io/rustix/0.38.3 \ + crate://crates.io/serde/1.0.168 \ + crate://crates.io/shlex/1.1.0 \ + crate://crates.io/strsim/0.10.0 \ + crate://crates.io/strum/0.20.0 \ + crate://crates.io/strum_macros/0.20.1 \ + crate://crates.io/syn/1.0.109 \ + crate://crates.io/syn/2.0.23 \ + crate://crates.io/system-deps/2.0.3 \ + crate://crates.io/termcolor/1.2.0 \ + crate://crates.io/thiserror/1.0.41 \ + crate://crates.io/thiserror-impl/1.0.41 \ + crate://crates.io/toml/0.5.11 \ + crate://crates.io/unicode-ident/1.0.11 \ + crate://crates.io/unicode-segmentation/1.10.1 \ + crate://crates.io/utf8parse/0.2.1 \ + crate://crates.io/version-compare/0.0.11 \ + crate://crates.io/vhost/0.8.0 \ + crate://crates.io/vhost-user-backend/0.10.0 \ + crate://crates.io/virtio-bindings/0.2.1 \ + crate://crates.io/virtio-queue/0.9.0 \ + crate://crates.io/vm-memory/0.12.0 \ + crate://crates.io/vmm-sys-util/0.11.1 \ + crate://crates.io/which/4.4.0 \ + crate://crates.io/winapi/0.3.9 \ + crate://crates.io/winapi-i686-pc-windows-gnu/0.4.0 \ + crate://crates.io/winapi-util/0.1.5 \ + crate://crates.io/winapi-x86_64-pc-windows-gnu/0.4.0 \ + crate://crates.io/windows-sys/0.48.0 \ + crate://crates.io/windows-targets/0.48.1 \ + crate://crates.io/windows_aarch64_gnullvm/0.48.0 \ + crate://crates.io/windows_aarch64_msvc/0.48.0 \ + crate://crates.io/windows_i686_gnu/0.48.0 \ + crate://crates.io/windows_i686_msvc/0.48.0 \ + crate://crates.io/windows_x86_64_gnu/0.48.0 \ + crate://crates.io/windows_x86_64_gnullvm/0.48.0 \ + crate://crates.io/windows_x86_64_msvc/0.48.0 \ +" + +SRC_URI[aho-corasick-1.0.2.sha256sum] = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +SRC_URI[anstream-0.3.2.sha256sum] = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +SRC_URI[anstyle-1.0.1.sha256sum] = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +SRC_URI[anstyle-parse-0.2.1.sha256sum] = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +SRC_URI[anstyle-query-1.0.0.sha256sum] = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +SRC_URI[anstyle-wincon-1.0.1.sha256sum] = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +SRC_URI[arc-swap-1.6.0.sha256sum] = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +SRC_URI[assert_matches-1.5.0.sha256sum] = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" +SRC_URI[bindgen-0.63.0.sha256sum] = "36d860121800b2a9a94f9b5604b332d5cffb234ce17609ea479d723dbc9d3885" +SRC_URI[bitflags-1.3.2.sha256sum] = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +SRC_URI[bitflags-2.3.3.sha256sum] = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +SRC_URI[cc-1.0.79.sha256sum] = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +SRC_URI[cexpr-0.6.0.sha256sum] = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +SRC_URI[cfg-if-1.0.0.sha256sum] = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +SRC_URI[clang-sys-1.6.1.sha256sum] = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +SRC_URI[clap-4.3.19.sha256sum] = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" +SRC_URI[clap_builder-4.3.19.sha256sum] = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" +SRC_URI[clap_derive-4.3.12.sha256sum] = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +SRC_URI[clap_lex-0.5.0.sha256sum] = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +SRC_URI[colorchoice-1.0.0.sha256sum] = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +SRC_URI[either-1.8.1.sha256sum] = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +SRC_URI[env_logger-0.10.0.sha256sum] = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +SRC_URI[errno-0.2.8.sha256sum] = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +SRC_URI[errno-0.3.1.sha256sum] = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +SRC_URI[errno-dragonfly-0.1.2.sha256sum] = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +SRC_URI[glob-0.3.1.sha256sum] = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +SRC_URI[heck-0.3.3.sha256sum] = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +SRC_URI[heck-0.4.1.sha256sum] = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +SRC_URI[hermit-abi-0.3.2.sha256sum] = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +SRC_URI[humantime-2.1.0.sha256sum] = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +SRC_URI[intmap-2.0.0.sha256sum] = "ee87fd093563344074bacf24faa0bb0227fb6969fb223e922db798516de924d6" +SRC_URI[is-terminal-0.4.9.sha256sum] = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +SRC_URI[lazy_static-1.4.0.sha256sum] = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +SRC_URI[lazycell-1.3.0.sha256sum] = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +SRC_URI[libc-0.2.147.sha256sum] = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +SRC_URI[libgpiod-0.1.0.sha256sum] = "9e9fdf4b437063f5697151f9ead12bafa223958e243f2f736107ec68c2b88231" +SRC_URI[libgpiod-sys-0.1.0.sha256sum] = "aa282e1da652deaeed776f6ef36d443689aeda19e5c0a3a2335c50b4611ce489" +SRC_URI[libloading-0.7.4.sha256sum] = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +SRC_URI[linux-raw-sys-0.4.3.sha256sum] = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +SRC_URI[log-0.4.19.sha256sum] = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +SRC_URI[memchr-2.5.0.sha256sum] = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +SRC_URI[minimal-lexical-0.2.1.sha256sum] = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +SRC_URI[nom-7.1.3.sha256sum] = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +SRC_URI[once_cell-1.18.0.sha256sum] = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +SRC_URI[peeking_take_while-0.1.2.sha256sum] = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +SRC_URI[pkg-config-0.3.27.sha256sum] = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +SRC_URI[proc-macro2-1.0.63.sha256sum] = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +SRC_URI[quote-1.0.29.sha256sum] = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +SRC_URI[regex-1.9.1.sha256sum] = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +SRC_URI[regex-automata-0.3.2.sha256sum] = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" +SRC_URI[regex-syntax-0.7.4.sha256sum] = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +SRC_URI[rustc-hash-1.1.0.sha256sum] = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +SRC_URI[rustix-0.38.3.sha256sum] = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" +SRC_URI[serde-1.0.168.sha256sum] = "d614f89548720367ded108b3c843be93f3a341e22d5674ca0dd5cd57f34926af" +SRC_URI[shlex-1.1.0.sha256sum] = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +SRC_URI[strsim-0.10.0.sha256sum] = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +SRC_URI[strum-0.20.0.sha256sum] = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c" +SRC_URI[strum_macros-0.20.1.sha256sum] = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149" +SRC_URI[syn-1.0.109.sha256sum] = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +SRC_URI[syn-2.0.23.sha256sum] = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" +SRC_URI[system-deps-2.0.3.sha256sum] = "1b59b8aafd652f3c1469f16e6c223121e8a8dbe40c71475209c1401cff3a67ef" +SRC_URI[termcolor-1.2.0.sha256sum] = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +SRC_URI[thiserror-1.0.41.sha256sum] = "c16a64ba9387ef3fdae4f9c1a7f07a0997fce91985c0336f1ddc1822b3b37802" +SRC_URI[thiserror-impl-1.0.41.sha256sum] = "d14928354b01c4d6a4f0e549069adef399a284e7995c7ccca94e8a07a5346c59" +SRC_URI[toml-0.5.11.sha256sum] = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +SRC_URI[unicode-ident-1.0.11.sha256sum] = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +SRC_URI[unicode-segmentation-1.10.1.sha256sum] = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +SRC_URI[utf8parse-0.2.1.sha256sum] = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +SRC_URI[version-compare-0.0.11.sha256sum] = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" +SRC_URI[vhost-0.8.0.sha256sum] = "73832f4d8d636d63d9b145e8ef22a2c50b93f4d24eb7a99c9e6781b1b08549cf" +SRC_URI[vhost-user-backend-0.10.0.sha256sum] = "e3ea9d5e8ec847cde4df1c04e586698a479706fd6beca37323f9d425b24b4c2f" +SRC_URI[virtio-bindings-0.2.1.sha256sum] = "c18d7b74098a946470ea265b5bacbbf877abc3373021388454de0d47735a5b98" +SRC_URI[virtio-queue-0.9.0.sha256sum] = "35aca00da06841bd99162c381ec65893cace23ca0fb89254302cfe4bec4c300f" +SRC_URI[vm-memory-0.12.0.sha256sum] = "a77c7a0891cbac53618f5f6eec650ed1dc4f7e506bbe14877aff49d94b8408b0" +SRC_URI[vmm-sys-util-0.11.1.sha256sum] = "dd64fe09d8e880e600c324e7d664760a17f56e9672b7495a86381b49e4f72f46" +SRC_URI[which-4.4.0.sha256sum] = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +SRC_URI[winapi-0.3.9.sha256sum] = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +SRC_URI[winapi-i686-pc-windows-gnu-0.4.0.sha256sum] = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +SRC_URI[winapi-util-0.1.5.sha256sum] = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +SRC_URI[winapi-x86_64-pc-windows-gnu-0.4.0.sha256sum] = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +SRC_URI[windows-sys-0.48.0.sha256sum] = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +SRC_URI[windows-targets-0.48.1.sha256sum] = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +SRC_URI[windows_aarch64_gnullvm-0.48.0.sha256sum] = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +SRC_URI[windows_aarch64_msvc-0.48.0.sha256sum] = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +SRC_URI[windows_i686_gnu-0.48.0.sha256sum] = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +SRC_URI[windows_i686_msvc-0.48.0.sha256sum] = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +SRC_URI[windows_x86_64_gnu-0.48.0.sha256sum] = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +SRC_URI[windows_x86_64_gnullvm-0.48.0.sha256sum] = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +SRC_URI[windows_x86_64_msvc-0.48.0.sha256sum] = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/meta-egvirt/recipes-extended/vhost-device/vhost-device-gpio_0.1.0.bb b/meta-egvirt/recipes-extended/vhost-device/vhost-device-gpio_0.1.0.bb new file mode 100644 index 00000000..179f1fb6 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device/vhost-device-gpio_0.1.0.bb @@ -0,0 +1,22 @@ +SUMMARY = "vhost gpio backend device" +DESCRIPTION = "A vhost-user backend that emulates a VirtIO GPIO device" +HOMEPAGE = "https://github.com/rust-vmm/vhost-device" +LICENSE = "Apache-2.0 | BSD-3-Clause" +LIC_FILES_CHKSUM = "\ + file://LICENSE-APACHE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://LICENSE-BSD-3-Clause;md5=2489db1359f496fff34bd393df63947e \ +" +DEPENDS += "libgpiod" + +# libgpiod-sys generates bindings using bindgen, which depends on clang +DEPENDS += "clang-native" + +SKIP_RECIPE[vhost-device-gpio] ?= "${@bb.utils.contains('BBFILE_COLLECTIONS', 'clang-layer', '', 'Depends on clang-native from meta-clang which is not included', d)}" + +SRC_URI += "crate://crates.io/vhost-device-gpio/0.1.0" +SRC_URI[vhost-device-gpio-0.1.0.sha256sum] = "f4789dd127ce746d4f702d50256ff09e47b19fdb2bfee88a254b7e48efbf1100" + +inherit cargo +inherit pkgconfig + +include vhost-device-gpio-crates.inc diff --git a/meta-egvirt/recipes-extended/vhost-device/vhost-device-rng-crates.inc b/meta-egvirt/recipes-extended/vhost-device/vhost-device-rng-crates.inc new file mode 100644 index 00000000..5a50da3e --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device/vhost-device-rng-crates.inc @@ -0,0 +1,158 @@ +# Autogenerated with 'bitbake -c update_crates vhost-device-rng' + +# from Cargo.lock +SRC_URI += " \ + crate://crates.io/aho-corasick/1.0.2 \ + crate://crates.io/anstream/0.3.2 \ + crate://crates.io/anstyle/1.0.1 \ + crate://crates.io/anstyle-parse/0.2.1 \ + crate://crates.io/anstyle-query/1.0.0 \ + crate://crates.io/anstyle-wincon/1.0.1 \ + crate://crates.io/arc-swap/1.6.0 \ + crate://crates.io/assert_matches/1.5.0 \ + crate://crates.io/autocfg/1.1.0 \ + crate://crates.io/bitflags/1.3.2 \ + crate://crates.io/bitflags/2.3.3 \ + crate://crates.io/cc/1.0.79 \ + crate://crates.io/cfg-if/1.0.0 \ + crate://crates.io/clap/4.3.19 \ + crate://crates.io/clap_builder/4.3.19 \ + crate://crates.io/clap_derive/4.3.12 \ + crate://crates.io/clap_lex/0.5.0 \ + crate://crates.io/colorchoice/1.0.0 \ + crate://crates.io/env_logger/0.10.0 \ + crate://crates.io/epoll/4.3.3 \ + crate://crates.io/errno/0.3.1 \ + crate://crates.io/errno-dragonfly/0.1.2 \ + crate://crates.io/fastrand/1.9.0 \ + crate://crates.io/getrandom/0.2.10 \ + crate://crates.io/heck/0.4.1 \ + crate://crates.io/hermit-abi/0.3.2 \ + crate://crates.io/humantime/2.1.0 \ + crate://crates.io/instant/0.1.12 \ + crate://crates.io/io-lifetimes/1.0.11 \ + crate://crates.io/is-terminal/0.4.9 \ + crate://crates.io/libc/0.2.147 \ + crate://crates.io/linux-raw-sys/0.3.8 \ + crate://crates.io/linux-raw-sys/0.4.3 \ + crate://crates.io/log/0.4.19 \ + crate://crates.io/memchr/2.5.0 \ + crate://crates.io/once_cell/1.18.0 \ + crate://crates.io/ppv-lite86/0.2.17 \ + crate://crates.io/proc-macro2/1.0.63 \ + crate://crates.io/quote/1.0.29 \ + crate://crates.io/rand/0.8.5 \ + crate://crates.io/rand_chacha/0.3.1 \ + crate://crates.io/rand_core/0.6.4 \ + crate://crates.io/redox_syscall/0.3.5 \ + crate://crates.io/regex/1.9.1 \ + crate://crates.io/regex-automata/0.3.2 \ + crate://crates.io/regex-syntax/0.7.4 \ + crate://crates.io/rustix/0.37.23 \ + crate://crates.io/rustix/0.38.3 \ + crate://crates.io/strsim/0.10.0 \ + crate://crates.io/syn/2.0.23 \ + crate://crates.io/tempfile/3.6.0 \ + crate://crates.io/termcolor/1.2.0 \ + crate://crates.io/thiserror/1.0.41 \ + crate://crates.io/thiserror-impl/1.0.41 \ + crate://crates.io/unicode-ident/1.0.11 \ + crate://crates.io/utf8parse/0.2.1 \ + crate://crates.io/vhost/0.8.0 \ + crate://crates.io/vhost-user-backend/0.10.0 \ + crate://crates.io/virtio-bindings/0.2.1 \ + crate://crates.io/virtio-queue/0.9.0 \ + crate://crates.io/vm-memory/0.12.0 \ + crate://crates.io/vmm-sys-util/0.11.1 \ + crate://crates.io/wasi/0.11.0+wasi-snapshot-preview1 \ + crate://crates.io/winapi/0.3.9 \ + crate://crates.io/winapi-i686-pc-windows-gnu/0.4.0 \ + crate://crates.io/winapi-util/0.1.5 \ + crate://crates.io/winapi-x86_64-pc-windows-gnu/0.4.0 \ + crate://crates.io/windows-sys/0.48.0 \ + crate://crates.io/windows-targets/0.48.1 \ + crate://crates.io/windows_aarch64_gnullvm/0.48.0 \ + crate://crates.io/windows_aarch64_msvc/0.48.0 \ + crate://crates.io/windows_i686_gnu/0.48.0 \ + crate://crates.io/windows_i686_msvc/0.48.0 \ + crate://crates.io/windows_x86_64_gnu/0.48.0 \ + crate://crates.io/windows_x86_64_gnullvm/0.48.0 \ + crate://crates.io/windows_x86_64_msvc/0.48.0 \ +" + +SRC_URI[aho-corasick-1.0.2.sha256sum] = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +SRC_URI[anstream-0.3.2.sha256sum] = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +SRC_URI[anstyle-1.0.1.sha256sum] = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +SRC_URI[anstyle-parse-0.2.1.sha256sum] = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +SRC_URI[anstyle-query-1.0.0.sha256sum] = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +SRC_URI[anstyle-wincon-1.0.1.sha256sum] = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +SRC_URI[arc-swap-1.6.0.sha256sum] = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +SRC_URI[assert_matches-1.5.0.sha256sum] = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" +SRC_URI[autocfg-1.1.0.sha256sum] = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +SRC_URI[bitflags-1.3.2.sha256sum] = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +SRC_URI[bitflags-2.3.3.sha256sum] = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +SRC_URI[cc-1.0.79.sha256sum] = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +SRC_URI[cfg-if-1.0.0.sha256sum] = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +SRC_URI[clap-4.3.19.sha256sum] = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" +SRC_URI[clap_builder-4.3.19.sha256sum] = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" +SRC_URI[clap_derive-4.3.12.sha256sum] = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +SRC_URI[clap_lex-0.5.0.sha256sum] = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +SRC_URI[colorchoice-1.0.0.sha256sum] = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +SRC_URI[env_logger-0.10.0.sha256sum] = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +SRC_URI[epoll-4.3.3.sha256sum] = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79" +SRC_URI[errno-0.3.1.sha256sum] = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +SRC_URI[errno-dragonfly-0.1.2.sha256sum] = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +SRC_URI[fastrand-1.9.0.sha256sum] = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +SRC_URI[getrandom-0.2.10.sha256sum] = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +SRC_URI[heck-0.4.1.sha256sum] = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +SRC_URI[hermit-abi-0.3.2.sha256sum] = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +SRC_URI[humantime-2.1.0.sha256sum] = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +SRC_URI[instant-0.1.12.sha256sum] = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +SRC_URI[io-lifetimes-1.0.11.sha256sum] = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +SRC_URI[is-terminal-0.4.9.sha256sum] = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +SRC_URI[libc-0.2.147.sha256sum] = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +SRC_URI[linux-raw-sys-0.3.8.sha256sum] = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +SRC_URI[linux-raw-sys-0.4.3.sha256sum] = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +SRC_URI[log-0.4.19.sha256sum] = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +SRC_URI[memchr-2.5.0.sha256sum] = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +SRC_URI[once_cell-1.18.0.sha256sum] = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +SRC_URI[ppv-lite86-0.2.17.sha256sum] = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +SRC_URI[proc-macro2-1.0.63.sha256sum] = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +SRC_URI[quote-1.0.29.sha256sum] = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +SRC_URI[rand-0.8.5.sha256sum] = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +SRC_URI[rand_chacha-0.3.1.sha256sum] = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +SRC_URI[rand_core-0.6.4.sha256sum] = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +SRC_URI[redox_syscall-0.3.5.sha256sum] = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +SRC_URI[regex-1.9.1.sha256sum] = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +SRC_URI[regex-automata-0.3.2.sha256sum] = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" +SRC_URI[regex-syntax-0.7.4.sha256sum] = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +SRC_URI[rustix-0.37.23.sha256sum] = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +SRC_URI[rustix-0.38.3.sha256sum] = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" +SRC_URI[strsim-0.10.0.sha256sum] = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +SRC_URI[syn-2.0.23.sha256sum] = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" +SRC_URI[tempfile-3.6.0.sha256sum] = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +SRC_URI[termcolor-1.2.0.sha256sum] = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +SRC_URI[thiserror-1.0.41.sha256sum] = "c16a64ba9387ef3fdae4f9c1a7f07a0997fce91985c0336f1ddc1822b3b37802" +SRC_URI[thiserror-impl-1.0.41.sha256sum] = "d14928354b01c4d6a4f0e549069adef399a284e7995c7ccca94e8a07a5346c59" +SRC_URI[unicode-ident-1.0.11.sha256sum] = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +SRC_URI[utf8parse-0.2.1.sha256sum] = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +SRC_URI[vhost-0.8.0.sha256sum] = "73832f4d8d636d63d9b145e8ef22a2c50b93f4d24eb7a99c9e6781b1b08549cf" +SRC_URI[vhost-user-backend-0.10.0.sha256sum] = "e3ea9d5e8ec847cde4df1c04e586698a479706fd6beca37323f9d425b24b4c2f" +SRC_URI[virtio-bindings-0.2.1.sha256sum] = "c18d7b74098a946470ea265b5bacbbf877abc3373021388454de0d47735a5b98" +SRC_URI[virtio-queue-0.9.0.sha256sum] = "35aca00da06841bd99162c381ec65893cace23ca0fb89254302cfe4bec4c300f" +SRC_URI[vm-memory-0.12.0.sha256sum] = "a77c7a0891cbac53618f5f6eec650ed1dc4f7e506bbe14877aff49d94b8408b0" +SRC_URI[vmm-sys-util-0.11.1.sha256sum] = "dd64fe09d8e880e600c324e7d664760a17f56e9672b7495a86381b49e4f72f46" +SRC_URI[wasi-0.11.0+wasi-snapshot-preview1.sha256sum] = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +SRC_URI[winapi-0.3.9.sha256sum] = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +SRC_URI[winapi-i686-pc-windows-gnu-0.4.0.sha256sum] = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +SRC_URI[winapi-util-0.1.5.sha256sum] = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +SRC_URI[winapi-x86_64-pc-windows-gnu-0.4.0.sha256sum] = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +SRC_URI[windows-sys-0.48.0.sha256sum] = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +SRC_URI[windows-targets-0.48.1.sha256sum] = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +SRC_URI[windows_aarch64_gnullvm-0.48.0.sha256sum] = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +SRC_URI[windows_aarch64_msvc-0.48.0.sha256sum] = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +SRC_URI[windows_i686_gnu-0.48.0.sha256sum] = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +SRC_URI[windows_i686_msvc-0.48.0.sha256sum] = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +SRC_URI[windows_x86_64_gnu-0.48.0.sha256sum] = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +SRC_URI[windows_x86_64_gnullvm-0.48.0.sha256sum] = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +SRC_URI[windows_x86_64_msvc-0.48.0.sha256sum] = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/meta-egvirt/recipes-extended/vhost-device/vhost-device-rng_0.1.0.bb b/meta-egvirt/recipes-extended/vhost-device/vhost-device-rng_0.1.0.bb new file mode 100644 index 00000000..17b5fbea --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device/vhost-device-rng_0.1.0.bb @@ -0,0 +1,16 @@ +SUMMARY = "vhost rng backend device" +DESCRIPTION = "A vhost-user backend that emulates a VirtIO random number \ + generator device" +HOMEPAGE = "https://github.com/rust-vmm/vhost-device" +LICENSE = "Apache-2.0 | BSD-3-Clause" +LIC_FILES_CHKSUM = " \ + file://LICENSE-APACHE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://LICENSE-BSD-3-Clause;md5=2489db1359f496fff34bd393df63947e \ +" + +SRC_URI += "crate://crates.io/vhost-device-rng/0.1.0" +SRC_URI[vhost-device-rng-0.1.0.sha256sum] = "51e0af4acaaefc19a3d6c2e2cb00caf2130d728d857494c857662781da9f1329" + +inherit cargo + +include vhost-device-rng-crates.inc -- cgit 1.2.3-korg